重构充值订单模块:用 tb_recharge_order + tb_payment 替换 tb_asset_recharge_record
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m32s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m32s
- 新增 RechargeOrder 和 Payment 模型及对应 Store - 新增 ClientRechargeOrderHandler 提供充值订单列表/详情接口 - 修改 client_wallet.go 使用新表读写充值数据 - 修改 callback/payment.go 将 CRCH 订单路由到 rechargeOrderService - 修改 client_order/service.go 的强充流程使用新表 - 修改 auto_purchase.go 从 tb_recharge_order 读取 linked_package_ids - 修改 order/service.go 的 WalletPay 使用 tb_payment 记录 - 修改 wechat_config_store.go 从 tb_recharge_order 统计待支付充值数 - 移除 AssetRechargeStore 和 AssetRechargeRecord 的注册引用 - 修复文档生成器缺失 ClientRechargeOrder handler - 状态枚举改为 0-based: Pending=0, Paid=1, Closed=2, Refunded=3
This commit is contained in:
196
internal/handler/app/client_recharge_order.go
Normal file
196
internal/handler/app/client_recharge_order.go
Normal file
@@ -0,0 +1,196 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/middleware"
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
||||
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/response"
|
||||
)
|
||||
|
||||
type ClientRechargeOrderHandler struct {
|
||||
rechargeOrderStore *postgres.RechargeOrderStore
|
||||
paymentStore *postgres.PaymentStore
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func NewClientRechargeOrderHandler(
|
||||
rechargeOrderStore *postgres.RechargeOrderStore,
|
||||
paymentStore *postgres.PaymentStore,
|
||||
logger *zap.Logger,
|
||||
) *ClientRechargeOrderHandler {
|
||||
return &ClientRechargeOrderHandler{
|
||||
rechargeOrderStore: rechargeOrderStore,
|
||||
paymentStore: paymentStore,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *ClientRechargeOrderHandler) ListRechargeOrders(c *fiber.Ctx) error {
|
||||
var req dto.ClientRechargeOrderListRequest
|
||||
if err := c.QueryParser(&req); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam)
|
||||
}
|
||||
|
||||
if req.Page < 1 {
|
||||
req.Page = 1
|
||||
}
|
||||
if req.PageSize < 1 {
|
||||
req.PageSize = constants.DefaultPageSize
|
||||
}
|
||||
if req.PageSize > constants.MaxPageSize {
|
||||
req.PageSize = constants.MaxPageSize
|
||||
}
|
||||
|
||||
customerID, ok := middleware.GetCustomerID(c)
|
||||
if !ok || customerID == 0 {
|
||||
return errors.New(errors.CodeUnauthorized)
|
||||
}
|
||||
|
||||
skipCtx := context.WithValue(c.UserContext(), constants.ContextKeySubordinateShopIDs, []uint{})
|
||||
|
||||
params := &postgres.ListRechargeOrderParams{
|
||||
Page: req.Page,
|
||||
PageSize: req.PageSize,
|
||||
UserID: &customerID,
|
||||
}
|
||||
if req.Status > 0 {
|
||||
params.Status = &req.Status
|
||||
}
|
||||
|
||||
orders, total, err := h.rechargeOrderStore.List(skipCtx, params)
|
||||
if err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "查询充值订单失败")
|
||||
}
|
||||
|
||||
items := make([]dto.ClientRechargeOrderListItem, 0, len(orders))
|
||||
for _, order := range orders {
|
||||
if order == nil {
|
||||
continue
|
||||
}
|
||||
items = append(items, dto.ClientRechargeOrderListItem{
|
||||
RechargeOrderID: order.ID,
|
||||
RechargeOrderNo: order.RechargeOrderNo,
|
||||
Amount: order.Amount,
|
||||
Status: order.Status,
|
||||
StatusName: getRechargeOrderStatusName(order.Status),
|
||||
AutoPurchaseStatus: order.AutoPurchaseStatus,
|
||||
CreatedAt: order.CreatedAt.Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
|
||||
return response.SuccessWithPagination(c, items, total, req.Page, req.PageSize)
|
||||
}
|
||||
|
||||
func (h *ClientRechargeOrderHandler) GetRechargeOrderDetail(c *fiber.Ctx) error {
|
||||
idStr := c.Params("id")
|
||||
if idStr == "" {
|
||||
return errors.New(errors.CodeInvalidParam)
|
||||
}
|
||||
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
return errors.New(errors.CodeInvalidParam)
|
||||
}
|
||||
|
||||
customerID, ok := middleware.GetCustomerID(c)
|
||||
if !ok || customerID == 0 {
|
||||
return errors.New(errors.CodeUnauthorized)
|
||||
}
|
||||
|
||||
skipCtx := context.WithValue(c.UserContext(), constants.ContextKeySubordinateShopIDs, []uint{})
|
||||
|
||||
order, err := h.rechargeOrderStore.GetByID(skipCtx, uint(id))
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "record not found") {
|
||||
return errors.New(errors.CodeNotFound, "充值订单不存在")
|
||||
}
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "查询充值订单失败")
|
||||
}
|
||||
|
||||
if order.UserID != customerID {
|
||||
return errors.New(errors.CodeForbidden, "无权访问该充值订单")
|
||||
}
|
||||
|
||||
payments, _ := h.paymentStore.ListByOrderID(skipCtx, order.ID, model.PaymentOrderTypeRecharge)
|
||||
|
||||
var paymentDetails []dto.ClientRechargeOrderPaymentDetail
|
||||
for _, p := range payments {
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
paymentDetails = append(paymentDetails, dto.ClientRechargeOrderPaymentDetail{
|
||||
PaymentID: p.ID,
|
||||
PaymentNo: p.PaymentNo,
|
||||
PaymentMethod: p.PaymentMethod,
|
||||
Amount: p.Amount,
|
||||
Status: p.Status,
|
||||
StatusName: getPaymentStatusName(p.Status),
|
||||
ThirdPartyTradeNo: p.ThirdPartyTradeNo,
|
||||
PaidAt: formatTimePtr(p.PaidAt),
|
||||
CreatedAt: p.CreatedAt.Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
|
||||
resp := &dto.ClientRechargeOrderDetail{
|
||||
RechargeOrderID: order.ID,
|
||||
RechargeOrderNo: order.RechargeOrderNo,
|
||||
Amount: order.Amount,
|
||||
Status: order.Status,
|
||||
StatusName: getRechargeOrderStatusName(order.Status),
|
||||
AutoPurchaseStatus: order.AutoPurchaseStatus,
|
||||
LinkedPackageIDs: order.LinkedPackageIDs,
|
||||
CreatedAt: order.CreatedAt.Format(time.RFC3339),
|
||||
PaidAt: formatTimePtr(order.PaidAt),
|
||||
Payments: paymentDetails,
|
||||
}
|
||||
|
||||
return response.Success(c, resp)
|
||||
}
|
||||
|
||||
func getRechargeOrderStatusName(status int) string {
|
||||
switch status {
|
||||
case model.RechargeOrderStatusPending:
|
||||
return "待支付"
|
||||
case model.RechargeOrderStatusPaid:
|
||||
return "已支付"
|
||||
case model.RechargeOrderStatusClosed:
|
||||
return "已关闭"
|
||||
case model.RechargeOrderStatusRefunded:
|
||||
return "已退款"
|
||||
default:
|
||||
return "未知"
|
||||
}
|
||||
}
|
||||
|
||||
func getPaymentStatusName(status int) string {
|
||||
switch status {
|
||||
case model.PaymentRecordStatusPending:
|
||||
return "待支付"
|
||||
case model.PaymentRecordStatusPaid:
|
||||
return "已支付"
|
||||
case model.PaymentRecordStatusFailed:
|
||||
return "已失败"
|
||||
case model.PaymentRecordStatusRefunded:
|
||||
return "已退款"
|
||||
default:
|
||||
return "未知"
|
||||
}
|
||||
}
|
||||
|
||||
func formatTimePtr(t *time.Time) string {
|
||||
if t == nil {
|
||||
return ""
|
||||
}
|
||||
return t.Format(time.RFC3339)
|
||||
}
|
||||
@@ -31,7 +31,8 @@ type ClientWalletHandler struct {
|
||||
personalDeviceStore *postgres.PersonalCustomerDeviceStore
|
||||
walletStore *postgres.AssetWalletStore
|
||||
transactionStore *postgres.AssetWalletTransactionStore
|
||||
rechargeStore *postgres.AssetRechargeStore
|
||||
rechargeOrderStore *postgres.RechargeOrderStore
|
||||
paymentStore *postgres.PaymentStore
|
||||
rechargeService *rechargeSvc.Service
|
||||
openIDStore *postgres.PersonalCustomerOpenIDStore
|
||||
wechatConfigService *wechatConfigSvc.Service
|
||||
@@ -48,7 +49,8 @@ func NewClientWalletHandler(
|
||||
personalDeviceStore *postgres.PersonalCustomerDeviceStore,
|
||||
walletStore *postgres.AssetWalletStore,
|
||||
transactionStore *postgres.AssetWalletTransactionStore,
|
||||
rechargeStore *postgres.AssetRechargeStore,
|
||||
rechargeOrderStore *postgres.RechargeOrderStore,
|
||||
paymentStore *postgres.PaymentStore,
|
||||
rechargeService *rechargeSvc.Service,
|
||||
openIDStore *postgres.PersonalCustomerOpenIDStore,
|
||||
wechatConfigService *wechatConfigSvc.Service,
|
||||
@@ -63,7 +65,8 @@ func NewClientWalletHandler(
|
||||
personalDeviceStore: personalDeviceStore,
|
||||
walletStore: walletStore,
|
||||
transactionStore: transactionStore,
|
||||
rechargeStore: rechargeStore,
|
||||
rechargeOrderStore: rechargeOrderStore,
|
||||
paymentStore: paymentStore,
|
||||
rechargeService: rechargeService,
|
||||
openIDStore: openIDStore,
|
||||
wechatConfigService: wechatConfigService,
|
||||
@@ -301,33 +304,45 @@ func (h *ClientWalletHandler) CreateRecharge(c *fiber.Ctx) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 支付通道确认可用后,再创建充值记录
|
||||
recharge := &model.AssetRechargeRecord{
|
||||
// 支付通道确认可用后,再创建充值订单和支付记录
|
||||
rechargeOrder := &model.RechargeOrder{
|
||||
RechargeOrderNo: rechargeNo,
|
||||
UserID: resolved.CustomerID,
|
||||
AssetWalletID: wallet.ID,
|
||||
ResourceType: resolved.ResourceType,
|
||||
ResourceID: resolved.Asset.AssetID,
|
||||
RechargeNo: rechargeNo,
|
||||
Amount: req.Amount,
|
||||
PaymentMethod: constants.RechargeMethodWechat,
|
||||
Status: model.RechargeOrderStatusPending,
|
||||
PaymentConfigID: &config.ID,
|
||||
Status: constants.RechargeStatusPending,
|
||||
ShopIDTag: wallet.ShopIDTag,
|
||||
EnterpriseIDTag: wallet.EnterpriseIDTag,
|
||||
OperatorType: constants.OperatorTypePersonalCustomer,
|
||||
Generation: resolved.Generation,
|
||||
}
|
||||
if err := h.rechargeStore.Create(resolved.SkipPermissionCtx, recharge); err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "创建充值记录失败")
|
||||
if err := h.rechargeOrderStore.Create(resolved.SkipPermissionCtx, rechargeOrder); err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "创建充值订单失败")
|
||||
}
|
||||
|
||||
payment := &model.Payment{
|
||||
PaymentNo: rechargeNo,
|
||||
OrderID: rechargeOrder.ID,
|
||||
OrderType: model.PaymentOrderTypeRecharge,
|
||||
PaymentMethod: model.PaymentByWechat,
|
||||
Amount: req.Amount,
|
||||
Status: model.PaymentRecordStatusPending,
|
||||
PaymentConfigID: &config.ID,
|
||||
}
|
||||
if err := h.paymentStore.Create(resolved.SkipPermissionCtx, payment); err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "创建支付记录失败")
|
||||
}
|
||||
|
||||
payConfig := buildClientRechargePayConfig(appID, payResult)
|
||||
resp := &dto.ClientRechargeResponse{
|
||||
Recharge: dto.ClientRechargeResult{
|
||||
RechargeID: recharge.ID,
|
||||
RechargeNo: recharge.RechargeNo,
|
||||
Amount: recharge.Amount,
|
||||
Status: recharge.Status,
|
||||
RechargeID: rechargeOrder.ID,
|
||||
RechargeNo: rechargeOrder.RechargeOrderNo,
|
||||
Amount: rechargeOrder.Amount,
|
||||
Status: rechargeOrder.Status,
|
||||
},
|
||||
PayConfig: payConfig,
|
||||
}
|
||||
@@ -359,7 +374,7 @@ func (h *ClientWalletHandler) GetRechargeList(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
query := h.db.WithContext(resolved.SkipPermissionCtx).
|
||||
Model(&model.AssetRechargeRecord{}).
|
||||
Model(&model.RechargeOrder{}).
|
||||
Where("resource_type = ? AND resource_id = ? AND generation = ?", resolved.ResourceType, resolved.Asset.AssetID, resolved.Generation)
|
||||
if req.Status != nil {
|
||||
query = query.Where("status = ?", *req.Status)
|
||||
@@ -367,28 +382,28 @@ func (h *ClientWalletHandler) GetRechargeList(c *fiber.Ctx) error {
|
||||
|
||||
var total int64
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "查询充值记录总数失败")
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "查询充值订单总数失败")
|
||||
}
|
||||
|
||||
var records []*model.AssetRechargeRecord
|
||||
var orders []*model.RechargeOrder
|
||||
offset := (req.Page - 1) * req.PageSize
|
||||
if err := query.Order("created_at DESC").Offset(offset).Limit(req.PageSize).Find(&records).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "查询充值记录失败")
|
||||
if err := query.Order("created_at DESC").Offset(offset).Limit(req.PageSize).Find(&orders).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "查询充值订单失败")
|
||||
}
|
||||
|
||||
items := make([]dto.ClientRechargeListItem, 0, len(records))
|
||||
for _, record := range records {
|
||||
if record == nil {
|
||||
items := make([]dto.ClientRechargeListItem, 0, len(orders))
|
||||
for _, order := range orders {
|
||||
if order == nil {
|
||||
continue
|
||||
}
|
||||
items = append(items, dto.ClientRechargeListItem{
|
||||
RechargeID: record.ID,
|
||||
RechargeNo: record.RechargeNo,
|
||||
Amount: record.Amount,
|
||||
Status: record.Status,
|
||||
PaymentMethod: record.PaymentMethod,
|
||||
CreatedAt: record.CreatedAt.Format(time.RFC3339),
|
||||
AutoPurchaseStatus: record.AutoPurchaseStatus,
|
||||
RechargeID: order.ID,
|
||||
RechargeNo: order.RechargeOrderNo,
|
||||
Amount: order.Amount,
|
||||
Status: order.Status,
|
||||
PaymentMethod: "", // 支付方式在 Payment 表中,列表查询不返回
|
||||
CreatedAt: order.CreatedAt.Format(time.RFC3339),
|
||||
AutoPurchaseStatus: order.AutoPurchaseStatus,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user