package finance import ( "context" "fmt" "net/http" "time" "tyapi-server/internal/application/finance/dto/commands" "tyapi-server/internal/application/finance/dto/queries" "tyapi-server/internal/application/finance/dto/responses" "tyapi-server/internal/config" finance_entities "tyapi-server/internal/domains/finance/entities" finance_repositories "tyapi-server/internal/domains/finance/repositories" finance_services "tyapi-server/internal/domains/finance/services" user_repositories "tyapi-server/internal/domains/user/repositories" "tyapi-server/internal/shared/database" "tyapi-server/internal/shared/export" "tyapi-server/internal/shared/interfaces" "tyapi-server/internal/shared/payment" "github.com/shopspring/decimal" "github.com/smartwalle/alipay/v3" "github.com/wechatpay-apiv3/wechatpay-go/services/payments" "go.uber.org/zap" ) // FinanceApplicationServiceImpl 财务应用服务实现 type FinanceApplicationServiceImpl struct { aliPayClient *payment.AliPayService wechatPayService *payment.WechatPayService walletService finance_services.WalletAggregateService rechargeRecordService finance_services.RechargeRecordService walletTransactionRepository finance_repositories.WalletTransactionRepository alipayOrderRepo finance_repositories.AlipayOrderRepository wechatOrderRepo finance_repositories.WechatOrderRepository rechargeRecordRepo finance_repositories.RechargeRecordRepository userRepo user_repositories.UserRepository txManager *database.TransactionManager exportManager *export.ExportManager logger *zap.Logger config *config.Config } // NewFinanceApplicationService 创建财务应用服务 func NewFinanceApplicationService( aliPayClient *payment.AliPayService, wechatPayService *payment.WechatPayService, walletService finance_services.WalletAggregateService, rechargeRecordService finance_services.RechargeRecordService, walletTransactionRepository finance_repositories.WalletTransactionRepository, alipayOrderRepo finance_repositories.AlipayOrderRepository, wechatOrderRepo finance_repositories.WechatOrderRepository, rechargeRecordRepo finance_repositories.RechargeRecordRepository, userRepo user_repositories.UserRepository, txManager *database.TransactionManager, logger *zap.Logger, config *config.Config, exportManager *export.ExportManager, ) FinanceApplicationService { return &FinanceApplicationServiceImpl{ aliPayClient: aliPayClient, wechatPayService: wechatPayService, walletService: walletService, rechargeRecordService: rechargeRecordService, walletTransactionRepository: walletTransactionRepository, alipayOrderRepo: alipayOrderRepo, wechatOrderRepo: wechatOrderRepo, rechargeRecordRepo: rechargeRecordRepo, userRepo: userRepo, txManager: txManager, exportManager: exportManager, logger: logger, config: config, } } func (s *FinanceApplicationServiceImpl) CreateWallet(ctx context.Context, cmd *commands.CreateWalletCommand) (*responses.WalletResponse, error) { // 调用钱包聚合服务创建钱包 wallet, err := s.walletService.CreateWallet(ctx, cmd.UserID) if err != nil { s.logger.Error("创建钱包失败", zap.Error(err)) return nil, err } return &responses.WalletResponse{ ID: wallet.ID, UserID: wallet.UserID, IsActive: wallet.IsActive, Balance: wallet.Balance, BalanceStatus: wallet.GetBalanceStatus(), IsArrears: wallet.IsArrears(), IsLowBalance: wallet.IsLowBalance(), CreatedAt: wallet.CreatedAt, UpdatedAt: wallet.UpdatedAt, }, nil } func (s *FinanceApplicationServiceImpl) GetWallet(ctx context.Context, query *queries.GetWalletInfoQuery) (*responses.WalletResponse, error) { // 调用钱包聚合服务获取钱包信息 wallet, err := s.walletService.LoadWalletByUserId(ctx, query.UserID) if err != nil { s.logger.Error("获取钱包信息失败", zap.Error(err)) return nil, err } return &responses.WalletResponse{ ID: wallet.ID, UserID: wallet.UserID, IsActive: wallet.IsActive, Balance: wallet.Balance, BalanceStatus: wallet.GetBalanceStatus(), IsArrears: wallet.IsArrears(), IsLowBalance: wallet.IsLowBalance(), CreatedAt: wallet.CreatedAt, UpdatedAt: wallet.UpdatedAt, }, nil } // CreateAlipayRechargeOrder 创建支付宝充值订单(完整流程编排) func (s *FinanceApplicationServiceImpl) CreateAlipayRechargeOrder(ctx context.Context, cmd *commands.CreateAlipayRechargeCommand) (*responses.AlipayRechargeOrderResponse, error) { cmd.Subject = "天远数据API充值" // 将字符串金额转换为 decimal.Decimal amount, err := decimal.NewFromString(cmd.Amount) if err != nil { s.logger.Error("金额格式错误", zap.String("amount", cmd.Amount), zap.Error(err)) return nil, fmt.Errorf("金额格式错误: %w", err) } // 验证金额是否大于0 if amount.LessThanOrEqual(decimal.Zero) { return nil, fmt.Errorf("充值金额必须大于0") } // 从配置中获取充值限制 minAmount, err := decimal.NewFromString(s.config.Wallet.MinAmount) if err != nil { s.logger.Error("配置中的最低充值金额格式错误", zap.String("min_amount", s.config.Wallet.MinAmount), zap.Error(err)) return nil, fmt.Errorf("系统配置错误: %w", err) } maxAmount, err := decimal.NewFromString(s.config.Wallet.MaxAmount) if err != nil { s.logger.Error("配置中的最高充值金额格式错误", zap.String("max_amount", s.config.Wallet.MaxAmount), zap.Error(err)) return nil, fmt.Errorf("系统配置错误: %w", err) } // 验证充值金额范围 if amount.LessThan(minAmount) { return nil, fmt.Errorf("充值金额不能少于%s元", minAmount.String()) } if amount.GreaterThan(maxAmount) { return nil, fmt.Errorf("单次充值金额不能超过%s元", maxAmount.String()) } // 1. 生成订单号 outTradeNo := s.aliPayClient.GenerateOutTradeNo() var payUrl string // 2. 进入事务,创建充值记录和支付宝订单本地记录 err = s.txManager.ExecuteInTx(ctx, func(txCtx context.Context) error { var err error // 创建充值记录 rechargeRecord, err := s.rechargeRecordService.CreateAlipayRecharge(txCtx, cmd.UserID, amount, outTradeNo) if err != nil { s.logger.Error("创建支付宝充值记录失败", zap.Error(err)) return fmt.Errorf("创建支付宝充值记录失败: %w", err) } // 创建支付宝订单本地记录 err = s.rechargeRecordService.CreateAlipayOrder(txCtx, rechargeRecord.ID, outTradeNo, cmd.Subject, amount, cmd.Platform) if err != nil { s.logger.Error("创建支付宝订单记录失败", zap.Error(err)) return fmt.Errorf("创建支付宝订单记录失败: %w", err) } // 3. 创建支付宝订单(调用支付宝API,非事务内) payUrl, err = s.aliPayClient.CreateAlipayOrder(ctx, cmd.Platform, amount, cmd.Subject, outTradeNo) if err != nil { s.logger.Error("创建支付宝订单失败", zap.Error(err)) return fmt.Errorf("创建支付宝订单失败: %w", err) } return nil }) if err != nil { return nil, err } s.logger.Info("支付宝充值订单创建成功", zap.String("user_id", cmd.UserID), zap.String("out_trade_no", outTradeNo), zap.String("amount", amount.String()), zap.String("platform", cmd.Platform), ) return &responses.AlipayRechargeOrderResponse{ PayURL: payUrl, OutTradeNo: outTradeNo, Amount: amount, Platform: cmd.Platform, Subject: cmd.Subject, }, nil } // CreateWechatRechargeOrder 创建微信充值订单(完整流程编排) func (s *FinanceApplicationServiceImpl) CreateWechatRechargeOrder(ctx context.Context, cmd *commands.CreateWechatRechargeCommand) (*responses.WechatRechargeOrderResponse, error) { cmd.Subject = "天远数据API充值" amount, err := decimal.NewFromString(cmd.Amount) if err != nil { s.logger.Error("金额格式错误", zap.String("amount", cmd.Amount), zap.Error(err)) return nil, fmt.Errorf("金额格式错误: %w", err) } if amount.LessThanOrEqual(decimal.Zero) { return nil, fmt.Errorf("充值金额必须大于0") } minAmount, err := decimal.NewFromString(s.config.Wallet.MinAmount) if err != nil { s.logger.Error("配置中的最低充值金额格式错误", zap.String("min_amount", s.config.Wallet.MinAmount), zap.Error(err)) return nil, fmt.Errorf("系统配置错误: %w", err) } maxAmount, err := decimal.NewFromString(s.config.Wallet.MaxAmount) if err != nil { s.logger.Error("配置中的最高充值金额格式错误", zap.String("max_amount", s.config.Wallet.MaxAmount), zap.Error(err)) return nil, fmt.Errorf("系统配置错误: %w", err) } if amount.LessThan(minAmount) { return nil, fmt.Errorf("充值金额不能少于%s元", minAmount.String()) } if amount.GreaterThan(maxAmount) { return nil, fmt.Errorf("单次充值金额不能超过%s元", maxAmount.String()) } platform := normalizeWechatPlatform(cmd.Platform) if platform != payment.PlatformWxNative && platform != payment.PlatformWxH5 { return nil, fmt.Errorf("不支持的支付平台: %s", cmd.Platform) } if s.wechatPayService == nil { return nil, fmt.Errorf("微信支付服务未初始化") } outTradeNo := s.wechatPayService.GenerateOutTradeNo() s.logger.Info("开始创建微信充值订单", zap.String("user_id", cmd.UserID), zap.String("out_trade_no", outTradeNo), zap.String("amount", amount.String()), zap.String("platform", cmd.Platform), zap.String("subject", cmd.Subject), ) var prepayData interface{} err = s.txManager.ExecuteInTx(ctx, func(txCtx context.Context) error { // 创建微信充值记录 rechargeRecord := finance_entities.NewWechatRechargeRecord(cmd.UserID, amount, outTradeNo) createdRecord, createErr := s.rechargeRecordRepo.Create(txCtx, *rechargeRecord) if createErr != nil { s.logger.Error("创建微信充值记录失败", zap.String("out_trade_no", outTradeNo), zap.String("user_id", cmd.UserID), zap.String("amount", amount.String()), zap.Error(createErr), ) return fmt.Errorf("创建微信充值记录失败: %w", createErr) } s.logger.Info("创建微信充值记录成功", zap.String("out_trade_no", outTradeNo), zap.String("recharge_id", createdRecord.ID), zap.String("user_id", cmd.UserID), ) // 创建微信订单本地记录 wechatOrder := finance_entities.NewWechatOrder(createdRecord.ID, outTradeNo, cmd.Subject, amount, platform) createdOrder, orderErr := s.wechatOrderRepo.Create(txCtx, *wechatOrder) if orderErr != nil { s.logger.Error("创建微信订单记录失败", zap.String("out_trade_no", outTradeNo), zap.String("recharge_id", createdRecord.ID), zap.Error(orderErr), ) return fmt.Errorf("创建微信订单记录失败: %w", orderErr) } s.logger.Info("创建微信订单记录成功", zap.String("out_trade_no", outTradeNo), zap.String("order_id", createdOrder.ID), zap.String("recharge_id", createdRecord.ID), ) return nil }) if err != nil { return nil, err } payCtx := context.WithValue(ctx, "platform", platform) payCtx = context.WithValue(payCtx, "user_id", cmd.UserID) s.logger.Info("调用微信支付接口创建订单", zap.String("out_trade_no", outTradeNo), zap.String("platform", platform), ) prepayData, err = s.wechatPayService.CreateWechatOrder(payCtx, amount.InexactFloat64(), cmd.Subject, outTradeNo) if err != nil { s.logger.Error("微信下单失败", zap.String("out_trade_no", outTradeNo), zap.String("user_id", cmd.UserID), zap.String("amount", amount.String()), zap.Error(err), ) // 回写失败状态 _ = s.txManager.ExecuteInTx(ctx, func(txCtx context.Context) error { order, getErr := s.wechatOrderRepo.GetByOutTradeNo(txCtx, outTradeNo) if getErr == nil && order != nil { order.MarkFailed("create_failed", err.Error()) updateErr := s.wechatOrderRepo.Update(txCtx, *order) if updateErr != nil { s.logger.Error("回写微信订单失败状态失败", zap.String("out_trade_no", outTradeNo), zap.Error(updateErr), ) } else { s.logger.Info("回写微信订单失败状态成功", zap.String("out_trade_no", outTradeNo), ) } } return nil }) return nil, fmt.Errorf("创建微信支付订单失败: %w", err) } s.logger.Info("微信充值订单创建成功", zap.String("user_id", cmd.UserID), zap.String("out_trade_no", outTradeNo), zap.String("amount", amount.String()), zap.String("platform", cmd.Platform), ) return &responses.WechatRechargeOrderResponse{ OutTradeNo: outTradeNo, Amount: amount, Platform: platform, Subject: cmd.Subject, PrepayData: prepayData, }, nil } // normalizeWechatPlatform 将兼容写法(h5/mini)转换为系统内使用的wx_h5/wx_mini func normalizeWechatPlatform(p string) string { switch p { case "h5", payment.PlatformWxH5: return payment.PlatformWxNative case "native": return payment.PlatformWxNative default: return p } } // TransferRecharge 对公转账充值 func (s *FinanceApplicationServiceImpl) TransferRecharge(ctx context.Context, cmd *commands.TransferRechargeCommand) (*responses.RechargeRecordResponse, error) { // 将字符串金额转换为 decimal.Decimal amount, err := decimal.NewFromString(cmd.Amount) if err != nil { s.logger.Error("金额格式错误", zap.String("amount", cmd.Amount), zap.Error(err)) return nil, fmt.Errorf("金额格式错误: %w", err) } // 验证金额是否大于0 if amount.LessThanOrEqual(decimal.Zero) { return nil, fmt.Errorf("充值金额必须大于0") } // 调用充值记录服务进行对公转账充值 rechargeRecord, err := s.rechargeRecordService.TransferRecharge(ctx, cmd.UserID, amount, cmd.TransferOrderID, cmd.Notes) if err != nil { s.logger.Error("对公转账充值失败", zap.Error(err)) return nil, err } transferOrderID := "" if rechargeRecord.TransferOrderID != nil { transferOrderID = *rechargeRecord.TransferOrderID } return &responses.RechargeRecordResponse{ ID: rechargeRecord.ID, UserID: rechargeRecord.UserID, Amount: rechargeRecord.Amount, RechargeType: string(rechargeRecord.RechargeType), Status: string(rechargeRecord.Status), TransferOrderID: transferOrderID, Notes: rechargeRecord.Notes, CreatedAt: rechargeRecord.CreatedAt, UpdatedAt: rechargeRecord.UpdatedAt, }, nil } // GiftRecharge 赠送充值 func (s *FinanceApplicationServiceImpl) GiftRecharge(ctx context.Context, cmd *commands.GiftRechargeCommand) (*responses.RechargeRecordResponse, error) { // 将字符串金额转换为 decimal.Decimal amount, err := decimal.NewFromString(cmd.Amount) if err != nil { s.logger.Error("金额格式错误", zap.String("amount", cmd.Amount), zap.Error(err)) return nil, fmt.Errorf("金额格式错误: %w", err) } // 验证金额是否大于0 if amount.LessThanOrEqual(decimal.Zero) { return nil, fmt.Errorf("充值金额必须大于0") } // 获取当前操作员ID(这里假设从上下文中获取,实际可能需要从认证中间件获取) operatorID := "system" // 临时使用,实际应该从认证上下文获取 // 调用充值记录服务进行赠送充值 rechargeRecord, err := s.rechargeRecordService.GiftRecharge(ctx, cmd.UserID, amount, operatorID, cmd.Notes) if err != nil { s.logger.Error("赠送充值失败", zap.Error(err)) return nil, err } return &responses.RechargeRecordResponse{ ID: rechargeRecord.ID, UserID: rechargeRecord.UserID, Amount: rechargeRecord.Amount, RechargeType: string(rechargeRecord.RechargeType), Status: string(rechargeRecord.Status), OperatorID: "system", // 临时使用,实际应该从认证上下文获取 Notes: rechargeRecord.Notes, CreatedAt: rechargeRecord.CreatedAt, UpdatedAt: rechargeRecord.UpdatedAt, }, nil } // GetUserWalletTransactions 获取用户钱包交易记录 func (s *FinanceApplicationServiceImpl) GetUserWalletTransactions(ctx context.Context, userID string, filters map[string]interface{}, options interfaces.ListOptions) (*responses.WalletTransactionListResponse, error) { // 查询钱包交易记录(包含产品名称) productNameMap, transactions, total, err := s.walletTransactionRepository.ListByUserIdWithFiltersAndProductName(ctx, userID, filters, options) if err != nil { s.logger.Error("查询钱包交易记录失败", zap.Error(err), zap.String("userID", userID)) return nil, err } // 转换为响应DTO var items []responses.WalletTransactionResponse for _, transaction := range transactions { item := responses.WalletTransactionResponse{ ID: transaction.ID, UserID: transaction.UserID, ApiCallID: transaction.ApiCallID, TransactionID: transaction.TransactionID, ProductID: transaction.ProductID, ProductName: productNameMap[transaction.ProductID], // 从映射中获取产品名称 Amount: transaction.Amount, CreatedAt: transaction.CreatedAt, UpdatedAt: transaction.UpdatedAt, } items = append(items, item) } return &responses.WalletTransactionListResponse{ Items: items, Total: total, Page: options.Page, Size: options.PageSize, }, nil } // GetAdminWalletTransactions 获取管理端钱包交易记录 func (s *FinanceApplicationServiceImpl) GetAdminWalletTransactions(ctx context.Context, filters map[string]interface{}, options interfaces.ListOptions) (*responses.WalletTransactionListResponse, error) { // 查询钱包交易记录(包含产品名称) productNameMap, transactions, total, err := s.walletTransactionRepository.ListWithFiltersAndProductName(ctx, filters, options) if err != nil { s.logger.Error("查询管理端钱包交易记录失败", zap.Error(err)) return nil, err } // 转换为响应DTO var items []responses.WalletTransactionResponse for _, transaction := range transactions { item := responses.WalletTransactionResponse{ ID: transaction.ID, UserID: transaction.UserID, ApiCallID: transaction.ApiCallID, TransactionID: transaction.TransactionID, ProductID: transaction.ProductID, ProductName: productNameMap[transaction.ProductID], // 从映射中获取产品名称 Amount: transaction.Amount, CreatedAt: transaction.CreatedAt, UpdatedAt: transaction.UpdatedAt, } // 获取用户信息和企业名称 user, err := s.userRepo.GetByIDWithEnterpriseInfo(ctx, transaction.UserID) if err == nil { companyName := "未知企业" if user.EnterpriseInfo != nil { companyName = user.EnterpriseInfo.CompanyName } item.CompanyName = companyName item.User = &responses.UserSimpleResponse{ ID: user.ID, CompanyName: companyName, Phone: user.Phone, } } items = append(items, item) } return &responses.WalletTransactionListResponse{ Items: items, Total: total, Page: options.Page, Size: options.PageSize, }, nil } // ExportAdminWalletTransactions 导出管理端钱包交易记录 func (s *FinanceApplicationServiceImpl) ExportAdminWalletTransactions(ctx context.Context, filters map[string]interface{}, format string) ([]byte, error) { const batchSize = 1000 // 每批处理1000条记录 var allTransactions []*finance_entities.WalletTransaction var productNameMap map[string]string // 分批获取数据 page := 1 for { // 查询当前批次的数据 batchProductNameMap, transactions, _, err := s.walletTransactionRepository.ListWithFiltersAndProductName(ctx, filters, interfaces.ListOptions{ Page: page, PageSize: batchSize, Sort: "created_at", Order: "desc", }) if err != nil { s.logger.Error("查询导出钱包交易记录失败", zap.Error(err)) return nil, err } // 合并产品名称映射 if productNameMap == nil { productNameMap = batchProductNameMap } else { for k, v := range batchProductNameMap { productNameMap[k] = v } } // 添加到总数据中 allTransactions = append(allTransactions, transactions...) // 如果当前批次数据少于批次大小,说明已经是最后一批 if len(transactions) < batchSize { break } page++ } // 检查是否有数据 if len(allTransactions) == 0 { return nil, fmt.Errorf("没有找到符合条件的数据") } // 批量获取企业名称映射,避免N+1查询问题 companyNameMap, err := s.batchGetCompanyNames(ctx, allTransactions) if err != nil { companyNameMap = make(map[string]string) } // 准备导出数据 headers := []string{"交易ID", "企业名称", "产品名称", "消费金额", "消费时间"} columnWidths := []float64{20, 25, 20, 15, 20} data := make([][]interface{}, len(allTransactions)) for i, transaction := range allTransactions { companyName := companyNameMap[transaction.UserID] if companyName == "" { companyName = "未知企业" } productName := productNameMap[transaction.ProductID] if productName == "" { productName = "未知产品" } data[i] = []interface{}{ transaction.TransactionID, companyName, productName, transaction.Amount.String(), transaction.CreatedAt.Format("2006-01-02 15:04:05"), } } // 创建导出配置 config := &export.ExportConfig{ SheetName: "消费记录", Headers: headers, Data: data, ColumnWidths: columnWidths, } // 使用导出管理器生成文件 return s.exportManager.Export(ctx, config, format) } // batchGetCompanyNames 批量获取企业名称映射 func (s *FinanceApplicationServiceImpl) batchGetCompanyNames(ctx context.Context, transactions []*finance_entities.WalletTransaction) (map[string]string, error) { // 收集所有唯一的用户ID userIDSet := make(map[string]bool) for _, transaction := range transactions { userIDSet[transaction.UserID] = true } // 转换为切片 userIDs := make([]string, 0, len(userIDSet)) for userID := range userIDSet { userIDs = append(userIDs, userID) } // 批量查询用户信息 users, err := s.userRepo.BatchGetByIDsWithEnterpriseInfo(ctx, userIDs) if err != nil { return nil, err } // 构建企业名称映射 companyNameMap := make(map[string]string) for _, user := range users { companyName := "未知企业" if user.EnterpriseInfo != nil { companyName = user.EnterpriseInfo.CompanyName } companyNameMap[user.ID] = companyName } return companyNameMap, nil } // ExportAdminRechargeRecords 导出管理端充值记录 func (s *FinanceApplicationServiceImpl) ExportAdminRechargeRecords(ctx context.Context, filters map[string]interface{}, format string) ([]byte, error) { const batchSize = 1000 // 每批处理1000条记录 var allRecords []finance_entities.RechargeRecord // 分批获取数据 page := 1 for { // 查询当前批次的数据 records, err := s.rechargeRecordService.GetAll(ctx, filters, interfaces.ListOptions{ Page: page, PageSize: batchSize, Sort: "created_at", Order: "desc", }) if err != nil { s.logger.Error("查询导出充值记录失败", zap.Error(err)) return nil, err } // 添加到总数据中 allRecords = append(allRecords, records...) // 如果当前批次数据少于批次大小,说明已经是最后一批 if len(records) < batchSize { break } page++ } // 批量获取企业名称映射,避免N+1查询问题 companyNameMap, err := s.batchGetCompanyNamesForRechargeRecords(ctx, convertToRechargeRecordPointers(allRecords)) if err != nil { s.logger.Warn("批量获取企业名称失败,使用默认值", zap.Error(err)) companyNameMap = make(map[string]string) } // 准备导出数据 headers := []string{"企业名称", "充值金额", "充值类型", "状态", "支付宝订单号", "微信订单号", "转账订单号", "备注", "充值时间"} columnWidths := []float64{25, 15, 15, 10, 20, 20, 20, 20, 20} data := make([][]interface{}, len(allRecords)) for i, record := range allRecords { // 从映射中获取企业名称 companyName := companyNameMap[record.UserID] if companyName == "" { companyName = "未知企业" } // 获取订单号 alipayOrderID := "" if record.AlipayOrderID != nil && *record.AlipayOrderID != "" { alipayOrderID = *record.AlipayOrderID } wechatOrderID := "" if record.WechatOrderID != nil && *record.WechatOrderID != "" { wechatOrderID = *record.WechatOrderID } transferOrderID := "" if record.TransferOrderID != nil && *record.TransferOrderID != "" { transferOrderID = *record.TransferOrderID } // 获取备注 notes := "" if record.Notes != "" { notes = record.Notes } // 格式化时间 createdAt := record.CreatedAt.Format("2006-01-02 15:04:05") data[i] = []interface{}{ companyName, record.Amount.String(), translateRechargeType(record.RechargeType), translateRechargeStatus(record.Status), alipayOrderID, wechatOrderID, transferOrderID, notes, createdAt, } } // 创建导出配置 config := &export.ExportConfig{ SheetName: "充值记录", Headers: headers, Data: data, ColumnWidths: columnWidths, } // 使用导出管理器生成文件 return s.exportManager.Export(ctx, config, format) } // translateRechargeType 翻译充值类型为中文 func translateRechargeType(rechargeType finance_entities.RechargeType) string { switch rechargeType { case finance_entities.RechargeTypeAlipay: return "支付宝充值" case finance_entities.RechargeTypeWechat: return "微信充值" case finance_entities.RechargeTypeTransfer: return "对公转账" case finance_entities.RechargeTypeGift: return "赠送" default: return "未知类型" } } // translateRechargeStatus 翻译充值状态为中文 func translateRechargeStatus(status finance_entities.RechargeStatus) string { switch status { case finance_entities.RechargeStatusPending: return "待处理" case finance_entities.RechargeStatusSuccess: return "成功" case finance_entities.RechargeStatusFailed: return "失败" case finance_entities.RechargeStatusCancelled: return "已取消" default: return "未知状态" } } // convertToRechargeRecordPointers 将RechargeRecord切片转换为指针切片 func convertToRechargeRecordPointers(records []finance_entities.RechargeRecord) []*finance_entities.RechargeRecord { pointers := make([]*finance_entities.RechargeRecord, len(records)) for i := range records { pointers[i] = &records[i] } return pointers } // batchGetCompanyNamesForRechargeRecords 批量获取企业名称映射(用于充值记录) func (s *FinanceApplicationServiceImpl) batchGetCompanyNamesForRechargeRecords(ctx context.Context, records []*finance_entities.RechargeRecord) (map[string]string, error) { // 收集所有唯一的用户ID userIDSet := make(map[string]bool) for _, record := range records { userIDSet[record.UserID] = true } // 转换为切片 userIDs := make([]string, 0, len(userIDSet)) for userID := range userIDSet { userIDs = append(userIDs, userID) } // 批量查询用户信息 users, err := s.userRepo.BatchGetByIDsWithEnterpriseInfo(ctx, userIDs) if err != nil { return nil, err } // 构建企业名称映射 companyNameMap := make(map[string]string) for _, user := range users { companyName := "未知企业" if user.EnterpriseInfo != nil { companyName = user.EnterpriseInfo.CompanyName } companyNameMap[user.ID] = companyName } return companyNameMap, nil } // HandleAlipayCallback 处理支付宝回调 func (s *FinanceApplicationServiceImpl) HandleAlipayCallback(ctx context.Context, r *http.Request) error { // 解析并验证支付宝回调通知 notification, err := s.aliPayClient.HandleAliPaymentNotification(r) if err != nil { s.logger.Error("支付宝回调验证失败", zap.Error(err)) return err } // 记录回调数据 s.logger.Info("支付宝回调数据", zap.String("out_trade_no", notification.OutTradeNo), zap.String("trade_no", notification.TradeNo), zap.String("trade_status", string(notification.TradeStatus)), zap.String("total_amount", notification.TotalAmount), zap.String("buyer_id", notification.BuyerId), zap.String("seller_id", notification.SellerId), ) // 检查交易状态 if !s.aliPayClient.IsAlipayPaymentSuccess(notification) { s.logger.Warn("支付宝交易未成功", zap.String("out_trade_no", notification.OutTradeNo), zap.String("trade_status", string(notification.TradeStatus)), ) return nil // 不返回错误,因为这是正常的业务状态 } // 使用公共方法处理支付成功逻辑 err = s.processAlipayPaymentSuccess(ctx, notification.OutTradeNo, notification.TradeNo, notification.TotalAmount, notification.BuyerId, notification.SellerId) if err != nil { s.logger.Error("处理支付宝支付成功失败", zap.String("out_trade_no", notification.OutTradeNo), zap.Error(err), ) return err } return nil } // processAlipayPaymentSuccess 处理支付宝支付成功的公共逻辑 func (s *FinanceApplicationServiceImpl) processAlipayPaymentSuccess(ctx context.Context, outTradeNo, tradeNo, totalAmount, buyerID, sellerID string) error { // 解析金额 amount, err := decimal.NewFromString(totalAmount) if err != nil { s.logger.Error("解析支付宝金额失败", zap.String("total_amount", totalAmount), zap.Error(err), ) return err } // 直接调用充值记录服务处理支付成功逻辑 // 该服务内部会处理所有必要的检查、事务和更新操作 err = s.rechargeRecordService.HandleAlipayPaymentSuccess(ctx, outTradeNo, amount, tradeNo) if err != nil { s.logger.Error("处理支付宝支付成功失败", zap.String("out_trade_no", outTradeNo), zap.Error(err), ) return err } s.logger.Info("支付宝支付成功处理完成", zap.String("out_trade_no", outTradeNo), zap.String("trade_no", tradeNo), zap.String("amount", amount.String()), ) return nil } // updateAlipayOrderStatus 根据支付宝状态更新本地订单状态 func (s *FinanceApplicationServiceImpl) updateAlipayOrderStatus(ctx context.Context, outTradeNo string, alipayStatus alipay.TradeStatus, tradeNo, totalAmount string) error { // 查找支付宝订单 alipayOrder, err := s.alipayOrderRepo.GetByOutTradeNo(ctx, outTradeNo) if err != nil { s.logger.Error("查找支付宝订单失败", zap.String("out_trade_no", outTradeNo), zap.Error(err)) return fmt.Errorf("查找支付宝订单失败: %w", err) } if alipayOrder == nil { s.logger.Error("支付宝订单不存在", zap.String("out_trade_no", outTradeNo)) return fmt.Errorf("支付宝订单不存在") } switch alipayStatus { case alipay.TradeStatusSuccess: // 支付成功,调用公共处理逻辑 return s.processAlipayPaymentSuccess(ctx, outTradeNo, tradeNo, totalAmount, "", "") case alipay.TradeStatusClosed: // 交易关闭 s.logger.Info("支付宝订单已关闭", zap.String("out_trade_no", outTradeNo)) alipayOrder.MarkClosed() err = s.alipayOrderRepo.Update(ctx, *alipayOrder) if err != nil { s.logger.Error("更新支付宝订单状态失败", zap.String("out_trade_no", outTradeNo), zap.Error(err)) return err } case alipay.TradeStatusWaitBuyerPay: // 等待买家付款,保持pending状态 s.logger.Info("支付宝订单等待买家付款", zap.String("out_trade_no", outTradeNo)) default: // 其他状态,记录日志 s.logger.Info("支付宝订单其他状态", zap.String("out_trade_no", outTradeNo), zap.String("status", string(alipayStatus))) } return nil } // HandleAlipayReturn 处理支付宝同步回调 func (s *FinanceApplicationServiceImpl) HandleAlipayReturn(ctx context.Context, outTradeNo string) (string, error) { if outTradeNo == "" { return "", fmt.Errorf("缺少商户订单号") } // 查找支付宝订单 alipayOrder, err := s.alipayOrderRepo.GetByOutTradeNo(ctx, outTradeNo) if err != nil { s.logger.Error("查找支付宝订单失败", zap.String("out_trade_no", outTradeNo), zap.Error(err)) return "", fmt.Errorf("查找支付宝订单失败: %w", err) } if alipayOrder == nil { s.logger.Error("支付宝订单不存在", zap.String("out_trade_no", outTradeNo)) return "", fmt.Errorf("支付宝订单不存在") } // 记录同步回调查询 s.logger.Info("支付宝同步回调查询订单状态", zap.String("out_trade_no", outTradeNo), zap.String("order_status", string(alipayOrder.Status)), zap.String("trade_no", func() string { if alipayOrder.TradeNo != nil { return *alipayOrder.TradeNo } return "" }()), ) // 返回订单状态 switch alipayOrder.Status { case finance_entities.AlipayOrderStatusSuccess: return "TRADE_SUCCESS", nil case finance_entities.AlipayOrderStatusPending: // 对于pending状态,需要特殊处理 // 可能是用户支付了但支付宝异步回调还没到,或者用户还没支付 // 这里可以尝试主动查询支付宝订单状态,但为了简化处理,先返回WAIT_BUYER_PAY // 让前端显示"支付处理中"的状态,用户可以通过刷新页面或等待异步回调来更新状态 s.logger.Info("支付宝订单状态为pending,建议用户等待异步回调或刷新页面", zap.String("out_trade_no", outTradeNo), ) return "WAIT_BUYER_PAY", nil case finance_entities.AlipayOrderStatusFailed: return "TRADE_FAILED", nil case finance_entities.AlipayOrderStatusClosed: return "TRADE_CLOSED", nil default: return "UNKNOWN", nil } } // GetAlipayOrderStatus 获取支付宝订单状态 func (s *FinanceApplicationServiceImpl) GetAlipayOrderStatus(ctx context.Context, outTradeNo string) (*responses.AlipayOrderStatusResponse, error) { if outTradeNo == "" { return nil, fmt.Errorf("缺少商户订单号") } // 查找支付宝订单 alipayOrder, err := s.alipayOrderRepo.GetByOutTradeNo(ctx, outTradeNo) if err != nil { s.logger.Error("查找支付宝订单失败", zap.String("out_trade_no", outTradeNo), zap.Error(err)) return nil, fmt.Errorf("查找支付宝订单失败: %w", err) } if alipayOrder == nil { s.logger.Error("支付宝订单不存在", zap.String("out_trade_no", outTradeNo)) return nil, fmt.Errorf("支付宝订单不存在") } // 如果订单状态为pending,主动查询支付宝订单状态 if alipayOrder.Status == finance_entities.AlipayOrderStatusPending { s.logger.Info("订单状态为pending,主动查询支付宝订单状态", zap.String("out_trade_no", outTradeNo)) // 调用支付宝查询接口 alipayResp, err := s.aliPayClient.QueryOrderStatus(ctx, outTradeNo) if err != nil { s.logger.Error("查询支付宝订单状态失败", zap.String("out_trade_no", outTradeNo), zap.Error(err)) // 查询失败不影响返回,继续使用数据库中的状态 } else { // 解析支付宝返回的状态 alipayStatus := alipayResp.TradeStatus s.logger.Info("支付宝返回订单状态", zap.String("out_trade_no", outTradeNo), zap.String("alipay_status", string(alipayStatus)), zap.String("trade_no", alipayResp.TradeNo), ) // 使用公共方法更新订单状态 err = s.updateAlipayOrderStatus(ctx, outTradeNo, alipayStatus, alipayResp.TradeNo, alipayResp.TotalAmount) if err != nil { s.logger.Error("更新支付宝订单状态失败", zap.String("out_trade_no", outTradeNo), zap.Error(err)) } // 重新获取更新后的订单信息 updatedOrder, err := s.alipayOrderRepo.GetByOutTradeNo(ctx, outTradeNo) if err == nil && updatedOrder != nil { alipayOrder = updatedOrder } } } // 判断是否处理中 isProcessing := alipayOrder.Status == finance_entities.AlipayOrderStatusPending // 判断是否可以重试(失败状态可以重试) canRetry := alipayOrder.Status == finance_entities.AlipayOrderStatusFailed // 转换为响应DTO response := &responses.AlipayOrderStatusResponse{ OutTradeNo: alipayOrder.OutTradeNo, TradeNo: alipayOrder.TradeNo, Status: string(alipayOrder.Status), Amount: alipayOrder.Amount, Subject: alipayOrder.Subject, Platform: alipayOrder.Platform, CreatedAt: alipayOrder.CreatedAt, UpdatedAt: alipayOrder.UpdatedAt, NotifyTime: alipayOrder.NotifyTime, ReturnTime: alipayOrder.ReturnTime, ErrorCode: &alipayOrder.ErrorCode, ErrorMessage: &alipayOrder.ErrorMessage, IsProcessing: isProcessing, CanRetry: canRetry, } // 如果错误码为空,设置为nil if alipayOrder.ErrorCode == "" { response.ErrorCode = nil } if alipayOrder.ErrorMessage == "" { response.ErrorMessage = nil } s.logger.Info("查询支付宝订单状态完成", zap.String("out_trade_no", outTradeNo), zap.String("status", string(alipayOrder.Status)), zap.Bool("is_processing", isProcessing), zap.Bool("can_retry", canRetry), ) return response, nil } // GetUserRechargeRecords 获取用户充值记录 func (s *FinanceApplicationServiceImpl) GetUserRechargeRecords(ctx context.Context, userID string, filters map[string]interface{}, options interfaces.ListOptions) (*responses.RechargeRecordListResponse, error) { // 确保 filters 不为 nil if filters == nil { filters = make(map[string]interface{}) } // 添加 user_id 筛选条件,确保只能查询当前用户的记录 filters["user_id"] = userID // 查询用户充值记录(使用筛选和分页功能) records, err := s.rechargeRecordService.GetAll(ctx, filters, options) if err != nil { s.logger.Error("查询用户充值记录失败", zap.Error(err), zap.String("userID", userID)) return nil, err } // 获取总数(使用筛选条件) total, err := s.rechargeRecordService.Count(ctx, filters) if err != nil { s.logger.Error("统计用户充值记录失败", zap.Error(err), zap.String("userID", userID)) return nil, err } // 转换为响应DTO var items []responses.RechargeRecordResponse for _, record := range records { item := responses.RechargeRecordResponse{ ID: record.ID, UserID: record.UserID, Amount: record.Amount, RechargeType: string(record.RechargeType), Status: string(record.Status), Notes: record.Notes, CreatedAt: record.CreatedAt, UpdatedAt: record.UpdatedAt, } // 根据充值类型设置相应的订单号和平台信息 if record.AlipayOrderID != nil { item.AlipayOrderID = *record.AlipayOrderID // 通过订单号获取平台信息 if alipayOrder, err := s.alipayOrderRepo.GetByOutTradeNo(ctx, *record.AlipayOrderID); err == nil && alipayOrder != nil { item.Platform = alipayOrder.Platform } } if record.WechatOrderID != nil { item.WechatOrderID = *record.WechatOrderID // 通过订单号获取平台信息 if wechatOrder, err := s.wechatOrderRepo.GetByOutTradeNo(ctx, *record.WechatOrderID); err == nil && wechatOrder != nil { item.Platform = wechatOrder.Platform } } if record.TransferOrderID != nil { item.TransferOrderID = *record.TransferOrderID } items = append(items, item) } return &responses.RechargeRecordListResponse{ Items: items, Total: total, Page: options.Page, Size: options.PageSize, }, nil } // GetAdminRechargeRecords 获取管理端充值记录 func (s *FinanceApplicationServiceImpl) GetAdminRechargeRecords(ctx context.Context, filters map[string]interface{}, options interfaces.ListOptions) (*responses.RechargeRecordListResponse, error) { // 查询充值记录 records, err := s.rechargeRecordService.GetAll(ctx, filters, options) if err != nil { s.logger.Error("查询管理端充值记录失败", zap.Error(err)) return nil, err } // 获取总数 total, err := s.rechargeRecordService.Count(ctx, filters) if err != nil { s.logger.Error("统计管理端充值记录失败", zap.Error(err)) return nil, err } // 转换为响应DTO var items []responses.RechargeRecordResponse for _, record := range records { item := responses.RechargeRecordResponse{ ID: record.ID, UserID: record.UserID, Amount: record.Amount, RechargeType: string(record.RechargeType), Status: string(record.Status), Notes: record.Notes, CreatedAt: record.CreatedAt, UpdatedAt: record.UpdatedAt, } // 根据充值类型设置相应的订单号和平台信息 if record.AlipayOrderID != nil { item.AlipayOrderID = *record.AlipayOrderID // 通过订单号获取平台信息 if alipayOrder, err := s.alipayOrderRepo.GetByOutTradeNo(ctx, *record.AlipayOrderID); err == nil && alipayOrder != nil { item.Platform = alipayOrder.Platform } } if record.WechatOrderID != nil { item.WechatOrderID = *record.WechatOrderID // 通过订单号获取平台信息 if wechatOrder, err := s.wechatOrderRepo.GetByOutTradeNo(ctx, *record.WechatOrderID); err == nil && wechatOrder != nil { item.Platform = wechatOrder.Platform } } if record.TransferOrderID != nil { item.TransferOrderID = *record.TransferOrderID } // 获取用户信息和企业名称 user, err := s.userRepo.GetByIDWithEnterpriseInfo(ctx, record.UserID) if err == nil { companyName := "未知企业" if user.EnterpriseInfo != nil { companyName = user.EnterpriseInfo.CompanyName } item.CompanyName = companyName item.User = &responses.UserSimpleResponse{ ID: user.ID, CompanyName: companyName, Phone: user.Phone, } } items = append(items, item) } return &responses.RechargeRecordListResponse{ Items: items, Total: total, Page: options.Page, Size: options.PageSize, }, nil } // GetRechargeConfig 获取充值配置 func (s *FinanceApplicationServiceImpl) GetRechargeConfig(ctx context.Context) (*responses.RechargeConfigResponse, error) { bonus := make([]responses.AlipayRechargeBonusRuleResponse, 0, len(s.config.Wallet.AliPayRechargeBonus)) for _, rule := range s.config.Wallet.AliPayRechargeBonus { bonus = append(bonus, responses.AlipayRechargeBonusRuleResponse{ RechargeAmount: rule.RechargeAmount, BonusAmount: rule.BonusAmount, }) } return &responses.RechargeConfigResponse{ MinAmount: s.config.Wallet.MinAmount, MaxAmount: s.config.Wallet.MaxAmount, AlipayRechargeBonus: bonus, }, nil } // GetWechatOrderStatus 获取微信订单状态 func (s *FinanceApplicationServiceImpl) GetWechatOrderStatus(ctx context.Context, outTradeNo string) (*responses.WechatOrderStatusResponse, error) { if outTradeNo == "" { return nil, fmt.Errorf("缺少商户订单号") } // 查找微信订单 wechatOrder, err := s.wechatOrderRepo.GetByOutTradeNo(ctx, outTradeNo) if err != nil { s.logger.Error("查找微信订单失败", zap.String("out_trade_no", outTradeNo), zap.Error(err)) return nil, fmt.Errorf("查找微信订单失败: %w", err) } if wechatOrder == nil { s.logger.Error("微信订单不存在", zap.String("out_trade_no", outTradeNo)) return nil, fmt.Errorf("微信订单不存在") } // 如果订单状态为pending,主动查询微信订单状态 if wechatOrder.Status == finance_entities.WechatOrderStatusPending { s.logger.Info("订单状态为pending,主动查询微信订单状态", zap.String("out_trade_no", outTradeNo), ) // 调用微信查询接口 transaction, err := s.wechatPayService.QueryOrderStatus(ctx, outTradeNo) if err != nil { s.logger.Error("查询微信订单状态失败", zap.String("out_trade_no", outTradeNo), zap.Error(err), ) // 查询失败不影响返回,继续使用数据库中的状态 } else { // 解析微信返回的状态 tradeState := "" transactionID := "" if transaction.TradeState != nil { tradeState = *transaction.TradeState } if transaction.TransactionId != nil { transactionID = *transaction.TransactionId } s.logger.Info("微信查询订单状态返回", zap.String("out_trade_no", outTradeNo), zap.String("trade_state", tradeState), zap.String("transaction_id", transactionID), ) // 使用公共方法更新订单状态 err = s.updateWechatOrderStatus(ctx, outTradeNo, tradeState, transaction) if err != nil { s.logger.Error("更新微信订单状态失败", zap.String("out_trade_no", outTradeNo), zap.String("trade_state", tradeState), zap.Error(err), ) } // 重新获取更新后的订单信息 updatedOrder, err := s.wechatOrderRepo.GetByOutTradeNo(ctx, outTradeNo) if err == nil && updatedOrder != nil { wechatOrder = updatedOrder } } } // 判断是否处理中 isProcessing := wechatOrder.Status == finance_entities.WechatOrderStatusPending // 判断是否可以重试(失败状态可以重试) canRetry := wechatOrder.Status == finance_entities.WechatOrderStatusFailed // 转换为响应DTO response := &responses.WechatOrderStatusResponse{ OutTradeNo: wechatOrder.OutTradeNo, TransactionID: wechatOrder.TradeNo, Status: string(wechatOrder.Status), Amount: wechatOrder.Amount, Subject: wechatOrder.Subject, Platform: wechatOrder.Platform, CreatedAt: wechatOrder.CreatedAt, UpdatedAt: wechatOrder.UpdatedAt, NotifyTime: wechatOrder.NotifyTime, ReturnTime: wechatOrder.ReturnTime, ErrorCode: &wechatOrder.ErrorCode, ErrorMessage: &wechatOrder.ErrorMessage, IsProcessing: isProcessing, CanRetry: canRetry, } // 如果错误码为空,设置为nil if wechatOrder.ErrorCode == "" { response.ErrorCode = nil } if wechatOrder.ErrorMessage == "" { response.ErrorMessage = nil } s.logger.Info("查询微信订单状态完成", zap.String("out_trade_no", outTradeNo), zap.String("status", string(wechatOrder.Status)), zap.Bool("is_processing", isProcessing), zap.Bool("can_retry", canRetry), ) return response, nil } // updateWechatOrderStatus 根据微信状态更新本地订单状态 func (s *FinanceApplicationServiceImpl) updateWechatOrderStatus(ctx context.Context, outTradeNo string, tradeState string, transaction *payments.Transaction) error { // 查找微信订单 wechatOrder, err := s.wechatOrderRepo.GetByOutTradeNo(ctx, outTradeNo) if err != nil { s.logger.Error("查找微信订单失败", zap.String("out_trade_no", outTradeNo), zap.Error(err)) return fmt.Errorf("查找微信订单失败: %w", err) } if wechatOrder == nil { s.logger.Error("微信订单不存在", zap.String("out_trade_no", outTradeNo)) return fmt.Errorf("微信订单不存在") } switch tradeState { case payment.TradeStateSuccess: // 支付成功,调用公共处理逻辑 transactionID := "" if transaction.TransactionId != nil { transactionID = *transaction.TransactionId } payAmount := decimal.Zero if transaction.Amount != nil && transaction.Amount.Total != nil { // 将分转换为元 payAmount = decimal.NewFromInt(*transaction.Amount.Total).Div(decimal.NewFromInt(100)) } return s.processWechatPaymentSuccess(ctx, outTradeNo, transactionID, payAmount) case payment.TradeStateClosed: // 交易关闭 s.logger.Info("微信订单交易关闭", zap.String("out_trade_no", outTradeNo), ) wechatOrder.MarkClosed() err = s.wechatOrderRepo.Update(ctx, *wechatOrder) if err != nil { s.logger.Error("更新微信订单关闭状态失败", zap.String("out_trade_no", outTradeNo), zap.Error(err), ) return err } s.logger.Info("微信订单关闭状态更新成功", zap.String("out_trade_no", outTradeNo), ) case payment.TradeStateNotPay: // 未支付,保持pending状态 s.logger.Info("微信订单未支付", zap.String("out_trade_no", outTradeNo), ) default: // 其他状态,记录日志 s.logger.Info("微信订单其他状态", zap.String("out_trade_no", outTradeNo), zap.String("trade_state", tradeState), ) } return nil } // HandleWechatPayCallback 处理微信支付回调 func (s *FinanceApplicationServiceImpl) HandleWechatPayCallback(ctx context.Context, r *http.Request) error { if s.wechatPayService == nil { s.logger.Error("微信支付服务未初始化") return fmt.Errorf("微信支付服务未初始化") } // 解析并验证微信支付回调通知 transaction, err := s.wechatPayService.HandleWechatPayNotification(ctx, r) if err != nil { s.logger.Error("微信支付回调验证失败", zap.Error(err)) return err } // 提取回调数据 outTradeNo := "" if transaction.OutTradeNo != nil { outTradeNo = *transaction.OutTradeNo } transactionID := "" if transaction.TransactionId != nil { transactionID = *transaction.TransactionId } tradeState := "" if transaction.TradeState != nil { tradeState = *transaction.TradeState } totalAmount := decimal.Zero if transaction.Amount != nil && transaction.Amount.Total != nil { // 将分转换为元 totalAmount = decimal.NewFromInt(*transaction.Amount.Total).Div(decimal.NewFromInt(100)) } // 记录回调数据 s.logger.Info("微信支付回调数据", zap.String("out_trade_no", outTradeNo), zap.String("transaction_id", transactionID), zap.String("trade_state", tradeState), zap.String("total_amount", totalAmount.String()), ) // 检查交易状态 if tradeState != payment.TradeStateSuccess { s.logger.Warn("微信支付交易未成功", zap.String("out_trade_no", outTradeNo), zap.String("trade_state", tradeState), ) return nil // 不返回错误,因为这是正常的业务状态 } // 处理支付成功逻辑 err = s.processWechatPaymentSuccess(ctx, outTradeNo, transactionID, totalAmount) if err != nil { s.logger.Error("处理微信支付成功失败", zap.String("out_trade_no", outTradeNo), zap.String("transaction_id", transactionID), zap.String("amount", totalAmount.String()), zap.Error(err), ) return err } return nil } // processWechatPaymentSuccess 处理微信支付成功的公共逻辑 func (s *FinanceApplicationServiceImpl) processWechatPaymentSuccess(ctx context.Context, outTradeNo, transactionID string, amount decimal.Decimal) error { // 查找微信订单 wechatOrder, err := s.wechatOrderRepo.GetByOutTradeNo(ctx, outTradeNo) if err != nil { s.logger.Error("查找微信订单失败", zap.String("out_trade_no", outTradeNo), zap.Error(err), ) return fmt.Errorf("查找微信订单失败: %w", err) } if wechatOrder == nil { s.logger.Error("微信订单不存在", zap.String("out_trade_no", outTradeNo), ) return fmt.Errorf("微信订单不存在") } // 查找对应的充值记录 rechargeRecord, err := s.rechargeRecordService.GetByID(ctx, wechatOrder.RechargeID) if err != nil { s.logger.Error("查找充值记录失败", zap.String("out_trade_no", outTradeNo), zap.String("recharge_id", wechatOrder.RechargeID), zap.Error(err), ) return fmt.Errorf("查找充值记录失败: %w", err) } // 检查订单和充值记录状态,如果都已成功则跳过(只记录一次日志) if wechatOrder.Status == finance_entities.WechatOrderStatusSuccess && rechargeRecord.Status == finance_entities.RechargeStatusSuccess { s.logger.Info("微信支付订单已处理成功,跳过重复处理", zap.String("out_trade_no", outTradeNo), zap.String("transaction_id", transactionID), zap.String("order_id", wechatOrder.ID), zap.String("recharge_id", rechargeRecord.ID), ) return nil } // 计算充值赠送金额(复用支付宝的赠送逻辑) bonusAmount := decimal.Zero if len(s.config.Wallet.AliPayRechargeBonus) > 0 { for i := len(s.config.Wallet.AliPayRechargeBonus) - 1; i >= 0; i-- { rule := s.config.Wallet.AliPayRechargeBonus[i] if amount.GreaterThanOrEqual(decimal.NewFromFloat(rule.RechargeAmount)) { bonusAmount = decimal.NewFromFloat(rule.BonusAmount) break } } } // 记录开始处理支付成功 s.logger.Info("开始处理微信支付成功", zap.String("out_trade_no", outTradeNo), zap.String("transaction_id", transactionID), zap.String("amount", amount.String()), zap.String("user_id", rechargeRecord.UserID), zap.String("bonus_amount", bonusAmount.String()), ) // 在事务中处理支付成功逻辑 err = s.txManager.ExecuteInTx(ctx, func(txCtx context.Context) error { // 更新微信订单状态 wechatOrder.MarkSuccess(transactionID, "", "", amount, amount) now := time.Now() wechatOrder.NotifyTime = &now err := s.wechatOrderRepo.Update(txCtx, *wechatOrder) if err != nil { s.logger.Error("更新微信订单状态失败", zap.String("out_trade_no", outTradeNo), zap.Error(err), ) return err } // 更新充值记录状态为成功 rechargeRecord.MarkSuccess() err = s.rechargeRecordRepo.Update(txCtx, *rechargeRecord) if err != nil { s.logger.Error("更新充值记录状态失败", zap.String("out_trade_no", outTradeNo), zap.String("recharge_id", rechargeRecord.ID), zap.Error(err), ) return err } // 如果有赠送金额,创建赠送充值记录 if bonusAmount.GreaterThan(decimal.Zero) { giftRechargeRecord := finance_entities.NewGiftRechargeRecord(rechargeRecord.UserID, bonusAmount, "充值活动赠送") createdGift, err := s.rechargeRecordRepo.Create(txCtx, *giftRechargeRecord) if err != nil { s.logger.Error("创建赠送充值记录失败", zap.String("out_trade_no", outTradeNo), zap.String("user_id", rechargeRecord.UserID), zap.String("bonus_amount", bonusAmount.String()), zap.Error(err), ) return err } s.logger.Info("创建赠送充值记录成功", zap.String("out_trade_no", outTradeNo), zap.String("gift_recharge_id", createdGift.ID), zap.String("bonus_amount", bonusAmount.String()), ) } // 充值到钱包(包含赠送金额) totalRechargeAmount := amount.Add(bonusAmount) err = s.walletService.Recharge(txCtx, rechargeRecord.UserID, totalRechargeAmount) if err != nil { s.logger.Error("充值到钱包失败", zap.String("out_trade_no", outTradeNo), zap.String("user_id", rechargeRecord.UserID), zap.String("total_amount", totalRechargeAmount.String()), zap.Error(err), ) return err } return nil }) if err != nil { s.logger.Error("处理微信支付成功失败", zap.String("out_trade_no", outTradeNo), zap.String("transaction_id", transactionID), zap.String("amount", amount.String()), zap.Error(err), ) return err } s.logger.Info("微信支付成功处理完成", zap.String("out_trade_no", outTradeNo), zap.String("transaction_id", transactionID), zap.String("amount", amount.String()), zap.String("bonus_amount", bonusAmount.String()), zap.String("user_id", rechargeRecord.UserID), ) return nil } // HandleWechatRefundCallback 处理微信退款回调 func (s *FinanceApplicationServiceImpl) HandleWechatRefundCallback(ctx context.Context, r *http.Request) error { if s.wechatPayService == nil { s.logger.Error("微信支付服务未初始化") return fmt.Errorf("微信支付服务未初始化") } // 解析并验证微信退款回调通知 refund, err := s.wechatPayService.HandleRefundNotification(ctx, r) if err != nil { s.logger.Error("微信退款回调验证失败", zap.Error(err)) return err } // 记录回调数据 s.logger.Info("微信退款回调数据", zap.String("out_trade_no", func() string { if refund.OutTradeNo != nil { return *refund.OutTradeNo } return "" }()), zap.String("out_refund_no", func() string { if refund.OutRefundNo != nil { return *refund.OutRefundNo } return "" }()), zap.String("refund_id", func() string { if refund.RefundId != nil { return *refund.RefundId } return "" }()), zap.Any("status", func() interface{} { if refund.Status != nil { return *refund.Status } return nil }()), ) // 处理退款逻辑 // 这里可以根据实际业务需求实现退款处理逻辑 s.logger.Info("微信退款回调处理完成", zap.String("out_trade_no", func() string { if refund.OutTradeNo != nil { return *refund.OutTradeNo } return "" }()), zap.String("refund_id", func() string { if refund.RefundId != nil { return *refund.RefundId } return "" }()), ) return nil }