Files
AquaControlAI/开发文档/三文档一致性与冲突审查报告.md
T
2026-07-11 18:04:51 +08:00

30 KiB
Raw Blame History

三文档一致性与冲突审查报告

1. 审查对象

  • spec-数据管理.mdv1.1,最后更新 2026-07-09
  • spec-历史数据.mdv1.0,最后更新 2026-07-09
  • code-standards.mdv1.1,最后更新 2026-07-11

本报告按本地文件实际行号定位。严重级别定义:

  • 阻断:按现有文档无法得到唯一、可运行或安全的实现,应在开发前解决。
  • :会造成接口不兼容、数据错误、安全问题或大范围返工。
  • :实现可以继续,但不同团队很可能产生不同解释。
  • :主要影响命名、示例准确性或长期维护。

2. 总体结论

共发现 38 项需要协调的问题,其中:

  • 阻断:4 项
  • 高:12 项
  • 中:18 项
  • 低:4 项

最需要优先解决的主题是:

  1. 服务进程模型与运行时内存状态的访问方式不一致。
  2. 历史模块直接读取数据管理模块数据库,违反模块自治要求。
  3. TDengine 中 UUID 字段长度与 API/PG UUID 表达不兼容。
  4. 表格查询间隔规则自相矛盾,且缺少实现所需的 history_interval
  5. 写入接口允许客户端自报操作者,和鉴权规范冲突。
  6. CSV、统一响应包、错误码、质量戳存在多套互不兼容的约定。
  7. 历史数据对禁用/删除点位不可访问,与“展示所有已存储历史数据”冲突。

3. 冲突明细

A. 架构与模块边界

C-01|阻断|历史模块直接访问数据管理内部数据,违反模块自治

位置

  • spec-历史数据.md L30-L37:历史模块直接查询 PostgreSQL 设备、采集点配置。
  • spec-历史数据.md L133-L139、L221-L223:直接读取 collection_points 构建树。
  • code-standards.md L29-L33:模块间通过 API 通信,不直接访问其他模块内部数据。
  • code-standards.md L614-L620:代码审查要求检查跨模块 Repo 调用。

冲突内容

历史规格把 collection_pointsdevices 当作历史模块可直接查询的数据源;代码规范则要求模块之间只能通过 API 通信。

影响

  • 历史模块会与数据管理表结构强耦合。
  • 数据管理字段或逻辑删除规则变化会直接破坏历史模块。
  • 无法明确历史 Service 应调用数据管理 API、共享领域服务,还是共享 Repository。

建议

二选一并写入架构决策:

  1. 模块化单体方案:允许 Service 通过共享只读领域接口访问配置数据,并删除“模块间只能通过 API”的绝对要求。
  2. 服务化方案:历史模块通过数据管理内部 API/客户端获取点位元数据,禁止直接读表。

C-02|阻断|server/collector 分进程结构与“直接共享内存单例”冲突

位置

  • code-standards.md L71-L76:存在独立的 cmd/servercmd/collector 可执行程序。
  • spec-数据管理.md L439-L443:最新值维护在采集引擎内存。
  • spec-数据管理.md L1024-L1051:采集引擎单例运行在服务端进程中,Handler 直接获得实例引用。
  • spec-数据管理.md L325-L329:设备 API 从采集引擎运行时状态组装 connection_status

冲突内容

目录规范表明 Web 服务和采集器是两个独立进程;数据管理规格却要求 API Handler 直接引用采集引擎内存对象。独立进程不能直接共享 Go 内存。

影响

latest_valueconnection_status 等接口无法按文档实现,除非实际部署结构与目录规范不同。

建议

明确唯一运行模型:

  • 若采集器嵌入 Web 服务:删除独立 cmd/collector,或说明它仅用于离线/独立部署。
  • 若保持双进程:引入 Redis、MQTT、NATS、数据库状态表或内部 RPC;Web 服务不得直接引用 CollectorManager。

C-03|高|Handler 直接访问采集引擎,违反三层架构

位置

  • spec-数据管理.md L325-L329、L1024-L1051API Handler 直接查询引擎实例并组装状态。
  • code-standards.md L165-L180Handler → Service → Repository 单向调用。
  • code-standards.md L184-L203Handler 仅做参数校验和响应转换。

冲突内容

运行时状态查询属于业务聚合逻辑,应由 Service 完成;规格明确要求 Handler 直接访问 Engine。

