2026-04-02 10:22:36 +08:00
|
|
|
|
package service
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"context"
|
|
|
|
|
|
"errors"
|
|
|
|
|
|
"fmt"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
|
2026-06-10 16:39:09 +08:00
|
|
|
|
"gitea.redpowerfuture.com/red-future/common/consul"
|
|
|
|
|
|
"gitea.redpowerfuture.com/red-future/common/http"
|
2026-04-02 10:22:36 +08:00
|
|
|
|
"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
|
|
|
|
|
|
}
|