# YMhut Unified Management Backend Architecture Review

## 结论

`server/unified-management` 目前已经具备统一服务的基本形态：一个 Go 后端同时承接旧版更新 JSON、下载、媒体源、反馈提交、新客户端 bootstrap、管理后台、SQLite/MySQL 存储和旧项目同步。现有 `go test ./...` 全部通过，说明核心兼容路由和主要模块有基本回归保障。

但当前实现仍属于“快速整合型单体”：`internal/web/router.go` 和 `internal/db/store.go` 承担了过多职责，后台能力已经铺开，但业务边界、数据访问边界、兼容适配边界还不够清晰。继续扩展前，应先把后台架构拆成稳定的分层单体，保留旧访问方式和旧反馈协议作为独立兼容层。

## 当前主要问题

1. 路由层过重

   `internal/web/router.go` 同时处理公共门户、旧版路由、客户端 API、后台 API、静态资源、错误本地化、SSE、CSV 导出和文件下载。后续新增后台功能时，容易出现路径匹配顺序冲突、权限遗漏和响应结构不一致。

2. 数据层过重

   `internal/db/store.go` 同时包含表结构迁移、实体定义、Repository、密码哈希、业务默认值、统计聚合、SQLite/MySQL failover、全表同步和旧原型 JSON 导入。它已经变成“全系统共享内核”，任何改动都容易影响多个业务域。

3. 兼容逻辑与新业务逻辑混杂

   旧版 `update-info.json`、`media-types.json`、`tool-status.json`、`modules.json`、`/downloads/*` 和旧反馈 `POST /` 都是必须保留的外部契约，但现在它们和新 API 共享服务/存储细节，长期看会让新后台被旧 JSON 格式牵着走。

4. 安全策略需要后台化

   当前有验证码、HttpOnly session cookie、CSRF、上传包校验和路径逃逸防护，这是好基础。但后台还需要更明确的密码策略、登录失败限流、会话持久化/吊销、审计事件规范、管理员角色模型。当前密码哈希是静态 SHA-256，建议迁移到 Argon2id 或 bcrypt。

5. 数据库双写/同步语义不清

   配置里有 failover 和 hot sync 字段，但当前更接近手动全表复制和运行时 MySQL 失败回退 SQLite。需要明确“单主数据库、备份数据库、灾备同步”的关系，否则后台管理员很难判断哪些操作会覆盖数据。

6. 前端后台状态集中

   `web/admin/src/App.vue` 集中了 API client、页面路由、全局状态、业务动作、表单转换、错误翻译和 SSE 连接。后台页面已经很多，建议拆成 `api/`、`stores/`、`features/`，否则维护体验会快速变差。

## 推荐后台架构

保持 Go 单体部署，但按边界拆成分层模块：

```text
cmd/unified-management
  app/                       # 进程启动、依赖组装、优雅退出

internal/http
  middleware/                # auth、csrf、security headers、request id、rate limit
  legacy/                    # 旧访问方式适配器
  clientapi/                 # 新客户端 API
  adminapi/                  # 后台管理 API
  setupapi/                  # 首次初始化 API
  static/                    # admin/portal/setup 前端资源

internal/domain
  feedback/                  # 反馈工单聚合、状态流转、附件规则
  releases/                  # 发布包、版本公告、manifest
  sources/                   # 数据源目录、健康检查、调用日志
  compatibility/             # 旧 JSON 文档模型和转换
  audit/                     # 审计事件
  admin/                     # 管理员、角色、会话
  system/                    # 健康检查、配置、数据库状态

internal/storage
  migrations/                # schema 和版本迁移
  repos/                     # 按领域拆分 Repository
  sqlstore/                  # SQLite/MySQL 方言、连接、事务
  sync/                      # SQLite/MySQL 同步策略

internal/jobs
  sourcecheck/               # 数据源健康检测
  legacysync/                # 旧项目导入/同步
  cleanup/                   # 过期会话、旧日志、临时文件清理

internal/contracts
  legacy/                    # 旧客户端响应 DTO
  client/                    # 新客户端响应 DTO
  admin/                     # 后台响应 DTO
```