影响

Handler 与运行时实现耦合,单元测试和未来进程拆分困难。

建议

定义 RuntimeStatusProvider 接口,由 Service 注入;Handler 只调用 Service。


C-04|高|历史“最新值”的数据源和语义未统一

位置

  • spec-数据管理.md L439-L454latest_value 明确定义为采集引擎内存中的实时值。
  • spec-历史数据.md L141-L159、L221-L273:历史树也返回同名 latest_value
  • spec-历史数据.md L32-L37:历史模块声明的数据源只有 PostgreSQL 和 TDengine。

冲突内容

历史模块没有说明如何取得实时内存值。若从 TDengine 读取,它受 history_interval 影响,不再是数据管理接口定义的“实时最新值”;若从采集引擎读取,又违反当前模块边界和分进程结构。

影响

同名字段在两个接口中可能代表不同时间新鲜度。

建议

明确命名和来源:

  • 实时缓存值:realtime_value,由运行时状态服务提供。
  • TDengine 最新存储值:latest_stored_value,并返回 stored_at/延迟说明。

B. 历史数据可见性与业务规则

C-05|高|“展示所有已存储历史数据”与禁用/删除过滤冲突

位置

  • spec-历史数据.md L23-L28:模块负责展示所有已存储历史时序数据。
  • spec-历史数据.md L99-L109:树仅显示点位和设备均启用、未删除的点。
  • spec-数据管理.md L142-L150:逻辑删除时数据保留。
  • spec-数据管理.md L243-L247:删除设备会逻辑删除其点位,但 TDengine 历史数据未删除。

冲突内容

禁用或逻辑删除后,历史数据仍保留,但点位从树中消失,用户无法再查询这些已存储数据。

影响

历史追溯、事故审计和删除前数据查看不可实现。

建议

历史树应区分:

  • 活跃点位;
  • 已禁用点位;
  • 已删除/归档点位。

至少提供“包含归档点位”筛选,并禁止采集但允许只读历史查询。


C-06|中|树声明“可用历史数据点”,实际只检查配置,不检查是否存在历史数据

位置

  • spec-历史数据.md L133-L139:只显示包含“可用历史数据点”的分组。
  • spec-历史数据.md L99-L109:判定条件仅为 enabled/deleted/store_history。
  • spec-数据管理.md L1017-L1022:子表和数据只有实际写入时才产生。

冲突内容

刚启用 store_history、尚未首次写入的点位也会进入树,但并不存在历史数据。

影响

“可用”含义不一致,用户勾选后可能得到空结果。

建议

将术语改为“已配置历史存储的点位”,或额外检查 TDengine 子表/首条数据是否存在,并返回 has_history_data


C-07|中|树过滤规则与查询接口校验规则不一致

位置

  • spec-历史数据.md L99-L109:树过滤 enabled/deleted/store_history。
  • spec-历史数据.md L302-L311、L371-L383:查询仅根据 point_ids 映射子表,没有说明重复校验业务状态。

冲突内容

客户端可绕过树,直接传入禁用、删除或 store_history=false 的旧点位 ID。

影响

UI 和 API 的业务边界不一致;可能访问本应隐藏的数据。

建议

明确查询权限策略:是否允许归档历史查询。Service 必须根据该策略重新校验点位,不信任前端树。


C. 数据模型与数据格式

C-08|阻断|TDengine UUID 字段长度与 PostgreSQL/API UUID 不兼容

位置

  • spec-数据管理.md L58-L63、L337-L346:主键为 PostgreSQL UUID
  • spec-历史数据.md L64-L74point_iddevice_idVARCHAR(32)
  • spec-数据管理.md L989-L1001:同样使用 VARCHAR(32)
  • spec-数据管理.md L1006-L1014:仅“子表名”明确去除 UUID 连字符。
  • API 示例普遍使用带连字符的 UUID/UUID 语义。

冲突内容

标准 UUID 字符串带连字符时长度为 36;VARCHAR(32) 只能保存去连字符格式。文档只规定子表名去连字符,没有规定普通字段和 TAG 也去连字符。

影响

插入失败、截断、查询无法匹配 PostgreSQL ID,属于数据完整性阻断问题。

建议

