驳回
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Failing after 7m17s

This commit is contained in:
2026-06-26 12:12:15 +09:00
parent 8cf921f11c
commit f4b6a55b27
18 changed files with 119 additions and 431 deletions

View File

@@ -141,8 +141,7 @@ func initHandlers(svc *services, deps *Dependencies) *Handlers {
AgentRecharge: admin.NewAgentRechargeHandler(svc.AgentRecharge, validate),
Refund: admin.NewRefundHandler(svc.Refund),
OrderPackageInvalidate: admin.NewOrderPackageInvalidateHandler(svc.OrderPackageInvalidate),
ClientWechat: app.NewClientWechatHandler(svc.WechatConfig, deps.Redis, deps.Logger),
AdminRechargeOrder: admin.NewAdminRechargeOrderHandler(rechargeOrderService, validate, deps.Logger),
ClientWechat: app.NewClientWechatHandler(svc.WechatConfig, deps.Redis, deps.Logger),
SuperAdmin: admin.NewSuperAdminHandler(svc.OperationPassword),
AgentOpenAPI: openapiHandler.NewHandler(svc.AgentOpenAPI, validate),
}

View File

@@ -65,7 +65,6 @@ type Handlers struct {
Refund *admin.RefundHandler
OrderPackageInvalidate *admin.OrderPackageInvalidateHandler
ClientWechat *app.ClientWechatHandler
AdminRechargeOrder *admin.AdminRechargeOrderHandler
SuperAdmin *admin.SuperAdminHandler
AgentOpenAPI *openapiHandler.Handler
}

View File

