package usecase import ( "backend/pkg/notification/domain" "backend/pkg/notification/domain/entity" "backend/pkg/notification/domain/repository" "backend/pkg/notification/domain/template" "backend/pkg/notification/domain/usecase" "context" "fmt" "strings" "backend/pkg/library/errs" "backend/pkg/library/errs/code" "github.com/zeromicro/go-zero/core/logx" ) type TemplateUseCaseParam struct { TemplateRepo repository.TemplateRepository // 可選的資料庫模板 repository } type TemplateUseCase struct { param TemplateUseCaseParam } func MustTemplateUseCase(param TemplateUseCaseParam) usecase.TemplateUseCase { return &TemplateUseCase{ param, } } // GetEmailTemplateByStatic 從靜態模板獲取郵件模板 func (use *TemplateUseCase) GetEmailTemplateByStatic(_ context.Context, language template.Language, templateID template.Type) (template.EmailTemplate, error) { // 查找指定語言的模板映射 templateByLang, exists := template.EmailTemplateMap[language] if !exists { return template.EmailTemplate{}, errs.ResourceNotFoundWithScope( code.CloudEPNotification, domain.FailedToGetTemplateErrorCode, fmt.Sprintf("email template not found for language: %s", language)) } // 查找指定類型的模板生成函數 templateFunc, exists := templateByLang[templateID] if !exists { return template.EmailTemplate{}, errs.ResourceNotFoundWithScope( code.CloudEPNotification, domain.FailedToGetTemplateErrorCode, fmt.Sprintf("email template not found for type ID: %s", templateID)) } // 執行模板生成函數 tmp, err := templateFunc() if err != nil { return template.EmailTemplate{}, errs.DatabaseErrorWithScope( code.CloudEPNotification, domain.FailedToGetTemplateErrorCode, fmt.Sprintf("error generating email template: %v", err)) } return tmp, nil } // GetEmailTemplate 獲取郵件模板(優先從資料庫,回退到靜態模板) func (use *TemplateUseCase) GetEmailTemplate(ctx context.Context, language template.Language, templateID template.Type) (template.EmailTemplate, error) { // 1. 嘗試從資料庫獲取模板 if use.param.TemplateRepo != nil { dbTemplate, err := use.param.TemplateRepo.GetTemplate(ctx, "email", string(language), string(templateID)) if err == nil && dbTemplate != nil && dbTemplate.IsActive { logx.WithContext(ctx).Infof("Using database template for %s/%s", language, templateID) return template.EmailTemplate{ Title: dbTemplate.Subject, Body: dbTemplate.Body, }, nil } // 記錄資料庫查詢失敗,但不返回錯誤,繼續使用靜態模板 if err != nil { logx.WithContext(ctx).WithFields(logx.LogField{Key: "error", Value: err.Error()}).Error("Failed to get template from database, falling back to static") } } // 2. 回退到靜態模板 logx.WithContext(ctx).Infof("Using static template for %s/%s", language, templateID) return use.GetEmailTemplateByStatic(ctx, language, templateID) } // GetSMSTemplate 獲取 SMS 模板(優先從資料庫,回退到靜態模板) func (use *TemplateUseCase) GetSMSTemplate(ctx context.Context, language template.Language, templateID template.Type) (usecase.SMSTemplateResp, error) { // 1. 嘗試從資料庫獲取模板 if use.param.TemplateRepo != nil { dbTemplate, err := use.param.TemplateRepo.GetTemplate(ctx, "sms", string(language), string(templateID)) if err == nil && dbTemplate != nil && dbTemplate.IsActive { logx.WithContext(ctx).Infof("Using database SMS template for %s/%s", language, templateID) return usecase.SMSTemplateResp{ Body: dbTemplate.Body, }, nil } // 記錄資料庫查詢失敗,但不返回錯誤,繼續使用靜態模板 if err != nil { logx.WithContext(ctx).WithFields(logx.LogField{Key: "error", Value: err.Error()}).Error("Failed to get SMS template from database, falling back to static") } } // 2. 回退到靜態模板(SMS 暫時沒有靜態模板,返回默認) logx.WithContext(ctx).Infof("Using default SMS template for %s/%s", language, templateID) return use.getDefaultSMSTemplate(templateID), nil } // RenderEmailTemplate 渲染郵件模板(替換變數) func (use *TemplateUseCase) RenderEmailTemplate(ctx context.Context, tmpl template.EmailTemplate, params entity.TemplateParams) (usecase.EmailTemplateResp, error) { renderedSubject := use.renderTemplate(tmpl.Title, params) renderedBody := use.renderTemplate(tmpl.Body, params) return usecase.EmailTemplateResp{ Subject: renderedSubject, Body: renderedBody, }, nil } // RenderSMSTemplate 渲染 SMS 模板(替換變數) func (use *TemplateUseCase) RenderSMSTemplate(ctx context.Context, tmpl usecase.SMSTemplateResp, params entity.TemplateParams) (usecase.SMSTemplateResp, error) { renderedBody := use.renderTemplate(tmpl.Body, params) return usecase.SMSTemplateResp{ Body: renderedBody, }, nil } // renderTemplate 渲染模板內容(簡單的字符串替換) func (use *TemplateUseCase) renderTemplate(content string, params entity.TemplateParams) string { result := content // 替換基本參數 result = strings.ReplaceAll(result, "{{.Username}}", params.Username) result = strings.ReplaceAll(result, "{{.VerifyCode}}", params.VerifyCode) // 替換額外參數 for key, value := range params.Extra { placeholder := fmt.Sprintf("{{.%s}}", key) result = strings.ReplaceAll(result, placeholder, value) } return result } // getDefaultSMSTemplate 獲取默認 SMS 模板 func (use *TemplateUseCase) getDefaultSMSTemplate(templateID template.Type) usecase.SMSTemplateResp { switch templateID { case template.ForgetPasswordVerify: return usecase.SMSTemplateResp{ Body: "您的密碼重設驗證碼是:{{.VerifyCode}},請在5分鐘內使用。如非本人操作請忽略此訊息。", } case template.BindingEmail: return usecase.SMSTemplateResp{ Body: "您的綁定驗證碼是:{{.VerifyCode}},請在5分鐘內使用。如非本人操作請忽略此訊息。", } default: return usecase.SMSTemplateResp{ Body: "您的驗證碼是:{{.VerifyCode}},請在5分鐘內使用。", } } }