统一一种方案:

  • 推荐 TDengine point_iddevice_id 改为 VARCHAR(36)API/PG/TD 全部使用标准 UUID 字符串;
  • 子表名使用固定安全前缀加 32 位无连字符形式,例如 p_<uuid32>

C-09|高|可变名称/设备/数据类型与 TDengine 冗余字段、TAGS 更新规则缺失

位置

  • spec-数据管理.md L235-L241、L504-L506:允许修改设备和采集点配置。
  • spec-数据管理.md L991-L1001TDengine 保存 point_namedevice_namedata_type
  • spec-数据管理.md L1004-L1014:子表按点位 ID 创建,TAGS 在创建时写入。
  • spec-历史数据.md L141-L159、L314-L344:历史接口返回名称和数据类型。

冲突内容

点位改名、换设备、改数据类型后,PostgreSQL 当前配置与 TDengine 历史行/TAGS 可能不同;文档没有规定更新 TAG、保留历史名称还是使用当前名称。

影响

同一条曲线可能显示错误设备名或数据类型;历史审计失去“当时配置”。

建议

明确元数据版本策略:

  • 历史响应默认使用当前配置,但另存 recorded_point_name
  • 或配置变更生成新点位 ID/新子表;
  • 若允许更新 TAG,写明 TDengine 更新流程及失败补偿。

C-10|高|表格要求显示“单位”,但数据模型和 API 没有单位字段

位置

  • spec-历史数据.md L665-L674:列标题为 点位名称(单位)
  • spec-数据管理.md L337-L363collection_pointsunit
  • spec-历史数据.md L141-L159、L314-L439:树、曲线和表格响应均无 unit

冲突内容

前端要求无法由任何已定义数据源实现。

影响

不同前端可能硬编码单位、忽略单位或自行推断,产生错误展示。

建议

collection_points 增加 unit VARCHAR(...),并在树、曲线、表格 API 元数据中返回;或删除单位要求。


C-11|中|树节点属性定义与实际 API 响应不一致

位置

  • spec-历史数据.md L141-L159:点位节点要求包含 group_name
  • spec-历史数据.md L225-L273GET /history/tree 的点位对象没有 group_name

冲突内容

同一文档对同一对象给出两种结构。

影响

前后端类型定义不一致。

建议

补齐 group_name,或从节点属性要求中删除并说明可由父节点推导。


C-12|中|质量缺失状态 none 与历史 API 的 null 表达冲突

位置

  • spec-数据管理.md L943-L952:无记录时 API 质量为 none,前端始终处理字符串质量戳。
  • code-standards.md L466-L477:重复同一约定。
  • spec-历史数据.md L378-L383、L394-L439:未匹配数据直接返回数组元素 null
  • spec-历史数据.md L467-L471CSV 中用

冲突内容

缺失数据到底是:

  • { "value": null, "quality": "none" }
  • 整个元素为 null
  • 或完全没有点;

文档没有统一。

影响

前端类型、图表 gap 处理、CSV 转换会出现分支差异。

建议

统一为显式对象,例如:

{ "value": null, "quality": "none", "matched_ts": null }

曲线原始序列可继续用“无记录即无点”,但表格对齐结果应保持固定对象结构。


C-13|中|坏质量数据是否必须有数值未定义

位置

  • spec-数据管理.md L917-L935、L943-L949:断线、读取失败、null、解析异常均为 bad。
  • spec-历史数据.md L327-L331bad 示例仍有数值。
  • spec-历史数据.md L590-L597:连续 bad 数据要求画虚线。

冲突内容

读取失败或 null 时通常没有可绘制数值;历史曲线规则却假设 bad 点有数值并可连接。

影响

实现者可能使用上次值、0、null 或丢点,曲线结果完全不同。

建议

定义 bad 子类型或值策略:

  • bad_with_value:有可疑数值,可虚线绘制;
  • bad_no_value:值为 null,形成断线;
  • 禁止用 0 或上次值隐式补齐,除非响应显式标注。

D. 历史查询接口与算法

C-14|阻断|interval_minutes 约束自相矛盾

位置

  • spec-历史数据.md L362-L367:范围 1~1440,“必须能被60整除”,同时建议 1、2、5、10、15、20、30、60。
  • spec-历史数据.md L658-L663:前端选项仅上述 8 个值。

冲突内容

中文“必须能被 60 整除”通常表示 interval % 60 == 0,则 1、2、5、10 等均不满足。文档实际可能想表达“必须是 60 的约数”。

