Files
AquaControlAI/开发文档/一致性决策基线.md
T
2026-07-12 02:42:44 +08:00

124 lines
6.9 KiB
Markdown
Raw 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.
# 污水厂智能控制平台 — 一致性决策基线
> 本文件是 `spec-数据管理.md`、`spec-历史数据.md` 和 `code-standards.md` 的跨文档最高优先级契约。出现歧义时,先按本文件执行,再修订对应文档。
## 1. 文档优先级与变更规则
1. 本文件中的“已采纳”决策优先于其他文档中的旧表述。
2. 任何 API、数据库或运行架构变更,必须同步修改三份关联文档和自动化契约测试。
3. 文档示例必须是合法 JSON、SQL、Go 或 TypeScript,不得在 JSON 代码块中写注释。
4. API 冻结后,破坏性变更必须升级 API 版本或提供兼容迁移期。
## 2. 已采纳决策
### ADR-001|运行模型与模块边界
- 生产环境使用单进程嵌入式模型:`cmd/server/main.go` 同时启动 Web API 和采集引擎。
- 模块间通过公开的 Service/领域接口通信,不通过本应用自身的 HTTP,也不得直接访问其他模块的 Repository、ORM 模型或数据库表。
- Handler 只负责参数绑定和响应转换;运行时状态、元数据聚合和权限判断均由 Service 完成。
### ADR-002|协议驱动与设备连接
- 协议注册表保存无状态 `ProtocolDriverFactory`,不保存有连接状态的驱动单例。
- 每个设备由工厂创建独立 `ProtocolConnection`;采集和写入共享该设备连接。
- 同一设备连接默认串行执行协议读写,除非驱动明确声明并通过并发测试证明支持并发。
- 采集调度采用有界 worker pool;“每点位一个任务”不等于“每点位一个 goroutine”。
### ADR-003UUID 与 TDengine 子表
- API、PostgreSQL、TDengine 普通字段和 TAG 中统一使用带连字符的 36 位标准 UUID。
- TDengine 子表名唯一格式为 `p_<uuid32>`,正则为 `^p_[0-9a-f]{32}$`
- 请求不得传入表名;表名只能由服务端对已验证 UUID 规范化后派生。
### ADR-004|动态 SQL 标识符
- 用户值必须参数化。
- 动态表名属于受控标识符例外:必须由服务端派生、通过固定正则白名单,并使用驱动提供的标识符引用能力;禁止直接拼接任何用户字符串。
### ADR-005|历史点位生命周期
- 历史模块默认展示活跃点位和仍有历史数据的归档点位。
- 活跃点位:点位和设备均未删除、均启用,且 `store_history=true`
- 归档点位:点位或设备已禁用/逻辑删除,或当前 `store_history=false`,但 TDengine 中仍存在历史数据。
- 归档点位只读、可查询、不可继续采集。
### ADR-006|历史元数据与配置变更
- `collection_points` 增加 `unit``valid_min``valid_max``history_started_at`
- 首次准备写入历史数据前设置 `history_started_at`;一旦非空,禁止修改 `device_id``data_type`。需要改变时必须新建点位。
- 点位名称、设备名称、分组和单位可以修改;历史 API 使用 PostgreSQL 当前元数据展示。
- TDengine 中的 `point_name``device_name``data_type` 是写入时快照,不是当前配置的权威来源。
### ADR-007|质量和空值模型
- API 质量枚举:`good | bad | none`
- TDengine 只存储 `good=0``bad=1``none` 表示查询无匹配记录,不落库。
- `value` 允许为 `null`:读取失败、断线或解析失败时写入 `quality=bad``value=null`
- 读取成功但超出配置有效范围时写入 `quality=bad` 且保留数值。
- TDengine 和 API 增加可空 `quality_reason`,使用稳定代码,例如 `timeout``disconnected``parse_error``out_of_range`
- 表格无匹配记录统一返回 `{value:null, quality:"none", quality_reason:null, matched_ts:null}`,不得返回整个元素为 `null`
### ADR-008|历史查询边界与降采样
- 所有历史查询要求 `end_time > start_time`,最大跨度 31 天。
- 曲线查询最多 20 个点位;`max_samples` 默认 2000,范围 100~10000。
- 原始记录超过上限时,服务端执行自适应 min-max 降采样,保留首尾、极值、质量变化和 gap 边界。
- 响应返回 `sampled``raw_count``sample_count`
### ADR-009|表格最近邻匹配
- 匹配窗口为 `interval_minutes / 2` 的精确时长,不进行整分钟向上取整。
- 原始查询范围扩展为 `[start_time-window, end_time+window]`
- 目标时间为 `start_time + n*interval` 且不晚于 `end_time`;不强制追加未对齐的 `end_time`
- 距离相同时选择较早的原始时间戳,避免使用未来值优先。
- 每个目标时间独立匹配;同一原始记录允许匹配相邻目标时间,并通过 `matched_ts` 明示。
### ADR-010API 响应和文件流
- JSON API 使用 `{code,message,data}` 包装。
- CSV、其他文件、SSE 和 WebSocket 是明确例外:成功响应直接返回对应流;失败响应仍返回统一 JSON 错误。
- 同步设备写入只有写入和回读均成功时返回 HTTP 200、`code=0`
### ADR-011HTTP 状态与业务错误码
- HTTP 状态表达错误类别;业务 `code` 表达模块内具体原因,不从数字前缀推断 HTTP 类别。
- 模块范围:通用/鉴权 `10001~10999`、设备 `40001~40999`、采集点 `41001~41999`、写入点 `42001~42999`、历史 `43001~43999`、采集引擎 `50001~50999`、写入引擎 `51001~51999`
### ADR-012CSV 分类和文件名
- 配置导入导出 CSV 使用固定 `snake_case` 标题,可再次导入。
- 用户报表 CSV 允许本地化和动态列名;重复点位名称追加 UUID 前 8 位。
- 文件名由服务端对已解析参数重新格式化,只允许 ASCII 字母、数字、下划线、短横线和点。
### ADR-013|写入值和回读
- 写入日志数据库可用 TEXT 保存原始值,但 API 必须按 `data_type` 返回 JSON boolean/number。
- `write_points` 增加 `readback_tolerance`BOOL/INT 严格相等,REAL 使用绝对误差 `abs(actual-target) <= readback_tolerance`
- 当前阶段仅支持内部网络中的未认证人工写入,服务端固定记录 `source=manual``operator=null`
### ADR-014|配置生效时限
- Service 在事务提交后发布进程内配置变更事件,正常情况下 1 秒内生效。
- 采集引擎每 5 秒执行一次全量版本校对作为丢事件后的兜底;因此对外 SLA 为 5 秒内最终生效。
### ADR-015|历史图表职责
- 历史 API 只返回原始值和元数据。
- 分段自适应 Y 轴由前端图表组件实现,必须标明非线性/断轴效果;不得把映射后的显示坐标冒充原始值。
### ADR-016|环境配置
- PostgreSQL 和 TDengine 的主机、端口、用户名、密码和数据库名均通过环境变量配置。
- TDengine 数据库名使用 `TDENGINE_DATABASE`,默认值可为 `aquacontrolai`,代码和迁移不得写死生产值。
## 3. 当前文档版本
| 文档 | 版本 |
|---|---|
| `spec-数据管理.md` | v1.2 |
| `spec-历史数据.md` | v1.1 |
| `code-standards.md` | v1.2 |
| 本基线 | v1.0 |
> 最后更新:2026-07-11