@epoint-testtech/ep-stage-skill 0.0.10 → 0.0.12
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/codex-skill/ep-stage/code-list-generate/SKILL.md +168 -0
- package/codex-skill/ep-stage/code-list-generate/assets/code_list.template.json5 +325 -0
- package/codex-skill/ep-stage/code-list-generate/assets/example-zwplace.json5 +588 -0
- package/codex-skill/ep-stage/code-list-generate/evals/evals.json +47 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/schemeresource-req/src/main/java/com/epoint/schemeresource/action/SchemeResourceAddAction.java +119 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/schemeresource-req/src/main/java/com/epoint/schemeresource/action/SchemeResourceDetailAction.java +70 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/schemeresource-req/src/main/java/com/epoint/schemeresource/action/SchemeResourceEditAction.java +107 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/schemeresource-req/src/main/java/com/epoint/schemeresource/action/SchemeResourceListAction.java +167 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/schemeresource-req/src/main/java/com/epoint/schemeresource/api/ISchemeResourceService.java +84 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/schemeresource-req/src/main/java/com/epoint/schemeresource/api/entity/SchemeResource.java +218 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/schemeresource-req/src/main/java/com/epoint/schemeresource/impl/SchemeResourceService.java +146 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/schemeresource-req/src/main/java/com/epoint/schemeresource/impl/SchemeResourceServiceImpl.java +110 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/schemeresource-req/src/main/webapp/perpage/schemeresource/schemeresourceadd.html +158 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/schemeresource-req/src/main/webapp/perpage/schemeresource/schemeresourcedetail.html +89 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/schemeresource-req/src/main/webapp/perpage/schemeresource/schemeresourceedit.html +125 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/schemeresource-req/src/main/webapp/perpage/schemeresource/schemeresourcelist.html +224 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/docs//345/234/272/346/211/200/347/252/227/345/217/243/344/277/241/346/201/257/347/256/241/347/220/206/345/217/212/345/220/204/347/253/257/347/273/237/350/256/241/346/243/200/347/264/242/346/237/245/347/234/213_prd.md +602 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/java/com/epoint/zwplace/action/GxhZwPlaceAddAction.java +210 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/java/com/epoint/zwplace/action/GxhZwPlaceDetailAction.java +69 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/java/com/epoint/zwplace/action/GxhZwPlaceEditAction.java +235 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/java/com/epoint/zwplace/action/GxhZwPlaceListAction.java +145 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/java/com/epoint/zwplace/api/IGxhZwPlaceService.java +87 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/java/com/epoint/zwplace/api/entity/GxhZwPlace.java +306 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/java/com/epoint/zwplace/api/entity/GxhZwPlaceHistory.java +205 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/java/com/epoint/zwplace/api/entity/GxhZwWindow.java +184 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/java/com/epoint/zwplace/impl/GxhZwPlaceService.java +151 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/java/com/epoint/zwplace/impl/GxhZwPlaceServiceImpl.java +96 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/webapp/perpage/zwplace/gxhzwplaceadd.html +253 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/webapp/perpage/zwplace/gxhzwplacedetail.html +146 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/webapp/perpage/zwplace/gxhzwplaceedit.html +275 -0
- package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/webapp/perpage/zwplace/gxhzwplacelist.html +168 -0
- package/codex-skill/ep-stage/code-list-generate/evals/grade-code-list.mjs +184 -0
- package/codex-skill/ep-stage/code-list-generate/references/epoint-extraction.md +106 -0
- package/codex-skill/ep-stage/code-list-generate/references/field-contract.md +122 -0
- package/codex-skill/ep-stage/code-list-generate/scripts/validate-code-list.mjs +202 -0
- package/codex-skill/ep-stage/glue-create-project/SKILL.md +227 -223
- package/dist/src/cli/index.js +0 -0
- package/dist/src/extractors/code-list-json5.d.ts +5 -0
- package/dist/src/extractors/code-list-json5.d.ts.map +1 -1
- package/dist/src/generators/stage-skeleton-script.d.ts +4 -0
- package/dist/src/generators/stage-skeleton-script.d.ts.map +1 -1
- package/dist/src/generators/stage-skeleton-script.js +389 -362
- package/dist/src/testcase/contract-testcase-assembler.d.ts +10 -1
- package/dist/src/testcase/contract-testcase-assembler.d.ts.map +1 -1
- package/dist/src/testcase/contract-testcase-assembler.js +80 -21
- package/package.json +14 -12
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-list-generate
|
|
3
|
+
description: 使用时机:当用户(上游研发/出码方)要「生成 / 编写 / 产出 code_list.json5(出码总结、测试输入物料清单)」、交付需求给下游胶水测试流程、或显式触发 `/code-list-generate <源码根路径>` 时使用。即使用户只说「帮我把这个需求的源码整理成下游测试要的输入文件」「code_list 怎么写」,也应主动使用本 skill。本 skill 引导从 epoint 系源码(perpage/*.html + *Action.java + Entity.java)和 PRD 中提取事实,产出符合下游契约的 code_list.json5。
|
|
4
|
+
version: 2026-08-17+delete-scope-fix
|
|
5
|
+
argument-hint: "<源码根路径>(可选;不给则打印输入引导逐项询问)"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# code-list-generate:辅助上游生成 code_list.json5
|
|
9
|
+
|
|
10
|
+
`code_list.json5` 是「上游出码总结」:下游 `glue-generate-testcase` 用它生成测试用例。你是上游(出码方),职责是**把本需求真实实现的页面、按钮、字段、后端逻辑、业务规则写成静态可溯源的事实清单**。
|
|
11
|
+
|
|
12
|
+
先建立三个心智,再动手:
|
|
13
|
+
|
|
14
|
+
1. **无来源不写入**。下游有条铁律"无来源不入契约":你写的每个非平凡字段都应能指出出处(源码文件#锚点 或 PRD#章节),所以提取时顺手记 `evidence`。静态扫描看不到的运行时值(字典首选项、真实环境 origin)**不要编**,写进 `downstreamRuntimeNeeds` 说明"运行时才可确认"即可。
|
|
15
|
+
2. **语义分类是锚点,比原文照抄更重要**。下游不靠中文按钮名猜功能,靠 `pages[].role`、`buttons[].action` 这些枚举值定位入口、生成用例。分类错一个,下游就少一类用例或定位错按钮。分类规则见下文「按钮 action 分类」。
|
|
16
|
+
3. **留空优于编造**。账号、密码、运行态 URL(loginUrl/directUrl)上游不确定就留空,下游有交互补齐机制;但结构事实(页面、按钮、字段、规则)不能空。
|
|
17
|
+
|
|
18
|
+
## 禁区(写了会污染下游,宁可缺)
|
|
19
|
+
|
|
20
|
+
- 账号、密码、真实凭据 —— 那是环境侧数据,进下游 `credentials.json5`
|
|
21
|
+
- 最终 Playwright locator / xpath —— 你只提供 HTML/MiniUI 元素线索,locator 由下游生成器产出
|
|
22
|
+
- 测试用例、generationStrategy、执行结果、修补结论 —— 都是下游产物,不反向上灌
|
|
23
|
+
|
|
24
|
+
## 调用方式
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
/code-list-generate F:/knowledge-project/epoint-web-v9.5.2 # 给源码根 → 校验通过后继续收集其余输入
|
|
28
|
+
/code-list-generate # 无参数 → 打印输入引导,逐项询问
|
|
29
|
+
帮我把 zwplace 模块整理成 code_list.json5,源码在 … # 自然语言 → 同样进入本流程
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
入参按「源码根路径」理解:必须是能覆盖 `src/main/webapp/perpage/<模块>/` 与 `src/main/java/com/epoint/<模块>/` 的源码目录。路径不存在或结构不吻合时,不猜,直接向用户确认。
|
|
33
|
+
|
|
34
|
+
## 执行流程
|
|
35
|
+
|
|
36
|
+
**任务清单协议**:load 后**最先**单行展示技能版本(取本 SKILL.md frontmatter 的 `version` 字段),格式 `技能版本:code-list-generate <version>`;随后立即打印初始任务清单(Step 0);每完成一步输出单行 `第 N 步 — <中文名> 已完成,进入第 N+1 步 — <下一中文名>...`;Step 6 输出全勾选终态清单。
|
|
37
|
+
|
|
38
|
+
### Step 0 — 打印任务清单
|
|
39
|
+
|
|
40
|
+
```markdown
|
|
41
|
+
**生成 code_list.json5 任务清单**
|
|
42
|
+
|
|
43
|
+
- [ ] 第 1 步 — 收集输入(源码根 / 需求目录 / 需求文档)
|
|
44
|
+
- [ ] 第 2 步 — 定位模块边界并确认
|
|
45
|
+
- [ ] 第 3 步 — 提取页面、后端、规则事实
|
|
46
|
+
- [ ] 第 4 步 — 组装 code_list.json5
|
|
47
|
+
- [ ] 第 5 步 — 自校验
|
|
48
|
+
- [ ] 第 6 步 — 交付与人审提示
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Step 1 — 收集输入
|
|
52
|
+
|
|
53
|
+
从用户消息 / args 抽取源码根路径并绝对化,校验存在性与 epoint 结构(含 `src/main/webapp/perpage/` 与 `src/main/java/com/epoint/`)。四样输入的齐全度决定问什么:
|
|
54
|
+
|
|
55
|
+
- **源码根**(必须):包含本需求出码的源码目录。缺失 / 路径不存在 / 结构不吻合 → 不猜,free text 询问
|
|
56
|
+
- **需求目录**(建议):决定落盘位置。**落盘契约是硬性的**:必须写到 `<需求名目录>/_docs/code_list.json5`——下游按"code_list.json5 父目录的父目录的 basename"推导需求名,父目录必须叫 `_docs`。注意 `_docs/<需求名>/code_list.json5` 这类布局会让需求名被算成 `_docs`,是错误落盘。若源码根里能定位到需求目录结构则推断并告知用户,否则询问
|
|
57
|
+
- **需求文档**(可选但推荐):PRD / 需求确认单。缺失时明确告知"将按纯源码出稿,requirement 节标待补",然后继续
|
|
58
|
+
- **需求 ID / 名称**(可选):写入 `requirement.id/name`;缺失时用需求目录名兜底并在注释里说明
|
|
59
|
+
|
|
60
|
+
**无参数调用时**,先打印输入引导再收集:
|
|
61
|
+
|
|
62
|
+
> **生成 code_list.json5 需要这些输入:**
|
|
63
|
+
>
|
|
64
|
+
> 1. **源码根路径**(必须)——包含 `src/main/webapp/perpage/<模块>/` 和 `src/main/java/com/epoint/<模块>/` 的源码目录,如 `F:/knowledge-project/epoint-web-v9.5.2`
|
|
65
|
+
> 2. **需求目录**(建议)——code_list.json5 会写到 `<需求目录>/_docs/` 下
|
|
66
|
+
> 3. **PRD / 需求文档路径**(可选)——有它需求事实、业务角色和 PRD 缺口对照才完整;没有就先按源码出稿
|
|
67
|
+
> 4. **需求 ID / 名称**(可选)——如 REQ0011 / 014-测试需求10
|
|
68
|
+
>
|
|
69
|
+
> 直接把路径发我即可(可一次给全)。
|
|
70
|
+
|
|
71
|
+
### Step 2 — 定位模块边界
|
|
72
|
+
|
|
73
|
+
从需求名/PRD 推模块英文标识:找 `src/main/webapp/perpage/<module>/` 目录和 `src/main/java/com/epoint/<module>/` 包。把找到的页面 HTML、Action、Service、Entity 文件列给用户确认:"我识别到模块 `<module>`,包含这 N 个页面、M 个 Action,对吗?"——模块边界圈错,后面全错。
|
|
74
|
+
|
|
75
|
+
### Step 3 — 提取事实
|
|
76
|
+
|
|
77
|
+
按 `references/epoint-extraction.md` 的方法逐类提取:页面四角色、查询字段、按钮、行操作、表单字段、子表、后端方法、数据模型、实体、业务规则。该文件含每类事实在 epoint 源码里的具体位置和抠取方法。
|
|
78
|
+
|
|
79
|
+
### Step 4 — 组装文件
|
|
80
|
+
|
|
81
|
+
1. 拷贝 `assets/code_list.template.json5` 为底稿,逐节替换占位内容
|
|
82
|
+
2. 拿不准某节的写法时,对照 `assets/example-zwplace.json5`(一份真实需求的完整实例)
|
|
83
|
+
3. 字段含义、必填性、下游消费点查 `references/field-contract.md`
|
|
84
|
+
|
|
85
|
+
### Step 5 — 自校验
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
node <skill目录>/scripts/validate-code-list.mjs <需求目录>/_docs/code_list.json5
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
脚本校验:JSON5 可解析、必填字段齐全、action 枚举合法、源码路径真实存在、无凭据泄漏。脚本全绿后再人工过一遍「交付前对照清单」(本文件末节)。
|
|
92
|
+
|
|
93
|
+
### Step 6 — 交付与人审提示
|
|
94
|
+
|
|
95
|
+
写入 `<需求目录>/_docs/code_list.json5`,然后中文提示用户:
|
|
96
|
+
|
|
97
|
+
> code_list.json5 已生成。请重点人工复核:
|
|
98
|
+
> 1. 按钮 action 分类(create/delete/export/custom)是否与页面实际一致
|
|
99
|
+
> 2. businessRules 是否漏了源码里的校验/阻断文案
|
|
100
|
+
> 3. prdMentionedButNotImplemented 是否如实反映了 PRD 有但未做的部分
|
|
101
|
+
>
|
|
102
|
+
> 交付下游后即可运行 `/glue-generate-testcase <需求目录>/_docs/code_list.json5`。
|
|
103
|
+
|
|
104
|
+
## 按钮 action 分类(下游效果的直接来源)
|
|
105
|
+
|
|
106
|
+
`pages[].buttons[].action` 只允许这几个值,每个值直接决定下游生成什么:
|
|
107
|
+
|
|
108
|
+
| action | 用在哪 | 下游效果 |
|
|
109
|
+
|---|---|---|
|
|
110
|
+
| `create` | 列表页"新增xx"按钮;新增页"保存并关闭" | 生成新增用例入口;label 用于拼定位表达式 |
|
|
111
|
+
| `delete` | 列表页"删除选定" | 生成删除用例;配合 businessRules 决定删除断言 |
|
|
112
|
+
| `export` | 列表页导出(mini-dataexport 组件) | 生成导出用例 |
|
|
113
|
+
| `custom` | 列表页其他业务按钮(如"同步窗口管理系统") | 进入 AI 候选用例,须补 `customAction` + `backendMethod` |
|
|
114
|
+
| `save` | 新增/编辑页"保存"(草稿) | 须补 `customAction`(如 saveDraft)|
|
|
115
|
+
| `update` | 编辑页"保存并关闭" | 生成更新用例的提交动作 |
|
|
116
|
+
| `cancel` | "关闭"按钮 | 不生成用例,仅记录 |
|
|
117
|
+
|
|
118
|
+
行操作 `rowActions[]`:列表行内"修改"用 `actionName: 'openEdit'` 或 `action: 'edit'`(下游按此找编辑入口);"查看"用 `actionName: 'openDetail'`。
|
|
119
|
+
|
|
120
|
+
**businessRules 的放大效应**:`scope` 含 `'delete'` 的规则会让下游把删除用例断言改为"阻断",并把 `assertionHint`(如"在用数据不可删除!")作为预期提示文案。源码里 Action 方法中的校验消息、必填提示都是业务规则,不要漏。
|
|
121
|
+
|
|
122
|
+
**`scope: ['delete']` 的纪律**:只有「记录状态/条件阻断删除」的规则(如"在用数据不可删除")才配 `scope: ['delete']` + `assertionHint`。「未勾选拦截」(如"请选择要删除的数据!")、「二次确认文案」这类**操作前置提示不标 delete scope**(可标 `scope: ['ui']` 或并入其他规则的 description)——下游把 scope 含 delete 的规则当作删除断言策略来源(装配器虽有"优先阻断语义文案"的自愈,但只是兜底,不应依赖)。若确有多条 delete scope 规则,记录级阻断规则必须排最前。
|
|
123
|
+
|
|
124
|
+
## 查询字段与数据主键
|
|
125
|
+
|
|
126
|
+
- `pages[role=list].queryFields[]`:`component: 'mini-textbox'` → 输入框;`'mini-combobox'` → 下拉。带 `bind: 'dataBean.xxx'` 时下游能推出字段名。
|
|
127
|
+
- 查询字段若同时出现在新增页 `formFields[]`,下游会为它生成完整 增/查/改/删 四条用例;只在列表页出现的只生成查询用例。
|
|
128
|
+
- `entities[].fields[]` 里标 `dataKey: true` 的字段是业务主键(如场所名称),下游用它做测试数据的种子值。
|
|
129
|
+
|
|
130
|
+
## 角色(凭据模板的行数来源)
|
|
131
|
+
|
|
132
|
+
角色有三个承载位置,**语义不同,不要混用**:
|
|
133
|
+
|
|
134
|
+
| 位置 | 语义 | 放什么 |
|
|
135
|
+
|---|---|---|
|
|
136
|
+
| `requirement.businessRoles[]` | PRD 角色**事实** | PRD 提到的全部角色,如实写全(含无实现入口的)——这是需求事实,不删 |
|
|
137
|
+
| **顶层 `roles[]`** | 本次交付**需要登录执行**的角色 | 只放有实现入口、要跑用例的角色(如 业务管理员)。**下游凭据模板/requiredRoles 的直接事实源** |
|
|
138
|
+
| `testAccess.systems[].roles[]` | 登录访问模板骨架 | 与顶层 `roles[]` 同一角色集,username/password 留空 |
|
|
139
|
+
|
|
140
|
+
**实测教训(假修复)**:只精简 testAccess 是不够的——下游消费优先级是 `顶层 roles[]` > `requirement.businessRoles` > `testAccess`。businessRoles 里的 PRD 角色删不得(是需求事实),不写顶层 roles[] 圈定,凭据模板就会按 businessRoles 全量生成,给用不到的角色造出凭据行。**凡是 PRD 角色 ≠ 需登录角色,就必须写顶层 roles[]**(每项:`systemName` / `role` / `permission` / `description` / `menuPath` 可选)。
|
|
141
|
+
|
|
142
|
+
## 系统名与模块名取源
|
|
143
|
+
|
|
144
|
+
- `systems[].name`:最终用户登录的那个系统的**正式中文全称**(须与下游 credentials.json5 的 systemName 严格一致)。取源优先级:PRD/需求文档写明的业务系统名 > 运行系统登录页/标题 > project_context 的 products[].name。**产品/项目代号不是系统名**(如实测中误取「综管」而真实系统名是「集成受理平台」);只有代号可用时,填代号并在 `downstreamRuntimeNeeds` 登记"系统名待确认"。
|
|
145
|
+
- `moduleName`:取需求/PRD 的**模块中文业务名**(如"场所窗口信息管理"),不取单个页面的 title("场所信息管理列表"这类进 `pages[].title`)。
|
|
146
|
+
|
|
147
|
+
## 交付前对照清单
|
|
148
|
+
|
|
149
|
+
- [ ] 顶层 `module` / `moduleName` / `systems` / `pages` 齐全
|
|
150
|
+
- [ ] 落盘位置是 `<需求名目录>/_docs/code_list.json5`(父目录必须叫 `_docs`,否则下游需求名推导出错)
|
|
151
|
+
- [ ] `sourceRoot.root` 是绝对路径,所有 `pages[].path`、`sourceFiles` 路径在其下真实存在
|
|
152
|
+
- [ ] `pages[role=list]` 有 `queryFields`、`buttons`、`rowActions`;`directUrl` 已知则填,不知留空
|
|
153
|
+
- [ ] 每个按钮的 `action` 分类与上表一致,`custom` 按钮带 `customAction` + `backendMethod`
|
|
154
|
+
- [ ] 子表列的 `component` 只写了 `input` / `listbox`(或直接省略),没有写 MiniUI 组件名
|
|
155
|
+
- [ ] `businessRules` 覆盖源码里所有校验/阻断文案;删除类规则带 `assertionHint`(一条规则一个主文案),且只有记录级阻断规则标 `scope: ['delete']`(拦截提示标 `ui`)
|
|
156
|
+
- [ ] `systems[].name` 是系统正式中文全称(非产品代号);`moduleName` 是模块业务名(非单页 title)
|
|
157
|
+
- [ ] PRD 角色与需登录角色不一致时,已写顶层 `roles[]` 圈定;`testAccess` 角色集与之对齐
|
|
158
|
+
- [ ] `routeHints.contextPathHint` 有来源才写,无来源则留空并登记 `downstreamRuntimeNeeds`
|
|
159
|
+
- [ ] 运行态才能确认的值都进了 `downstreamRuntimeNeeds`,没有编造的静态事实
|
|
160
|
+
- [ ] 全文无账号密码、无最终 locator
|
|
161
|
+
- [ ] validate 脚本全绿
|
|
162
|
+
|
|
163
|
+
## 按需加载
|
|
164
|
+
|
|
165
|
+
- `references/field-contract.md` — 逐字段契约:含义、必填性、下游消费点、枚举值
|
|
166
|
+
- `references/epoint-extraction.md` — epoint 系源码提取方法(MiniUI 组件、Action 注解、实体映射)
|
|
167
|
+
- `assets/code_list.template.json5` — 带注释的空模板(组装的起点)
|
|
168
|
+
- `assets/example-zwplace.json5` — 完整真实实例(写法拿不准时对照)
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* code_list.json5 模板 —— 上游出码总结
|
|
3
|
+
*
|
|
4
|
+
* 用法:拷贝本文件到 <需求目录>/_docs/code_list.json5,逐节替换 <尖括号> 占位内容,
|
|
5
|
+
* 删除用不到的节(如无子表删 childGrids、无 custom 按钮删对应条目)。
|
|
6
|
+
* 完整实例参考同目录 example-zwplace.json5;字段契约见 references/field-contract.md。
|
|
7
|
+
*
|
|
8
|
+
* 三条铁律:无来源不写入(带 evidence);语义分类是锚点(role/action 枚举);
|
|
9
|
+
* 留空优于编造(账号密码、运行态 URL 可留空,结构事实不能空)。
|
|
10
|
+
*/
|
|
11
|
+
{
|
|
12
|
+
schemaVersion: 'ep-stage-code-list/v1-draft',
|
|
13
|
+
|
|
14
|
+
/* ─────── 需求事实(从 PRD 提取)─────── */
|
|
15
|
+
requirement: {
|
|
16
|
+
id: '<需求ID,如 REQ0011;缺失时用需求目录名兜底并注释说明>',
|
|
17
|
+
name: '<需求名称>',
|
|
18
|
+
type: '<新增功能/功能优化/缺陷修复>',
|
|
19
|
+
priority: '<高/中/低>',
|
|
20
|
+
targetEnds: ['<PC-管理端/H5-移动端/...>'],
|
|
21
|
+
businessRoles: [
|
|
22
|
+
{
|
|
23
|
+
label: '<角色名,如 业务管理员>',
|
|
24
|
+
targetEnd: '<所属端>',
|
|
25
|
+
responsibilities: ['<职责描述>'],
|
|
26
|
+
evidence: ['<PRD文件名>#<章节锚点>'],
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
/* ─────── 源码根(下游解析 pages[].path 的依据)─────── */
|
|
32
|
+
sourceRoot: {
|
|
33
|
+
root: '<源码根绝对路径,如 F:/project/epoint-web-v9.5.2>',
|
|
34
|
+
pathBase: 'knowledgeRoot',
|
|
35
|
+
sourceFilesAreRelativeTo: 'sourceRoot.root',
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
/* ─────── 模块标识(CLI 四大必填之二)─────── */
|
|
39
|
+
module: '<模块英文标识,取 perpage 目录名/java 包名>',
|
|
40
|
+
moduleName: '<模块中文名>',
|
|
41
|
+
description: '<一句话概括本次出码覆盖范围>',
|
|
42
|
+
|
|
43
|
+
/* ─────── 业务系统(CLI 四大必填之三)─────── */
|
|
44
|
+
systems: [
|
|
45
|
+
{
|
|
46
|
+
// 系统正式中文全称(最终用户登录的那个系统),非产品/项目代号;
|
|
47
|
+
// 取源优先级:PRD > 登录页标题 > project_context products[].name;只有代号可用时登记 downstreamRuntimeNeeds
|
|
48
|
+
name: '<系统中文全称;须与下游 credentials.json5 的 systemName 严格一致>',
|
|
49
|
+
version: '<平台版本>',
|
|
50
|
+
frameVersion: '<框架版本>',
|
|
51
|
+
targetEnd: '<目标端>',
|
|
52
|
+
defaultLoginUrl: '<登录地址样板;不确定可留空>',
|
|
53
|
+
evidence: ['<出处>'],
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
|
|
57
|
+
/* ─────── 顶层 roles[]:本次交付需要登录执行的角色(下游凭据模板/requiredRoles 的事实源)─────── */
|
|
58
|
+
/* 下游优先级:roles[] > requirement.businessRoles > testAccess。PRD 角色 ≠ 需登录角色时必须写本节圈定,
|
|
59
|
+
否则凭据模板会按 businessRoles 全量生成。两者一致时本节可省略。 */
|
|
60
|
+
roles: [
|
|
61
|
+
{
|
|
62
|
+
systemName: '<与 systems[].name 一致>',
|
|
63
|
+
role: '<角色名>',
|
|
64
|
+
permission: '<权限说明>',
|
|
65
|
+
description: '<角色业务描述>',
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
|
|
69
|
+
/* ─────── 登录访问模板骨架(账号密码一律留空;角色集与顶层 roles[] 对齐)─────── */
|
|
70
|
+
testAccess: {
|
|
71
|
+
systems: [
|
|
72
|
+
{
|
|
73
|
+
systemName: '<与 systems[].name 一致>',
|
|
74
|
+
loginUrl: '',
|
|
75
|
+
roles: [
|
|
76
|
+
{
|
|
77
|
+
role: '<角色名>',
|
|
78
|
+
username: '',
|
|
79
|
+
password: '',
|
|
80
|
+
permissions: ['<权限点,如 新增/修改/删除/导出/查看>'],
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
moduleInfo: {
|
|
88
|
+
id: '<同 module>',
|
|
89
|
+
name: '<同 moduleName>',
|
|
90
|
+
packagePath: 'com.epoint.<module>',
|
|
91
|
+
frontendBasePath: 'src/main/webapp/perpage/<module>',
|
|
92
|
+
backendBasePath: 'src/main/java/com/epoint/<module>',
|
|
93
|
+
deriveRule: '<如何归纳出模块标识的一句话>',
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
/* ─────── 实现范围与 PRD 缺口(双向对照,只写静态可证实的)─────── */
|
|
97
|
+
implementedScope: [
|
|
98
|
+
{
|
|
99
|
+
featureId: 'F001',
|
|
100
|
+
featureName: '<功能点名>',
|
|
101
|
+
implementedBy: ['pages.list', 'backend.actions.<XxxAction>'],
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
prdMentionedButNotImplemented: [
|
|
105
|
+
{
|
|
106
|
+
featureIds: ['F0xx'],
|
|
107
|
+
featureName: '<PRD 有但未实现的功能>',
|
|
108
|
+
reason: '<PRD 描述了…,但 sourceFiles 中未发现对应页面或实现文件>',
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
|
|
112
|
+
/* ─────── 出码文件清单(页面 evidence 的引用目标)─────── */
|
|
113
|
+
sourceFiles: {
|
|
114
|
+
frontend: [
|
|
115
|
+
{ role: 'list', path: 'src/main/webapp/perpage/<module>/<x>list.html', title: '<页面标题>' },
|
|
116
|
+
// 按需补 add / edit / detail
|
|
117
|
+
],
|
|
118
|
+
actions: [
|
|
119
|
+
{ name: '<XxxListAction>', path: 'src/main/java/com/epoint/<module>/action/<XxxListAction>.java', restController: '<xxxlistaction>' },
|
|
120
|
+
],
|
|
121
|
+
services: [
|
|
122
|
+
{ name: '<IXxxService>', path: '.../api/<IXxxService>.java', kind: 'interface' },
|
|
123
|
+
{ name: '<XxxServiceImpl>', path: '.../impl/<XxxServiceImpl>.java', kind: 'serviceImpl' },
|
|
124
|
+
{ name: '<XxxService>', path: '.../impl/<XxxService>.java', kind: 'dao' },
|
|
125
|
+
],
|
|
126
|
+
entities: [
|
|
127
|
+
{ name: '<XxxEntity>', path: '.../api/entity/<XxxEntity>.java', table: '<表名>' },
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
/* ─────── 页面事实(CLI 四大必填之四)─────── */
|
|
132
|
+
pages: [
|
|
133
|
+
/* ─── 列表页 ─── */
|
|
134
|
+
{
|
|
135
|
+
pageId: '<x>list',
|
|
136
|
+
role: 'list',
|
|
137
|
+
title: '<页面标题>',
|
|
138
|
+
path: 'src/main/webapp/perpage/<module>/<x>list.html',
|
|
139
|
+
initAction: '<xxxlistaction>',
|
|
140
|
+
directUrl: '<待测列表页直达链接;不确定留空,下游交互补齐>',
|
|
141
|
+
description: '<页面能力一句话>',
|
|
142
|
+
queryFields: [
|
|
143
|
+
// mini-textbox → 输入框;mini-combobox → 下拉(补 modelRef)
|
|
144
|
+
{ label: '<查询字段中文标签>', bind: 'dataBean.<field>', component: 'mini-textbox' },
|
|
145
|
+
{ label: '<下拉查询字段>', bind: 'dataBean.<field>', component: 'mini-combobox', modelRef: '<field>Model' },
|
|
146
|
+
],
|
|
147
|
+
toolbarActions: [
|
|
148
|
+
{ label: '<按钮原文>', actionName: '<JS函数名>', opens: 'pages.add' },
|
|
149
|
+
],
|
|
150
|
+
// action 枚举:create/delete/export/custom/save/update/cancel —— 分类决定下游生成什么用例
|
|
151
|
+
buttons: [
|
|
152
|
+
{ label: '<新增xx>', action: 'create', backendMethod: null },
|
|
153
|
+
{ label: '<删除选定>', action: 'delete', backendMethod: '<deleteSelect>' },
|
|
154
|
+
{ label: '<导出>', action: 'export', backendMethod: '<getExportModel>' },
|
|
155
|
+
// 其他业务按钮:action: 'custom' + customAction + backendMethod → 进入 AI 候选用例
|
|
156
|
+
],
|
|
157
|
+
rowActions: [
|
|
158
|
+
{ label: '修改', actionName: 'openEdit', opens: 'pages.edit' },
|
|
159
|
+
{ label: '查看', actionName: 'openDetail', opens: 'pages.detail' },
|
|
160
|
+
],
|
|
161
|
+
dataGrid: {
|
|
162
|
+
id: 'datagrid',
|
|
163
|
+
idField: '<主键字段,如 rowguid>',
|
|
164
|
+
backendMethod: '<getDataGridData>',
|
|
165
|
+
columns: [{ field: '<field>', label: '<列头中文>' }],
|
|
166
|
+
},
|
|
167
|
+
backendMethods: ['<该页 Action 的方法名清单>'],
|
|
168
|
+
evidence: ['sourceFiles.frontend[role=list]', 'sourceFiles.actions[name=<XxxListAction>]'],
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
/* ─── 新增页 ─── */
|
|
172
|
+
{
|
|
173
|
+
pageId: '<x>add',
|
|
174
|
+
role: 'add',
|
|
175
|
+
title: '<页面标题>',
|
|
176
|
+
path: 'src/main/webapp/perpage/<module>/<x>add.html',
|
|
177
|
+
initAction: '<xxxaddaction>',
|
|
178
|
+
description: '<页面能力一句话>',
|
|
179
|
+
formFields: [
|
|
180
|
+
{ label: '<字段中文>', bind: 'dataBean.<field>', component: 'mini-textbox', required: true, maxLength: 50 },
|
|
181
|
+
],
|
|
182
|
+
toolbarActions: [
|
|
183
|
+
{ label: '保存', actionName: 'save', backendMethod: '<saveDraft>' },
|
|
184
|
+
{ label: '保存并关闭', actionName: 'saveAndClose', backendMethod: '<add>' },
|
|
185
|
+
{ label: '关闭', actionName: "epoint.closeDialog('close')" },
|
|
186
|
+
],
|
|
187
|
+
buttons: [
|
|
188
|
+
{ label: '保存', action: 'save', customAction: '<saveDraft>', backendMethod: '<saveDraft>' },
|
|
189
|
+
{ label: '保存并关闭', action: 'create', backendMethod: '<add>' },
|
|
190
|
+
{ label: '关闭', action: 'cancel', backendMethod: null },
|
|
191
|
+
],
|
|
192
|
+
rowActions: [],
|
|
193
|
+
// 有内嵌子表时取消注释;无子表整节删除
|
|
194
|
+
// 注意:columns[].component 只能写 'input'/'listbox'(下游门禁强校验),拿不准就不写该键
|
|
195
|
+
// childGrids: [
|
|
196
|
+
// {
|
|
197
|
+
// id: '<grid的DOM id>',
|
|
198
|
+
// label: '<子表中文名>',
|
|
199
|
+
// actions: [{ label: '<添加xx>', actionName: '<addXxx>' }],
|
|
200
|
+
// columns: [{ field: '<field>', label: '<列头>', required: true, component: 'input', modelRef: '<下拉时补>' }],
|
|
201
|
+
// },
|
|
202
|
+
// ],
|
|
203
|
+
backendMethods: ['<方法名清单>'],
|
|
204
|
+
evidence: ['sourceFiles.frontend[role=add]', 'sourceFiles.actions[name=<XxxAddAction>]'],
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
/* ─── 编辑页(字段/子表与新增页相同可用 same_as 引用)─── */
|
|
208
|
+
{
|
|
209
|
+
pageId: '<x>edit',
|
|
210
|
+
role: 'edit',
|
|
211
|
+
title: '<页面标题>',
|
|
212
|
+
path: 'src/main/webapp/perpage/<module>/<x>edit.html',
|
|
213
|
+
initAction: '<xxxeditaction>',
|
|
214
|
+
description: '<页面能力一句话>',
|
|
215
|
+
formFields: ['same_as_pages.add.formFields'],
|
|
216
|
+
childGrids: ['same_as_pages.add.childGrids'],
|
|
217
|
+
toolbarActions: [
|
|
218
|
+
{ label: '保存', actionName: 'save', backendMethod: '<saveDraft>' },
|
|
219
|
+
{ label: '保存并关闭', actionName: 'saveAndClose', backendMethod: '<save>' },
|
|
220
|
+
{ label: '关闭', actionName: "epoint.closeDialog('close')" },
|
|
221
|
+
],
|
|
222
|
+
buttons: [
|
|
223
|
+
{ label: '保存', action: 'save', customAction: '<saveDraft>', backendMethod: '<saveDraft>' },
|
|
224
|
+
{ label: '保存并关闭', action: 'update', backendMethod: '<save>' },
|
|
225
|
+
{ label: '关闭', action: 'cancel', backendMethod: null },
|
|
226
|
+
],
|
|
227
|
+
rowActions: [],
|
|
228
|
+
backendMethods: ['<pageLoad 等方法清单>'],
|
|
229
|
+
evidence: ['sourceFiles.frontend[role=edit]', 'sourceFiles.actions[name=<XxxEditAction>]'],
|
|
230
|
+
},
|
|
231
|
+
|
|
232
|
+
/* ─── 详情页(只读;无则整节删除)─── */
|
|
233
|
+
{
|
|
234
|
+
pageId: '<x>detail',
|
|
235
|
+
role: 'detail',
|
|
236
|
+
title: '<页面标题>',
|
|
237
|
+
path: 'src/main/webapp/perpage/<module>/<x>detail.html',
|
|
238
|
+
initAction: '<xxxdetailaction>',
|
|
239
|
+
description: '<只读展示说明>',
|
|
240
|
+
buttons: [{ label: '关闭', action: 'cancel', backendMethod: null }],
|
|
241
|
+
rowActions: [],
|
|
242
|
+
backendMethods: ['<pageLoad 等>'],
|
|
243
|
+
evidence: ['sourceFiles.frontend[role=detail]'],
|
|
244
|
+
},
|
|
245
|
+
],
|
|
246
|
+
|
|
247
|
+
/* ─────── 后端事实 ─────── */
|
|
248
|
+
backend: {
|
|
249
|
+
callChain: '前端页面 -> Action -> I<Xxx>Service -> <Xxx>ServiceImpl -> <Xxx>Service(DAO) -> 数据库',
|
|
250
|
+
actions: [
|
|
251
|
+
{
|
|
252
|
+
name: '<XxxListAction>',
|
|
253
|
+
restController: '<xxxlistaction>',
|
|
254
|
+
methods: [
|
|
255
|
+
{ name: '<方法名>', description: '<一句话说明,含校验/阻断行为>' },
|
|
256
|
+
],
|
|
257
|
+
},
|
|
258
|
+
],
|
|
259
|
+
services: [
|
|
260
|
+
{ name: '<XxxService>', kind: 'dao', methods: ['<insert>', '<deleteByGuid>'] },
|
|
261
|
+
],
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
/* ─────── 下拉数据模型(有下拉才保留)─────── */
|
|
265
|
+
dataModels: [
|
|
266
|
+
{
|
|
267
|
+
ref: '<field>Model',
|
|
268
|
+
label: '<中文名>',
|
|
269
|
+
sourceKind: 'dictionary_runtime', // dictionary_runtime / static_options / db_runtime
|
|
270
|
+
actionMethods: ['<XxxAction.getXxxModel>'],
|
|
271
|
+
dictionaryCode: '<字典code>', // dictionary_runtime 时填;static_options 改用 options: [...]
|
|
272
|
+
optionsKnownAtStaticTime: false, // 运行态才知道选项时务必 false,不要把猜的选项写进来
|
|
273
|
+
},
|
|
274
|
+
],
|
|
275
|
+
|
|
276
|
+
/* ─────── 实体(业务主键标 dataKey: true,下游用它做测试数据种子)─────── */
|
|
277
|
+
entities: [
|
|
278
|
+
{
|
|
279
|
+
name: '<XxxEntity>',
|
|
280
|
+
table: '<表名>',
|
|
281
|
+
description: '<实体说明>',
|
|
282
|
+
fields: [
|
|
283
|
+
{ name: '<field>', type: 'String(50)', label: '<中文>', dataKey: true },
|
|
284
|
+
],
|
|
285
|
+
},
|
|
286
|
+
],
|
|
287
|
+
|
|
288
|
+
/* ─────── 业务规则(源码里的校验/阻断文案,删除类规则直接决定下游删除断言)─────── */
|
|
289
|
+
businessRules: [
|
|
290
|
+
{
|
|
291
|
+
ruleId: 'delete.<xxx>',
|
|
292
|
+
description: '<如:仅草稿状态可删除,非草稿删除被阻断>',
|
|
293
|
+
scope: ['delete'],
|
|
294
|
+
sourceKind: 'static_scan',
|
|
295
|
+
assertionHint: '<阻断提示原文,如 在用数据不可删除!>',
|
|
296
|
+
evidence: ['src/main/java/com/epoint/<module>/action/<XxxListAction>.java#<deleteSelect>'],
|
|
297
|
+
},
|
|
298
|
+
],
|
|
299
|
+
|
|
300
|
+
/* ─────── 集成点(无则整节删除)───────
|
|
301
|
+
integrations: [
|
|
302
|
+
{
|
|
303
|
+
integrationId: '<id>',
|
|
304
|
+
label: '<名称>',
|
|
305
|
+
trigger: '<触发入口>',
|
|
306
|
+
currentImplementation: '<service_stub / 已实现 / ...>',
|
|
307
|
+
evidence: ['<出处>'],
|
|
308
|
+
},
|
|
309
|
+
],
|
|
310
|
+
─────── */
|
|
311
|
+
|
|
312
|
+
/* ─────── 路由线索 ─────── */
|
|
313
|
+
routeHints: {
|
|
314
|
+
// 有来源才写(真实部署 context path / loginUrl 反推);无来源留空字符串并登记 downstreamRuntimeNeeds
|
|
315
|
+
contextPathHint: '/epoint-web',
|
|
316
|
+
rule: '运行环境 origin + contextPathHint + 页面 path 去掉 src/main/webapp 后的路径。',
|
|
317
|
+
pages: [{ pageId: '<x>list', routePath: '/perpage/<module>/<x>list.html' }],
|
|
318
|
+
runtimeOriginRequired: true,
|
|
319
|
+
},
|
|
320
|
+
|
|
321
|
+
/* ─────── 运行态待补(静态扫不到的值登记在这里,不编造)─────── */
|
|
322
|
+
downstreamRuntimeNeeds: [
|
|
323
|
+
{ needId: 'runtime.origin', description: '真实运行环境 origin 由运行配置或人工交互提供。' },
|
|
324
|
+
],
|
|
325
|
+
}
|