diff --git a/create/create.sh b/create/create.sh index 1700524..6c3c1e9 100644 --- a/create/create.sh +++ b/create/create.sh @@ -1,9 +1,9 @@ #!/bin/bash -# create.sh - 启动Python容器执行create.py脚本 +# create.sh - 启动Python容器执行create_table.py脚本 set -e # 遇到错误立即退出 -echo "🚀 启动Python容器执行create.py..." +echo "🚀 启动Python容器执行create_table.py..." # 获取脚本所在目录的绝对路径 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -34,8 +34,8 @@ docker run --rm \ echo '🔧 安装依赖(如果需要)...' pip install --quiet --no-cache-dir psycopg2-binary >/dev/null 2>&1 || true - echo '⚙️ 执行 create.py...' - python create/create.py + echo '⚙️ 执行 create_table.py...' + python create/create_table.py echo '' echo '✅ 执行完成!' diff --git a/create/create.py b/create/create_table.py similarity index 100% rename from create/create.py rename to create/create_table.py diff --git a/docker-compose.yaml b/docker-compose.yaml index 6873e04..14060f7 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -23,7 +23,7 @@ services: cn_futures_trading_records: build: context: services/cn_futures_trading_records/src - dockerfile: Dockerfile # 如果文件名不是 Dockerfile 再写 + dockerfile: Dockerfile image: ai-trading-api/cn_futures_trading_records:1.0.0 container_name: api_cn_futures_trading_records restart: always diff --git a/services/cn_futures_trading_records/README.md b/services/cn_futures_trading_records/README.md deleted file mode 100644 index e0129c9..0000000 --- a/services/cn_futures_trading_records/README.md +++ /dev/null @@ -1,26 +0,0 @@ -curl -X POST http://127.0.0.1:20001/cn_futures_trading_record/open \ - -H "Content-Type: application/json" \ - -H "X-TradingRecordsRequest-ID: $(uuidgen)" \ - -d '{ - "payload": { - "open_year": 2025, - "open_month": 6, - "open_day": 19, - "symbol": "RB", - "contract": "2510", - "direction": "long", - "open_price": 3800, - "open_fee": 12.5, - "close_year": 2025, - "close_month": 6, - "close_day": 20, - "close_price": 3850, - "close_fee": 12.5, - "price_diff": 50, - "min_tick": 1, - "tick_price": 10, - "diff_pnl": 500, - "total_fee": 25, - "close_pnl": 475 - } - }' \ No newline at end of file diff --git a/services/cn_futures_trading_records/dev.sh b/services/cn_futures_trading_records/dev.sh deleted file mode 100644 index 6436fd8..0000000 --- a/services/cn_futures_trading_records/dev.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" - -sudo docker stop go_cn_futures_trading_records_dev -sudo docker rm go_cn_futures_trading_records_dev -sudo docker run -itd \ - --name go_cn_futures_trading_records_dev \ - -v "$SCRIPT_DIR/:/app" \ - -p 20010:80 \ - golang:1.25.0-alpine3.22 \ No newline at end of file diff --git a/services/cn_futures_trading_records/src/Dockerfile b/services/cn_futures_trading_records/src/Dockerfile deleted file mode 100644 index 9ad31e5..0000000 --- a/services/cn_futures_trading_records/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 - - -# ==================== 第二阶段:运行程序(运行阶段)==================== -# 使用轻量级的scratch(大幅减小最终镜像体积) -FROM scratch - -# 设置工作目录 -WORKDIR /app - -# 从构建阶段复制编译好的二进制文件到当前镜像(仅复制最终产物,减小体积) -COPY --from=builder /app/app ./ - -# 暴露程序运行端口(与代码中一致) -EXPOSE 80 - -# 容器启动时执行的命令:运行二进制文件 -CMD ["./app"] \ No newline at end of file diff --git a/services/cn_futures_trading_records/src/crud/create.go b/services/cn_futures_trading_records/src/crud/create.go deleted file mode 100644 index 3de0bf5..0000000 --- a/services/cn_futures_trading_records/src/crud/create.go +++ /dev/null @@ -1,210 +0,0 @@ -package crud - -import ( - "bytes" - "encoding/csv" - "encoding/json" - "fmt" - "net/http" - "reflect" - "strconv" - "strings" - "time" - - "trading_records/infra" - "trading_records/model" - - "github.com/gin-gonic/gin" - "github.com/gocarina/gocsv" - "github.com/google/uuid" - "go.uber.org/zap" -) - -/* ---------- 包级常量:带 csv 标签的字段个数 ---------- */ -var payloadCSVCols int - -func init() { - typ := reflect.TypeFor[model.Payload]() - for i := 0; i < typ.NumField(); i++ { - if _, ok := typ.Field(i).Tag.Lookup("csv"); ok { - payloadCSVCols++ - } - } -} - -/* ---------- 单条创建 ---------- */ -type TradingRecordsCreateRequest struct { - Payload model.Payload `json:"payload" binding:"required"` -} - -type TradingRecordsCreateResponse struct { - Success bool `json:"success"` - Message string `json:"message"` - Data TradingRecordsCreateData `json:"data"` -} - -type TradingRecordsCreateData struct { - RecordID string `json:"record_id"` -} - -func CreateHandler(c *gin.Context) { - start := time.Now() - reqID := c.Request.Header.Get("X-TradingRecordsRequest-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 TradingRecordsCreateRequest - if err := c.ShouldBindJSON(&req); err != nil { - zap.L().Warn("⚠️ 请求参数验证失败", zap.String("req_id", reqID), zap.Error(err)) - c.JSON(http.StatusOK, TradingRecordsCreateResponse{Success: false, Message: "请求参数错误,请检查字段格式与必填项"}) - return - } - - zap.L().Debug("✅ 请求参数验证通过", zap.String("req_id", reqID), zap.Any("payload", req.Payload)) - - tx, err := infra.DB.Begin() - if err != nil { - zap.L().Error("❌ 事务开启失败", zap.String("req_id", reqID), zap.Error(err)) - c.JSON(http.StatusOK, TradingRecordsCreateResponse{Success: false, Message: "系统错误,请稍后重试"}) - return - } - defer func() { - if r := recover(); r != nil { - _ = tx.Rollback() - zap.L().Error("💥 事务处理发生panic", zap.String("req_id", reqID), zap.Any("recover", r)) - c.JSON(http.StatusOK, TradingRecordsCreateResponse{Success: false, Message: "系统错误,请稍后重试"}) - } - }() - - payloadBytes, err := json.Marshal(req.Payload) - if err != nil { - tx.Rollback() - zap.L().Error("❌ payload 序列化失败", zap.String("req_id", reqID), zap.Error(err)) - c.JSON(http.StatusOK, TradingRecordsCreateResponse{Success: false, Message: "内部数据转换错误"}) - return - } - - var recordID string - err = tx.QueryRow(`INSERT INTO trading_records (payload) VALUES ($1) RETURNING id`, payloadBytes).Scan(&recordID) - if err != nil { - tx.Rollback() - zap.L().Error("❌ 插入失败", zap.String("req_id", reqID), zap.Error(err)) - c.JSON(http.StatusOK, TradingRecordsCreateResponse{Success: false, Message: "保存交易记录失败"}) - return - } - - if err := tx.Commit(); err != nil { - tx.Rollback() - zap.L().Error("❌ 事务提交失败", zap.String("req_id", reqID), zap.String("record_id", recordID), zap.Error(err)) - c.JSON(http.StatusOK, TradingRecordsCreateResponse{Success: false, Message: "数据提交失败,请稍后重试"}) - return - } - - zap.L().Info("✅ 交易记录创建完成", zap.String("req_id", reqID), zap.String("record_id", recordID), zap.Duration("duration", time.Since(start))) - c.JSON(http.StatusOK, TradingRecordsCreateResponse{Success: true, Message: "创建成功", Data: TradingRecordsCreateData{RecordID: recordID}}) -} - -/* ---------- 批量创建 ---------- */ -type TradingRecordsBatchCreateRequest struct { - Rows [][]string `json:"rows" binding:"required,min=1,max=5000"` -} - -type TradingRecordsBatchCreateResponse struct { - Success bool `json:"success"` - Message string `json:"message"` - RecordIDs []string `json:"record_ids"` -} - -func CreateBatchHandler(c *gin.Context) { - start := time.Now() - reqID := c.Request.Header.Get("X-TradingRecordsRequest-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 TradingRecordsBatchCreateRequest - if err := c.ShouldBindJSON(&req); err != nil { - zap.L().Warn("⚠️ 请求参数验证失败", zap.String("req_id", reqID), zap.Error(err)) - c.JSON(http.StatusOK, TradingRecordsBatchCreateResponse{Success: false, Message: "请求参数错误,请检查字段格式与数量"}) - return - } - - zap.L().Debug("✅ 请求参数验证通过", zap.String("req_id", reqID), zap.Int("row_count", len(req.Rows))) - - tx, err := infra.DB.Begin() - if err != nil { - zap.L().Error("❌ 事务开启失败", zap.String("req_id", reqID), zap.Error(err)) - c.JSON(http.StatusOK, TradingRecordsBatchCreateResponse{Success: false, Message: "系统错误,请稍后重试"}) - return - } - defer func() { - if r := recover(); r != nil { - _ = tx.Rollback() - zap.L().Error("💥 事务处理发生panic", zap.String("req_id", reqID), zap.Any("recover", r)) - c.JSON(http.StatusOK, TradingRecordsBatchCreateResponse{Success: false, Message: "系统错误,请稍后重试"}) - } - }() - - stmt, err := tx.Prepare(`INSERT INTO trading_records (payload) VALUES ($1) RETURNING id`) - if err != nil { - tx.Rollback() - zap.L().Error("❌ 预编译语句失败", zap.String("req_id", reqID), zap.Error(err)) - c.JSON(http.StatusOK, TradingRecordsBatchCreateResponse{Success: false, Message: "系统错误,请稍后重试"}) - return - } - defer stmt.Close() - - var recordIDs []string - for idx, row := range req.Rows { - if len(row) != payloadCSVCols { // ✅ 自动随结构体同步 - tx.Rollback() - zap.L().Warn("⚠️ 字段数量不匹配", zap.String("req_id", reqID), zap.Int("row_index", idx), zap.Int("field_count", len(row)), zap.Int("expected", payloadCSVCols)) - c.JSON(http.StatusOK, TradingRecordsBatchCreateResponse{Success: false, Message: "第 " + strconv.Itoa(idx+1) + " 行字段数量不足 " + strconv.Itoa(payloadCSVCols) + " 个"}) - return - } - - // 一次性映射 - var p model.Payload - if err := gocsv.UnmarshalCSV(csv.NewReader(bytes.NewReader([]byte(strings.Join(row, ",")+"\n"))), &p); err != nil { - tx.Rollback() - zap.L().Error("❌ CSV 映射失败", zap.String("req_id", reqID), zap.Int("row_index", idx), zap.Error(err)) - c.JSON(http.StatusOK, TradingRecordsBatchCreateResponse{Success: false, Message: fmt.Sprintf("第 %d 行数据格式错误: %s", idx+1, err.Error())}) - return - } - - payloadBytes, _ := json.Marshal(p) - var id string - if err := stmt.QueryRow(payloadBytes).Scan(&id); err != nil { - tx.Rollback() - zap.L().Error("❌ 单行插入失败", zap.String("req_id", reqID), zap.Int("row_index", idx), zap.Error(err)) - c.JSON(http.StatusOK, TradingRecordsBatchCreateResponse{Success: false, Message: "第 " + strconv.Itoa(idx+1) + " 行插入失败"}) - return - } - recordIDs = append(recordIDs, id) - } - - if err := tx.Commit(); err != nil { - tx.Rollback() - zap.L().Error("❌ 事务提交失败", zap.String("req_id", reqID), zap.Error(err)) - c.JSON(http.StatusOK, TradingRecordsBatchCreateResponse{Success: false, Message: "数据提交失败,请稍后重试"}) - return - } - - zap.L().Info("✅ 交易记录批量创建完成", zap.String("req_id", reqID), zap.Int("inserted_count", len(recordIDs)), zap.Duration("duration", time.Since(start))) - c.JSON(http.StatusOK, TradingRecordsBatchCreateResponse{Success: true, Message: "批量创建成功", RecordIDs: recordIDs}) -} diff --git a/services/cn_futures_trading_records/src/go.mod b/services/cn_futures_trading_records/src/go.mod deleted file mode 100644 index fc78798..0000000 --- a/services/cn_futures_trading_records/src/go.mod +++ /dev/null @@ -1,58 +0,0 @@ -module trading_records - -go 1.25.0 - -require ( - github.com/gin-contrib/cors v1.7.6 - github.com/gin-gonic/gin v1.11.0 - github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1 - 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/services/cn_futures_trading_records/src/go.sum b/services/cn_futures_trading_records/src/go.sum deleted file mode 100644 index 59207b6..0000000 --- a/services/cn_futures_trading_records/src/go.sum +++ /dev/null @@ -1,135 +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/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1 h1:FWNFq4fM1wPfcK40yHE5UO3RUdSNPaBC+j3PokzA6OQ= -github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI= -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/services/cn_futures_trading_records/src/infra/launch.go b/services/cn_futures_trading_records/src/infra/launch.go deleted file mode 100644 index 86a6a62..0000000 --- a/services/cn_futures_trading_records/src/infra/launch.go +++ /dev/null @@ -1,50 +0,0 @@ -package infra - -import ( - "time" - - "github.com/gin-contrib/cors" - "github.com/gin-gonic/gin" - "go.uber.org/zap" -) - -// Launch 初始化基础设施 -func Launch() { - launchLogger() - launchDB() - cnfigGin() -} - -func launchLogger() { - InitLogger() -} - -func launchDB() { - InitDB() - // 程序退出时关闭数据库连接(defer确保在函数退出前执行) - defer DB.Close() -} - -func cnfigGin() { - // 设置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("✅ 配置跨域中间件完成") -} diff --git a/services/cn_futures_trading_records/src/infra/logger.go b/services/cn_futures_trading_records/src/infra/logger.go deleted file mode 100644 index f718db0..0000000 --- a/services/cn_futures_trading_records/src/infra/logger.go +++ /dev/null @@ -1,85 +0,0 @@ -package infra - -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 InitLogger() { - // 日志级别转换 - 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/services/cn_futures_trading_records/src/infra/postgres.go b/services/cn_futures_trading_records/src/infra/postgres.go deleted file mode 100644 index ec4e6fe..0000000 --- a/services/cn_futures_trading_records/src/infra/postgres.go +++ /dev/null @@ -1,53 +0,0 @@ -package infra - -import ( - "database/sql" - "fmt" - "os" - "time" - - _ "github.com/lib/pq" - "go.uber.org/zap" -) - -var DB *sql.DB - -// 初始化数据库连接 -func InitDB() { - // 从环境变量获取数据库配置 - 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/services/cn_futures_trading_records/src/main.go b/services/cn_futures_trading_records/src/main.go deleted file mode 100644 index 6e71510..0000000 --- a/services/cn_futures_trading_records/src/main.go +++ /dev/null @@ -1,35 +0,0 @@ -package main - -import ( - "trading_records/crud" - "trading_records/infra" - - "github.com/gin-gonic/gin" // Gin框架,用于构建HTTP服务 - _ "github.com/lib/pq" // PostgreSQL数据库驱动(下划线表示仅初始化不直接使用) - "go.uber.org/zap" // Zap日志库,用于结构化日志输出 -) - -func main() { - infra.Launch() - configRouter() - startServe() -} - -func configRouter() { - // 创建Gin默认路由器 - r := gin.Default() - // 注册用户接口 - trading := r.Group("/cn_futures_trading_record") - { - trading.POST("/open", crud.CreateHandler) - } - zap.L().Info("✅ 中国期货交易记录接口注册完成") -} - -func startServe() { - // 创建Gin默认路由器 - r := gin.Default() - // 记录服务启动日志,监听80端口 - zap.L().Info("✅ 服务启动在80端口") - r.Run(":80") -} diff --git a/services/cn_futures_trading_records/src/model/payload.go b/services/cn_futures_trading_records/src/model/payload.go deleted file mode 100644 index cebf0fc..0000000 --- a/services/cn_futures_trading_records/src/model/payload.go +++ /dev/null @@ -1,25 +0,0 @@ -package model - -type Payload struct { - Status int `csv:"status" json:"status" binding:"required,min=-1,max=1"` - Market int `csv:"market" json:"market" binding:"required,min=0,max=500"` - OpenYear int `csv:"open_year" json:"open_year" binding:"required,min=1900,max=2200"` - OpenMonth int `csv:"open_month" json:"open_month" binding:"required,min=1,max=12"` - OpenDay int `csv:"open_day" json:"open_day" binding:"required,min=1,max=31"` - Symbol string `csv:"symbol" json:"symbol" binding:"required"` - Contract string `csv:"contract" json:"contract" binding:"required"` - Direction int `csv:"direction" json:"direction" binding:"required,min=-1,max=1"` - OpenPrice float64 `csv:"open_price" json:"open_price" binding:"required"` - OpenFee float64 `csv:"open_fee" json:"open_fee" binding:"required,min=0"` - CloseYear int `csv:"close_year" json:"close_year" binding:"required,min=1900,max=2200"` - CloseMonth int `csv:"close_month" json:"close_month" binding:"required,min=1,max=12"` - CloseDay int `csv:"close_day" json:"close_day" binding:"required,min=1,max=31"` - ClosePrice float64 `csv:"close_price" json:"close_price" binding:"required"` - CloseFee float64 `csv:"close_fee" json:"close_fee" binding:"required,min=0"` - PriceDiff float64 `csv:"price_diff" json:"price_diff" binding:"required"` - MinTick float64 `csv:"min_tick" json:"min_tick" binding:"required"` - TickPrice float64 `csv:"tick_price" json:"tick_price" binding:"required"` - DiffPnL float64 `csv:"diff_pnl" json:"diff_pnl" binding:"required"` - TotalFee float64 `csv:"total_fee" json:"total_fee" binding:"required,min=0"` - ClosePnL float64 `csv:"close_pnl" json:"close_pnl" binding:"required"` -}