package service import ( "context" "crypto/md5" "errors" "fmt" "time" "github.com/gogf/gf/v2/util/gconv" knapsackConsts "shop-user-trade/consts/knapsack" knapsackDao "shop-user-trade/dao/knapsack" knapsackDto "shop-user-trade/model/dto/knapsack" knapsackEntity "shop-user-trade/model/entity/knapsack" ) type knapsack struct{} // Knapsack 背包服务 var Knapsack = new(knapsack) // Create 创建背包项 func (s *knapsack) Create(ctx context.Context, req *knapsackDto.CreateKnapsackReq) (int64, error) { id, err := knapsackDao.Knapsack.Insert(ctx, req) if err != nil { return 0, fmt.Errorf("创建背包项失败: %w", err) } return id, nil } // GetOne 获取单个背包项 func (s *knapsack) GetOne(ctx context.Context, req *knapsackDto.GetKnapsackReq) (*knapsackDto.GetKnapsackRes, error) { item, err := knapsackDao.Knapsack.GetOne(ctx, req) if err != nil { return nil, err } if item == nil { return nil, errors.New("背包项不存在") } return &knapsackDto.GetKnapsackRes{ KnapsackItem: s.entityToItem(item), }, nil } // List 获取背包列表(支持分页和搜索) func (s *knapsack) List(ctx context.Context, req *knapsackDto.ListKnapsackReq) (*knapsackDto.ListKnapsackRes, error) { list, total, err := knapsackDao.Knapsack.List(ctx, req) if err != nil { return nil, err } res := &knapsackDto.ListKnapsackRes{Total: total} for _, item := range list { itemCopy := item res.List = append(res.List, s.entityToItem(&itemCopy)) } return res, nil } // Use 使用背包项 func (s *knapsack) Use(ctx context.Context, req *knapsackDto.UseKnapsackReq) error { item, err := knapsackDao.Knapsack.GetByID(ctx, req.ID) if err != nil { return err } if item == nil { return errors.New("背包项不存在") } if err = s.canBeUsed(item); err != nil { return err } now := time.Now().Unix() usedStatus := knapsackConsts.KnapsackStatusUsed updateReq := &knapsackDto.UpdateKnapsackReq{ Id: item.Id, Status: &usedStatus, UsedAt: &now, Updater: req.OperatorName, } if _, err = knapsackDao.Knapsack.Update(ctx, updateReq); err != nil { return fmt.Errorf("更新背包项失败: %w", err) } return nil } // ListToMarket 上架背包项到市场 func (s *knapsack) ListToMarket(ctx context.Context, req *knapsackDto.ListToMarketReq) error { item, err := knapsackDao.Knapsack.GetByID(ctx, req.ID) if err != nil { return err } if item == nil { return errors.New("背包项不存在") } if err = s.canBeListed(item); err != nil { return err } listedStatus := knapsackConsts.KnapsackStatusListed updateReq := &knapsackDto.UpdateKnapsackReq{ Id: item.Id, Status: &listedStatus, Updater: req.OperatorName, } if _, err = knapsackDao.Knapsack.Update(ctx, updateReq); err != nil { return fmt.Errorf("更新背包项失败: %w", err) } return nil } // UnlistKnapsack 从市场下架背包项 func (s *knapsack) UnlistKnapsack(ctx context.Context, req *knapsackDto.UnlistKnapsackReq) error { item, err := knapsackDao.Knapsack.GetByID(ctx, req.ID) if err != nil { return err } if item == nil { return errors.New("背包项不存在") } if item.Status != knapsackConsts.KnapsackStatusListed { return errors.New("只有已上架的物品才能下架") } activeStatus := knapsackConsts.KnapsackStatusActive updateReq := &knapsackDto.UpdateKnapsackReq{ Id: item.Id, Status: &activeStatus, Updater: req.OperatorName, } if _, err = knapsackDao.Knapsack.Update(ctx, updateReq); err != nil { return fmt.Errorf("更新背包项失败: %w", err) } return nil } // Verify 核销背包项 func (s *knapsack) Verify(ctx context.Context, req *knapsackDto.VerifyKnapsackReq) error { item, err := knapsackDao.Knapsack.GetByID(ctx, req.ID) if err != nil { return err } if item == nil { return errors.New("背包项不存在") } if err = s.canBeVerified(item); err != nil { return err } now := time.Now().Unix() usedStatus := knapsackConsts.KnapsackStatusUsed updateReq := &knapsackDto.UpdateKnapsackReq{ Id: item.Id, Status: &usedStatus, UsedAt: &now, Updater: req.OperatorName, } if _, err = knapsackDao.Knapsack.Update(ctx, updateReq); err != nil { return fmt.Errorf("更新背包项失败: %w", err) } return nil } // GenerateQRCode 生成核销二维码 func (s *knapsack) GenerateQRCode(ctx context.Context, req *knapsackDto.GenerateQRCodeReq) (*knapsackDto.GenerateQRCodeRes, error) { item, err := knapsackDao.Knapsack.GetByID(ctx, req.ID) if err != nil { return nil, err } if item == nil { return nil, errors.New("背包项不存在") } if err = s.canBeUsed(item); err != nil { return nil, err } now := time.Now().Unix() expireTime := now + req.ExpireDuration verifyCode := s.generateVerifyCode(gconv.String(item.Id), now) qrContent := fmt.Sprintf("%d:%d:%d:%s", item.Id, now, expireTime, verifyCode) qrCode := fmt.Sprintf("QR_%x", md5.Sum([]byte(qrContent))) return &knapsackDto.GenerateQRCodeRes{ QRCode: qrCode, VerifyCode: verifyCode, }, nil } // ExpireExpiredItems 将过期的背包项标记为过期状态(定时任务调用) func (s *knapsack) ExpireExpiredItems(ctx context.Context) (int64, error) { expiredList, err := knapsackDao.Knapsack.ListExpired(ctx) if err != nil { return 0, fmt.Errorf("查询过期物品失败: %w", err) } if len(expiredList) == 0 { return 0, nil } count := int64(len(expiredList)) expiredStatus := knapsackConsts.KnapsackStatusExpired for _, item := range expiredList { updateReq := &knapsackDto.UpdateKnapsackReq{ Id: item.Id, Status: &expiredStatus, } if _, err = knapsackDao.Knapsack.Update(ctx, updateReq); err != nil { return count, fmt.Errorf("更新过期物品状态失败: %w", err) } } return count, nil } // canBeUsed 检查背包项是否可以使用 func (s *knapsack) canBeUsed(item *knapsackEntity.Knapsack) error { if item.Status != knapsackConsts.KnapsackStatusActive { return errors.New("物品状态不可用") } if item.ExpireAt != nil && *item.ExpireAt < time.Now().Unix() { return errors.New("物品已过期") } return nil } // canBeListed 检查背包项是否可以上架 func (s *knapsack) canBeListed(item *knapsackEntity.Knapsack) error { if item.Status != knapsackConsts.KnapsackStatusActive { return errors.New("只有启用状态的物品才能上架") } if item.ExpireAt != nil && *item.ExpireAt < time.Now().Unix() { return errors.New("已过期的物品不能上架") } return nil } // canBeVerified 检查背包项是否可以核销 func (s *knapsack) canBeVerified(item *knapsackEntity.Knapsack) error { if item.Status != knapsackConsts.KnapsackStatusActive { return errors.New("物品状态不可核销") } if item.ExpireAt != nil && *item.ExpireAt < time.Now().Unix() { return errors.New("物品已过期") } return nil } // generateVerifyCode 生成验证码 func (s *knapsack) generateVerifyCode(knapsackID string, timestamp int64) string { data := fmt.Sprintf("%s_%d_verify", knapsackID, timestamp) hash := md5.Sum([]byte(data)) return fmt.Sprintf("V%x", hash)[:8] } // entityToItem 实体转换为Item func (s *knapsack) entityToItem(e *knapsackEntity.Knapsack) *knapsackDto.KnapsackItem { item := &knapsackDto.KnapsackItem{} if err := gconv.Struct(e, item); err != nil { return item } item.ID = e.Id item.Status = e.Status item.Type = e.Type if e.CreatedAt != nil { item.CreatedAt = e.CreatedAt.String() } if e.UpdatedAt != nil { item.UpdatedAt = e.UpdatedAt.String() } return item }