@@ -74,6 +74,29 @@ func (h *AgentRechargeHandler) Get(c *fiber.Ctx) error {
return response.Success(c, result)
}
// Reject 驳回代理充值订单
// POST /api/admin/agent-recharges/:id/reject
func (h *AgentRechargeHandler) Reject(c *fiber.Ctx) error {
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
if err != nil {
return errors.New(errors.CodeInvalidParam, "无效的充值记录ID")
}
var req dto.AgentRechargeRejectRequest
if err := c.BodyParser(&req); err != nil {
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
}
if err := h.validator.Struct(&req); err != nil {
return errors.New(errors.CodeInvalidParam)
}
if err := h.service.Reject(c.UserContext(), uint(id), req.RejectionReason); err != nil {
return err
}
return response.Success(c, nil)
}
// OfflinePay 确认线下充值
// POST /api/admin/agent-recharges/:id/offline-pay
func (h *AgentRechargeHandler) OfflinePay(c *fiber.Ctx) error {

View File

@@ -1,212 +0,0 @@
package admin
import (
"strconv"
"time"
"github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/internal/model/dto"
rechargeOrderSvc "github.com/break/junhong_cmp_fiber/internal/service/recharge_order"
"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"
)
// AdminRechargeOrderHandler 后台充值订单 Handler
type AdminRechargeOrderHandler struct {
service *rechargeOrderSvc.Service
validator *validator.Validate
logger *zap.Logger
}
// NewAdminRechargeOrderHandler 创建后台充值订单 Handler
func NewAdminRechargeOrderHandler(service *rechargeOrderSvc.Service, validator *validator.Validate, logger *zap.Logger) *AdminRechargeOrderHandler {
return &AdminRechargeOrderHandler{service: service, validator: validator, logger: logger}
}
// List 后台充值订单列表
// GET /api/admin/recharge-orders
func (h *AdminRechargeOrderHandler) List(c *fiber.Ctx) error {
var req dto.AdminRechargeOrderListRequest
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
}
params := &postgres.ListRechargeOrderParams{
Page: req.Page,
PageSize: req.PageSize,
Status: req.Status,
}
if req.ResourceType != "" && req.ResourceID != nil {
params.ResourceType = &req.ResourceType
params.ResourceID = req.ResourceID
}
if req.StartTime != "" {
t, err := time.Parse(time.RFC3339, req.StartTime)
if err == nil {
params.StartTime = t
}
}
if req.EndTime != "" {
t, err := time.Parse(time.RFC3339, req.EndTime)
if err == nil {
params.EndTime = t
}
}
orders, total, err := h.service.AdminList(c.UserContext(), params)
if err != nil {
return err
}
items := make([]dto.AdminRechargeOrderListItem, 0, len(orders))
for _, order := range orders {
if order == nil {
continue
}
items = append(items, dto.AdminRechargeOrderListItem{
ID: order.ID,
RechargeOrderNo: order.RechargeOrderNo,
UserID: order.UserID,
ResourceType: order.ResourceType,
ResourceID: order.ResourceID,
Amount: order.Amount,
Status: order.Status,
StatusName: getAdminRechargeOrderStatusName(order.Status),
AutoPurchaseStatus: order.AutoPurchaseStatus,
RejectionReason: order.RejectionReason,
CreatedAt: order.CreatedAt.Format(time.RFC3339),
})
}
return response.SuccessWithPagination(c, items, total, req.Page, req.PageSize)
}
// Get 后台充值订单详情
// GET /api/admin/recharge-orders/:id
func (h *AdminRechargeOrderHandler) Get(c *fiber.Ctx) error {
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
if err != nil {
return errors.New(errors.CodeInvalidParam, "无效的订单 ID")
}
order, payments, err := h.service.AdminGet(c.UserContext(), uint(id))
if err != nil {
return err
}
var paymentDetails []dto.AdminRechargeOrderPaymentDetail
for _, p := range payments {
if p == nil {
continue
}
paymentDetails = append(paymentDetails, dto.AdminRechargeOrderPaymentDetail{
PaymentID: p.ID,
PaymentNo: p.PaymentNo,
PaymentMethod: p.PaymentMethod,
Amount: p.Amount,
Status: p.Status,
StatusName: getAdminPaymentStatusName(p.Status),
ThirdPartyTradeNo: p.ThirdPartyTradeNo,
PaidAt: formatAdminTimePtr(p.PaidAt),
CreatedAt: p.CreatedAt.Format(time.RFC3339),
})
}
detail := &dto.AdminRechargeOrderDetail{
ID: order.ID,
RechargeOrderNo: order.RechargeOrderNo,
UserID: order.UserID,
AssetWalletID: order.AssetWalletID,
ResourceType: order.ResourceType,
ResourceID: order.ResourceID,
Amount: order.Amount,
Status: order.Status,
StatusName: getAdminRechargeOrderStatusName(order.Status),
AutoPurchaseStatus: order.AutoPurchaseStatus,
LinkedPackageIDs: order.LinkedPackageIDs,
RejectionReason: order.RejectionReason,
CreatedAt: order.CreatedAt.Format(time.RFC3339),
PaidAt: formatAdminTimePtr(order.PaidAt),
Payments: paymentDetails,
}
return response.Success(c, detail)
}
// Reject 驳回充值订单
// POST /api/admin/recharge-orders/:id/reject
func (h *AdminRechargeOrderHandler) Reject(c *fiber.Ctx) error {
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
if err != nil {
return errors.New(errors.CodeInvalidParam, "无效的订单 ID")
}
var req dto.AdminRechargeOrderRejectRequest
if err := c.BodyParser(&req); err != nil {
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
}
if err := h.validator.Struct(&req); err != nil {
return errors.New(errors.CodeInvalidParam)
}
if err := h.service.Reject(c.UserContext(), uint(id), req.RejectionReason); err != nil {
return err
}
return response.Success(c, nil)
}
func getAdminRechargeOrderStatusName(status int) string {
switch status {
case model.RechargeOrderStatusPending:
return "待支付"
case model.RechargeOrderStatusPaid:
return "已支付"
case model.RechargeOrderStatusClosed:
return "已关闭"
case model.RechargeOrderStatusRefunded:
return "已退款"
case model.RechargeOrderStatusRejected:
return "已驳回"
default:
return "未知"
}
}
func getAdminPaymentStatusName(status int) string {
switch status {
case model.PaymentRecordStatusPending:
return "待支付"
case model.PaymentRecordStatusPaid:
return "已支付"
case model.PaymentRecordStatusFailed:
return "已失败"
case model.PaymentRecordStatusRefunded:
return "已退款"
default:
return "未知"
}
}
func formatAdminTimePtr(t *time.Time) string {
if t == nil {
return ""
}
return t.Format(time.RFC3339)
}

View File

