package admin import ( "bytes" "strconv" "strings" "time" "github.com/bytedance/sonic" "github.com/go-playground/validator/v10" "github.com/gofiber/fiber/v2" agentrechargeapp "github.com/break/junhong_cmp_fiber/internal/application/agentrecharge" systemconfigapp "github.com/break/junhong_cmp_fiber/internal/application/systemconfig" "github.com/break/junhong_cmp_fiber/internal/model/dto" agentrechargequery "github.com/break/junhong_cmp_fiber/internal/query/agentrecharge" agentRechargeSvc "github.com/break/junhong_cmp_fiber/internal/service/agent_recharge" "github.com/break/junhong_cmp_fiber/pkg/constants" "github.com/break/junhong_cmp_fiber/pkg/errors" "github.com/break/junhong_cmp_fiber/pkg/middleware" "github.com/break/junhong_cmp_fiber/pkg/response" ) // AgentRechargeHandler 代理预充值 Handler type AgentRechargeHandler struct { service *agentRechargeSvc.Service online *agentrechargeapp.OnlineCreationService status *agentrechargequery.PaymentStatusQuery ocr *agentrechargeapp.PaymentVoucherOCRService config *systemconfigapp.UpdateService validator *validator.Validate } // SetOnlineCreationService 注入代理在线扫码充值用例。 func (h *AgentRechargeHandler) SetOnlineCreationService(service *agentrechargeapp.OnlineCreationService) { h.online = service } // SetPaymentStatusQuery 注入代理充值本地支付状态 Query。 func (h *AgentRechargeHandler) SetPaymentStatusQuery(query *agentrechargequery.PaymentStatusQuery) { h.status = query } // SetPaymentVoucherOCRService 注入付款凭证识别用例。 func (h *AgentRechargeHandler) SetPaymentVoucherOCRService(service *agentrechargeapp.PaymentVoucherOCRService) { h.ocr = service } // SetSystemConfigUpdateService 注入受控系统配置写服务,用于代理自充允许范围修改。 func (h *AgentRechargeHandler) SetSystemConfigUpdateService(service *systemconfigapp.UpdateService) { h.config = service } // NewAgentRechargeHandler 创建代理预充值 Handler func NewAgentRechargeHandler(service *agentRechargeSvc.Service, validator *validator.Validate) *AgentRechargeHandler { return &AgentRechargeHandler{service: service, validator: validator} } // Create 创建代理充值订单 // POST /api/admin/agent-recharges func (h *AgentRechargeHandler) Create(c *fiber.Ctx) error { var req dto.CreateAgentRechargeRequest decoder := sonic.ConfigStd.NewDecoder(bytes.NewReader(c.Body())) decoder.DisallowUnknownFields() if err := decoder.Decode(&req); err != nil { return errors.New(errors.CodeInvalidParam, "请求参数解析失败") } if err := h.validator.Struct(&req); err != nil { return errors.New(errors.CodeInvalidParam) } if req.PaymentMethod == constants.RechargeMethodWechat || req.PaymentMethod == constants.RechargeMethodAlipay { return h.createOnline(c, req) } if strings.TrimSpace(req.RequestID) != "" { return errors.New(errors.CodeInvalidParam, "线下充值不能传入在线请求标识") } result, err := h.service.Create(c.UserContext(), &req) if err != nil { return err } return response.Success(c, result) } func (h *AgentRechargeHandler) createOnline(c *fiber.Ctx, req dto.CreateAgentRechargeRequest) error { if h.online == nil { return errors.New(errors.CodeServiceUnavailable, "代理在线充值能力未配置") } if req.ShopID != nil || len(req.PaymentVoucherKey) > 0 || len(req.OtherVoucherKey) > 0 || req.OfflinePaymentMethodID != 0 || strings.TrimSpace(req.ExternalTransactionNo) != "" || strings.TrimSpace(req.Remark) != "" { return errors.New(errors.CodeInvalidParam, "在线充值不能指定店铺、收款方式、交易流水号、支付凭证或运营备注") } result, err := h.online.Execute(c.UserContext(), agentrechargeapp.CreateOnlineCommand{ AccountID: middleware.GetUserIDFromContext(c.UserContext()), UserType: middleware.GetUserTypeFromContext(c.UserContext()), CurrentShopID: middleware.GetShopIDFromContext(c.UserContext()), Amount: req.Amount, PaymentMethod: req.PaymentMethod, RequestID: req.RequestID, PayerClientIP: c.IP(), }) if err != nil { return err } rechargeSource, rechargeSourceName := constants.GetAgentRechargeSource(result.Payment.PaymentMethod) return response.Success(c, &dto.AgentRechargeOnlineResponse{ RechargeID: result.Recharge.ID, RechargeNo: result.Recharge.RechargeNo, PaymentNo: result.Payment.PaymentNo, PaymentMethod: result.Payment.PaymentMethod, Amount: result.Payment.Amount, QRContent: result.Payment.QRContent, RechargeSource: rechargeSource, RechargeSourceName: rechargeSourceName, Status: result.Recharge.Status, StatusName: constants.GetRechargeStatusName(result.Recharge.Status), }) } // PaymentMethods 查询代理在线充值可用支付方式。 // GET /api/admin/agent-recharges/payment-methods func (h *AgentRechargeHandler) PaymentMethods(c *fiber.Ctx) error { if h.online == nil { return errors.New(errors.CodeServiceUnavailable, "代理在线充值能力未配置") } result, err := h.online.AvailablePaymentMethods(c.UserContext(), middleware.GetUserTypeFromContext(c.UserContext())) if err != nil { return err } return response.Success(c, &dto.AgentRechargePaymentMethodsResponse{ Methods: result.Methods, MinAmount: result.MinAmount, MaxAmount: result.MaxAmount, }) } // SelfRechargePaymentMethods 查询代理自充实际可用支付方式。 // GET /api/admin/agent-self-recharge-payment-methods // 响应只含交集结果,不含允许范围、商户身份或凭证。 func (h *AgentRechargeHandler) SelfRechargePaymentMethods(c *fiber.Ctx) error { return h.PaymentMethods(c) } // UpdateSelfRechargePaymentMethods 修改代理在线自充允许范围。 // PUT /api/admin/agent-self-recharge-payment-methods // 复用受控系统配置写服务,获得超级管理员限定、咨询锁串行与前后值审计。 func (h *AgentRechargeHandler) UpdateSelfRechargePaymentMethods(c *fiber.Ctx) error { var req dto.AgentSelfRechargePaymentMethodsUpdateRequest decoder := sonic.ConfigStd.NewDecoder(bytes.NewReader(c.Body())) decoder.DisallowUnknownFields() if err := decoder.Decode(&req); err != nil { return errors.New(errors.CodeInvalidParam, "请求参数解析失败") } if err := h.validator.Struct(&req); err != nil { return errors.New(errors.CodeInvalidParam) } if h.config == nil { return errors.New(errors.CodeServiceUnavailable, "代理自充允许范围维护能力未配置") } item, err := h.config.Execute(c.UserContext(), constants.SystemConfigAgentSelfRechargeAllowedMethods, dto.UpdateSystemConfigRequest{Value: req.AllowedMethods}) if err != nil { return err } updatedAt := "" if item.UpdatedAt != nil { updatedAt = item.UpdatedAt.UTC().Format(time.RFC3339) } return response.Success(c, &dto.AgentSelfRechargePaymentMethodsUpdateResponse{ AllowedMethods: item.Value, AllowedMethodsName: constants.GetAgentSelfRechargeAllowedMethodsName(item.Value), UpdatedAt: updatedAt, }) } // PaymentVoucherOCR 识别付款凭证,只返回交易流水号预填值。 // POST /api/admin/agent-recharges/payment-voucher-ocr // 识别失败返回明确失败,不影响提交人人工填写交易流水号后创建申请。 func (h *AgentRechargeHandler) PaymentVoucherOCR(c *fiber.Ctx) error { var req dto.AgentRechargePaymentVoucherOCRRequest decoder := sonic.ConfigStd.NewDecoder(bytes.NewReader(c.Body())) decoder.DisallowUnknownFields() if err := decoder.Decode(&req); err != nil { return errors.New(errors.CodeInvalidParam, "请求参数解析失败") } if err := h.validator.Struct(&req); err != nil { return errors.New(errors.CodeInvalidParam) } if h.ocr == nil { return errors.New(errors.CodeServiceUnavailable, "付款凭证识别能力未配置") } result, err := h.ocr.Recognize(c.UserContext(), req.PaymentVoucherKey) if err != nil { return err } return response.Success(c, &dto.AgentRechargePaymentVoucherOCRResponse{ ExternalTransactionNo: result.ExternalTransactionNo, }) } // List 查询代理充值订单列表 // GET /api/admin/agent-recharges func (h *AgentRechargeHandler) List(c *fiber.Ctx) error { var req dto.AgentRechargeListRequest if err := c.QueryParser(&req); err != nil { return errors.New(errors.CodeInvalidParam, "请求参数解析失败") } if err := h.validator.Struct(&req); err != nil { return errors.New(errors.CodeInvalidParam) } list, total, err := h.service.List(c.UserContext(), &req) if err != nil { return err } return response.SuccessWithPagination(c, list, total, req.Page, req.PageSize) } // Get 查询代理充值订单详情 // GET /api/admin/agent-recharges/:id func (h *AgentRechargeHandler) Get(c *fiber.Ctx) error { id, err := strconv.ParseUint(c.Params("id"), 10, 64) if err != nil { return errors.New(errors.CodeInvalidParam, "无效的充值记录ID") } result, err := h.service.GetByID(c.UserContext(), uint(id)) if err != nil { return err } return response.Success(c, result) } // TriggerApproval 主动补发历史线下代理充值审批。 // POST /api/admin/agent-recharges/:id/trigger-approval func (h *AgentRechargeHandler) TriggerApproval(c *fiber.Ctx) error { id, err := strconv.ParseUint(c.Params("id"), 10, 64) if err != nil || id == 0 { return errors.New(errors.CodeInvalidParam, "无效的充值记录ID") } result, err := h.service.TriggerApproval(c.UserContext(), uint(id)) if err != nil { return err } return response.Success(c, result) } // PaymentStatus 查询代理充值本地支付与到账状态。 // GET /api/admin/agent-recharges/:id/payment-status func (h *AgentRechargeHandler) PaymentStatus(c *fiber.Ctx) error { if h.status == nil { return errors.New(errors.CodeServiceUnavailable, "代理充值支付状态查询未配置") } id, err := strconv.ParseUint(c.Params("id"), 10, 64) if err != nil || id == 0 { return errors.New(errors.CodeInvalidParam, "无效的充值记录ID") } result, err := h.status.Get(c.UserContext(), uint(id)) if err != nil { return err } 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 { id, err := strconv.ParseUint(c.Params("id"), 10, 64) if err != nil { return errors.New(errors.CodeInvalidParam, "无效的充值记录ID") } var req dto.AgentOfflinePayRequest if err := c.BodyParser(&req); err != nil { return errors.New(errors.CodeInvalidParam, "请求参数解析失败") } result, err := h.service.OfflinePay(c.UserContext(), uint(id), &req) if err != nil { return err } return response.Success(c, result) }