283 lines
8.4 KiB
Go
283 lines
8.4 KiB
Go
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
|
|
}
|