package audit import ( "context" "strconv" "gorm.io/gorm" agentrecharge "github.com/break/junhong_cmp_fiber/internal/application/agentrecharge" "github.com/break/junhong_cmp_fiber/internal/model" "github.com/break/junhong_cmp_fiber/pkg/constants" "github.com/break/junhong_cmp_fiber/pkg/errors" ) // WriteAgentRecharge 将代理充值申请、终态和实际入账写入统一 Audit Event。 func (w *Writer) WriteAgentRecharge(ctx context.Context, tx *gorm.DB, change agentrecharge.RechargeAudit) error { if change.Record == nil || change.Record.ID == 0 || change.Record.RechargeNo == "" { return errors.New(errors.CodeInvalidParam, "代理充值审计资源不完整") } resources, err := agentRechargeResources(ctx, tx, change) if err != nil { return err } return w.Append(ctx, tx, AppendInput{ ActionCode: change.ActionCode, Summary: change.Summary, ScopeType: constants.AuditScopePlatform, Result: constants.AuditResultSuccess, CorrelationID: change.Record.RechargeNo, Resources: resources, }) } func agentRechargeResources(ctx context.Context, tx *gorm.DB, change agentrecharge.RechargeAudit) ([]ResourceInput, error) { record := change.Record id := strconv.FormatUint(uint64(record.ID), 10) primary := ResourceInput{ Type: constants.AuditResourceAgentRecharge, ID: &id, Key: record.RechargeNo, DisplayName: record.RechargeNo, Relation: constants.AuditResourceRelationPrimary, Role: constants.AuditResourceRoleRechargeTarget, IdentitySnapshot: map[string]any{ "id": record.ID, "recharge_no": record.RechargeNo, "user_id": record.UserID, "shop_id": record.ShopID, "agent_wallet_id": record.AgentWalletID, "amount": record.Amount, "payment_method": record.PaymentMethod, "payment_channel": record.PaymentChannel, "payment_transaction_id": record.PaymentTransactionID, "approval_instance_id": record.ApprovalInstanceID, "status": record.Status, }, BeforeData: change.BeforeData, AfterData: change.AfterData, SubjectVisibility: constants.AuditSubjectResult, SubjectSummary: change.Summary, } resources := []ResourceInput{primary} var account model.Account if record.UserID > 0 { if err := tx.WithContext(ctx).Unscoped().First(&account, record.UserID).Error; err != nil && err != gorm.ErrRecordNotFound { return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询代理充值提交人审计快照失败") } if account.ID > 0 { accountID := strconv.FormatUint(uint64(account.ID), 10) resources = append(resources, ResourceInput{ Type: constants.AuditResourceAccount, ID: &accountID, Key: accountID, DisplayName: account.Username, Relation: constants.AuditResourceRelationReference, Role: constants.AuditResourceRoleRechargeSubmitter, IdentitySnapshot: accountIdentity(&account), }) } } var shop model.Shop if err := tx.WithContext(ctx).Unscoped().First(&shop, record.ShopID).Error; err != nil { return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询代理充值店铺审计快照失败") } resources = append(resources, ShopResource(&shop, constants.AuditResourceRelationReference, constants.AuditResourceRoleRechargeShop)) if change.Payment != nil { resources = append(resources, PaymentResource(change.Payment, constants.AuditResourceRelationReference, constants.AuditResourceRolePaymentTarget, nil, nil)) } if change.Approval != nil { approvalID := strconv.FormatUint(uint64(change.Approval.ID), 10) resources = append(resources, ResourceInput{ Type: constants.AuditResourceApprovalInstance, ID: &approvalID, Key: approvalID, DisplayName: "审批实例 " + approvalID, Relation: constants.AuditResourceRelationReference, Role: constants.AuditResourceRoleRechargeApproval, IdentitySnapshot: map[string]any{ "id": change.Approval.ID, "business_type": change.Approval.BusinessType, "business_id": change.Approval.BusinessID, "submitter_account_id": change.Approval.SubmitterAccountID, "provider": change.Approval.Provider, "external_ref": change.Approval.ExternalRef, "correlation_id": change.Approval.CorrelationID, "status": change.Approval.Status, }, }) } if change.Wallet != nil { walletID := strconv.FormatUint(uint64(change.Wallet.ID), 10) relation := constants.AuditResourceRelationReference if change.Transaction != nil { relation = constants.AuditResourceRelationAffected } wallet := ResourceInput{ Type: constants.AuditResourceAgentWallet, ID: &walletID, Key: walletID, DisplayName: "代理主钱包 " + walletID, Relation: relation, Role: constants.AuditResourceRoleRechargeWallet, IdentitySnapshot: map[string]any{ "id": change.Wallet.ID, "shop_id": change.Wallet.ShopID, "wallet_type": change.Wallet.WalletType, "currency": change.Wallet.Currency, "status": change.Wallet.Status, }, SubjectVisibility: constants.AuditSubjectResult, SubjectSummary: change.Summary, } if change.Transaction != nil { wallet.BeforeData = map[string]any{"balance": change.Transaction.BalanceBefore} wallet.AfterData = map[string]any{"balance": change.Transaction.BalanceAfter} } resources = append(resources, wallet) } if change.Transaction != nil { transactionID := strconv.FormatUint(uint64(change.Transaction.ID), 10) resources = append(resources, ResourceInput{ Type: constants.AuditResourceAgentWalletTransaction, ID: &transactionID, Key: transactionID, DisplayName: "代理钱包流水 " + transactionID, Relation: constants.AuditResourceRelationAffected, Role: constants.AuditResourceRoleRechargeWalletTransaction, IdentitySnapshot: map[string]any{ "id": change.Transaction.ID, "agent_wallet_id": change.Transaction.AgentWalletID, "shop_id": change.Transaction.ShopID, "transaction_type": change.Transaction.TransactionType, "transaction_subtype": change.Transaction.TransactionSubtype, "reference_type": change.Transaction.ReferenceType, "reference_id": change.Transaction.ReferenceID, "status": change.Transaction.Status, }, AfterData: map[string]any{ "amount": change.Transaction.Amount, "balance_before": change.Transaction.BalanceBefore, "balance_after": change.Transaction.BalanceAfter, }, }) } return resources, nil } // AssetRechargeReferences 构造个人资产充值关联的提交人、钱包和资产资源。 func AssetRechargeReferences(ctx context.Context, tx *gorm.DB, recharge *model.RechargeOrder) ([]ResourceInput, error) { if recharge == nil || recharge.ID == 0 { return nil, errors.New(errors.CodeInvalidParam, "资产充值审计资源不完整") } resources := make([]ResourceInput, 0, 3) var customer model.PersonalCustomer if err := tx.WithContext(ctx).Unscoped().First(&customer, recharge.UserID).Error; err != nil { return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询资产充值提交人审计快照失败") } customerID := strconv.FormatUint(uint64(customer.ID), 10) resources = append(resources, ResourceInput{ Type: constants.AuditResourcePersonalCustomer, ID: &customerID, Key: customerID, DisplayName: customer.Nickname, Relation: constants.AuditResourceRelationReference, Role: constants.AuditResourceRoleRechargeSubmitter, IdentitySnapshot: map[string]any{ "id": customer.ID, "nickname": customer.Nickname, "wx_open_id": customer.WxOpenID, "wx_union_id": customer.WxUnionID, "status": customer.Status, }, }) var wallet model.AssetWallet if err := tx.WithContext(ctx).First(&wallet, recharge.AssetWalletID).Error; err != nil { return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询资产充值钱包审计快照失败") } walletID := strconv.FormatUint(uint64(wallet.ID), 10) resources = append(resources, ResourceInput{ Type: constants.AuditResourceAssetWallet, ID: &walletID, Key: walletID, DisplayName: "资产钱包 " + walletID, Relation: constants.AuditResourceRelationReference, Role: constants.AuditResourceRoleRechargeWallet, IdentitySnapshot: map[string]any{ "id": wallet.ID, "resource_type": wallet.ResourceType, "resource_id": wallet.ResourceID, "currency": wallet.Currency, "shop_id_tag": wallet.ShopIDTag, "enterprise_id_tag": wallet.EnterpriseIDTag, }, }) switch recharge.ResourceType { case constants.AssetWalletResourceTypeIotCard: var card model.IotCard if err := tx.WithContext(ctx).Unscoped().First(&card, recharge.ResourceID).Error; err != nil { return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询资产充值卡审计快照失败") } cardID := strconv.FormatUint(uint64(card.ID), 10) resources = append(resources, ResourceInput{ Type: constants.AuditResourceIotCard, ID: &cardID, Key: IotCardResourceKey(&card), DisplayName: card.ICCID, Relation: constants.AuditResourceRelationReference, Role: constants.AuditResourceRoleOrderAsset, IdentitySnapshot: IotCardIdentitySnapshot(&card), SubjectVisibility: constants.AuditSubjectResult, SubjectSummary: "资产充值状态已更新", }) case constants.AssetWalletResourceTypeDevice: var device model.Device if err := tx.WithContext(ctx).Unscoped().First(&device, recharge.ResourceID).Error; err != nil { return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询资产充值设备审计快照失败") } deviceID := strconv.FormatUint(uint64(device.ID), 10) resources = append(resources, ResourceInput{ Type: constants.AuditResourceDevice, ID: &deviceID, Key: DeviceResourceKey(&device), DisplayName: device.VirtualNo, Relation: constants.AuditResourceRelationReference, Role: constants.AuditResourceRoleOrderAsset, IdentitySnapshot: DeviceIdentitySnapshot(&device), SubjectVisibility: constants.AuditSubjectResult, SubjectSummary: "资产充值状态已更新", }) } return resources, nil }