diff --git a/kills/kill.go b/kills/kill.go new file mode 100644 index 0000000..cf89f11 --- /dev/null +++ b/kills/kill.go @@ -0,0 +1,142 @@ +package kills + +import ( + "encoding/base64" + "fmt" + "github.com/gin-gonic/gin" + "gorm.io/gorm" + "log" + "math/rand" + "time" + "toutoukan/init/databaseInit" +) + +const ( + colorRed = "\033[31m" // 红色 + colorGreen = "\033[32m" // 绿色 + colorYellow = "\033[33m" // 黄色 + colorBlue = "\033[34m" // 蓝色 + colorReset = "\033[0m" // 重置颜色 +) + +type Goods struct { + GID uint `gorm:"column:gid"` + Stock int `gorm:"column:stock"` + StartTime time.Time `gorm:"column:start_time"` + EndTime time.Time `gorm:"column:end_time"` +} + +// Order 订单模型,对应 orders_list 表 +type Order struct { + OrderId string `gorm:"column:order_id"` // 关联商品ID + TradeTime time.Time `gorm:"column:trade_time"` +} + +// 为模型指定数据库表名 +func (Goods) TableName() string { + return "goods_list" +} + +func (Order) TableName() string { + return "oders_list" +} + +// 生成25位随机订单编号(string类型) +func GenerateOrderID() string { + // 1. 取当前时间戳(精确到毫秒,8位数字) + timestamp := time.Now().Format("20060102150405") // 年月日时分秒(14位) + + // 2. 生成随机字符串(11位,补足25位) + randomBytes := make([]byte, 8) // 8字节随机数经base64编码后约11字符 + _, err := rand.Read(randomBytes) + if err != nil { + // 出错时降级为伪随机数 + return fmt.Sprintf("ORD%s%011d", timestamp, time.Now().UnixNano()%100000000000) + } + + // 3. 拼接前缀+时间戳+随机字符串(确保25位) + randomStr := base64.URLEncoding.EncodeToString(randomBytes)[:11] // 截取前11位 + return fmt.Sprintf("ORD%s%s", timestamp, randomStr) +} + +func Userkill(c *gin.Context) { + // 直接使用全局初始化好的 UserDB 连接池 + db := databaseInit.UserDB + if db == nil { + log.Printf("%sUserDB 未初始化,请先调用 InitUserDB()%s\n", colorRed, colorReset) + c.JSON(500, gin.H{"error": "系统数据库未初始化"}) + return + } + fmt.Printf("%s用户请求处理完成%s\n", colorBlue, colorReset) + + // 定义要查询的商品ID + var targetGID uint = 1 + var goods Goods + + tx := db.Begin() + if tx.Error != nil { + log.Printf("%s开启事务失败: %v%s\n", colorRed, tx.Error, colorReset) + c.JSON(500, gin.H{"body": "事务开启失败"}) + return + } + result := tx.Set("gorm:query_option", "FOR UPDATE").Where("gid = ?", targetGID).First(&goods) + if result.Error != nil { + tx.Rollback() // 失败回滚事务 + log.Printf("%s查询商品失败: %v%s\n", colorRed, result.Error, colorReset) + c.JSON(404, gin.H{"error": "商品不存在"}) + return + } + + // 2. 检查库存是否充足 + if goods.Stock <= 0 { + tx.Rollback() + log.Printf("%s商品库存不足%s\n", colorYellow, colorReset) + c.JSON(400, gin.H{"error": "商品库存不足"}) + return + } + + // 3. 生成订单 + order := Order{ + OrderId: GenerateOrderID(), + TradeTime: time.Now(), + } + if err := tx.Create(&order).Error; err != nil { + tx.Rollback() + log.Printf("%s创建订单失败: %v%s\n", colorRed, err, colorReset) + c.JSON(500, gin.H{"error": "创建订单失败"}) + return + } + + updateResult := tx.Model(&Goods{}). + Where("gid = ? AND stock > 0", targetGID). + Update("stock", gorm.Expr("stock - 1")) + if updateResult.Error != nil { + tx.Rollback() + log.Printf("%s扣减库存失败: %v%s\n", colorRed, updateResult.Error, colorReset) + c.JSON(500, gin.H{"error": "库存更新失败"}) + return + } + + // 5. 检查库存是否实际被扣减(防止并发下库存已被其他事务耗尽) + if updateResult.RowsAffected == 0 { + tx.Rollback() + log.Printf("%s库存扣减失败:并发冲突,库存已不足%s\n", colorRed, colorReset) + c.JSON(400, gin.H{"error": "商品库存不足"}) + return + } + + // 5. 提交事务 + if err := tx.Commit().Error; err != nil { + tx.Rollback() + log.Printf("%s提交事务失败: %v%s\n", colorRed, err, colorReset) + c.JSON(500, gin.H{"error": "系统错误"}) + return + } + + log.Printf("%s订单创建成功,订单ID: %s,剩余库存: %d%s\n", colorGreen, order.OrderId, goods.Stock-1, colorReset) + c.JSON(200, gin.H{ + "message": "下单成功", + "order_id": order.OrderId, + "stock": goods.Stock - 1, + }) +}