263 lines
7.4 KiB
Go
263 lines
7.4 KiB
Go
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
|
|
}
|