// Package agent_recharge 提供代理预充值的业务逻辑服务 // 包含充值订单创建、线下确认、支付回调处理、列表查询等功能 package agent_recharge import ( "context" "fmt" "math/rand" "strings" "time" "github.com/redis/go-redis/v9" "go.uber.org/zap" "gorm.io/gorm" agentrechargeapp "github.com/break/junhong_cmp_fiber/internal/application/agentrecharge" walletapp "github.com/break/junhong_cmp_fiber/internal/application/wallet" "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/config" "github.com/break/junhong_cmp_fiber/pkg/constants" "github.com/break/junhong_cmp_fiber/pkg/errors" "github.com/break/junhong_cmp_fiber/pkg/middleware" ) // AuditServiceInterface 审计日志服务接口 type AuditServiceInterface interface { LogOperation(ctx context.Context, log *model.AccountOperationLog) } // OperationPasswordServiceInterface 全局操作密码服务接口 type OperationPasswordServiceInterface interface { Verify(ctx context.Context, inputPassword string) error } // WechatConfigServiceInterface 支付配置服务接口 type WechatConfigServiceInterface interface { GetActiveConfig(ctx context.Context) (*model.WechatConfig, error) } // Service 代理预充值业务服务 // 负责代理钱包充值订单的创建、线下确认、回调处理等业务逻辑 type Service struct { db *gorm.DB agentRechargeStore *postgres.AgentRechargeStore agentWalletStore *postgres.AgentWalletStore agentWalletPosting *walletapp.PostingService offlineCreation *agentrechargeapp.OfflineCreationService shopStore *postgres.ShopStore wechatConfigService WechatConfigServiceInterface auditService AuditServiceInterface operationPasswordService OperationPasswordServiceInterface redis *redis.Client logger *zap.Logger } // New 创建代理预充值服务实例 func New( db *gorm.DB, agentRechargeStore *postgres.AgentRechargeStore, agentWalletStore *postgres.AgentWalletStore, shopStore *postgres.ShopStore, wechatConfigService WechatConfigServiceInterface, auditService AuditServiceInterface, operationPasswordService OperationPasswordServiceInterface, rdb *redis.Client, logger *zap.Logger, ) *Service { return &Service{ db: db, agentRechargeStore: agentRechargeStore, agentWalletStore: agentWalletStore, shopStore: shopStore, wechatConfigService: wechatConfigService, auditService: auditService, operationPasswordService: operationPasswordService, redis: rdb, logger: logger, } } // SetAgentWalletPostingService 注入代理主钱包统一入账用例。 func (s *Service) SetAgentWalletPostingService(service *walletapp.PostingService) { s.agentWalletPosting = service } // SetOfflineCreationService 注入员工线下代充值审批申请用例。 func (s *Service) SetOfflineCreationService(service *agentrechargeapp.OfflineCreationService) { s.offlineCreation = service } // Create 创建代理充值订单 // POST /api/admin/agent-recharges func (s *Service) Create(ctx context.Context, req *dto.CreateAgentRechargeRequest) (*dto.AgentRechargeResponse, error) { userID := middleware.GetUserIDFromContext(ctx) userType := middleware.GetUserTypeFromContext(ctx) userShopID := middleware.GetShopIDFromContext(ctx) // 代理只能充自己店铺 if userType == constants.UserTypeAgent && req.ShopID != userShopID { return nil, errors.New(errors.CodeForbidden, "代理只能为自己的店铺充值") } // 线下充值仅平台可用 if req.PaymentMethod == constants.RechargeMethodOffline && userType != constants.UserTypePlatform && userType != constants.UserTypeSuperAdmin { return nil, errors.New(errors.CodeForbidden, "线下充值仅平台管理员可操作") } // 线下充值必须上传支付凭证 if req.PaymentMethod == constants.RechargeMethodOffline && len(req.PaymentVoucherKey) == 0 { return nil, errors.New(errors.CodeInvalidParam, "线下充值必须上传支付凭证") } if req.Amount < constants.AgentRechargeMinAmount || req.Amount > constants.AgentRechargeMaxAmount { return nil, errors.New(errors.CodeInvalidParam, "充值金额超出允许范围") } rechargeNo := s.generateRechargeNo() if req.PaymentMethod == constants.RechargeMethodOffline { return s.createOffline(ctx, req, userID, userType, rechargeNo) } // 查找目标店铺的主钱包 wallet, err := s.agentWalletStore.GetMainWallet(ctx, req.ShopID) if err != nil { if err == gorm.ErrRecordNotFound { return nil, errors.New(errors.CodeNotFound, "目标店铺主钱包不存在") } return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询店铺主钱包失败") } // 查询店铺名称 shop, err := s.shopStore.GetByID(ctx, req.ShopID) if err != nil { if err == gorm.ErrRecordNotFound { return nil, errors.New(errors.CodeNotFound, "目标店铺不存在") } return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询店铺失败") } // 在线支付需要查询生效的支付配置 var paymentConfigID *uint var paymentChannel string if req.PaymentMethod == "wechat" { activeConfig, cfgErr := s.wechatConfigService.GetActiveConfig(ctx) if cfgErr != nil || activeConfig == nil { return nil, errors.New(errors.CodeNoPaymentConfig, "当前无可用的支付配置,请联系管理员") } paymentConfigID = &activeConfig.ID paymentChannel = activeConfig.ProviderType } else { paymentChannel = "offline" } record := &model.AgentRechargeRecord{ UserID: userID, AgentWalletID: wallet.ID, ShopID: req.ShopID, RechargeNo: rechargeNo, Amount: req.Amount, PaymentMethod: req.PaymentMethod, PaymentChannel: &paymentChannel, PaymentConfigID: paymentConfigID, PaymentVoucherKey: model.StringJSONBArray(req.PaymentVoucherKey), Remark: req.Remark, Status: constants.RechargeStatusPending, ShopIDTag: wallet.ShopIDTag, EnterpriseIDTag: wallet.EnterpriseIDTag, } if err := s.agentRechargeStore.Create(ctx, record); err != nil { return nil, errors.Wrap(errors.CodeDatabaseError, err, "创建充值订单失败") } s.logger.Info("创建代理充值订单成功", zap.Uint("recharge_id", record.ID), zap.String("recharge_no", rechargeNo), zap.Int64("amount", req.Amount), zap.Uint("shop_id", req.ShopID), zap.Uint("user_id", userID), ) resp := toResponse(record, shop.ShopName) resp.SubmitterName = s.loadSubmitterNameBestEffort(ctx, record.UserID) return resp, nil } func (s *Service) createOffline( ctx context.Context, req *dto.CreateAgentRechargeRequest, userID uint, userType int, rechargeNo string, ) (*dto.AgentRechargeResponse, error) { if s.offlineCreation == nil { return nil, errors.New(errors.CodeServiceUnavailable, "员工线下代充值审批能力未配置") } result, err := s.offlineCreation.Execute(ctx, agentrechargeapp.CreateOfflineCommand{ SubmitterAccountID: userID, SubmitterUserType: userType, ShopID: req.ShopID, RechargeNo: rechargeNo, Amount: req.Amount, PaymentVoucherKeys: req.PaymentVoucherKey, Remark: req.Remark, }) if err != nil { return nil, err } resp := toResponse(result.Record, result.ShopName) resp.SubmitterName = result.SubmitterName resp.ApprovalProvider = constants.IntegrationProviderWeCom resp.ApprovalStatus = &result.ApprovalStatus resp.ApprovalStatusName = constants.GetApprovalStatusName(result.ApprovalStatus) s.logger.Info("创建员工线下代充值审批申请成功", zap.Uint("recharge_id", result.Record.ID), zap.String("recharge_no", result.Record.RechargeNo), zap.Uint("approval_instance_id", *result.Record.ApprovalInstanceID), zap.Int64("amount", result.Record.Amount), zap.Uint("shop_id", result.Record.ShopID), zap.Uint("submitter_id", result.Record.UserID), ) return resp, nil } // OfflinePay 线下充值确认 // POST /api/admin/agent-recharges/:id/offline-pay func (s *Service) OfflinePay(ctx context.Context, id uint, req *dto.AgentOfflinePayRequest) (*dto.AgentRechargeResponse, error) { if !legacyOfflineRechargePayEnabled() { return nil, errors.New(errors.CodeInvalidStatus, "线下充值人工确认入口已停用,请查看企业微信审批状态") } userID := middleware.GetUserIDFromContext(ctx) userType := middleware.GetUserTypeFromContext(ctx) // 仅平台账号可操作 if userType != constants.UserTypePlatform && userType != constants.UserTypeSuperAdmin { return nil, errors.New(errors.CodeForbidden, "仅平台管理员可确认线下充值") } // 验证全局操作密码 if err := s.operationPasswordService.Verify(ctx, req.OperationPassword); err != nil { return nil, err } record, err := s.agentRechargeStore.GetByID(ctx, id) if err != nil { if err == gorm.ErrRecordNotFound { return nil, errors.New(errors.CodeNotFound, "充值记录不存在") } return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询充值记录失败") } if record.PaymentMethod != constants.RechargeMethodOffline { return nil, errors.New(errors.CodeInvalidParam, "该订单非线下充值,不支持此操作") } if record.ApprovalInstanceID != nil { return nil, errors.New(errors.CodeInvalidStatus, "该线下充值申请由企业微信审批决定,不能人工确认") } if record.Status != constants.RechargeStatusPending { return nil, errors.New(errors.CodeInvalidParam, "该订单状态不允许确认支付") } now := time.Now() err = s.db.Transaction(func(tx *gorm.DB) error { // 条件更新充值记录状态 result := tx.Model(&model.AgentRechargeRecord{}). Where("id = ? AND status = ?", record.ID, constants.RechargeStatusPending). Updates(map[string]interface{}{ "status": constants.RechargeStatusCompleted, "paid_at": now, "completed_at": now, }) if result.Error != nil { return errors.Wrap(errors.CodeDatabaseError, result.Error, "更新充值记录状态失败") } if result.RowsAffected == 0 { return errors.New(errors.CodeInvalidParam, "充值记录状态已变更") } if s.agentWalletPosting == nil { return errors.New(errors.CodeInternalError, "代理主钱包入账能力未配置") } requestID := "" if value := middleware.GetRequestIDFromContext(ctx); value != nil { requestID = *value } if _, err := s.agentWalletPosting.PostInTx(ctx, tx, walletapp.PostingCommand{ ShopID: record.ShopID, WalletID: record.AgentWalletID, Amount: record.Amount, ReferenceType: constants.ReferenceTypeTopup, ReferenceID: record.ID, TransactionType: constants.AgentTransactionTypeRecharge, UserID: userID, Creator: userID, Remark: "线下充值确认", RequestID: requestID, CorrelationID: record.RechargeNo, }); err != nil { return err } return nil }) if err != nil { return nil, err } // 异步记录审计日志 go s.auditService.LogOperation(ctx, &model.AccountOperationLog{ OperatorID: userID, OperatorType: userType, OperationType: "offline_recharge_confirm", OperationDesc: fmt.Sprintf("确认线下充值,充值单号: %s,金额: %d分", record.RechargeNo, record.Amount), RequestID: middleware.GetRequestIDFromContext(ctx), IPAddress: middleware.GetIPFromContext(ctx), UserAgent: middleware.GetUserAgentFromContext(ctx), }) shop, _ := s.shopStore.GetByID(ctx, record.ShopID) shopName := "" if shop != nil { shopName = shop.ShopName } // 更新本地对象以反映最新状态 record.Status = constants.RechargeStatusCompleted record.PaidAt = &now record.CompletedAt = &now resp := toResponse(record, shopName) resp.SubmitterName = s.loadSubmitterNameBestEffort(ctx, record.UserID) approvalSummaries, err := s.loadApprovalSummaries(ctx, []*model.AgentRechargeRecord{record}) if err != nil { return nil, err } applyApprovalSummary(resp, approvalSummaries, record.ApprovalInstanceID) return resp, nil } func legacyOfflineRechargePayEnabled() bool { cfg := config.Get() return cfg == nil || cfg.Approval.LegacyOfflineRechargePayEnabled } // HandlePaymentCallback 处理支付回调 // 幂等处理:status != 待支付则直接返回成功 func (s *Service) HandlePaymentCallback(ctx context.Context, rechargeNo string, paymentMethod string, paymentTransactionID string, paidAmount int64) error { paymentTransactionID = strings.TrimSpace(paymentTransactionID) record, err := s.agentRechargeStore.GetByRechargeNo(ctx, rechargeNo) if err != nil { if err == gorm.ErrRecordNotFound { return errors.New(errors.CodeNotFound, "充值订单不存在") } return errors.Wrap(errors.CodeDatabaseError, err, "查询充值订单失败") } if err := validateAgentRechargePayment(record, paymentMethod, paymentTransactionID, paidAmount); err != nil { return err } // 已完成订单仅在第三方交易号一致时按同一回调幂等成功。 if record.Status != constants.RechargeStatusPending { if record.Status == constants.RechargeStatusCompleted && record.PaymentTransactionID != nil && *record.PaymentTransactionID == strings.TrimSpace(paymentTransactionID) { s.logger.Info("代理充值支付回调幂等命中", zap.String("recharge_no", rechargeNo), zap.Int("status", record.Status)) return nil } return errors.New(errors.CodeInvalidStatus, "充值订单状态不允许确认支付") } now := time.Now() err = s.db.Transaction(func(tx *gorm.DB) error { // 条件更新(WHERE status = 1) result := tx.Model(&model.AgentRechargeRecord{}). Where("id = ? AND status = ? AND payment_method <> ? AND amount = ?", record.ID, constants.RechargeStatusPending, constants.RechargeMethodOffline, paidAmount). Updates(map[string]interface{}{ "status": constants.RechargeStatusCompleted, "payment_transaction_id": paymentTransactionID, "paid_at": now, "completed_at": now, }) if result.Error != nil { return errors.Wrap(errors.CodeDatabaseError, result.Error, "更新充值记录状态失败") } if result.RowsAffected == 0 { var current model.AgentRechargeRecord if err := tx.WithContext(ctx).Unscoped(). Where("id = ?", record.ID).First(¤t).Error; err != nil { return errors.Wrap(errors.CodeDatabaseError, err, "复核充值记录终态失败") } if current.Status == constants.RechargeStatusCompleted && current.PaymentTransactionID != nil && *current.PaymentTransactionID == paymentTransactionID { return nil } return errors.New(errors.CodeConflict, "充值订单已被其他请求处理") } if s.agentWalletPosting == nil { return errors.New(errors.CodeInternalError, "代理主钱包入账能力未配置") } requestID := "" if value := middleware.GetRequestIDFromContext(ctx); value != nil { requestID = *value } if _, err := s.agentWalletPosting.PostInTx(ctx, tx, walletapp.PostingCommand{ ShopID: record.ShopID, WalletID: record.AgentWalletID, Amount: record.Amount, ReferenceType: constants.ReferenceTypeTopup, ReferenceID: record.ID, TransactionType: constants.AgentTransactionTypeRecharge, UserID: record.UserID, Creator: record.UserID, Remark: "在线支付充值", RequestID: requestID, CorrelationID: record.RechargeNo, }); err != nil { return err } return nil }) if err != nil { return err } s.logger.Info("代理充值支付回调处理成功", zap.String("recharge_no", rechargeNo), zap.Int64("amount", record.Amount), zap.Uint("shop_id", record.ShopID), ) return nil } func validateAgentRechargePayment(record *model.AgentRechargeRecord, paymentMethod, paymentTransactionID string, paidAmount int64) error { if record == nil || strings.TrimSpace(paymentTransactionID) == "" || paidAmount <= 0 || paidAmount != record.Amount { return errors.New(errors.CodeInvalidParam, "代理充值支付回调金额或交易号不匹配") } if record.PaymentMethod == constants.RechargeMethodOffline || record.PaymentConfigID == nil || record.PaymentChannel == nil { return errors.New(errors.CodeInvalidStatus, "线下充值订单不能通过支付回调确认") } channel := strings.TrimSpace(*record.PaymentChannel) switch strings.TrimSpace(paymentMethod) { case model.PaymentMethodWechat: if record.PaymentMethod != constants.RechargeMethodWechat || (channel != model.ProviderTypeWechat && channel != model.ProviderTypeWechatV2) { return errors.New(errors.CodeInvalidParam, "代理充值支付渠道不匹配") } case model.ProviderTypeFuiou: if record.PaymentMethod != constants.RechargeMethodWechat || channel != model.ProviderTypeFuiou { return errors.New(errors.CodeInvalidParam, "代理充值支付渠道不匹配") } default: return errors.New(errors.CodeInvalidParam, "代理充值支付渠道不受支持") } 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 record.ApprovalInstanceID != nil { 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) { record, err := s.agentRechargeStore.GetByID(ctx, id) if err != nil { if err == gorm.ErrRecordNotFound { return nil, errors.New(errors.CodeNotFound, "充值记录不存在") } return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询充值记录失败") } shop, _ := s.shopStore.GetByID(ctx, record.ShopID) shopName := "" if shop != nil { shopName = shop.ShopName } resp := toResponse(record, shopName) resp.SubmitterName = s.loadSubmitterNameBestEffort(ctx, record.UserID) approvalSummaries, err := s.loadApprovalSummaries(ctx, []*model.AgentRechargeRecord{record}) if err != nil { return nil, err } applyApprovalSummary(resp, approvalSummaries, record.ApprovalInstanceID) return resp, nil } // List 分页查询充值订单列表 // GET /api/admin/agent-recharges func (s *Service) List(ctx context.Context, req *dto.AgentRechargeListRequest) ([]*dto.AgentRechargeResponse, int64, error) { page := req.Page pageSize := req.PageSize if page == 0 { page = 1 } if pageSize == 0 { pageSize = constants.DefaultPageSize } query := s.db.WithContext(ctx).Model(&model.AgentRechargeRecord{}) if req.ShopID != nil { query = query.Where("shop_id = ?", *req.ShopID) } if req.Status != nil { query = query.Where("status = ?", *req.Status) } if req.StartDate != "" { query = query.Where("created_at >= ?", req.StartDate+" 00:00:00") } if req.EndDate != "" { query = query.Where("created_at <= ?", req.EndDate+" 23:59:59") } var total int64 if err := query.Count(&total).Error; err != nil { return nil, 0, errors.Wrap(errors.CodeDatabaseError, err, "查询充值记录总数失败") } var records []*model.AgentRechargeRecord offset := (page - 1) * pageSize if err := query.Order("created_at DESC").Offset(offset).Limit(pageSize).Find(&records).Error; err != nil { return nil, 0, errors.Wrap(errors.CodeDatabaseError, err, "查询充值记录列表失败") } // 批量查询店铺名称 shopIDs := make([]uint, 0, len(records)) for _, r := range records { shopIDs = append(shopIDs, r.ShopID) } shopMap := make(map[uint]string) if len(shopIDs) > 0 { shops, err := s.shopStore.GetByIDs(ctx, shopIDs) if err == nil { for _, sh := range shops { shopMap[sh.ID] = sh.ShopName } } } submitterIDs := make([]uint, 0, len(records)) for _, record := range records { if record.UserID > 0 { submitterIDs = append(submitterIDs, record.UserID) } } submitterNames, err := s.loadSubmitterNames(ctx, submitterIDs) if err != nil { return nil, 0, errors.Wrap(errors.CodeDatabaseError, err, "查询充值提交人失败") } approvalSummaries, err := s.loadApprovalSummaries(ctx, records) if err != nil { return nil, 0, err } list := make([]*dto.AgentRechargeResponse, 0, len(records)) for _, r := range records { item := toResponse(r, shopMap[r.ShopID]) item.SubmitterName = submitterNames[r.UserID] applyApprovalSummary(item, approvalSummaries, r.ApprovalInstanceID) list = append(list, item) } return list, total, nil } // generateRechargeNo 生成代理充值订单号 // 格式: ARCH + 14位时间戳 + 6位随机数 func (s *Service) generateRechargeNo() string { timestamp := time.Now().Format("20060102150405") randomNum := rand.Intn(1000000) return fmt.Sprintf("%s%s%06d", constants.AgentRechargeOrderPrefix, timestamp, randomNum) } // toResponse 将模型转换为响应 DTO func toResponse(record *model.AgentRechargeRecord, shopName string) *dto.AgentRechargeResponse { resp := &dto.AgentRechargeResponse{ ID: record.ID, RechargeNo: record.RechargeNo, ShopID: record.ShopID, ShopName: shopName, AgentWalletID: record.AgentWalletID, Amount: record.Amount, PaymentMethod: record.PaymentMethod, PaymentVoucherKey: []string(record.PaymentVoucherKey), Remark: record.Remark, Status: record.Status, StatusName: constants.GetRechargeStatusName(record.Status), RejectionReason: record.RejectionReason, SubmitterID: record.UserID, ApprovalInstanceID: record.ApprovalInstanceID, CreatedAt: record.CreatedAt.Format("2006-01-02 15:04:05"), UpdatedAt: record.UpdatedAt.Format("2006-01-02 15:04:05"), } if record.PaymentChannel != nil { resp.PaymentChannel = *record.PaymentChannel } if record.PaymentConfigID != nil { resp.PaymentConfigID = record.PaymentConfigID } if record.PaymentTransactionID != nil { resp.PaymentTransactionID = *record.PaymentTransactionID } if record.PaidAt != nil { t := record.PaidAt.Format("2006-01-02 15:04:05") resp.PaidAt = &t } if record.CompletedAt != nil { t := record.CompletedAt.Format("2006-01-02 15:04:05") resp.CompletedAt = &t } return resp } type approvalSummary struct { Provider string Status int } func (s *Service) loadApprovalSummaries( ctx context.Context, records []*model.AgentRechargeRecord, ) (map[uint]approvalSummary, error) { ids := make([]uint, 0, len(records)) seen := make(map[uint]struct{}, len(records)) for _, record := range records { if record == nil || record.ApprovalInstanceID == nil || *record.ApprovalInstanceID == 0 { continue } id := *record.ApprovalInstanceID if _, exists := seen[id]; exists { continue } seen[id] = struct{}{} ids = append(ids, id) } summaries := make(map[uint]approvalSummary, len(ids)) if len(ids) == 0 { return summaries, nil } var instances []model.ApprovalInstance if err := s.db.WithContext(ctx).Select("id", "provider", "status").Where("id IN ?", ids).Find(&instances).Error; err != nil { return nil, errors.Wrap(errors.CodeDatabaseError, err, "批量查询充值审批状态失败") } for _, instance := range instances { summaries[instance.ID] = approvalSummary{Provider: instance.Provider, Status: instance.Status} } return summaries, nil } func applyApprovalSummary(response *dto.AgentRechargeResponse, summaries map[uint]approvalSummary, instanceID *uint) { if response == nil || instanceID == nil { return } summary, exists := summaries[*instanceID] if !exists { return } status := summary.Status response.ApprovalProvider = summary.Provider response.ApprovalStatus = &status response.ApprovalStatusName = constants.GetApprovalStatusName(status) } func (s *Service) loadSubmitterNames(ctx context.Context, ids []uint) (map[uint]string, error) { accounts, err := postgres.NewAccountStore(s.db, nil).GetDisplayAccountsByIDs(ctx, ids) if err != nil { return nil, err } names := make(map[uint]string, len(accounts)) for _, account := range accounts { names[account.ID] = account.Username } return names, nil } func (s *Service) loadSubmitterNameBestEffort(ctx context.Context, id uint) string { names, err := s.loadSubmitterNames(ctx, []uint{id}) if err != nil { s.logger.Warn("查询充值提交人失败", zap.Uint("submitter_id", id), zap.Error(err)) return "" } return names[id] }