diff --git a/create/README.md b/create/README.md index 78f9652..0762f1c 100644 --- a/create/README.md +++ b/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) - - # 确保END $$;后面有一个空行再接DO $$ - text = re.sub(r'END \$\$;(\s*)DO \$\$', r'END $$;\n\nDO $$', text) - - # 清理开头和结尾的多余空行 - text = text.strip() + '\n' - - return text +# 复制整个项目代码到工作目录 +COPY . . -def update_sql_file(): - """将生成的SQL语句追加到03_create_table.sql文件中""" - - # 定义文件路径 - sql_file_path = os.path.join( - os.path.dirname(os.path.dirname(__file__)), - 'infra', - 'postgres', - 'sql', - '03_create_table.sql' - ) - - try: - # 读取现有文件内容 - with open(sql_file_path, 'r', encoding='utf-8') as f: - content = f.read() - - # 查找插入位置(在\"部署完成\"日志前) - 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}") +# 构建Go程序: +# - CGO_ENABLED=0:禁用CGO,生成静态链接的二进制文件(避免依赖系统库,保证镜像兼容性) +# - -o app:指定输出二进制文件名为app +# - ./main.go:指定入口文件 +RUN CGO_ENABLED=0 GOOS=linux go build -o app ./main.go -if __name__ == "__main__": - print(f"🚀 开始生成表 '{table_name}' 的SQL语句...") - update_sql_file() -``` \ No newline at end of file + +# ==================== 第二阶段:运行程序(运行阶段)==================== +# 使用轻量级的scratch(大幅减小最终镜像体积) +FROM scratch + +# 设置工作目录 +WORKDIR /app + +# 从构建阶段复制编译好的二进制文件到当前镜像(仅复制最终产物,减小体积) +COPY --from=builder /app/app ./ + +# 暴露程序运行端口(与代码中一致) +EXPOSE 80 + +# 容器启动时执行的命令:运行二进制文件 +CMD ["./app"] +``` + +3、重新调整一下 shell 脚本逻辑,执行create_src.py成功后,立马执行 create_src_dockerfile.py 文件。 \ No newline at end of file