影响

后端校验会产生两种完全不同的实现。

建议

直接定义枚举,避免自然语言:

interval_minutes ∈ {1, 2, 5, 10, 15, 20, 30, 60}

若确需支持到 1440,应给出完整规则和前端选项。


C-15|高|前端需要 history_interval,树接口却不返回

位置

  • spec-历史数据.md L369:前端应根据已选点位最大的 history_interval 限制或提示。
  • spec-历史数据.md L141-L159、L225-L273:树节点不含 history_interval
  • spec-数据管理.md L355-L357:该字段只存在于采集点配置。

冲突内容

历史页面没有已定义接口可获得每个勾选点位的 history_interval

影响

前端只能忽略要求,或逐点调用数据管理详情接口,造成跨模块依赖和 N+1 请求。

建议

在历史树节点中增加 history_interval,或增加批量元数据接口。


C-16|高|history_interval 无上限,与历史表格可选范围不兼容

位置

  • spec-数据管理.md L355-L357、L491-L499:仅规定最小值 1 分钟,没有上限。
  • spec-历史数据.md L367:表格 API 最大 1440 分钟。
  • spec-历史数据.md L662:前端最大选项仅 60 分钟。
  • spec-历史数据.md L369:建议表格间隔不小于最大 history_interval

冲突内容

若点位 history_interval=120,前端没有可选值满足建议;若大于 1440,连 API 都无法满足。

影响

合法的数据管理配置会产生无法正确展示的历史点位。

建议

统一约束。推荐:

  • history_interval 枚举与历史表格可选间隔共享;
  • 或前端动态生成可选值并允许到 1440;
  • 数据管理 API 增加最大值校验。

C-17|高|代码规范要求最大 31 天,历史接口未定义该限制

位置

  • code-standards.md L571-L579:时间范围最大跨度不超过 31 天。
  • spec-历史数据.md L284-L300、L349-L367:只要求起止时间,未规定顺序和最大跨度。
  • spec-历史数据.md L498-L508:自定义时间范围未规定上限。

冲突内容

规格可被理解为允许任意时间跨度,代码规范要求拒绝超过 31 天。

影响

前后端校验不一致;大查询可能导致内存和网络压力。

建议

在三个历史接口中明确:

  • end_time > start_time
  • 最大 31 天;
  • 超限错误码;
  • 导出是否允许更长范围以及异步策略。

C-18|高|动态子表 SQL 与“禁止字符串拼接 SQL”冲突

位置

  • spec-历史数据.md L302-L311FROM {subtable_name} 动态替换表名。
  • code-standards.md L222-L226、L562-L569:必须参数化,禁止字符串拼接 SQL。

冲突内容

表名通常不能作为普通参数绑定;历史规格又要求动态表名。

影响

开发者可能直接拼接用户可影响的标识,形成 SQL 注入风险,或违反代码审查规则。

建议

为动态标识符增加明确例外和安全实现:

  1. 只接受数据库查询得到的 UUID
  2. 服务端派生固定格式 p_<uuid32>
  3. 使用严格正则白名单;
  4. 值条件仍使用参数绑定;
  5. 禁止直接使用请求中的表名。

C-19|中|最近邻窗口与原始查询边界不一致

位置

  • spec-历史数据.md L375:只查询 start_time ~ end_time
  • spec-历史数据.md L378-L382:每个目标点在 ± interval/2 窗口内寻找最近值。

冲突内容

第一个目标点可能需要 start_time 之前的数据,最后一个目标点可能需要 end_time 之后的数据;当前查询范围把这些候选值排除。

影响

边界行会出现不必要的 null,与“最近邻窗口”定义不符。

建议

原始查询范围扩展为:

[start_time - window, end_time + window]

最终输出仍只保留目标时间序列。


C-20|中|最近邻算法缺少并列、复用和非整除终点规则

位置

  • spec-历史数据.md L373-L383。

缺失内容

  • 前后两个点距离相同,选前还是选后;
  • 同一原始点能否匹配两个目标时间;
  • end_time-start_time 不是 interval 整数倍时是否追加 end_time
  • bad 与 good 距离相同时是否优先 good。

影响

不同后端实现会返回不同表格和 CSV。

建议

补充确定性规则,并建立算法测试样例。


C-21|高|曲线原始查询缺少降采样/结果上限,难以满足性能规范