@@ -89,7 +89,6 @@ func (h *ClientRechargeOrderHandler) ListRechargeOrders(c *fiber.Ctx) error {
Status: order.Status,
StatusName: getRechargeOrderStatusName(order.Status),
AutoPurchaseStatus: order.AutoPurchaseStatus,
RejectionReason: order.RejectionReason,
CreatedAt: order.CreatedAt.Format(time.RFC3339),
})
}
@@ -155,7 +154,6 @@ func (h *ClientRechargeOrderHandler) GetRechargeOrderDetail(c *fiber.Ctx) error
StatusName: getRechargeOrderStatusName(order.Status),
AutoPurchaseStatus: order.AutoPurchaseStatus,
LinkedPackageIDs: order.LinkedPackageIDs,
RejectionReason: order.RejectionReason,
CreatedAt: order.CreatedAt.Format(time.RFC3339),
PaidAt: formatTimePtr(order.PaidAt),
Payments: paymentDetails,
@@ -174,8 +172,6 @@ func getRechargeOrderStatusName(status int) string {
return "已关闭"
case model.RechargeOrderStatusRefunded:
return "已退款"
case model.RechargeOrderStatusRejected:
return "已驳回"
default:
return "未知"
}

View File

@@ -84,6 +84,7 @@ type AgentRechargeRecord struct {
Status int `gorm:"column:status;type:int;not null;default:1;comment:充值状态(1-待支付 2-已支付 3-已完成 4-已关闭 5-已退款)" json:"status"`
PaymentVoucherKey StringJSONBArray `gorm:"column:payment_voucher_key;type:jsonb;comment:支付凭证对象存储Key列表线下支付时必填最多5个微信支付时为空" json:"payment_voucher_key"`
Remark string `gorm:"column:remark;type:text;comment:运营备注(创建时填写,不可修改)" json:"remark,omitempty"`
RejectionReason *string `gorm:"column:rejection_reason;type:varchar(500);comment:驳回原因,仅驳回时写入" json:"rejection_reason,omitempty"`
PaidAt *time.Time `gorm:"column:paid_at;comment:支付时间" json:"paid_at,omitempty"`
CompletedAt *time.Time `gorm:"column:completed_at;comment:完成时间" json:"completed_at,omitempty"`
ShopIDTag uint `gorm:"column:shop_id_tag;not null;index;comment:店铺ID标签(多租户过滤)" json:"shop_id_tag"`

View File

@@ -1,64 +0,0 @@
package dto
// AdminRechargeOrderListRequest 后台充值订单列表请求
type AdminRechargeOrderListRequest struct {
Page int `json:"page" query:"page" description:"页码"`
PageSize int `json:"page_size" query:"page_size" description:"每页数量"`
Status *int `json:"status" query:"status" description:"状态过滤 (0:待支付, 1:已支付, 2:已关闭, 3:已退款, 4:已驳回)"`
ResourceType string `json:"resource_type" query:"resource_type" description:"资产类型 (iot_card/device)"`
ResourceID *uint `json:"resource_id" query:"resource_id" description:"资产 ID"`
StartTime string `json:"start_time" query:"start_time" description:"创建时间起始RFC3339"`
EndTime string `json:"end_time" query:"end_time" description:"创建时间结束RFC3339"`
}
// AdminRechargeOrderListItem 后台充值订单列表项
type AdminRechargeOrderListItem struct {
ID uint `json:"id" description:"充值订单ID"`
RechargeOrderNo string `json:"recharge_order_no" description:"充值订单号"`
UserID uint `json:"user_id" description:"用户ID"`
ResourceType string `json:"resource_type" description:"资产类型 (iot_card/device)"`
ResourceID uint `json:"resource_id" description:"资产ID"`
Amount int64 `json:"amount" description:"充值金额(分)"`
Status int `json:"status" description:"状态 (0:待支付, 1:已支付, 2:已关闭, 3:已退款, 4:已驳回)"`
StatusName string `json:"status_name" description:"状态名称(中文)"`
AutoPurchaseStatus string `json:"auto_purchase_status" description:"自动购包状态 (pending/success/failed)"`
RejectionReason *string `json:"rejection_reason,omitempty" description:"驳回原因(已驳回时有值)"`
CreatedAt string `json:"created_at" description:"创建时间"`
}
// AdminRechargeOrderDetail 后台充值订单详情
type AdminRechargeOrderDetail struct {
ID uint `json:"id" description:"充值订单ID"`
RechargeOrderNo string `json:"recharge_order_no" description:"充值订单号"`
UserID uint `json:"user_id" description:"用户ID"`
AssetWalletID uint `json:"asset_wallet_id" description:"资产钱包ID"`
ResourceType string `json:"resource_type" description:"资产类型 (iot_card/device)"`
ResourceID uint `json:"resource_id" description:"资产ID"`
Amount int64 `json:"amount" description:"充值金额(分)"`
Status int `json:"status" description:"状态 (0:待支付, 1:已支付, 2:已关闭, 3:已退款, 4:已驳回)"`
StatusName string `json:"status_name" description:"状态名称(中文)"`
AutoPurchaseStatus string `json:"auto_purchase_status" description:"自动购包状态"`
LinkedPackageIDs interface{} `json:"linked_package_ids" description:"关联套餐ID列表"`
RejectionReason *string `json:"rejection_reason,omitempty" description:"驳回原因(已驳回时有值)"`
CreatedAt string `json:"created_at" description:"创建时间"`
PaidAt string `json:"paid_at,omitempty" description:"支付时间"`
Payments []AdminRechargeOrderPaymentDetail `json:"payments,omitempty" description:"支付记录列表"`
}
// AdminRechargeOrderPaymentDetail 后台充值订单支付记录
type AdminRechargeOrderPaymentDetail struct {
PaymentID uint `json:"payment_id" description:"支付记录ID"`
PaymentNo string `json:"payment_no" description:"支付单号"`
PaymentMethod string `json:"payment_method" description:"支付方式 (wechat/alipay/wallet)"`
Amount int64 `json:"amount" description:"支付金额(分)"`
Status int `json:"status" description:"状态 (0:待支付, 1:已支付, 2:已失败, 3:已退款)"`
StatusName string `json:"status_name" description:"状态名称(中文)"`
ThirdPartyTradeNo string `json:"third_party_trade_no,omitempty" description:"第三方交易号"`
PaidAt string `json:"paid_at,omitempty" description:"支付时间"`
CreatedAt string `json:"created_at" description:"创建时间"`
}
// AdminRechargeOrderRejectRequest 后台驳回充值订单请求
type AdminRechargeOrderRejectRequest struct {
RejectionReason string `json:"rejection_reason" validate:"required,max=500" description:"驳回原因必填最多500字"`
}

View File

@@ -22,24 +22,30 @@ type AgentOfflinePayParams struct {
// AgentRechargeResponse 代理充值记录响应
type AgentRechargeResponse struct {
ID uint `json:"id" description:"充值记录ID"`
RechargeNo string `json:"recharge_no" description:"充值单号(ARCH前缀)"`
ShopID uint `json:"shop_id" description:"店铺ID"`
ShopName string `json:"shop_name" description:"店铺名称"`
AgentWalletID uint `json:"agent_wallet_id" description:"代理钱包ID"`
Amount int64 `json:"amount" description:"充值金额(分)"`
PaymentMethod string `json:"payment_method" description:"支付方式 (wechat:微信在线支付, offline:线下转账)"`
PaymentChannel string `json:"payment_channel" description:"实际支付通道 (wechat_direct:微信直连, fuyou:富友, offline:线下转账)"`
PaymentConfigID *uint `json:"payment_config_id" description:"关联支付配置ID线下充值为null"`
PaymentTransactionID string `json:"payment_transaction_id" description:"第三方支付流水号"`
ID uint `json:"id" description:"充值记录ID"`
RechargeNo string `json:"recharge_no" description:"充值单号(ARCH前缀)"`
ShopID uint `json:"shop_id" description:"店铺ID"`
ShopName string `json:"shop_name" description:"店铺名称"`
AgentWalletID uint `json:"agent_wallet_id" description:"代理钱包ID"`
Amount int64 `json:"amount" description:"充值金额(分)"`
PaymentMethod string `json:"payment_method" description:"支付方式 (wechat:微信在线支付, offline:线下转账)"`
PaymentChannel string `json:"payment_channel" description:"实际支付通道 (wechat_direct:微信直连, fuyou:富友, offline:线下转账)"`
PaymentConfigID *uint `json:"payment_config_id" description:"关联支付配置ID线下充值为null"`
PaymentTransactionID string `json:"payment_transaction_id" description:"第三方支付流水号"`
PaymentVoucherKey []string `json:"payment_voucher_key" description:"支付凭证对象存储Key列表线下支付时存在最多5个"`
Remark string `json:"remark,omitempty" description:"运营备注"`
Status int `json:"status" description:"状态 (1:待支付, 2:已支付, 3:已完成, 4:已关闭, 5:已退款)"`
StatusName string `json:"status_name" description:"状态名称(中文)"`
PaidAt *string `json:"paid_at" description:"支付时间"`
CompletedAt *string `json:"completed_at" description:"完成时间"`
CreatedAt string `json:"created_at" description:"创建时间"`
UpdatedAt string `json:"updated_at" description:"更新时间"`
Status int `json:"status" description:"状态 (1:待支付, 2:已支付, 3:已完成, 4:已关闭, 5:已退款, 6:已驳回)"`
StatusName string `json:"status_name" description:"状态名称(中文)"`
RejectionReason *string `json:"rejection_reason,omitempty" description:"驳回原因,仅 status=6 时有值"`
PaidAt *string `json:"paid_at" description:"支付时间"`
CompletedAt *string `json:"completed_at" description:"完成时间"`
CreatedAt string `json:"created_at" description:"创建时间"`
UpdatedAt string `json:"updated_at" description:"更新时间"`
}
// AgentRechargeRejectRequest 驳回代理充值订单请求
type AgentRechargeRejectRequest struct {
RejectionReason string `json:"rejection_reason" validate:"required,max=500" required:"true" description:"驳回原因必填最多500字"`
}
// AgentRechargeListRequest 代理充值记录列表请求
@@ -47,7 +53,7 @@ type AgentRechargeListRequest struct {
Page int `json:"page" query:"page" validate:"omitempty,min=1" minimum:"1" description:"页码默认1"`
PageSize int `json:"page_size" query:"page_size" validate:"omitempty,min=1,max=100" minimum:"1" maximum:"100" description:"每页条数默认20最大100"`
ShopID *uint `json:"shop_id" query:"shop_id" description:"按店铺ID过滤"`
Status *int `json:"status" query:"status" description:"按状态过滤 (1:待支付, 2:已支付, 3:已完成, 4:已关闭, 5:已退款)"`
Status *int `json:"status" query:"status" description:"按状态过滤 (1:待支付, 2:已支付, 3:已完成, 4:已关闭, 5:已退款, 6:已驳回)"`
StartDate string `json:"start_date" query:"start_date" description:"创建时间起始日期(YYYY-MM-DD)"`
EndDate string `json:"end_date" query:"end_date" description:"创建时间截止日期(YYYY-MM-DD)"`
}

View File

@@ -4,19 +4,18 @@ package dto
type ClientRechargeOrderListRequest struct {
Page int `json:"page" query:"page" description:"页码"`
PageSize int `json:"page_size" query:"page_size" description:"每页数量"`
Status int `json:"status" query:"status" description:"状态过滤 (0:待支付, 1:已支付, 2:已关闭, 3:已退款, 4:已驳回)"`
Status int `json:"status" query:"status" description:"状态过滤 (0:待支付, 1:已支付, 2:已关闭, 3:已退款)"`
}
// ClientRechargeOrderListItem C6 充值订单列表项
type ClientRechargeOrderListItem struct {
RechargeOrderID uint `json:"recharge_order_id" description:"充值订单ID"`
RechargeOrderNo string `json:"recharge_order_no" description:"充值订单号"`
Amount int64 `json:"amount" description:"充值金额(分)"`
Status int `json:"status" description:"状态 (0:待支付, 1:已支付, 2:已关闭, 3:已退款, 4:已驳回)"`
StatusName string `json:"status_name" description:"状态名称(中文)"`
AutoPurchaseStatus string `json:"auto_purchase_status" description:"自动购包状态 (pending/success/failed)"`
RejectionReason *string `json:"rejection_reason,omitempty" description:"驳回原因(已驳回时有值)"`
CreatedAt string `json:"created_at" description:"创建时间"`
RechargeOrderID uint `json:"recharge_order_id" description:"充值订单ID"`
RechargeOrderNo string `json:"recharge_order_no" description:"充值订单号"`
Amount int64 `json:"amount" description:"充值金额(分)"`
Status int `json:"status" description:"状态 (0:待支付, 1:已支付, 2:已关闭, 3:已退款)"`
StatusName string `json:"status_name" description:"状态名称(中文)"`
AutoPurchaseStatus string `json:"auto_purchase_status" description:"自动购包状态 (pending/success/failed)"`
CreatedAt string `json:"created_at" description:"创建时间"`
}
// ClientRechargeOrderDetail C7 充值订单详情
@@ -24,11 +23,10 @@ type ClientRechargeOrderDetail struct {
RechargeOrderID uint `json:"recharge_order_id" description:"充值订单ID"`
RechargeOrderNo string `json:"recharge_order_no" description:"充值订单号"`
Amount int64 `json:"amount" description:"充值金额(分)"`
Status int `json:"status" description:"状态 (0:待支付, 1:已支付, 2:已关闭, 3:已退款, 4:已驳回)"`
Status int `json:"status" description:"状态 (0:待支付, 1:已支付, 2:已关闭, 3:已退款)"`
StatusName string `json:"status_name" description:"状态名称(中文)"`
AutoPurchaseStatus string `json:"auto_purchase_status" description:"自动购包状态"`
LinkedPackageIDs interface{} `json:"linked_package_ids" description:"关联套餐ID列表"`
RejectionReason *string `json:"rejection_reason,omitempty" description:"驳回原因(已驳回时有值)"`
CreatedAt string `json:"created_at" description:"创建时间"`
PaidAt string `json:"paid_at,omitempty" description:"支付时间"`
Payments []ClientRechargeOrderPaymentDetail `json:"payments,omitempty" description:"支付记录列表"`

View File

@@ -31,7 +31,6 @@ type RechargeOrder struct {
AutoPurchaseStatus string `gorm:"column:auto_purchase_status;type:varchar(20)" json:"auto_purchase_status,omitempty"`
AccumulatedRechargeSeries int64 `gorm:"column:accumulated_recharge_series;not null;default:0" json:"accumulated_recharge_series"`
PaymentConfigID *uint `gorm:"column:payment_config_id;index" json:"payment_config_id,omitempty"`
RejectionReason *string `gorm:"column:rejection_reason;type:varchar(500)" json:"rejection_reason,omitempty"`
CreatedAt time.Time `gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;not null;default:CURRENT_TIMESTAMP" json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;index" json:"deleted_at,omitempty"`
@@ -47,7 +46,6 @@ const (
RechargeOrderStatusPaid = 1 // 已支付
RechargeOrderStatusClosed = 2 // 已关闭
RechargeOrderStatusRefunded = 3 // 已退款
RechargeOrderStatusRejected = 4 // 已驳回
)
const (

View File

@@ -128,9 +128,6 @@ func RegisterAdminRoutes(router fiber.Router, handlers *bootstrap.Handlers, midd
if handlers.OrderPackageInvalidate != nil {
registerOrderPackageInvalidateRoutes(authGroup, handlers.OrderPackageInvalidate, doc, basePath)
}
if handlers.AdminRechargeOrder != nil {
registerAdminRechargeOrderRoutes(authGroup, handlers.AdminRechargeOrder, doc, basePath)
}
if handlers.SuperAdmin != nil {
registerSuperAdminRoutes(authGroup, handlers.SuperAdmin, doc, basePath)
}

View File

@@ -1,36 +0,0 @@
package routes
import (
"github.com/gofiber/fiber/v2"
"github.com/break/junhong_cmp_fiber/internal/handler/admin"
"github.com/break/junhong_cmp_fiber/internal/model/dto"
"github.com/break/junhong_cmp_fiber/pkg/openapi"
)
// registerAdminRechargeOrderRoutes 注册后台充值订单路由
func registerAdminRechargeOrderRoutes(router fiber.Router, handler *admin.AdminRechargeOrderHandler, doc *openapi.Generator, basePath string) {
Register(router, doc, basePath, "GET", "/recharge-orders", handler.List, RouteSpec{
Summary: "后台充值订单列表",
Tags: []string{"充值订单管理"},
Input: new(dto.AdminRechargeOrderListRequest),
Output: new(dto.AdminRechargeOrderListItem),
Auth: true,
})
Register(router, doc, basePath, "GET", "/recharge-orders/:id", handler.Get, RouteSpec{
Summary: "后台充值订单详情",
Tags: []string{"充值订单管理"},
Input: nil,
Output: new(dto.AdminRechargeOrderDetail),
Auth: true,
})
Register(router, doc, basePath, "POST", "/recharge-orders/:id/reject", handler.Reject, RouteSpec{
Summary: "驳回充值订单",
Tags: []string{"充值订单管理"},
Input: new(dto.AdminRechargeOrderRejectRequest),
Output: nil,
Auth: true,
})
}

View File

@@ -52,4 +52,12 @@ func registerAgentRechargeRoutes(router fiber.Router, handler *admin.AgentRechar
Output: new(dto.AgentRechargeResponse),
Auth: true,
})
Register(group, doc, groupPath, "POST", "/:id/reject", handler.Reject, RouteSpec{
Summary: "驳回代理充值订单",
Tags: []string{"代理预充值"},
Input: new(dto.AgentRechargeRejectRequest),
Output: nil,
Auth: true,
})
}

View File

@@ -387,6 +387,35 @@ func (s *Service) HandlePaymentCallback(ctx context.Context, rechargeNo string,
return nil
}
// Reject 驳回代理充值订单
// 仅 status=1待支付的订单可驳回驳回后状态变为 6已驳回为终态
func (s *Service) Reject(ctx context.Context, id uint, rejectionReason string) error {
record, err := s.agentRechargeStore.GetByID(ctx, id)
if err != nil {
if err == gorm.ErrRecordNotFound {
return errors.New(errors.CodeNotFound, "充值记录不存在")
}
return errors.Wrap(errors.CodeDatabaseError, err, "查询充值记录失败")
}
if record.Status != constants.RechargeStatusPending {
return errors.New(errors.CodeInvalidStatus, "仅待支付订单可驳回")
}
if err := s.agentRechargeStore.UpdateStatusWithRejection(ctx, id, rejectionReason); err != nil {
if err == gorm.ErrRecordNotFound {
return errors.New(errors.CodeInvalidStatus, "仅待支付订单可驳回")
}
return errors.Wrap(errors.CodeDatabaseError, err, "驳回充值订单失败")
}
s.logger.Info("代理充值订单驳回成功",
zap.Uint("record_id", id),
zap.String("rejection_reason", rejectionReason),
)
return nil
}
// GetByID 根据ID查询充值订单详情
// GET /api/admin/agent-recharges/:id
func (s *Service) GetByID(ctx context.Context, id uint) (*dto.AgentRechargeResponse, error) {
@@ -490,6 +519,7 @@ func toResponse(record *model.AgentRechargeRecord, shopName string) *dto.AgentRe
Remark: record.Remark,
Status: record.Status,
StatusName: constants.GetRechargeStatusName(record.Status),
RejectionReason: record.RejectionReason,
CreatedAt: record.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: record.UpdatedAt.Format("2006-01-02 15:04:05"),
}

View File

@@ -60,65 +60,6 @@ func New(
}
}
// AdminList 后台分页查询充值订单列表
func (s *Service) AdminList(ctx context.Context, params *postgres.ListRechargeOrderParams) ([]*model.RechargeOrder, int64, error) {
orders, total, err := s.rechargeOrderStore.List(ctx, params)
if err != nil {
return nil, 0, errors.Wrap(errors.CodeDatabaseError, err, "查询充值订单列表失败")
}
return orders, total, nil
}
// AdminGet 后台查询充值订单详情(含支付记录)
func (s *Service) AdminGet(ctx context.Context, id uint) (*model.RechargeOrder, []*model.Payment, error) {
order, err := s.rechargeOrderStore.GetByID(ctx, id)
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil, errors.New(errors.CodeNotFound, "充值订单不存在")
}
return nil, nil, errors.Wrap(errors.CodeDatabaseError, err, "查询充值订单失败")
}
payments, err := s.paymentStore.ListByOrderID(ctx, id, model.PaymentOrderTypeRecharge)
if err != nil {
return nil, nil, errors.Wrap(errors.CodeDatabaseError, err, "查询支付记录失败")
}
return order, payments, nil
}
// Reject 驳回充值订单
// 仅待支付订单可驳回,驳回后状态变为已驳回(终态)
func (s *Service) Reject(ctx context.Context, id uint, rejectionReason string) error {
order, err := s.rechargeOrderStore.GetByID(ctx, id)
if err != nil {
if err == gorm.ErrRecordNotFound {
return errors.New(errors.CodeNotFound, "充值订单不存在")
}
return errors.Wrap(errors.CodeDatabaseError, err, "查询充值订单失败")
}
if order.Status != model.RechargeOrderStatusPending {
return errors.New(errors.CodeInvalidStatus, "仅待支付订单可驳回")
}
if err := s.rechargeOrderStore.UpdateStatusWithRejection(ctx, id, rejectionReason); err != nil {
if err == gorm.ErrRecordNotFound {
// 并发驳回:重查状态给出明确错误
latest, queryErr := s.rechargeOrderStore.GetByID(ctx, id)
if queryErr == nil && latest.Status != model.RechargeOrderStatusPending {
return errors.New(errors.CodeInvalidStatus, "仅待支付订单可驳回")
}
return errors.New(errors.CodeInvalidStatus, "仅待支付订单可驳回")
}
return errors.Wrap(errors.CodeDatabaseError, err, "驳回充值订单失败")
}
s.logger.Info("充值订单驳回成功",
zap.Uint("order_id", id),
zap.String("rejection_reason", rejectionReason),
)
return nil
}
func (s *Service) HandlePaymentCallback(ctx context.Context, paymentNo string, paymentMethod string, transactionID string) error {
payment, err := s.paymentStore.GetByPaymentNo(ctx, paymentNo)
if err != nil {

View File

@@ -4,6 +4,7 @@ import (
"context"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/pkg/constants"
"github.com/redis/go-redis/v9"
"gorm.io/gorm"
)
@@ -74,6 +75,25 @@ func (s *AgentRechargeStore) Update(ctx context.Context, record *model.AgentRech
return s.db.WithContext(ctx).Save(record).Error
}
// UpdateStatusWithRejection 条件更新状态为已驳回并写入驳回原因
// 仅当 status = 1待支付时更新RowsAffected=0 说明订单不存在或状态已变更
func (s *AgentRechargeStore) UpdateStatusWithRejection(ctx context.Context, id uint, rejectionReason string) error {
result := s.db.WithContext(ctx).
Model(&model.AgentRechargeRecord{}).
Where("id = ? AND status = ?", id, constants.RechargeStatusPending).
Updates(map[string]interface{}{
"status": constants.RechargeStatusRejected,
"rejection_reason": rejectionReason,
})
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return gorm.ErrRecordNotFound
}
return nil
}
// UpdateWithTx 更新充值记录(带事务)
func (s *AgentRechargeStore) UpdateWithTx(ctx context.Context, tx *gorm.DB, record *model.AgentRechargeRecord) error {
return tx.WithContext(ctx).Save(record).Error

View File

@@ -94,25 +94,6 @@ func (s *RechargeOrderStore) Update(ctx context.Context, order *model.RechargeOr
return s.db.WithContext(ctx).Save(order).Error
}
// UpdateStatusWithRejection 条件更新订单状态为已驳回并写入驳回原因
// 仅当 status = 0待支付时更新RowsAffected=0 说明订单不存在或状态已变更
func (s *RechargeOrderStore) UpdateStatusWithRejection(ctx context.Context, id uint, rejectionReason string) error {
result := s.db.WithContext(ctx).
Model(&model.RechargeOrder{}).
Where("id = ? AND status = ?", id, model.RechargeOrderStatusPending).
Updates(map[string]interface{}{
"status": model.RechargeOrderStatusRejected,
"rejection_reason": rejectionReason,
})
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return gorm.ErrRecordNotFound
}
return nil
}
func (s *RechargeOrderStore) ListByCard(ctx context.Context, iotCardID uint, generation int, offset, limit int) ([]*model.RechargeOrder, error) {
var orders []*model.RechargeOrder
query := s.db.WithContext(ctx).

View File

@@ -98,6 +98,7 @@ const (
RechargeStatusCompleted = 3 // 已完成
RechargeStatusClosed = 4 // 已关闭
RechargeStatusRefunded = 5 // 已退款
RechargeStatusRejected = 6 // 已驳回
)
// 充值支付方式
@@ -121,7 +122,7 @@ const (
// ========== Redis Key 生成函数 ==========
// GetRechargeStatusName 获取充值状态名称
// 对应 RechargeStatus* 常量1=待支付, 2=已支付, 3=已完成, 4=已关闭, 5=已退款
// 对应 RechargeStatus* 常量1=待支付, 2=已支付, 3=已完成, 4=已关闭, 5=已退款, 6=已驳回
func GetRechargeStatusName(status int) string {
switch status {
case RechargeStatusPending:
@@ -134,6 +135,8 @@ func GetRechargeStatusName(status int) string {
return "已关闭"
case RechargeStatusRefunded:
return "已退款"
case RechargeStatusRejected:
return "已驳回"
default:
return "未知"
}