package kills import ( "context" "encoding/base64" "fmt" "github.com/gin-gonic/gin" "log" "math/rand" "net/http" "time" "toutoukan/init/redisInit" "toutoukan/model/usermodel/userOrder" "toutoukan/services/order/killOrder/orderMq/mqproducer" "toutoukan/utill/luaScripts" ) func GenerateOrderID() string { timestamp := time.Now().Format("20060102150405") randomBytes := make([]byte, 8) _, err := rand.Read(randomBytes) if err != nil { return fmt.Sprintf("ORD%s%011d", timestamp, time.Now().UnixNano()%100000000000) } randomStr := base64.URLEncoding.EncodeToString(randomBytes)[:11] return fmt.Sprintf("ORD%s%s", timestamp, randomStr) } func Userkill(c *gin.Context) { var req userOrder.UserRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数错误", "detail": err.Error()}) return } fmt.Printf("用户%d请求购买%s", req.UserID, req.Order) OrderID := GenerateOrderID() // 1. Redis扣减库存(不变) result, err := redisInit.RedisClient.Eval(context.Background(), luaScripts.Luascript_forkill, []string{req.Order, "mayiming"}, 1, OrderID).Int() if err != nil { log.Printf("Redis 脚本执行错误: %v", err) c.JSON(500, gin.H{"error": "库存操作失败", "detail": err.Error()}) return } if result != 1 { c.JSON(200, gin.H{"result": "库存不足,扣减失败"}) return } // 2. 发送MQ消息(关键:发送失败需回滚Redis) msgSent := false defer func() { // 若消息未发送成功,回滚Redis库存 if !msgSent { log.Printf("消息发送失败,回滚Redis库存,商品: %s,用户: %d", req.Order, req.UserID) // 回滚Redis:库存+1,同时移除可能的订单记录(根据Lua脚本逻辑调整) redisInit.RedisClient.Incr(context.Background(), req.Order) // 若Lua脚本中记录了订单ID(如存到set中),也需删除 //redisInit.RedisClient.SRem(context.Background(), req.Order, OrderID) } }() // 发送消息(同步发送,确保拿到发送结果) sendErr := mqproducer.SendNormalMessage(OrderID, req.Order, req.UserID) if sendErr != nil { log.Printf("MQ消息发送失败: %v", sendErr) c.JSON(500, gin.H{"error": "消息发送失败", "detail": sendErr.Error()}) return } // 消息发送成功,标记为已发送(避免defer回滚) msgSent = true c.JSON(200, gin.H{"result": "库存扣减成功"}) }