位置

  • spec-历史数据.md L298-L311:最多 20 点,但返回范围内全部原始数据。
  • spec-数据管理.md L355:采集周期最小 1 秒。
  • code-standards.md L631-L638:要求审查分页、索引和连接池等性能问题。
  • code-standards.md L577:最大跨度仍可达 31 天。

问题

单个 1 秒点位 31 天约 267 万条;20 点可能超过 5300 万条。API 没有 max_points、降采样或服务端聚合。

影响

内存、TDengine 查询、JSON 序列化、浏览器 ECharts 都可能失控。

建议

根据像素宽度/最大点数服务端降采样,或要求客户端传 max_samples;原始明细导出采用流式响应。


C-22|中|Y 轴“后端转换”方案与 API 响应结构不匹配

位置

  • spec-历史数据.md L553-L577:推荐后端把原始值转换为显示坐标。
  • spec-历史数据.md L314-L344:曲线 API 只返回原始 value,没有 display_value 或映射元数据。

冲突内容

推荐方案没有数据合同支持,前端无法同时绘制映射值和显示原始 tooltip。

建议

确定唯一职责:

  • 推荐纯前端映射;或
  • API 返回 display_value、原始 value 和分段映射定义。

E. REST API、错误和安全

C-23|高|所有 API 统一 JSON 包装与 CSV 文件响应冲突

位置

  • code-standards.md L360-L376:“所有 API 响应”统一为 {code,message,data}
  • spec-数据管理.md L249-L273、L591-L605:导出直接返回 CSV。
  • spec-历史数据.md L442-L471:导出直接返回 CSV。

冲突内容

文件下载不可能同时是原始 CSV 和 JSON 包装。

建议

在代码规范中增加明确例外:文件流、SSE、WebSocket 不使用统一 JSON 包装;错误响应仍返回统一 JSON。


C-24|高|写入失败/超时仍返回 code=0message=success

位置

  • spec-数据管理.md L805-L836result=failed/timeout,但 envelope 仍为成功。
  • code-standards.md L372-L376、L393-L403code=0 表示成功,非 0 表示业务错误。

冲突内容

同一响应同时表示“成功”和“写入失败”。

影响

通用请求层可能把失败当作成功;告警和重试逻辑不可靠。

建议

区分两类情况:

  • 请求成功且设备写入成功:code=0
  • 请求已处理但设备写入失败:使用明确业务错误码,HTTP 409/422/502/504 按原因选择,同时 data 可附审计记录 ID。

C-25|高|写入请求允许客户端指定 operator,与鉴权规范冲突

位置

  • spec-数据管理.md L736-L767:客户端提交 sourceoperator
  • spec-数据管理.md L1092-L1098:写入需要登录权限。
  • code-standards.md L581-L586:人工写入需登录,自动写入需 API Token。

冲突内容

已鉴权身份应由服务端从登录会话或 Token 推导;允许请求体任意填写 operator 会导致身份伪造。source 同样不应完全信任客户端声明。

影响

写入审计日志不可可信,存在严重审计与安全风险。

建议

  • 从认证上下文生成 operator 和调用方类型;
  • 请求体删除 operator,必要时仅保留 operator_display_name 作为非权威备注;
  • manual/auto 使用不同凭证或不同内部路由。

C-26|中|写入日志错误字段命名不一致

位置

  • spec-数据管理.md L719-L723:日志列表返回 error_message
  • spec-数据管理.md L805-L836:写入结果返回 error
  • spec-数据管理.md L851-L855:数据库字段为 error_message
  • code-standards.md L29-L30:同一概念跨层命名一致。

影响

前端需要两套字段,Go DTO 和模型映射易混乱。

建议

统一为 error_message,或明确 error 是标准错误对象而不是字符串。


C-27|中|写入值在不同 API 中类型不一致

位置

  • spec-数据管理.md L719-L720:日志列表的 target_valuereadback_value 是字符串。
  • spec-数据管理.md L790-L800:写入响应 valuereadback_value 是数字。
  • spec-数据管理.md L851-L853:数据库统一存 TEXT。

影响

同一业务值在接口间不能复用类型,BOOL/INT/REAL 转换规则不清楚。

建议

日志 API 返回:

{
  "data_type": "REAL",
  "target_value": 20.5,
  "readback_value": 20.5,
  "raw_target_value": "20.5"
}

