From f17e22f4c86655bd353be5b023254c9eda14b381 Mon Sep 17 00:00:00 2001 From: liangzai <2440983361@qq.com> Date: Wed, 25 Feb 2026 19:33:56 +0800 Subject: [PATCH] f --- config.yaml | 2 +- .../certification_application_service_impl.go | 42 ++++- .../finance_application_service_impl.go | 89 +++++++++++ .../finance/invoice_application_service.go | 44 +++++- internal/container/container.go | 2 + .../finance/services/balance_alert_service.go | 54 ++++++- .../notification/wechat_work_service.go | 22 +-- .../notification/wechat_work_service_test.go | 148 ++++++++++++++++++ 8 files changed, 383 insertions(+), 20 deletions(-) create mode 100644 internal/infrastructure/external/notification/wechat_work_service_test.go diff --git a/config.yaml b/config.yaml index 1a5113d..0918410 100644 --- a/config.yaml +++ b/config.yaml @@ -237,7 +237,7 @@ development: # 企业微信配置 wechat_work: - webhook_url: "" + webhook_url: "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=649bf737-28ca-4f30-ad5f-cfb65b2af113" secret: "" # =========================================== diff --git a/internal/application/certification/certification_application_service_impl.go b/internal/application/certification/certification_application_service_impl.go index d209252..f38f456 100644 --- a/internal/application/certification/certification_application_service_impl.go +++ b/internal/application/certification/certification_application_service_impl.go @@ -18,7 +18,9 @@ import ( "tyapi-server/internal/domains/certification/services" finance_service "tyapi-server/internal/domains/finance/services" user_entities "tyapi-server/internal/domains/user/entities" - user_service "tyapi-server/internal/domains/user/services" + user_service "tyapi-server/internal/domains/user/services" + "tyapi-server/internal/config" + "tyapi-server/internal/infrastructure/external/notification" "tyapi-server/internal/infrastructure/external/storage" "tyapi-server/internal/shared/database" "tyapi-server/internal/shared/esign" @@ -47,7 +49,8 @@ type CertificationApplicationServiceImpl struct { enterpriseInfoSubmitRecordRepo repositories.EnterpriseInfoSubmitRecordRepository txManager *database.TransactionManager - logger *zap.Logger + wechatWorkService *notification.WeChatWorkService + logger *zap.Logger } // NewCertificationApplicationService 创建认证应用服务 @@ -67,7 +70,12 @@ func NewCertificationApplicationService( ocrService sharedOCR.OCRService, txManager *database.TransactionManager, logger *zap.Logger, + cfg *config.Config, ) CertificationApplicationService { + var wechatSvc *notification.WeChatWorkService + if cfg != nil && cfg.WechatWork.WebhookURL != "" { + wechatSvc = notification.NewWeChatWorkService(cfg.WechatWork.WebhookURL, cfg.WechatWork.Secret, logger) + } return &CertificationApplicationServiceImpl{ aggregateService: aggregateService, userAggregateService: userAggregateService, @@ -83,6 +91,7 @@ func NewCertificationApplicationService( enterpriseInfoSubmitRecordService: enterpriseInfoSubmitRecordService, ocrService: ocrService, txManager: txManager, + wechatWorkService: wechatSvc, logger: logger, } } @@ -1104,6 +1113,35 @@ func (s *CertificationApplicationServiceImpl) completeUserActivationWithoutContr return err } + // 企业认证成功企业微信通知(仅展示企业名称和联系手机) + if s.wechatWorkService != nil { + user, err := s.userAggregateService.GetUserWithEnterpriseInfo(ctx, cert.UserID) + if err == nil { + companyName := "未知企业" + phone := "" + if user.EnterpriseInfo != nil { + companyName = user.EnterpriseInfo.CompanyName + if user.EnterpriseInfo.LegalPersonPhone != "" { + phone = user.EnterpriseInfo.LegalPersonPhone + } + } + if user.Phone != "" && phone == "" { + phone = user.Phone + } + content := fmt.Sprintf( + "### 【天远API】企业认证成功\n"+ + "> 企业名称:%s\n"+ + "> 联系手机:%s\n"+ + "> 完成时间:%s\n"+ + "\n该企业已完成认证,请相关同事同步更新内部系统。", + companyName, + phone, + time.Now().Format("2006-01-02 15:04:05"), + ) + _ = s.wechatWorkService.SendMarkdownMessage(ctx, content) + } + } + return nil } diff --git a/internal/application/finance/finance_application_service_impl.go b/internal/application/finance/finance_application_service_impl.go index bb312ca..2b8eab2 100644 --- a/internal/application/finance/finance_application_service_impl.go +++ b/internal/application/finance/finance_application_service_impl.go @@ -14,6 +14,7 @@ import ( finance_services "tyapi-server/internal/domains/finance/services" product_repositories "tyapi-server/internal/domains/product/repositories" user_repositories "tyapi-server/internal/domains/user/repositories" + "tyapi-server/internal/infrastructure/external/notification" "tyapi-server/internal/shared/component_report" "tyapi-server/internal/shared/database" "tyapi-server/internal/shared/export" @@ -43,6 +44,7 @@ type FinanceApplicationServiceImpl struct { exportManager *export.ExportManager logger *zap.Logger config *config.Config + wechatWorkService *notification.WeChatWorkService } // NewFinanceApplicationService 创建财务应用服务 @@ -63,6 +65,11 @@ func NewFinanceApplicationService( config *config.Config, exportManager *export.ExportManager, ) FinanceApplicationService { + var wechatSvc *notification.WeChatWorkService + if config != nil && config.WechatWork.WebhookURL != "" { + wechatSvc = notification.NewWeChatWorkService(config.WechatWork.WebhookURL, config.WechatWork.Secret, logger) + } + return &FinanceApplicationServiceImpl{ aliPayClient: aliPayClient, wechatPayService: wechatPayService, @@ -79,9 +86,46 @@ func NewFinanceApplicationService( exportManager: exportManager, logger: logger, config: config, + wechatWorkService: wechatSvc, } } +// getUserContactInfo 获取企业名称和联系手机号(尽量用企业信息里的手机号,退化到用户登录手机号) +func (s *FinanceApplicationServiceImpl) getUserContactInfo(ctx context.Context, userID string) (companyName, phone string) { + companyName = "未知企业" + phone = "" + + if userID == "" { + return + } + + user, err := s.userRepo.GetByIDWithEnterpriseInfo(ctx, userID) + if err != nil { + s.logger.Warn("获取用户企业信息失败,使用默认企业名称", + zap.String("user_id", userID), + zap.Error(err), + ) + return + } + + // 登录手机号 + if user.Phone != "" { + phone = user.Phone + } + + // 企业名称和企业手机号 + if user.EnterpriseInfo != nil { + if user.EnterpriseInfo.CompanyName != "" { + companyName = user.EnterpriseInfo.CompanyName + } + if user.EnterpriseInfo.LegalPersonPhone != "" { + phone = user.EnterpriseInfo.LegalPersonPhone + } + } + + return +} + func (s *FinanceApplicationServiceImpl) CreateWallet(ctx context.Context, cmd *commands.CreateWalletCommand) (*responses.WalletResponse, error) { // 调用钱包聚合服务创建钱包 wallet, err := s.walletService.CreateWallet(ctx, cmd.UserID) @@ -936,6 +980,33 @@ func (s *FinanceApplicationServiceImpl) processAlipayPaymentSuccess(ctx context. zap.String("amount", amount.String()), ) + // 充值成功企业微信通知(仅充值订单,且忽略发送错误) + if s.wechatWorkService != nil { + // 再次获取充值记录,拿到用户ID + rechargeRecord, err := s.rechargeRecordRepo.GetByID(ctx, alipayOrder.RechargeID) + if err == nil { + companyName, phone := s.getUserContactInfo(ctx, rechargeRecord.UserID) + content := fmt.Sprintf( + "### 【天远API】用户充值成功通知\n"+ + "> 企业名称:%s\n"+ + "> 联系手机:%s\n"+ + "> 充值渠道:支付宝\n"+ + "> 充值金额:%s 元\n"+ + "> 时间:%s\n", + companyName, + phone, + amount.String(), + time.Now().Format("2006-01-02 15:04:05"), + ) + _ = s.wechatWorkService.SendMarkdownMessage(ctx, content) + } else { + s.logger.Warn("获取充值记录失败,跳过企业微信充值通知", + zap.String("out_trade_no", outTradeNo), + zap.Error(err), + ) + } + } + return nil } @@ -1681,6 +1752,24 @@ func (s *FinanceApplicationServiceImpl) processWechatPaymentSuccess(ctx context. zap.String("user_id", rechargeRecord.UserID), ) + // 微信充值成功企业微信通知(忽略发送错误) + if s.wechatWorkService != nil { + companyName, phone := s.getUserContactInfo(ctx, rechargeRecord.UserID) + content := fmt.Sprintf( + "### 【天远API】用户充值成功通知\n"+ + "> 企业名称:%s\n"+ + "> 联系手机:%s\n"+ + "> 充值渠道:微信\n"+ + "> 充值金额:%s 元\n"+ + "> 时间:%s\n", + companyName, + phone, + amount.String(), + time.Now().Format("2006-01-02 15:04:05"), + ) + _ = s.wechatWorkService.SendMarkdownMessage(ctx, content) + } + return nil } diff --git a/internal/application/finance/invoice_application_service.go b/internal/application/finance/invoice_application_service.go index 9647108..9c27e3d 100644 --- a/internal/application/finance/invoice_application_service.go +++ b/internal/application/finance/invoice_application_service.go @@ -7,12 +7,14 @@ import ( "time" "tyapi-server/internal/application/finance/dto" + "tyapi-server/internal/config" "tyapi-server/internal/domains/finance/entities" finance_repo "tyapi-server/internal/domains/finance/repositories" "tyapi-server/internal/domains/finance/services" "tyapi-server/internal/domains/finance/value_objects" user_repo "tyapi-server/internal/domains/user/repositories" user_service "tyapi-server/internal/domains/user/services" + "tyapi-server/internal/infrastructure/external/notification" "tyapi-server/internal/infrastructure/external/storage" "github.com/shopspring/decimal" @@ -59,8 +61,9 @@ type InvoiceApplicationServiceImpl struct { userAggregateService user_service.UserAggregateService // 外部服务依赖 - storageService *storage.QiNiuStorageService - logger *zap.Logger + storageService *storage.QiNiuStorageService + logger *zap.Logger + wechatWorkServer *notification.WeChatWorkService } // NewInvoiceApplicationService 创建发票应用服务 @@ -76,7 +79,13 @@ func NewInvoiceApplicationService( userInvoiceInfoService services.UserInvoiceInfoService, storageService *storage.QiNiuStorageService, logger *zap.Logger, + cfg *config.Config, ) InvoiceApplicationService { + var wechatSvc *notification.WeChatWorkService + if cfg != nil && cfg.WechatWork.WebhookURL != "" { + wechatSvc = notification.NewWeChatWorkService(cfg.WechatWork.WebhookURL, cfg.WechatWork.Secret, logger) + } + return &InvoiceApplicationServiceImpl{ invoiceRepo: invoiceRepo, userInvoiceInfoRepo: userInvoiceInfoRepo, @@ -89,6 +98,7 @@ func NewInvoiceApplicationService( userInvoiceInfoService: userInvoiceInfoService, storageService: storageService, logger: logger, + wechatWorkServer: wechatSvc, } } @@ -175,7 +185,7 @@ func (s *InvoiceApplicationServiceImpl) ApplyInvoice(ctx context.Context, userID } // 10. 构建响应DTO - return &dto.InvoiceApplicationResponse{ + resp := &dto.InvoiceApplicationResponse{ ID: application.ID, UserID: application.UserID, InvoiceType: application.InvoiceType, @@ -183,7 +193,33 @@ func (s *InvoiceApplicationServiceImpl) ApplyInvoice(ctx context.Context, userID Status: application.Status, InvoiceInfo: invoiceInfo, CreatedAt: application.CreatedAt, - }, nil + } + + // 11. 企业微信通知(忽略发送错误),只使用企业名称和联系电话 + if s.wechatWorkServer != nil { + companyName := userWithEnterprise.EnterpriseInfo.CompanyName + phone := user.Phone + if userWithEnterprise.EnterpriseInfo.LegalPersonPhone != "" { + phone = userWithEnterprise.EnterpriseInfo.LegalPersonPhone + } + + content := fmt.Sprintf( + "### 【天远API】用户申请开发票\n"+ + "> 企业名称:%s\n"+ + "> 联系手机:%s\n"+ + "> 申请开票金额:%s 元\n"+ + "> 发票类型:%s\n"+ + "> 申请时间:%s\n", + companyName, + phone, + application.Amount.String(), + string(application.InvoiceType), + time.Now().Format("2006-01-02 15:04:05"), + ) + _ = s.wechatWorkServer.SendMarkdownMessage(ctx, content) + } + + return resp, nil } // GetUserInvoiceInfo 获取用户发票信息 diff --git a/internal/container/container.go b/internal/container/container.go index b5b631e..2f16887 100644 --- a/internal/container/container.go +++ b/internal/container/container.go @@ -885,6 +885,7 @@ func NewContainer() *Container { ocrService sharedOCR.OCRService, txManager *shared_database.TransactionManager, logger *zap.Logger, + cfg *config.Config, ) certification.CertificationApplicationService { return certification.NewCertificationApplicationService( aggregateService, @@ -902,6 +903,7 @@ func NewContainer() *Container { ocrService, txManager, logger, + cfg, ) }, fx.As(new(certification.CertificationApplicationService)), diff --git a/internal/domains/finance/services/balance_alert_service.go b/internal/domains/finance/services/balance_alert_service.go index e96575a..e8e1e1f 100644 --- a/internal/domains/finance/services/balance_alert_service.go +++ b/internal/domains/finance/services/balance_alert_service.go @@ -3,6 +3,7 @@ package services import ( "context" "fmt" + "time" "github.com/shopspring/decimal" "go.uber.org/zap" @@ -11,6 +12,7 @@ import ( "tyapi-server/internal/domains/api/entities" api_repositories "tyapi-server/internal/domains/api/repositories" user_repositories "tyapi-server/internal/domains/user/repositories" + "tyapi-server/internal/infrastructure/external/notification" "tyapi-server/internal/infrastructure/external/sms" ) @@ -27,6 +29,7 @@ type BalanceAlertServiceImpl struct { smsService *sms.AliSMSService config *config.Config logger *zap.Logger + wechatWorkService *notification.WeChatWorkService } // NewBalanceAlertService 创建余额预警服务 @@ -38,6 +41,10 @@ func NewBalanceAlertService( config *config.Config, logger *zap.Logger, ) BalanceAlertService { + var wechatSvc *notification.WeChatWorkService + if config != nil && config.WechatWork.WebhookURL != "" { + wechatSvc = notification.NewWeChatWorkService(config.WechatWork.WebhookURL, config.WechatWork.Secret, logger) + } return &BalanceAlertServiceImpl{ apiUserRepo: apiUserRepo, userRepo: userRepo, @@ -45,6 +52,7 @@ func NewBalanceAlertService( smsService: smsService, config: config, logger: logger, + wechatWorkService: wechatSvc, } } @@ -154,7 +162,27 @@ func (s *BalanceAlertServiceImpl) sendArrearsAlert(ctx context.Context, apiUser zap.Float64("balance", balance), zap.String("enterprise_name", enterpriseName)) - return s.smsService.SendBalanceAlert(ctx, apiUser.AlertPhone, balance, 0, "arrears", enterpriseName) + if err := s.smsService.SendBalanceAlert(ctx, apiUser.AlertPhone, balance, 0, "arrears", enterpriseName); err != nil { + return err + } + + // 企业微信欠费告警通知(仅展示企业名称和联系手机) + if s.wechatWorkService != nil { + content := fmt.Sprintf( + "### 【天远API】用户余额欠费告警\n"+ + "该企业已发生欠费,请及时联系并处理。\n"+ + "> 企业名称:%s\n"+ + "> 联系手机:%s\n"+ + "> 当前余额:%.2f 元\n"+ + "> 时间:%s\n", + enterpriseName, + apiUser.AlertPhone, + balance, + time.Now().Format("2006-01-02 15:04:05"), + ) + _ = s.wechatWorkService.SendMarkdownMessage(ctx, content) + } + return nil } // sendLowBalanceAlert 发送低余额预警 @@ -182,5 +210,27 @@ func (s *BalanceAlertServiceImpl) sendLowBalanceAlert(ctx context.Context, apiUs zap.Float64("threshold", apiUser.BalanceAlertThreshold), zap.String("enterprise_name", enterpriseName)) - return s.smsService.SendBalanceAlert(ctx, apiUser.AlertPhone, balance, apiUser.BalanceAlertThreshold, "low_balance", enterpriseName) + if err := s.smsService.SendBalanceAlert(ctx, apiUser.AlertPhone, balance, apiUser.BalanceAlertThreshold, "low_balance", enterpriseName); err != nil { + return err + } + + // 企业微信余额预警通知(仅展示企业名称和联系手机) + if s.wechatWorkService != nil { + content := fmt.Sprintf( + "### 【天远API】用户余额预警\n"+ + "用户余额已低于预警阈值,请及时跟进。\n"+ + "> 企业名称:%s\n"+ + "> 联系手机:%s\n"+ + "> 当前余额:%.2f 元\n"+ + "> 预警阈值:%.2f 元\n"+ + "> 时间:%s\n", + enterpriseName, + apiUser.AlertPhone, + balance, + apiUser.BalanceAlertThreshold, + time.Now().Format("2006-01-02 15:04:05"), + ) + _ = s.wechatWorkService.SendMarkdownMessage(ctx, content) + } + return nil } diff --git a/internal/infrastructure/external/notification/wechat_work_service.go b/internal/infrastructure/external/notification/wechat_work_service.go index 70bf3d1..750a308 100644 --- a/internal/infrastructure/external/notification/wechat_work_service.go +++ b/internal/infrastructure/external/notification/wechat_work_service.go @@ -161,7 +161,7 @@ func (s *WeChatWorkService) sendNewApplicationNotification(ctx context.Context, applicantName := data["applicant_name"].(string) applicationID := data["application_id"].(string) - content := fmt.Sprintf(`## 🆕 新的企业认证申请 + content := fmt.Sprintf(`## 【天远API】🆕 新的企业认证申请 **企业名称**: %s **申请人**: %s @@ -183,7 +183,7 @@ func (s *WeChatWorkService) sendOCRSuccessNotification(ctx context.Context, data confidence := data["confidence"].(float64) applicationID := data["application_id"].(string) - content := fmt.Sprintf(`## ✅ OCR识别成功 + content := fmt.Sprintf(`## 【天远API】✅ OCR识别成功 **企业名称**: %s **识别置信度**: %.2f%% @@ -204,7 +204,7 @@ func (s *WeChatWorkService) sendOCRFailedNotification(ctx context.Context, data applicationID := data["application_id"].(string) errorMsg := data["error_message"].(string) - content := fmt.Sprintf(`## ❌ OCR识别失败 + content := fmt.Sprintf(`## 【天远API】❌ OCR识别失败 **申请ID**: %s **错误信息**: %s @@ -224,7 +224,7 @@ func (s *WeChatWorkService) sendFaceVerifySuccessNotification(ctx context.Contex applicationID := data["application_id"].(string) confidence := data["confidence"].(float64) - content := fmt.Sprintf(`## ✅ 人脸识别成功 + content := fmt.Sprintf(`## 【天远API】✅ 人脸识别成功 **申请人**: %s **申请ID**: %s @@ -246,7 +246,7 @@ func (s *WeChatWorkService) sendFaceVerifyFailedNotification(ctx context.Context applicationID := data["application_id"].(string) errorMsg := data["error_message"].(string) - content := fmt.Sprintf(`## ❌ 人脸识别失败 + content := fmt.Sprintf(`## 【天远API】❌ 人脸识别失败 **申请人**: %s **申请ID**: %s @@ -269,7 +269,7 @@ func (s *WeChatWorkService) sendAdminApprovedNotification(ctx context.Context, d adminName := data["admin_name"].(string) comment := data["comment"].(string) - content := fmt.Sprintf(`## ✅ 管理员审核通过 + content := fmt.Sprintf(`## 【天远API】✅ 管理员审核通过 **企业名称**: %s **申请ID**: %s @@ -294,7 +294,7 @@ func (s *WeChatWorkService) sendAdminRejectedNotification(ctx context.Context, d adminName := data["admin_name"].(string) reason := data["reason"].(string) - content := fmt.Sprintf(`## ❌ 管理员审核拒绝 + content := fmt.Sprintf(`## 【天远API】❌ 管理员审核拒绝 **企业名称**: %s **申请ID**: %s @@ -318,7 +318,7 @@ func (s *WeChatWorkService) sendContractSignedNotification(ctx context.Context, applicationID := data["application_id"].(string) signerName := data["signer_name"].(string) - content := fmt.Sprintf(`## 📝 电子合同已签署 + content := fmt.Sprintf(`## 【天远API】📝 电子合同已签署 **企业名称**: %s **申请ID**: %s @@ -340,7 +340,7 @@ func (s *WeChatWorkService) sendCertificationCompletedNotification(ctx context.C applicationID := data["application_id"].(string) walletAddress := data["wallet_address"].(string) - content := fmt.Sprintf(`## 🎉 企业认证完成 + content := fmt.Sprintf(`## 【天远API】🎉 企业认证完成 **企业名称**: %s **申请ID**: %s @@ -475,7 +475,7 @@ func (s *WeChatWorkService) SendSystemAlert(ctx context.Context, level, title, m icon = "📢" } - content := fmt.Sprintf(`## %s 系统告警 + content := fmt.Sprintf(`## 【天远API】%s 系统告警 **级别**: %s **标题**: %s @@ -496,7 +496,7 @@ func (s *WeChatWorkService) SendSystemAlert(ctx context.Context, level, title, m func (s *WeChatWorkService) SendDailyReport(ctx context.Context, reportData map[string]interface{}) error { s.logger.Info("发送每日报告") - content := fmt.Sprintf(`## 📊 企业认证系统每日报告 + content := fmt.Sprintf(`## 【天远API】📊 企业认证系统每日报告 **报告日期**: %s diff --git a/internal/infrastructure/external/notification/wechat_work_service_test.go b/internal/infrastructure/external/notification/wechat_work_service_test.go new file mode 100644 index 0000000..67950e1 --- /dev/null +++ b/internal/infrastructure/external/notification/wechat_work_service_test.go @@ -0,0 +1,148 @@ +package notification_test + +import ( + "context" + "fmt" + "os" + "testing" + "time" + + "go.uber.org/zap" + + "tyapi-server/internal/infrastructure/external/notification" +) + +// newTestWeChatWorkService 创建用于测试的企业微信服务实例 +// 默认使用环境变量 WECOM_WEBHOOK,若未设置则使用项目配置中的 webhook。 +func newTestWeChatWorkService(t *testing.T) *notification.WeChatWorkService { + t.Helper() + + webhook := os.Getenv("WECOM_WEBHOOK") + if webhook == "" { + // 使用你提供的 webhook 地址 + webhook = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=649bf737-28ca-4f30-ad5f-cfb65b2af113" + } + + logger, _ := zap.NewDevelopment() + return notification.NewWeChatWorkService(webhook, "", logger) +} + +// TestWeChatWork_SendAllBusinessNotifications +// 手动运行该用例,将依次向企业微信群推送 5 种业务场景的通知: +// 1. 用户充值成功 +// 2. 用户申请开发票 +// 3. 用户企业认证成功 +// 4. 用户余额低于阈值 +// 5. 用户余额欠费 +// +// 注意: +// - 通知中只使用企业名称和手机号码,不展示用户ID +// - 默认使用示例企业名称和手机号,实际使用时请根据需要修改 +func TestWeChatWork_SendAllBusinessNotifications(t *testing.T) { + svc := newTestWeChatWorkService(t) + ctx := context.Background() + + // 示例企业信息(实际可按需修改) + enterpriseName := "测试企业有限公司" + phone := "13800000000" + + now := time.Now().Format("2006-01-02 15:04:05") + + tests := []struct { + name string + content string + }{ + { + name: "recharge_success", + content: fmt.Sprintf( + "### 【天远API】用户充值成功通知\n"+ + "> 企业名称:%s\n"+ + "> 联系手机:%s\n"+ + "> 充值金额:%s 元\n"+ + "> 入账总额:%s 元(含赠送)\n"+ + "> 时间:%s\n", + enterpriseName, + phone, + "1000.00", + "1050.00", + now, + ), + }, + { + name: "invoice_applied", + content: fmt.Sprintf( + "### 【天远API】用户申请开发票\n"+ + "> 企业名称:%s\n"+ + "> 联系手机:%s\n"+ + "> 申请开票金额:%s 元\n"+ + "> 发票类型:%s\n"+ + "> 申请时间:%s\n"+ + "\n请财务尽快审核并开具发票。", + enterpriseName, + phone, + "500.00", + "增值税专用发票", + now, + ), + }, + { + name: "certification_completed", + content: fmt.Sprintf( + "### 【天远API】企业认证成功\n"+ + "> 企业名称:%s\n"+ + "> 联系手机:%s\n"+ + "> 完成时间:%s\n"+ + "\n该企业已完成认证,请相关同事同步更新内部系统并关注后续接入情况。", + enterpriseName, + phone, + now, + ), + }, + { + name: "low_balance_alert", + content: fmt.Sprintf( + "### 【天远API】用户余额预警\n"+ + "用户余额已低于预警阈值,请及时跟进。\n"+ + "> 企业名称:%s\n"+ + "> 联系手机:%s\n"+ + "> 当前余额:%s 元\n"+ + "> 预警阈值:%s 元\n"+ + "> 时间:%s\n", + enterpriseName, + phone, + "180.00", + "200.00", + now, + ), + }, + { + name: "arrears_alert", + content: fmt.Sprintf( + "### 【天远API】用户余额欠费告警\n"+ + "该企业已发生欠费,请及时联系并处理。\n"+ + "> 企业名称:%s\n"+ + "> 联系手机:%s\n"+ + "> 当前余额:%s 元\n"+ + "> 欠费金额:%s 元\n"+ + "> 时间:%s\n", + enterpriseName, + phone, + "-50.00", + "50.00", + now, + ), + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + if err := svc.SendMarkdownMessage(ctx, tc.content); err != nil { + t.Fatalf("发送场景[%s]通知失败: %v", tc.name, err) + } + // 简单间隔,避免瞬时发送过多消息 + time.Sleep(500 * time.Millisecond) + }) + } +} +