From f4b6a55b27f998d942a49b233f6ab738ef670ae7 Mon Sep 17 00:00:00 2001 From: break Date: Fri, 26 Jun 2026 12:12:15 +0900 Subject: [PATCH] =?UTF-8?q?=E9=A9=B3=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/bootstrap/handlers.go | 3 +- internal/bootstrap/types.go | 1 - internal/handler/admin/agent_recharge.go | 23 ++ internal/handler/admin/recharge_order.go | 212 ------------------ internal/handler/app/client_recharge_order.go | 4 - internal/model/agent_wallet.go | 1 + .../model/dto/admin_recharge_order_dto.go | 64 ------ internal/model/dto/agent_recharge_dto.go | 40 ++-- .../model/dto/client_recharge_order_dto.go | 20 +- internal/model/recharge_order.go | 2 - internal/routes/admin.go | 3 - internal/routes/admin_recharge_order.go | 36 --- internal/routes/agent_recharge.go | 8 + internal/service/agent_recharge/service.go | 30 +++ internal/service/recharge_order/service.go | 59 ----- .../store/postgres/agent_recharge_store.go | 20 ++ .../store/postgres/recharge_order_store.go | 19 -- pkg/constants/wallet.go | 5 +- 18 files changed, 119 insertions(+), 431 deletions(-) delete mode 100644 internal/handler/admin/recharge_order.go delete mode 100644 internal/model/dto/admin_recharge_order_dto.go delete mode 100644 internal/routes/admin_recharge_order.go diff --git a/internal/bootstrap/handlers.go b/internal/bootstrap/handlers.go index a52880a..fe0c567 100644 --- a/internal/bootstrap/handlers.go +++ b/internal/bootstrap/handlers.go @@ -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), } diff --git a/internal/bootstrap/types.go b/internal/bootstrap/types.go index 4e3bfd9..06cad8d 100644 --- a/internal/bootstrap/types.go +++ b/internal/bootstrap/types.go @@ -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 } diff --git a/internal/handler/admin/agent_recharge.go b/internal/handler/admin/agent_recharge.go index 0b22891..9c3acde 100644 --- a/internal/handler/admin/agent_recharge.go +++ b/internal/handler/admin/agent_recharge.go @@ -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 { diff --git a/internal/handler/admin/recharge_order.go b/internal/handler/admin/recharge_order.go deleted file mode 100644 index b9260d6..0000000 --- a/internal/handler/admin/recharge_order.go +++ /dev/null @@ -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) -} diff --git a/internal/handler/app/client_recharge_order.go b/internal/handler/app/client_recharge_order.go index 1549302..f565dc7 100644 --- a/internal/handler/app/client_recharge_order.go +++ b/internal/handler/app/client_recharge_order.go @@ -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 "未知" } diff --git a/internal/model/agent_wallet.go b/internal/model/agent_wallet.go index 3479e00..e1fffe3 100644 --- a/internal/model/agent_wallet.go +++ b/internal/model/agent_wallet.go @@ -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"` diff --git a/internal/model/dto/admin_recharge_order_dto.go b/internal/model/dto/admin_recharge_order_dto.go deleted file mode 100644 index 889cb3c..0000000 --- a/internal/model/dto/admin_recharge_order_dto.go +++ /dev/null @@ -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字)"` -} diff --git a/internal/model/dto/agent_recharge_dto.go b/internal/model/dto/agent_recharge_dto.go index ca57ac2..0e6c7a0 100644 --- a/internal/model/dto/agent_recharge_dto.go +++ b/internal/model/dto/agent_recharge_dto.go @@ -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)"` } diff --git a/internal/model/dto/client_recharge_order_dto.go b/internal/model/dto/client_recharge_order_dto.go index d572e8f..0e45e43 100644 --- a/internal/model/dto/client_recharge_order_dto.go +++ b/internal/model/dto/client_recharge_order_dto.go @@ -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:"支付记录列表"` diff --git a/internal/model/recharge_order.go b/internal/model/recharge_order.go index 790c357..7ded3ec 100644 --- a/internal/model/recharge_order.go +++ b/internal/model/recharge_order.go @@ -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 ( diff --git a/internal/routes/admin.go b/internal/routes/admin.go index 9c30b1b..ac26298 100644 --- a/internal/routes/admin.go +++ b/internal/routes/admin.go @@ -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) } diff --git a/internal/routes/admin_recharge_order.go b/internal/routes/admin_recharge_order.go deleted file mode 100644 index 24144f1..0000000 --- a/internal/routes/admin_recharge_order.go +++ /dev/null @@ -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, - }) -} diff --git a/internal/routes/agent_recharge.go b/internal/routes/agent_recharge.go index deabbf4..6ad569a 100644 --- a/internal/routes/agent_recharge.go +++ b/internal/routes/agent_recharge.go @@ -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, + }) } diff --git a/internal/service/agent_recharge/service.go b/internal/service/agent_recharge/service.go index 688a8bf..7a30531 100644 --- a/internal/service/agent_recharge/service.go +++ b/internal/service/agent_recharge/service.go @@ -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"), } diff --git a/internal/service/recharge_order/service.go b/internal/service/recharge_order/service.go index 7865bf2..613e07f 100644 --- a/internal/service/recharge_order/service.go +++ b/internal/service/recharge_order/service.go @@ -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 { diff --git a/internal/store/postgres/agent_recharge_store.go b/internal/store/postgres/agent_recharge_store.go index 2d8689e..1907d50 100644 --- a/internal/store/postgres/agent_recharge_store.go +++ b/internal/store/postgres/agent_recharge_store.go @@ -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 diff --git a/internal/store/postgres/recharge_order_store.go b/internal/store/postgres/recharge_order_store.go index ce00680..f39272c 100644 --- a/internal/store/postgres/recharge_order_store.go +++ b/internal/store/postgres/recharge_order_store.go @@ -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). diff --git a/pkg/constants/wallet.go b/pkg/constants/wallet.go index b7e14c2..7cf5340 100644 --- a/pkg/constants/wallet.go +++ b/pkg/constants/wallet.go @@ -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 "未知" }