# 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 前有备份。