或统一使用可辨别联合类型。


C-28|中|日志查询筛选值遗漏 timeout

位置

  • spec-数据管理.md L693-L695result 仅说明 success/failed。
  • spec-数据管理.md L822-L835:存在 timeout。
  • spec-数据管理.md L854:数据库枚举包含 timeout。

建议

筛选枚举统一为 success | failed | timeout


C-29|高|错误码分类和模块分段无法同时成立

位置

  • code-standards.md L395-L403400xx=参数、401xx=鉴权、403xx=权限、404xx=未找到、409xx=冲突。
  • code-standards.md L405-L414:采集点=4100141999,写入点=4200142999,历史=43001~43999。

冲突内容

例如历史数据参数错误按类型应是 400xx,但按模块必须是 430xx;历史数据未找到按类型应是 404xx,但又必须是 430xx。

影响

无法设计一致的错误码,前端不能按前缀分类。

建议

采用一种二维编码方式,例如:

  • HTTP 状态表达错误类别;
  • 业务码按模块分段;
  • 或业务码格式 模块两位 + 类别两位 + 序号,并给出算法。

C-30|中|代码规范示例直接返回 err.Error(),与安全审查项冲突

位置

  • code-standards.md L184-L199:绑定失败时把 err.Error() 返回前端。
  • code-standards.md L640-L647:禁止直接向前端泄露错误信息。

影响

开发者会复制示例,可能暴露内部字段、解析器或库错误。

建议

对外仅返回稳定校验消息;原始错误写结构化日志。


F. CSV 与文件规范

C-31|高|历史 CSV 表头不符合统一 snake_case

位置

  • spec-历史数据.md L457-L470:表头为中文“时间”、动态点位名和“_质量”。
  • code-standards.md L504-L515CSV 标题字段名统一使用 snake_case
  • spec-数据管理.md L263-L267、L595-L600:配置 CSV 使用 snake_case。

冲突内容

历史展示型 CSV 和配置导入型 CSV 实际需求不同,但代码规范没有区分。

建议

在规范中分为:

  1. 机器可导入配置 CSV:固定 snake_case。
  2. 用户展示/报表 CSV:允许本地化和动态列名,但需定义转义和重复点名处理。

C-32|高|历史 CSV 文件名使用用户输入,违反路径安全规范

位置

  • spec-历史数据.md L676-L682:文件名包含 {start_time}{end_time}{interval}
  • code-standards.md L571-L579:导出文件名不得包含用户输入。

影响

除路径穿越外,ISO 时间中的冒号、加号在部分系统不适合作为文件名。

建议

服务端解析并重新格式化为安全值,例如:

history_20260709T000000_20260709T010000_10m.csv

只使用数字、ASCII 字母、下划线和短横线。


G. 前端交互和接口契约

C-33|中|模式切换“不重复请求”与“自动重新查询”冲突

位置

  • spec-历史数据.md L715-L720:切换模式“不重复请求数据,按需加载”。
  • spec-历史数据.md L740-L741:切换模式自动重新查询。

冲突内容

无法判断是每次切换都请求、首次进入某模式请求一次,还是使用缓存。

建议

明确缓存键:mode + point_ids + start_time + end_time + interval。只有缓存不存在或参数变化时请求。


C-34|中|20 点上限同时被描述为建议和强制

位置

  • spec-历史数据.md L164-L171:建议最多 20。
  • spec-历史数据.md L296-L300、L362-L367、L748-L753API/附录要求最多 20。

建议

统一为强制规则;前端在第 21 个点时阻止并提示,后端仍校验。


C-35|中|设备 PUT 声称与新增同结构,但新增结构没有 enabled

位置

  • spec-数据管理.md L182-L198:新增请求体无 enabled
  • spec-数据管理.md L235-L241:PUT 与新增相同,同时说明可修改 enabled

影响

无法按请求合同执行启用/禁用。

建议

使用独立 UpdateDeviceRequest,明确字段可选性;或增加专用动作接口 /devices/{id}/enable/disable


C-36|中|“立即生效”与 5 秒轮询实现不一致

位置

  • spec-数据管理.md L243-L247、L504-L511、L970-L981:删除/变更后立即停止或动态更新。
  • spec-数据管理.md L983:可每 5 秒轮询数据库。

冲突内容

轮询方案最多延迟约 5 秒,不是“立即”。

