This commit is contained in:
vipg
2025-12-22 17:45:23 +08:00
parent 7715886da3
commit 6a74bfdcd4
2 changed files with 19 additions and 10 deletions

View File

@@ -3,10 +3,8 @@
-- ========================================================= -- =========================================================
\pset pager off \pset pager off
\timing on \timing on
-- 切换到目标数据库
\c postgres; \c postgres;
-- 1 保证扩展存在(重复执行无害)
CREATE EXTENSION IF NOT EXISTS "moddatetime" SCHEMA public; CREATE EXTENSION IF NOT EXISTS "moddatetime" SCHEMA public;
DO $$ DO $$
@@ -14,7 +12,6 @@ BEGIN
RAISE NOTICE '🚀============ cn_futures_trading_records 部署开始 ============🚀'; RAISE NOTICE '🚀============ cn_futures_trading_records 部署开始 ============🚀';
END $$; END $$;
-- 2 建表(幂等)
DO $$ DO $$
BEGIN BEGIN
IF NOT EXISTS ( IF NOT EXISTS (

View File

@@ -7,8 +7,8 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"reflect"
"strconv" "strconv"
"strings"
"time" "time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@@ -17,7 +17,7 @@ import (
"go.uber.org/zap" "go.uber.org/zap"
) )
/* ---------- 公共 Payload 结构体 ---------- */ /* ---------- 公共 Payload ---------- */
type Payload struct { type Payload struct {
Status int `csv:"status" json:"status" binding:"required,min=-1,max=1"` Status int `csv:"status" json:"status" binding:"required,min=-1,max=1"`
OpenYear int `csv:"open_year" json:"open_year" binding:"required,min=1900,max=2200"` OpenYear int `csv:"open_year" json:"open_year" binding:"required,min=1900,max=2200"`
@@ -38,7 +38,19 @@ type Payload struct {
TickPrice float64 `csv:"tick_price" json:"tick_price" binding:"required"` TickPrice float64 `csv:"tick_price" json:"tick_price" binding:"required"`
DiffPnL float64 `csv:"diff_pnl" json:"diff_pnl" binding:"required"` DiffPnL float64 `csv:"diff_pnl" json:"diff_pnl" binding:"required"`
TotalFee float64 `csv:"total_fee" json:"total_fee" binding:"required,min=0"` TotalFee float64 `csv:"total_fee" json:"total_fee" binding:"required,min=0"`
ClosePnL float64 `csv:"close_pnl" json:"close_pnl" binding:"required"` // 客户端算好 ClosePnL float64 `csv:"close_pnl" json:"close_pnl" binding:"required"`
}
/* ---------- 包级常量:带 csv 标签的字段个数 ---------- */
var payloadCSVCols int
func init() {
typ := reflect.TypeOf(Payload{})
for i := 0; i < typ.NumField(); i++ {
if _, ok := typ.Field(i).Tag.Lookup("csv"); ok {
payloadCSVCols++
}
}
} }
/* ---------- 单条创建 ---------- */ /* ---------- 单条创建 ---------- */
@@ -180,14 +192,14 @@ func CreateBatchHandler(c *gin.Context) {
var recordIDs []string var recordIDs []string
for idx, row := range req.Rows { for idx, row := range req.Rows {
if len(row) != 19 { // 19 列(含 ClosePnL if len(row) != payloadCSVCols { // ✅ 自动随结构体同步
tx.Rollback() tx.Rollback()
zap.L().Warn("⚠️ 字段数量不匹配", zap.String("req_id", reqID), zap.Int("row_index", idx), zap.Int("field_count", len(row))) 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) + " 行字段数量不足 19 个"}) c.JSON(http.StatusOK, TradingRecordsBatchCreateResponse{Success: false, Message: "第 " + strconv.Itoa(idx+1) + " 行字段数量不足 " + strconv.Itoa(payloadCSVCols) + " 个"})
return return
} }
// 用 gocsv 一次性映射 // 一次性映射
var p Payload var p Payload
if err := gocsv.UnmarshalCSV(csv.NewReader(bytes.NewReader([]byte(strings.Join(row, ",")+"\n"))), &p); err != nil { if err := gocsv.UnmarshalCSV(csv.NewReader(bytes.NewReader([]byte(strings.Join(row, ",")+"\n"))), &p); err != nil {
tx.Rollback() tx.Rollback()