Conversation
There was a problem hiding this comment.
Pull request overview
该 PR 以“质量基线”建设为主,围绕后端错误语义统一、分层职责清晰化、测试补强,以及工程化任务/CI 与文档结构重整,提升整体一致性与可维护性。
Changes:
- 后端:引入领域错误(
model包)并在 API Handler 侧集中映射 HTTP 状态码;Service/Store 依赖改为接口化,补充 SQLite Store 与 Handler 单测。 - 工程化:扩展 Task 任务(fmt/lint/test/vet/build + docs 校验),CI/Release 工作流对齐;新增 TODO 索引生成器。
- 文档:重构标准/契约/设计/执行计划目录结构,补齐规范与入口导航(AGENTS.md)。
Reviewed changes
Copilot reviewed 33 out of 33 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/todo-gen/main.go | 新增 TODO 索引生成器(扫描注释并输出 Markdown)。 |
| frontend/src/views/ContainerList.vue | 增补关键交互注释,明确视图层职责。 |
| frontend/src/stores/containers.ts | 增补 store 副作用/错误收敛注释,强化“视图只触发 action”边界。 |
| frontend/src/components/TopBar.vue | 注释更新,强调外部点击监听成对移除。 |
| frontend/src/components/Sidebar.vue | 增补状态开关语义注释。 |
| frontend/src/api/index.ts | 统一非 2xx 错误提取逻辑(优先读取后端 error 字段)。 |
| docs/standards/ops.md | 新增运维/CI/CD 标准与任务门禁说明。 |
| docs/standards/frontend.md | 新增前端规范(架构、注释、异常处理、样式约束等)。 |
| docs/standards/directory.md | 新增项目目录结构规范说明。 |
| docs/standards/backend.md | 新增后端规范(错误处理、依赖注入、SQLite 并发等)。 |
| docs/ops_engineering_standards.md | 删除旧运维标准文档(被新结构替代)。 |
| docs/exec-plans/TODO.md | 新增 TODO 索引产物(由脚本生成)。 |
| docs/exec-plans/README.md | 新增执行计划目录说明与模板约定。 |
| docs/domain_instance_lifecycle.md | 删除旧领域文档(迁移到设计文档目录)。 |
| docs/design-docs/instance_lifecycle.md | 新增实例生命周期设计文档(含一致性/回滚策略)。 |
| docs/api/contracts.md | 新增 API 合约文档(路径、状态码、样例)。 |
| docs/02_API_Contracts.md | 删除旧 API 合约文档(被新合约替代)。 |
| docs/01_Frontend_Standards.md | 删除旧前端规范文档(被新规范替代)。 |
| docs/01_Backend_Standards.md | 删除旧后端规范文档(被新规范替代)。 |
| docs/00_Root_Context.md | 删除旧 Root Context 文档(目录重构)。 |
| backend/internal/store/sqlite_test.go | 新增 SQLite Store 单测(Save/Get/List/Delete 与唯一约束)。 |
| backend/internal/store/sqlite.go | 统一错误语义(使用 model.ErrNameExists),补充注释与 schema 初始化。 |
| backend/internal/service/docker_service.go | store 依赖改为接口,错误 wrap 更一致,补充关键流程注释/TODO。 |
| backend/internal/model/errors.go | 新增领域错误定义(名称冲突、运行中删除等)。 |
| backend/internal/api/router.go | 增补路由/中间件注释,使用集中路由注册。 |
| backend/internal/api/handlers_test.go | 新增 Handler 单测(成功/异常/冲突映射)。 |
| backend/internal/api/handlers.go | 引入 InstanceService 接口与集中 mapErrorCode,统一错误映射。 |
| backend/.golangci.yml | 新增后端 lint 配置(govet/staticcheck 等)。 |
| Taskfile.yml | 扩展 fmt/lint/docs 校验任务,CI 门禁对齐。 |
| Readme.md | 精简运行指南片段与待办清单排版调整。 |
| AGENTS.md | 新增文档导航入口。 |
| .github/workflows/release.yml | workflow 对齐新的 task 名称(fmt:check)。 |
| .github/workflows/ci.yml | CI 对齐新的 fmt:check 任务。 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
docs/exec-plans/TODO.md
Outdated
| Generated at: 2026-03-30T00:42:39+08:00 | ||
| Source pattern: TODO: description | ||
| Repository root: C:/Users/guzem/Desktop/Core/毕设/MineDock |
There was a problem hiding this comment.
生成的 TODO 文档里包含了本机绝对路径(Repository root: ...)和精确时间戳。把这类信息提交到仓库会泄露本地目录结构,并导致每次生成都产生无意义的 diff。建议在生成时改为输出仓库相对路径/固定值(例如 .),并考虑移除时间戳或改成日期级别,确保产物可复现。
| Generated at: 2026-03-30T00:42:39+08:00 | |
| Source pattern: TODO: description | |
| Repository root: C:/Users/guzem/Desktop/Core/毕设/MineDock | |
| Generated at: 2026-03-30 | |
| Source pattern: TODO: description | |
| Repository root: . |
scripts/todo-gen/main.go
Outdated
| if _, err := fmt.Fprintf(f, "\nGenerated at: %s\n", now); err != nil { | ||
| return err | ||
| } | ||
| if _, err := fmt.Fprintln(f, "Source pattern: TODO: description"); err != nil { | ||
| return err | ||
| } | ||
| if _, err := fmt.Fprintf(f, "Repository root: %s\n", filepath.ToSlash(root)); err != nil { | ||
| return err | ||
| } |
There was a problem hiding this comment.
writeMarkdown 会把 root(通常是绝对路径)写入输出文件的 Repository root: 字段,导致生成的 TODO.md 带上开发者本机路径、且产物不可复现。建议改为写入仓库相对路径(如 .)或直接省略该字段,并与 Taskfile 的执行目录/参数保持一致。
frontend/src/api/index.ts
Outdated
| function getResponseErrorMessage(data: unknown, status: number): string { | ||
| // 后端契约约定优先返回 error 字段,前端统一在这里做错误信息提取。 | ||
| if (data && typeof data === "object" && "error" in data) { | ||
| const error = (data as { error?: unknown }).error; | ||
| if (typeof error === "string" && error.trim().length > 0) { | ||
| return error; | ||
| } | ||
| } | ||
| return `request failed: ${status}`; | ||
| } |
There was a problem hiding this comment.
fallback 分支返回的错误信息是硬编码英文字符串(request failed: ${status}),而该 Error 会被上层直接展示到 output 区域。根据新增的前端规范(错误提示用 i18n key 输出),这里更适合返回可翻译的 key/结构化错误(如携带 status 供 store 映射),避免把英文文案直接暴露到 UI。
frontend/src/stores/containers.ts
Outdated
| // 统一的输出区文本,视图层只负责渲染该结果。 | ||
| const output = ref<string>(""); | ||
|
|
||
| // 将异常对象收敛为可展示文本,避免视图层分散处理错误结构。 | ||
| function getErrorMessage(error: unknown): string { |
There was a problem hiding this comment.
output 被定义为“统一输出区文本”,但当前 store 的 printError 会生成硬编码前缀(ERROR:)并透出后端/网络层原始字符串到 UI。若按前端规范要求错误提示使用 i18n key 输出,建议在 store 层把错误映射为稳定的 i18n key(或至少移除硬编码英文前缀),由视图层用 t(...) 渲染。
docs/standards/frontend.md
Outdated
| ### 格式化 | ||
|
|
||
| - 使用 ESLint + Prettier。 | ||
| - 使用 TypeScript严格模式。 |
There was a problem hiding this comment.
文档里 “TypeScript严格模式” 少了空格,建议统一为 “TypeScript 严格模式”。
| - 使用 TypeScript严格模式。 | |
| - 使用 TypeScript 严格模式。 |
docs/standards/directory.md
Outdated
|
|
||
| ```text | ||
| MineDock/ | ||
| ├── .github/ # 存放ci/cd |
There was a problem hiding this comment.
目录说明里的 “ci/cd” 建议按常见写法大写为 “CI/CD”,与文档其它地方的用词保持一致。
| ├── .github/ # 存放ci/cd | |
| ├── .github/ # 存放 CI/CD |
Taskfile.yml
Outdated
| docs:todo: | ||
| desc: Generate docs/exec-plans/TODO.md from TODO comments | ||
| cmds: | ||
| - go run ./scripts/todo-gen/main.go -root .. -out ../docs/exec-plans/TODO.md |
There was a problem hiding this comment.
docs:todo 任务当前从仓库根目录执行,但参数 -root .. -out ../docs/... 会把扫描根目录指向仓库上一级、并把输出写到仓库外的 ../docs。这会导致生成结果不稳定(还可能扫到无关目录),且在 CI/本地容易误写文件。建议把 root/out 改为仓库内路径(例如 -root . -out docs/exec-plans/TODO.md),或为该 task 设置 dir: scripts/todo-gen 后再使用相对路径。
| - go run ./scripts/todo-gen/main.go -root .. -out ../docs/exec-plans/TODO.md | |
| - go run ./scripts/todo-gen/main.go -root . -out docs/exec-plans/TODO.md |
Taskfile.yml
Outdated
| desc: Run backend golangci-lint checks | ||
| dir: backend | ||
| cmds: | ||
| - go run github.com/golangci/golangci-lint/cmd/golangci-lint@latest run --config .golangci.yml ./... |
There was a problem hiding this comment.
backend:lint 使用 go run ...@latest 会让 CI/本地 lint 结果随时间漂移(不同日期拉到不同版本),可能导致不可复现的失败。建议固定 golangci-lint 的版本号(例如用明确的 @vX.Y.Z 或在工具链/CI 中单独安装固定版本),保证门禁稳定。
| - go run github.com/golangci/golangci-lint/cmd/golangci-lint@latest run --config .golangci.yml ./... | |
| - go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.59.1 run --config .golangci.yml ./... |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 35 out of 35 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| inst := model.Instance{ContainerID: resp.ID, Name: name, Status: "Stopped"} | ||
| if err := s.store.Save(ctx, inst); err != nil { | ||
| // 说明:请求上下文取消时,清理逻辑会使用独立上下文做尽力回收。 | ||
| _ = s.cli.ContainerRemove(context.Background(), resp.ID, container.RemoveOptions{Force: true}) | ||
| return "", err | ||
| return "", fmt.Errorf("save instance record: %w", err) | ||
| } |
There was a problem hiding this comment.
CreateInstance wraps store.Save errors with additional context ("save instance record: %w"). When the underlying error is a domain sentinel like model.ErrNameExists, this changes the string sent back in the API response (handlers currently return err.Error()), which breaks the frontend’s backendMessage-to-i18n mapping (it expects exactly "instance name already exists"). Consider either not wrapping known domain errors (return them as-is) or ensuring the handler returns a stable client-facing message for errors.Is(model.ErrNameExists).
| code = http.StatusConflict | ||
| } | ||
| writeJSON(w, code, statusResponse{Status: "error", Error: err.Error()}) | ||
| writeJSON(w, mapErrorCode(err), statusResponse{Status: "error", Error: err.Error()}) |
There was a problem hiding this comment.
Handlers return err.Error() directly in the JSON body even when the status code is derived from errors.Is(...) (e.g., ErrNameExists/ErrInstanceRunning). This makes the client-facing error field unstable when upstream layers wrap errors, and may leak internal context strings. Prefer emitting a stable, canonical message for known domain errors (e.g., use the sentinel’s Error() value or a fixed string) while still keeping wrapped errors for server-side logging.
| writeJSON(w, mapErrorCode(err), statusResponse{Status: "error", Error: err.Error()}) | |
| status := mapErrorCode(err) | |
| msg := http.StatusText(status) | |
| if msg == "" { | |
| msg = "error" | |
| } | |
| writeJSON(w, status, statusResponse{Status: "error", Error: msg}) |
| rootFlag := flag.String("root", "..", "repository root path") | ||
| outFlag := flag.String("out", "../docs/exec-plans/TODO.md", "output markdown file path") | ||
| flag.Parse() |
There was a problem hiding this comment.
The default flag values assume the binary is run from within scripts/todo-gen (root="..", out="../docs/..."), but the Taskfile runs it from the repo root with explicit flags. If someone runs go run ./scripts/todo-gen/main.go without flags, it will walk the parent directory and write outside the repo. Consider defaulting -root to "." and -out to "docs/exec-plans/TODO.md" to make the tool safe to run from the repo root.
docs/exec-plans/README.md
Outdated
|
|
||
| ## 拟定更改 (Proposed Changes) | ||
|
|
||
| 按组件名称(如具体的 package、功能区或依赖层)将将要修改的文件分组归类,并按逻辑顺序(例如先依赖后实现)展开描述。建议使用水平分割线来区分不同组件。 |
There was a problem hiding this comment.
存在重复用词,建议将“将将要修改”改为“将要修改”。
| 按组件名称(如具体的 package、功能区或依赖层)将将要修改的文件分组归类,并按逻辑顺序(例如先依赖后实现)展开描述。建议使用水平分割线来区分不同组件。 | |
| 按组件名称(如具体的 package、功能区或依赖层)将要修改的文件分组归类,并按逻辑顺序(例如先依赖后实现)展开描述。建议使用水平分割线来区分不同组件。 |
背景
本 PR 主要聚焦质量改进与可维护性提升,不引入新的业务功能。目标是统一错误语义、补齐关键测试、规范工程流程,并整理文档结构。
主要改动
影响评估
验证建议