diff --git a/backend/country/.env b/backend/.env similarity index 100% rename from backend/country/.env rename to backend/.env diff --git a/backend/country/deploy.sh b/backend/country/deploy.sh deleted file mode 100644 index 6dbdbba..0000000 --- a/backend/country/deploy.sh +++ /dev/null @@ -1,86 +0,0 @@ -#!/bin/bash -set -euo pipefail # 更严格的错误检查:未定义变量报错、管道错误传递 - -# 定义日志函数(带时间戳和级别) -log_info() { - echo "[$(date +'%Y-%m-%d %H:%M:%S')] [INFO] $1" -} - -log_warn() { - echo "[$(date +'%Y-%m-%d %H:%M:%S')] [WARN] $1" >&2 -} - -log_error() { - echo "[$(date +'%Y-%m-%d %H:%M:%S')] [ERROR] $1" >&2 -} - -# 定义配置常量(等号两侧无空格!集中管理,便于修改) -IMAGE_NAME="country-api" -IMAGE_TAG="1.0.0" -FULL_IMAGE="${IMAGE_NAME}:${IMAGE_TAG}" -COMPOSE_PROJECT_NAME="country_service" -DOCKER_COMPOSE_FILE="./docker-compose.yaml" -SRC_DIR="./src" -DOCKERFILE_PATH="${SRC_DIR}/Dockerfile" - -# 检查目录和文件存在性的通用函数 -check_exists() { - local path="$1" # 变量引用加引号,避免路径含空格报错 - local type="$2" # "file" 或 "dir" - local desc="$3" - - if [ "$type" = "file" ] && [ ! -f "$path" ]; then - log_error "缺失必要文件: $desc ($path)" - exit 1 - elif [ "$type" = "dir" ] && [ ! -d "$path" ]; then - log_error "缺失必要目录: $desc ($path)" - exit 1 - fi -} - -log_info "===== 开始执行构建脚本 =====" - -# 前置检查:确保必要文件和目录存在 -check_exists "$DOCKER_COMPOSE_FILE" "file" "docker-compose配置文件" -check_exists "$SRC_DIR" "dir" "源代码目录" -check_exists "$DOCKERFILE_PATH" "file" "Dockerfile" - -# 步骤1:停止docker-compose服务(变量引用加引号,兼容路径含空格) -log_info "开始停止编排服务: ${COMPOSE_PROJECT_NAME}" -if docker-compose -f "$DOCKER_COMPOSE_FILE" -p "$COMPOSE_PROJECT_NAME" down; then - log_info "编排服务 ${COMPOSE_PROJECT_NAME} 已成功停止" -else - log_warn "编排服务 ${COMPOSE_PROJECT_NAME} 停止失败或未运行,继续执行后续步骤" -fi - -# 步骤2:删除现有镜像(忽略不存在的情况) -log_info "尝试删除现有镜像: ${FULL_IMAGE}" -if sudo docker rmi -f "${FULL_IMAGE}" >/dev/null 2>&1; then - log_info "镜像 ${FULL_IMAGE} 删除成功" -else - log_warn "镜像 ${FULL_IMAGE} 不存在或无法删除,跳过删除步骤" -fi - -# 步骤3:构建新镜像(切换到src目录,避免路径问题) -log_info "开始构建新镜像: ${FULL_IMAGE}(Dockerfile位于${DOCKERFILE_PATH})" -if cd "$SRC_DIR" && sudo docker build -t "${FULL_IMAGE}" -f Dockerfile .; then - log_info "镜像 ${FULL_IMAGE} 构建成功" -else - log_error "镜像 ${FULL_IMAGE} 构建失败" - exit 1 -fi - -# 步骤4:启动docker-compose服务(变量引用加引号) -log_info "开始启动编排服务: ${COMPOSE_PROJECT_NAME}" -cd .. -if docker-compose -f "$DOCKER_COMPOSE_FILE" -p "$COMPOSE_PROJECT_NAME" up -d; then - log_info "编排服务 ${COMPOSE_PROJECT_NAME} 已成功启动" - # 额外输出运行状态,提升用户体验 - log_info "当前运行的容器:" - docker-compose -f "$DOCKER_COMPOSE_FILE" -p "$COMPOSE_PROJECT_NAME" ps -else - log_error "编排服务 ${COMPOSE_PROJECT_NAME} 启动失败" - exit 1 -fi - -log_info "===== 构建脚本执行完成 =====" diff --git a/backend/country/dev-test.sh b/backend/country/dev-test.sh deleted file mode 100644 index 4bee787..0000000 --- a/backend/country/dev-test.sh +++ /dev/null @@ -1 +0,0 @@ -docker run -itd --name go_country_dev -v $(pwd)/src:/app -p 20010:80 golang:1.25.0-alpine3.22 \ No newline at end of file diff --git a/backend/country/dev.sh b/backend/country/dev.sh deleted file mode 100644 index dde6ad4..0000000 --- a/backend/country/dev.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash - -# 日志函数 -log_info() { - echo "[$(date +'%Y-%m-%d %H:%M:%S')] [DEV_COMPOSE] $1" -} - -log_error() { - echo "[$(date +'%Y-%m-%d %H:%M:%S')] [DEV_ERROR] $1" >&2 -} - -# 获取脚本所在目录的绝对路径 -SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) -# 拼接得到 docker-compose 文件的绝对路径 -COMPOSE_FILE="$SCRIPT_DIR/docker-compose-dev.yaml" - -log_info "开始启动开发环境docker-compose服务" - -# 检查文件是否存在 -if [ ! -f "$COMPOSE_FILE" ]; then - log_error "未找到docker-compose文件: $COMPOSE_FILE" - exit 1 -fi - -# 启动服务 -log_info "执行命令: sudo docker-compose -f $COMPOSE_FILE up -d" -if sudo docker-compose -f "$COMPOSE_FILE" up -d; then - log_info "开发环境服务启动成功" - # 额外输出运行中的容器信息 - log_info "当前运行的容器:" - sudo docker-compose -f "$COMPOSE_FILE" ps -else - log_error "开发环境服务启动失败" - exit 1 -fi \ No newline at end of file diff --git a/backend/country/docker-compose-dev.yaml b/backend/country/docker-compose-dev.yaml deleted file mode 100644 index b50734a..0000000 --- a/backend/country/docker-compose-dev.yaml +++ /dev/null @@ -1,44 +0,0 @@ -services: - postgres: - image: postgres:17.4-alpine - container_name: country_db - restart: always - ports: - - 20011:5432 - entrypoint: - - /scripts/db-lanuch-entrypoint.sh - environment: - POSTGRES_USER: ${DB_USER} - POSTGRES_PASSWORD: ${DB_PASSWORD} - POSTGRES_DB: ${DB_NAME} - TZ: ${TZ} - volumes: - - ./shared_data/country_db:/var/lib/postgresql/data - - ./sql:/docker-entrypoint-initdb.d - - ./scripts:/scripts - networks: - - country-network - country: - image: golang:1.25.0-alpine3.22 - container_name: country_api - restart: always - ports: - - 20010:80 - depends_on: - - postgres - networks: - - country-network - environment: - DB_HOST: postgres - DB_PORT: ${DB_PORT} - DB_USER: ${DB_USER} - DB_PASSWORD: ${DB_PASSWORD} - DB_NAME: ${DB_NAME} - TZ: ${TZ} - volumes: - - ./src:/app - command: sh -c "cd /app && go mod tidy && go run main.go" -networks: - country-network: - driver: bridge -volumes: {} diff --git a/backend/country/docker-compose.yaml b/backend/country/docker-compose.yaml deleted file mode 100644 index 5e5f9e9..0000000 --- a/backend/country/docker-compose.yaml +++ /dev/null @@ -1,44 +0,0 @@ -services: - postgres: - image: postgres:17.4-alpine - container_name: country_db - restart: always - ports: - - 20011:5432 - entrypoint: - - /scripts/db-lanuch-entrypoint.sh - environment: - POSTGRES_USER: ${DB_USER} - POSTGRES_PASSWORD: ${DB_PASSWORD} - POSTGRES_DB: ${DB_NAME} - TZ: ${TZ} - volumes: - - ./shared_data/country_db:/var/lib/postgresql/data - - ./sql:/docker-entrypoint-initdb.d - - ./scripts:/scripts - networks: - - country-network - country: - image: country-api:1.0.0 - container_name: country_api - restart: always - ports: - - 20010:80 - depends_on: - - postgres - networks: - - country-network - environment: - DB_HOST: postgres - DB_PORT: ${DB_PORT} - DB_USER: ${DB_USER} - DB_PASSWORD: ${DB_PASSWORD} - DB_NAME: ${DB_NAME} - TZ: ${TZ} - volumes: - # 挂载添加日志目录挂载,将容器内日志日志目录映射到宿主机的 ./logs 目录 - - ./logs:/app/logs # 假设代码中日志存储路径为 /app/logs -networks: - country-network: - driver: bridge -volumes: {} diff --git a/backend/country/scripts/db-lanuch-entrypoint.sh b/backend/country/scripts/db-lanuch-entrypoint.sh deleted file mode 100755 index a4a8ddb..0000000 --- a/backend/country/scripts/db-lanuch-entrypoint.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/bin/sh -set -e - -# 日志函数(带时间戳) -log_info() { - echo "[$(date +'%Y-%m-%d %H:%M:%S')] [DB_INIT] $1" -} - -log_error() { - echo "[$(date +'%Y-%m-%d %H:%M:%S')] [DB_ERROR] $1" >&2 -} - -# 1. 启动PostgreSQL服务 -log_info "启动PostgreSQL服务(后台运行)" -docker-entrypoint.sh postgres & -PG_PID=$! -log_info "PostgreSQL主进程ID: $PG_PID" - -# 2. 等待数据库就绪 -log_info "等待PostgreSQL服务就绪(主机: localhost, 端口: 5432)" -retry_count=0 -max_retries=30 # 最多等待30秒 -until pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB" -h "localhost" -p "5432"; do - retry_count=$((retry_count + 1)) - if [ $retry_count -ge $max_retries ]; then - log_error "等待PostgreSQL超时(超过30秒)" - exit 1 - fi - log_info "数据库未就绪,等待1秒(重试次数: $retry_count)" - sleep 1 -done -log_info "PostgreSQL服务已就绪" - -# 3. 执行SQL脚本 -log_info "开始执行/docker-entrypoint-initdb.d目录下的SQL脚本" -script_count=0 -for script in /docker-entrypoint-initdb.d/*.sql; do - if [ -f "$script" ]; then - script_count=$((script_count + 1)) - log_info "执行脚本 ($script_count): $script" - if psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -h "localhost" -p "5432" -f "$script" --set=ON_ERROR_STOP=1; then - log_info "脚本执行成功: $script" - else - log_error "脚本执行失败: $script" - exit 1 - fi - fi -done - -if [ $script_count -eq 0 ]; then - log_info "未发现需要执行的SQL脚本" -else - log_info "所有SQL脚本执行完成(共$script_count个)" -fi - -# 4. 等待主进程 -log_info "等待PostgreSQL主进程结束(PID: $PG_PID)" -wait $PG_PID -log_info "PostgreSQL进程已退出" \ No newline at end of file diff --git a/backend/country/sql/01_uuid_v7_setup.sql b/backend/country/sql/01_uuid_v7_setup.sql deleted file mode 100644 index b317d1c..0000000 --- a/backend/country/sql/01_uuid_v7_setup.sql +++ /dev/null @@ -1,48 +0,0 @@ --- 切换到目标数据库 -\c postgres; - --- 检查并创建UUID扩展(如果不存在) -CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; - --- 定义检测UUID v7支持的函数 -CREATE OR REPLACE FUNCTION check_uuid_v7_support() RETURNS BOOLEAN AS $$ -DECLARE - test_uuid UUID; -BEGIN - BEGIN - SELECT gen_random_uuid_v7() INTO test_uuid; - RETURN TRUE; - EXCEPTION - WHEN undefined_function THEN - RETURN FALSE; - END; -END; -$$ LANGUAGE plpgsql VOLATILE; - --- 创建UUID v7兼容函数(修复UUID格式长度问题) -CREATE OR REPLACE FUNCTION gen_random_uuid_v7() RETURNS uuid AS $$ -DECLARE - unix_ts_ms BIGINT; - rand_a BIGINT; - rand_b BIGINT; - hex_str TEXT; -BEGIN - -- 获取当前毫秒级Unix时间戳 - unix_ts_ms := (EXTRACT(EPOCH FROM NOW()) * 1000)::BIGINT; - - -- 生成随机数(调整随机数范围以确保总长度正确) - rand_a := (random() * (2^20 - 1))::BIGINT; - rand_b := (random() * (2^44 - 1))::BIGINT; -- 从48位调整为44位,减少2个字节 - - -- 组合UUID v7格式(确保总长度为32个十六进制字符) - hex_str := - lpad(to_hex(unix_ts_ms >> 12), 8, '0') || - lpad(to_hex((unix_ts_ms & 4095) << 4), 4, '0') || - '7' || lpad(to_hex(rand_a >> 18), 3, '0') || - lpad(to_hex(8 + (rand_a & 16383) >> 12), 2, '0') || - lpad(to_hex(rand_a & 4095), 3, '0') || - lpad(to_hex(rand_b), 11, '0'); -- 从12位调整为11位 - - RETURN hex_str::uuid; -END; -$$ LANGUAGE plpgsql VOLATILE; \ No newline at end of file diff --git a/backend/country/sql/02_create_country_table.sql b/backend/country/sql/02_create_country_table.sql deleted file mode 100644 index a464553..0000000 --- a/backend/country/sql/02_create_country_table.sql +++ /dev/null @@ -1,105 +0,0 @@ --- 切换到目标数据库 -\c postgres; - -CREATE OR REPLACE FUNCTION update_country_modified_column() -RETURNS TRIGGER AS $$ -BEGIN - NEW.updated_at = CURRENT_TIMESTAMP; - RETURN NEW; -END; -$$ LANGUAGE plpgsql VOLATILE; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'country') THEN - CREATE TABLE "country" ( -- country是关键字,用双引号包裹 - id UUID DEFAULT gen_random_uuid_v7() PRIMARY KEY NOT NULL, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP - ); - CREATE TRIGGER update_country_updated_at - BEFORE UPDATE ON "country" - FOR EACH ROW - EXECUTE FUNCTION update_country_modified_column(); - - RAISE NOTICE 'Created country table and trigger'; - ELSE - RAISE NOTICE 'country table already exists'; - END IF; - - IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'name') THEN - CREATE TABLE name ( - id UUID DEFAULT gen_random_uuid_v7() PRIMARY KEY NOT NULL, - country_id UUID NOT NULL, - name VARCHAR NOT NULL, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP - ); - CREATE TRIGGER update_name_updated_at - BEFORE UPDATE ON "name" - FOR EACH ROW - EXECUTE FUNCTION update_name_modified_column(); - - RAISE NOTICE 'Created name table and trigger'; - ELSE - RAISE NOTICE 'name table already exists'; - END IF; - - IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'code') THEN - CREATE TABLE code ( - id UUID DEFAULT gen_random_uuid_v7() PRIMARY KEY NOT NULL, - country_id UUID NOT NULL, - code VARCHAR NOT NULL, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP - ); - CREATE TRIGGER update_code_updated_at - BEFORE UPDATE ON "code" - FOR EACH ROW - EXECUTE FUNCTION update_code_modified_column(); - - RAISE NOTICE 'Created code table and trigger'; - ELSE - RAISE NOTICE 'code table already exists'; - END IF; -END $$; - -DO $$ -DECLARE - view_exists BOOLEAN; -BEGIN - -- 检查视图是否已存在 - SELECT EXISTS ( - SELECT 1 FROM information_schema.views - WHERE table_name = 'country_info_view' - ) INTO view_exists; - - -- 创建或更新视图 - CREATE OR REPLACE VIEW country_info_view AS - SELECT - u.id AS country_id, - n.name AS name, - c.code AS code, - u.deleted AS deleted - FROM - "country" u - JOIN - name n ON u.id = n.country_id - JOIN - code c ON u.id = c.country_id - WHERE - u.deleted = FALSE; - - -- 根据视图是否已存在输出不同提示 - IF view_exists THEN - RAISE NOTICE '视图 country_info_view 已更新'; - ELSE - RAISE NOTICE '视图 country_info_view 已创建'; - END IF; -EXCEPTION - WHEN OTHERS THEN - RAISE NOTICE '处理视图时发生错误: %', SQLERRM; -END $$; diff --git a/backend/country/sql/03_create_name_table.sql b/backend/country/sql/03_create_name_table.sql deleted file mode 100644 index 938798b..0000000 --- a/backend/country/sql/03_create_name_table.sql +++ /dev/null @@ -1,32 +0,0 @@ --- 切换到目标数据库 -\c postgres; - -CREATE OR REPLACE FUNCTION update_name_modified_column() -RETURNS TRIGGER AS $$ -BEGIN - NEW.updated_at = CURRENT_TIMESTAMP; - RETURN NEW; -END; -$$ LANGUAGE plpgsql VOLATILE; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'name') THEN - CREATE TABLE name ( - id UUID DEFAULT gen_random_uuid_v7() PRIMARY KEY NOT NULL, - country_id UUID NOT NULL, - name VARCHAR NOT NULL, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP - ); - CREATE TRIGGER update_name_updated_at - BEFORE UPDATE ON "name" - FOR EACH ROW - EXECUTE FUNCTION update_name_modified_column(); - - RAISE NOTICE 'Created name table and trigger'; - ELSE - RAISE NOTICE 'name table already exists'; - END IF; -END $$; \ No newline at end of file diff --git a/backend/country/sql/04_create_code_table.sql b/backend/country/sql/04_create_code_table.sql deleted file mode 100644 index 08d09a8..0000000 --- a/backend/country/sql/04_create_code_table.sql +++ /dev/null @@ -1,32 +0,0 @@ --- 切换到目标数据库 -\c postgres; - -CREATE OR REPLACE FUNCTION update_code_modified_column() -RETURNS TRIGGER AS $$ -BEGIN - NEW.updated_at = CURRENT_TIMESTAMP; - RETURN NEW; -END; -$$ LANGUAGE plpgsql VOLATILE; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'code') THEN - CREATE TABLE code ( - id UUID DEFAULT gen_random_uuid_v7() PRIMARY KEY NOT NULL, - country_id UUID NOT NULL, - code VARCHAR NOT NULL, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP - ); - CREATE TRIGGER update_code_updated_at - BEFORE UPDATE ON "code" - FOR EACH ROW - EXECUTE FUNCTION update_code_modified_column(); - - RAISE NOTICE 'Created code table and trigger'; - ELSE - RAISE NOTICE 'code table already exists'; - END IF; -END $$; \ No newline at end of file diff --git a/backend/country/sql/05_create_info_view.sql b/backend/country/sql/05_create_info_view.sql deleted file mode 100644 index f26ed74..0000000 --- a/backend/country/sql/05_create_info_view.sql +++ /dev/null @@ -1,38 +0,0 @@ -\c postgres; - -DO $$ -DECLARE - view_exists BOOLEAN; -BEGIN - -- 检查视图是否已存在 - SELECT EXISTS ( - SELECT 1 FROM information_schema.views - WHERE table_name = 'country_info_view' - ) INTO view_exists; - - -- 创建或更新视图 - CREATE OR REPLACE VIEW country_info_view AS - SELECT - u.id AS country_id, - n.name AS name, - c.code AS code, - u.deleted AS deleted - FROM - "country" u - JOIN - name n ON u.id = n.country_id - JOIN - code c ON u.id = c.country_id - WHERE - u.deleted = FALSE; - - -- 根据视图是否已存在输出不同提示 - IF view_exists THEN - RAISE NOTICE '视图 country_info_view 已更新'; - ELSE - RAISE NOTICE '视图 country_info_view 已创建'; - END IF; -EXCEPTION - WHEN OTHERS THEN - RAISE NOTICE '处理视图时发生错误: %', SQLERRM; -END $$; diff --git a/backend/country/src/Dockerfile b/backend/country/src/Dockerfile deleted file mode 100644 index a54892c..0000000 --- a/backend/country/src/Dockerfile +++ /dev/null @@ -1,38 +0,0 @@ -# ==================== 第一阶段:构建Go程序(构建阶段)==================== -# 使用官方Go镜像作为构建基础,选择与项目匹配的Go版本(示例用1.25.0,可根据实际调整) -FROM golang:1.25.0-alpine3.22 AS builder - -# 设置工作目录(容器内的目录,规范文件位置) -WORKDIR /app - -# 复制go.mod和go.sum(先复制依赖文件,利用Docker缓存机制,避免每次代码变动都重新下载依赖) -COPY go.mod go.sum ./ - -# 下载项目依赖(仅当go.mod/go.sum变动时才会重新执行) -RUN go mod download - -# 复制整个项目代码到工作目录 -COPY . . - -# 构建Go程序: -# - CGO_ENABLED=0:禁用CGO,生成静态链接的二进制文件(避免依赖系统库,保证镜像兼容性) -# - -o app:指定输出二进制文件名为app -# - ./main.go:指定入口文件 -RUN CGO_ENABLED=0 GOOS=linux go build -o app ./main.go - - -# ==================== 第二阶段:运行程序(运行阶段)==================== -# 使用轻量级的alpine镜像(仅5MB左右,大幅减小最终镜像体积) -FROM alpine:3.19 - -# 设置工作目录 -WORKDIR /app - -# 从构建阶段复制编译好的二进制文件到当前镜像(仅复制最终产物,减小体积) -COPY --from=builder /app/app ./ - -# 暴露程序运行端口(与代码中一致) -EXPOSE 80 - -# 容器启动时执行的命令:运行二进制文件 -CMD ["./app"] \ No newline at end of file diff --git a/backend/country/src/db/postgres.go b/backend/country/src/db/postgres.go deleted file mode 100644 index 40681a3..0000000 --- a/backend/country/src/db/postgres.go +++ /dev/null @@ -1,54 +0,0 @@ -package db - -import ( - "database/sql" - "fmt" - "os" - "time" - - _ "github.com/lib/pq" - "go.uber.org/zap" -) - -var DB *sql.DB - -// 初始化数据库连接 -func Init() { - // 从环境变量获取数据库配置 - dbHost := os.Getenv("DB_HOST") - dbPort := os.Getenv("DB_PORT") - dbUser := os.Getenv("DB_USER") - dbPassword := os.Getenv("DB_PASSWORD") - dbName := os.Getenv("DB_NAME") - zap.L().Info( - "💡 读取数据库配置", - zap.String("host", dbHost), - zap.String("port", dbPort), - zap.String("user", dbUser), - zap.String("dbname", dbName), - ) - - // 构建数据库连接字符串 - connStr := fmt.Sprintf( - "host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", - dbHost, dbPort, dbUser, dbPassword, dbName, - ) - - var err error - DB, err = sql.Open("postgres", connStr) - if err != nil { - zap.L().Panic("❌ 无法连接数据库", zap.Error(err)) - } - - // 设置连接池参数 - DB.SetMaxOpenConns(100) // 最大打开连接数 - DB.SetMaxIdleConns(20) // 最大空闲连接数 - DB.SetConnMaxLifetime(time.Hour) // 连接最大存活时间 - - // 验证数据库连接 - if err := DB.Ping(); err != nil { - zap.L().Panic("❌ 数据库连接失败", zap.Error(err)) - } - - zap.L().Info("✅ 数据库连接验证成功") -} \ No newline at end of file diff --git a/backend/country/src/go.mod b/backend/country/src/go.mod deleted file mode 100644 index 13d4c8f..0000000 --- a/backend/country/src/go.mod +++ /dev/null @@ -1,57 +0,0 @@ -module country - -go 1.25.0 - -require ( - github.com/gin-contrib/cors v1.7.6 - github.com/gin-gonic/gin v1.11.0 - github.com/google/uuid v1.6.0 - github.com/lib/pq v1.10.9 - github.com/spf13/viper v1.21.0 - go.uber.org/zap v1.27.0 - gopkg.in/natefinch/lumberjack.v2 v2.2.1 -) - -require ( - github.com/bytedance/sonic v1.14.0 // indirect - github.com/bytedance/sonic/loader v0.3.0 // indirect - github.com/cloudwego/base64x v0.1.6 // indirect - github.com/fsnotify/fsnotify v1.9.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.9 // indirect - github.com/gin-contrib/sse v1.1.0 // indirect - github.com/go-playground/locales v0.14.1 // indirect - github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.27.0 // indirect - github.com/go-viper/mapstructure/v2 v2.4.0 // indirect - github.com/goccy/go-json v0.10.5 // indirect - github.com/goccy/go-yaml v1.18.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.3.0 // indirect - github.com/leodido/go-urn v1.4.0 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/quic-go/qpack v0.5.1 // indirect - github.com/quic-go/quic-go v0.54.0 // indirect - github.com/sagikazarmark/locafero v0.11.0 // indirect - github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect - github.com/spf13/afero v1.15.0 // indirect - github.com/spf13/cast v1.10.0 // indirect - github.com/spf13/pflag v1.0.10 // indirect - github.com/subosito/gotenv v1.6.0 // indirect - github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.3.0 // indirect - go.uber.org/mock v0.5.0 // indirect - go.uber.org/multierr v1.10.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/arch v0.20.0 // indirect - golang.org/x/crypto v0.43.0 // indirect - golang.org/x/mod v0.28.0 // indirect - golang.org/x/net v0.45.0 // indirect - golang.org/x/sync v0.17.0 // indirect - golang.org/x/sys v0.37.0 // indirect - golang.org/x/text v0.30.0 // indirect - golang.org/x/tools v0.37.0 // indirect - google.golang.org/protobuf v1.36.9 // indirect -) diff --git a/backend/country/src/go.sum b/backend/country/src/go.sum deleted file mode 100644 index 0117005..0000000 --- a/backend/country/src/go.sum +++ /dev/null @@ -1,133 +0,0 @@ -github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ= -github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA= -github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= -github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= -github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= -github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= -github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= -github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= -github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok= -github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY= -github.com/gin-contrib/cors v1.7.6/go.mod h1:Ulcl+xN4jel9t1Ry8vqph23a60FwH9xVLd+3ykmTjOk= -github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= -github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= -github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= -github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls= -github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= -github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= -github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4= -github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= -github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= -github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= -github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= -github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= -github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= -github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= -github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= -github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= -github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg= -github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= -github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= -github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= -github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= -github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= -github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= -github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= -github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= -github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= -github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= -github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= -github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= -github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= -github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= -go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= -go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= -go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c= -golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= -golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= -golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= -golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= -golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= -golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= -golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= -golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= -golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= -google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= -google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= -gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/backend/country/src/logger/logger.go b/backend/country/src/logger/logger.go deleted file mode 100644 index 8021baa..0000000 --- a/backend/country/src/logger/logger.go +++ /dev/null @@ -1,86 +0,0 @@ -package logger - -import ( - "log" - "os" - "time" - - "github.com/spf13/viper" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" - "gopkg.in/natefinch/lumberjack.v2" -) - -var shanghaiLoc *time.Location - -func init() { - var err error - shanghaiLoc, err = time.LoadLocation("Asia/Shanghai") - if err != nil { - // 尝试备选时区名称 - shanghaiLoc, err = time.LoadLocation("PRC") - if err != nil { - // 若仍失败,手动设置东八区偏移 - shanghaiLoc = time.FixedZone("CST", 8*3600) - log.Printf("警告:加载时区失败,使用手动东八区偏移: %v", err) - } - } -} - -// Init 初始化日志(依赖配置文件已加载) -func Init() { - // 日志级别转换 - level := zap.InfoLevel - switch viper.GetString("logger.level") { - case "debug": - level = zap.DebugLevel - case "warn": - level = zap.WarnLevel - case "error": - level = zap.ErrorLevel - } - - // 日志轮转配置(lumberjack) - hook := lumberjack.Logger{ - Filename: viper.GetString("logger.path") + "logs/app.log", // 日志文件路径 - MaxSize: viper.GetInt("logger.max_size"), // 单个文件最大大小(MB) - MaxBackups: viper.GetInt("logger.max_backup"), // 最大备份数 - MaxAge: viper.GetInt("logger.max_age"), // 最大保留天数 - Compress: true, // 是否压缩 - } - - // 编码器配置 - encoderConfig := zapcore.EncoderConfig{ - TimeKey: "time", - LevelKey: "level", - NameKey: "logger", - CallerKey: "caller", - MessageKey: "msg", - StacktraceKey: "stacktrace", - LineEnding: zapcore.DefaultLineEnding, - EncodeLevel: zapcore.CapitalLevelEncoder, // 日志级别大写(DEBUG/INFO) - EncodeTime: customTimeEncoder, // 自定义时间格式 - EncodeDuration: zapcore.SecondsDurationEncoder, - EncodeCaller: zapcore.ShortCallerEncoder, // 精简调用者路径 - } - - // 输出配置(控制台+文件) - core := zapcore.NewTee( - zapcore.NewCore(zapcore.NewConsoleEncoder(encoderConfig), zapcore.AddSync(os.Stdout), level), - zapcore.NewCore(zapcore.NewJSONEncoder(encoderConfig), zapcore.AddSync(&hook), level), - ) - - // 创建logger实例(开启调用者信息和堆栈跟踪) - logger := zap.New(core, zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel)) - zap.ReplaceGlobals(logger) - - zap.L().Info("✅ 日志初始化成功", zap.String("level", level.String())) -} - -// customTimeEncoder 自定义时间格式(强制东八区,若加载失败则使用UTC) -func customTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { - // 使用提前初始化好的时区,避免每次调用都加载 - beijingTime := t.In(shanghaiLoc) - // 格式化输出 - enc.AppendString(beijingTime.Format("2006-01-02 15:04:05.000")) -} \ No newline at end of file diff --git a/backend/country/src/logic/create.go b/backend/country/src/logic/create.go deleted file mode 100644 index a156519..0000000 --- a/backend/country/src/logic/create.go +++ /dev/null @@ -1,193 +0,0 @@ -package logic - -import ( - "net/http" - "country/db" // 数据库操作相关包 - "time" // 时间处理包 - "github.com/google/uuid" // UUID生成工具 - "github.com/gin-gonic/gin" // Gin框架,用于处理HTTP请求 - "go.uber.org/zap" // 日志库 -) - -// CreateRequest 注册请求参数结构 -// 用于接收客户端发送的JSON数据,绑定并验证必填字段 -type CreateRequest struct { - Name string `json:"name" binding:"required"` // 国家名称,必填 - Code string `json:"code" binding:"required"` // 国家代码,必填 -} - -// CreateResponse 注册响应结构 -// 统一的API响应格式,包含成功状态、提示信息和数据 -type CreateResponse struct { - Success bool `json:"success"` // 操作是否成功 - Message string `json:"message"` // 提示信息 - Data CreateData `json:"data"` // 响应数据 -} - -// CreateData 响应数据结构 -// 包含创建成功后的国家ID -type CreateData struct { - CountryID string `json:"country_id"` // 国家唯一标识ID -} - -// CreateHandler 处理国家创建逻辑 -// 接收HTTP请求,完成参数验证、数据库事务处理并返回响应 -func CreateHandler(c *gin.Context) { - startTime := time.Now() // 记录请求开始时间,用于统计耗时 - // 获取或生成请求ID,用于追踪整个请求链路 - reqID := c.Request.Header.Get("X-RegisterRequest-ID") - if reqID == "" { - reqID = uuid.New().String() - zap.L().Debug("✨ 生成新的请求ID", zap.String("req_id", reqID)) - } - - // 记录请求接收日志,包含关键追踪信息 - zap.L().Info("📥 收到国家创建请求", - zap.String("req_id", reqID), - zap.String("path", c.Request.URL.Path), - zap.String("method", c.Request.Method), - ) - - var req CreateRequest - // 绑定并验证请求参数(检查name和code是否存在) - if err := c.ShouldBindJSON(&req); err != nil { - zap.L().Warn("⚠️ 请求参数验证失败", - zap.String("req_id", reqID), - zap.Error(err), - zap.Any("request_body", c.Request.Body), - ) - // 返回参数错误响应 - c.JSON(http.StatusBadRequest, CreateResponse{ - Success: false, - Message: "请求参数错误:name和code为必填项", - }) - return - } - - // 记录通过验证的请求参数 - zap.L().Debug("✅ 请求参数验证通过", - zap.String("req_id", reqID), - zap.String("name", req.Name), - zap.String("code", req.Code), - ) - - // 开启数据库事务,确保多表操作原子性(要么全成功,要么全失败) - tx, err := db.DB.Begin() - if err != nil { - zap.L().Error("❌ 事务开启失败", - zap.String("req_id", reqID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, CreateResponse{ - Success: false, - Message: "系统错误,请稍后重试", - }) - return - } - // 延迟执行的恢复函数,处理panic情况 - defer func() { - if r := recover(); r != nil { // 捕获panic - // 回滚事务 - if err := tx.Rollback(); err != nil { - zap.L().Error("💥 panic后事务回滚失败", - zap.String("req_id", reqID), - zap.Error(err), - ) - } - zap.L().Error("💥 事务处理发生panic", - zap.String("req_id", reqID), - zap.Any("recover", r), - ) - // 返回系统错误响应 - c.JSON(http.StatusInternalServerError, CreateResponse{ - Success: false, - Message: "系统错误,请稍后重试", - }) - } - }() - - // 1. 在country表中创建记录并获取自动生成的ID - var countryID string - err = tx.QueryRow("INSERT INTO country DEFAULT VALUES RETURNING id").Scan(&countryID) - if err != nil { - tx.Rollback() // 操作失败,回滚事务 - zap.L().Error("❌ country表插入失败", - zap.String("req_id", reqID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, CreateResponse{ - Success: false, - Message: "创建国家记录失败", - }) - return - } - - zap.L().Debug("📝 country表插入成功", - zap.String("req_id", reqID), - zap.String("country_id", countryID), - ) - - // 2. 插入国家名称到name表(与country_id关联) - _, err = tx.Exec("INSERT INTO name (country_id, name) VALUES ($1, $2)", countryID, req.Name) - if err != nil { - tx.Rollback() // 操作失败,回滚事务 - zap.L().Error("❌ name表插入失败", - zap.String("req_id", reqID), - zap.String("country_id", countryID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, CreateResponse{ - Success: false, - Message: "保存名称信息失败", - }) - return - } - - // 3. 插入国家代码到code表(与country_id关联) - _, err = tx.Exec("INSERT INTO code (country_id, code) VALUES ($1, $2)", countryID, req.Code) - if err != nil { - tx.Rollback() // 操作失败,回滚事务 - zap.L().Error("❌ code表插入失败", - zap.String("req_id", reqID), - zap.String("country_id", countryID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, CreateResponse{ - Success: false, - Message: "保存代码信息失败", - }) - return - } - - // 提交事务(所有操作成功后确认提交) - if err := tx.Commit(); err != nil { - tx.Rollback() // 提交失败时尝试回滚 - zap.L().Error("❌ 事务提交失败", - zap.String("req_id", reqID), - zap.String("country_id", countryID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, CreateResponse{ - Success: false, - Message: "数据提交失败,请稍后重试", - }) - return - } - - // 记录请求处理耗时 - duration := time.Since(startTime) - zap.L().Info("✅ 国家创建请求处理完成", - zap.String("req_id", reqID), - zap.String("country_id", countryID), - zap.Duration("duration", duration), - ) - - // 返回成功响应,包含创建的国家ID - c.JSON(http.StatusOK, CreateResponse{ - Success: true, - Message: "创建成功", - Data: CreateData{ - CountryID: countryID, - }, - }) -} \ No newline at end of file diff --git a/backend/country/src/logic/delete.go b/backend/country/src/logic/delete.go deleted file mode 100644 index 1ee98c8..0000000 --- a/backend/country/src/logic/delete.go +++ /dev/null @@ -1,167 +0,0 @@ -package logic - -import ( - "net/http" - "country/db" - "time" - "github.com/google/uuid" - "github.com/gin-gonic/gin" - "go.uber.org/zap" -) - -// DeleteRequest 删除请求参数结构 -type DeleteRequest struct { - CountryID string `json:"country_id" binding:"required"` // 国家ID,必填 -} - -// DeleteResponse 删除响应结构 -type DeleteResponse struct { - Success bool `json:"success"` // 操作是否成功 - Message string `json:"message"` // 提示信息 -} - -// DeleteHandler 处理国家删除逻辑(软删除) -func DeleteHandler(c *gin.Context) { - startTime := time.Now() - reqID := c.Request.Header.Get("X-DeleteRequest-ID") - if reqID == "" { - reqID = uuid.New().String() - zap.L().Debug("✨ 生成新的请求ID", zap.String("req_id", reqID)) - } - - zap.L().Info("📥 收到国家删除请求", - zap.String("req_id", reqID), - zap.String("path", c.Request.URL.Path), - zap.String("method", c.Request.Method), - ) - - var req DeleteRequest - // 绑定并验证请求参数 - if err := c.ShouldBindJSON(&req); err != nil { - zap.L().Warn("⚠️ 请求参数验证失败", - zap.String("req_id", reqID), - zap.Error(err), - ) - c.JSON(http.StatusBadRequest, DeleteResponse{ - Success: false, - Message: "请求参数错误:country_id为必填项", - }) - return - } - - zap.L().Debug("✅ 请求参数验证通过", - zap.String("req_id", reqID), - zap.String("country_id", req.CountryID), - ) - - // 开启数据库事务 - tx, err := db.DB.Begin() - if err != nil { - zap.L().Error("❌ 事务开启失败", - zap.String("req_id", reqID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, DeleteResponse{ - Success: false, - Message: "系统错误,请稍后重试", - }) - return - } - - // 延迟处理panic情况 - defer func() { - if r := recover(); r != nil { - if err := tx.Rollback(); err != nil { - zap.L().Error("💥 panic后事务回滚失败", - zap.String("req_id", reqID), - zap.Error(err), - ) - } - zap.L().Error("💥 事务处理发生panic", - zap.String("req_id", reqID), - zap.Any("recover", r), - ) - c.JSON(http.StatusInternalServerError, DeleteResponse{ - Success: false, - Message: "系统错误,请稍后重试", - }) - } - }() - - // 3.1 更新country表 - _, err = tx.Exec("UPDATE country SET deleted = TRUE WHERE id = $1", req.CountryID) - if err != nil { - tx.Rollback() - zap.L().Error("❌ country表更新失败", - zap.String("req_id", reqID), - zap.String("country_id", req.CountryID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, DeleteResponse{ - Success: false, - Message: "删除国家记录失败", - }) - return - } - - // 3.2 更新name表 - _, err = tx.Exec("UPDATE name SET deleted = TRUE WHERE country_id = $1", req.CountryID) - if err != nil { - tx.Rollback() - zap.L().Error("❌ name表更新失败", - zap.String("req_id", reqID), - zap.String("country_id", req.CountryID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, DeleteResponse{ - Success: false, - Message: "删除名称信息失败", - }) - return - } - - // 3.3 更新code表 - _, err = tx.Exec("UPDATE code SET deleted = TRUE WHERE country_id = $1", req.CountryID) - if err != nil { - tx.Rollback() - zap.L().Error("❌ code表更新失败", - zap.String("req_id", reqID), - zap.String("country_id", req.CountryID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, DeleteResponse{ - Success: false, - Message: "删除代码信息失败", - }) - return - } - - // 提交事务 - if err := tx.Commit(); err != nil { - tx.Rollback() - zap.L().Error("❌ 事务提交失败", - zap.String("req_id", reqID), - zap.String("country_id", req.CountryID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, DeleteResponse{ - Success: false, - Message: "数据提交失败,请稍后重试", - }) - return - } - - // 记录请求处理耗时 - duration := time.Since(startTime) - zap.L().Info("✅ 国家删除请求处理完成", - zap.String("req_id", reqID), - zap.String("country_id", req.CountryID), - zap.Duration("duration", duration), - ) - - // 返回成功响应 - c.JSON(http.StatusOK, DeleteResponse{ - Success: true, - Message: "删除成功", - }) -} \ No newline at end of file diff --git a/backend/country/src/logic/read.go b/backend/country/src/logic/read.go deleted file mode 100644 index 84d3b88..0000000 --- a/backend/country/src/logic/read.go +++ /dev/null @@ -1,230 +0,0 @@ -package logic - -import ( - "country/db" - "net/http" - "strconv" - "time" - "strings" - - "fmt" - "github.com/gin-gonic/gin" - "github.com/google/uuid" - "go.uber.org/zap" -) - -// ReadRequest 读取请求参数结构 -type ReadRequest struct { - CountryID string `form:"country_id"` // 国家ID,可选 - Name string `form:"name"` // 国家名称,可选 - Code string `form:"code"` // 国家代码,可选 - Page string `form:"page"` // 页码,可选 - PageSize string `form:"page_size"` // 每页条数,可选 -} - -// ReadData 读取响应数据结构 -type ReadData struct { - Total int64 `json:"total"` // 总条数 - Page int `json:"page"` // 当前页码 - PageSize int `json:"page_size"`// 每页条数 - Items []CountryInfoViewItem `json:"items"` // 数据列表 -} - -// CountryInfoViewItem 视图数据项结构 -type CountryInfoViewItem struct { - CountryID string `json:"country_id"` // 国家ID - Name string `json:"name"` // 国家名称 - Code string `json:"code"` // 国家代码 -} - -// ReadResponse 读取响应结构 -type ReadResponse struct { - Success bool `json:"success"` // 操作是否成功 - Message string `json:"message"` // 提示信息 - Data ReadData `json:"data"` // 响应数据 -} - -// ReadHandler 处理国家信息查询逻辑 -func ReadHandler(c *gin.Context) { - startTime := time.Now() - // 获取或生成请求ID - reqID := c.Request.Header.Get("X-ReadRequest-ID") - if reqID == "" { - reqID = uuid.New().String() - zap.L().Debug("✨ 生成新的请求ID", zap.String("req_id", reqID)) - } - - // 记录请求接收日志 - zap.L().Info("📥 收到国家查询请求", - zap.String("req_id", reqID), - zap.String("path", c.Request.URL.Path), - zap.String("method", c.Request.Method), - ) - - // 绑定请求参数 - var req ReadRequest - if err := c.ShouldBindQuery(&req); err != nil { - zap.L().Warn("⚠️ 请求参数解析失败", - zap.String("req_id", reqID), - zap.Error(err), - ) - c.JSON(http.StatusBadRequest, ReadResponse{ - Success: false, - Message: "请求参数格式错误", - }) - return - } - - // 验证查询条件至少有一个不为空 - if req.CountryID == "" && req.Name == "" && req.Code == "" { - zap.L().Warn("⚠️ 请求参数验证失败", - zap.String("req_id", reqID), - zap.String("reason", "country_id、name、code不能同时为空"), - ) - c.JSON(http.StatusBadRequest, ReadResponse{ - Success: false, - Message: "请求参数错误:country_id、name、code不能同时为空", - }) - return - } - - // 处理分页参数默认值 - page, err := strconv.Atoi(req.Page) - if err != nil || page < 1 { - page = 1 - } - pageSize, err := strconv.Atoi(req.PageSize) - if err != nil || pageSize < 1 { - pageSize = 20 - } - - zap.L().Debug("✅ 请求参数验证通过", - zap.String("req_id", reqID), - zap.String("country_id", req.CountryID), - zap.String("name", req.Name), - zap.String("code", req.Code), - zap.Int("page", page), - zap.Int("page_size", pageSize), - ) - - // 构建查询条件和参数 - whereClauses := []string{} - args := []interface{}{} - paramIndex := 1 - - if req.CountryID != "" { - whereClauses = append(whereClauses, "country_id = $"+strconv.Itoa(paramIndex)) - args = append(args, req.CountryID) - paramIndex++ - } - if req.Name != "" { - whereClauses = append(whereClauses, "name LIKE $"+strconv.Itoa(paramIndex)) - args = append(args, "%"+req.Name+"%") - paramIndex++ - } - if req.Code != "" { - whereClauses = append(whereClauses, "code LIKE $"+strconv.Itoa(paramIndex)) - args = append(args, "%"+req.Code+"%") - paramIndex++ - } - - // 构建基础SQL - baseSQL := "SELECT country_id, name, code FROM country_info_view" - countSQL := "SELECT COUNT(*) FROM country_info_view" - if len(whereClauses) > 0 { - whereStr := " WHERE " + strings.Join(whereClauses, " AND ") - baseSQL += whereStr - countSQL += whereStr - } - - // 计算分页偏移量 - offset := (page - 1) * pageSize - - // 拼接分页SQL(使用fmt.Sprintf更清晰) - querySQL := fmt.Sprintf("%s ORDER BY country_id LIMIT $%d OFFSET $%d", baseSQL, paramIndex, paramIndex+1) - args = append(args, pageSize, offset) - - // 查询总条数(修正参数传递方式) - var total int64 - countArgs := args[:len(args)-2] // 排除分页参数 - err = db.DB.QueryRow(countSQL, countArgs...).Scan(&total) - if err != nil { - zap.L().Error("❌ 查询总条数失败", - zap.String("req_id", reqID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, ReadResponse{ - Success: false, - Message: "查询数据失败,请稍后重试", - }) - return - } - - // 执行分页查询 - rows, err := db.DB.Query(querySQL, args...) - if err != nil { - zap.L().Error("❌ 分页查询失败", - zap.String("req_id", reqID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, ReadResponse{ - Success: false, - Message: "查询数据失败,请稍后重试", - }) - return - } - defer rows.Close() - - // 处理查询结果 - var items []CountryInfoViewItem - for rows.Next() { - var item CountryInfoViewItem - if err := rows.Scan(&item.CountryID, &item.Name, &item.Code); err != nil { - zap.L().Error("❌ 解析查询结果失败", - zap.String("req_id", reqID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, ReadResponse{ - Success: false, - Message: "数据处理失败,请稍后重试", - }) - return - } - items = append(items, item) - } - - // 检查行迭代过程中是否发生错误 - if err := rows.Err(); err != nil { - zap.L().Error("❌ 行迭代错误", - zap.String("req_id", reqID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, ReadResponse{ - Success: false, - Message: "查询数据失败,请稍后重试", - }) - return - } - - // 记录请求处理耗时 - duration := time.Since(startTime) - zap.L().Info("✅ 国家查询请求处理完成", - zap.String("req_id", reqID), - zap.Int64("total", total), - zap.Int("page", page), - zap.Int("page_size", pageSize), - zap.Duration("duration", duration), - ) - - // 返回成功响应 - c.JSON(http.StatusOK, ReadResponse{ - Success: true, - Message: "查询成功", - Data: ReadData{ - Total: total, - Page: page, - PageSize: pageSize, - Items: items, - }, - }) -} \ No newline at end of file diff --git a/backend/country/src/logic/update.go b/backend/country/src/logic/update.go deleted file mode 100644 index 51dc9d7..0000000 --- a/backend/country/src/logic/update.go +++ /dev/null @@ -1,184 +0,0 @@ -package logic - -import ( - "country/db" - "net/http" - "time" - - "github.com/gin-gonic/gin" - "github.com/google/uuid" - "go.uber.org/zap" -) - -// UpdateRequest 更新请求参数结构 -type UpdateRequest struct { - CountryID string `json:"country_id" binding:"required"` // 国家ID,必填 - Name string `json:"name"` // 国家名称,可选 - Code string `json:"code"` // 国家代码,可选 -} - -// UpdateResponse 更新响应结构 -type UpdateResponse struct { - Success bool `json:"success"` // 操作是否成功 - Message string `json:"message"` // 提示信息 -} - -// UpdateHandler 处理国家信息更新逻辑 -func UpdateHandler(c *gin.Context) { - startTime := time.Now() - // 获取或生成请求ID - reqID := c.Request.Header.Get("X-UpdateRequest-ID") - if reqID == "" { - reqID = uuid.New().String() - zap.L().Debug("✨ 生成新的请求ID", zap.String("req_id", reqID)) - } - - // 记录请求接收日志 - zap.L().Info("📥 收到国家更新请求", - zap.String("req_id", reqID), - zap.String("path", c.Request.URL.Path), - zap.String("method", c.Request.Method), - ) - - var req UpdateRequest - // 绑定并验证请求参数(主要验证country_id必填) - if err := c.ShouldBindJSON(&req); err != nil { - zap.L().Warn("⚠️ 请求参数验证失败", - zap.String("req_id", reqID), - zap.Error(err), - ) - c.JSON(http.StatusBadRequest, UpdateResponse{ - Success: false, - Message: "请求参数错误:country_id为必填项", - }) - return - } - - // 验证name和code不能同时为空 - if req.Name == "" && req.Code == "" { - zap.L().Warn("⚠️ 请求参数验证失败", - zap.String("req_id", reqID), - zap.String("country_id", req.CountryID), - zap.String("reason", "name和code不能同时为空"), - ) - c.JSON(http.StatusBadRequest, UpdateResponse{ - Success: false, - Message: "请求参数错误:name和code不能同时为空", - }) - return - } - - zap.L().Debug("✅ 请求参数验证通过", - zap.String("req_id", reqID), - zap.String("country_id", req.CountryID), - zap.String("name", req.Name), - zap.String("code", req.Code), - ) - - // 开启数据库事务 - tx, err := db.DB.Begin() - if err != nil { - zap.L().Error("❌ 事务开启失败", - zap.String("req_id", reqID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, UpdateResponse{ - Success: false, - Message: "系统错误,请稍后重试", - }) - return - } - - // 延迟处理panic情况 - defer func() { - if r := recover(); r != nil { - if err := tx.Rollback(); err != nil { - zap.L().Error("💥 panic后事务回滚失败", - zap.String("req_id", reqID), - zap.Error(err), - ) - } - zap.L().Error("💥 事务处理发生panic", - zap.String("req_id", reqID), - zap.Any("recover", r), - ) - c.JSON(http.StatusInternalServerError, UpdateResponse{ - Success: false, - Message: "系统错误,请稍后重试", - }) - } - }() - - // 如果name不为空,更新name表 - if req.Name != "" { - _, err = tx.Exec("UPDATE name SET name = $1 WHERE country_id = $2", req.Name, req.CountryID) - if err != nil { - tx.Rollback() - zap.L().Error("❌ name表更新失败", - zap.String("req_id", reqID), - zap.String("country_id", req.CountryID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, UpdateResponse{ - Success: false, - Message: "更新名称信息失败", - }) - return - } - zap.L().Debug("📝 name表更新成功", - zap.String("req_id", reqID), - zap.String("country_id", req.CountryID), - ) - } - - // 如果code不为空,更新code表 - if req.Code != "" { - _, err = tx.Exec("UPDATE code SET code = $1 WHERE country_id = $2", req.Code, req.CountryID) - if err != nil { - tx.Rollback() - zap.L().Error("❌ code表更新失败", - zap.String("req_id", reqID), - zap.String("country_id", req.CountryID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, UpdateResponse{ - Success: false, - Message: "更新代码信息失败", - }) - return - } - zap.L().Debug("📝 code表更新成功", - zap.String("req_id", reqID), - zap.String("country_id", req.CountryID), - ) - } - - // 提交事务 - if err := tx.Commit(); err != nil { - tx.Rollback() - zap.L().Error("❌ 事务提交失败", - zap.String("req_id", reqID), - zap.String("country_id", req.CountryID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, UpdateResponse{ - Success: false, - Message: "数据提交失败,请稍后重试", - }) - return - } - - // 记录请求处理耗时 - duration := time.Since(startTime) - zap.L().Info("✅ 国家更新请求处理完成", - zap.String("req_id", reqID), - zap.String("country_id", req.CountryID), - zap.Duration("duration", duration), - ) - - // 返回成功响应 - c.JSON(http.StatusOK, UpdateResponse{ - Success: true, - Message: "更新成功", - }) -} \ No newline at end of file diff --git a/backend/country/src/main.go b/backend/country/src/main.go deleted file mode 100644 index 1dc053e..0000000 --- a/backend/country/src/main.go +++ /dev/null @@ -1,72 +0,0 @@ -package main - -import ( - "country/db" // 数据库相关操作包 - "country/logger" // 日志工具包 - "country/logic" // 业务逻辑处理包 - - "time" - "github.com/gin-contrib/cors" - "github.com/gin-gonic/gin" // Gin框架,用于构建HTTP服务 - _ "github.com/lib/pq" // PostgreSQL数据库驱动(下划线表示仅初始化不直接使用) - "go.uber.org/zap" // Zap日志库,用于结构化日志输出 -) - -// main函数是程序的入口点 -func main() { - // 初始化日志配置 - logger.Init() - // 记录服务初始化日志 - zap.L().Info("🚀 用户服务初始化") - - // 记录数据库初始化开始日志 - zap.L().Info("⌛️ 数据库初始化开始") - // 初始化数据库连接 - db.Init() - // 程序退出时关闭数据库连接(defer确保在函数退出前执行) - defer db.DB.Close() - // 记录数据库初始化成功日志 - zap.L().Info("✅ 数据库初始化成功") - - // 设置Gin框架为发布模式(关闭调试信息) - gin.SetMode(gin.ReleaseMode) - // 创建Gin默认路由器 - r := gin.Default() - - // 配置跨域中间件 - r.Use(cors.New(cors.Config{ - // 允许所有来源(生产环境建议指定具体域名) - AllowOrigins: []string{"*"}, - // 允许的请求方法 - AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"}, - // 允许的请求头 - AllowHeaders: []string{"Origin", "Content-Type", "Content-Length", "Accept-Encoding", "X-CSRF-Token", "Authorization", "X-LoginRequest-ID"}, - // 允许前端读取的响应头 - ExposeHeaders: []string{"Content-Length"}, - // 是否允许携带cookie - AllowCredentials: true, - // 预检请求的缓存时间 - MaxAge: 12 * time.Hour, - })) - zap.L().Info("✅ 配置跨域中间件完成") - - // 注册创建国家的接口,POST请求,由logic.CreateHandler处理 - r.POST("/country/create", logic.CreateHandler) - zap.L().Info("✅ 创建接口注册完成: POST /country/create") - - // 注册读取国家的接口,POST请求,由logic.ReadHandler - r.POST("/country/read", logic.ReadHandler) - zap.L().Info("✅ 读取接口注册完成: POST /country/read") - - // 注册更新国家的接口,POST请求,由logic.UpdateHandler - r.POST("/country/update", logic.UpdateHandler) - zap.L().Info("✅ 更新接口注册完成: POST /country/update") - - // 注册删除国家的接口,POST请求,由logic.DeleteHandler处理 - r.POST("/country/delete", logic.DeleteHandler) - zap.L().Info("✅ 删除接口注册完成: POST /country/delete") - - // 记录服务启动日志,监听80端口 - zap.L().Info("✅ 服务启动在80端口") - r.Run(":80") -} \ No newline at end of file diff --git a/backend/asset_assistant/deploy.sh b/backend/deploy.sh similarity index 100% rename from backend/asset_assistant/deploy.sh rename to backend/deploy.sh diff --git a/backend/asset_assistant/dev-test.sh b/backend/dev-test.sh similarity index 100% rename from backend/asset_assistant/dev-test.sh rename to backend/dev-test.sh diff --git a/backend/asset_assistant/dev.sh b/backend/dev.sh similarity index 100% rename from backend/asset_assistant/dev.sh rename to backend/dev.sh diff --git a/backend/asset_assistant/docker-compose-dev.yaml b/backend/docker-compose-dev.yaml similarity index 100% rename from backend/asset_assistant/docker-compose-dev.yaml rename to backend/docker-compose-dev.yaml diff --git a/backend/asset_assistant/docker-compose.yaml b/backend/docker-compose.yaml similarity index 100% rename from backend/asset_assistant/docker-compose.yaml rename to backend/docker-compose.yaml diff --git a/backend/futures_trade_record/.env b/backend/futures_trade_record/.env deleted file mode 100644 index a8b6a3f..0000000 --- a/backend/futures_trade_record/.env +++ /dev/null @@ -1,21 +0,0 @@ -# 数据库配置 -DB_USER=postgres -DB_PASSWORD=postgres12341234 -DB_NAME=postgres -DB_PORT=5432 -DB_SSL_MODE=disable -DB_MAX_OPEN_CONNS=25 -DB_MAX_IDLE_CONNS=25 -DB_TIMEOUT=30s - -# 时区配置 -TZ=Asia/Shanghai - -# 网关端口 -PORT=80 - -# 日志配置 -LOG_LEVEL=info - -# Gin模式 (debug/release/test) -GIN_MODE=debug \ No newline at end of file diff --git a/backend/futures_trade_record/README.md b/backend/futures_trade_record/README.md deleted file mode 100644 index a07b115..0000000 --- a/backend/futures_trade_record/README.md +++ /dev/null @@ -1,16 +0,0 @@ -curl -X POST http://localhost:20010/variety/create \ - -H "Content-Type: application/json" \ - -d '{ - "name": "螺纹钢", - "code": "RB", - "tick": 1.0, - "tick_price": 10.0 - }' - - - curl -X POST http://localhost:20010/exchange/create \ - -H "Content-Type: application/json" \ - -d '{ - "name": "1234", - "code": "ads" - }' \ No newline at end of file diff --git a/backend/futures_trade_record/deploy.sh b/backend/futures_trade_record/deploy.sh deleted file mode 100644 index 40acb2a..0000000 --- a/backend/futures_trade_record/deploy.sh +++ /dev/null @@ -1,86 +0,0 @@ -#!/bin/bash -set -euo pipefail # 更严格的错误检查:未定义变量报错、管道错误传递 - -# 定义日志函数(带时间戳和级别) -log_info() { - echo "[$(date +'%Y-%m-%d %H:%M:%S')] [INFO] $1" -} - -log_warn() { - echo "[$(date +'%Y-%m-%d %H:%M:%S')] [WARN] $1" >&2 -} - -log_error() { - echo "[$(date +'%Y-%m-%d %H:%M:%S')] [ERROR] $1" >&2 -} - -# 定义配置常量(等号两侧无空格!集中管理,便于修改) -IMAGE_NAME="futures-trade-record-api" -IMAGE_TAG="1.0.0" -FULL_IMAGE="${IMAGE_NAME}:${IMAGE_TAG}" -COMPOSE_PROJECT_NAME="futures_trade_record_service" -DOCKER_COMPOSE_FILE="./docker-compose.yaml" -SRC_DIR="./src" -DOCKERFILE_PATH="${SRC_DIR}/Dockerfile" - -# 检查目录和文件存在性的通用函数 -check_exists() { - local path="$1" # 变量引用加引号,避免路径含空格报错 - local type="$2" # "file" 或 "dir" - local desc="$3" - - if [ "$type" = "file" ] && [ ! -f "$path" ]; then - log_error "缺失必要文件: $desc ($path)" - exit 1 - elif [ "$type" = "dir" ] && [ ! -d "$path" ]; then - log_error "缺失必要目录: $desc ($path)" - exit 1 - fi -} - -log_info "===== 开始执行构建脚本 =====" - -# 前置检查:确保必要文件和目录存在 -check_exists "$DOCKER_COMPOSE_FILE" "file" "docker-compose配置文件" -check_exists "$SRC_DIR" "dir" "源代码目录" -check_exists "$DOCKERFILE_PATH" "file" "Dockerfile" - -# 步骤1:停止docker-compose服务(变量引用加引号,兼容路径含空格) -log_info "开始停止编排服务: ${COMPOSE_PROJECT_NAME}" -if docker-compose -f "$DOCKER_COMPOSE_FILE" -p "$COMPOSE_PROJECT_NAME" down; then - log_info "编排服务 ${COMPOSE_PROJECT_NAME} 已成功停止" -else - log_warn "编排服务 ${COMPOSE_PROJECT_NAME} 停止失败或未运行,继续执行后续步骤" -fi - -# 步骤2:删除现有镜像(忽略不存在的情况) -log_info "尝试删除现有镜像: ${FULL_IMAGE}" -if sudo docker rmi -f "${FULL_IMAGE}" >/dev/null 2>&1; then - log_info "镜像 ${FULL_IMAGE} 删除成功" -else - log_warn "镜像 ${FULL_IMAGE} 不存在或无法删除,跳过删除步骤" -fi - -# 步骤3:构建新镜像(切换到src目录,避免路径问题) -log_info "开始构建新镜像: ${FULL_IMAGE}(Dockerfile位于${DOCKERFILE_PATH})" -if cd "$SRC_DIR" && sudo docker build -t "${FULL_IMAGE}" -f Dockerfile .; then - log_info "镜像 ${FULL_IMAGE} 构建成功" -else - log_error "镜像 ${FULL_IMAGE} 构建失败" - exit 1 -fi - -# 步骤4:启动docker-compose服务(变量引用加引号) -log_info "开始启动编排服务: ${COMPOSE_PROJECT_NAME}" -cd .. -if docker-compose -f "$DOCKER_COMPOSE_FILE" -p "$COMPOSE_PROJECT_NAME" up -d; then - log_info "编排服务 ${COMPOSE_PROJECT_NAME} 已成功启动" - # 额外输出运行状态,提升用户体验 - log_info "当前运行的容器:" - docker-compose -f "$DOCKER_COMPOSE_FILE" -p "$COMPOSE_PROJECT_NAME" ps -else - log_error "编排服务 ${COMPOSE_PROJECT_NAME} 启动失败" - exit 1 -fi - -log_info "===== 构建脚本执行完成 =====" diff --git a/backend/futures_trade_record/dev-test.sh b/backend/futures_trade_record/dev-test.sh deleted file mode 100644 index 3410fc2..0000000 --- a/backend/futures_trade_record/dev-test.sh +++ /dev/null @@ -1,3 +0,0 @@ -docker stop go_futures_trade_record_dev -docker rm go_futures_trade_record_dev -docker run -itd --name go_futures_trade_record_dev -v $(pwd)/src:/app -p 20110:80 golang:1.25.0-alpine3.22 \ No newline at end of file diff --git a/backend/futures_trade_record/dev.sh b/backend/futures_trade_record/dev.sh deleted file mode 100644 index dde6ad4..0000000 --- a/backend/futures_trade_record/dev.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash - -# 日志函数 -log_info() { - echo "[$(date +'%Y-%m-%d %H:%M:%S')] [DEV_COMPOSE] $1" -} - -log_error() { - echo "[$(date +'%Y-%m-%d %H:%M:%S')] [DEV_ERROR] $1" >&2 -} - -# 获取脚本所在目录的绝对路径 -SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) -# 拼接得到 docker-compose 文件的绝对路径 -COMPOSE_FILE="$SCRIPT_DIR/docker-compose-dev.yaml" - -log_info "开始启动开发环境docker-compose服务" - -# 检查文件是否存在 -if [ ! -f "$COMPOSE_FILE" ]; then - log_error "未找到docker-compose文件: $COMPOSE_FILE" - exit 1 -fi - -# 启动服务 -log_info "执行命令: sudo docker-compose -f $COMPOSE_FILE up -d" -if sudo docker-compose -f "$COMPOSE_FILE" up -d; then - log_info "开发环境服务启动成功" - # 额外输出运行中的容器信息 - log_info "当前运行的容器:" - sudo docker-compose -f "$COMPOSE_FILE" ps -else - log_error "开发环境服务启动失败" - exit 1 -fi \ No newline at end of file diff --git a/backend/futures_trade_record/docker-compose-dev.yaml b/backend/futures_trade_record/docker-compose-dev.yaml deleted file mode 100644 index 89212a1..0000000 --- a/backend/futures_trade_record/docker-compose-dev.yaml +++ /dev/null @@ -1,44 +0,0 @@ -services: - postgres: - image: postgres:17.4-alpine - container_name: futures_trade_record_db - restart: always - ports: - - 20011:5432 - entrypoint: - - /scripts/db-lanuch-entrypoint.sh - environment: - POSTGRES_USER: ${DB_USER} - POSTGRES_PASSWORD: ${DB_PASSWORD} - POSTGRES_DB: ${DB_NAME} - TZ: ${TZ} - volumes: - - ./shared_data/futures_trade_record_db:/var/lib/postgresql/data - - ./sql:/docker-entrypoint-initdb.d - - ./scripts:/scripts - networks: - - futures-trade-record-network - futures_trade_record: - image: golang:1.25.0-alpine3.22 - container_name: futures_trade_record_api - restart: always - ports: - - 20010:80 - depends_on: - - postgres - networks: - - futures-trade-record-network - environment: - DB_HOST: postgres - DB_PORT: ${DB_PORT} - DB_USER: ${DB_USER} - DB_PASSWORD: ${DB_PASSWORD} - DB_NAME: ${DB_NAME} - TZ: ${TZ} - volumes: - - ./src:/app - command: sh -c "cd /app && go mod tidy && go run main.go" -networks: - futures-trade-record-network: - driver: bridge -volumes: {} diff --git a/backend/futures_trade_record/docker-compose.yaml b/backend/futures_trade_record/docker-compose.yaml deleted file mode 100644 index 76d58f9..0000000 --- a/backend/futures_trade_record/docker-compose.yaml +++ /dev/null @@ -1,44 +0,0 @@ -services: - postgres: - image: postgres:17.4-alpine - container_name: futures_trade_record_db - restart: always - ports: - - 20011:5432 - entrypoint: - - /scripts/db-lanuch-entrypoint.sh - environment: - POSTGRES_USER: ${DB_USER} - POSTGRES_PASSWORD: ${DB_PASSWORD} - POSTGRES_DB: ${DB_NAME} - TZ: ${TZ} - volumes: - - ./shared_data/futures_trade_record_db:/var/lib/postgresql/data - - ./sql:/docker-entrypoint-initdb.d - - ./scripts:/scripts - networks: - - futures-trade-record-network - futures_trade_record: - image: futures-trade-record-api:1.0.0 - container_name: futures_trade_record_api - restart: always - ports: - - 20010:80 - depends_on: - - postgres - networks: - - futures-trade-record-network - environment: - DB_HOST: postgres - DB_PORT: ${DB_PORT} - DB_USER: ${DB_USER} - DB_PASSWORD: ${DB_PASSWORD} - DB_NAME: ${DB_NAME} - TZ: ${TZ} - volumes: - # 挂载添加日志目录挂载,将容器内日志日志目录映射到宿主机的 ./logs 目录 - - ./logs:/app/logs # 假设代码中日志存储路径为 /app/logs -networks: - futures-trade-record-network: - driver: bridge -volumes: {} diff --git a/backend/futures_trade_record/scripts/db-lanuch-entrypoint.sh b/backend/futures_trade_record/scripts/db-lanuch-entrypoint.sh deleted file mode 100755 index a4a8ddb..0000000 --- a/backend/futures_trade_record/scripts/db-lanuch-entrypoint.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/bin/sh -set -e - -# 日志函数(带时间戳) -log_info() { - echo "[$(date +'%Y-%m-%d %H:%M:%S')] [DB_INIT] $1" -} - -log_error() { - echo "[$(date +'%Y-%m-%d %H:%M:%S')] [DB_ERROR] $1" >&2 -} - -# 1. 启动PostgreSQL服务 -log_info "启动PostgreSQL服务(后台运行)" -docker-entrypoint.sh postgres & -PG_PID=$! -log_info "PostgreSQL主进程ID: $PG_PID" - -# 2. 等待数据库就绪 -log_info "等待PostgreSQL服务就绪(主机: localhost, 端口: 5432)" -retry_count=0 -max_retries=30 # 最多等待30秒 -until pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB" -h "localhost" -p "5432"; do - retry_count=$((retry_count + 1)) - if [ $retry_count -ge $max_retries ]; then - log_error "等待PostgreSQL超时(超过30秒)" - exit 1 - fi - log_info "数据库未就绪,等待1秒(重试次数: $retry_count)" - sleep 1 -done -log_info "PostgreSQL服务已就绪" - -# 3. 执行SQL脚本 -log_info "开始执行/docker-entrypoint-initdb.d目录下的SQL脚本" -script_count=0 -for script in /docker-entrypoint-initdb.d/*.sql; do - if [ -f "$script" ]; then - script_count=$((script_count + 1)) - log_info "执行脚本 ($script_count): $script" - if psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -h "localhost" -p "5432" -f "$script" --set=ON_ERROR_STOP=1; then - log_info "脚本执行成功: $script" - else - log_error "脚本执行失败: $script" - exit 1 - fi - fi -done - -if [ $script_count -eq 0 ]; then - log_info "未发现需要执行的SQL脚本" -else - log_info "所有SQL脚本执行完成(共$script_count个)" -fi - -# 4. 等待主进程 -log_info "等待PostgreSQL主进程结束(PID: $PG_PID)" -wait $PG_PID -log_info "PostgreSQL进程已退出" \ No newline at end of file diff --git a/backend/futures_trade_record/sql/01_uuid_v7_setup.sql b/backend/futures_trade_record/sql/01_uuid_v7_setup.sql deleted file mode 100644 index b317d1c..0000000 --- a/backend/futures_trade_record/sql/01_uuid_v7_setup.sql +++ /dev/null @@ -1,48 +0,0 @@ --- 切换到目标数据库 -\c postgres; - --- 检查并创建UUID扩展(如果不存在) -CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; - --- 定义检测UUID v7支持的函数 -CREATE OR REPLACE FUNCTION check_uuid_v7_support() RETURNS BOOLEAN AS $$ -DECLARE - test_uuid UUID; -BEGIN - BEGIN - SELECT gen_random_uuid_v7() INTO test_uuid; - RETURN TRUE; - EXCEPTION - WHEN undefined_function THEN - RETURN FALSE; - END; -END; -$$ LANGUAGE plpgsql VOLATILE; - --- 创建UUID v7兼容函数(修复UUID格式长度问题) -CREATE OR REPLACE FUNCTION gen_random_uuid_v7() RETURNS uuid AS $$ -DECLARE - unix_ts_ms BIGINT; - rand_a BIGINT; - rand_b BIGINT; - hex_str TEXT; -BEGIN - -- 获取当前毫秒级Unix时间戳 - unix_ts_ms := (EXTRACT(EPOCH FROM NOW()) * 1000)::BIGINT; - - -- 生成随机数(调整随机数范围以确保总长度正确) - rand_a := (random() * (2^20 - 1))::BIGINT; - rand_b := (random() * (2^44 - 1))::BIGINT; -- 从48位调整为44位,减少2个字节 - - -- 组合UUID v7格式(确保总长度为32个十六进制字符) - hex_str := - lpad(to_hex(unix_ts_ms >> 12), 8, '0') || - lpad(to_hex((unix_ts_ms & 4095) << 4), 4, '0') || - '7' || lpad(to_hex(rand_a >> 18), 3, '0') || - lpad(to_hex(8 + (rand_a & 16383) >> 12), 2, '0') || - lpad(to_hex(rand_a & 4095), 3, '0') || - lpad(to_hex(rand_b), 11, '0'); -- 从12位调整为11位 - - RETURN hex_str::uuid; -END; -$$ LANGUAGE plpgsql VOLATILE; \ No newline at end of file diff --git a/backend/futures_trade_record/sql/02_create_function.sql b/backend/futures_trade_record/sql/02_create_function.sql deleted file mode 100644 index 267ab90..0000000 --- a/backend/futures_trade_record/sql/02_create_function.sql +++ /dev/null @@ -1,20 +0,0 @@ -CREATE OR REPLACE FUNCTION update_data_modified_column() -RETURNS TRIGGER AS $$ -BEGIN - NEW.updated_at = CURRENT_TIMESTAMP; - RETURN NEW; -END; -$$ LANGUAGE plpgsql VOLATILE; - --- 创建自动格式化小数的函数(按需去除尾部多余0) -CREATE OR REPLACE FUNCTION format_numeric_to_original(n NUMERIC) -RETURNS TEXT AS $$ -BEGIN - -- 逻辑:如果是整数(小数部分全0),返回整数文本;否则返回去除尾部0的文本 - IF n = TRUNC(n) THEN - RETURN TRUNC(n)::TEXT; -- 整数场景:1.000000 → '1' - ELSE - RETURN TRIM(TRAILING '0' FROM TRIM(TRAILING '.' FROM n::TEXT)); -- 小数场景:1.230000 → '1.23',1.002000 → '1.002' - END IF; -END; -$$ LANGUAGE plpgsql IMMUTABLE; -- IMMUTABLE:相同输入返回相同输出,支持索引 \ No newline at end of file diff --git a/backend/futures_trade_record/sql/03_create_trade_table.sql b/backend/futures_trade_record/sql/03_create_trade_table.sql deleted file mode 100644 index 1a99404..0000000 --- a/backend/futures_trade_record/sql/03_create_trade_table.sql +++ /dev/null @@ -1,75 +0,0 @@ --- 切换到目标数据库 -\c postgres; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'record') THEN - CREATE TABLE record ( - id UUID DEFAULT gen_random_uuid_v7() PRIMARY KEY NOT NULL, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP - ); - CREATE TRIGGER update_record_updated_at - BEFORE UPDATE ON "record" - FOR EACH ROW - EXECUTE FUNCTION update_data_modified_column(); - - RAISE NOTICE 'created record table and trigger'; - ELSE - RAISE NOTICE 'record table already exists'; - END IF; - - IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'record_deal') THEN - CREATE TABLE record_deal ( - id UUID DEFAULT gen_random_uuid_v7() PRIMARY KEY NOT NULL, - record_id UUID NOT NULL, - variety_id VARCHAR(50) NOT NULL, -- 品种id - variety_name VARCHAR(50) NOT NULL, -- 品种 - contract VARCHAR(50) NOT NULL, -- 合约(如:2401) - direction VARCHAR(20) NOT NULL CHECK (direction IN ('多', '空')), -- 限制合法方向 - open_year INT NOT NULL, - open_month INT NOT NULL, - open_day INT NOT NULL, - open_price NUMERIC(12, 6), - open_commission NUMERIC(10, 6) DEFAULT 0.00, - close_year INT NOT NULL DEFAULT 0, - close_month INT NOT NULL DEFAULT 0, - close_day INT NOT NULL DEFAULT 0, - close_price NUMERIC(12, 6) DEFAULT 0.00, - close_commission NUMERIC(10, 6) DEFAULT 0.00, - variety_tick NUMERIC(12, 6), - close_tick NUMERIC(12, 6) DEFAULT 0.00, -- 平仓跳点 - variety_tick_price NUMERIC(12, 6), - close_tick_profit NUMERIC(12, 6) DEFAULT 0.00, - total_commission NUMERIC(10, 6) DEFAULT 0.00, - close_profit NUMERIC(12, 6) DEFAULT 0.00, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP - ); - CREATE TRIGGER update_record_deal_updated_at - BEFORE UPDATE ON "record_deal" - FOR EACH ROW - EXECUTE FUNCTION update_data_modified_column(); - - -- 1. 开仓日期索引(支持「未删除+开仓日期」查询) - CREATE INDEX idx_trade_open_date ON record_deal (deleted, open_year, open_month, open_day); - - -- 2. 品种+合约+方向组合索引(支持「未删除+品种+合约+方向」高频查询) - CREATE INDEX idx_trade_variety_contract_dir ON record_deal (deleted, variety_name, contract, direction); - - -- 3. 未平仓记录索引(支持「未删除+未平仓」查询) - CREATE INDEX idx_trade_unclosed ON record_deal (deleted, variety_name, contract) WHERE close_year IS NULL; - - -- 4. 平仓日期索引(支持「未删除+已平仓+平仓日期」查询) - CREATE INDEX idx_trade_close_date ON record_deal (deleted, close_year, close_month, close_day) WHERE close_year IS NOT NULL; - - -- 5. 盈亏排序索引(支持「未删除+已平仓+盈亏排序」查询) - CREATE INDEX idx_trade_profit ON record_deal (deleted, close_profit DESC) WHERE close_year IS NOT NULL; - - RAISE NOTICE 'created record_deal table and trigger'; - ELSE - RAISE NOTICE 'record_deal table already exists'; - END IF; -END $$; \ No newline at end of file diff --git a/backend/futures_trade_record/sql/04_create_variety_table.sql b/backend/futures_trade_record/sql/04_create_variety_table.sql deleted file mode 100644 index b8ec96e..0000000 --- a/backend/futures_trade_record/sql/04_create_variety_table.sql +++ /dev/null @@ -1,199 +0,0 @@ --- 切换到目标数据库 -\c postgres; - -DO $$ -BEGIN - -- 创建主表 variety - IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'variety') THEN - CREATE TABLE variety ( - id UUID DEFAULT gen_random_uuid_v7() PRIMARY KEY NOT NULL, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP - ); - CREATE TRIGGER update_variety_updated_at - BEFORE UPDATE ON "variety" - FOR EACH ROW - EXECUTE FUNCTION update_data_modified_column(); - - -- 主表索引:deleted+created_at 组合索引(常用于查询未删除数据并排序) - CREATE INDEX idx_variety_deleted_created_at ON variety(deleted, created_at); - - RAISE NOTICE 'created variety table, trigger and indexes'; - ELSE - RAISE NOTICE 'variety table already exists'; - END IF; - - -- 创建子表 variety_name 并添加外键和索引 - IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'variety_name') THEN - CREATE TABLE variety_name ( - id UUID DEFAULT gen_random_uuid_v7() PRIMARY KEY NOT NULL, - variety_id UUID NOT NULL, - name VARCHAR(50) NOT NULL, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - -- 外键约束,关联variety主表 - CONSTRAINT fk_variety_name_variety - FOREIGN KEY (variety_id) - REFERENCES variety(id) - ON DELETE CASCADE - ON UPDATE CASCADE - ); - CREATE TRIGGER update_variety_name_updated_at - BEFORE UPDATE ON "variety_name" - FOR EACH ROW - EXECUTE FUNCTION update_data_modified_column(); - - -- 子表索引:外键+deleted组合索引(关联查询时高效过滤) - CREATE INDEX idx_variety_name_variety_id_deleted ON variety_name(variety_id, deleted); - -- 名称查询索引(支持按名称搜索品种) - CREATE INDEX idx_variety_name_name_deleted ON variety_name(name, deleted); - - RAISE NOTICE 'created variety_name table, trigger, foreign key and indexes'; - ELSE - RAISE NOTICE 'variety_name table already exists'; - END IF; - - -- 创建子表 variety_code 并添加外键和索引 - IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'variety_code') THEN - CREATE TABLE variety_code ( - id UUID DEFAULT gen_random_uuid_v7() PRIMARY KEY NOT NULL, - variety_id UUID NOT NULL, - code VARCHAR(50) NOT NULL, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - -- 外键约束,关联variety主表 - CONSTRAINT fk_variety_code_variety - FOREIGN KEY (variety_id) - REFERENCES variety(id) - ON DELETE CASCADE - ON UPDATE CASCADE - ); - CREATE TRIGGER update_variety_code_updated_at - BEFORE UPDATE ON "variety_code" - FOR EACH ROW - EXECUTE FUNCTION update_data_modified_column(); - - -- 子表索引:外键+deleted组合索引 - CREATE INDEX idx_variety_code_variety_id_deleted ON variety_code(variety_id, deleted); - -- 代码查询索引(支持按代码搜索品种) - CREATE INDEX idx_variety_code_code_deleted ON variety_code(code, deleted); - - RAISE NOTICE 'created variety_code table, trigger, foreign key and indexes'; - ELSE - RAISE NOTICE 'variety_code table already exists'; - END IF; - - -- 创建子表 variety_tick 并添加外键和索引 - IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'variety_tick') THEN - CREATE TABLE variety_tick ( - id UUID DEFAULT gen_random_uuid_v7() PRIMARY KEY NOT NULL, - variety_id UUID NOT NULL, - tick NUMERIC(12, 6), - deleted BOOLEAN NOT NULL DEFAULT FALSE, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - -- 外键约束,关联variety主表 - CONSTRAINT fk_variety_tick_variety - FOREIGN KEY (variety_id) - REFERENCES variety(id) - ON DELETE CASCADE - ON UPDATE CASCADE - ); - CREATE TRIGGER update_variety_tick_updated_at - BEFORE UPDATE ON "variety_tick" - FOR EACH ROW - EXECUTE FUNCTION update_data_modified_column(); - - -- 子表索引:外键+deleted组合索引 - CREATE INDEX idx_variety_tick_variety_id_deleted ON variety_tick(variety_id, deleted); - -- 跳点值范围查询索引 - CREATE INDEX idx_variety_tick_tick_deleted ON variety_tick(tick, deleted); - - RAISE NOTICE 'created variety_tick table, trigger, foreign key and indexes'; - ELSE - RAISE NOTICE 'variety_tick table already exists'; - END IF; - - -- 创建子表 variety_tick_price 并添加外键和索引 - IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'variety_tick_price') THEN - CREATE TABLE variety_tick_price ( - id UUID DEFAULT gen_random_uuid_v7() PRIMARY KEY NOT NULL, - variety_id UUID NOT NULL, - price NUMERIC(12, 6), - deleted BOOLEAN NOT NULL DEFAULT FALSE, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - -- 外键约束,关联variety主表 - CONSTRAINT fk_variety_tick_price_variety - FOREIGN KEY (variety_id) - REFERENCES variety(id) - ON DELETE CASCADE - ON UPDATE CASCADE - ); - CREATE TRIGGER update_variety_tick_price_updated_at - BEFORE UPDATE ON "variety_tick_price" - FOR EACH ROW - EXECUTE FUNCTION update_data_modified_column(); - - -- 子表索引:外键+deleted组合索引 - CREATE INDEX idx_variety_tick_price_variety_id_deleted ON variety_tick_price(variety_id, deleted); - -- 价格范围查询索引 - CREATE INDEX idx_variety_tick_price_price_deleted ON variety_tick_price(price, deleted); - - RAISE NOTICE 'created variety_tick_price table, trigger, foreign key and indexes'; - ELSE - RAISE NOTICE 'variety_tick_price table already exists'; - END IF; -END $$; - -DO $$ -DECLARE - view_exists BOOLEAN; -BEGIN - -- 检查视图是否已存在 - SELECT EXISTS ( - SELECT 1 FROM information_schema.views - WHERE table_name = 'variety_info_view' - ) INTO view_exists; - - -- 创建或更新视图 - CREATE OR REPLACE VIEW variety_info_view AS - SELECT - v.id AS variety_id, - vn.name AS name, - vc.code AS code, - -- 调用格式化函数:自动去除尾部多余0 - format_numeric_to_original(vt.tick) AS tick, - format_numeric_to_original(vtp.price) AS tick_price, - -- 可选:保留原始精度字段(供计算使用) - vt.tick AS tick_original, - vtp.price AS tick_price_original - FROM - variety v - LEFT JOIN variety_name vn - ON v.id = vn.variety_id - AND vn.deleted = FALSE - LEFT JOIN variety_code vc - ON v.id = vc.variety_id - AND vc.deleted = FALSE - LEFT JOIN variety_tick vt - ON v.id = vt.variety_id - AND vt.deleted = FALSE - LEFT JOIN variety_tick_price vtp - ON v.id = vtp.variety_id - AND vtp.deleted = FALSE - WHERE - v.deleted = FALSE; - - IF view_exists THEN - RAISE NOTICE '视图 variety_info_view 已更新'; - ELSE - RAISE NOTICE '视图 variety_info_view 已创建'; - END IF; -EXCEPTION - WHEN OTHERS THEN - RAISE NOTICE '处理视图时发生错误: %', SQLERRM; -END $$; \ No newline at end of file diff --git a/backend/futures_trade_record/sql/05_create_exchange_table.sql b/backend/futures_trade_record/sql/05_create_exchange_table.sql deleted file mode 100644 index 11ab980..0000000 --- a/backend/futures_trade_record/sql/05_create_exchange_table.sql +++ /dev/null @@ -1,126 +0,0 @@ --- 切换到目标数据库 -\c postgres; - -DO $$ -BEGIN - -- 创建主表 exchange - IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'exchange') THEN - CREATE TABLE exchange ( - id UUID DEFAULT gen_random_uuid_v7() PRIMARY KEY NOT NULL, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP - ); - CREATE TRIGGER update_exchange_updated_at - BEFORE UPDATE ON "exchange" - FOR EACH ROW - EXECUTE FUNCTION update_data_modified_column(); - - -- 主表索引:deleted+created_at 组合索引(常用于查询未删除数据并排序) - CREATE INDEX idx_exchange_deleted_created_at ON exchange(deleted, created_at); - - RAISE NOTICE 'created exchange table, trigger and indexes'; - ELSE - RAISE NOTICE 'exchange table already exists'; - END IF; - - -- 创建子表 exchange_name 并添加外键和索引 - IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'exchange_name') THEN - CREATE TABLE exchange_name ( - id UUID DEFAULT gen_random_uuid_v7() PRIMARY KEY NOT NULL, - exchange_id UUID NOT NULL, - name VARCHAR(50) NOT NULL, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - -- 外键约束,关联exchange主表 - CONSTRAINT fk_exchange_name_exchange - FOREIGN KEY (exchange_id) - REFERENCES exchange(id) - ON DELETE CASCADE - ON UPDATE CASCADE - ); - CREATE TRIGGER update_exchange_name_updated_at - BEFORE UPDATE ON "exchange_name" - FOR EACH ROW - EXECUTE FUNCTION update_data_modified_column(); - - -- 子表索引:外键+deleted组合索引(关联查询时高效过滤) - CREATE INDEX idx_exchange_name_exchange_id_deleted ON exchange_name(exchange_id, deleted); - -- 名称查询索引(支持按名称搜索品种) - CREATE INDEX idx_exchange_name_name_deleted ON exchange_name(name, deleted); - - RAISE NOTICE 'created exchange_name table, trigger, foreign key and indexes'; - ELSE - RAISE NOTICE 'exchange_name table already exists'; - END IF; - - -- 创建子表 exchange_code 并添加外键和索引 - IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'exchange_code') THEN - CREATE TABLE exchange_code ( - id UUID DEFAULT gen_random_uuid_v7() PRIMARY KEY NOT NULL, - exchange_id UUID NOT NULL, - code VARCHAR(50) NOT NULL, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - -- 外键约束,关联exchange主表 - CONSTRAINT fk_exchange_code_exchange - FOREIGN KEY (exchange_id) - REFERENCES exchange(id) - ON DELETE CASCADE - ON UPDATE CASCADE - ); - CREATE TRIGGER update_exchange_code_updated_at - BEFORE UPDATE ON "exchange_code" - FOR EACH ROW - EXECUTE FUNCTION update_data_modified_column(); - - -- 子表索引:外键+deleted组合索引 - CREATE INDEX idx_exchange_code_exchange_id_deleted ON exchange_code(exchange_id, deleted); - -- 代码查询索引(支持按代码搜索品种) - CREATE INDEX idx_exchange_code_code_deleted ON exchange_code(code, deleted); - - RAISE NOTICE 'created exchange_code table, trigger, foreign key and indexes'; - ELSE - RAISE NOTICE 'exchange_code table already exists'; - END IF; -END $$; - -DO $$ -DECLARE - view_exists BOOLEAN; -BEGIN - -- 检查视图是否已存在 - SELECT EXISTS ( - SELECT 1 FROM information_schema.views - WHERE table_name = 'exchange_info_view' - ) INTO view_exists; - - -- 创建/更新视图:整合交易所ID、名称、代码(仅显示未删除数据) - CREATE OR REPLACE VIEW exchange_info_view AS - SELECT - e.id AS exchange_id, -- 交易所主表唯一ID - en.name AS name, -- 交易所名称(来自exchange_name子表) - ec.code AS code -- 交易所代码(来自exchange_code子表) - FROM - exchange e - -- 左连接:主表存在时,即使子表无数据也保留记录(避免数据丢失) - LEFT JOIN exchange_name en - ON e.id = en.exchange_id - AND en.deleted = FALSE -- 子句中筛选未删除数据,关联时直接过滤更高效 - LEFT JOIN exchange_code ec - ON e.id = ec.exchange_id - AND ec.deleted = FALSE -- 子表未删除数据筛选 - WHERE - e.deleted = FALSE; -- 主表筛选未删除的交易所 - - IF view_exists THEN - RAISE NOTICE '视图 exchange_info_view 已更新'; - ELSE - RAISE NOTICE '视图 exchange_info_view 已创建'; - END IF; -EXCEPTION - WHEN OTHERS THEN - RAISE NOTICE '处理视图时发生错误: %', SQLERRM; -END $$; \ No newline at end of file diff --git a/backend/futures_trade_record/src/Dockerfile b/backend/futures_trade_record/src/Dockerfile deleted file mode 100644 index a54892c..0000000 --- a/backend/futures_trade_record/src/Dockerfile +++ /dev/null @@ -1,38 +0,0 @@ -# ==================== 第一阶段:构建Go程序(构建阶段)==================== -# 使用官方Go镜像作为构建基础,选择与项目匹配的Go版本(示例用1.25.0,可根据实际调整) -FROM golang:1.25.0-alpine3.22 AS builder - -# 设置工作目录(容器内的目录,规范文件位置) -WORKDIR /app - -# 复制go.mod和go.sum(先复制依赖文件,利用Docker缓存机制,避免每次代码变动都重新下载依赖) -COPY go.mod go.sum ./ - -# 下载项目依赖(仅当go.mod/go.sum变动时才会重新执行) -RUN go mod download - -# 复制整个项目代码到工作目录 -COPY . . - -# 构建Go程序: -# - CGO_ENABLED=0:禁用CGO,生成静态链接的二进制文件(避免依赖系统库,保证镜像兼容性) -# - -o app:指定输出二进制文件名为app -# - ./main.go:指定入口文件 -RUN CGO_ENABLED=0 GOOS=linux go build -o app ./main.go - - -# ==================== 第二阶段:运行程序(运行阶段)==================== -# 使用轻量级的alpine镜像(仅5MB左右,大幅减小最终镜像体积) -FROM alpine:3.19 - -# 设置工作目录 -WORKDIR /app - -# 从构建阶段复制编译好的二进制文件到当前镜像(仅复制最终产物,减小体积) -COPY --from=builder /app/app ./ - -# 暴露程序运行端口(与代码中一致) -EXPOSE 80 - -# 容器启动时执行的命令:运行二进制文件 -CMD ["./app"] \ No newline at end of file diff --git a/backend/futures_trade_record/src/db/postgres.go b/backend/futures_trade_record/src/db/postgres.go deleted file mode 100644 index 6753a5e..0000000 --- a/backend/futures_trade_record/src/db/postgres.go +++ /dev/null @@ -1,54 +0,0 @@ -package db - -import ( - "database/sql" - "fmt" - "os" - "time" - - _ "github.com/lib/pq" - "go.uber.org/zap" -) - -var DB *sql.DB - -// 初始化数据库连接 -func Init() { - // 从环境变量获取数据库配置 - dbHost := os.Getenv("DB_HOST") - dbPort := os.Getenv("DB_PORT") - dbUser := os.Getenv("DB_USER") - dbPassword := os.Getenv("DB_PASSWORD") - dbName := os.Getenv("DB_NAME") - zap.L().Info( - "🟢 读取数据库配置", - zap.String("host", dbHost), - zap.String("port", dbPort), - zap.String("user", dbUser), - zap.String("dbname", dbName), - ) - - // 构建数据库连接字符串 - connStr := fmt.Sprintf( - "host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", - dbHost, dbPort, dbUser, dbPassword, dbName, - ) - - var err error - DB, err = sql.Open("postgres", connStr) - if err != nil { - zap.L().Panic("❌ 无法连接数据库", zap.Error(err)) - } - - // 设置连接池参数 - DB.SetMaxOpenConns(100) // 最大打开连接数 - DB.SetMaxIdleConns(20) // 最大空闲连接数 - DB.SetConnMaxLifetime(time.Hour) // 连接最大存活时间 - - // 验证数据库连接 - if err := DB.Ping(); err != nil { - zap.L().Panic("❌ 数据库连接失败", zap.Error(err)) - } - - zap.L().Info("✅ 数据库连接验证成功") -} diff --git a/backend/futures_trade_record/src/go.mod b/backend/futures_trade_record/src/go.mod deleted file mode 100644 index 12dbbad..0000000 --- a/backend/futures_trade_record/src/go.mod +++ /dev/null @@ -1,57 +0,0 @@ -module futures_trade_record - -go 1.25.0 - -require ( - github.com/gin-contrib/cors v1.7.6 - github.com/gin-gonic/gin v1.11.0 - github.com/google/uuid v1.6.0 - github.com/lib/pq v1.10.9 - github.com/spf13/viper v1.21.0 - go.uber.org/zap v1.27.0 - gopkg.in/natefinch/lumberjack.v2 v2.2.1 -) - -require ( - github.com/bytedance/sonic v1.14.0 // indirect - github.com/bytedance/sonic/loader v0.3.0 // indirect - github.com/cloudwego/base64x v0.1.6 // indirect - github.com/fsnotify/fsnotify v1.9.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.9 // indirect - github.com/gin-contrib/sse v1.1.0 // indirect - github.com/go-playground/locales v0.14.1 // indirect - github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.27.0 // indirect - github.com/go-viper/mapstructure/v2 v2.4.0 // indirect - github.com/goccy/go-json v0.10.5 // indirect - github.com/goccy/go-yaml v1.18.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.3.0 // indirect - github.com/leodido/go-urn v1.4.0 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/quic-go/qpack v0.5.1 // indirect - github.com/quic-go/quic-go v0.54.0 // indirect - github.com/sagikazarmark/locafero v0.11.0 // indirect - github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect - github.com/spf13/afero v1.15.0 // indirect - github.com/spf13/cast v1.10.0 // indirect - github.com/spf13/pflag v1.0.10 // indirect - github.com/subosito/gotenv v1.6.0 // indirect - github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.3.0 // indirect - go.uber.org/mock v0.5.0 // indirect - go.uber.org/multierr v1.10.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/arch v0.20.0 // indirect - golang.org/x/crypto v0.43.0 // indirect - golang.org/x/mod v0.28.0 // indirect - golang.org/x/net v0.45.0 // indirect - golang.org/x/sync v0.17.0 // indirect - golang.org/x/sys v0.37.0 // indirect - golang.org/x/text v0.30.0 // indirect - golang.org/x/tools v0.37.0 // indirect - google.golang.org/protobuf v1.36.9 // indirect -) diff --git a/backend/futures_trade_record/src/go.sum b/backend/futures_trade_record/src/go.sum deleted file mode 100644 index 0117005..0000000 --- a/backend/futures_trade_record/src/go.sum +++ /dev/null @@ -1,133 +0,0 @@ -github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ= -github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA= -github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= -github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= -github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= -github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= -github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= -github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= -github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok= -github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY= -github.com/gin-contrib/cors v1.7.6/go.mod h1:Ulcl+xN4jel9t1Ry8vqph23a60FwH9xVLd+3ykmTjOk= -github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= -github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= -github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= -github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls= -github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= -github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= -github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4= -github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= -github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= -github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= -github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= -github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= -github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= -github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= -github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= -github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= -github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg= -github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= -github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= -github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= -github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= -github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= -github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= -github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= -github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= -github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= -github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= -github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= -github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= -github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= -github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= -go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= -go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= -go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c= -golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= -golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= -golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= -golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= -golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= -golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= -golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= -golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= -golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= -google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= -google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= -gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/backend/futures_trade_record/src/logger/logger.go b/backend/futures_trade_record/src/logger/logger.go deleted file mode 100644 index d7eee0b..0000000 --- a/backend/futures_trade_record/src/logger/logger.go +++ /dev/null @@ -1,86 +0,0 @@ -package logger - -import ( - "log" - "os" - "time" - - "github.com/spf13/viper" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" - "gopkg.in/natefinch/lumberjack.v2" -) - -var shanghaiLoc *time.Location - -func init() { - var err error - shanghaiLoc, err = time.LoadLocation("Asia/Shanghai") - if err != nil { - // 尝试备选时区名称 - shanghaiLoc, err = time.LoadLocation("PRC") - if err != nil { - // 若仍失败,手动设置东八区偏移 - shanghaiLoc = time.FixedZone("CST", 8*3600) - log.Printf("警告:加载时区失败,使用手动东八区偏移: %v", err) - } - } -} - -// Init 初始化日志(依赖配置文件已加载) -func Init() { - // 日志级别转换 - level := zap.InfoLevel - switch viper.GetString("logger.level") { - case "debug": - level = zap.DebugLevel - case "warn": - level = zap.WarnLevel - case "error": - level = zap.ErrorLevel - } - - // 日志轮转配置(lumberjack) - hook := lumberjack.Logger{ - Filename: viper.GetString("logger.path") + "logs/app.log", // 日志文件路径 - MaxSize: viper.GetInt("logger.max_size"), // 单个文件最大大小(MB) - MaxBackups: viper.GetInt("logger.max_backup"), // 最大备份数 - MaxAge: viper.GetInt("logger.max_age"), // 最大保留天数 - Compress: true, // 是否压缩 - } - - // 编码器配置 - encoderConfig := zapcore.EncoderConfig{ - TimeKey: "time", - LevelKey: "level", - NameKey: "logger", - CallerKey: "caller", - MessageKey: "msg", - StacktraceKey: "stacktrace", - LineEnding: zapcore.DefaultLineEnding, - EncodeLevel: zapcore.CapitalLevelEncoder, // 日志级别大写(DEBUG/INFO) - EncodeTime: customTimeEncoder, // 自定义时间格式 - EncodeDuration: zapcore.SecondsDurationEncoder, - EncodeCaller: zapcore.ShortCallerEncoder, // 精简调用者路径 - } - - // 输出配置(控制台+文件) - core := zapcore.NewTee( - zapcore.NewCore(zapcore.NewConsoleEncoder(encoderConfig), zapcore.AddSync(os.Stdout), level), - zapcore.NewCore(zapcore.NewJSONEncoder(encoderConfig), zapcore.AddSync(&hook), level), - ) - - // 创建logger实例(开启调用者信息和堆栈跟踪) - logger := zap.New(core, zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel)) - zap.ReplaceGlobals(logger) - - zap.L().Info("✅ 日志初始化成功", zap.String("level", level.String())) -} - -// customTimeEncoder 自定义时间格式(强制东八区,若加载失败则使用UTC) -func customTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { - // 使用提前初始化好的时区,避免每次调用都加载 - beijingTime := t.In(shanghaiLoc) - // 格式化输出 - enc.AppendString(beijingTime.Format("2006-01-02 15:04:05.000")) -} diff --git a/backend/futures_trade_record/src/logic4exchange/exchange_create.go b/backend/futures_trade_record/src/logic4exchange/exchange_create.go deleted file mode 100644 index 5e52df8..0000000 --- a/backend/futures_trade_record/src/logic4exchange/exchange_create.go +++ /dev/null @@ -1,177 +0,0 @@ -package logic4exchange - -import ( - "futures_trade_record/db" - "net/http" - "time" - - "github.com/gin-gonic/gin" - "github.com/google/uuid" - "go.uber.org/zap" -) - -// CreateExchangeRequest 交易所创建请求参数(仅包含SQL中定义的必要字段) -type CreateExchangeRequest struct { - Name string `json:"name" binding:"required"` // 交易所名称(对应exchange_name表) - Code string `json:"code" binding:"required"` // 交易所代码(对应exchange_code表) -} - -// CreateExchangeResponse 响应结构 -type CreateExchangeResponse struct { - Success bool `json:"success"` - Message string `json:"message"` - Data CreateExchangeData `json:"data"` -} - -// CreateExchangeData 响应数据 -type CreateExchangeData struct { - ExchangeID string `json:"exchange_id"` -} - -// CreateExchangeHandler 处理交易所创建逻辑 -func CreateExchangeHandler(c *gin.Context) { - startTime := time.Now() - reqID := c.Request.Header.Get("X-RegisterRequest-ID") - if reqID == "" { - reqID = uuid.New().String() - zap.L().Debug("✨ 生成新的请求ID", zap.String("req_id", reqID)) - } - - zap.L().Info("📥 收到交易所创建请求", - zap.String("req_id", reqID), - zap.String("path", c.Request.URL.Path), - zap.String("method", c.Request.Method), - ) - - var req CreateExchangeRequest - // 绑定并验证请求参数(仅验证name和code) - if err := c.ShouldBindJSON(&req); err != nil { - zap.L().Warn("🔴 请求参数验证失败", - zap.String("req_id", reqID), - zap.Error(err), - zap.Any("request_body", c.Request.Body), - ) - c.JSON(http.StatusBadRequest, CreateExchangeResponse{ - Success: false, - Message: "请求参数错误:name和code为必填项", - }) - return - } - - zap.L().Debug("✅ 请求参数验证通过", - zap.String("req_id", reqID), - zap.String("name", req.Name), - zap.String("code", req.Code), - ) - - // 开启数据库事务 - tx, err := db.DB.Begin() - if err != nil { - zap.L().Error("❌ 事务开启失败", - zap.String("req_id", reqID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, CreateExchangeResponse{ - Success: false, - Message: "系统错误,请稍后重试", - }) - return - } - defer func() { - if r := recover(); r != nil { - if err := tx.Rollback(); err != nil { - zap.L().Error("💥 panic后事务回滚失败", - zap.String("req_id", reqID), - zap.Error(err), - ) - } - zap.L().Error("💥 事务处理发生panic", - zap.String("req_id", reqID), - zap.Any("recover", r), - ) - c.JSON(http.StatusInternalServerError, CreateExchangeResponse{ - Success: false, - Message: "系统错误,请稍后重试", - }) - } - }() - - // 1. 在exchange主表创建记录并获取ID - var exchangeID string - err = tx.QueryRow("INSERT INTO exchange DEFAULT VALUES RETURNING id").Scan(&exchangeID) - if err != nil { - tx.Rollback() - zap.L().Error("❌ exchange表插入失败", - zap.String("req_id", reqID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, CreateExchangeResponse{ - Success: false, - Message: "创建交易所记录失败", - }) - return - } - - // 2. 插入交易所名称到exchange_name子表 - _, err = tx.Exec("INSERT INTO exchange_name (exchange_id, name) VALUES ($1, $2)", exchangeID, req.Name) - if err != nil { - tx.Rollback() - zap.L().Error("❌ exchange_name表插入失败", - zap.String("req_id", reqID), - zap.String("exchange_id", exchangeID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, CreateExchangeResponse{ - Success: false, - Message: "保存名称信息失败", - }) - return - } - - // 3. 插入交易所代码到exchange_code子表 - _, err = tx.Exec("INSERT INTO exchange_code (exchange_id, code) VALUES ($1, $2)", exchangeID, req.Code) - if err != nil { - tx.Rollback() - zap.L().Error("❌ exchange_code表插入失败", - zap.String("req_id", reqID), - zap.String("exchange_id", exchangeID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, CreateExchangeResponse{ - Success: false, - Message: "保存代码信息失败", - }) - return - } - - // 提交事务 - if err := tx.Commit(); err != nil { - tx.Rollback() - zap.L().Error("❌ 事务提交失败", - zap.String("req_id", reqID), - zap.String("exchange_id", exchangeID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, CreateExchangeResponse{ - Success: false, - Message: "数据提交失败,请稍后重试", - }) - return - } - - // 记录处理耗时并返回成功响应 - duration := time.Since(startTime) - zap.L().Info("✅ 交易所创建请求处理完成", - zap.String("req_id", reqID), - zap.String("exchange_id", exchangeID), - zap.Duration("duration", duration), - ) - - c.JSON(http.StatusOK, CreateExchangeResponse{ - Success: true, - Message: "创建成功", - Data: CreateExchangeData{ - ExchangeID: exchangeID, - }, - }) -} diff --git a/backend/futures_trade_record/src/logic4variety/variety_create.go b/backend/futures_trade_record/src/logic4variety/variety_create.go deleted file mode 100644 index 424ba49..0000000 --- a/backend/futures_trade_record/src/logic4variety/variety_create.go +++ /dev/null @@ -1,219 +0,0 @@ -package logic4variety - -import ( - "futures_trade_record/db" // 数据库操作相关包 - "net/http" - "time" // 时间处理包 - - "github.com/gin-gonic/gin" // Gin框架,用于处理HTTP请求 - "github.com/google/uuid" // UUID生成工具 - "go.uber.org/zap" // 日志库 -) - -// CreateVarietyRequest 注册请求参数结构 -type CreateVarietyRequest struct { - Name string `json:"name" binding:"required"` // 品种名称,必填 - Code string `json:"code" binding:"required"` // 品种代码,必填 - Tick float64 `json:"tick" binding:"min=0"` // 跳点值,必填,需大于等于0 - TickPrice float64 `json:"tick_price" binding:"min=0"` // 跳点价格,必填,需大于等于0 -} - -// CreateVarietyResponse 注册响应结构 -type CreateVarietyResponse struct { - Success bool `json:"success"` // 操作是否成功 - Message string `json:"message"` // 提示信息 - Data CreateVarietyData `json:"data"` // 响应数据 -} - -// CreateVarietyData 响应数据结构 -type CreateVarietyData struct { - VarietyID string `json:"variety_id"` // 品种唯一标识ID -} - -// CreateVarietyHandler 处理品种创建逻辑 -func CreateVarietyHandler(c *gin.Context) { - startTime := time.Now() - reqID := c.Request.Header.Get("X-RegisterRequest-ID") - if reqID == "" { - reqID = uuid.New().String() - zap.L().Debug("✨ 生成新的请求ID", zap.String("req_id", reqID)) - } - - zap.L().Info("📥 收到品种创建请求", - zap.String("req_id", reqID), - zap.String("path", c.Request.URL.Path), - zap.String("method", c.Request.Method), - ) - - var req CreateVarietyRequest - // 绑定并验证请求参数(包含新增的tick和tick_price) - if err := c.ShouldBindJSON(&req); err != nil { - zap.L().Warn("🔴 请求参数验证失败", - zap.String("req_id", reqID), - zap.Error(err), - zap.Any("request_body", c.Request.Body), - ) - c.JSON(http.StatusBadRequest, CreateVarietyResponse{ - Success: false, - Message: "请求参数错误:name、code、tick和tick_price为必填项,且tick和tick_price需大于等于0", - }) - return - } - - zap.L().Debug("✅ 请求参数验证通过", - zap.String("req_id", reqID), - zap.String("name", req.Name), - zap.String("code", req.Code), - zap.Float64("tick", req.Tick), - zap.Float64("tick_price", req.TickPrice), - ) - - // 开启数据库事务 - tx, err := db.DB.Begin() - if err != nil { - zap.L().Error("❌ 事务开启失败", - zap.String("req_id", reqID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, CreateVarietyResponse{ - Success: false, - Message: "系统错误,请稍后重试", - }) - return - } - defer func() { - if r := recover(); r != nil { - if err := tx.Rollback(); err != nil { - zap.L().Error("💥 panic后事务回滚失败", - zap.String("req_id", reqID), - zap.Error(err), - ) - } - zap.L().Error("💥 事务处理发生panic", - zap.String("req_id", reqID), - zap.Any("recover", r), - ) - c.JSON(http.StatusInternalServerError, CreateVarietyResponse{ - Success: false, - Message: "系统错误,请稍后重试", - }) - } - }() - - // 1. 在variety表中创建记录并获取自动生成的ID - var varietyID string - err = tx.QueryRow("INSERT INTO variety DEFAULT VALUES RETURNING id").Scan(&varietyID) - if err != nil { - tx.Rollback() - zap.L().Error("❌ variety表插入失败", - zap.String("req_id", reqID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, CreateVarietyResponse{ - Success: false, - Message: "创建品种记录失败", - }) - return - } - - zap.L().Debug("📝 variety表插入成功", - zap.String("req_id", reqID), - zap.String("variety_id", varietyID), - ) - - // 2. 插入品种名称到variety_name表(修正表名) - _, err = tx.Exec("INSERT INTO variety_name (variety_id, name) VALUES ($1, $2)", varietyID, req.Name) - if err != nil { - tx.Rollback() - zap.L().Error("❌ variety_name表插入失败", - zap.String("req_id", reqID), - zap.String("variety_id", varietyID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, CreateVarietyResponse{ - Success: false, - Message: "保存名称信息失败", - }) - return - } - - // 3. 插入品种代码到variety_code表(修正表名) - _, err = tx.Exec("INSERT INTO variety_code (variety_id, code) VALUES ($1, $2)", varietyID, req.Code) - if err != nil { - tx.Rollback() - zap.L().Error("❌ variety_code表插入失败", - zap.String("req_id", reqID), - zap.String("variety_id", varietyID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, CreateVarietyResponse{ - Success: false, - Message: "保存代码信息失败", - }) - return - } - - // 4. 插入跳点值到variety_tick表(新增) - _, err = tx.Exec("INSERT INTO variety_tick (variety_id, tick) VALUES ($1, $2)", varietyID, req.Tick) - if err != nil { - tx.Rollback() - zap.L().Error("❌ variety_tick表插入失败", - zap.String("req_id", reqID), - zap.String("variety_id", varietyID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, CreateVarietyResponse{ - Success: false, - Message: "保存跳点信息失败", - }) - return - } - - // 5. 插入跳点价格到variety_tick_price表(新增) - _, err = tx.Exec("INSERT INTO variety_tick_price (variety_id, price) VALUES ($1, $2)", varietyID, req.TickPrice) - if err != nil { - tx.Rollback() - zap.L().Error("❌ variety_tick_price表插入失败", - zap.String("req_id", reqID), - zap.String("variety_id", varietyID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, CreateVarietyResponse{ - Success: false, - Message: "保存跳点价格信息失败", - }) - return - } - - // 提交事务 - if err := tx.Commit(); err != nil { - tx.Rollback() - zap.L().Error("❌ 事务提交失败", - zap.String("req_id", reqID), - zap.String("variety_id", varietyID), - zap.Error(err), - ) - c.JSON(http.StatusInternalServerError, CreateVarietyResponse{ - Success: false, - Message: "数据提交失败,请稍后重试", - }) - return - } - - // 记录请求处理耗时 - duration := time.Since(startTime) - zap.L().Info("✅ 品种创建请求处理完成", - zap.String("req_id", reqID), - zap.String("variety_id", varietyID), - zap.Duration("duration", duration), - ) - - // 返回成功响应 - c.JSON(http.StatusOK, CreateVarietyResponse{ - Success: true, - Message: "创建成功", - Data: CreateVarietyData{ - VarietyID: varietyID, - }, - }) -} diff --git a/backend/futures_trade_record/src/main.go b/backend/futures_trade_record/src/main.go deleted file mode 100644 index e4c4050..0000000 --- a/backend/futures_trade_record/src/main.go +++ /dev/null @@ -1,73 +0,0 @@ -package main - -import ( - "futures_trade_record/db" // 数据库相关操作包 - "futures_trade_record/logger" // 日志工具包 - "futures_trade_record/logic4exchange" - "futures_trade_record/logic4variety" - - // 业务逻辑处理包 - "time" - - "github.com/gin-contrib/cors" - "github.com/gin-gonic/gin" // Gin框架,用于构建HTTP服务 - _ "github.com/lib/pq" // PostgreSQL数据库驱动(下划线表示仅初始化不直接使用) - "go.uber.org/zap" // Zap日志库,用于结构化日志输出 -) - -// main函数是程序的入口点 -func main() { - // 初始化日志配置 - logger.Init() - // 记录服务初始化日志 - zap.L().Info("🟢 用户服务初始化") - - // 记录数据库初始化开始日志 - zap.L().Info("🟢 数据库初始化开始") - // 初始化数据库连接 - db.Init() - // 程序退出时关闭数据库连接(defer确保在函数退出前执行) - defer db.DB.Close() - // 记录数据库初始化成功日志 - zap.L().Info("✅ 数据库初始化成功") - - // 设置Gin框架为发布模式(关闭调试信息) - gin.SetMode(gin.ReleaseMode) - // 创建Gin默认路由器 - r := gin.Default() - - // 配置跨域中间件 - r.Use(cors.New(cors.Config{ - // 允许所有来源(生产环境建议指定具体域名) - AllowOrigins: []string{"*"}, - // 允许的请求方法 - AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"}, - // 允许的请求头 - AllowHeaders: []string{"Origin", "Content-Type", "Content-Length", "Accept-Encoding", "X-CSRF-Token", "Authorization", "X-LoginRequest-ID"}, - // 允许前端读取的响应头 - ExposeHeaders: []string{"Content-Length"}, - // 是否允许携带cookie - AllowCredentials: true, - // 预检请求的缓存时间 - MaxAge: 12 * time.Hour, - })) - zap.L().Info("✅ 配置跨域中间件完成") - - // 注册品种接口 - variety := r.Group("/variety") - { - variety.POST("/create", logic4variety.CreateVarietyHandler) - } - zap.L().Info("✅ 品种接口注册完成") - - // 注册交易所接口 - exchangeGroup := r.Group("/exchange") - { - exchangeGroup.POST("/create", logic4exchange.CreateExchangeHandler) - } - zap.L().Info("✅ 交易所接口注册完成") - - // 记录服务启动日志,监听80端口 - zap.L().Info("✅ 服务启动在80端口") - r.Run(":80") -} diff --git a/backend/asset_assistant/scripts/db-lanuch-entrypoint.sh b/backend/scripts/db-lanuch-entrypoint.sh similarity index 100% rename from backend/asset_assistant/scripts/db-lanuch-entrypoint.sh rename to backend/scripts/db-lanuch-entrypoint.sh diff --git a/backend/asset_assistant/sql/01_uuid_v7_setup.sql b/backend/sql/01_uuid_v7_setup.sql similarity index 100% rename from backend/asset_assistant/sql/01_uuid_v7_setup.sql rename to backend/sql/01_uuid_v7_setup.sql diff --git a/backend/asset_assistant/sql/02_create_function.sql b/backend/sql/02_create_function.sql similarity index 100% rename from backend/asset_assistant/sql/02_create_function.sql rename to backend/sql/02_create_function.sql diff --git a/backend/asset_assistant/sql/03_user.sql b/backend/sql/03_user.sql similarity index 100% rename from backend/asset_assistant/sql/03_user.sql rename to backend/sql/03_user.sql diff --git a/backend/asset_assistant/sql/04_country.sql b/backend/sql/04_country.sql similarity index 100% rename from backend/asset_assistant/sql/04_country.sql rename to backend/sql/04_country.sql diff --git a/backend/asset_assistant/sql/05_exchange.sql b/backend/sql/05_exchange.sql similarity index 100% rename from backend/asset_assistant/sql/05_exchange.sql rename to backend/sql/05_exchange.sql diff --git a/backend/asset_assistant/src/Dockerfile b/backend/src/Dockerfile similarity index 100% rename from backend/asset_assistant/src/Dockerfile rename to backend/src/Dockerfile diff --git a/backend/asset_assistant/src/db/postgres.go b/backend/src/db/postgres.go similarity index 100% rename from backend/asset_assistant/src/db/postgres.go rename to backend/src/db/postgres.go diff --git a/backend/asset_assistant/src/go.mod b/backend/src/go.mod similarity index 100% rename from backend/asset_assistant/src/go.mod rename to backend/src/go.mod diff --git a/backend/asset_assistant/src/go.sum b/backend/src/go.sum similarity index 100% rename from backend/asset_assistant/src/go.sum rename to backend/src/go.sum diff --git a/backend/asset_assistant/src/logger/logger.go b/backend/src/logger/logger.go similarity index 100% rename from backend/asset_assistant/src/logger/logger.go rename to backend/src/logger/logger.go diff --git a/backend/asset_assistant/src/logic/create.go b/backend/src/logic/create.go similarity index 100% rename from backend/asset_assistant/src/logic/create.go rename to backend/src/logic/create.go diff --git a/backend/asset_assistant/src/logic/delete.go b/backend/src/logic/delete.go similarity index 100% rename from backend/asset_assistant/src/logic/delete.go rename to backend/src/logic/delete.go diff --git a/backend/asset_assistant/src/logic/read.go b/backend/src/logic/read.go similarity index 100% rename from backend/asset_assistant/src/logic/read.go rename to backend/src/logic/read.go diff --git a/backend/asset_assistant/src/logic/update.go b/backend/src/logic/update.go similarity index 100% rename from backend/asset_assistant/src/logic/update.go rename to backend/src/logic/update.go diff --git a/backend/user/src/logic/login.go b/backend/src/logic4user/login.go similarity index 99% rename from backend/user/src/logic/login.go rename to backend/src/logic4user/login.go index 036ac36..8e96c8a 100644 --- a/backend/user/src/logic/login.go +++ b/backend/src/logic4user/login.go @@ -1,4 +1,4 @@ -package logic +package logic4user import ( "database/sql" diff --git a/backend/user/src/logic/register.go b/backend/src/logic4user/register.go similarity index 99% rename from backend/user/src/logic/register.go rename to backend/src/logic4user/register.go index fa259af..33f2c9a 100644 --- a/backend/user/src/logic/register.go +++ b/backend/src/logic4user/register.go @@ -1,4 +1,4 @@ -package logic +package logic4user import ( "net/http" diff --git a/backend/asset_assistant/src/main.go b/backend/src/main.go similarity index 65% rename from backend/asset_assistant/src/main.go rename to backend/src/main.go index d0f5b13..669fdf9 100644 --- a/backend/asset_assistant/src/main.go +++ b/backend/src/main.go @@ -50,21 +50,28 @@ func main() { })) zap.L().Info("✅ 配置跨域中间件完成") - // 注册创建国家的接口,POST请求,由logic.CreateHandler处理 - r.POST("/asset_assistant/create", logic.CreateHandler) - zap.L().Info("✅ 创建接口注册完成: POST /asset_assistant/create") + // 注册品种接口 + variety := r.Group("/user") + { + variety.POST("/create", logic4variety.CreateVarietyHandler) + } + zap.L().Info("✅ 用户接口注册完成") - // 注册读取国家的接口,POST请求,由logic.ReadHandler - r.POST("/asset_assistant/read", logic.ReadHandler) - zap.L().Info("✅ 读取接口注册完成: POST /asset_assistant/read") + // // 注册创建国家的接口,POST请求,由logic.CreateHandler处理 + // r.POST("/asset_assistant/create", logic.CreateHandler) + // zap.L().Info("✅ 创建接口注册完成: POST /asset_assistant/create") - // 注册更新国家的接口,POST请求,由logic.UpdateHandler - r.POST("/asset_assistant/update", logic.UpdateHandler) - zap.L().Info("✅ 更新接口注册完成: POST /asset_assistant/update") + // // 注册读取国家的接口,POST请求,由logic.ReadHandler + // r.POST("/asset_assistant/read", logic.ReadHandler) + // zap.L().Info("✅ 读取接口注册完成: POST /asset_assistant/read") + + // // 注册更新国家的接口,POST请求,由logic.UpdateHandler + // r.POST("/asset_assistant/update", logic.UpdateHandler) + // zap.L().Info("✅ 更新接口注册完成: POST /asset_assistant/update") - // 注册删除国家的接口,POST请求,由logic.DeleteHandler处理 - r.POST("/asset_assistant/delete", logic.DeleteHandler) - zap.L().Info("✅ 删除接口注册完成: POST /asset_assistant/delete") + // // 注册删除国家的接口,POST请求,由logic.DeleteHandler处理 + // r.POST("/asset_assistant/delete", logic.DeleteHandler) + // zap.L().Info("✅ 删除接口注册完成: POST /asset_assistant/delete") // 记录服务启动日志,监听80端口 zap.L().Info("✅ 服务启动在80端口") diff --git a/backend/user/.env b/backend/user/.env deleted file mode 100644 index a8b6a3f..0000000 --- a/backend/user/.env +++ /dev/null @@ -1,21 +0,0 @@ -# 数据库配置 -DB_USER=postgres -DB_PASSWORD=postgres12341234 -DB_NAME=postgres -DB_PORT=5432 -DB_SSL_MODE=disable -DB_MAX_OPEN_CONNS=25 -DB_MAX_IDLE_CONNS=25 -DB_TIMEOUT=30s - -# 时区配置 -TZ=Asia/Shanghai - -# 网关端口 -PORT=80 - -# 日志配置 -LOG_LEVEL=info - -# Gin模式 (debug/release/test) -GIN_MODE=debug \ No newline at end of file diff --git a/backend/user/deploy.sh b/backend/user/deploy.sh deleted file mode 100644 index 3a58c7a..0000000 --- a/backend/user/deploy.sh +++ /dev/null @@ -1,86 +0,0 @@ -#!/bin/bash -set -euo pipefail # 更严格的错误检查:未定义变量报错、管道错误传递 - -# 定义日志函数(带时间戳和级别) -log_info() { - echo "[$(date +'%Y-%m-%d %H:%M:%S')] [INFO] $1" -} - -log_warn() { - echo "[$(date +'%Y-%m-%d %H:%M:%S')] [WARN] $1" >&2 -} - -log_error() { - echo "[$(date +'%Y-%m-%d %H:%M:%S')] [ERROR] $1" >&2 -} - -# 定义配置常量(等号两侧无空格!集中管理,便于修改) -IMAGE_NAME="user-api" -IMAGE_TAG="1.0.0" -FULL_IMAGE="${IMAGE_NAME}:${IMAGE_TAG}" -COMPOSE_PROJECT_NAME="user_service" -DOCKER_COMPOSE_FILE="./docker-compose.yaml" # ✅ 关键修复:等号两侧无空格 -SRC_DIR="./src" -DOCKERFILE_PATH="${SRC_DIR}/Dockerfile" - -# 检查目录和文件存在性的通用函数 -check_exists() { - local path="$1" # 变量引用加引号,避免路径含空格报错 - local type="$2" # "file" 或 "dir" - local desc="$3" - - if [ "$type" = "file" ] && [ ! -f "$path" ]; then - log_error "缺失必要文件: $desc ($path)" - exit 1 - elif [ "$type" = "dir" ] && [ ! -d "$path" ]; then - log_error "缺失必要目录: $desc ($path)" - exit 1 - fi -} - -log_info "===== 开始执行构建脚本 =====" - -# 前置检查:确保必要文件和目录存在 -check_exists "$DOCKER_COMPOSE_FILE" "file" "docker-compose配置文件" -check_exists "$SRC_DIR" "dir" "源代码目录" -check_exists "$DOCKERFILE_PATH" "file" "Dockerfile" - -# 步骤1:停止docker-compose服务(变量引用加引号,兼容路径含空格) -log_info "开始停止编排服务: ${COMPOSE_PROJECT_NAME}" -if docker-compose -f "$DOCKER_COMPOSE_FILE" -p "$COMPOSE_PROJECT_NAME" down; then - log_info "编排服务 ${COMPOSE_PROJECT_NAME} 已成功停止" -else - log_warn "编排服务 ${COMPOSE_PROJECT_NAME} 停止失败或未运行,继续执行后续步骤" -fi - -# 步骤2:删除现有镜像(忽略不存在的情况) -log_info "尝试删除现有镜像: ${FULL_IMAGE}" -if sudo docker rmi -f "${FULL_IMAGE}" >/dev/null 2>&1; then - log_info "镜像 ${FULL_IMAGE} 删除成功" -else - log_warn "镜像 ${FULL_IMAGE} 不存在或无法删除,跳过删除步骤" -fi - -# 步骤3:构建新镜像(切换到src目录,避免路径问题) -log_info "开始构建新镜像: ${FULL_IMAGE}(Dockerfile位于${DOCKERFILE_PATH})" -if cd "$SRC_DIR" && sudo docker build -t "${FULL_IMAGE}" -f Dockerfile .; then - log_info "镜像 ${FULL_IMAGE} 构建成功" -else - log_error "镜像 ${FULL_IMAGE} 构建失败" - exit 1 -fi - -# 步骤4:启动docker-compose服务(变量引用加引号) -log_info "开始启动编排服务: ${COMPOSE_PROJECT_NAME}" -cd .. -if docker-compose -f "$DOCKER_COMPOSE_FILE" -p "$COMPOSE_PROJECT_NAME" up -d; then - log_info "编排服务 ${COMPOSE_PROJECT_NAME} 已成功启动" - # 额外输出运行状态,提升用户体验 - log_info "当前运行的容器:" - docker-compose -f "$DOCKER_COMPOSE_FILE" -p "$COMPOSE_PROJECT_NAME" ps -else - log_error "编排服务 ${COMPOSE_PROJECT_NAME} 启动失败" - exit 1 -fi - -log_info "===== 构建脚本执行完成 =====" \ No newline at end of file diff --git a/backend/user/dev-test.sh b/backend/user/dev-test.sh deleted file mode 100644 index 05d9214..0000000 --- a/backend/user/dev-test.sh +++ /dev/null @@ -1 +0,0 @@ -docker run -itd --name go_user_dev -v $(pwd)/src:/app -p 20000:80 golang:1.25.0-alpine3.22 \ No newline at end of file diff --git a/backend/user/dev.sh b/backend/user/dev.sh deleted file mode 100644 index dde6ad4..0000000 --- a/backend/user/dev.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash - -# 日志函数 -log_info() { - echo "[$(date +'%Y-%m-%d %H:%M:%S')] [DEV_COMPOSE] $1" -} - -log_error() { - echo "[$(date +'%Y-%m-%d %H:%M:%S')] [DEV_ERROR] $1" >&2 -} - -# 获取脚本所在目录的绝对路径 -SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) -# 拼接得到 docker-compose 文件的绝对路径 -COMPOSE_FILE="$SCRIPT_DIR/docker-compose-dev.yaml" - -log_info "开始启动开发环境docker-compose服务" - -# 检查文件是否存在 -if [ ! -f "$COMPOSE_FILE" ]; then - log_error "未找到docker-compose文件: $COMPOSE_FILE" - exit 1 -fi - -# 启动服务 -log_info "执行命令: sudo docker-compose -f $COMPOSE_FILE up -d" -if sudo docker-compose -f "$COMPOSE_FILE" up -d; then - log_info "开发环境服务启动成功" - # 额外输出运行中的容器信息 - log_info "当前运行的容器:" - sudo docker-compose -f "$COMPOSE_FILE" ps -else - log_error "开发环境服务启动失败" - exit 1 -fi \ No newline at end of file diff --git a/backend/user/docker-compose-dev.yaml b/backend/user/docker-compose-dev.yaml deleted file mode 100644 index e031819..0000000 --- a/backend/user/docker-compose-dev.yaml +++ /dev/null @@ -1,44 +0,0 @@ -services: - postgres: - image: postgres:17.4-alpine - container_name: user_db - restart: always - ports: - - 20001:5432 - entrypoint: - - /scripts/db-lanuch-entrypoint.sh - environment: - POSTGRES_USER: ${DB_USER} - POSTGRES_PASSWORD: ${DB_PASSWORD} - POSTGRES_DB: ${DB_NAME} - TZ: ${TZ} - volumes: - - ./shared_data/user_db:/var/lib/postgresql/data - - ./sql:/docker-entrypoint-initdb.d - - ./scripts:/scripts - networks: - - user-network - user: - image: golang:1.25.0-alpine3.22 - container_name: user_api - restart: always - ports: - - 20000:80 - depends_on: - - postgres - networks: - - user-network - environment: - DB_HOST: postgres - DB_PORT: ${DB_PORT} - DB_USER: ${DB_USER} - DB_PASSWORD: ${DB_PASSWORD} - DB_NAME: ${DB_NAME} - TZ: ${TZ} - volumes: - - ./src:/app - command: sh -c "cd /app && go mod tidy && go run main.go" -networks: - user-network: - driver: bridge -volumes: {} diff --git a/backend/user/docker-compose.yaml b/backend/user/docker-compose.yaml deleted file mode 100644 index cadd96e..0000000 --- a/backend/user/docker-compose.yaml +++ /dev/null @@ -1,44 +0,0 @@ -services: - postgres: - image: postgres:17.4-alpine - container_name: user_db - restart: always - ports: - - 20001:5432 - entrypoint: - - /scripts/db-lanuch-entrypoint.sh - environment: - POSTGRES_USER: ${DB_USER} - POSTGRES_PASSWORD: ${DB_PASSWORD} - POSTGRES_DB: ${DB_NAME} - TZ: ${TZ} - volumes: - - ./shared_data/user_db:/var/lib/postgresql/data - - ./sql:/docker-entrypoint-initdb.d - - ./scripts:/scripts - networks: - - user-network - user: - image: user-api:1.0.0 - container_name: user_api - restart: always - ports: - - 20000:80 - depends_on: - - postgres - networks: - - user-network - environment: - DB_HOST: postgres - DB_PORT: ${DB_PORT} - DB_USER: ${DB_USER} - DB_PASSWORD: ${DB_PASSWORD} - DB_NAME: ${DB_NAME} - TZ: ${TZ} - volumes: - # 挂载添加日志目录挂载,将容器内日志日志目录映射到宿主机的 ./logs 目录 - - ./logs:/app/logs # 假设代码中日志存储路径为 /app/logs -networks: - user-network: - driver: bridge -volumes: {} diff --git a/backend/user/scripts/db-lanuch-entrypoint.sh b/backend/user/scripts/db-lanuch-entrypoint.sh deleted file mode 100755 index a4a8ddb..0000000 --- a/backend/user/scripts/db-lanuch-entrypoint.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/bin/sh -set -e - -# 日志函数(带时间戳) -log_info() { - echo "[$(date +'%Y-%m-%d %H:%M:%S')] [DB_INIT] $1" -} - -log_error() { - echo "[$(date +'%Y-%m-%d %H:%M:%S')] [DB_ERROR] $1" >&2 -} - -# 1. 启动PostgreSQL服务 -log_info "启动PostgreSQL服务(后台运行)" -docker-entrypoint.sh postgres & -PG_PID=$! -log_info "PostgreSQL主进程ID: $PG_PID" - -# 2. 等待数据库就绪 -log_info "等待PostgreSQL服务就绪(主机: localhost, 端口: 5432)" -retry_count=0 -max_retries=30 # 最多等待30秒 -until pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB" -h "localhost" -p "5432"; do - retry_count=$((retry_count + 1)) - if [ $retry_count -ge $max_retries ]; then - log_error "等待PostgreSQL超时(超过30秒)" - exit 1 - fi - log_info "数据库未就绪,等待1秒(重试次数: $retry_count)" - sleep 1 -done -log_info "PostgreSQL服务已就绪" - -# 3. 执行SQL脚本 -log_info "开始执行/docker-entrypoint-initdb.d目录下的SQL脚本" -script_count=0 -for script in /docker-entrypoint-initdb.d/*.sql; do - if [ -f "$script" ]; then - script_count=$((script_count + 1)) - log_info "执行脚本 ($script_count): $script" - if psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -h "localhost" -p "5432" -f "$script" --set=ON_ERROR_STOP=1; then - log_info "脚本执行成功: $script" - else - log_error "脚本执行失败: $script" - exit 1 - fi - fi -done - -if [ $script_count -eq 0 ]; then - log_info "未发现需要执行的SQL脚本" -else - log_info "所有SQL脚本执行完成(共$script_count个)" -fi - -# 4. 等待主进程 -log_info "等待PostgreSQL主进程结束(PID: $PG_PID)" -wait $PG_PID -log_info "PostgreSQL进程已退出" \ No newline at end of file diff --git a/backend/user/sql/01_uuid_v7_setup.sql b/backend/user/sql/01_uuid_v7_setup.sql deleted file mode 100644 index b317d1c..0000000 --- a/backend/user/sql/01_uuid_v7_setup.sql +++ /dev/null @@ -1,48 +0,0 @@ --- 切换到目标数据库 -\c postgres; - --- 检查并创建UUID扩展(如果不存在) -CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; - --- 定义检测UUID v7支持的函数 -CREATE OR REPLACE FUNCTION check_uuid_v7_support() RETURNS BOOLEAN AS $$ -DECLARE - test_uuid UUID; -BEGIN - BEGIN - SELECT gen_random_uuid_v7() INTO test_uuid; - RETURN TRUE; - EXCEPTION - WHEN undefined_function THEN - RETURN FALSE; - END; -END; -$$ LANGUAGE plpgsql VOLATILE; - --- 创建UUID v7兼容函数(修复UUID格式长度问题) -CREATE OR REPLACE FUNCTION gen_random_uuid_v7() RETURNS uuid AS $$ -DECLARE - unix_ts_ms BIGINT; - rand_a BIGINT; - rand_b BIGINT; - hex_str TEXT; -BEGIN - -- 获取当前毫秒级Unix时间戳 - unix_ts_ms := (EXTRACT(EPOCH FROM NOW()) * 1000)::BIGINT; - - -- 生成随机数(调整随机数范围以确保总长度正确) - rand_a := (random() * (2^20 - 1))::BIGINT; - rand_b := (random() * (2^44 - 1))::BIGINT; -- 从48位调整为44位,减少2个字节 - - -- 组合UUID v7格式(确保总长度为32个十六进制字符) - hex_str := - lpad(to_hex(unix_ts_ms >> 12), 8, '0') || - lpad(to_hex((unix_ts_ms & 4095) << 4), 4, '0') || - '7' || lpad(to_hex(rand_a >> 18), 3, '0') || - lpad(to_hex(8 + (rand_a & 16383) >> 12), 2, '0') || - lpad(to_hex(rand_a & 4095), 3, '0') || - lpad(to_hex(rand_b), 11, '0'); -- 从12位调整为11位 - - RETURN hex_str::uuid; -END; -$$ LANGUAGE plpgsql VOLATILE; \ No newline at end of file diff --git a/backend/user/sql/02_create_user_table.sql b/backend/user/sql/02_create_user_table.sql deleted file mode 100644 index f3b0b0e..0000000 --- a/backend/user/sql/02_create_user_table.sql +++ /dev/null @@ -1,105 +0,0 @@ --- 切换到目标数据库 -\c postgres; - -CREATE OR REPLACE FUNCTION update_user_modified_column() -RETURNS TRIGGER AS $$ -BEGIN - NEW.updated_at = CURRENT_TIMESTAMP; - RETURN NEW; -END; -$$ LANGUAGE plpgsql VOLATILE; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'user') THEN - CREATE TABLE "user" ( -- user是关键字,用双引号包裹 - id UUID DEFAULT gen_random_uuid_v7() PRIMARY KEY NOT NULL, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP - ); - CREATE TRIGGER update_user_updated_at - BEFORE UPDATE ON "user" - FOR EACH ROW - EXECUTE FUNCTION update_user_modified_column(); - - RAISE NOTICE 'created user table and trigger'; - ELSE - RAISE NOTICE 'user table already exists'; - END IF; - - IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'account') THEN - CREATE TABLE account ( - id UUID DEFAULT gen_random_uuid_v7() PRIMARY KEY NOT NULL, - user_id UUID NOT NULL, - account VARCHAR NOT NULL, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP - ); - CREATE TRIGGER update_account_updated_at - BEFORE UPDATE ON "account" - FOR EACH ROW - EXECUTE FUNCTION update_account_modified_column(); - - RAISE NOTICE 'created account table and trigger'; - ELSE - RAISE NOTICE 'account table already exists'; - END IF; - - IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'password') THEN - CREATE TABLE password ( - id UUID DEFAULT gen_random_uuid_v7() PRIMARY KEY NOT NULL, - user_id UUID NOT NULL, - password VARCHAR NOT NULL, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP - ); - CREATE TRIGGER update_password_updated_at - BEFORE UPDATE ON "password" - FOR EACH ROW - EXECUTE FUNCTION update_password_modified_column(); - - RAISE NOTICE 'created password table and trigger'; - ELSE - RAISE NOTICE 'password table already exists'; - END IF; -END $$; - -DO $$ -DECLARE - view_exists BOOLEAN; -BEGIN - -- 检查视图是否已存在 - SELECT EXISTS ( - SELECT 1 FROM information_schema.views - WHERE table_name = 'user_info_view' - ) INTO view_exists; - - -- 创建或更新视图 - CREATE OR REPLACE VIEW user_info_view AS - SELECT - u.id AS user_id, - ua.account AS account, - up.password AS password, - u.deleted AS deleted - FROM - "user" u - JOIN - account ua ON u.id = ua.user_id - JOIN - password up ON u.id = up.user_id - WHERE - u.deleted = FALSE; - - -- 根据视图是否已存在输出不同提示 - IF view_exists THEN - RAISE NOTICE '视图 user_info_view 已更新'; - ELSE - RAISE NOTICE '视图 user_info_view 已创建'; - END IF; -EXCEPTION - WHEN OTHERS THEN - RAISE NOTICE '处理视图时发生错误: %', SQLERRM; -END $$; diff --git a/backend/user/sql/03_create_account_table.sql b/backend/user/sql/03_create_account_table.sql deleted file mode 100644 index 803233f..0000000 --- a/backend/user/sql/03_create_account_table.sql +++ /dev/null @@ -1,32 +0,0 @@ --- 切换到目标数据库 -\c postgres; - -CREATE OR REPLACE FUNCTION update_account_modified_column() -RETURNS TRIGGER AS $$ -BEGIN - NEW.updated_at = CURRENT_TIMESTAMP; - RETURN NEW; -END; -$$ LANGUAGE plpgsql VOLATILE; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'account') THEN - CREATE TABLE account ( - id UUID DEFAULT gen_random_uuid_v7() PRIMARY KEY NOT NULL, - user_id UUID NOT NULL, - account VARCHAR NOT NULL, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP - ); - CREATE TRIGGER update_account_updated_at - BEFORE UPDATE ON "account" - FOR EACH ROW - EXECUTE FUNCTION update_account_modified_column(); - - RAISE NOTICE 'Created account table and trigger'; - ELSE - RAISE NOTICE 'account table already exists'; - END IF; -END $$; \ No newline at end of file diff --git a/backend/user/sql/04_create_password_table.sql b/backend/user/sql/04_create_password_table.sql deleted file mode 100644 index 1d82da0..0000000 --- a/backend/user/sql/04_create_password_table.sql +++ /dev/null @@ -1,32 +0,0 @@ --- 切换到目标数据库 -\c postgres; - -CREATE OR REPLACE FUNCTION update_password_modified_column() -RETURNS TRIGGER AS $$ -BEGIN - NEW.updated_at = CURRENT_TIMESTAMP; - RETURN NEW; -END; -$$ LANGUAGE plpgsql VOLATILE; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'password') THEN - CREATE TABLE password ( - id UUID DEFAULT gen_random_uuid_v7() PRIMARY KEY NOT NULL, - user_id UUID NOT NULL, - password VARCHAR NOT NULL, - deleted BOOLEAN NOT NULL DEFAULT FALSE, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP - ); - CREATE TRIGGER update_password_updated_at - BEFORE UPDATE ON "password" - FOR EACH ROW - EXECUTE FUNCTION update_password_modified_column(); - - RAISE NOTICE 'Created password table and trigger'; - ELSE - RAISE NOTICE 'password table already exists'; - END IF; -END $$; \ No newline at end of file diff --git a/backend/user/sql/05_create_info_view.sql b/backend/user/sql/05_create_info_view.sql deleted file mode 100644 index ecfa2a1..0000000 --- a/backend/user/sql/05_create_info_view.sql +++ /dev/null @@ -1,38 +0,0 @@ -\c postgres; - -DO $$ -DECLARE - view_exists BOOLEAN; -BEGIN - -- 检查视图是否已存在 - SELECT EXISTS ( - SELECT 1 FROM information_schema.views - WHERE table_name = 'user_info_view' - ) INTO view_exists; - - -- 创建或更新视图 - CREATE OR REPLACE VIEW user_info_view AS - SELECT - u.id AS user_id, - ua.account AS account, - up.password AS password, - u.deleted AS deleted - FROM - "user" u - JOIN - account ua ON u.id = ua.user_id - JOIN - password up ON u.id = up.user_id - WHERE - u.deleted = FALSE; - - -- 根据视图是否已存在输出不同提示 - IF view_exists THEN - RAISE NOTICE '视图 user_info_view 已更新'; - ELSE - RAISE NOTICE '视图 user_info_view 已创建'; - END IF; -EXCEPTION - WHEN OTHERS THEN - RAISE NOTICE '处理视图时发生错误: %', SQLERRM; -END $$; diff --git a/backend/user/src/db/postgres.go b/backend/user/src/db/postgres.go deleted file mode 100644 index 40681a3..0000000 --- a/backend/user/src/db/postgres.go +++ /dev/null @@ -1,54 +0,0 @@ -package db - -import ( - "database/sql" - "fmt" - "os" - "time" - - _ "github.com/lib/pq" - "go.uber.org/zap" -) - -var DB *sql.DB - -// 初始化数据库连接 -func Init() { - // 从环境变量获取数据库配置 - dbHost := os.Getenv("DB_HOST") - dbPort := os.Getenv("DB_PORT") - dbUser := os.Getenv("DB_USER") - dbPassword := os.Getenv("DB_PASSWORD") - dbName := os.Getenv("DB_NAME") - zap.L().Info( - "💡 读取数据库配置", - zap.String("host", dbHost), - zap.String("port", dbPort), - zap.String("user", dbUser), - zap.String("dbname", dbName), - ) - - // 构建数据库连接字符串 - connStr := fmt.Sprintf( - "host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", - dbHost, dbPort, dbUser, dbPassword, dbName, - ) - - var err error - DB, err = sql.Open("postgres", connStr) - if err != nil { - zap.L().Panic("❌ 无法连接数据库", zap.Error(err)) - } - - // 设置连接池参数 - DB.SetMaxOpenConns(100) // 最大打开连接数 - DB.SetMaxIdleConns(20) // 最大空闲连接数 - DB.SetConnMaxLifetime(time.Hour) // 连接最大存活时间 - - // 验证数据库连接 - if err := DB.Ping(); err != nil { - zap.L().Panic("❌ 数据库连接失败", zap.Error(err)) - } - - zap.L().Info("✅ 数据库连接验证成功") -} \ No newline at end of file diff --git a/backend/user/src/dockerfile b/backend/user/src/dockerfile deleted file mode 100644 index a54892c..0000000 --- a/backend/user/src/dockerfile +++ /dev/null @@ -1,38 +0,0 @@ -# ==================== 第一阶段:构建Go程序(构建阶段)==================== -# 使用官方Go镜像作为构建基础,选择与项目匹配的Go版本(示例用1.25.0,可根据实际调整) -FROM golang:1.25.0-alpine3.22 AS builder - -# 设置工作目录(容器内的目录,规范文件位置) -WORKDIR /app - -# 复制go.mod和go.sum(先复制依赖文件,利用Docker缓存机制,避免每次代码变动都重新下载依赖) -COPY go.mod go.sum ./ - -# 下载项目依赖(仅当go.mod/go.sum变动时才会重新执行) -RUN go mod download - -# 复制整个项目代码到工作目录 -COPY . . - -# 构建Go程序: -# - CGO_ENABLED=0:禁用CGO,生成静态链接的二进制文件(避免依赖系统库,保证镜像兼容性) -# - -o app:指定输出二进制文件名为app -# - ./main.go:指定入口文件 -RUN CGO_ENABLED=0 GOOS=linux go build -o app ./main.go - - -# ==================== 第二阶段:运行程序(运行阶段)==================== -# 使用轻量级的alpine镜像(仅5MB左右,大幅减小最终镜像体积) -FROM alpine:3.19 - -# 设置工作目录 -WORKDIR /app - -# 从构建阶段复制编译好的二进制文件到当前镜像(仅复制最终产物,减小体积) -COPY --from=builder /app/app ./ - -# 暴露程序运行端口(与代码中一致) -EXPOSE 80 - -# 容器启动时执行的命令:运行二进制文件 -CMD ["./app"] \ No newline at end of file diff --git a/backend/user/src/go.mod b/backend/user/src/go.mod deleted file mode 100644 index 2d56f24..0000000 --- a/backend/user/src/go.mod +++ /dev/null @@ -1,57 +0,0 @@ -module user - -go 1.25.0 - -require ( - github.com/gin-contrib/cors v1.7.6 - github.com/gin-gonic/gin v1.11.0 - github.com/google/uuid v1.6.0 - github.com/lib/pq v1.10.9 - github.com/spf13/viper v1.21.0 - go.uber.org/zap v1.27.0 - golang.org/x/crypto v0.43.0 - gopkg.in/natefinch/lumberjack.v2 v2.2.1 -) - -require ( - github.com/bytedance/sonic v1.14.0 // indirect - github.com/bytedance/sonic/loader v0.3.0 // indirect - github.com/cloudwego/base64x v0.1.6 // indirect - github.com/fsnotify/fsnotify v1.9.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.9 // indirect - github.com/gin-contrib/sse v1.1.0 // indirect - github.com/go-playground/locales v0.14.1 // indirect - github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.27.0 // indirect - github.com/go-viper/mapstructure/v2 v2.4.0 // indirect - github.com/goccy/go-json v0.10.5 // indirect - github.com/goccy/go-yaml v1.18.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.3.0 // indirect - github.com/leodido/go-urn v1.4.0 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/quic-go/qpack v0.5.1 // indirect - github.com/quic-go/quic-go v0.54.0 // indirect - github.com/sagikazarmark/locafero v0.11.0 // indirect - github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect - github.com/spf13/afero v1.15.0 // indirect - github.com/spf13/cast v1.10.0 // indirect - github.com/spf13/pflag v1.0.10 // indirect - github.com/subosito/gotenv v1.6.0 // indirect - github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.3.0 // indirect - go.uber.org/mock v0.5.0 // indirect - go.uber.org/multierr v1.10.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/arch v0.20.0 // indirect - golang.org/x/mod v0.28.0 // indirect - golang.org/x/net v0.45.0 // indirect - golang.org/x/sync v0.17.0 // indirect - golang.org/x/sys v0.37.0 // indirect - golang.org/x/text v0.30.0 // indirect - golang.org/x/tools v0.37.0 // indirect - google.golang.org/protobuf v1.36.9 // indirect -) diff --git a/backend/user/src/go.sum b/backend/user/src/go.sum deleted file mode 100644 index 0117005..0000000 --- a/backend/user/src/go.sum +++ /dev/null @@ -1,133 +0,0 @@ -github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ= -github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA= -github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= -github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= -github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= -github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= -github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= -github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= -github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok= -github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY= -github.com/gin-contrib/cors v1.7.6/go.mod h1:Ulcl+xN4jel9t1Ry8vqph23a60FwH9xVLd+3ykmTjOk= -github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= -github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= -github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= -github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls= -github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= -github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= -github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4= -github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= -github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= -github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= -github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= -github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= -github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= -github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= -github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= -github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= -github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg= -github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= -github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= -github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= -github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= -github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= -github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= -github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= -github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= -github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= -github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= -github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= -github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= -github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= -github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= -go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= -go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= -go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c= -golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= -golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= -golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= -golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= -golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= -golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= -golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= -golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= -golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= -google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= -google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= -gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/backend/user/src/logger/logger.go b/backend/user/src/logger/logger.go deleted file mode 100644 index 8021baa..0000000 --- a/backend/user/src/logger/logger.go +++ /dev/null @@ -1,86 +0,0 @@ -package logger - -import ( - "log" - "os" - "time" - - "github.com/spf13/viper" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" - "gopkg.in/natefinch/lumberjack.v2" -) - -var shanghaiLoc *time.Location - -func init() { - var err error - shanghaiLoc, err = time.LoadLocation("Asia/Shanghai") - if err != nil { - // 尝试备选时区名称 - shanghaiLoc, err = time.LoadLocation("PRC") - if err != nil { - // 若仍失败,手动设置东八区偏移 - shanghaiLoc = time.FixedZone("CST", 8*3600) - log.Printf("警告:加载时区失败,使用手动东八区偏移: %v", err) - } - } -} - -// Init 初始化日志(依赖配置文件已加载) -func Init() { - // 日志级别转换 - level := zap.InfoLevel - switch viper.GetString("logger.level") { - case "debug": - level = zap.DebugLevel - case "warn": - level = zap.WarnLevel - case "error": - level = zap.ErrorLevel - } - - // 日志轮转配置(lumberjack) - hook := lumberjack.Logger{ - Filename: viper.GetString("logger.path") + "logs/app.log", // 日志文件路径 - MaxSize: viper.GetInt("logger.max_size"), // 单个文件最大大小(MB) - MaxBackups: viper.GetInt("logger.max_backup"), // 最大备份数 - MaxAge: viper.GetInt("logger.max_age"), // 最大保留天数 - Compress: true, // 是否压缩 - } - - // 编码器配置 - encoderConfig := zapcore.EncoderConfig{ - TimeKey: "time", - LevelKey: "level", - NameKey: "logger", - CallerKey: "caller", - MessageKey: "msg", - StacktraceKey: "stacktrace", - LineEnding: zapcore.DefaultLineEnding, - EncodeLevel: zapcore.CapitalLevelEncoder, // 日志级别大写(DEBUG/INFO) - EncodeTime: customTimeEncoder, // 自定义时间格式 - EncodeDuration: zapcore.SecondsDurationEncoder, - EncodeCaller: zapcore.ShortCallerEncoder, // 精简调用者路径 - } - - // 输出配置(控制台+文件) - core := zapcore.NewTee( - zapcore.NewCore(zapcore.NewConsoleEncoder(encoderConfig), zapcore.AddSync(os.Stdout), level), - zapcore.NewCore(zapcore.NewJSONEncoder(encoderConfig), zapcore.AddSync(&hook), level), - ) - - // 创建logger实例(开启调用者信息和堆栈跟踪) - logger := zap.New(core, zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel)) - zap.ReplaceGlobals(logger) - - zap.L().Info("✅ 日志初始化成功", zap.String("level", level.String())) -} - -// customTimeEncoder 自定义时间格式(强制东八区,若加载失败则使用UTC) -func customTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { - // 使用提前初始化好的时区,避免每次调用都加载 - beijingTime := t.In(shanghaiLoc) - // 格式化输出 - enc.AppendString(beijingTime.Format("2006-01-02 15:04:05.000")) -} \ No newline at end of file diff --git a/backend/user/src/main.go b/backend/user/src/main.go deleted file mode 100644 index cc531ac..0000000 --- a/backend/user/src/main.go +++ /dev/null @@ -1,53 +0,0 @@ -package main - -import ( - "user/db" - "user/logger" - "user/logic" - - "github.com/gin-contrib/cors" - "github.com/gin-gonic/gin" - _ "github.com/lib/pq" - "go.uber.org/zap" - "time" -) - -func main() { - logger.Init() - zap.L().Info("🚀 用户服务初始化") - zap.L().Info("⌛️ 数据库初始化开始") - db.Init() - defer db.DB.Close() // 应用退出时关闭连接 - zap.L().Info("✅ 数据库初始化成功") - - gin.SetMode(gin.ReleaseMode) - r := gin.Default() - - // 配置跨域中间件 - r.Use(cors.New(cors.Config{ - // 允许所有来源(生产环境建议指定具体域名) - AllowOrigins: []string{"*"}, - // 允许的请求方法 - AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"}, - // 允许的请求头 - AllowHeaders: []string{"Origin", "Content-Type", "Content-Length", "Accept-Encoding", "X-CSRF-Token", "Authorization", "X-LoginRequest-ID"}, - // 允许前端读取的响应头 - ExposeHeaders: []string{"Content-Length"}, - // 是否允许携带cookie - AllowCredentials: true, - // 预检请求的缓存时间 - MaxAge: 12 * time.Hour, - })) - zap.L().Info("✅ 配置跨域中间件完成") - - // 登录接口 - r.POST("/user/login", logic.LoginHandler) - zap.L().Info("✅ 登录接口注册完成: POST /user/login") - // 注册接口 - r.POST("/user/register", logic.RegisterHandler) - zap.L().Info("✅ 注册接口注册完成: POST /user/register") - - // 启动服务,监听80端口 - zap.L().Info("✅ 服务启动在80端口") - r.Run(":80") -} \ No newline at end of file