核心原则：

- `legacy` 只负责旧协议输入输出，不直接写业务表细节。
- `domain` 负责业务规则，不依赖 HTTP request/response。
- `storage` 负责持久化和事务，不包含业务默认文案、状态流转和 UI 语义。
- `adminapi` 是后台编排层，只调用 domain service，不直接拼 SQL。
- 所有对外 JSON 响应都定义 DTO，避免直接暴露数据库结构。

## 必须保留的旧版访问契约

这些路径应作为长期兼容 API，不随后台重构而改变：

| 旧版入口 | 当前用途 | 建议归属 |
| --- | --- | --- |
| `GET /update-info.json`、`GET /update-info` | 旧客户端更新信息 | `internal/http/legacy/update.go` |
| `GET /tool-status.json`、`GET /tool-status` | 旧工具状态 | `internal/http/legacy/static_json.go` |
| `GET /modules.json`、`GET /modules`、`GET /api/modules` | 旧模块配置 | `internal/http/legacy/static_json.go` |
| `GET /media-types.json`、`GET /media-types` | 旧媒体源目录 | `internal/http/legacy/media_types.go` |
| `GET /downloads/:filename` | 旧下载包 | `internal/http/legacy/downloads.go` |
| `POST /` | 旧反馈提交 | `internal/http/legacy/feedback.go` |
| `GET /?api=status&code=:code` | 旧反馈状态查询 | `internal/http/legacy/feedback.go` |

兼容层的响应字段应保持“只增不删、不改名、不改含义”。新后台可以增加内部字段，但旧接口输出必须通过 legacy DTO 过滤，避免把后台工单详情、内部备注、附件路径等泄漏给旧客户端。

## 旧版反馈兼容设计

旧反馈应当分为三个入口模型：

1. 简单 JSON 表单

   兼容旧客户端或网页直接提交 `title/type/severity/contact/body/message`。服务端生成反馈码，返回旧状态结构。

2. 普通 multipart 表单

   兼容旧表单字段 `title/subject/category/priority/message/description/email`。如果没有签名字段，按简单反馈处理。

3. 签名加密包 multipart

   继续保留 `payload/timestamp/nonce/packageSha256/signature/package`。校验顺序固定为：请求大小 -> 时间窗 -> SHA256 格式 -> HMAC 签名 -> 加密包 magic -> 包哈希 -> 解密 -> zip 安全检查 -> 写入附件 -> 创建工单。

后台内部统一落到 `feedback_tickets` 聚合：

```text
legacy feedback request
  -> LegacyFeedbackAdapter
  -> FeedbackCommand(CreateTicket)
  -> FeedbackService
  -> FeedbackRepository + AttachmentStorage
  -> LegacyFeedbackStatusDTO
```

旧状态查询只返回：

- `ok`
- `code`
- `status`
- `statusLabel`
- `statusDetail`
- `category`
- `priority`
- `hasReply`
- `reply`
- `receivedAt`
- `updatedAt`
- `mailSent`
- `duplicate`

不返回内部 `note`、`assignee`、`handledBy`、本地文件路径、审计日志、mail record。

## 后台管理能力设计

后台建议分为以下域：

1. 仪表盘

   提供反馈数量、今日反馈、数据源总数、可见源数量、发布版本、数据库状态、最近心跳、最近客户端调用。只读，适合高频刷新。

2. 反馈工单

   支持分页、筛选、搜索、状态流转、公开回复、内部备注、评论、标签、分派、优先级、SLA、附件查看、CSV 导出。状态流转应集中在 `FeedbackService`，不要由 Store 直接决定。

3. 发布管理

   管理发布包上传、版本号、平台/架构、SHA256、manifest 生成、`update-info.json` 同步、版本公告保存、公告历史恢复。

4. 兼容 JSON

   管理 `update-info.json` 和 `media-types.json`，提供验证、预览、保存、修订历史、恢复。该模块属于兼容层后台，不应成为新客户端的唯一数据来源。