建议

定义 SLA,例如 5 秒内生效;若必须立即,使用事务后事件、LISTEN/NOTIFY 或消息总线。


H. 命名、代码风格和内部标准

C-37|中|索引命名不符合 idx_{表名}_{字段名}

位置

  • code-standards.md L432-L442:索引命名要求包含实际字段名。
  • spec-数据管理.md L368-L372idx_collection_points_device/group,实际字段为 device_id/group_name
  • spec-数据管理.md L643-L645idx_write_points_device/group
  • spec-数据管理.md L863-L866idx_write_logs_point/device/time,实际字段为 point_id/device_id/created_at

建议

改为:

  • idx_collection_points_device_id
  • idx_collection_points_group_name
  • idx_write_logs_created_at
  • 其他同理

或放宽规范,明确允许语义化简称。


C-38|低|核心类型命名 CollectPointCollectionPoint 不一致

位置

  • spec-数据管理.md L39-L46:概念图使用 CollectPoint
  • code-standards.md L664-L669Go 模型使用 CollectionPoint
  • API/table/package 也使用 collection

建议

统一为 CollectionPoint,避免出现第三种命名。


4. 其他需要补充但尚不足以判定为直接冲突的事项

以下问题属于规格缺失或高风险歧义,建议一并处理:

  1. spec-数据管理.md L926-L929 使用“值在合理范围内”判定 good,但数据模型没有合理范围、量程或校验表达式字段。
  2. Modbus byte_orderword_order 的职责存在重叠;CDAB 已涉及字交换,同时又提供 word_order=BA,组合语义需要重新定义。
  3. 历史查询同一原始点是否可匹配多个表格时间点未规定。
  4. 历史树、分组、点位的排序规则未规定。
  5. 最新值不存在、采集器未运行或缓存过期时,latest_valuenull、省略还是返回 quality=none 未规定。
  6. 导出大文件是否流式传输、是否设置 Content-Disposition、超时和最大行数未规定。
  7. history_retention_days 只有参数说明,没有系统配置存储模型、API、权限和变更失败补偿。
  8. CREATE/ALTER DATABASE aquacontrolai 使用固定数据库名,而代码规范要求连接参数环境化;需明确数据库名是否也是环境配置。
  9. 数据管理导出请求示例含 // 可选 注释,不是合法 JSON。
  10. 代码规范日志示例用 INFO 记录“连接成功”,但日志埋点表把“设备连接/断开”统一定为 WARN;应拆分成功 INFO、断开/失败 WARN。

5. 建议的协调优先级

第一批:开发前必须定稿

  1. C-02 服务进程模型和运行时状态传输。
  2. C-01 模块边界和历史元数据访问方式。
  3. C-08 UUID 存储格式。
  4. C-14/C-15/C-16 表格间隔与 history_interval 合同。
  5. C-25 写入身份来源与鉴权。
  6. C-24/C-29 错误语义和错误码体系。
  7. C-05 归档历史数据可见性。

第二批:接口冻结前完成

  1. C-10 单位字段。
  2. C-12 质量缺失表示。
  3. C-17/C-21 时间范围、降采样、结果上限。
  4. C-23 文件响应例外。
  5. C-31/C-32 CSV 两类规范和安全文件名。
  6. C-09 TDengine 元数据变更策略。

第三批:编码规范和文档清理

  1. C-33 至 C-38。
  2. 补齐排序、空值、超时、流式导出和配置变更 SLA。
  3. 为关键算法增加契约测试样例。

6. 推荐的统一决策摘要

建议最终统一为以下基线:

  • Web API 与 Collector 保持双进程,通过内部状态服务或消息/缓存同步状态。
  • 历史模块通过只读领域接口取得点位元数据,不直接依赖数据管理 Repo。
  • UUID 在 API、PG、TDengine 字段中统一使用 36 字符标准形式;仅表名使用 p_<uuid32>
  • 历史树返回 history_intervalunitarchivedhas_history_data
  • 表格间隔采用明确枚举;缺失值统一 {value:null, quality:"none"}
  • 历史查询最大 31 天并强制降采样/最大点数。
  • 写入操作者从认证上下文生成,失败使用非零业务码。
  • JSON API 使用统一 envelopeCSV 文件流为明确例外。
  • 配置 CSV 使用固定 snake_case;历史报表 CSV 允许本地化动态表头。