dsh-erpbot 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -29
- package/bot-identity.js +246 -0
- package/client.js +2438 -627
- package/cordis.patch.yml +128 -0
- package/docs/README.md +39 -0
- package/docs/acceptance.md +136 -0
- package/docs/architecture.md +161 -0
- package/docs/bot-agents-spec.md +205 -0
- package/docs/bot-collaboration-spec.md +161 -0
- package/docs/data-contract.md +262 -0
- package/docs/mcp-inventory-analysis.md +58 -0
- package/docs/requirements.md +234 -0
- package/index.js +270 -15
- package/package.json +6 -2
- package/scripts/check-bot-identity.mjs +71 -0
- package/scripts/lint-factories.mjs +315 -0
- package/scripts/lint-scopes.mjs +179 -0
- package/scripts/scaffold-bots.mjs +76 -0
- package/scripts/test-manifest.mjs +72 -0
- package/scripts/verify.mjs +159 -0
- package/spec/README.md +299 -0
- package/spec/base-content-spec.md +129 -0
- package/spec/cai-content-spec.md +257 -0
- package/spec/shui-content-spec.md +252 -0
- package/spec/spec-CORE.md +155 -0
- package/spec/tui-content-spec.md +259 -0
- package/spec/zi-content-spec.md +256 -0
- package/src/assistant/index.js +389 -4
- package/src/bot-data.js +25 -13
- package/src/bundle.json +1 -0
- package/src/cards/index.js +18 -4
- package/src/dashboard/index.js +65 -25
- package/src/entry.js +7 -7
- package/src/i18n/index.js +80 -60
- package/src/mount.js +19 -2
- package/src/nav.js +7 -5
- package/src/pages-data.js +29 -262
- package/src/shell/index.js +57 -33
- package/src/styles.js +396 -78
- package/src/workbench/index.js +1297 -85
package/cordis.patch.yml
CHANGED
|
@@ -2,3 +2,131 @@
|
|
|
2
2
|
- id: dsh-erpbot
|
|
3
3
|
name: dsh-erpbot
|
|
4
4
|
config: {}
|
|
5
|
+
|
|
6
|
+
# ── Team collaboration engine ─────────────────────────────────────────────
|
|
7
|
+
# The shared task board (agent-team) and its model tools (tool-agent-team)
|
|
8
|
+
# are NOT mounted by the base/web bundles. erpbot owns them so the "团队模式"
|
|
9
|
+
# nav entry can run a real team of the four finance bots. Team tools are
|
|
10
|
+
# session-gated: they are visible to the model only while the root session
|
|
11
|
+
# has Team mode enabled, so single-bot pages are untouched.
|
|
12
|
+
- id: agent-team
|
|
13
|
+
name: '@deepseek-ai/dsh-agent-team'
|
|
14
|
+
config:
|
|
15
|
+
defaultEnabled: false
|
|
16
|
+
|
|
17
|
+
- id: tool-agent-team
|
|
18
|
+
name: '@deepseek-ai/dsh-tool-agent-team'
|
|
19
|
+
|
|
20
|
+
# ── Four finance-bot agent presets ─────────────────────────────────────────
|
|
21
|
+
# Each preset carries the bot's persona (identity four-piece from bot-data).
|
|
22
|
+
# root delegates a task to a bot via `subagent` with `agent_preset: <id>`.
|
|
23
|
+
# The persona text is a compact restatement of the AGENTS.md identity, so the
|
|
24
|
+
# single-bot page (AGENTS.md injection) and the team child (preset persona)
|
|
25
|
+
# share one identity source — kept in sync by scripts/check-bot-identity.mjs.
|
|
26
|
+
- id: preset-erp-finance
|
|
27
|
+
name: '@deepseek-ai/dsh-agent-preset'
|
|
28
|
+
config:
|
|
29
|
+
id: erp-finance
|
|
30
|
+
name: 账房 · The Ledger
|
|
31
|
+
description: 把账记对、把账关好、把证据留全。
|
|
32
|
+
plugins:
|
|
33
|
+
- id: persona
|
|
34
|
+
name: '@deepseek-ai/dsh-persona'
|
|
35
|
+
config:
|
|
36
|
+
prefix: |-
|
|
37
|
+
你是「账房 / The Ledger」,erpBot 财务团队的记账 Bot。
|
|
38
|
+
角色:把账记对、把账关好、把证据留全。
|
|
39
|
+
职责:解释本期账与表的事实(凭证与分录、账簿与试算、往来与资产明细、期末关账、法定报表、核对底稿);先结论,再口径与证据路径。
|
|
40
|
+
语气:严谨、审计口吻;短句;主动披露差异与例外;不确定就写「待复核」;不寒暄、不夸张。
|
|
41
|
+
边界:不审不批不退;不直写 ERP;不替人签批;不重跑批次、不锁账、不解锁、不重出报表、不申报;不改口径、不在前端重算业务数。
|
|
42
|
+
不适用:外币折算/汇兑损益;合并范围/合并抵消/权益法。
|
|
43
|
+
数字可信:每个数字必须能答出口径、时点、数据集、证据四项;无依据即不下结论。
|
|
44
|
+
红线:不直写 ERP、不代为审批/签批/申报、不勾选确认、不改口径、不覆盖历史版本;反写仅经受控写通道并留回执。
|
|
45
|
+
不确定就挂起转人工,不猜测、不静默重试。
|
|
46
|
+
越界转介:资金缺口→司库;预算预测→军师;税务申报→税官。
|
|
47
|
+
你是被委派的子 agent,只产出结论与证据,不替用户做签批决定。
|
|
48
|
+
- id: agent-instructions
|
|
49
|
+
name: '@deepseek-ai/dsh-agent-instructions'
|
|
50
|
+
config:
|
|
51
|
+
maxBytes: 65536
|
|
52
|
+
|
|
53
|
+
- id: preset-erp-funds
|
|
54
|
+
name: '@deepseek-ai/dsh-agent-preset'
|
|
55
|
+
config:
|
|
56
|
+
id: erp-funds
|
|
57
|
+
name: 司库 · The Treasurer
|
|
58
|
+
description: 守住可支付能力与安全垫。
|
|
59
|
+
plugins:
|
|
60
|
+
- id: persona
|
|
61
|
+
name: '@deepseek-ai/dsh-persona'
|
|
62
|
+
config:
|
|
63
|
+
prefix: |-
|
|
64
|
+
你是「司库 / The Treasurer」,erpBot 财务团队的资金 Bot。
|
|
65
|
+
角色:守住可支付能力与安全垫。
|
|
66
|
+
职责:解释现在能动多少钱(账户与余额、收付待办、对账与未达、资金计划、受限资金、票据与授信);先给可用口径,再给时点。
|
|
67
|
+
语气:直接、风险优先、数字先行;先给结论与风险,再给依据;不做预测保证。
|
|
68
|
+
边界:不动账、不调账;不对外付款;不承诺授信;不改口径、不在前端重算业务数。
|
|
69
|
+
不适用:外币资金与汇兑损益;融资性保函定价。
|
|
70
|
+
数字可信:每个数字必须能答出口径、时点、数据集、证据四项;无依据即不下结论。
|
|
71
|
+
红线:不直写 ERP、不代为审批/签批/申报、不勾选确认、不改口径、不覆盖历史版本;反写仅经受控写通道并留回执。
|
|
72
|
+
不确定就挂起转人工,不猜测、不静默重试。
|
|
73
|
+
越界转介:账务处理→账房;资金计划预测→军师;涉税→税官。
|
|
74
|
+
你是被委派的子 agent,只产出结论与证据,不替用户做付款决定。
|
|
75
|
+
- id: agent-instructions
|
|
76
|
+
name: '@deepseek-ai/dsh-agent-instructions'
|
|
77
|
+
config:
|
|
78
|
+
maxBytes: 65536
|
|
79
|
+
|
|
80
|
+
- id: preset-erp-forecast
|
|
81
|
+
name: '@deepseek-ai/dsh-agent-preset'
|
|
82
|
+
config:
|
|
83
|
+
id: erp-forecast
|
|
84
|
+
name: 军师 · The Planner
|
|
85
|
+
description: 把不确定性与假设讲清楚。
|
|
86
|
+
plugins:
|
|
87
|
+
- id: persona
|
|
88
|
+
name: '@deepseek-ai/dsh-persona'
|
|
89
|
+
config:
|
|
90
|
+
prefix: |-
|
|
91
|
+
你是「军师 / The Planner」,erpBot 财务团队的预测 Bot。
|
|
92
|
+
角色:把不确定性与假设讲清楚。
|
|
93
|
+
职责:解释未来怎么走(预算编制、滚动预测、情景模拟、假设版本、成本分摊、管报编制、差异归因、资金计划);先给区间与假设,再给结论。
|
|
94
|
+
语气:结构化、假设先行、区间优先;区分「预测」与「承诺」;不替业务拍板。
|
|
95
|
+
边界:不改变已生效口径;不把预测写成承诺;不改版本链、不覆盖历史版本;不在前端重算业务数。
|
|
96
|
+
不适用:年度以上战略规划;合并口径预测。
|
|
97
|
+
数字可信:每个数字必须能答出口径、时点、数据集、证据四项;无依据即不下结论。
|
|
98
|
+
红线:不直写 ERP、不代为审批/签批/申报、不勾选确认、不改口径、不覆盖历史版本;反写仅经受控写通道并留回执。
|
|
99
|
+
不确定就挂起转人工,不猜测、不静默重试。
|
|
100
|
+
越界转介:已发生事实与账务→账房/司库;税务申报→税官。
|
|
101
|
+
你是被委派的子 agent,只产出结论与证据,不替用户做业务决定。
|
|
102
|
+
- id: agent-instructions
|
|
103
|
+
name: '@deepseek-ai/dsh-agent-instructions'
|
|
104
|
+
config:
|
|
105
|
+
maxBytes: 65536
|
|
106
|
+
|
|
107
|
+
- id: preset-erp-tax
|
|
108
|
+
name: '@deepseek-ai/dsh-agent-preset'
|
|
109
|
+
config:
|
|
110
|
+
id: erp-tax
|
|
111
|
+
name: 税官 · The Tax Officer
|
|
112
|
+
description: 不错报、不漏报、红线可见。
|
|
113
|
+
plugins:
|
|
114
|
+
- id: persona
|
|
115
|
+
name: '@deepseek-ai/dsh-persona'
|
|
116
|
+
config:
|
|
117
|
+
prefix: |-
|
|
118
|
+
你是「税官 / The Tax Officer」,erpBot 财务团队的税务 Bot。
|
|
119
|
+
角色:不错报、不漏报、红线可见。
|
|
120
|
+
职责:解释税务事实与红线(销项发票、进项勾选、发票查验、纳税申报、报送与缴款、税负与风险);预计口径与确定口径必须分开说。
|
|
121
|
+
语气:法条式、合规优先、数字先行;先给结论与截止日,再给依据;不编造政策。
|
|
122
|
+
边界:不替人勾选确认;不代签报送;不把预计口径写成确定口径;不编造政策;不在前端重算业务数。
|
|
123
|
+
不适用:关税与进出口退税;跨地区汇总纳税。
|
|
124
|
+
数字可信:每个数字必须能答出口径、时点、数据集、证据四项;无依据即不下结论。
|
|
125
|
+
红线:不直写 ERP、不代为审批/签批/申报、不勾选确认、不改口径、不覆盖历史版本;反写仅经受控写通道并留回执。
|
|
126
|
+
不确定就挂起转人工,不猜测、不静默重试。
|
|
127
|
+
越界转介:账务与资金→账房/司库;预算→军师。
|
|
128
|
+
你是被委派的子 agent,只产出结论与证据,不替用户做申报决定。
|
|
129
|
+
- id: agent-instructions
|
|
130
|
+
name: '@deepseek-ai/dsh-agent-instructions'
|
|
131
|
+
config:
|
|
132
|
+
maxBytes: 65536
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# dsh-erpbot 文档索引
|
|
2
|
+
|
|
3
|
+
> 本目录是 dsh-erpbot 插件的需求与规格文档,用于**评估现有实现**与**指导未来开发**。
|
|
4
|
+
> 文档描述的是「应该是什么」(需求与契约),与 `README.md`「现在是什么」(技术现状)互补。
|
|
5
|
+
|
|
6
|
+
## 文档地图
|
|
7
|
+
|
|
8
|
+
| 文档 | 定位 | 读者 | 何时读 |
|
|
9
|
+
|---|---|---|---|
|
|
10
|
+
| [`requirements.md`](./requirements.md) | 需求规格 PRD:目标、范围、功能需求(FR)、非功能需求(NFR)、术语、演进路线 | 评审人、产品/开发者 | 评估「做没做对」、规划新功能 |
|
|
11
|
+
| [`data-contract.md`](./data-contract.md) | 数据契约:PAGES payload 结构、7 种 KPI 变体、12 种渲染器字段、金额规则、i18n 规范 | 开发者 | 改数据、加页面、加渲染器 |
|
|
12
|
+
| [`acceptance.md`](./acceptance.md) | 验收标准、回归检查点、已知坑 | 评审人、开发者 | 验收交付、回归测试、防踩坑 |
|
|
13
|
+
| [`architecture.md`](./architecture.md) | 架构方案与编码规范(7 工厂 + 纯拼接 + 构建校验) | 开发者 | 重构、加板块、改构建链 |
|
|
14
|
+
| [`bot-agents-spec.md`](./bot-agents-spec.md) | Bot 身份文件规格:`AGENTS.md` 必备 9 段、禁写 5 类、四 Bot 差异化、验收判定标准 | 评审人、开发者 | 改 `buildAgentsDoc()`、评审 Bot 身份文件 |
|
|
15
|
+
| [`../spec/base-content-spec.md`](../spec/base-content-spec.md) | 底座(MCP 与数据)单页规格:4 KPI、8 清单项、技能注册表、与四 Bot 的关系、边界与验收 | 评审人、产品/开发者 | 验收底座页、改底座 payload |
|
|
16
|
+
|
|
17
|
+
## 阅读顺序
|
|
18
|
+
|
|
19
|
+
1. **评估现状**:先读 `requirements.md`(要什么)→ 对照 `README.md`(有什么)→ 用 `acceptance.md` 逐项验收。
|
|
20
|
+
2. **开发新功能**:先读 `requirements.md` 对应 FR → `data-contract.md` 相关字段 → `architecture.md` 工程约束 → 实现后按 `acceptance.md` 回归。
|
|
21
|
+
|
|
22
|
+
## 文档与代码的对应关系
|
|
23
|
+
|
|
24
|
+
| 文档章节 | 代码位置 |
|
|
25
|
+
|---|---|
|
|
26
|
+
| 页面配置 / KPI / 渲染器 | `src/pages-data.js`(PAGES) |
|
|
27
|
+
| KPI 变体渲染 | `src/workbench/index.js`(PageKpi/ShellKpi)、`src/cards/index.js`(KpiCard) |
|
|
28
|
+
| 渲染器分发 | `src/workbench/index.js`(renderPane)、`src/cards/index.js`(CardView) |
|
|
29
|
+
| 金额单位换算 | `src/metric-format.js`(moneyParts/compactMoney) |
|
|
30
|
+
| i18n 字典 | `src/i18n/index.js`(ZH/EN) |
|
|
31
|
+
| 内嵌 AI 助手 | `src/assistant/index.js` |
|
|
32
|
+
| 主题桥 | `src/theme-bridge.js` |
|
|
33
|
+
| 构建与校验 | `scripts/build.mjs`、`scripts/verify.mjs`、`scripts/lint-factories.mjs` |
|
|
34
|
+
|
|
35
|
+
## 维护约定
|
|
36
|
+
|
|
37
|
+
- 需求变更先改 `requirements.md`,再改 `data-contract.md`(若涉及字段),最后改代码与 `README.md`。
|
|
38
|
+
- 文档不得描述「未实现」的功能;实现落后于文档时,在 `requirements.md` 对应 FR 标注 `[待实现]`。
|
|
39
|
+
- 金额单位规则、i18n key 规范是「权威契约」,代码与文档冲突时以文档为准并修代码。
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# dsh-erpbot 验收标准与回归检查
|
|
2
|
+
|
|
3
|
+
> 状态:v1(2026-11-12)— 用于验收交付、回归测试、防踩坑。
|
|
4
|
+
> 关联:FR 编号见 [`requirements.md`](./requirements.md)。
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 1. 功能验收清单
|
|
9
|
+
|
|
10
|
+
按 `requirements.md` 的 FR 编号逐项勾验。**验收前提**:改完 `client.js` 已重启 DSH 或刷新页面。
|
|
11
|
+
|
|
12
|
+
### 1.1 导航(FR-NAV)
|
|
13
|
+
|
|
14
|
+
- [ ] 左侧导航顶部(plugins 之上)出现 Erp 图标(FR-NAV-1)
|
|
15
|
+
- [ ] 点击图标切到 Erp 主面板;切走再切回正常(FR-NAV-2)
|
|
16
|
+
- [ ] 左栏 6 个条目(总览 + 四 Bot + 底座),当前页高亮(FR-NAV-3)
|
|
17
|
+
- [ ] 切页后 URL hash 变为 `#/erp/<id>`,刷新停原页(FR-NAV-4)
|
|
18
|
+
- [ ] 矮面板下左栏底部「设置 + 语言」组不被挤走(FR-NAV-6)
|
|
19
|
+
|
|
20
|
+
### 1.2 概览页(FR-OV)
|
|
21
|
+
|
|
22
|
+
- [ ] 4 张 KPI 统计卡 + 迷你折线 + 环比齐全(FR-OV-1)
|
|
23
|
+
- [ ] 核心块轮播:箭头 / 圆点 / 自动轮播 / 悬停暂停 / 手动翻页后计时器重置(FR-OV-2)
|
|
24
|
+
- [ ] 收支趋势图有 tooltip、峰值高亮(FR-OV-3)
|
|
25
|
+
- [ ] 支出环形图 + 图例 + 增长徽标(FR-OV-4)
|
|
26
|
+
- [ ] 账龄卡分段占比条正确(FR-OV-5)
|
|
27
|
+
- [ ] 时间线 + AI 卡正常(FR-OV-6/7)
|
|
28
|
+
|
|
29
|
+
### 1.3 KPI 卡(FR-KPI)
|
|
30
|
+
|
|
31
|
+
- [ ] 七种变体(value/split/stack/gauge/countdown/status/delta)各至少一页可见(FR-KPI-2)
|
|
32
|
+
- [ ] 每卡只留「主数字 + 一行口径 + 一个微型图」(FR-KPI-1)
|
|
33
|
+
- [ ] 点卡打开 `MetricDetailModal`:精确金额 + 完整口径 + 构成明细 + 统计口径 + 更新时点(FR-KPI-3)
|
|
34
|
+
- [ ] 金额显示为 `万/亿`(zh)或 `K/M/B`(en),弹窗「精确金额」仍给到分(FR-KPI-4)
|
|
35
|
+
|
|
36
|
+
### 1.4 工作台(FR-WB)
|
|
37
|
+
|
|
38
|
+
- [ ] 各工作台页 4 KPI + 段工作台(左栏清单 + 右侧主内容);底座为单页 8 清单项(FR-WB-1)
|
|
39
|
+
- [ ] 底座为单页:4 张 KPI + 8 个清单项,页内无 tab;`#/erp/shu` / `#/erp/guan` 落到同一页
|
|
40
|
+
- [ ] 左栏 `pill` 徽标、右栏 `summary` 摘要块正常(FR-WB-2)
|
|
41
|
+
- [ ] `chart`/`donut`/`bars` 段的「图表 / 清单」切换可用,切段回到图表视图(FR-WB-3/4)
|
|
42
|
+
- [ ] 右栏 `foot` 口径落款显示(FR-WB-5)
|
|
43
|
+
|
|
44
|
+
### 1.5 双语(FR-I18N)
|
|
45
|
+
|
|
46
|
+
- [ ] 左下角圆按钮显示当前语言(`中` / `EN`),点击即切(FR-I18N-2)
|
|
47
|
+
- [ ] 切换后全部文案(含 AI 助手、弹窗、chips 预填)跟随切换(FR-I18N-1)
|
|
48
|
+
- [ ] 切换语言不丢失当前页面(FR-I18N-3)
|
|
49
|
+
|
|
50
|
+
### 1.6 主题(FR-THEME)
|
|
51
|
+
|
|
52
|
+
- [ ] 切换 DSH 明暗外观,面板跟随(FR-THEME-1)
|
|
53
|
+
- [ ] 明暗两套下文字对比度满足 WCAG AA(FR-THEME-2)
|
|
54
|
+
|
|
55
|
+
### 1.7 AI 助手(FR-AI)
|
|
56
|
+
|
|
57
|
+
- [ ] 点右上角 spark 或总览 AI 卡打开弹窗(FR-AI-1)
|
|
58
|
+
- [ ] 弹窗内嵌官方会话,不跳转(FR-AI-2)
|
|
59
|
+
- [ ] 首次使用自动建 `erpbot-assistant` 工作区会话并持久化(FR-AI-3)
|
|
60
|
+
- [ ] 四态机 `closed/modal/dock/rail` 切换正确,`modal` 记住上一状态(FR-AI-4)
|
|
61
|
+
- [ ] 停靠/折叠状态刷新后恢复(FR-AI-5)
|
|
62
|
+
- [ ] 会话列表抽屉 + 新建 + 切换 + 相对时间 + 状态点(FR-AI-6)
|
|
63
|
+
- [ ] 内嵌契约缺失时降级「打开对话页」,不白屏(FR-AI-7)
|
|
64
|
+
|
|
65
|
+
### 1.8 响应式(FR-RESP)
|
|
66
|
+
|
|
67
|
+
- [ ] 宽度断点 1180/900/720/560 分级重排正确(FR-RESP-2)
|
|
68
|
+
- [ ] 高度断点 820/680/560/460 分级降级不溢出(FR-RESP-3)
|
|
69
|
+
- [ ] 图表矮盒仍绘图(tight 模式),不空白(FR-RESP-6)
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 2. 回归检查(改代码后必跑)
|
|
74
|
+
|
|
75
|
+
### 2.1 构建与静态检查
|
|
76
|
+
|
|
77
|
+
```powershell
|
|
78
|
+
node scripts/build.mjs --check # 产物与 src 同步(exit 0)
|
|
79
|
+
node scripts/test-manifest.mjs # AST 顺序校验负面测试(全 ok)
|
|
80
|
+
node scripts/lint-factories.mjs # 工厂未注入自由引用(全 ok)
|
|
81
|
+
node --check client.js # 产物语法
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
> `lint-factories.mjs` 是防「运行时空白」的关键防线:它静态检查每个 `createXxx` 工厂
|
|
85
|
+
> 函数体里的自由标识符,减去 deps 解构、任意层作用域声明、平台全局后必须为空。
|
|
86
|
+
> 历史上 0c41a01 工厂化重构漏注入 `LANGS` 等符号导致面板空白,就是它拦的(详见 §3 已知坑 K1)。
|
|
87
|
+
|
|
88
|
+
### 2.2 语义回归(若恢复 `_erp-dev` 工具链)
|
|
89
|
+
|
|
90
|
+
README 引用了但未入库的工具(未来补):
|
|
91
|
+
|
|
92
|
+
```powershell
|
|
93
|
+
node _erp-dev/check.cjs # 各页 4 KPI 变体、rail 数量、英文无中文泄漏、跨页锚点、金额格式
|
|
94
|
+
node _erp-dev/tools/check-anchors.js # 段内合计对平
|
|
95
|
+
node _erp-dev/tools/validate-page.js <id> # 单页 payload 校验
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 3. 已知坑(K)
|
|
101
|
+
|
|
102
|
+
> 这些是本插件踩过、未来可能再踩的坑,改动相关代码前必读。
|
|
103
|
+
|
|
104
|
+
### K1 工厂化重构漏注入 → 面板空白(2026-11-12 已修复)
|
|
105
|
+
|
|
106
|
+
**症状**:`build --check` 全绿、重启仍空白。
|
|
107
|
+
**根因**:`verify.mjs` 只查「加载期直接子声明初始化表达式」,故意不递归函数体;工厂函数体里按顶层符号引用了被挪进闭包的 `LANGS`/`SHORT_LOCALE`/`LANG_BADGE`/`readLang`/`setLang`/`drawTrend`。
|
|
108
|
+
**防线**:`lint-factories.mjs`(§2.1 必跑)。
|
|
109
|
+
|
|
110
|
+
### K2 CSS 注释里不能有反引号
|
|
111
|
+
|
|
112
|
+
`const CSS = \`` 是模板字符串;CSS 注释里出现反引号会提前终止它,运行时报 `ReferenceError`。
|
|
113
|
+
|
|
114
|
+
### K3 面板尺寸不能用 `@media`
|
|
115
|
+
|
|
116
|
+
面板尺寸 ≠ 窗口尺寸;必须用容器查询 `@container erp/board/exp/donut`。否则 DSH 分配的窄面板不触发断点。
|
|
117
|
+
|
|
118
|
+
### K4 金额不能写进数据
|
|
119
|
+
|
|
120
|
+
payload 永远精确元 `¥` 千分位两位小数,禁止 `万/亿/K/M/B`;单位只在 `metric-format.js` 渲染层换算。数据里写 `万` 会破坏 `moneyAmount` 解析。
|
|
121
|
+
|
|
122
|
+
### K5 批量改中文源码禁用 PowerShell 字符串替换
|
|
123
|
+
|
|
124
|
+
本机默认编码会把 UTF-8 中文写成错位字节、整文件损坏。改用 `edit` 工具或 Node `readFileSync(p,'utf8')`/`writeFileSync(p,s)`。
|
|
125
|
+
|
|
126
|
+
### K6 概览 KPI 与工作台 KPI 字段名不同
|
|
127
|
+
|
|
128
|
+
概览用 `labelKey`/`subKey`(字典键),工作台用 `label`/`sub`(内联 `{zh,en}`),勿混。
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## 4. 验收记录
|
|
133
|
+
|
|
134
|
+
| 日期 | 范围 | 结果 | 备注 |
|
|
135
|
+
|---|---|---|---|
|
|
136
|
+
| 2026-11-12 | 空白修复后冒烟(总览 + 财页深度渲染探测) | 通过 | 18 类组件可渲染,无 ReferenceError |
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# dsh-erpbot 架构与编码规范
|
|
2
|
+
|
|
3
|
+
> 状态:v3(2026-11-12)— 描述**当前已落地**的架构,非目标稿。
|
|
4
|
+
> 来源:由根仓库 `docs/erp-conventions.md`(v2 目标稿)校正而来,补充了 v2 之后实际发生的变化(工厂化落地、依赖显式注入、lint-factories 防线)。
|
|
5
|
+
> 适用范围:`dsh-erpbot` 插件(Aurora 风格财务 / ERP 面板)。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. 目标架构(已落地)
|
|
10
|
+
|
|
11
|
+
`client.js` 不是手写单文件,而是由 **7 个功能板块工厂 + 9 个横切小片** 经 `scripts/build.mjs` **纯拼接**成的 DSH 客户端 bundle。
|
|
12
|
+
|
|
13
|
+
- **架构 = 功能板块**:每个板块是一个工厂函数——**依赖对象进,命名空间对象出**。
|
|
14
|
+
- 板块间依赖通过 `deps` 参数**显式传入**,不靠自由变量、不靠 import。
|
|
15
|
+
- 拼接顺序唯一声明在 `src/bundle.json`,构建脚本按拓扑序拼接并在构建期校验。
|
|
16
|
+
|
|
17
|
+
### 1.1 为什么不是 ESM + esbuild
|
|
18
|
+
|
|
19
|
+
| 维度 | A'(工厂函数 + 纯拼接) | ESM + esbuild |
|
|
20
|
+
|---|---|---|
|
|
21
|
+
| 依赖强制 | 参数注入 + manifest 声明 + 构建期校验 | 语言级 import |
|
|
22
|
+
| 构建 | 拼接零依赖;校验用只读 acorn(devDependency) | 引入 esbuild(参与产出) |
|
|
23
|
+
| 产物 | 可读单文件,`factory(require)` 结构原封不动 | import 重写成 IIFE 闭包,不可读 |
|
|
24
|
+
| 与 `__ModuleLoader__.load` 契合 | 产物可直接字面嵌进 factory | 需额外桥接 |
|
|
25
|
+
| 自证能力 | 语义等价改写可 sha256 自证 | 字节必变,只能功能目测 |
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 2. 板块划分(7 工厂 + 9 小片)
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
createI18n() → { ZH, EN, t, L, setLang, readLang, LANGS, SHORT_LOCALE, LANG_BADGE, … }
|
|
33
|
+
createChart({ SVG_NS, t }) → { mk, smooth, drawTrend, drawOverview, INCOME, EXPENSE, HILITE, … }
|
|
34
|
+
createAssistant({ React, h, t, readLang, AI_CHIPS, Icon, useHostTheme }) → { useAssistant, assistantTone, AssistantDock, … }
|
|
35
|
+
createCards({ React, h, t, metricLabel, metricSub, PAGES, Icon, Spark, drawTrend }) → { KpiCard, CardView, … }
|
|
36
|
+
createDashboard({ React, h, t, readLang, setLang, LANGS, SHORT_LOCALE, LANG_BADGE, … }) → { TopBar, Rail }
|
|
37
|
+
createWorkbench({ React, h, t, L, readLang, KpiFigure, compactMoney, … }) → { SectionShell, BotShell, PageKpi, renderSection }
|
|
38
|
+
createShell({ React, h, readLang, subscribeLang, useHostTheme, … }) → { ErpBotPanel, ErpBotNavIcon }
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**横切小片**(顶层 `.js`,非工厂):`icons.js` `styles.js` `board-data.js` `pages-data.js` `bot-data.js` `nav.js` `metric-format.js` `theme-bridge.js` `primitives.js` `kpi-detail.js`。
|
|
42
|
+
|
|
43
|
+
> `bot-data.js` 存四个 Bot 的身份定义与技能配置;`createAssistant` 是四 Bot 的会话引擎(DSH 官方会话嵌入);`createWorkbench` 额外导出 `BotShell`(对话页)、`SectionShell`(单页工作台,底座「MCP 与数据」单页也走它)与 `renderSection`(画布复用 12 渲染器)。
|
|
44
|
+
|
|
45
|
+
依赖方向(只允许「上依赖下」):
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
shell ──► { dashboard, workbench, cards, assistant }
|
|
49
|
+
dashboard ──► assistant ──► { primitives, theme, board-data, i18n }
|
|
50
|
+
dashboard ──► { cards, primitives, nav, chart, i18n }
|
|
51
|
+
workbench ──► { cards, primitives, metric, kpi-detail, chart, i18n }
|
|
52
|
+
cards ──► { primitives, metric, chart, i18n }
|
|
53
|
+
i18n / chart:叶子(i18n 无依赖;chart 依赖 SVG_NS + i18n 的 t)
|
|
54
|
+
primitives / board-data / theme / nav / metric:被多板块共享的横切小片
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## 3. 关键设计规则
|
|
60
|
+
|
|
61
|
+
1. **横切关注点是模块,不是散落**:`i18n`、`chart` 是叶子模块,任何板块需要它们就**在 `deps` 参数里接收**,不通过 factory 作用域自由变量隐式传递。
|
|
62
|
+
2. **数据与渲染分离**:`PAGES` 是纯数据(`pages-data.js`,无函数、无 `h(`),渲染器不感知具体数字。
|
|
63
|
+
3. **每个板块有且仅有一个工厂**:`src/<板块>/index.js` 导出 `createXxx`,其它板块只能通过工厂返回值访问,不得跨板块深引用。
|
|
64
|
+
4. **工厂只依赖两样东西**:`deps` 注入的 + 本文件声明的。返回值就是该板块对外暴露的全部符号。
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 4. 构建工具链
|
|
69
|
+
|
|
70
|
+
### 4.1 两阶段分离
|
|
71
|
+
|
|
72
|
+
- **拼接(产出)**:`build.mjs` 读 `src/bundle.json`,按 `order` 升序把片段拼进 `client.js`。不解析、不重写、不压缩。
|
|
73
|
+
- **校验(只读)**:acorn(devDependency)做 AST 级校验,只读不产出。
|
|
74
|
+
|
|
75
|
+
### 4.2 `src/bundle.json`(组装契约)
|
|
76
|
+
|
|
77
|
+
`fragments` 数组声明拼接顺序(`order` 升序),每个片段含 `file` / `order` / `role`。
|
|
78
|
+
|
|
79
|
+
### 4.3 三个强制机制
|
|
80
|
+
|
|
81
|
+
1. **显式依赖落地**:`entry.js` 按拓扑序调用工厂,把上游返回值作为 `deps` 传给下游。
|
|
82
|
+
2. **加载期顺序校验(`verify.mjs`)**:acorn 检查「直接子声明的初始化表达式」的跨片段引用,前置片段不得引用后置片段声明——加载期 ReferenceError 提前到构建期暴露。
|
|
83
|
+
3. **运行时自由引用校验(`lint-factories.mjs`)**:见 §4.5,补 `verify.mjs` 不覆盖的函数体引用盲区。
|
|
84
|
+
|
|
85
|
+
### 4.4 构建命令
|
|
86
|
+
|
|
87
|
+
```powershell
|
|
88
|
+
node scripts/build.mjs # 拼接 + 顺序校验 → client.js
|
|
89
|
+
node scripts/build.mjs --check # 校验产物与 src 同步(CI 用)
|
|
90
|
+
node scripts/test-manifest.mjs # verify.mjs 的负面测试
|
|
91
|
+
node scripts/lint-factories.mjs # 工厂未注入自由引用(防空白回归)
|
|
92
|
+
node --check client.js # 产物语法
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 4.5 lint-factories.mjs(重要防线)
|
|
96
|
+
|
|
97
|
+
`verify.mjs` **只查加载期**(直接子声明初始化表达式),**故意不递归函数体**(函数体是调用期求值)。工厂化重构把顶层符号挪进闭包后,其它工厂函数体里按顶层符号引用会漏网——症状是 `build --check` 全绿但面板空白(2026-11-12 实踩)。
|
|
98
|
+
|
|
99
|
+
`lint-factories.mjs` 补齐这块:对每个 `createXxx` 工厂收集**全函数体**(含嵌套)的标识符,减去 deps 解构、任意层作用域声明、平台全局后必须为空。**改工厂 `deps` 签名后必跑**。
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 5. 编码规范
|
|
104
|
+
|
|
105
|
+
### 5.1 命名
|
|
106
|
+
|
|
107
|
+
| 类别 | 规范 | 例 |
|
|
108
|
+
|---|---|---|
|
|
109
|
+
| 组件 | PascalCase | `StatCard` `MetricDetailModal` |
|
|
110
|
+
| helper / 函数 | camelCase | `moneyParts` `compactMoney` |
|
|
111
|
+
| 常量 | UPPER_SNAKE | `CARD_LIMIT` `PAGES` |
|
|
112
|
+
| 字典键 | 点分小写 | `nav.erp` `chart.axisK` |
|
|
113
|
+
| 板块目录 / 文件 | kebab-case | `src/workbench/` |
|
|
114
|
+
|
|
115
|
+
### 5.2 模块与依赖
|
|
116
|
+
|
|
117
|
+
- 跨板块只能通过工厂 `deps` 访问;`deps` 键与解构必须一致。
|
|
118
|
+
- 不得用自由变量跨板块传值。
|
|
119
|
+
- 工厂 `deps` 解构顺序:平台(`React`/`h`)→ 叶子板块(`i18n`/`chart`)→ 上层板块。
|
|
120
|
+
|
|
121
|
+
### 5.3 i18n
|
|
122
|
+
|
|
123
|
+
- 所有用户可见文案走 `t(key)` 或 `L({zh,en})`,禁止组件裸写。
|
|
124
|
+
- `zh`/`en` 字典成对、key 集合一致。
|
|
125
|
+
- 字典键分层:`nav.*` `chart.*` `ai.*` `kpi.*` `metric.*` `page.*`。
|
|
126
|
+
|
|
127
|
+
### 5.4 CSS
|
|
128
|
+
|
|
129
|
+
- 选择器 `.erp-app` 前缀作用域化。
|
|
130
|
+
- 调色板收敛进 CSS 变量(明/暗两套),组件只写 `var(--erp-*)`。
|
|
131
|
+
- 明暗**显式内置**、`data-theme` 切换、跟随 DSH,不依赖 `prefers-color-scheme`。
|
|
132
|
+
- 面板尺寸用容器查询,**禁止** `@media`。
|
|
133
|
+
- CSS 注释里**禁止反引号**(`const CSS = \`` 会提前终止)。
|
|
134
|
+
|
|
135
|
+
### 5.5 组件
|
|
136
|
+
|
|
137
|
+
- 纯函数组件;`useEffect` 里的 `setInterval` / `addEventListener` / `ResizeObserver` 必须返回清理函数。
|
|
138
|
+
- 不写 `document.body`、不读其它插件 DOM、不依赖宿主内部 class。
|
|
139
|
+
- 复用小图(Spark / MiniChart / Donut)走 `chart` 板块。
|
|
140
|
+
|
|
141
|
+
### 5.6 产物与提交
|
|
142
|
+
|
|
143
|
+
- `client.js` 只能由 `build.mjs` 生成,禁止手改。
|
|
144
|
+
- 提交前 `--check` + `lint-factories` + `node --check client.js` 全绿。
|
|
145
|
+
- 一个板块一个提交,保证可回滚、可独立验证。
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## 6. 加一页 / 加一板块
|
|
150
|
+
|
|
151
|
+
- **加一页**:改 `NAV` + `ZH/EN` 各一条 + `PAGES` 一条(见 data-contract §9)。
|
|
152
|
+
- **加一板块**:新建 `src/<板块>/index.js` 导出一个工厂,在 `src/bundle.json` 的 `fragments` 加片段(order 按拓扑序),在 `entry.js` 实例化并传 deps,跑 `build.mjs` + `lint-factories.mjs`。
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## 7. 变更记录
|
|
157
|
+
|
|
158
|
+
| 日期 | 变更 |
|
|
159
|
+
|---|---|
|
|
160
|
+
| 2026-11-12 | 由 erp-conventions.md(v2 目标稿)校正为 v3 落地态,补 lint-factories 防线与显式依赖签名 |
|
|
161
|
+
| 2026-11-12 | 底座合并:原数据 / 治理两页合并为「底座 · MCP 与数据」单页(4 KPI + 8 清单项,无页内 tab),`BaseShell` 退役,底座走 `SectionShell`;`#/erp/shu` / `#/erp/guan` 作为深链别名落到 `base` |
|