add
This commit is contained in:
250
create/README.md
250
create/README.md
@@ -1,4 +1,120 @@
|
||||
代码一:
|
||||
|
||||
这是最新的文件夹结构:
|
||||
|
||||
``` tree
|
||||
.
|
||||
├── LICENSE
|
||||
├── README.md
|
||||
├── create
|
||||
│ ├── README.md
|
||||
│ ├── create.sh
|
||||
│ ├── create_src.py
|
||||
│ ├── create_src_dockerfile.py
|
||||
│ └── create_table.py
|
||||
├── docker-compose.yaml
|
||||
├── infra
|
||||
│ └── postgres
|
||||
│ ├── scripts
|
||||
│ │ └── db-lanuch-entrypoint.sh
|
||||
│ └── sql
|
||||
│ ├── 01_uuid_v7_setup.sql
|
||||
│ ├── 02_create_function.sql
|
||||
│ └── 03_create_table.sql
|
||||
└── services
|
||||
```
|
||||
|
||||
|
||||
|
||||
以下是 shell 脚本内容。
|
||||
|
||||
``` shell
|
||||
#!/bin/bash
|
||||
# create.sh - 启动Python容器执行create_table.py和create_src.py脚本
|
||||
|
||||
set -e # 遇到错误立即退出
|
||||
|
||||
# 定义表名变量,可以根据需要修改
|
||||
TABLE_NAME="cn_pmi_records"
|
||||
|
||||
echo "🚀 开始创建流程,表名: ${TABLE_NAME}"
|
||||
echo "=========================================="
|
||||
|
||||
# ========== 第一部分:创建数据库表 ==========
|
||||
echo "📋 第一步:创建数据库表结构..."
|
||||
echo "🚀 启动Python容器执行create_table.py..."
|
||||
|
||||
# 获取脚本所在目录的绝对路径
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
# 容器名称
|
||||
CONTAINER_NAME="python-create-runner"
|
||||
|
||||
# 检查是否已存在同名容器,如果存在则删除
|
||||
if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
||||
echo "🧹 清理已存在的容器 ${CONTAINER_NAME}..."
|
||||
docker rm -f "${CONTAINER_NAME}" >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
# 运行Python容器执行create_table.py
|
||||
echo "📦 启动Python容器执行create_table.py..."
|
||||
docker run --rm \
|
||||
--name "${CONTAINER_NAME}" \
|
||||
-v "${SCRIPT_DIR}:/app/create" \
|
||||
-v "${PROJECT_ROOT}/infra/postgres/sql:/app/infra/postgres/sql" \
|
||||
-e TABLE_NAME="${TABLE_NAME}" \
|
||||
-w /app \
|
||||
python:3.13.7-alpine3.22 \
|
||||
sh -c "
|
||||
echo '📋 容器内环境信息:'
|
||||
python --version
|
||||
echo ''
|
||||
|
||||
echo '🔧 安装依赖(如果需要)...'
|
||||
pip install --quiet --no-cache-dir psycopg2-binary >/dev/null 2>&1 || true
|
||||
|
||||
echo '⚙️ 执行 create_table.py...'
|
||||
python create/create_table.py
|
||||
|
||||
echo ''
|
||||
echo '✅ create_table.py 执行完成!'
|
||||
"
|
||||
|
||||
# ========== 第二部分:创建服务结构 ==========
|
||||
echo ""
|
||||
echo "📋 第二步:创建服务文件夹结构..."
|
||||
echo "🚀 执行create_src.py..."
|
||||
|
||||
# 运行Python容器执行create_src.py
|
||||
docker run --rm \
|
||||
--name "${CONTAINER_NAME}-src" \
|
||||
-v "${SCRIPT_DIR}:/app/create" \
|
||||
-v "${PROJECT_ROOT}/services:/app/services" \
|
||||
-e TABLE_NAME="${TABLE_NAME}" \
|
||||
-w /app \
|
||||
python:3.13.7-alpine3.22 \
|
||||
sh -c "
|
||||
echo '📋 容器内环境信息:'
|
||||
python --version
|
||||
echo ''
|
||||
|
||||
echo '⚙️ 执行 create_src.py...'
|
||||
python create/create_src.py
|
||||
|
||||
echo ''
|
||||
echo '✅ create_src.py 执行完成!'
|
||||
"
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "🎉 所有任务执行完成!"
|
||||
echo "📋 表名: ${TABLE_NAME}"
|
||||
echo "📁 服务目录: ${PROJECT_ROOT}/services/${TABLE_NAME}"
|
||||
```
|
||||
|
||||
|
||||
|
||||
以下是 create_src.py 内容:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
@@ -90,117 +206,51 @@ if __name__ == "__main__":
|
||||
|
||||
|
||||
|
||||
代码二:
|
||||
你的任务是完成 create_src_dockerfile.py 编写,需求如下:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
create.py - 动态生成PostgreSQL建表SQL语句
|
||||
"""
|
||||
1、读取create_src.py中创建的Dockerfile文件。
|
||||
|
||||
import os
|
||||
import re
|
||||
2、清空文件内容后,原封不动地写入以下内容:
|
||||
|
||||
# 1. 从环境变量获取表名,如果没有设置则使用默认值
|
||||
table_name = os.environ.get('TABLE_NAME', 'records')
|
||||
```dockerfile
|
||||
# ==================== 第一阶段:构建Go程序(构建阶段)====================
|
||||
# 使用官方Go镜像作为构建基础,选择与项目匹配的Go版本(示例用1.25.0,可根据实际调整)
|
||||
FROM golang:1.25.0-alpine3.22 AS builder
|
||||
|
||||
# 2. 定义SQL模板
|
||||
sql_template = f"""DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = '{table_name}'
|
||||
) THEN
|
||||
CREATE TABLE {table_name} (
|
||||
id UUID DEFAULT gen_random_uuid() PRIMARY KEY, -- id
|
||||
payload JSONB NOT NULL, -- 数据
|
||||
deleted BOOLEAN NOT NULL DEFAULT FALSE, -- 删除状态
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 记录创建时间
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP -- 记录修改时间
|
||||
);
|
||||
# 设置工作目录(容器内的目录,规范文件位置)
|
||||
WORKDIR /app
|
||||
|
||||
-- 3 触发器:自动刷新 updated_at
|
||||
CREATE TRIGGER trg_{table_name}_at
|
||||
BEFORE UPDATE ON {table_name}
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION moddatetime(updated_at);
|
||||
# 复制go.mod和go.sum(先复制依赖文件,利用Docker缓存机制,避免每次代码变动都重新下载依赖)
|
||||
COPY go.mod go.sum ./
|
||||
|
||||
RAISE NOTICE '{table_name} 表已创建';
|
||||
ELSE
|
||||
RAISE NOTICE '{table_name} 表已存在,跳过';
|
||||
END IF;
|
||||
END $$;"""
|
||||
# 下载项目依赖(仅当go.mod/go.sum变动时才会重新执行)
|
||||
RUN go mod download
|
||||
|
||||
def normalize_blank_lines(text):
|
||||
"""规范化空行:确保END $$;和DO $$之间只有一个空行"""
|
||||
# 将多个空行替换为单个空行
|
||||
text = re.sub(r'\n{3,}', '\n\n', text)
|
||||
# 复制整个项目代码到工作目录
|
||||
COPY . .
|
||||
|
||||
# 确保END $$;后面有一个空行再接DO $$
|
||||
text = re.sub(r'END \$\$;(\s*)DO \$\$', r'END $$;\n\nDO $$', text)
|
||||
# 构建Go程序:
|
||||
# - CGO_ENABLED=0:禁用CGO,生成静态链接的二进制文件(避免依赖系统库,保证镜像兼容性)
|
||||
# - -o app:指定输出二进制文件名为app
|
||||
# - ./main.go:指定入口文件
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -o app ./main.go
|
||||
|
||||
# 清理开头和结尾的多余空行
|
||||
text = text.strip() + '\n'
|
||||
|
||||
return text
|
||||
# ==================== 第二阶段:运行程序(运行阶段)====================
|
||||
# 使用轻量级的scratch(大幅减小最终镜像体积)
|
||||
FROM scratch
|
||||
|
||||
def update_sql_file():
|
||||
"""将生成的SQL语句追加到03_create_table.sql文件中"""
|
||||
# 设置工作目录
|
||||
WORKDIR /app
|
||||
|
||||
# 定义文件路径
|
||||
sql_file_path = os.path.join(
|
||||
os.path.dirname(os.path.dirname(__file__)),
|
||||
'infra',
|
||||
'postgres',
|
||||
'sql',
|
||||
'03_create_table.sql'
|
||||
)
|
||||
# 从构建阶段复制编译好的二进制文件到当前镜像(仅复制最终产物,减小体积)
|
||||
COPY --from=builder /app/app ./
|
||||
|
||||
try:
|
||||
# 读取现有文件内容
|
||||
with open(sql_file_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
# 暴露程序运行端口(与代码中一致)
|
||||
EXPOSE 80
|
||||
|
||||
# 查找插入位置(在\"部署完成\"日志前)
|
||||
insert_pattern = r'(DO \$\$.*?RAISE NOTICE \'🚀============ 数据库表部署开始 ============🚀\'.*?END \$\$;)(.*?)(DO \$\$.*?RAISE NOTICE \'✅============ 数据库表部署完成 ============✅\'.*?END \$\$;)'
|
||||
|
||||
match = re.search(insert_pattern, content, re.DOTALL)
|
||||
|
||||
if match:
|
||||
# 分割内容
|
||||
before_insert = match.group(1)
|
||||
existing_middle = match.group(2)
|
||||
after_insert = match.group(3)
|
||||
|
||||
# 规范化空行
|
||||
existing_middle = normalize_blank_lines(existing_middle)
|
||||
|
||||
# 组合新内容
|
||||
new_content = f"""{before_insert}\n\n{sql_template}\n\n{existing_middle}\n{after_insert}"""
|
||||
|
||||
# 规范化整个内容的空行
|
||||
new_content = normalize_blank_lines(new_content)
|
||||
|
||||
# 写回文件
|
||||
with open(sql_file_path, 'w', encoding='utf-8') as f:
|
||||
f.write(new_content)
|
||||
|
||||
print(f"✅ 成功更新 {sql_file_path}")
|
||||
print(f"📋 生成的表名: {table_name}")
|
||||
print(f"📝 SQL内容已追加到文件中")
|
||||
|
||||
else:
|
||||
print("❌ 无法找到插入位置,请检查文件格式")
|
||||
|
||||
except FileNotFoundError:
|
||||
print(f"❌ 文件 {sql_file_path} 不存在")
|
||||
except Exception as e:
|
||||
print(f"❌ 处理文件时出错: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(f"🚀 开始生成表 '{table_name}' 的SQL语句...")
|
||||
update_sql_file()
|
||||
# 容器启动时执行的命令:运行二进制文件
|
||||
CMD ["./app"]
|
||||
```
|
||||
|
||||
3、重新调整一下 shell 脚本逻辑,执行create_src.py成功后,立马执行 create_src_dockerfile.py 文件。
|
||||
Reference in New Issue
Block a user