Files
shop-user-trade/service/market/market_service.go
2026-04-02 10:22:36 +08:00

250 lines
6.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package service
import (
"context"
"errors"
"fmt"
"time"
"gitea.com/red-future/common/consul"
"gitea.com/red-future/common/http"
"github.com/gogf/gf/v2/util/gconv"
marketConsts "shop-user-trade/consts/market"
marketDao "shop-user-trade/dao/market"
marketDto "shop-user-trade/model/dto/market"
marketEntity "shop-user-trade/model/entity/market"
)
type market struct{}
// Market 市场服务
var Market = new(market)
// Create 创建市场物品
func (s *market) Create(ctx context.Context, req *marketDto.CreateMarketReq) (int64, error) {
// 检查该背包项是否已经在市场中
existing, _ := marketDao.Market.GetByKnapsackID(ctx, req.KnapsackID)
if existing != nil && existing.Id > 0 {
return 0, errors.New("该物品已在市场中")
}
// 设置上架过期时间默认7天
if req.ListExpireAt == nil {
defaultExpire := time.Now().Add(7 * 24 * time.Hour).Unix()
req.ListExpireAt = &defaultExpire
}
id, err := marketDao.Market.Insert(ctx, req)
if err != nil {
return 0, fmt.Errorf("创建市场物品失败: %w", err)
}
return id, nil
}
// GetOne 获取单个市场物品
func (s *market) GetOne(ctx context.Context, req *marketDto.GetMarketReq) (*marketDto.GetMarketRes, error) {
item, err := marketDao.Market.GetOne(ctx, req)
if err != nil {
return nil, err
}
if item == nil {
return nil, errors.New("市场物品不存在")
}
return &marketDto.GetMarketRes{
MarketItem: s.entityToItem(item),
}, nil
}
// List 获取市场列表(支持分页和搜索)
func (s *market) List(ctx context.Context, req *marketDto.ListMarketReq) (*marketDto.ListMarketRes, error) {
list, total, err := marketDao.Market.List(ctx, req)
if err != nil {
return nil, err
}
res := &marketDto.ListMarketRes{Total: total}
for _, item := range list {
itemCopy := item
res.List = append(res.List, s.entityToItem(&itemCopy))
}
return res, nil
}
// Unlist 下架市场物品
func (s *market) Unlist(ctx context.Context, req *marketDto.UnlistMarketReq) error {
item, err := marketDao.Market.GetByID(ctx, req.ID)
if err != nil {
return err
}
if item == nil {
return errors.New("市场物品不存在")
}
if item.Status != marketConsts.MarketStatusActive {
return errors.New("只有活跃状态的物品才能下架")
}
inactiveStatus := marketConsts.MarketStatusInactive
updateReq := &marketDto.UpdateMarketReq{
Id: item.Id,
Status: &inactiveStatus,
Updater: req.OperatorName,
}
if _, err = marketDao.Market.Update(ctx, updateReq); err != nil {
return fmt.Errorf("更新市场物品失败: %w", err)
}
return nil
}
// UpdatePrice 更新价格
func (s *market) UpdatePrice(ctx context.Context, req *marketDto.UpdatePriceReq) error {
item, err := marketDao.Market.GetByID(ctx, req.ID)
if err != nil {
return err
}
if item == nil {
return errors.New("市场物品不存在")
}
if item.Status != marketConsts.MarketStatusActive {
return errors.New("只有活跃状态的物品才能更新价格")
}
updateReq := &marketDto.UpdateMarketReq{
Id: item.Id,
Price: &req.Price,
Updater: req.OperatorName,
}
if _, err = marketDao.Market.Update(ctx, updateReq); err != nil {
return fmt.Errorf("更新价格失败: %w", err)
}
return nil
}
// Buy 购买市场物品 - 调用order模块创建订单
func (s *market) Buy(ctx context.Context, req *marketDto.BuyMarketReq) (string, error) {
item, err := marketDao.Market.GetByID(ctx, req.MarketID)
if err != nil {
return "", err
}
if item == nil {
return "", errors.New("市场物品不存在")
}
if err = s.canBeBought(item); err != nil {
return "", err
}
// 检查买家不能是卖家
if item.UserID == req.BuyerID {
return "", errors.New("不能购买自己上架的物品")
}
// 调用order模块创建订单
orderAddr, err := consul.GetInstanceAddr(ctx, "order")
if err != nil {
return "", fmt.Errorf("获取order服务地址失败: %w", err)
}
orderReq := map[string]interface{}{
"user_id": req.BuyerID,
"order_type": "market",
"subject": item.AssetName,
"description": item.Description,
"order_items": []map[string]interface{}{
{
"asset_id": item.AssetID,
"asset_name": item.AssetName,
"asset_type": item.Type,
"image_url": item.ImageURL,
"stocks": []map[string]interface{}{
{
"stock_id": item.StockDetailID,
"batch_id": item.BatchID,
"batch_no": item.BatchNo,
"quantity": 1,
"price": item.Price,
"stock_mode": item.StockMode,
"stock_attrs": map[string]interface{}{
"market_id": item.Id,
"seller_id": item.UserID,
},
},
},
},
},
"shipping_info": map[string]interface{}{},
}
orderRes := &struct {
Code int `json:"code"`
Message string `json:"message"`
Data struct {
OrderNo string `json:"order_no"`
TotalAmount int64 `json:"total_amount"`
PayAmount int64 `json:"pay_amount"`
ExpiredAt string `json:"expired_at"`
} `json:"data"`
}{}
err = http.Post(ctx, fmt.Sprintf("http://%s/order/create", orderAddr), nil, orderRes, orderReq) // #nosec G107
if err != nil {
return "", fmt.Errorf("创建订单失败: %w", err)
}
// 更新市场物品状态为已售出
soldStatus := marketConsts.MarketStatusSold
updateReq := &marketDto.UpdateMarketReq{
Id: item.Id,
Status: &soldStatus,
}
if _, err = marketDao.Market.Update(ctx, updateReq); err != nil {
return "", fmt.Errorf("更新市场物品失败: %w", err)
}
return orderRes.Data.OrderNo, nil
}
// ExpireExpiredItems 将过期的市场物品标记为过期状态(定时任务调用)
func (s *market) ExpireExpiredItems(ctx context.Context) (int64, error) {
expiredList, err := marketDao.Market.ListExpired(ctx)
if err != nil {
return 0, fmt.Errorf("查询过期物品失败: %w", err)
}
if len(expiredList) == 0 {
return 0, nil
}
count := int64(len(expiredList))
expiredStatus := marketConsts.MarketStatusExpired
for _, item := range expiredList {
updateReq := &marketDto.UpdateMarketReq{
Id: item.Id,
Status: &expiredStatus,
}
if _, err = marketDao.Market.Update(ctx, updateReq); err != nil {
return count, fmt.Errorf("更新过期物品状态失败: %w", err)
}
}
return count, nil
}
// canBeBought 检查市场物品是否可以购买
func (s *market) canBeBought(item *marketEntity.Market) error {
if item.Status != marketConsts.MarketStatusActive {
return errors.New("物品状态不可购买")
}
if item.ListExpireAt != nil && *item.ListExpireAt < time.Now().Unix() {
return errors.New("物品已过期")
}
return nil
}
// entityToItem 实体转换为Item
func (s *market) entityToItem(e *marketEntity.Market) *marketDto.MarketItem {
item := &marketDto.MarketItem{}
if err := gconv.Struct(e, item); err != nil {
return item
}
item.ID = e.Id
item.Status = e.Status
if e.CreatedAt != nil {
item.CreatedAt = e.CreatedAt.String()
}
if e.UpdatedAt != nil {
item.UpdatedAt = e.UpdatedAt.String()
}
return item
}