|
@@ -6,7 +6,7 @@
|
|
|
*
|
|
*
|
|
|
* 分八段:
|
|
* 分八段:
|
|
|
* A 唯一准入判据(只认后端 renderable;前端不纠偏、不查范围、不用默认坐标)
|
|
* A 唯一准入判据(只认后端 renderable;前端不纠偏、不查范围、不用默认坐标)
|
|
|
- * B 原因文案(后端六个 reason 逐一命中;「缺失」与「非法」分开;认不出的不泄露原因码)
|
|
|
|
|
|
|
+ * B 原因文案(后端各 reason 逐一命中;「缺失」与「非法」分开;认不出的不泄露原因码)
|
|
|
* C 切分不静默丢对象(不上图 ≠ 不存在,两堆都要交出去)
|
|
* C 切分不静默丢对象(不上图 ≠ 不存在,两堆都要交出去)
|
|
|
* D 两个口径分开(后端全量 vs 前端已加载,不混成一个数)
|
|
* D 两个口径分开(后端全量 vs 前端已加载,不混成一个数)
|
|
|
* E 后端汇总进领域批次(票 05 遗留的漏读;且不得跨页累加)
|
|
* E 后端汇总进领域批次(票 05 遗留的漏读;且不得跨页累加)
|
|
@@ -14,8 +14,12 @@
|
|
|
* G 静态守卫(domain 不重算坐标范围;界面零原因码;不上图清单由领域层供货)
|
|
* G 静态守卫(domain 不重算坐标范围;界面零原因码;不上图清单由领域层供货)
|
|
|
* H 「已上图」措辞不再把被拦下的对象算进去
|
|
* H 「已上图」措辞不再把被拦下的对象算进去
|
|
|
*
|
|
*
|
|
|
- * 契约依据是**后端实现**:`CockpitCoordinateValidator`(六种 reason、state 三态、
|
|
|
|
|
- * renderable 是结论)与 `CockpitMapSnapshotService`(汇总按整份快照算、三桶分法)。
|
|
|
|
|
|
|
+ * 契约依据是**后端实现**:`CockpitCoordinateValidator`(state 三态、renderable 是结论)
|
|
|
|
|
+ * 与 `CockpitMapSnapshotService`(汇总按整份快照算、三桶分法)。
|
|
|
|
|
+ *
|
|
|
|
|
+ * 2026-10-12 后端裁定:取消无入口的人工校核前置,改为「结构合法 + 落在主场景区内」自动准入。
|
|
|
|
|
+ * 后端 reason 因此增为七个(新增 `OUTSIDE_PRIMARY_AREA`);`UNVERIFIED_COORDINATE` 仍在词表里,
|
|
|
|
|
+ * 供历史快照与「后端未给校验段」的防御路径使用,但不再是新数据的常态结论。
|
|
|
*/
|
|
*/
|
|
|
import { readFileSync } from 'node:fs'
|
|
import { readFileSync } from 'node:fs'
|
|
|
import { createPinia, setActivePinia } from 'pinia'
|
|
import { createPinia, setActivePinia } from 'pinia'
|
|
@@ -65,7 +69,7 @@ function object({ code = 'X', kind = 'monitoring-point', name = '', longitude =
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/** 后端校验段的六种取值(照抄 `CockpitCoordinateValidator`)。 */
|
|
|
|
|
|
|
+/** 后端校验段的取值(照抄 `CockpitCoordinateValidator`;2026-10-12 起含区外原因)。 */
|
|
|
const BACKEND_CASES = [
|
|
const BACKEND_CASES = [
|
|
|
{ state: 'VALID', reason: null, renderable: true },
|
|
{ state: 'VALID', reason: null, renderable: true },
|
|
|
{ state: 'UNVERIFIED', reason: 'UNVERIFIED_COORDINATE', renderable: false },
|
|
{ state: 'UNVERIFIED', reason: 'UNVERIFIED_COORDINATE', renderable: false },
|
|
@@ -74,6 +78,7 @@ const BACKEND_CASES = [
|
|
|
{ state: 'INVALID', reason: 'LONGITUDE_OUT_OF_RANGE', renderable: false },
|
|
{ state: 'INVALID', reason: 'LONGITUDE_OUT_OF_RANGE', renderable: false },
|
|
|
{ state: 'INVALID', reason: 'LATITUDE_OUT_OF_RANGE', renderable: false },
|
|
{ state: 'INVALID', reason: 'LATITUDE_OUT_OF_RANGE', renderable: false },
|
|
|
{ state: 'INVALID', reason: 'ZERO_COORDINATE', renderable: false },
|
|
{ state: 'INVALID', reason: 'ZERO_COORDINATE', renderable: false },
|
|
|
|
|
+ { state: 'INVALID', reason: 'OUTSIDE_PRIMARY_AREA', renderable: false },
|
|
|
]
|
|
]
|
|
|
|
|
|
|
|
/** 后端载荷里的对象 → 领域对象(走真实的适配层,不手搓 coordinate 段)。 */
|
|
/** 后端载荷里的对象 → 领域对象(走真实的适配层,不手搓 coordinate 段)。 */
|
|
@@ -150,13 +155,14 @@ function rawToObject(raw, kind = 'monitoring-point') {
|
|
|
),
|
|
),
|
|
|
)
|
|
)
|
|
|
check(
|
|
check(
|
|
|
- 'B1-six-reasons-have-text',
|
|
|
|
|
- texts.length === 6 && texts.every((item) => item.text && item.text !== NO_MAP_LOCATION_TEXT && item.text !== UNVALIDATED_TEXT),
|
|
|
|
|
|
|
+ 'B1-every-reason-has-text',
|
|
|
|
|
+ texts.length === BACKEND_CASES.filter((item) => item.reason).length &&
|
|
|
|
|
+ texts.every((item) => item.text && item.text !== NO_MAP_LOCATION_TEXT && item.text !== UNVALIDATED_TEXT),
|
|
|
JSON.stringify(texts.map((item) => `${item.code}→${item.text}`)),
|
|
JSON.stringify(texts.map((item) => `${item.code}→${item.text}`)),
|
|
|
)
|
|
)
|
|
|
check(
|
|
check(
|
|
|
'B2-reason-dictionary-covers-backend',
|
|
'B2-reason-dictionary-covers-backend',
|
|
|
- Object.keys(COORDINATE_REASON_TEXT).length === 6 &&
|
|
|
|
|
|
|
+ Object.keys(COORDINATE_REASON_TEXT).length === 7 &&
|
|
|
BACKEND_CASES.filter((item) => item.reason).every((item) => !!COORDINATE_REASON_TEXT[item.reason]),
|
|
BACKEND_CASES.filter((item) => item.reason).every((item) => !!COORDINATE_REASON_TEXT[item.reason]),
|
|
|
JSON.stringify(Object.keys(COORDINATE_REASON_TEXT)),
|
|
JSON.stringify(Object.keys(COORDINATE_REASON_TEXT)),
|
|
|
)
|
|
)
|