5. 数据源目录

   管理媒体/数据源、健康检测、客户端可见性、缓存时间、代理策略、调用日志。健康检测任务应进入 jobs 模块，并持久化任务结果。

6. 数据库与同步

   明确 SQLite/MySQL 角色：推荐 SQLite 单机默认，MySQL 生产主库，SQLite 作为本地备份。后台按钮应显示方向、影响范围、覆盖风险和最后同步结果。

7. 系统设置

   管理管理员密码、会话、旧项目路径、BaseURL、上传限制、签名密钥轮换、服务健康。

8. 审计日志

   所有后台写操作、旧项目同步、发布包上传、JSON 恢复、密码修改都写入审计。审计事件统一字段：`actor/type/target/message/ip/userAgent/createdAt`。

## 数据模型建议

将数据库实体按业务域拆分：

- `admin_users`
- `admin_sessions`
- `feedback_tickets`
- `feedback_comments`
- `feedback_attachments`
- `feedback_events`
- `release_packages`
- `release_notices`
- `release_notice_revisions`
- `legacy_json_revisions`
- `source_categories`
- `source_endpoints`
- `endpoint_health_checks`
- `endpoint_call_logs`
- `audit_logs`
- `database_sync_jobs`
- `legacy_sync_jobs`

建议新增：

- `schema_migrations`：记录数据库迁移版本。
- `admin_roles`、`admin_user_roles`：为后续多管理员预留。
- `settings`：保存可后台修改的配置项，和 `config.json` 做边界区分。
- `background_jobs`：统一记录旧同步、源检查、清理任务。
- `api_tokens`：为未来自动发布、CI 上传包、客户端管理接口预留。

## 改造优先级

第一阶段：稳住兼容契约

- 为旧版路径建立专门测试：`/update-info.json`、`/tool-status.json`、`/modules.json`、`/media-types.json`、`/downloads/*`、`POST /`、`/?api=status`。
- 把旧响应结构固定为 DTO 测试快照。
- 给旧反馈三种提交方式分别补测试。

第二阶段：拆 HTTP 层

- 把 `router.go` 拆成 `legacy_routes.go`、`client_routes.go`、`admin_feedback_routes.go`、`admin_release_routes.go`、`admin_source_routes.go`、`admin_system_routes.go`、`static_routes.go`。
- 引入统一 `RouteGroup` 或 `http.ServeMux` 包装，避免一个巨大 switch 继续增长。

第三阶段：拆 Store

- 将实体移动到 `internal/domain/*` 或 `internal/contracts/*`。
- 将 SQL 拆成 `FeedbackRepository`、`ReleaseRepository`、`SourceRepository`、`AuditRepository`、`AdminRepository`。
- 将迁移、连接、failover、sync 从业务 repo 中拆出。

第四阶段：完善后台安全

- 密码哈希迁移到 Argon2id/bcrypt，并保留旧 SHA-256 登录后自动升级。
- 登录失败限流和验证码刷新频率限制。
- 会话持久化、会话吊销、Secure cookie 配置。
- 敏感配置和密钥不在 bootstrap 或日志中明文输出。

第五阶段：前端后台模块化

- `web/admin/src/api/*`：封装后台 API。
- `web/admin/src/stores/*`：按域拆状态。
- `web/admin/src/features/*`：按页面拆业务动作。
- `App.vue` 只保留壳层、导航、路由出口和全局 toast。

## 验证清单

当前已通过：

```powershell
go test ./...
```

后续每次重构必须至少验证：

- 旧版更新 JSON 路由仍返回 200 且字段不减少。
- 旧版反馈 `POST /` 能创建工单，重复反馈码返回 duplicate。
- 旧版反馈状态查询不泄漏内部字段。
- 管理后台写操作未登录返回 401，未带 CSRF 返回 403。
- 发布包上传拒绝路径逃逸和不支持扩展名。
- 数据源健康检测拒绝非 HTTP/HTTPS 重定向。
- SQLite 默认启动正常，MySQL 配置失败时 failover 行为明确。
- 旧项目同步 dry-run 不写数据，run 前有备份。

