package lotterymanage import ( "encoding/json" "errors" "fmt" "github.com/gin-gonic/gin" model "go-admin/app/admin/models" orm "go-admin/common/global" "go-admin/tools/app" "gorm.io/gorm" "net/http" "time" ) // GetPublicLotteryConfigHandler 查询抽奖配置(公开接口) // @Summary 查询抽奖模块配置(公开) // @Tags 积分抽奖,V1.5.0 // @Produce json // @Success 200 {object} models.LotteryConfig // @Router /api/v1/lottery/config/public [post] func GetPublicLotteryConfigHandler(c *gin.Context) { cfg, err := model.GetLotteryConfig() if err != nil { app.Error(c, http.StatusInternalServerError, nil, "获取抽奖配置失败") return } app.OK(c, cfg, "配置更新成功") } // UpdateLotteryConfig 更新抽奖参数配置 // @Summary 更新积分抽奖参数配置 // @Tags 积分抽奖,V1.5.0 // @Accept json // @Produce json // @Param data body models.UpdateLotteryConfigRequest true "抽奖配置" // @Success 200 {object} app.Response // @Router /api/v1/lottery/config/update [post] func UpdateLotteryConfig(c *gin.Context) { var req model.UpdateLotteryConfigRequest if err := c.ShouldBindJSON(&req); err != nil { app.Error(c, http.StatusBadRequest, nil, "参数错误: "+err.Error()) return } // 转为 JSON 字符串 valueBytes, err := json.Marshal(req) if err != nil { app.Error(c, http.StatusInternalServerError, nil, "配置序列化失败") return } // 尝试读取是否已有记录 var config model.Config tx := orm.Eloquent err = tx.Where("name = ?", model.ConfigNameLotteryLimit).First(&config).Error if errors.Is(err, gorm.ErrRecordNotFound) { // 不存在则创建 newCfg := model.Config{ Name: model.ConfigNameLotteryLimit, Value: string(valueBytes), } if err := tx.Create(&newCfg).Error; err != nil { app.Error(c, http.StatusInternalServerError, nil, "创建配置失败: "+err.Error()) return } } else if err != nil { app.Error(c, http.StatusInternalServerError, nil, "查询配置失败: "+err.Error()) return } else { // 存在则更新 if err := tx.Model(&config). Update("value", string(valueBytes)).Error; err != nil { app.Error(c, http.StatusInternalServerError, nil, "更新配置失败: "+err.Error()) return } } app.OK(c, nil, "配置更新成功") } // CreateLotteryPrize 创建奖品 // @Summary 创建奖品 // @Tags 积分抽奖,V1.5.0 // @Accept json // @Produce json // @Param data body models.CreatePrizeRequest true "奖品信息" // @Success 200 {object} models.CreatePrizeResp // @Router /api/v1/lottery/prize/create [post] func CreateLotteryPrize(c *gin.Context) { var req model.CreatePrizeRequest if err := c.ShouldBindJSON(&req); err != nil { app.Error(c, http.StatusBadRequest, nil, "参数错误: "+err.Error()) return } prize := model.LotteryPrize{ Name: req.Name, PrizeType: req.PrizeType, PrizeValue: req.PrizeValue, Level: req.Level, Weight: req.Weight, Stock: req.Stock, Status: req.Status, UnlockUserCount: req.UnlockUserCount, UnlockTotalCount: req.UnlockTotalCount, Images: req.Images, } if err := orm.Eloquent.Create(&prize).Error; err != nil { app.Error(c, http.StatusInternalServerError, nil, "创建失败: "+err.Error()) return } app.OK(c, model.CreatePrizeResp{ID: prize.ID}, "创建成功") } // UpdateLotteryPrize 编辑奖品 // @Summary 编辑奖品 // @Tags 积分抽奖,V1.5.0 // @Accept json // @Produce json // @Param data body models.UpdatePrizeRequest true "奖品信息" // @Success 200 {object} models.CreatePrizeResp // @Router /api/v1/lottery/prize/update [post] func UpdateLotteryPrize(c *gin.Context) { var req model.UpdatePrizeRequest if err := c.ShouldBindJSON(&req); err != nil { app.Error(c, http.StatusBadRequest, nil, "参数错误: "+err.Error()) return } var prize model.LotteryPrize if err := orm.Eloquent.First(&prize, req.ID).Error; err != nil { app.Error(c, http.StatusNotFound, nil, "未找到该奖品") return } // 更新字段 prize.Name = req.Name prize.PrizeType = req.PrizeType prize.PrizeValue = req.PrizeValue prize.Level = req.Level prize.Weight = req.Weight prize.Stock = req.Stock prize.Status = req.Status prize.UnlockUserCount = req.UnlockUserCount prize.UnlockTotalCount = req.UnlockTotalCount prize.Images = req.Images if err := orm.Eloquent.Save(&prize).Error; err != nil { app.Error(c, http.StatusInternalServerError, nil, "更新失败: "+err.Error()) return } app.OK(c, model.CreatePrizeResp{ID: prize.ID}, "编辑成功") } // DeleteLotteryPrize 删除奖品 // @Summary 删除奖品 // @Tags 积分抽奖,V1.5.0 // @Accept json // @Produce json // @Param data body models.DeletePrizeRequest true "奖品ID" // @Success 200 {object} app.Response // @Router /api/v1/lottery/prize/delete [post] func DeleteLotteryPrize(c *gin.Context) { var req model.DeletePrizeRequest if err := c.ShouldBindJSON(&req); err != nil { app.Error(c, http.StatusBadRequest, nil, "参数错误: "+err.Error()) return } if err := orm.Eloquent.Delete(&model.LotteryPrize{}, req.ID).Error; err != nil { app.Error(c, http.StatusInternalServerError, nil, "删除失败: "+err.Error()) return } app.OK(c, nil, "删除成功") } // LotteryPrizeList 查询奖品列表(分页) // @Summary 查询奖品列表 // @Tags 积分抽奖,V1.5.0 // @Accept json // @Produce json // @Param data body models.PrizeListRequest true "查询参数" // @Success 200 {object} models.PrizeListResponse // @Router /api/v1/lottery/prize/list [post] func LotteryPrizeList(c *gin.Context) { var req model.PrizeListRequest if err := c.ShouldBindJSON(&req); err != nil { app.Error(c, http.StatusBadRequest, nil, "参数错误") return } if req.Page <= 0 { req.Page = 1 } if req.PageSize <= 0 { req.PageSize = 10 } // 查询启用状态奖品的总权重(或根据筛选条件的权重) weightDB := orm.Eloquent.Model(&model.LotteryPrize{}) if req.Status != 0 { weightDB = weightDB.Where("status = ?", req.Status) } if req.Level > 0 { weightDB = weightDB.Where("level = ?", req.Level) } if req.Name != "" { weightDB = weightDB.Where("name LIKE ?", "%"+req.Name+"%") } var totalWeight int64 row := weightDB.Select("SUM(weight)").Row() if err := row.Scan(&totalWeight); err != nil { app.Error(c, http.StatusInternalServerError, nil, "计算总权重失败") return } // 查询分页数据 db := orm.Eloquent.Model(&model.LotteryPrize{}) if req.Name != "" { db = db.Where("name LIKE ?", "%"+req.Name+"%") } if req.Status != 0 { db = db.Where("status = ?", req.Status) } if req.Level > 0 { db = db.Where("level = ?", req.Level) } var total int64 if err := db.Count(&total).Error; err != nil { app.Error(c, http.StatusInternalServerError, nil, "查询总数失败") return } var list []model.LotteryPrize if err := db.Order("id ASC"). Offset((req.Page - 1) * req.PageSize). Limit(req.PageSize). Find(&list).Error; err != nil { app.Error(c, http.StatusInternalServerError, nil, "查询失败") return } // 动态计算中奖概率,避免小概率显示为 0.00% if totalWeight > 0 { for i := range list { rate := float64(list[i].Weight) / float64(totalWeight) * 100 switch { case rate >= 1: list[i].Probability = fmt.Sprintf("%.2f%%", rate) case rate >= 0.01: list[i].Probability = fmt.Sprintf("%.4f%%", rate) default: list[i].Probability = fmt.Sprintf("%.6f%%", rate) } list[i].Probability = model.TrimTrailingZerosFromPercent(list[i].Probability) } } else { for i := range list { list[i].Probability = "0.000000%" } } resp := model.PrizeListResponse{ List: list, Total: total, Page: req.Page, PageSize: req.PageSize, } app.OK(c, resp, "查询成功") } // LotteryRecordList 查询抽奖记录列表 // @Summary 查询抽奖记录列表 // @Tags 积分抽奖,V1.5.0 // @Accept json // @Produce json // @Param data body models.LotteryRecordListRequest true "查询参数" // @Success 200 {object} models.LotteryRecordListResponse // @Router /api/v1/lottery/record/list [post] func LotteryRecordList(c *gin.Context) { var req model.LotteryRecordListRequest if err := c.ShouldBindJSON(&req); err != nil { app.Error(c, http.StatusBadRequest, nil, "参数错误") return } if req.Page <= 0 { req.Page = 1 } if req.PageSize <= 0 { req.PageSize = 10 } db := orm.Eloquent.Model(&model.LotteryRecord{}). Select(`lottery_record.id, lottery_record.created_at, lottery_record.updated_at, lottery_record.deleted_at, lottery_record.uid, lottery_record.prize_id, lottery_record.prize_name, lottery_record.prize_type, lottery_record.prize_level, lottery_record.prize_value, lottery_record.status, lottery_record.is_win, user.tel, user.store_id as store_id, user.member_level, store.name as store_name`). Joins("LEFT JOIN user ON user.uid = lottery_record.uid"). Joins("LEFT JOIN store ON store.id = user.store_id") // 构建基础查询 query := orm.Eloquent.Model(&model.LotteryRecord{}). Select(`lottery_record.id, lottery_record.created_at, lottery_record.updated_at, lottery_record.deleted_at, lottery_record.uid, lottery_record.prize_id, lottery_record.prize_name, lottery_record.prize_type, lottery_record.prize_level, lottery_record.prize_value, lottery_record.status, lottery_record.is_win, user.tel, user.store_id as store_id, user.member_level, store.name as store_name`). Joins("LEFT JOIN user ON user.uid = lottery_record.uid"). Joins("LEFT JOIN store ON store.id = user.store_id") // 条件过滤 if req.Uid > 0 { db = db.Where("lottery_record.uid = ?", req.Uid) query = query.Where("lottery_record.uid = ?", req.Uid) } if req.Tel != "" { db = db.Where("user.tel = ?", req.Tel) query = query.Where("user.tel = ?", req.Tel) } if req.StoreId > 0 { db = db.Where("user.store_id = ?", req.StoreId) query = query.Where("user.store_id = ?", req.StoreId) } if req.MemberLevel > 0 { db = db.Where("user.member_level = ?", req.MemberLevel) query = query.Where("user.member_level = ?", req.MemberLevel) } if req.PrizeLevel > 0 { db = db.Where("lottery_record.prize_level = ?", req.PrizeLevel) query = query.Where("lottery_record.prize_level = ?", req.PrizeLevel) } if req.StartTime != "" { if t, err := time.Parse("2006-01-02", req.StartTime); err == nil { db = db.Where("lottery_record.created_at >= ?", t) query = query.Where("lottery_record.created_at >= ?", t) } } // 根据 win_status 筛选记录:0=全部,1=已中奖,2=未中奖 switch req.WinStatus { case 1: db = db.Where("lottery_record.is_win = ?", true) case 2: db = db.Where("lottery_record.is_win = ?", false) } if req.EndTime != "" { if t, err := time.Parse("2006-01-02", req.EndTime); err == nil { db = db.Where("lottery_record.created_at <= ?", t.Add(24*time.Hour)) query = query.Where("lottery_record.created_at <= ?", t.Add(24*time.Hour)) } } var total int64 if err := query.Count(&total).Error; err != nil { app.Error(c, http.StatusInternalServerError, nil, "统计失败") return } var list []model.LotteryRecordWithUserInfo if err := db.Order("lottery_record.id DESC"). Offset((req.Page - 1) * req.PageSize). Limit(req.PageSize). Scan(&list).Error; err != nil { app.Error(c, http.StatusInternalServerError, nil, "查询失败") return } app.OK(c, model.LotteryRecordListResponse{ List: list, Total: total, Page: req.Page, PageSize: req.PageSize, }, "查询成功") } // LotteryPrizeOrderList 查询抽奖订单列表 // @Summary 查询抽奖订单列表 // @Tags 积分抽奖,V1.5.0 // @Accept json // @Produce json // @Param data body models.LotteryPrizeOrderListRequest true "查询参数" // @Success 200 {object} models.LotteryPrizeOrderListResponse // @Router /api/v1/lottery/prize_order/list [post] func LotteryPrizeOrderList(c *gin.Context) { var req model.LotteryPrizeOrderListRequest if err := c.ShouldBindJSON(&req); err != nil { app.Error(c, http.StatusBadRequest, nil, "参数错误") return } if req.Page <= 0 { req.Page = 1 } if req.PageSize <= 0 { req.PageSize = 10 } db := orm.Eloquent.Model(&model.LotteryPrizeOrder{}). Select(`lottery_prize_order.*, user.tel, user.store_id as store_id, user.member_level, store.name as store_name`). Joins("LEFT JOIN user ON user.uid = lottery_prize_order.uid"). Joins("LEFT JOIN store ON store.id = user.store_id") // 筛选条件 if req.Uid > 0 { db = db.Where("lottery_prize_order.uid = ?", req.Uid) } if req.Tel != "" { db = db.Where("lottery_prize_order.tel = ?", req.Tel) } if req.StoreId > 0 { db = db.Where("user.store_id = ?", req.StoreId) } if req.MemberLevel > 0 { db = db.Where("user.member_level = ?", req.MemberLevel) } if req.PrizeName != "" { db = db.Where("lottery_prize_order.prize_name LIKE ?", "%"+req.PrizeName+"%") } if req.Status != model.LotteryPrizeOrderStatusAll { db = db.Where("lottery_prize_order.status = ?", req.Status) } if req.StartTime != "" { if t, err := time.Parse("2006-01-02", req.StartTime); err == nil { db = db.Where("lottery_prize_order.created_at >= ?", t) } } if req.EndTime != "" { if t, err := time.Parse("2006-01-02", req.EndTime); err == nil { db = db.Where("lottery_prize_order.created_at <= ?", t.Add(24*time.Hour)) } } // 分页统计 var total int64 if err := db.Count(&total).Error; err != nil { app.Error(c, http.StatusInternalServerError, nil, "统计失败") return } var list []model.LotteryPrizeOrderItem if err := db.Order("lottery_prize_order.id DESC"). Offset((req.Page - 1) * req.PageSize). Limit(req.PageSize). Scan(&list).Error; err != nil { app.Error(c, http.StatusInternalServerError, nil, "查询失败") return } app.OK(c, model.LotteryPrizeOrderListResponse{ List: list, Total: total, Page: req.Page, PageSize: req.PageSize, }, "查询成功") } // GetLotteryPrizeOrderDetail 查询抽奖订单详情 // @Summary 查询抽奖订单详情 // @Tags 积分抽奖,V1.5.0 // @Accept json // @Produce json // @Param data body models.GetLotteryPrizeOrderDetailRequest true "查询参数" // @Success 200 {object} models.LotteryPrizeOrderDetailResponse // @Router /api/v1/lottery/prize_order/detail [post] func GetLotteryPrizeOrderDetail(c *gin.Context) { var req model.GetLotteryPrizeOrderDetailRequest if err := c.ShouldBindJSON(&req); err != nil || req.OrderID == 0 { app.Error(c, http.StatusBadRequest, nil, "参数错误:order_id 必传") return } var detail model.LotteryPrizeOrderDetailResponse db := orm.Eloquent.Table("lottery_prize_order"). Select(`lottery_prize_order.*, user.wx_name as nickname, user.tel, user.member_level, store.name as store_name, lottery_prize.prize_type, lottery_prize.level as prize_level, lottery_prize.prize_value`). Joins("LEFT JOIN user ON user.uid = lottery_prize_order.uid"). Joins("LEFT JOIN store ON store.id = user.store_id"). Joins("LEFT JOIN lottery_prize ON lottery_prize.id = lottery_prize_order.prize_id"). Where("lottery_prize_order.id = ?", req.OrderID) if err := db.First(&detail).Error; err != nil { app.Error(c, http.StatusNotFound, nil, "未找到对应的订单记录") return } app.OK(c, detail, "查询成功") } // ShipLotteryPrizeOrder 抽奖订单发货 // @Summary 抽奖订单发货 // @Tags 积分抽奖,V1.5.0 // @Accept json // @Produce json // @Param data body models.ShipLotteryPrizeOrderRequest true "查询参数" // @Success 200 {object} app.Response // @Router /api/v1/lottery/prize_order/ship [post] func ShipLotteryPrizeOrder(c *gin.Context) { var req model.ShipLotteryPrizeOrderRequest if err := c.ShouldBindJSON(&req); err != nil { app.Error(c, http.StatusBadRequest, nil, "参数错误:"+err.Error()) return } var order model.LotteryPrizeOrder if err := orm.Eloquent.First(&order, req.OrderID).Error; err != nil { app.Error(c, http.StatusNotFound, nil, "订单不存在") return } if order.Status != 0 { app.Error(c, http.StatusBadRequest, nil, "订单状态不为待发货,无法发货") return } err := orm.Eloquent.Model(&order).Updates(map[string]interface{}{ "logistics_company": req.LogisticsCompany, "logistics_number": req.LogisticsNumber, "shipped_at": time.Now(), "status": 1, // 已发货 }).Error if err != nil { app.Error(c, http.StatusInternalServerError, nil, "发货失败:"+err.Error()) return } app.OK(c, nil, "发货成功") }