Files
WarrenZhou aca5515e15 docs: add soft copyright application documents and fix import indent
1. 新增软著申请所需的全套文档:README说明、操作说明书、软件设计说明书
2. 修复task.go文件中的import代码缩进格式
2026-08-19 23:05:42 +08:00

123 lines
3.6 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package controller
import (
"encoding/json"
"net/http"
"strconv"
"stock-analysis/internal/cache"
"stock-analysis/internal/job"
)
// 任务接口错误(包级哨兵错误,便于测试断言)。
var (
errBadTaskType = errString("无效的任务类型,支持 full / incremental")
errMissingTaskID = errString("缺少任务 ID")
errTaskNotFound = errString("任务不存在")
errTaskNotRunning = errString("任务不存在或已结束,无法取消")
)
// errString 将字符串转为 error,避免重复 fmt.Errorf 调用。
type errString string
func (e errString) Error() string { return string(e) }
// TaskController 提供数据入库任务管理的 HTTP 接口。
// 任务由 job.Manager 异步执行,状态实时写入 Redis,持久化到 SQLite。
type TaskController struct {
mgr *job.Manager
}
// NewTaskController 创建任务控制器。
func NewTaskController(mgr *job.Manager) *TaskController {
return &TaskController{mgr: mgr}
}
// taskRequest 创建任务请求参数。
type taskRequest struct {
Type string `json:"type"` // full | incremental
Since string `json:"since"` // 增量起点(YYYY-MM-DD),可选,留空自动取最后K线日期
Limit int `json:"limit"` // 限制处理数量,0 表示全部
}
// CreateTask 处理 POST /api/tasks,创建数据入库任务(全量/增量)。
// 任务异步执行,立即返回 pending 状态的任务快照,前端通过轮询 GetTask 获取进度。
func (c *TaskController) CreateTask(w http.ResponseWriter, r *http.Request) {
var req taskRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeError(w, http.StatusBadRequest, err)
return
}
var typ cache.TaskType
switch req.Type {
case "full", "":
typ = cache.TaskTypeFull
case "incremental":
typ = cache.TaskTypeIncremental
default:
writeError(w, http.StatusBadRequest, errBadTaskType)
return
}
task, err := c.mgr.Start(r.Context(), typ, req.Since, req.Limit)
if err != nil {
writeError(w, http.StatusInternalServerError, err)
return
}
writeJSON(w, http.StatusOK, task)
}
// ListTasks 处理 GET /api/tasks,返回任务列表(按开始时间降序)。
// 默认返回最近 50 条,可通过 ?limit=N 调整。
// 运行中任务会合并 Redis 实时进度。
func (c *TaskController) ListTasks(w http.ResponseWriter, r *http.Request) {
limit := 50
if q := r.URL.Query().Get("limit"); q != "" {
if n, err := strconv.Atoi(q); err == nil && n > 0 {
limit = n
}
}
tasks, err := c.mgr.List(r.Context(), limit)
if err != nil {
writeError(w, http.StatusInternalServerError, err)
return
}
writeJSON(w, http.StatusOK, tasks)
}
// GetTask 处理 GET /api/tasks/{id},返回单个任务状态。
// 优先从 Redis 读取实时状态,回退 SQLite 持久化记录。
func (c *TaskController) GetTask(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
if id == "" {
writeError(w, http.StatusBadRequest, errMissingTaskID)
return
}
task, err := c.mgr.Get(r.Context(), id)
if err != nil {
writeError(w, http.StatusNotFound, err)
return
}
if task == nil {
writeError(w, http.StatusNotFound, errTaskNotFound)
return
}
writeJSON(w, http.StatusOK, task)
}
// CancelTask 处理 POST /api/tasks/{id}/cancel,取消运行中任务。
// 仅运行中任务可取消,已完成任务忽略。
func (c *TaskController) CancelTask(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
if id == "" {
writeError(w, http.StatusBadRequest, errMissingTaskID)
return
}
if ok := c.mgr.Cancel(id); !ok {
writeError(w, http.StatusNotFound, errTaskNotRunning)
return
}
writeJSON(w, http.StatusOK, map[string]string{"message": "任务已取消"})
}