优化支付流程,扩展产品价格返回字段
This commit is contained in:
@@ -53,6 +53,7 @@ func (s *channelGostProvider) prepareCreate(ctx *channelCreateContext) (*channel
|
||||
Recorders: []g.GostRecorderConfig{
|
||||
{Name: "record-http-otel", Record: "recorder.service.handler"},
|
||||
},
|
||||
Limiter: "limiter-8m",
|
||||
}
|
||||
|
||||
if ctx.AuthWhitelist {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
q "platform/web/queries"
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
"gorm.io/gen/field"
|
||||
)
|
||||
|
||||
@@ -20,7 +21,7 @@ func (s *productService) AllProducts() ([]*m.Product, error) {
|
||||
Find()
|
||||
}
|
||||
|
||||
func (s *productService) AllProductSaleInfos() ([]*m.Product, error) {
|
||||
func (s *productService) AllProductSaleInfos() ([]any, error) {
|
||||
products, err := q.Product.
|
||||
Select(
|
||||
q.Product.ID,
|
||||
@@ -43,7 +44,17 @@ func (s *productService) AllProductSaleInfos() ([]*m.Product, error) {
|
||||
pids[i] = p.ID
|
||||
}
|
||||
|
||||
skus, err := q.ProductSku.
|
||||
type SkuInfo struct {
|
||||
ID int32 `json:"id"`
|
||||
ProductID int32 `json:"product_id"`
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
Price decimal.Decimal `json:"price"`
|
||||
CountMin decimal.Decimal `json:"count_min"`
|
||||
Discount float64 `json:"discount"`
|
||||
}
|
||||
var skus []*SkuInfo
|
||||
err = q.ProductSku.
|
||||
Select(
|
||||
q.ProductSku.ID,
|
||||
q.ProductSku.ProductID,
|
||||
@@ -51,29 +62,47 @@ func (s *productService) AllProductSaleInfos() ([]*m.Product, error) {
|
||||
q.ProductSku.Code,
|
||||
q.ProductSku.Price,
|
||||
q.ProductSku.CountMin,
|
||||
q.ProductDiscount.Discount,
|
||||
).
|
||||
Where(
|
||||
q.ProductSku.ProductID.In(pids...),
|
||||
q.ProductSku.Status.Eq(int32(m.SkuStatusEnabled)),
|
||||
).
|
||||
LeftJoin(q.ProductDiscount, q.ProductDiscount.ID.EqCol(q.ProductSku.DiscountId)).
|
||||
Order(q.ProductSku.Sort).
|
||||
Find()
|
||||
Scan(&skus)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pmap := make(map[int32]*m.Product, len(products))
|
||||
type ProductInfo struct {
|
||||
m.Product
|
||||
Skus []*SkuInfo `json:"skus,omitempty"`
|
||||
}
|
||||
pmap := make(map[int32]*ProductInfo, len(products))
|
||||
for _, p := range products {
|
||||
pmap[p.ID] = p
|
||||
p.Skus = make([]*m.ProductSku, 0)
|
||||
pmap[p.ID] = &ProductInfo{Product: *p, Skus: make([]*SkuInfo, 0)}
|
||||
}
|
||||
for _, s := range skus {
|
||||
if p, ok := pmap[s.ProductID]; ok {
|
||||
p.Skus = append(p.Skus, s)
|
||||
p.Skus = append(p.Skus, &SkuInfo{
|
||||
ID: s.ID,
|
||||
ProductID: s.ProductID,
|
||||
Name: s.Name,
|
||||
Code: s.Code,
|
||||
Price: s.Price,
|
||||
CountMin: s.CountMin,
|
||||
Discount: s.Discount,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return products, nil
|
||||
plist := make([]any, 0, len(pmap))
|
||||
for _, p := range pmap {
|
||||
plist = append(plist, p)
|
||||
}
|
||||
|
||||
return plist, nil
|
||||
}
|
||||
|
||||
// 新增产品
|
||||
|
||||
@@ -204,12 +204,7 @@ func (s *tradeService) Create(user *m.User, tradeData *CreateTradeData, productD
|
||||
// 缓存产品数据
|
||||
w := bytes.Buffer{}
|
||||
gob.NewEncoder(&w).Encode(detail)
|
||||
err = g.Redis.Set(
|
||||
context.Background(),
|
||||
tradeProductKey(tradeNo),
|
||||
w.Bytes(),
|
||||
expireIn,
|
||||
).Err()
|
||||
err = g.Redis.Set(context.Background(), tradeProductKey(tradeNo), w.Bytes(), 0).Err()
|
||||
if err != nil {
|
||||
return nil, core.NewServErr("保存购买信息失败", err)
|
||||
}
|
||||
@@ -271,7 +266,8 @@ func (s *tradeService) OnCompleteTrade(user *m.User, interNo string, outerNo str
|
||||
}
|
||||
|
||||
// 恢复购买信息;如果反序列化失败,检查开头 init 函数中是否注册了对应的 struct 类型
|
||||
detailBytes, err := g.Redis.Get(context.Background(), tradeProductKey(interNo)).Bytes()
|
||||
tradeKey := tradeProductKey(interNo)
|
||||
detailBytes, err := g.Redis.Get(context.Background(), tradeKey).Bytes()
|
||||
if err != nil {
|
||||
return core.NewServErr("恢复购买信息失败", err)
|
||||
}
|
||||
@@ -344,6 +340,12 @@ func (s *tradeService) OnCompleteTrade(user *m.User, interNo string, outerNo str
|
||||
}
|
||||
}
|
||||
|
||||
// 删除缓存
|
||||
err = g.Redis.Del(context.Background(), tradeKey).Err()
|
||||
if err != nil {
|
||||
return core.NewServErr("删除缓存失败", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
@@ -353,6 +355,23 @@ func (s *tradeService) OnCompleteTrade(user *m.User, interNo string, outerNo str
|
||||
return nil
|
||||
}
|
||||
|
||||
// 转换交易
|
||||
func (s *tradeService) ConvertTradeToBalance(admin *m.Admin, ref *TradeRef) error {
|
||||
trade, err := q.Trade.Where(q.Trade.InnerNo.Eq(ref.TradeNo)).First()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user, err := q.User.Where(q.User.ID.Eq(trade.UserID)).First()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return q.Q.Transaction(func(q *q.Query) error {
|
||||
return User.UpdateBalance(q, user, trade.Payment, "管理员订单补余额", &admin.ID, nil)
|
||||
})
|
||||
}
|
||||
|
||||
// 取消交易
|
||||
func (s *tradeService) CancelTrade(ref *TradeRef) error {
|
||||
now := time.Now()
|
||||
@@ -394,7 +413,7 @@ func (s *tradeService) CancelTrade(ref *TradeRef) error {
|
||||
})
|
||||
if err != nil {
|
||||
slog.Debug(fmt.Sprintf("订单无需关闭: %s", err.Error()))
|
||||
return nil
|
||||
return ErrTradeStatusIgnored
|
||||
}
|
||||
|
||||
default:
|
||||
@@ -575,6 +594,12 @@ func (s *tradeService) CheckTrade(ref *TradeRef) (*CheckTradeResult, error) {
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// 更新备注
|
||||
func (s *tradeService) UpdateRemark(tradeNo string, remark string) error {
|
||||
_, err := q.Trade.Where(q.Trade.InnerNo.Eq(tradeNo)).UpdateColumn(q.Trade.Remark, remark)
|
||||
return err
|
||||
}
|
||||
|
||||
func tradeProductKey(no string) string {
|
||||
return fmt.Sprintf("trade:%s:product", no)
|
||||
}
|
||||
@@ -639,4 +664,5 @@ func (e TradeErr) Error() string {
|
||||
|
||||
var (
|
||||
ErrTransactionNotSupported = core.NewBizErr("不支持的支付方式")
|
||||
ErrTradeStatusIgnored = core.NewBizErr("交易状态已忽略")
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user