This commit is contained in:
ryan
2026-06-08 14:57:19 +08:00
parent 72c74803be
commit f136c9abdb
9 changed files with 821 additions and 36 deletions
+41 -1
View File
@@ -17,6 +17,7 @@ limitations under the License.
package task
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
@@ -49,6 +50,7 @@ type DispatchTaskRequest struct {
StartTime *time.Time `json:"start_time"`
EndTime *time.Time `json:"end_time"`
UserID *uint64 `json:"user_id"`
Payload string `json:"payload"`
}
// DispatchTask 下发任务
@@ -78,7 +80,45 @@ func DispatchTask(c *gin.Context) {
return
}
taskID, err := task.DispatchTask(c.Request.Context(), req.TaskType, nil, "manual")
var payloadBytes []byte
if req.TaskType == task.TaskTypeSendEmail {
if strings.TrimSpace(req.Payload) == "" {
c.JSON(http.StatusBadRequest, util.Err("任务参数 Payload 不能为空"))
return
}
var mailPayload struct {
To string `json:"to"`
Subject string `json:"subject"`
Body string `json:"body"`
}
if err := json.Unmarshal([]byte(req.Payload), &mailPayload); err != nil {
c.JSON(http.StatusBadRequest, util.Err("无效的 JSON 格式: "+err.Error()))
return
}
mailPayload.To = strings.TrimSpace(mailPayload.To)
mailPayload.Subject = strings.TrimSpace(mailPayload.Subject)
mailPayload.Body = strings.TrimSpace(mailPayload.Body)
if mailPayload.To == "" || mailPayload.Subject == "" || mailPayload.Body == "" {
c.JSON(http.StatusBadRequest, util.Err("收件人地址 (to)、邮件主题 (subject) 和邮件内容 (body) 不能为空"))
return
}
var err error
payloadBytes, err = json.Marshal(mailPayload)
if err != nil {
c.JSON(http.StatusInternalServerError, util.Err("构造邮件任务载荷失败"))
return
}
} else {
if req.Payload != "" {
payloadBytes = []byte(req.Payload)
}
}
taskID, err := task.DispatchTask(c.Request.Context(), req.TaskType, payloadBytes, "manual")
if err != nil {
c.JSON(http.StatusInternalServerError, util.Err(fmt.Sprintf("%s: %v", TaskDispatchFailed, err)))
return
+53
View File
@@ -125,6 +125,59 @@ func TestDispatchTask(t *testing.T) {
assert.NotEmpty(t, taskID)
})
t.Run("dispatch send_email task successfully with valid payload", func(t *testing.T) {
payload := DispatchTaskRequest{
TaskType: task.TaskTypeSendEmail,
Payload: `{"to":"receiver@example.com","subject":"Test Subject","body":"Test Body"}`,
}
body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", "/api/v1/admin/tasks/dispatch", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code, "Body: %s", w.Body.String())
var resp util.ResponseAny
json.Unmarshal(w.Body.Bytes(), &resp)
assert.Empty(t, resp.ErrorMsg)
assert.NotNil(t, resp.Data)
})
t.Run("dispatch send_email task failure with invalid payload json", func(t *testing.T) {
payload := DispatchTaskRequest{
TaskType: task.TaskTypeSendEmail,
Payload: `{"to":`,
}
body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", "/api/v1/admin/tasks/dispatch", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusBadRequest, w.Code)
var resp util.ResponseAny
json.Unmarshal(w.Body.Bytes(), &resp)
assert.Contains(t, resp.ErrorMsg, "无效的 JSON 格式")
})
t.Run("dispatch send_email task failure with missing fields", func(t *testing.T) {
payload := DispatchTaskRequest{
TaskType: task.TaskTypeSendEmail,
Payload: `{"to":"","subject":"Test","body":"Test"}`,
}
body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", "/api/v1/admin/tasks/dispatch", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusBadRequest, w.Code)
var resp util.ResponseAny
json.Unmarshal(w.Body.Bytes(), &resp)
assert.Contains(t, resp.ErrorMsg, "不能为空")
})
t.Run("dispatch invalid task type failure", func(t *testing.T) {
payload := DispatchTaskRequest{
TaskType: "invalid_task_type",
+37
View File
@@ -31,6 +31,16 @@ const (
TaskTypeSendEmail = "send_email"
)
// TaskParam 任务参数定义
type TaskParam struct {
Name string `json:"Name"` // 参数键名
Label string `json:"Label"` // 显示名称
Type string `json:"Type"` // 类型:string, text, number
Required bool `json:"Required"` // 是否必填
Placeholder string `json:"Placeholder"` // 占位符
Description string `json:"Description"` // 描述
}
// TaskMeta 任务元数据
type TaskMeta struct {
Type string
@@ -41,6 +51,7 @@ type TaskMeta struct {
MaxRetry int
Queue string
Retryable bool // 是否支持手动重试
Params []TaskParam
}
// DispatchableTasks 可下发的任务列表
@@ -64,6 +75,32 @@ var DispatchableTasks = []TaskMeta{
MaxRetry: 3,
Queue: QueueDefault,
Retryable: true,
Params: []TaskParam{
{
Name: "to",
Label: "接收邮箱 (To)",
Type: "string",
Required: true,
Placeholder: "receiver@example.com",
Description: "接收邮件的目标邮箱地址",
},
{
Name: "subject",
Label: "邮件主题 (Subject)",
Type: "string",
Required: true,
Placeholder: "请输入邮件主题",
Description: "发送邮件的主题标题",
},
{
Name: "body",
Label: "邮件内容 (Body)",
Type: "text",
Required: true,
Placeholder: "请输入邮件内容(支持 HTML 格式)",
Description: "发送邮件的内容主体",
},
},
},
}