Files
OpenFlare/internal/apps/user/tasks.go
T
2026-06-08 20:38:01 +08:00

129 lines
3.6 KiB
Go

/*
Copyright 2026 linux.do
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package user
import (
"context"
"encoding/json"
"errors"
"fmt"
"strconv"
"strings"
"github.com/linux-do/credit/internal/model"
"github.com/linux-do/credit/internal/task"
"github.com/linux-do/credit/internal/util/mail"
)
// SendEmailPayload 邮件发送任务载荷
type SendEmailPayload struct {
To string `json:"to"`
Subject string `json:"subject"`
Body string `json:"body"`
}
// SendEmailHandler 发送验证码邮件的异步任务处理器
type SendEmailHandler struct{}
// ValidatePayload 实现 task.PayloadValidator 接口
// 校验并标准化邮件发送参数,框架在 Admin 下发时自动调用
func (h *SendEmailHandler) ValidatePayload(payload []byte) ([]byte, error) {
if len(payload) == 0 {
return nil, errors.New("任务参数不能为空")
}
var req SendEmailPayload
if err := json.Unmarshal(payload, &req); err != nil {
return nil, fmt.Errorf("无效的 JSON 格式: %w", err)
}
req.To = strings.TrimSpace(req.To)
req.Subject = strings.TrimSpace(req.Subject)
req.Body = strings.TrimSpace(req.Body)
if req.To == "" || req.Subject == "" || req.Body == "" {
return nil, errors.New("to、subject、body 不能为空")
}
return json.Marshal(req)
}
// Execute 执行邮件异步发送逻辑
func (h *SendEmailHandler) Execute(ctx context.Context, payload []byte) (*task.TaskResult, error) {
var req SendEmailPayload
if err := json.Unmarshal(payload, &req); err != nil {
task.AppendLog(ctx, "解析邮件发送参数失败: %v", err)
return nil, fmt.Errorf("解析邮件发送参数失败: %w", err)
}
task.AppendLog(ctx, "开始准备发送邮件到: %s, 主题: %s", req.To, req.Subject)
// 从数据库读取最新的 SMTP 系统配置
var smtpHost string
var smtpPortVal string
var smtpUsername string
var smtpPassword string
var sc model.SystemConfig
if err := sc.GetByKey(ctx, model.ConfigKeySMTPHost); err == nil {
smtpHost = sc.Value
}
if err := sc.GetByKey(ctx, model.ConfigKeySMTPPort); err == nil {
smtpPortVal = sc.Value
}
if err := sc.GetByKey(ctx, model.ConfigKeySMTPUsername); err == nil {
smtpUsername = sc.Value
}
if err := sc.GetByKey(ctx, model.ConfigKeySMTPPassword); err == nil {
smtpPassword = sc.Value
}
if smtpHost == "" || smtpPortVal == "" || smtpUsername == "" {
err := errors.New("系统 SMTP 邮件服务配置不完整")
task.AppendLog(ctx, "发送失败: %v", err)
return nil, err
}
smtpPort, err := strconv.Atoi(smtpPortVal)
if err != nil {
smtpPort = 587
}
cfg := mail.Config{
Host: smtpHost,
Port: smtpPort,
Username: smtpUsername,
Password: smtpPassword,
}
task.AppendLog(ctx, "连接 SMTP 服务器: %s:%d, 用户名: %s", smtpHost, smtpPort, smtpUsername)
// 调用 SendMailHTML 执行邮件发送,这里会有 5s 拨号超时和 10s 读写限制
err = mail.SendMailHTML(cfg, req.To, req.Subject, req.Body)
if err != nil {
task.AppendLog(ctx, "邮件发送失败: %v", err)
return nil, fmt.Errorf("发送邮件失败: %w", err)
}
msg := fmt.Sprintf("邮件成功发送至: %s", req.To)
task.AppendLog(ctx, "%s", msg)
return &task.TaskResult{
Message: msg,
}, nil
}