Files
toutoukan/kills/kill.go
JACKYMYPERSON f83b78dffb 秒杀
2025-09-13 20:00:27 +08:00

143 lines
4.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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,
})
}