All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m52s
- 受控配置新增代理在线自充允许范围(仅微信/仅支付宝/同时支持),读侧与创建侧取允许范围与可用商户池交集,两侧失败关闭 - 新增允许范围查询与修改端点,读限代理与平台账号、写限超级管理员,复用受控配置写服务留痕 - tb_agent_recharge_record 新增交易流水号、线下收款方式三列快照与其他凭证列(成对迁移 000213) - 线下申请校验启用的收款方式字典项与必填交易流水号,交易流水号独立于在线渠道交易号、不参与去重 - 扩展 offline_recharge_approval 场景可映射字段白名单与字典引用保护 - 新增付款凭证识别能力与交易流水号预填接口,识别不落库、日志不记录载荷
404 lines
19 KiB
Go
404 lines
19 KiB
Go
// Package agentrecharge 收口员工线下代充值申请和审批终态业务用例。
|
||
package agentrecharge
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"strings"
|
||
|
||
"github.com/bytedance/sonic"
|
||
"gorm.io/gorm"
|
||
"gorm.io/gorm/clause"
|
||
|
||
approvalapp "github.com/break/junhong_cmp_fiber/internal/application/approval"
|
||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||
)
|
||
|
||
// CreateOfflineCommand 描述员工创建线下代充值审批申请的稳定输入。
|
||
type CreateOfflineCommand struct {
|
||
SubmitterAccountID uint
|
||
SubmitterUserType int
|
||
ShopID uint
|
||
RechargeNo string
|
||
Amount int64
|
||
PaymentVoucherKeys []string
|
||
OtherVoucherKeys []string
|
||
// OfflinePaymentMethodID 是提交人选择的线下收款方式字典项 ID。
|
||
OfflinePaymentMethodID uint
|
||
// ExternalTransactionNo 是人工确认后的交易流水号,独立于在线渠道第三方交易号。
|
||
ExternalTransactionNo string
|
||
Remark string
|
||
}
|
||
|
||
// CreateOfflineResult 返回已原子保存的业务申请和初始审批状态。
|
||
type CreateOfflineResult struct {
|
||
Record *model.AgentRechargeRecord
|
||
ShopName string
|
||
SubmitterName string
|
||
ApprovalStatus int
|
||
}
|
||
|
||
// OfflineCreationService 创建员工线下代充值申请及唯一通用审批实例。
|
||
type OfflineCreationService struct {
|
||
db *gorm.DB
|
||
approval approvalapp.Port
|
||
audit RechargeAuditWriter
|
||
}
|
||
|
||
// NewOfflineCreationService 创建员工线下代充值申请用例。
|
||
func NewOfflineCreationService(db *gorm.DB, approval approvalapp.Port, audit RechargeAuditWriter) *OfflineCreationService {
|
||
return &OfflineCreationService{db: db, approval: approval, audit: audit}
|
||
}
|
||
|
||
// TriggerHistorical 为历史待审批线下代充值补发一次企业微信审批。
|
||
func (s *OfflineCreationService) TriggerHistorical(ctx context.Context, recordID uint) (*CreateOfflineResult, error) {
|
||
if s == nil || s.db == nil || s.approval == nil || s.audit == nil || recordID == 0 {
|
||
return nil, errors.New(errors.CodeServiceUnavailable, "员工线下代充值审批能力未配置")
|
||
}
|
||
|
||
var record model.AgentRechargeRecord
|
||
if err := s.db.WithContext(ctx).First(&record, recordID).Error; err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
return nil, errors.New(errors.CodeNotFound, "充值记录不存在")
|
||
}
|
||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询历史线下代充值申请失败")
|
||
}
|
||
if record.PaymentMethod != constants.RechargeMethodOffline || record.Status != constants.RechargeStatusPending || record.ApprovalInstanceID != nil {
|
||
return nil, errors.New(errors.CodeConflict, "充值申请状态不允许补发审批")
|
||
}
|
||
account, shop, wallet, err := s.loadHistoricalFacts(ctx, &record)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
preparation, err := s.approval.Prepare(ctx, approvalapp.PrepareRequest{
|
||
BusinessType: constants.ApprovalBusinessTypeOfflineRecharge, SubmitterAccountID: record.UserID,
|
||
CorrelationID: record.RechargeNo,
|
||
})
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
command := CreateOfflineCommand{
|
||
SubmitterAccountID: record.UserID, SubmitterUserType: account.UserType, ShopID: record.ShopID,
|
||
RechargeNo: record.RechargeNo, Amount: record.Amount,
|
||
PaymentVoucherKeys: []string(record.PaymentVoucherKey), Remark: record.Remark,
|
||
OtherVoucherKeys: []string(record.OtherVoucherKeys),
|
||
}
|
||
if record.ExternalTransactionNo != nil {
|
||
command.ExternalTransactionNo = *record.ExternalTransactionNo
|
||
}
|
||
// 补发审批使用历史记录已冻结的收款方式快照,不回查当前字典,避免历史材料被字典变更改写。
|
||
var frozenCode, frozenName string
|
||
if record.OfflinePaymentMethodCode != nil {
|
||
frozenCode = *record.OfflinePaymentMethodCode
|
||
}
|
||
if record.OfflinePaymentMethodName != nil {
|
||
frozenName = *record.OfflinePaymentMethodName
|
||
}
|
||
submitterSnapshot, requestSnapshot, err := offlineApprovalSnapshots(command, account.Username, shop.ShopName, frozenCode, frozenName)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
var approvalStatus int
|
||
err = s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||
var current model.AgentRechargeRecord
|
||
if err := tx.WithContext(ctx).Clauses(clause.Locking{Strength: "UPDATE"}).First(¤t, recordID).Error; err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
return errors.New(errors.CodeNotFound, "充值记录不存在")
|
||
}
|
||
return errors.Wrap(errors.CodeDatabaseError, err, "锁定历史线下代充值申请失败")
|
||
}
|
||
if current.PaymentMethod != constants.RechargeMethodOffline || current.Status != constants.RechargeStatusPending || current.ApprovalInstanceID != nil {
|
||
return errors.New(errors.CodeConflict, "充值申请状态不允许补发审批")
|
||
}
|
||
reference, err := s.approval.CreateInTx(ctx, tx, approvalapp.CreateRequest{
|
||
Preparation: preparation, BusinessType: constants.ApprovalBusinessTypeOfflineRecharge,
|
||
BusinessID: current.ID, SubmitterAccountID: current.UserID,
|
||
SubmitterSnapshot: submitterSnapshot, RequestSnapshot: requestSnapshot,
|
||
CorrelationID: current.RechargeNo,
|
||
})
|
||
if err != nil {
|
||
return err
|
||
}
|
||
result := tx.WithContext(ctx).Model(&model.AgentRechargeRecord{}).
|
||
Where("id = ? AND payment_method = ? AND status = ? AND approval_instance_id IS NULL", current.ID, constants.RechargeMethodOffline, constants.RechargeStatusPending).
|
||
Update("approval_instance_id", reference.InstanceID)
|
||
if result.Error != nil {
|
||
return errors.Wrap(errors.CodeDatabaseError, result.Error, "关联线下代充值审批实例失败")
|
||
}
|
||
if result.RowsAffected != 1 {
|
||
return errors.New(errors.CodeConflict, "线下代充值审批实例关联已变化")
|
||
}
|
||
current.ApprovalInstanceID = &reference.InstanceID
|
||
record = current
|
||
approvalStatus = reference.Status
|
||
var instance model.ApprovalInstance
|
||
if err := tx.WithContext(ctx).First(&instance, reference.InstanceID).Error; err != nil {
|
||
return errors.Wrap(errors.CodeDatabaseError, err, "查询线下代充值审批审计快照失败")
|
||
}
|
||
return s.audit.WriteAgentRecharge(ctx, tx, RechargeAudit{
|
||
ActionCode: constants.AuditActionAgentRechargeCreated, Summary: "补发员工线下代充值审批",
|
||
Record: ¤t, Approval: &instance, Wallet: wallet,
|
||
AfterData: map[string]any{"status": current.Status, "approval_instance_id": current.ApprovalInstanceID},
|
||
})
|
||
})
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return &CreateOfflineResult{
|
||
Record: &record, ShopName: shop.ShopName, SubmitterName: account.Username, ApprovalStatus: approvalStatus,
|
||
}, nil
|
||
}
|
||
|
||
// Execute 在业务写入前校验审批渠道,并在同一事务保存充值申请、审批实例和提交 Outbox。
|
||
func (s *OfflineCreationService) Execute(ctx context.Context, command CreateOfflineCommand) (*CreateOfflineResult, error) {
|
||
if s == nil || s.db == nil || s.approval == nil || s.audit == nil {
|
||
return nil, errors.New(errors.CodeServiceUnavailable, "员工线下代充值审批能力未配置")
|
||
}
|
||
if err := validateCreateOfflineCommand(command); err != nil {
|
||
return nil, err
|
||
}
|
||
account, shop, wallet, err := s.loadCreationFacts(ctx, command)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
preparation, err := s.approval.Prepare(ctx, approvalapp.PrepareRequest{
|
||
BusinessType: constants.ApprovalBusinessTypeOfflineRecharge, SubmitterAccountID: command.SubmitterAccountID,
|
||
CorrelationID: strings.TrimSpace(command.RechargeNo),
|
||
})
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
paymentChannel := constants.RechargeMethodOffline
|
||
externalTransactionNo := strings.TrimSpace(command.ExternalTransactionNo)
|
||
var record *model.AgentRechargeRecord
|
||
var approvalStatus int
|
||
err = s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||
paymentMethod, err := loadEnabledOfflinePaymentMethod(ctx, tx, command.OfflinePaymentMethodID)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
submitterSnapshot, requestSnapshot, err := offlineApprovalSnapshots(command, account.Username, shop.ShopName, paymentMethod.Code, paymentMethod.Name)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
record = &model.AgentRechargeRecord{
|
||
UserID: command.SubmitterAccountID, AgentWalletID: wallet.ID, ShopID: command.ShopID,
|
||
RechargeNo: strings.TrimSpace(command.RechargeNo), Amount: command.Amount,
|
||
PaymentMethod: constants.RechargeMethodOffline, PaymentChannel: &paymentChannel,
|
||
PaymentVoucherKey: model.StringJSONBArray(command.PaymentVoucherKeys), Remark: strings.TrimSpace(command.Remark),
|
||
ExternalTransactionNo: &externalTransactionNo,
|
||
OfflinePaymentMethodID: &paymentMethod.ID,
|
||
OfflinePaymentMethodCode: &paymentMethod.Code,
|
||
OfflinePaymentMethodName: &paymentMethod.Name,
|
||
OtherVoucherKeys: model.StringJSONBArray(command.OtherVoucherKeys),
|
||
Status: constants.RechargeStatusPending, ShopIDTag: wallet.ShopIDTag, EnterpriseIDTag: wallet.EnterpriseIDTag,
|
||
}
|
||
if err := tx.WithContext(ctx).Create(record).Error; err != nil {
|
||
return errors.Wrap(errors.CodeDatabaseError, err, "创建员工线下代充值申请失败")
|
||
}
|
||
reference, err := s.approval.CreateInTx(ctx, tx, approvalapp.CreateRequest{
|
||
Preparation: preparation, BusinessType: constants.ApprovalBusinessTypeOfflineRecharge,
|
||
BusinessID: record.ID, SubmitterAccountID: command.SubmitterAccountID,
|
||
SubmitterSnapshot: submitterSnapshot, RequestSnapshot: requestSnapshot,
|
||
CorrelationID: record.RechargeNo,
|
||
})
|
||
if err != nil {
|
||
return err
|
||
}
|
||
result := tx.WithContext(ctx).Model(&model.AgentRechargeRecord{}).
|
||
Where("id = ? AND approval_instance_id IS NULL", record.ID).
|
||
Update("approval_instance_id", reference.InstanceID)
|
||
if result.Error != nil {
|
||
return errors.Wrap(errors.CodeDatabaseError, result.Error, "关联员工线下代充值审批实例失败")
|
||
}
|
||
if result.RowsAffected != 1 {
|
||
return errors.New(errors.CodeConflict, "员工线下代充值审批实例关联已变化")
|
||
}
|
||
record.ApprovalInstanceID = &reference.InstanceID
|
||
approvalStatus = reference.Status
|
||
var instance model.ApprovalInstance
|
||
if err := tx.WithContext(ctx).First(&instance, reference.InstanceID).Error; err != nil {
|
||
return errors.Wrap(errors.CodeDatabaseError, err, "查询线下代充值审批审计快照失败")
|
||
}
|
||
return s.audit.WriteAgentRecharge(ctx, tx, RechargeAudit{
|
||
ActionCode: constants.AuditActionAgentRechargeCreated, Summary: "创建员工线下代充值申请",
|
||
Record: record, Approval: &instance, Wallet: wallet, AfterData: map[string]any{"status": record.Status},
|
||
})
|
||
})
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return &CreateOfflineResult{
|
||
Record: record, ShopName: shop.ShopName, SubmitterName: account.Username, ApprovalStatus: approvalStatus,
|
||
}, nil
|
||
}
|
||
|
||
func validateCreateOfflineCommand(command CreateOfflineCommand) error {
|
||
if command.SubmitterUserType != constants.UserTypePlatform && command.SubmitterUserType != constants.UserTypeSuperAdmin {
|
||
return errors.New(errors.CodeForbidden, "线下充值仅平台管理员可操作")
|
||
}
|
||
if command.SubmitterAccountID == 0 || command.ShopID == 0 || strings.TrimSpace(command.RechargeNo) == "" {
|
||
return errors.New(errors.CodeInvalidParam)
|
||
}
|
||
if command.Amount < constants.AgentRechargeMinAmount || command.Amount > constants.AgentRechargeMaxAmount {
|
||
return errors.New(errors.CodeInvalidParam, "充值金额超出允许范围")
|
||
}
|
||
if command.OfflinePaymentMethodID == 0 {
|
||
return errors.New(errors.CodeInvalidParam, "线下充值必须选择线下收款方式")
|
||
}
|
||
if err := validateRechargeTransactionNo(command.ExternalTransactionNo); err != nil {
|
||
return err
|
||
}
|
||
if err := validateVoucherKeys(command.PaymentVoucherKeys, 1, constants.AgentRechargePaymentVoucherMaxCount, "线下充值必须上传 1 至 5 个支付凭证"); err != nil {
|
||
return err
|
||
}
|
||
return validateVoucherKeys(command.OtherVoucherKeys, 0, constants.AgentRechargeOtherVoucherMaxCount, "线下充值其他凭证最多 5 个")
|
||
}
|
||
|
||
// validateRechargeTransactionNo 校验交易流水号必填且不超过长度上限;不参与去重与幂等判定。
|
||
func validateRechargeTransactionNo(value string) error {
|
||
trimmed := strings.TrimSpace(value)
|
||
if trimmed == "" {
|
||
return errors.New(errors.CodeInvalidParam, "线下充值必须填写交易流水号")
|
||
}
|
||
if len([]rune(trimmed)) > constants.AgentRechargeExternalTransactionNoMaxLength {
|
||
return errors.New(errors.CodeInvalidParam, "交易流水号长度超出限制")
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// validateVoucherKeys 校验凭证对象键数量与内容,minCount 为 0 时允许为空。
|
||
func validateVoucherKeys(keys []string, minCount, maxCount int, message string) error {
|
||
if len(keys) < minCount || len(keys) > maxCount {
|
||
return errors.New(errors.CodeInvalidParam, message)
|
||
}
|
||
seen := make(map[string]struct{}, len(keys))
|
||
for _, key := range keys {
|
||
trimmed := strings.TrimSpace(key)
|
||
if trimmed == "" {
|
||
return errors.New(errors.CodeInvalidParam, "线下充值凭证对象键不能为空")
|
||
}
|
||
if len([]rune(trimmed)) > constants.AgentRechargeVoucherKeyMaxLength {
|
||
return errors.New(errors.CodeInvalidParam, "线下充值凭证对象键长度超出限制")
|
||
}
|
||
if _, exists := seen[trimmed]; exists {
|
||
return errors.New(errors.CodeInvalidParam, "线下充值凭证对象键不能重复")
|
||
}
|
||
seen[trimmed] = struct{}{}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// loadEnabledOfflinePaymentMethod 读取启用的线下收款方式字典项;不存在或已停用一律拒绝。
|
||
// 仅校验存在性与启停,不做编码或名称的二次改写,快照以字典当前值为准。
|
||
func loadEnabledOfflinePaymentMethod(ctx context.Context, tx *gorm.DB, id uint) (*model.EmployeeCollectionPaymentMethod, error) {
|
||
if id == 0 {
|
||
return nil, errors.New(errors.CodeInvalidParam, "线下充值必须选择线下收款方式")
|
||
}
|
||
var paymentMethod model.EmployeeCollectionPaymentMethod
|
||
if err := tx.WithContext(ctx).First(&paymentMethod, id).Error; err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
return nil, errors.New(errors.CodeEmployeeCollectionPaymentMethodNotFound)
|
||
}
|
||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询线下收款方式失败")
|
||
}
|
||
if paymentMethod.Status != constants.EmployeeCollectionPaymentMethodStatusEnabled {
|
||
return nil, errors.New(errors.CodeEmployeeCollectionPaymentMethodDisabled)
|
||
}
|
||
return &paymentMethod, nil
|
||
}
|
||
|
||
func (s *OfflineCreationService) loadHistoricalFacts(
|
||
ctx context.Context, record *model.AgentRechargeRecord,
|
||
) (*model.Account, *model.Shop, *model.AgentWallet, error) {
|
||
if record == nil || record.UserID == 0 || record.ShopID == 0 || record.AgentWalletID == 0 {
|
||
return nil, nil, nil, errors.New(errors.CodeInvalidParam)
|
||
}
|
||
var account model.Account
|
||
if err := s.db.WithContext(ctx).Where("id = ? AND status = ?", record.UserID, constants.StatusEnabled).First(&account).Error; err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
return nil, nil, nil, errors.New(errors.CodeForbidden, "原创建账号不可用")
|
||
}
|
||
return nil, nil, nil, errors.Wrap(errors.CodeDatabaseError, err, "查询历史线下代充值创建人失败")
|
||
}
|
||
var shop model.Shop
|
||
if err := s.db.WithContext(ctx).Where("id = ?", record.ShopID).First(&shop).Error; err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
return nil, nil, nil, errors.New(errors.CodeNotFound, "目标店铺不存在")
|
||
}
|
||
return nil, nil, nil, errors.Wrap(errors.CodeDatabaseError, err, "查询历史线下代充值目标店铺失败")
|
||
}
|
||
var wallet model.AgentWallet
|
||
if err := s.db.WithContext(ctx).
|
||
Where("id = ? AND shop_id = ? AND wallet_type = ? AND status = ?", record.AgentWalletID, record.ShopID, constants.AgentWalletTypeMain, constants.AgentWalletStatusNormal).
|
||
First(&wallet).Error; err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
return nil, nil, nil, errors.New(errors.CodeWalletNotFound, "原充值主钱包不存在或不可用")
|
||
}
|
||
return nil, nil, nil, errors.Wrap(errors.CodeDatabaseError, err, "查询历史线下代充值主钱包失败")
|
||
}
|
||
return &account, &shop, &wallet, nil
|
||
}
|
||
|
||
func (s *OfflineCreationService) loadCreationFacts(
|
||
ctx context.Context,
|
||
command CreateOfflineCommand,
|
||
) (*model.Account, *model.Shop, *model.AgentWallet, error) {
|
||
var account model.Account
|
||
if err := s.db.WithContext(ctx).Where("id = ? AND status = ?", command.SubmitterAccountID, constants.StatusEnabled).First(&account).Error; err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
return nil, nil, nil, errors.New(errors.CodeForbidden, "提交人账号不可用")
|
||
}
|
||
return nil, nil, nil, errors.Wrap(errors.CodeDatabaseError, err, "查询线下代充值提交人失败")
|
||
}
|
||
var shop model.Shop
|
||
if err := s.db.WithContext(ctx).Where("id = ?", command.ShopID).First(&shop).Error; err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
return nil, nil, nil, errors.New(errors.CodeNotFound, "目标店铺不存在")
|
||
}
|
||
return nil, nil, nil, errors.Wrap(errors.CodeDatabaseError, err, "查询目标店铺失败")
|
||
}
|
||
var wallet model.AgentWallet
|
||
if err := s.db.WithContext(ctx).
|
||
Where("shop_id = ? AND wallet_type = ? AND status = ?", command.ShopID, constants.AgentWalletTypeMain, constants.AgentWalletStatusNormal).
|
||
First(&wallet).Error; err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
return nil, nil, nil, errors.New(errors.CodeWalletNotFound, "目标店铺主钱包不存在或不可用")
|
||
}
|
||
return nil, nil, nil, errors.Wrap(errors.CodeDatabaseError, err, "查询目标店铺主钱包失败")
|
||
}
|
||
return &account, &shop, &wallet, nil
|
||
}
|
||
|
||
func offlineApprovalSnapshots(command CreateOfflineCommand, submitterName, shopName, paymentMethodCode, paymentMethodName string) ([]byte, []byte, error) {
|
||
submitterSnapshot, err := sonic.Marshal(map[string]any{
|
||
"account_id": command.SubmitterAccountID, "account_name": submitterName,
|
||
"user_type": command.SubmitterUserType,
|
||
})
|
||
if err != nil {
|
||
return nil, nil, errors.Wrap(errors.CodeInternalError, err, "编码线下代充值提交人快照失败")
|
||
}
|
||
requestSnapshot, err := sonic.Marshal(map[string]any{
|
||
constants.ApprovalFieldRechargeNo: strings.TrimSpace(command.RechargeNo),
|
||
constants.ApprovalFieldShopID: command.ShopID,
|
||
constants.ApprovalFieldShopName: shopName,
|
||
constants.ApprovalFieldAmount: fmt.Sprintf("%d.%02d", command.Amount/100, command.Amount%100),
|
||
constants.ApprovalFieldAmountCent: command.Amount,
|
||
constants.ApprovalFieldPaymentVoucherKey: command.PaymentVoucherKeys,
|
||
constants.ApprovalFieldRemark: strings.TrimSpace(command.Remark),
|
||
constants.ApprovalFieldSubmitterID: command.SubmitterAccountID,
|
||
constants.ApprovalFieldSubmitterName: submitterName,
|
||
constants.ApprovalFieldOfflinePaymentMethod: paymentMethodName,
|
||
constants.ApprovalFieldOfflinePaymentMethodCode: paymentMethodCode,
|
||
constants.ApprovalFieldExternalTransactionNo: strings.TrimSpace(command.ExternalTransactionNo),
|
||
constants.ApprovalFieldOtherVoucherKey: command.OtherVoucherKeys,
|
||
})
|
||
if err != nil {
|
||
return nil, nil, errors.Wrap(errors.CodeInternalError, err, "编码线下代充值审批业务快照失败")
|
||
}
|
||
return submitterSnapshot, requestSnapshot, nil
|
||
}
|