30 KiB
三文档一致性与冲突审查报告
1. 审查对象
spec-数据管理.md:v1.1,最后更新 2026-07-09spec-历史数据.md:v1.0,最后更新 2026-07-09code-standards.md:v1.1,最后更新 2026-07-11
本报告按本地文件实际行号定位。严重级别定义:
- 阻断:按现有文档无法得到唯一、可运行或安全的实现,应在开发前解决。
- 高:会造成接口不兼容、数据错误、安全问题或大范围返工。
- 中:实现可以继续,但不同团队很可能产生不同解释。
- 低:主要影响命名、示例准确性或长期维护。
2. 总体结论
共发现 38 项需要协调的问题,其中:
- 阻断:4 项
- 高:12 项
- 中:18 项
- 低:4 项
最需要优先解决的主题是:
- 服务进程模型与运行时内存状态的访问方式不一致。
- 历史模块直接读取数据管理模块数据库,违反模块自治要求。
- TDengine 中 UUID 字段长度与 API/PG UUID 表达不兼容。
- 表格查询间隔规则自相矛盾,且缺少实现所需的
history_interval。 - 写入接口允许客户端自报操作者,和鉴权规范冲突。
- CSV、统一响应包、错误码、质量戳存在多套互不兼容的约定。
- 历史数据对禁用/删除点位不可访问,与“展示所有已存储历史数据”冲突。
3. 冲突明细
A. 架构与模块边界
C-01|阻断|历史模块直接访问数据管理内部数据,违反模块自治
位置
spec-历史数据.mdL30-L37:历史模块直接查询 PostgreSQL 设备、采集点配置。spec-历史数据.mdL133-L139、L221-L223:直接读取collection_points构建树。code-standards.mdL29-L33:模块间通过 API 通信,不直接访问其他模块内部数据。code-standards.mdL614-L620:代码审查要求检查跨模块 Repo 调用。
冲突内容
历史规格把 collection_points、devices 当作历史模块可直接查询的数据源;代码规范则要求模块之间只能通过 API 通信。
影响
- 历史模块会与数据管理表结构强耦合。
- 数据管理字段或逻辑删除规则变化会直接破坏历史模块。
- 无法明确历史 Service 应调用数据管理 API、共享领域服务,还是共享 Repository。
建议
二选一并写入架构决策:
- 模块化单体方案:允许 Service 通过共享只读领域接口访问配置数据,并删除“模块间只能通过 API”的绝对要求。
- 服务化方案:历史模块通过数据管理内部 API/客户端获取点位元数据,禁止直接读表。
C-02|阻断|server/collector 分进程结构与“直接共享内存单例”冲突
位置
code-standards.mdL71-L76:存在独立的cmd/server和cmd/collector可执行程序。spec-数据管理.mdL439-L443:最新值维护在采集引擎内存。spec-数据管理.mdL1024-L1051:采集引擎单例运行在服务端进程中,Handler 直接获得实例引用。spec-数据管理.mdL325-L329:设备 API 从采集引擎运行时状态组装connection_status。
冲突内容
目录规范表明 Web 服务和采集器是两个独立进程;数据管理规格却要求 API Handler 直接引用采集引擎内存对象。独立进程不能直接共享 Go 内存。
影响
latest_value、connection_status 等接口无法按文档实现,除非实际部署结构与目录规范不同。
建议
明确唯一运行模型:
- 若采集器嵌入 Web 服务:删除独立
cmd/collector,或说明它仅用于离线/独立部署。 - 若保持双进程:引入 Redis、MQTT、NATS、数据库状态表或内部 RPC;Web 服务不得直接引用 CollectorManager。
C-03|高|Handler 直接访问采集引擎,违反三层架构
位置
spec-数据管理.mdL325-L329、L1024-L1051:API Handler 直接查询引擎实例并组装状态。code-standards.mdL165-L180:Handler → Service → Repository 单向调用。code-standards.mdL184-L203:Handler 仅做参数校验和响应转换。
冲突内容
运行时状态查询属于业务聚合逻辑,应由 Service 完成;规格明确要求 Handler 直接访问 Engine。
影响
Handler 与运行时实现耦合,单元测试和未来进程拆分困难。
建议
定义 RuntimeStatusProvider 接口,由 Service 注入;Handler 只调用 Service。
C-04|高|历史“最新值”的数据源和语义未统一
位置
spec-数据管理.mdL439-L454:latest_value明确定义为采集引擎内存中的实时值。spec-历史数据.mdL141-L159、L221-L273:历史树也返回同名latest_value。spec-历史数据.mdL32-L37:历史模块声明的数据源只有 PostgreSQL 和 TDengine。
冲突内容
历史模块没有说明如何取得实时内存值。若从 TDengine 读取,它受 history_interval 影响,不再是数据管理接口定义的“实时最新值”;若从采集引擎读取,又违反当前模块边界和分进程结构。
影响
同名字段在两个接口中可能代表不同时间新鲜度。
建议
明确命名和来源:
- 实时缓存值:
realtime_value,由运行时状态服务提供。 - TDengine 最新存储值:
latest_stored_value,并返回stored_at/延迟说明。
B. 历史数据可见性与业务规则
C-05|高|“展示所有已存储历史数据”与禁用/删除过滤冲突
位置
spec-历史数据.mdL23-L28:模块负责展示所有已存储历史时序数据。spec-历史数据.mdL99-L109:树仅显示点位和设备均启用、未删除的点。spec-数据管理.mdL142-L150:逻辑删除时数据保留。spec-数据管理.mdL243-L247:删除设备会逻辑删除其点位,但 TDengine 历史数据未删除。
冲突内容
禁用或逻辑删除后,历史数据仍保留,但点位从树中消失,用户无法再查询这些已存储数据。
影响
历史追溯、事故审计和删除前数据查看不可实现。
建议
历史树应区分:
- 活跃点位;
- 已禁用点位;
- 已删除/归档点位。
至少提供“包含归档点位”筛选,并禁止采集但允许只读历史查询。
C-06|中|树声明“可用历史数据点”,实际只检查配置,不检查是否存在历史数据
位置
spec-历史数据.mdL133-L139:只显示包含“可用历史数据点”的分组。spec-历史数据.mdL99-L109:判定条件仅为 enabled/deleted/store_history。spec-数据管理.mdL1017-L1022:子表和数据只有实际写入时才产生。
冲突内容
刚启用 store_history、尚未首次写入的点位也会进入树,但并不存在历史数据。
影响
“可用”含义不一致,用户勾选后可能得到空结果。
建议
将术语改为“已配置历史存储的点位”,或额外检查 TDengine 子表/首条数据是否存在,并返回 has_history_data。
C-07|中|树过滤规则与查询接口校验规则不一致
位置
spec-历史数据.mdL99-L109:树过滤 enabled/deleted/store_history。spec-历史数据.mdL302-L311、L371-L383:查询仅根据point_ids映射子表,没有说明重复校验业务状态。
冲突内容
客户端可绕过树,直接传入禁用、删除或 store_history=false 的旧点位 ID。
影响
UI 和 API 的业务边界不一致;可能访问本应隐藏的数据。
建议
明确查询权限策略:是否允许归档历史查询。Service 必须根据该策略重新校验点位,不信任前端树。
C. 数据模型与数据格式
C-08|阻断|TDengine UUID 字段长度与 PostgreSQL/API UUID 不兼容
位置
spec-数据管理.mdL58-L63、L337-L346:主键为 PostgreSQLUUID。spec-历史数据.mdL64-L74:point_id、device_id为VARCHAR(32)。spec-数据管理.mdL989-L1001:同样使用VARCHAR(32)。spec-数据管理.mdL1006-L1014:仅“子表名”明确去除 UUID 连字符。- API 示例普遍使用带连字符的 UUID/UUID 语义。
冲突内容
标准 UUID 字符串带连字符时长度为 36;VARCHAR(32) 只能保存去连字符格式。文档只规定子表名去连字符,没有规定普通字段和 TAG 也去连字符。
影响
插入失败、截断、查询无法匹配 PostgreSQL ID,属于数据完整性阻断问题。
建议
统一一种方案:
- 推荐 TDengine
point_id、device_id改为VARCHAR(36),API/PG/TD 全部使用标准 UUID 字符串; - 子表名使用固定安全前缀加 32 位无连字符形式,例如
p_<uuid32>。
C-09|高|可变名称/设备/数据类型与 TDengine 冗余字段、TAGS 更新规则缺失
位置
spec-数据管理.mdL235-L241、L504-L506:允许修改设备和采集点配置。spec-数据管理.mdL991-L1001:TDengine 保存point_name、device_name、data_type。spec-数据管理.mdL1004-L1014:子表按点位 ID 创建,TAGS 在创建时写入。spec-历史数据.mdL141-L159、L314-L344:历史接口返回名称和数据类型。
冲突内容
点位改名、换设备、改数据类型后,PostgreSQL 当前配置与 TDengine 历史行/TAGS 可能不同;文档没有规定更新 TAG、保留历史名称还是使用当前名称。
影响
同一条曲线可能显示错误设备名或数据类型;历史审计失去“当时配置”。
建议
明确元数据版本策略:
- 历史响应默认使用当前配置,但另存
recorded_point_name; - 或配置变更生成新点位 ID/新子表;
- 若允许更新 TAG,写明 TDengine 更新流程及失败补偿。
C-10|高|表格要求显示“单位”,但数据模型和 API 没有单位字段
位置
spec-历史数据.mdL665-L674:列标题为点位名称(单位)。spec-数据管理.mdL337-L363:collection_points无unit。spec-历史数据.mdL141-L159、L314-L439:树、曲线和表格响应均无unit。
冲突内容
前端要求无法由任何已定义数据源实现。
影响
不同前端可能硬编码单位、忽略单位或自行推断,产生错误展示。
建议
在 collection_points 增加 unit VARCHAR(...),并在树、曲线、表格 API 元数据中返回;或删除单位要求。
C-11|中|树节点属性定义与实际 API 响应不一致
位置
spec-历史数据.mdL141-L159:点位节点要求包含group_name。spec-历史数据.mdL225-L273:GET /history/tree的点位对象没有group_name。
冲突内容
同一文档对同一对象给出两种结构。
影响
前后端类型定义不一致。
建议
补齐 group_name,或从节点属性要求中删除并说明可由父节点推导。
C-12|中|质量缺失状态 none 与历史 API 的 null 表达冲突
位置
spec-数据管理.mdL943-L952:无记录时 API 质量为none,前端始终处理字符串质量戳。code-standards.mdL466-L477:重复同一约定。spec-历史数据.mdL378-L383、L394-L439:未匹配数据直接返回数组元素null。spec-历史数据.mdL467-L471:CSV 中用—。
冲突内容
缺失数据到底是:
{ "value": null, "quality": "none" };- 整个元素为
null; - 或完全没有点;
文档没有统一。
影响
前端类型、图表 gap 处理、CSV 转换会出现分支差异。
建议
统一为显式对象,例如:
{ "value": null, "quality": "none", "matched_ts": null }
曲线原始序列可继续用“无记录即无点”,但表格对齐结果应保持固定对象结构。
C-13|中|坏质量数据是否必须有数值未定义
位置
spec-数据管理.mdL917-L935、L943-L949:断线、读取失败、null、解析异常均为 bad。spec-历史数据.mdL327-L331:bad 示例仍有数值。spec-历史数据.mdL590-L597:连续 bad 数据要求画虚线。
冲突内容
读取失败或 null 时通常没有可绘制数值;历史曲线规则却假设 bad 点有数值并可连接。
影响
实现者可能使用上次值、0、null 或丢点,曲线结果完全不同。
建议
定义 bad 子类型或值策略:
bad_with_value:有可疑数值,可虚线绘制;bad_no_value:值为 null,形成断线;- 禁止用 0 或上次值隐式补齐,除非响应显式标注。
D. 历史查询接口与算法
C-14|阻断|interval_minutes 约束自相矛盾
位置
spec-历史数据.mdL362-L367:范围 1~1440,“必须能被60整除”,同时建议 1、2、5、10、15、20、30、60。spec-历史数据.mdL658-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-历史数据.mdL369:前端应根据已选点位最大的history_interval限制或提示。spec-历史数据.mdL141-L159、L225-L273:树节点不含history_interval。spec-数据管理.mdL355-L357:该字段只存在于采集点配置。
冲突内容
历史页面没有已定义接口可获得每个勾选点位的 history_interval。
影响
前端只能忽略要求,或逐点调用数据管理详情接口,造成跨模块依赖和 N+1 请求。
建议
在历史树节点中增加 history_interval,或增加批量元数据接口。
C-16|高|history_interval 无上限,与历史表格可选范围不兼容
位置
spec-数据管理.mdL355-L357、L491-L499:仅规定最小值 1 分钟,没有上限。spec-历史数据.mdL367:表格 API 最大 1440 分钟。spec-历史数据.mdL662:前端最大选项仅 60 分钟。spec-历史数据.mdL369:建议表格间隔不小于最大history_interval。
冲突内容
若点位 history_interval=120,前端没有可选值满足建议;若大于 1440,连 API 都无法满足。
影响
合法的数据管理配置会产生无法正确展示的历史点位。
建议
统一约束。推荐:
history_interval枚举与历史表格可选间隔共享;- 或前端动态生成可选值并允许到 1440;
- 数据管理 API 增加最大值校验。
C-17|高|代码规范要求最大 31 天,历史接口未定义该限制
位置
code-standards.mdL571-L579:时间范围最大跨度不超过 31 天。spec-历史数据.mdL284-L300、L349-L367:只要求起止时间,未规定顺序和最大跨度。spec-历史数据.mdL498-L508:自定义时间范围未规定上限。
冲突内容
规格可被理解为允许任意时间跨度,代码规范要求拒绝超过 31 天。
影响
前后端校验不一致;大查询可能导致内存和网络压力。
建议
在三个历史接口中明确:
end_time > start_time;- 最大 31 天;
- 超限错误码;
- 导出是否允许更长范围以及异步策略。
C-18|高|动态子表 SQL 与“禁止字符串拼接 SQL”冲突
位置
spec-历史数据.mdL302-L311:FROM {subtable_name}动态替换表名。code-standards.mdL222-L226、L562-L569:必须参数化,禁止字符串拼接 SQL。
冲突内容
表名通常不能作为普通参数绑定;历史规格又要求动态表名。
影响
开发者可能直接拼接用户可影响的标识,形成 SQL 注入风险,或违反代码审查规则。
建议
为动态标识符增加明确例外和安全实现:
- 只接受数据库查询得到的 UUID;
- 服务端派生固定格式
p_<uuid32>; - 使用严格正则白名单;
- 值条件仍使用参数绑定;
- 禁止直接使用请求中的表名。
C-19|中|最近邻窗口与原始查询边界不一致
位置
spec-历史数据.mdL375:只查询start_time ~ end_time。spec-历史数据.mdL378-L382:每个目标点在± interval/2窗口内寻找最近值。
冲突内容
第一个目标点可能需要 start_time 之前的数据,最后一个目标点可能需要 end_time 之后的数据;当前查询范围把这些候选值排除。
影响
边界行会出现不必要的 null,与“最近邻窗口”定义不符。
建议
原始查询范围扩展为:
[start_time - window, end_time + window]
最终输出仍只保留目标时间序列。
C-20|中|最近邻算法缺少并列、复用和非整除终点规则
位置
spec-历史数据.mdL373-L383。
缺失内容
- 前后两个点距离相同,选前还是选后;
- 同一原始点能否匹配两个目标时间;
end_time-start_time不是 interval 整数倍时是否追加end_time;- bad 与 good 距离相同时是否优先 good。
影响
不同后端实现会返回不同表格和 CSV。
建议
补充确定性规则,并建立算法测试样例。
C-21|高|曲线原始查询缺少降采样/结果上限,难以满足性能规范
位置
spec-历史数据.mdL298-L311:最多 20 点,但返回范围内全部原始数据。spec-数据管理.mdL355:采集周期最小 1 秒。code-standards.mdL631-L638:要求审查分页、索引和连接池等性能问题。code-standards.mdL577:最大跨度仍可达 31 天。
问题
单个 1 秒点位 31 天约 267 万条;20 点可能超过 5300 万条。API 没有 max_points、降采样或服务端聚合。
影响
内存、TDengine 查询、JSON 序列化、浏览器 ECharts 都可能失控。
建议
根据像素宽度/最大点数服务端降采样,或要求客户端传 max_samples;原始明细导出采用流式响应。
C-22|中|Y 轴“后端转换”方案与 API 响应结构不匹配
位置
spec-历史数据.mdL553-L577:推荐后端把原始值转换为显示坐标。spec-历史数据.mdL314-L344:曲线 API 只返回原始value,没有display_value或映射元数据。
冲突内容
推荐方案没有数据合同支持,前端无法同时绘制映射值和显示原始 tooltip。
建议
确定唯一职责:
- 推荐纯前端映射;或
- API 返回
display_value、原始value和分段映射定义。
E. REST API、错误和安全
C-23|高|所有 API 统一 JSON 包装与 CSV 文件响应冲突
位置
code-standards.mdL360-L376:“所有 API 响应”统一为{code,message,data}。spec-数据管理.mdL249-L273、L591-L605:导出直接返回 CSV。spec-历史数据.mdL442-L471:导出直接返回 CSV。
冲突内容
文件下载不可能同时是原始 CSV 和 JSON 包装。
建议
在代码规范中增加明确例外:文件流、SSE、WebSocket 不使用统一 JSON 包装;错误响应仍返回统一 JSON。
C-24|高|写入失败/超时仍返回 code=0、message=success
位置
spec-数据管理.mdL805-L836:result=failed/timeout,但 envelope 仍为成功。code-standards.mdL372-L376、L393-L403:code=0表示成功,非 0 表示业务错误。
冲突内容
同一响应同时表示“成功”和“写入失败”。
影响
通用请求层可能把失败当作成功;告警和重试逻辑不可靠。
建议
区分两类情况:
- 请求成功且设备写入成功:
code=0。 - 请求已处理但设备写入失败:使用明确业务错误码,HTTP 409/422/502/504 按原因选择,同时
data可附审计记录 ID。
C-25|高|写入请求允许客户端指定 operator,与鉴权规范冲突
位置
spec-数据管理.mdL736-L767:客户端提交source、operator。spec-数据管理.mdL1092-L1098:写入需要登录权限。code-standards.mdL581-L586:人工写入需登录,自动写入需 API Token。
冲突内容
已鉴权身份应由服务端从登录会话或 Token 推导;允许请求体任意填写 operator 会导致身份伪造。source 同样不应完全信任客户端声明。
影响
写入审计日志不可可信,存在严重审计与安全风险。
建议
- 从认证上下文生成
operator和调用方类型; - 请求体删除
operator,必要时仅保留operator_display_name作为非权威备注; - manual/auto 使用不同凭证或不同内部路由。
C-26|中|写入日志错误字段命名不一致
位置
spec-数据管理.mdL719-L723:日志列表返回error_message。spec-数据管理.mdL805-L836:写入结果返回error。spec-数据管理.mdL851-L855:数据库字段为error_message。code-standards.mdL29-L30:同一概念跨层命名一致。
影响
前端需要两套字段,Go DTO 和模型映射易混乱。
建议
统一为 error_message,或明确 error 是标准错误对象而不是字符串。
C-27|中|写入值在不同 API 中类型不一致
位置
spec-数据管理.mdL719-L720:日志列表的target_value、readback_value是字符串。spec-数据管理.mdL790-L800:写入响应value、readback_value是数字。spec-数据管理.mdL851-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-数据管理.mdL693-L695:result仅说明 success/failed。spec-数据管理.mdL822-L835:存在 timeout。spec-数据管理.mdL854:数据库枚举包含 timeout。
建议
筛选枚举统一为 success | failed | timeout。
C-29|高|错误码分类和模块分段无法同时成立
位置
code-standards.mdL395-L403:400xx=参数、401xx=鉴权、403xx=权限、404xx=未找到、409xx=冲突。code-standards.mdL405-L414:采集点=4100141999,写入点=4200142999,历史=43001~43999。
冲突内容
例如历史数据参数错误按类型应是 400xx,但按模块必须是 430xx;历史数据未找到按类型应是 404xx,但又必须是 430xx。
影响
无法设计一致的错误码,前端不能按前缀分类。
建议
采用一种二维编码方式,例如:
- HTTP 状态表达错误类别;
- 业务码按模块分段;
- 或业务码格式
模块两位 + 类别两位 + 序号,并给出算法。
C-30|中|代码规范示例直接返回 err.Error(),与安全审查项冲突
位置
code-standards.mdL184-L199:绑定失败时把err.Error()返回前端。code-standards.mdL640-L647:禁止直接向前端泄露错误信息。
影响
开发者会复制示例,可能暴露内部字段、解析器或库错误。
建议
对外仅返回稳定校验消息;原始错误写结构化日志。
F. CSV 与文件规范
C-31|高|历史 CSV 表头不符合统一 snake_case
位置
spec-历史数据.mdL457-L470:表头为中文“时间”、动态点位名和“_质量”。code-standards.mdL504-L515:CSV 标题字段名统一使用snake_case。spec-数据管理.mdL263-L267、L595-L600:配置 CSV 使用 snake_case。
冲突内容
历史展示型 CSV 和配置导入型 CSV 实际需求不同,但代码规范没有区分。
建议
在规范中分为:
- 机器可导入配置 CSV:固定 snake_case。
- 用户展示/报表 CSV:允许本地化和动态列名,但需定义转义和重复点名处理。
C-32|高|历史 CSV 文件名使用用户输入,违反路径安全规范
位置
spec-历史数据.mdL676-L682:文件名包含{start_time}、{end_time}、{interval}。code-standards.mdL571-L579:导出文件名不得包含用户输入。
影响
除路径穿越外,ISO 时间中的冒号、加号在部分系统不适合作为文件名。
建议
服务端解析并重新格式化为安全值,例如:
history_20260709T000000_20260709T010000_10m.csv
只使用数字、ASCII 字母、下划线和短横线。
G. 前端交互和接口契约
C-33|中|模式切换“不重复请求”与“自动重新查询”冲突
位置
spec-历史数据.mdL715-L720:切换模式“不重复请求数据,按需加载”。spec-历史数据.mdL740-L741:切换模式自动重新查询。
冲突内容
无法判断是每次切换都请求、首次进入某模式请求一次,还是使用缓存。
建议
明确缓存键:mode + point_ids + start_time + end_time + interval。只有缓存不存在或参数变化时请求。
C-34|中|20 点上限同时被描述为建议和强制
位置
spec-历史数据.mdL164-L171:建议最多 20。spec-历史数据.mdL296-L300、L362-L367、L748-L753:API/附录要求最多 20。
建议
统一为强制规则;前端在第 21 个点时阻止并提示,后端仍校验。
C-35|中|设备 PUT 声称与新增同结构,但新增结构没有 enabled
位置
spec-数据管理.mdL182-L198:新增请求体无enabled。spec-数据管理.mdL235-L241:PUT 与新增相同,同时说明可修改enabled。
影响
无法按请求合同执行启用/禁用。
建议
使用独立 UpdateDeviceRequest,明确字段可选性;或增加专用动作接口 /devices/{id}/enable、/disable。
C-36|中|“立即生效”与 5 秒轮询实现不一致
位置
spec-数据管理.mdL243-L247、L504-L511、L970-L981:删除/变更后立即停止或动态更新。spec-数据管理.mdL983:可每 5 秒轮询数据库。
冲突内容
轮询方案最多延迟约 5 秒,不是“立即”。
建议
定义 SLA,例如 5 秒内生效;若必须立即,使用事务后事件、LISTEN/NOTIFY 或消息总线。
H. 命名、代码风格和内部标准
C-37|中|索引命名不符合 idx_{表名}_{字段名}
位置
code-standards.mdL432-L442:索引命名要求包含实际字段名。spec-数据管理.mdL368-L372:idx_collection_points_device/group,实际字段为device_id/group_name。spec-数据管理.mdL643-L645:idx_write_points_device/group。spec-数据管理.mdL863-L866:idx_write_logs_point/device/time,实际字段为point_id/device_id/created_at。
建议
改为:
idx_collection_points_device_ididx_collection_points_group_nameidx_write_logs_created_at- 其他同理
或放宽规范,明确允许语义化简称。
C-38|低|核心类型命名 CollectPoint 与 CollectionPoint 不一致
位置
spec-数据管理.mdL39-L46:概念图使用CollectPoint。code-standards.mdL664-L669:Go 模型使用CollectionPoint。- API/table/package 也使用
collection。
建议
统一为 CollectionPoint,避免出现第三种命名。
4. 其他需要补充但尚不足以判定为直接冲突的事项
以下问题属于规格缺失或高风险歧义,建议一并处理:
spec-数据管理.mdL926-L929 使用“值在合理范围内”判定 good,但数据模型没有合理范围、量程或校验表达式字段。- Modbus
byte_order与word_order的职责存在重叠;CDAB已涉及字交换,同时又提供word_order=BA,组合语义需要重新定义。 - 历史查询同一原始点是否可匹配多个表格时间点未规定。
- 历史树、分组、点位的排序规则未规定。
- 最新值不存在、采集器未运行或缓存过期时,
latest_value是null、省略还是返回quality=none未规定。 - 导出大文件是否流式传输、是否设置 Content-Disposition、超时和最大行数未规定。
history_retention_days只有参数说明,没有系统配置存储模型、API、权限和变更失败补偿。CREATE/ALTER DATABASE aquacontrolai使用固定数据库名,而代码规范要求连接参数环境化;需明确数据库名是否也是环境配置。- 数据管理导出请求示例含
// 可选注释,不是合法 JSON。 - 代码规范日志示例用 INFO 记录“连接成功”,但日志埋点表把“设备连接/断开”统一定为 WARN;应拆分成功 INFO、断开/失败 WARN。
5. 建议的协调优先级
第一批:开发前必须定稿
- C-02 服务进程模型和运行时状态传输。
- C-01 模块边界和历史元数据访问方式。
- C-08 UUID 存储格式。
- C-14/C-15/C-16 表格间隔与
history_interval合同。 - C-25 写入身份来源与鉴权。
- C-24/C-29 错误语义和错误码体系。
- C-05 归档历史数据可见性。
第二批:接口冻结前完成
- C-10 单位字段。
- C-12 质量缺失表示。
- C-17/C-21 时间范围、降采样、结果上限。
- C-23 文件响应例外。
- C-31/C-32 CSV 两类规范和安全文件名。
- C-09 TDengine 元数据变更策略。
第三批:编码规范和文档清理
- C-33 至 C-38。
- 补齐排序、空值、超时、流式导出和配置变更 SLA。
- 为关键算法增加契约测试样例。
6. 推荐的统一决策摘要
建议最终统一为以下基线:
- Web API 与 Collector 保持双进程,通过内部状态服务或消息/缓存同步状态。
- 历史模块通过只读领域接口取得点位元数据,不直接依赖数据管理 Repo。
- UUID 在 API、PG、TDengine 字段中统一使用 36 字符标准形式;仅表名使用
p_<uuid32>。 - 历史树返回
history_interval、unit、archived、has_history_data。 - 表格间隔采用明确枚举;缺失值统一
{value:null, quality:"none"}。 - 历史查询最大 31 天并强制降采样/最大点数。
- 写入操作者从认证上下文生成,失败使用非零业务码。
- JSON API 使用统一 envelope;CSV 文件流为明确例外。
- 配置 CSV 使用固定 snake_case;历史报表 CSV 允许本地化动态表头。