package plant import ( "errors" "sundynix-go/global" "sundynix-go/model/plant" plantReq "sundynix-go/model/plant/request" "time" "gorm.io/gorm" ) type ExchangeService struct{} // ==================== 管理端 ==================== // CreateItem 创建兑换商品 func (s *ExchangeService) CreateItem(req plantReq.ExchangeItemCreateReq) error { item := plant.ExchangeItem{ Name: req.Name, Description: req.Description, ImageId: req.ImageId, Type: req.Type, CostSunlight: req.CostSunlight, Stock: req.Stock, LimitPerUser: req.LimitPerUser, Sort: req.Sort, Status: 1, // 默认上架 } if req.StartTime != "" { t, _ := time.Parse("2006-01-02 15:04:05", req.StartTime) item.StartTime = &t } if req.EndTime != "" { t, _ := time.Parse("2006-01-02 15:04:05", req.EndTime) item.EndTime = &t } return global.DB.Create(&item).Error } // UpdateItem 更新兑换商品 func (s *ExchangeService) UpdateItem(req plantReq.ExchangeItemUpdateReq) error { updateMap := map[string]interface{}{ "name": req.Name, "description": req.Description, "image_id": req.ImageId, "type": req.Type, "cost_sunlight": req.CostSunlight, "stock": req.Stock, "limit_per_user": req.LimitPerUser, "status": req.Status, "sort": req.Sort, } if req.StartTime != "" { t, _ := time.Parse("2006-01-02 15:04:05", req.StartTime) updateMap["start_time"] = t } if req.EndTime != "" { t, _ := time.Parse("2006-01-02 15:04:05", req.EndTime) updateMap["end_time"] = t } return global.DB.Model(&plant.ExchangeItem{}).Where("id = ?", req.Id).Updates(updateMap).Error } // DeleteItem 删除兑换商品 func (s *ExchangeService) DeleteItem(id string) error { return global.DB.Where("id = ?", id).Delete(&plant.ExchangeItem{}).Error } // AdminItemList 管理端商品列表(含下架) func (s *ExchangeService) AdminItemList(req plantReq.ExchangeItemListReq) (list []plant.ExchangeItem, total int64, err error) { db := global.DB.Model(&plant.ExchangeItem{}).Preload("Image") if req.Type != "" { db = db.Where("type = ?", req.Type) } if req.Status != 0 { db = db.Where("status = ?", req.Status) } if req.Keyword != "" { db = db.Where("name LIKE ?", "%"+req.Keyword+"%") } err = db.Count(&total).Error if err != nil { return } err = db.Scopes(req.Paginate()).Order("sort asc, created_at desc").Find(&list).Error return } // AdminOrderList 管理端订单列表 func (s *ExchangeService) AdminOrderList(req plantReq.ExchangeOrderListReq) (list []plant.ExchangeOrder, total int64, err error) { db := global.DB.Model(&plant.ExchangeOrder{}).Preload("Item", func(db *gorm.DB) *gorm.DB { return db.Preload("Image") }) if req.Status != 0 { db = db.Where("status = ?", req.Status) } if req.UserId != "" { db = db.Where("user_id = ?", req.UserId) } if req.Keyword != "" { db = db.Where("item_name LIKE ? OR recipient_name LIKE ?", "%"+req.Keyword+"%", "%"+req.Keyword+"%") } err = db.Count(&total).Error if err != nil { return } err = db.Scopes(req.Paginate()).Order("created_at desc").Find(&list).Error return } // UpdateOrderStatus 更新订单状态(管理端) func (s *ExchangeService) UpdateOrderStatus(req plantReq.ExchangeOrderUpdateReq) error { updateMap := map[string]interface{}{ "status": req.Status, } if req.TrackingNo != "" { updateMap["tracking_no"] = req.TrackingNo } if req.Remark != "" { updateMap["remark"] = req.Remark } if req.Status == plant.OrderStatusCompleted { now := time.Now() updateMap["completed_at"] = now } // 如果取消订单,退还阳光值 if req.Status == plant.OrderStatusCancelled { return global.DB.Transaction(func(tx *gorm.DB) error { var order plant.ExchangeOrder if err := tx.Where("id = ?", req.Id).First(&order).Error; err != nil { return err } if order.Status == plant.OrderStatusCancelled { return errors.New("订单已取消") } // 退还阳光值 if err := tx.Model(&plant.UserProfile{}).Where("user_id = ?", order.UserId). Update("current_sunlight", gorm.Expr("current_sunlight + ?", order.CostSunlight)).Error; err != nil { return err } // 恢复库存 if err := tx.Model(&plant.ExchangeItem{}).Where("id = ? AND stock >= 0", order.ItemId). Update("stock", gorm.Expr("stock + ?", order.Quantity)).Error; err != nil { // 库存为-1(无限)时,WHERE条件不匹配,不影响 } return tx.Model(&plant.ExchangeOrder{}).Where("id = ?", req.Id).Updates(updateMap).Error }) } return global.DB.Model(&plant.ExchangeOrder{}).Where("id = ?", req.Id).Updates(updateMap).Error } // ==================== 用户端 ==================== // UserItemList 用户端商品列表(仅上架) func (s *ExchangeService) UserItemList(req plantReq.ExchangeItemListReq) (list []plant.ExchangeItem, total int64, err error) { db := global.DB.Model(&plant.ExchangeItem{}).Preload("Image"). Where("status = 1") if req.Type != "" { db = db.Where("type = ?", req.Type) } if req.Keyword != "" { db = db.Where("name LIKE ?", "%"+req.Keyword+"%") } err = db.Count(&total).Error if err != nil { return } err = db.Scopes(req.Paginate()).Order("sort asc, created_at desc").Find(&list).Error return } // UserItemDetail 商品详情 func (s *ExchangeService) UserItemDetail(itemId string) (plant.ExchangeItem, error) { var item plant.ExchangeItem err := global.DB.Preload("Image").Where("id = ? AND status = 1", itemId).First(&item).Error return item, err } // UserExchange 用户发起兑换 func (s *ExchangeService) UserExchange(req plantReq.ExchangeReq, userId string) error { if req.Quantity <= 0 { req.Quantity = 1 } return global.DB.Transaction(func(tx *gorm.DB) error { // 1. 查询并锁定商品 var item plant.ExchangeItem if err := tx.Set("gorm:query_option", "FOR UPDATE"). Where("id = ? AND status = 1", req.ItemId).First(&item).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return errors.New("商品不存在或已下架") } return err } // 2. 检查有效期 now := time.Now() if item.StartTime != nil && now.Before(*item.StartTime) { return errors.New("兑换尚未开始") } if item.EndTime != nil && now.After(*item.EndTime) { return errors.New("兑换已结束") } // 3. 检查库存 if item.Stock >= 0 && item.Stock < req.Quantity { return errors.New("库存不足") } // 4. 检查每人限兑 if item.LimitPerUser > 0 { var count int64 tx.Model(&plant.ExchangeOrder{}).Where("user_id = ? AND item_id = ? AND status != ?", userId, req.ItemId, plant.OrderStatusCancelled).Count(&count) if int(count)+req.Quantity > item.LimitPerUser { return errors.New("已达到兑换上限") } } // 5. 计算总消耗 totalCost := item.CostSunlight * int64(req.Quantity) // 6. 扣减阳光值 result := tx.Model(&plant.UserProfile{}). Where("user_id = ? AND current_sunlight >= ?", userId, totalCost). Update("current_sunlight", gorm.Expr("current_sunlight - ?", totalCost)) if result.Error != nil { return result.Error } if result.RowsAffected == 0 { return errors.New("阳光值不足") } // 7. 扣减库存 if item.Stock >= 0 { if err := tx.Model(&plant.ExchangeItem{}).Where("id = ? AND stock >= ?", item.Id, req.Quantity). Update("stock", gorm.Expr("stock - ?", req.Quantity)).Error; err != nil { return err } } // 8. 创建订单 order := plant.ExchangeOrder{ UserId: userId, ItemId: item.Id, ItemName: item.Name, CostSunlight: totalCost, Quantity: req.Quantity, Status: plant.OrderStatusPending, ItemType: item.Type, RecipientName: req.RecipientName, Phone: req.Phone, Address: req.Address, } // 虚拟商品自动完成 if item.Type == "VIRTUAL" { order.Status = plant.OrderStatusCompleted now := time.Now() order.CompletedAt = &now } return tx.Create(&order).Error }) } // UserOrderList 用户订单列表 func (s *ExchangeService) UserOrderList(req plantReq.ExchangeOrderListReq, userId string) (list []plant.ExchangeOrder, total int64, err error) { db := global.DB.Model(&plant.ExchangeOrder{}).Preload("Item", func(db *gorm.DB) *gorm.DB { return db.Preload("Image") }).Where("user_id = ?", userId) if req.Status != 0 { db = db.Where("status = ?", req.Status) } err = db.Count(&total).Error if err != nil { return } err = db.Scopes(req.Paginate()).Order("created_at desc").Find(&list).Error return }