8.2 KiB
name, description
| name | description |
|---|---|
| new-async-task | 项目专用:当新增或修改 Asynq 异步任务、后台任务、定时任务、任务元数据、TaskHandler、TaskParam、PayloadValidator、AppendLog、任务重试、任务执行记录或 Admin 任务 API 时必须使用。本技能按项目约束指导常量定义、处理器实现、统一注册、Worker 路由、Cron 配置、Swagger、测试和 code-check 验证。 |
异步任务开发
本技能只覆盖 Asynq 任务工作流。开始前先读仓库根目录 AGENTS.md,并遵守其中的项目级规则:HTTP 路由只在 internal/router/router.go 注册、API 变更后运行 make swagger、提交前运行 make code-check、不要删除 frontend/node_modules、internal/util/ 不引入框架依赖。
先定位真实链路
新增或修改任务前,先快速查看这些文件,确认当前实现没有漂移:
internal/task/handler.go:TaskHandler、TaskResult、可选PayloadValidator。internal/task/constants.go: 框架通用常量,如QueueDefault和DefaultMaxRetry。internal/task/meta.go: 框架任务元数据结构体(TaskParam、TaskMeta)及全局动态注册与查询接口。internal/task/executor.go:RegisterHandler、ValidateAndNormalizePayload、DispatchTask、RetryTask、ProcessTask、AppendLog。internal/task/handlers/register.go: 内置 handler 和元数据的统一注册点,Admin API 和 Worker 都依赖它。internal/task/worker/worker.go: Asynq mux 动态路由分发和队列配置。internal/task/scheduler/scheduler.go: Cron 调度。internal/apps/admin/task/routers.go: Admin 下发、查询、详情、重试 API。- 现有参考:
internal/apps/upload/tasks.go(无参数任务)、internal/apps/user/tasks.go(带参数任务)。
当前任务执行链路:
Admin dispatch -> ValidateAndNormalizePayload -> DispatchTask
-> Asynq Redis queue -> worker mux -> ProcessTask
-> registered TaskHandler.Execute -> TaskExecution status/log/result
修改检查清单
按任务影响面选择对应步骤。不要只改其中一条链路。
需要可复制的代码模板时,阅读 references/CODE-EXAMPLES.md。那里包含任务常量、无参数 handler、带参数
PayloadValidator、统一注册、Worker 路由、Cron 配置和测试示例。
-
定义任务元数据与常量。
- 业务包常量与元数据:在对应业务模块的
internal/apps/<module>/tasks.go中定义 Asynq 任务类型常量(如CleanupUnusedUploadsTask = "upload:cleanup_unused")和 Admin 任务类型常量(如TaskTypeCleanupUploads = "cleanup_unused_uploads")。 - 在同一
tasks.go文件中定义该任务的TaskMeta元数据变量(如CleanupUnusedUploadsMeta = task.TaskMeta{...}),配置Type、AsynqTask、Name、Description、MaxRetry、Queue、Retryable等字段。 - 有参数任务在
Params中描述前端表单字段。TaskParam.Name必须与 payload JSON tag 对齐。
- 业务包常量与元数据:在对应业务模块的
-
实现 handler。
- 优先放在对应业务模块的
internal/apps/<module>/tasks.go。 - handler 必须实现
task.TaskHandler。 - 带参数任务定义 payload struct,并实现
task.PayloadValidator做服务端校验和标准化。 Execute中仍要解析 payload,因为 Scheduler、重试或其他入口不一定经过 Admin 校验。- 不要在 handler 中写复杂 SQL;复杂查询放到
internal/model/或internal/service/。 - 新增 Go 文件后检查 license header;必要时运行
make license。
- 优先放在对应业务模块的
-
统一注册 handler 与元数据。
- 在
internal/task/handlers/register.go导入业务模块,调用task.RegisterHandler(asynqTaskType, handler)注册处理器。 - 同时,在该文件中调用
task.RegisterTaskMeta(meta)注册刚才在业务模块中定义的任务元数据。 - 这里是 Admin 校验、元数据获取和 Worker 执行共同依赖的注册点。
- 在
-
如需 Cron 调度,系统默认定时任务必须通过 SQL 迁移(goose)初始化。
- 确保任务已正确注册并载入全局元数据池中。
- 在
internal/db/migrator/goose/postgres和sqlite下编写 migration 脚本,使用INSERT INTO schedules语句初始化任务,指定task_type和cron等字段。必须妥善处理冲突(如ON CONFLICT DO NOTHING)以支持幂等。
-
如改动 Admin API。
- handler 放在
internal/apps/admin/<module>/或现有 Admin task 模块内。 - 路由只在
internal/router/router.go注册。 - 响应保持
{ "error_msg": "", "data": ... },分页保持{ "total": 0, "results": [] }。 - 补完整 Swagger 注释并运行
make swagger。
- handler 放在
Handler 模式
约定:
ValidatePayload是 Admin 下发时的服务端校验入口,返回值会作为标准化 payload 存库和入队。TaskParam只是前端表单元数据,不代替服务端校验。- 成功返回
&task.TaskResult{Message: "...", Detail: "..."};失败返回nil, fmt.Errorf("..."),由ProcessTask标记失败并交给 Asynq 重试。 - 错误要返回给框架,不在 handler 内吞掉;可继续的单条失败可用
AppendLog记录后继续处理。
在新增无参数任务、带参数任务或
PayloadValidator时,阅读 references/CODE-EXAMPLES.md 的 Handler 示例。
AppendLog 规则
在 TaskHandler.Execute 内用 task.AppendLog(ctx, format, args...) 写任务日志。
- 任务开始、参数摘要、批次进度、关键状态、可继续错误、完成摘要适合记录。
- 避免对大循环中的每条记录都写日志;每次
AppendLog都可能触发一次数据库更新。 - 如果上下文中没有 taskID,
AppendLog会降级为普通应用日志,不应额外兜底报错。
重试规则
该项目有两层重试:
- Asynq 自动重试:
ProcessTask返回 error 后按入队的MaxRetry处理。 - Admin 手动重试:
POST /api/v1/admin/tasks/executions/:id/retry创建新的TaskExecution,要求原任务状态为 failed、Retryable=true、RetryCount < MaxRetry。
修改重试语义时同时检查 internal/task/executor.go、internal/model/task_execution.go、internal/apps/admin/task/routers.go 和前端任务执行列表。
Frontend/Admin 任务 UI
只有任务元数据变化时,通常不需要写新页面;现有 Admin UI 会根据 DispatchableTasks 和 Params 动态渲染。
若确实要改前端:
- 读 shadcn skill。
- 业务组件优先放在
frontend/components/common/admin/。 - API 访问走
frontend/lib/services/的 service class 和servicesexport。 - 不使用
any。 - 页面根容器保持
w-full,不要加页面级max-w-*。
验证
根据改动范围运行最小有效验证,最后提交前必须运行项目门禁。
- Handler 单测:覆盖成功、失败、日志关键路径;带参数任务覆盖
ValidatePayload成功、空 payload、非法 JSON、缺失必填、标准化。 - Admin dispatch 单测:合法 payload 返回成功,非法 payload 返回 400 且错误清晰。
- Retry 单测:failed 且可重试能创建新执行记录;非 failed、
Retryable=false、超过MaxRetry都拒绝。 - 目标包测试示例:
go test ./internal/task ./internal/apps/admin/task ./internal/apps/<module>
- API 改动后:
make swagger
- 提交前:
make code-check
如涉及前端或整体构建,补跑 make build-test。测试中需要 Redis/Asynq 时,可用现有测试模式或 miniredis;不要为了测试便利把 internal/task 反向塞进通用 util/testhelper,避免 import cycle。
相关 Skills
- Go 错误处理:在设计 handler 返回错误、包装底层错误或避免“记录并返回”时,参见 go-error-handling。
- Go 测试:在编写 handler、dispatch 或 retry 单测时,参见 go-testing。
- Go context:在任务业务逻辑传播取消、超时或 request scoped 值时,参见 go-context。
- Go logging:在决定任务日志、应用日志和日志级别边界时,参见 go-logging。
- shadcn:在修改 Admin 任务 UI 时,参见 shadcn。