diff --git a/backend/prompt.md b/backend/prompt.md index f19c9d0..24096c4 100644 --- a/backend/prompt.md +++ b/backend/prompt.md @@ -110,3 +110,6 @@ updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP --- 读取./sql/08_trade.sql,完善trade_info_view视图逻辑,加入trade_profit信息。 --- +1、看一下./sql/07_variety.sql的内容,并记住这些内容。 +2、打开./src/logic4variety/create.go,并维持风格不变的前提下,将业务调整为1中的信息。 +--- \ No newline at end of file diff --git a/backend/src/logic4variety/create.go b/backend/src/logic4variety/create.go index 92360ce..59f699d 100644 --- a/backend/src/logic4variety/create.go +++ b/backend/src/logic4variety/create.go @@ -1 +1,235 @@ -package logic4variety \ No newline at end of file +package logic4variety + +import ( + "asset_assistant/db" + "net/http" + "time" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" + "go.uber.org/zap" +) + +// CreateRequest 创建品种请求参数结构 +type CreateRequest struct { + Name string `json:"name" binding:"required"` // 品种名称,必填 + Code string `json:"code" binding:"required"` // 品种代码,必填 + Tick float64 `json:"tick" binding:"required"` // 品种tick,必填 + TickPrice float64 `json:"tick_price" binding:"required"` // 品种tick价格,必填 + ExchangeID string `json:"exchange_id" binding:"required"` // 交易所ID,必填 +} + +// CreateResponse 创建品种响应结构 +type CreateResponse struct { + Success bool `json:"success"` + Message string `json:"message"` + Data CreateData `json:"data"` +} + +// CreateData 响应数据结构 +type CreateData struct { + VarietyID string `json:"variety_id"` +} + +// CreateHandler 处理创建品种逻辑 +func CreateHandler(c *gin.Context) { + startTime := time.Now() + reqID := c.Request.Header.Get("X-VarietyCreateRequest-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 + // 绑定参数时会自动验证所有必填字段 + 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、tick、tick_price和exchange_id为必填项", + }) + 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), + zap.String("exchange_id", req.ExchangeID), + ) + + 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 + } + + 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, CreateResponse{ + Success: false, + Message: "系统错误,请稍后重试", + }) + } + }() + + // 1. 创建variety主记录 + 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, CreateResponse{ + Success: false, + Message: "创建品种记录失败", + }) + return + } + + zap.L().Debug("📝 variety表插入成功", + zap.String("req_id", reqID), + zap.String("variety_id", varietyID), + ) + + // 2. 插入交易所关联信息 + _, err = tx.Exec("INSERT INTO variety_exchange (variety_id, exchange_id) VALUES ($1, $2)", varietyID, req.ExchangeID) + if err != nil { + tx.Rollback() + zap.L().Error("❌ variety_exchange表插入失败", + zap.String("req_id", reqID), + zap.String("variety_id", varietyID), + zap.Error(err), + ) + c.JSON(http.StatusInternalServerError, CreateResponse{ + Success: false, + Message: "保存交易所关联信息失败", + }) + return + } + + // 3. 插入名称信息 + _, 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, CreateResponse{ + Success: false, + Message: "保存名称信息失败", + }) + return + } + + // 4. 插入代码信息 + _, 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, CreateResponse{ + Success: false, + Message: "保存代码信息失败", + }) + return + } + + // 5. 插入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, CreateResponse{ + Success: false, + Message: "保存tick信息失败", + }) + return + } + + // 6. 插入tick价格信息 + _, 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, CreateResponse{ + Success: false, + Message: "保存tick价格信息失败", + }) + 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, CreateResponse{ + 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, CreateResponse{ + Success: true, + Message: "创建成功", + Data: CreateData{ + VarietyID: varietyID, + }, + }) +}