h3factory 0.0.0-stage → 0.2.2
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 +241 -2
- package/bin/h3factory.js +87 -0
- package/cli.py +951 -0
- package/config.example.json +8 -0
- package/docs/platform_gotchas.md +118 -0
- package/docs/requirement_checklist.md +54 -0
- package/docs/schema_doc.md +216 -0
- package/docs/workflow.md +146 -0
- package/h3design/__init__.py +0 -0
- package/h3design/controls.py +414 -0
- package/h3design/dsl.py +553 -0
- package/h3design/newbuilder.py +852 -0
- package/h3platform/__init__.py +0 -0
- package/h3platform/api.py +214 -0
- package/h3platform/h3console.py +99 -0
- package/h3platform/settings.py +74 -0
- package/h3verify/__init__.py +0 -0
- package/h3verify/verify.py +217 -0
- package/package.json +51 -4
- package/projects/demo/dicts.json +10 -0
- package/projects/demo/generated/product.cs +82 -0
- package/projects/demo/groups.json +3 -0
- package/projects/demo/sheets/product.json +24 -0
- package/scripts/postinstall.js +109 -0
- package/skills/h3factory/SKILL.md +137 -0
- package/skills/h3yun-dev-assistant/BACKEND_GUIDELINES.md +120 -0
- package/skills/h3yun-dev-assistant/DEV_DOC_ROUTING.md +39 -0
- package/skills/h3yun-dev-assistant/FORM_ACTIONS_GUIDELINES.md +116 -0
- package/skills/h3yun-dev-assistant/FRONTEND_GUIDELINES.md +47 -0
- package/skills/h3yun-dev-assistant/OPENAPI_GUIDELINES.md +8 -0
- package/skills/h3yun-dev-assistant/PLATFORM_CONSTRAINTS.md +160 -0
- package/skills/h3yun-dev-assistant/POST_GUIDELINES.md +47 -0
- package/skills/h3yun-dev-assistant/PRACTICE_NOTES.md +626 -0
- package/skills/h3yun-dev-assistant/SKILL.md +70 -0
- package/skills/h3yun-dev-assistant/SQL_GUIDELINES.md +9 -0
- package/skills/h3yun-dev-assistant/TOOL_FUNCTIONS.md +96 -0
- package/templates/controller.cs.tpl +78 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// =====================================================================
|
|
2
|
+
// 产品登记表 —— product 提交时 OnSubmit 服务端代码(h3factory cli gen 骨架)
|
|
3
|
+
// 【粘贴位置】表单设计器 → 业务规则/后端事件 → 提交时
|
|
4
|
+
// 【类名】必须等于当前表单 SchemaCode(本文件已填:product)
|
|
5
|
+
//
|
|
6
|
+
// 通用模式参考(奥驰样例已编译通过的实现,可直接照抄结构):
|
|
7
|
+
// 料号生成_料号申请表.cs —— 5-key 计数 upsert(NextCounter)、跨表查重、
|
|
8
|
+
// 表单存名称→字典查代码、规格描述按模板单遍替换
|
|
9
|
+
// D:\AI\自动编号\料号生成_料号申请表.cs
|
|
10
|
+
// =====================================================================
|
|
11
|
+
using System;
|
|
12
|
+
using System.Collections.Generic;
|
|
13
|
+
using System.Data;
|
|
14
|
+
using System.Text;
|
|
15
|
+
using System.Text.RegularExpressions;
|
|
16
|
+
using H3;
|
|
17
|
+
|
|
18
|
+
public class product : H3.SmartForm.SmartFormController
|
|
19
|
+
{
|
|
20
|
+
// ---------------- SchemaCode(与本工程 projects registry.json 一致) ----------------
|
|
21
|
+
private const string SELF_SCHEMA = "product"; // 产品登记表(本表)
|
|
22
|
+
|
|
23
|
+
private const string F_PCODE = "pcode"; // 产品编码
|
|
24
|
+
private const string F_PNAME = "pname"; // 品名
|
|
25
|
+
private const string F_BIGCAT = "bigcat"; // 大类别
|
|
26
|
+
private const string F_PRICE = "price"; // 参考价
|
|
27
|
+
private const string F_ENABLED = "enabled"; // 启用
|
|
28
|
+
private const string F_SPECDESC = "specdesc"; // 规格描述
|
|
29
|
+
private const string F_STATUS = "status"; // 状态
|
|
30
|
+
// ================= 工具 =================
|
|
31
|
+
private static string SqlStr(string v) { return (v + string.Empty).Replace("'", "''"); }
|
|
32
|
+
private static string S(object v) { return (v + string.Empty).Trim(); } // BizObject 字段值为 object
|
|
33
|
+
private bool Flag(object v)
|
|
34
|
+
{
|
|
35
|
+
if (v == null) return false;
|
|
36
|
+
string s = (v + string.Empty).ToLower();
|
|
37
|
+
return s == "true" || s == "1" || s == "是";
|
|
38
|
+
}
|
|
39
|
+
private long AsLong(object v)
|
|
40
|
+
{
|
|
41
|
+
long n;
|
|
42
|
+
if (v == null || !long.TryParse(v + string.Empty, out n)) return 0;
|
|
43
|
+
return n;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public product(H3.SmartForm.SmartFormRequest request) : base(request) { }
|
|
47
|
+
|
|
48
|
+
protected override void OnLoad(H3.SmartForm.LoadSmartFormResponse response)
|
|
49
|
+
{
|
|
50
|
+
base.OnLoad(response);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ================= 提交时入口 =================
|
|
54
|
+
protected override void OnSubmit(string actionName, H3.SmartForm.SmartFormPostValue postValue,
|
|
55
|
+
H3.SmartForm.SubmitSmartFormResponse response)
|
|
56
|
+
{
|
|
57
|
+
try
|
|
58
|
+
{
|
|
59
|
+
H3.DataModel.BizObject form = this.Request.BizObject;
|
|
60
|
+
|
|
61
|
+
// 仅"草稿 → 首次提交"触发生成;编辑重提/流转保存直接跳过。
|
|
62
|
+
bool firstSubmit = actionName == "Submit" &&
|
|
63
|
+
this.Request.BizObject.Status == H3.DataModel.BizObjectStatus.Draft;
|
|
64
|
+
if (!firstSubmit) return;
|
|
65
|
+
|
|
66
|
+
// ============================================================
|
|
67
|
+
// TODO 业务规则写在这里:
|
|
68
|
+
// 1. 读本表单字段 string v = S(form[F_XXX]);
|
|
69
|
+
// 2. 读关联表记录 BizObject rule = BizObject.Load(this.Request.UserContext.UserId,
|
|
70
|
+
// this.Engine, REF_SCHEMA, refId, false);
|
|
71
|
+
// 3. 计数/查重等跨表逻辑参考 料号生成_料号申请表.cs
|
|
72
|
+
// (Engine.Query.QueryTable / COALESCE / BizObject.Create+Update)
|
|
73
|
+
// 4. 错误拦截 response.Errors.Add("提示信息"); return;
|
|
74
|
+
// 5. 生成字段写回 form[F_XXX] = "结果"; (本表字段直接改 form 即保存)
|
|
75
|
+
// ============================================================
|
|
76
|
+
}
|
|
77
|
+
catch (Exception ex)
|
|
78
|
+
{
|
|
79
|
+
response.Errors.Add("生成失败: " + ex.Message);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "产品登记表",
|
|
3
|
+
"nameSchema": "{pname}",
|
|
4
|
+
"useOwner": true,
|
|
5
|
+
"layout": "auto4",
|
|
6
|
+
"group": "主数据",
|
|
7
|
+
"controls": [
|
|
8
|
+
{ "type": "text", "key": "pcode", "label": "产品编码" },
|
|
9
|
+
{ "type": "text", "key": "pname", "label": "品名", "required": true },
|
|
10
|
+
{ "type": "dropdown", "key": "bigcat", "label": "大类别", "dict": "大类别" },
|
|
11
|
+
{ "type": "number", "key": "price", "label": "参考价", "decimal": 2 },
|
|
12
|
+
{ "type": "switch", "key": "enabled", "label": "启用", "checked": true },
|
|
13
|
+
{ "type": "textarea", "key": "specdesc", "label": "规格描述", "rows": 2 },
|
|
14
|
+
{ "type": "dropdown", "key": "status", "label": "状态", "options": ["在制", "已停产"],
|
|
15
|
+
"hideWhen": "{enabled}==\"false\"" },
|
|
16
|
+
{ "type": "subtable", "key": "specs", "label": "规格参数明细",
|
|
17
|
+
"columns": [
|
|
18
|
+
{ "type": "text", "key": "param", "label": "参数名" },
|
|
19
|
+
{ "type": "dropdown", "key": "unit", "label": "单位", "options": ["mm", "kg", "件"] },
|
|
20
|
+
{ "type": "number", "key": "val", "label": "数值", "decimal": 2 },
|
|
21
|
+
{ "type": "date", "key": "since", "label": "生效日期" }
|
|
22
|
+
] }
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* npm postinstall:把包内 skills/ 安装到所有已知 AI 工具的用户级技能目录。
|
|
6
|
+
* 这样 Claude Code / Codex / Cursor / OpenCode / QwenWork / Qoder 等
|
|
7
|
+
* 安装完 npm 包后都能直接识别 h3factory 技能。
|
|
8
|
+
* 任何失败都不阻断 npm install;可用 H3FACTORY_SKIP_SKILLS=1 跳过。
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const os = require('os');
|
|
14
|
+
|
|
15
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
16
|
+
const SKILLS_SRC = path.join(PACKAGE_ROOT, 'skills');
|
|
17
|
+
const HOME = os.homedir();
|
|
18
|
+
const QUIET = process.argv.includes('--quiet');
|
|
19
|
+
|
|
20
|
+
// 与 cli.py 的 TOOL_SKILL_DIRS 保持一致
|
|
21
|
+
const TOOL_DIRS = [
|
|
22
|
+
'.claude',
|
|
23
|
+
'.codex',
|
|
24
|
+
'.cursor',
|
|
25
|
+
'.opencode',
|
|
26
|
+
'.qwenworkcn',
|
|
27
|
+
'.qoder',
|
|
28
|
+
'.qoderwork',
|
|
29
|
+
'.mulerun',
|
|
30
|
+
'.codebuddy',
|
|
31
|
+
'.workbuddy',
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
function copyDir(src, dest) {
|
|
35
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
36
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
37
|
+
const from = path.join(src, entry.name);
|
|
38
|
+
const to = path.join(dest, entry.name);
|
|
39
|
+
if (entry.isDirectory()) {
|
|
40
|
+
copyDir(from, to);
|
|
41
|
+
} else {
|
|
42
|
+
fs.copyFileSync(from, to);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function skillNames() {
|
|
48
|
+
if (!fs.existsSync(SKILLS_SRC)) {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
return fs
|
|
52
|
+
.readdirSync(SKILLS_SRC, { withFileTypes: true })
|
|
53
|
+
.filter(
|
|
54
|
+
(entry) =>
|
|
55
|
+
entry.isDirectory() &&
|
|
56
|
+
fs.existsSync(path.join(SKILLS_SRC, entry.name, 'SKILL.md'))
|
|
57
|
+
)
|
|
58
|
+
.map((entry) => entry.name);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (process.env.H3FACTORY_SKIP_SKILLS === '1') {
|
|
62
|
+
if (!QUIET) {
|
|
63
|
+
console.log('[h3factory] H3FACTORY_SKIP_SKILLS=1,跳过技能安装');
|
|
64
|
+
}
|
|
65
|
+
process.exit(0);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const skills = skillNames();
|
|
70
|
+
if (skills.length) {
|
|
71
|
+
const installed = [];
|
|
72
|
+
for (const toolDir of TOOL_DIRS) {
|
|
73
|
+
const destination = path.join(HOME, toolDir, 'skills');
|
|
74
|
+
let touched = false;
|
|
75
|
+
for (const skill of skills) {
|
|
76
|
+
try {
|
|
77
|
+
const target = path.join(destination, skill);
|
|
78
|
+
fs.rmSync(target, { recursive: true, force: true });
|
|
79
|
+
copyDir(path.join(SKILLS_SRC, skill), target);
|
|
80
|
+
touched = true;
|
|
81
|
+
} catch (error) {
|
|
82
|
+
if (!QUIET) {
|
|
83
|
+
console.warn(
|
|
84
|
+
'[h3factory] 技能 ' + skill + ' 安装到 ' + destination +
|
|
85
|
+
' 失败(不影响 CLI): ' + error.message
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (touched) {
|
|
91
|
+
installed.push(destination);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (!QUIET && installed.length) {
|
|
95
|
+
console.log(
|
|
96
|
+
'[h3factory] 已安装技能: ' +
|
|
97
|
+
skills.join(', ') +
|
|
98
|
+
' → ' +
|
|
99
|
+
installed.join(', ')
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
} catch (error) {
|
|
104
|
+
if (!QUIET) {
|
|
105
|
+
console.warn('[h3factory] 技能安装跳过(不影响 CLI): ' + error.message);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
process.exit(0);
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: h3factory
|
|
3
|
+
description: 氚云(H3Yun)表单工厂总入口。用户提到氚云自动建表、表单工厂、批量建表/建表单、需求转表单、h3factory、check/build/verify/gen 时使用。负责澄清需求、产出 sheets JSON、调用 h3factory CLI 建表与只读核对;平台二开代码规范路由到 h3yun-dev-assistant。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# h3factory 编排技能
|
|
7
|
+
|
|
8
|
+
把「需求文档 → 表单设计 JSON → 线上建表 → 只读核对 → 代码骨架」串成流水线。
|
|
9
|
+
确定性部分(校验/建表/回读/导数据/生成骨架)全部由 `h3factory` CLI 完成;
|
|
10
|
+
你负责需求澄清、JSON 设计和业务规则代码。
|
|
11
|
+
|
|
12
|
+
## 第 0 步:凭据门禁(每次会话必须先执行)
|
|
13
|
+
|
|
14
|
+
> **硬门禁**:没有通过 `auth status`,不得执行 `build/verify/group/import`。
|
|
15
|
+
|
|
16
|
+
1. 确认命令可用:`h3factory --version`。失败则让客户安装:`npm install -g h3factory`。
|
|
17
|
+
2. 运行 `h3factory auth status --json`,读取 `ok`、`missing`、`jwt_expired`。
|
|
18
|
+
3. 如果 `ok=false`,**优先让客户在自己的终端录入**(token 不会进入对话记录):
|
|
19
|
+
- 只缺 `authorization`:客户运行 `h3factory auth set-token`,输入时回显被隐藏。
|
|
20
|
+
- 还缺 `baseUrl/engineCode/engineSecret/appCode`:客户运行 `h3factory init` 交互式填全部。
|
|
21
|
+
- 客户执行完后,你再运行 `h3factory auth status --json`,直到 `ok=true`。
|
|
22
|
+
4. 只有客户**主动在对话里给出 token** 时,才用 stdin 写入;禁止放进命令行参数,
|
|
23
|
+
禁止在回答里回显。写入后提醒客户:token 已进入对话记录,完成后建议轮换。
|
|
24
|
+
```bash
|
|
25
|
+
h3factory auth set --json <<'JSON'
|
|
26
|
+
{"authorization": "<客户提供的 token>"}
|
|
27
|
+
JSON
|
|
28
|
+
```
|
|
29
|
+
多个字段放在同一个 JSON 对象里即可。
|
|
30
|
+
5. token 会过期:**每次新会话都重新检查**,过期就重复第 3/4 步换新 token。
|
|
31
|
+
|
|
32
|
+
## 不可违反的规则
|
|
33
|
+
|
|
34
|
+
1. **绝不打印或回显** `config.json`、`engineSecret`、`authorization` 的内容;不要把配置文件贴进回答。
|
|
35
|
+
2. **建表前必须让用户过目设计**(表数量、字段、下拉取值、编号规则、存储口径)。这是唯一不能省的环节。
|
|
36
|
+
3. `verify` 只读、可重复跑;`build` 默认跳过线上已存在的表。**只有用户明确要求**才加 `--force`——它会整表覆盖,抹掉界面上手工配置的必填/联动/审批。
|
|
37
|
+
4. 设计文件和 registry 一律写在工作区的 `projects/<项目名>/`,不要写进 npm 安装目录。
|
|
38
|
+
5. 不确定的表单编码、控件编码、接口签名,先查 `h3factory docs` 里的文档或问用户,不得臆造。
|
|
39
|
+
6. 二开 C#/ES5/SQL 代码规范、平台控件与事件细节,读取并遵循 `h3yun-dev-assistant` 技能,不要凭记忆猜。
|
|
40
|
+
|
|
41
|
+
## 工作区与安装
|
|
42
|
+
|
|
43
|
+
- 命令:`h3factory`(npm 包名 `h3factory`;源码目录也可 `python -X utf8 cli.py`)。
|
|
44
|
+
- **当前目录 = 工作区**;项目在 `./projects/<项目名>/`。
|
|
45
|
+
- 配置查找顺序:`H3FACTORY_CONFIG` → `./config.json` → `~/.h3factory/config.json`;项目级 `projects/<项目名>/config.json` 优先于以上。
|
|
46
|
+
- 首次使用:`h3factory auth status --json` → 需要时 `auth set` 录入 token → `h3factory doctor`。
|
|
47
|
+
- 安装包会把技能自动装到 Claude Code / Codex / Cursor / OpenCode / QwenWork / Qoder / QoderWork / Mulerun / CodeBuddy / WorkBuddy 的用户技能目录;手动重装用 `h3factory skill install --tool all`。
|
|
48
|
+
- 不自动加载技能的 Agent,用 `h3factory prompt` 打印通用提示语,或在项目里运行 `h3factory init` 写入 `AGENTS.md`。
|
|
49
|
+
- 查看内置文档:`h3factory docs` 列出,`h3factory docs schema_doc` 打印内容。
|
|
50
|
+
|
|
51
|
+
## 标准流程
|
|
52
|
+
|
|
53
|
+
### 1. 澄清需求
|
|
54
|
+
先读 `docs/requirement_checklist.md`(`h3factory docs requirement_checklist`),逐条确认:
|
|
55
|
+
表单盘点、字段类型/必填/唯一、枚举与「存名称还是代码」、编号规则、
|
|
56
|
+
审批流、权限、数据来源、界面手工配置项。歧义不澄清就建表 = 返工。
|
|
57
|
+
|
|
58
|
+
### 2. 产出设计 JSON
|
|
59
|
+
在工作区创建 `projects/<项目名>/`:
|
|
60
|
+
- `sheets/<表单key>.json` —— 每张表单一个文件,格式见 `docs/schema_doc.md`
|
|
61
|
+
- `dicts.json` —— 枚举字典
|
|
62
|
+
- `groups.json` —— 可选,表单分组与菜单顺序
|
|
63
|
+
- 一页设计说明:表关系、编号规则、口径决定
|
|
64
|
+
|
|
65
|
+
同时把平台坑位过一遍:`h3factory docs platform_gotchas`。
|
|
66
|
+
|
|
67
|
+
### 3. 本地校验(不联网)
|
|
68
|
+
```bash
|
|
69
|
+
h3factory check <项目名> [表..]
|
|
70
|
+
```
|
|
71
|
+
检查字段数、隐藏规则、行组、关联、子表、布局项、分组声明。语法/引用错误会直接报。
|
|
72
|
+
|
|
73
|
+
### 4. 用户过目
|
|
74
|
+
把设计说明和 `check` 输出交给用户确认。**未确认不得 build。**
|
|
75
|
+
|
|
76
|
+
### 5. 建表
|
|
77
|
+
```bash
|
|
78
|
+
h3factory build <项目名> [表..] # 已存在且结构一致会跳过
|
|
79
|
+
h3factory build <项目名> <表> --force # 仅用户明确要求时;整表覆盖
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 6. 只读核对
|
|
83
|
+
```bash
|
|
84
|
+
h3factory verify <项目名> [表..]
|
|
85
|
+
h3factory list <项目名>
|
|
86
|
+
```
|
|
87
|
+
报告写到 `projects/<项目名>/logs/verify_report.txt`。有 FAIL 先修定义/重跑,不要掩盖。
|
|
88
|
+
|
|
89
|
+
### 7. 生成代码骨架
|
|
90
|
+
```bash
|
|
91
|
+
h3factory gen <项目名> <表>
|
|
92
|
+
```
|
|
93
|
+
输出 `projects/<项目名>/generated/<表>.cs`。按 TODO 补业务规则;
|
|
94
|
+
代码规范、控件编码、事件签名调用 `h3yun-dev-assistant` 技能确认。
|
|
95
|
+
|
|
96
|
+
### 8. 交付
|
|
97
|
+
除代码外,必须给用户「界面手工配置清单」:必填、审批流、字段联动、只读、
|
|
98
|
+
按钮、权限等 API 配不了的项(参考 `docs/platform_gotchas.md`)。
|
|
99
|
+
|
|
100
|
+
## 命令速查
|
|
101
|
+
|
|
102
|
+
| 命令 | 作用 | 是否联网 |
|
|
103
|
+
|---|---|---|
|
|
104
|
+
| `h3factory auth status [--json]` | 凭据门禁:检查配置与 JWT | 否 |
|
|
105
|
+
| `h3factory auth set --json` | 从 stdin 读 JSON 写入凭据(不回显) | 否 |
|
|
106
|
+
| `h3factory auth set-token` | 从 stdin 读 token 写入 | 否 |
|
|
107
|
+
| `h3factory init [--no-input]` | 交互生成工作区 config.json | 否 |
|
|
108
|
+
| `h3factory doctor` | 检查 Python/配置/JWT/技能 | 否 |
|
|
109
|
+
| `h3factory check <p> [表..]` | 本地校验表单定义 | 否 |
|
|
110
|
+
| `h3factory list <p>` | 查看 registry 表编码与状态 | 否 |
|
|
111
|
+
| `h3factory build <p> [表..] [--force]` | 建表 | 是 |
|
|
112
|
+
| `h3factory verify <p> [表..]` | LoadForm 只读回读比对 | 是 |
|
|
113
|
+
| `h3factory group <p> [表..]` | 建分组并归位(幂等) | 是 |
|
|
114
|
+
| `h3factory import <p> <表> <data.json> [--submit]` | 批量导数据 | 是 |
|
|
115
|
+
| `h3factory gen <p> <表>` | 生成 .cs 骨架 | 否 |
|
|
116
|
+
| `h3factory docs [名称]` | 列出/打印内置文档 | 否 |
|
|
117
|
+
| `h3factory skill install` | 安装本技能与二开技能 | 否 |
|
|
118
|
+
|
|
119
|
+
## 目录约定(工作区)
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
projects/<项目名>/
|
|
123
|
+
├─ config.json # 可选,覆盖工作区凭据(勿提交)
|
|
124
|
+
├─ dicts.json # 枚举字典
|
|
125
|
+
├─ groups.json # 可选,分组顺序
|
|
126
|
+
├─ sheets/*.json # 表单定义(每表一个)
|
|
127
|
+
├─ registry.json # 平台编码注册表,勿手改
|
|
128
|
+
├─ logs/ # verify/group 报告
|
|
129
|
+
└─ generated/ # .cs 骨架
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## 与 h3yun-dev-assistant 的分工
|
|
133
|
+
|
|
134
|
+
- 本技能:需求 → 表单 JSON → CLI 建表/核对 → 生成骨架 → 交付清单。
|
|
135
|
+
- `h3yun-dev-assistant`:C# 后端、ES5 前端、SQL、控件编码、事件、平台限制。
|
|
136
|
+
写或改业务代码前先加载它,尤其是 OnLoad/OnSubmit、PostForm、ListView.Post、
|
|
137
|
+
高级数据源和权限相关实现。
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# 后端 C# 规则
|
|
2
|
+
|
|
3
|
+
## 硬性限制
|
|
4
|
+
|
|
5
|
+
- 使用 .NET Framework 4.6.2 兼容语法;不使用 C# 8+ 特性、反射、`HashSet<T>` 或三目运算符。
|
|
6
|
+
- `if`、`else if`、`else`、`for`、`foreach`、`while`、`do while` 等控制流语句必须使用大括号,即使代码块中只有一条语句也不得省略;大括号采用 Allman 风格,单独占一行。只调整既有代码格式时不得改变业务逻辑。
|
|
7
|
+
- 不使用 `Task`、线程、线程池、普通 .NET 定时器或本地文件 I/O。涉及这些场景时先读取 `PLATFORM_CONSTRAINTS.md`。
|
|
8
|
+
- 不自行建立数据库连接,不使用 DDL,不直接用 SQL 增删改业务数据;业务数据操作使用 `BizObject`,批量操作使用平台批量接口。
|
|
9
|
+
- 在表单 `Post` 请求中,如果需要更新或删除当前业务对象,不能直接操作 `this.Request.BizObject`;必须先使用当前业务对象的 `ObjectId` 调用 `H3.DataModel.BizObject.Load` 加载完整业务对象,再执行赋值、`Update()` 或 `Remove()`。
|
|
10
|
+
- 避免循环调用 Engine、数据库或服务端接口;大数据量须分页或增加过滤条件。
|
|
11
|
+
|
|
12
|
+
## 事件与异常
|
|
13
|
+
|
|
14
|
+
- `OnLoad` 通常调用 `base.OnLoad(response)`。
|
|
15
|
+
- `OnSubmit` 无论成功、校验失败或提前返回,都必须调用 `base.OnSubmit(actionName, postValue, response)`。
|
|
16
|
+
- 校验方法优先命名为 `CheckData(H3.IEngine engine, H3.DataModel.BizObject thisBo)`,失败时 `throw new Exception("明确的错误说明")`。
|
|
17
|
+
- `OnSubmit` 统一捕获业务异常,以 `response.Errors.Add(ex.Message)` 返回。
|
|
18
|
+
- 比较时优先常量在前,例如 `"Submit".Equals(actionName)`。
|
|
19
|
+
- 流程表单的 `Submit` 同时可能表示发起提交和审批同意。仅在发起时执行的后端逻辑,必须结合数据状态和当前流程节点判断,不能只判断 `"Submit".Equals(actionName)`。
|
|
20
|
+
|
|
21
|
+
## 列表删除获取选中数据
|
|
22
|
+
|
|
23
|
+
- 列表后端处理系统删除按钮时,使用 `postValue.Data["ObjectIds"]` 获取用户选中的业务对象 Id 数组,标准写法为:`string[] selectedIds = (string[]) postValue.Data["ObjectIds"];`。
|
|
24
|
+
- 该写法来自官方后端代码示例“列表-删除时获得用户选择的数据”:https://h3yunpro.github.io/docs/cs-example/#%E5%88%97%E8%A1%A8-%E5%88%A0%E9%99%A4%E6%97%B6%E8%8E%B7%E5%BE%97%E7%94%A8%E6%88%B7%E9%80%89%E6%8B%A9%E7%9A%84%E6%95%B0%E6%8D%AE 。不得猜测为 `postValue["seIds"]`、`postValue["ObjectIds"]` 或其他参数名。
|
|
25
|
+
- 使用前必须判断数组不为 `null` 且长度大于 0;需要批量查询时,用 `ObjectId` 配合 `ComparisonOperatorType.In` 构造筛选器,并校验查询结果数量与选中 Id 数量一致。
|
|
26
|
+
- 列表 `OnSubmit` 处理删除时,无论自定义逻辑成功或异常,都必须保证 `base.OnSubmit(actionName, postValue, response)` 执行;异常应写入 `response.Errors` 后返回。
|
|
27
|
+
|
|
28
|
+
```csharp
|
|
29
|
+
protected override void OnSubmit(string actionName, H3.SmartForm.ListViewPostValue postValue, H3.SmartForm.SubmitListViewResponse response)
|
|
30
|
+
{
|
|
31
|
+
try
|
|
32
|
+
{
|
|
33
|
+
if("Remove".Equals(actionName))
|
|
34
|
+
{
|
|
35
|
+
// 列表删除-用户选中的业务对象 Id
|
|
36
|
+
string[] selectedIds = (string[]) postValue.Data["ObjectIds"];
|
|
37
|
+
if(selectedIds != null && selectedIds.Length > 0)
|
|
38
|
+
{
|
|
39
|
+
// 列表表单-按选中业务对象 Id 批量查询
|
|
40
|
+
H3.Data.Filter.Filter filter = new H3.Data.Filter.Filter();
|
|
41
|
+
H3.Data.Filter.And andMatcher = new H3.Data.Filter.And();
|
|
42
|
+
andMatcher.Add(new H3.Data.Filter.ItemMatcher("ObjectId", H3.Data.ComparisonOperatorType.In, selectedIds));
|
|
43
|
+
filter.Matcher = andMatcher;
|
|
44
|
+
filter.FromRowNum = 0;
|
|
45
|
+
filter.ToRowNum = 1000;
|
|
46
|
+
H3.DataModel.BizObject[] boArray = H3.DataModel.BizObject.GetList(
|
|
47
|
+
this.Engine,
|
|
48
|
+
H3.Organization.User.SystemUserId,
|
|
49
|
+
this.Request.Schema,
|
|
50
|
+
H3.DataModel.GetListScopeType.GlobalAll,
|
|
51
|
+
filter
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch(Exception ex)
|
|
57
|
+
{
|
|
58
|
+
response.Errors.Add(ex.Message);
|
|
59
|
+
base.OnSubmit(actionName, postValue, response);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
base.OnSubmit(actionName, postValue, response);
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## 表单按钮增删
|
|
68
|
+
|
|
69
|
+
- 在表单后端 `OnLoad` 中,先调用 `base.OnLoad(response)`,再通过 `response.Actions` 调整按钮。
|
|
70
|
+
- 删除已有按钮前先用 `response.Actions.ContainsKey("按钮编码")` 判断,避免不存在时异常。
|
|
71
|
+
- 动态新增按钮使用 `response.Actions.Add("按钮编码", new H3.SmartForm.ViewAction("按钮编码", "按钮名称", ""))`。
|
|
72
|
+
- 需要按业务状态显示按钮时,读取 `this.Request.BizObject["字段编码"]` 后再决定是否添加;不要依赖前端直接操作 DOM。
|
|
73
|
+
- 动态按钮的编码必须与 `OnSubmit` 的 `actionName` 判断完全一致。
|
|
74
|
+
|
|
75
|
+
```csharp
|
|
76
|
+
protected override void OnLoad(H3.SmartForm.LoadSmartFormResponse response)
|
|
77
|
+
{
|
|
78
|
+
base.OnLoad(response);
|
|
79
|
+
|
|
80
|
+
if(response.Actions != null && response.Actions.ContainsKey("Save"))
|
|
81
|
+
{
|
|
82
|
+
response.Actions.Remove("Save");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 示例表单-执行状态:F0000004
|
|
86
|
+
string executeStatus = this.Request.BizObject["F0000004"] + string.Empty;
|
|
87
|
+
if("暂停".Equals(executeStatus) && !response.Actions.ContainsKey("ContinueRecognition"))
|
|
88
|
+
{
|
|
89
|
+
response.Actions.Add(
|
|
90
|
+
"ContinueRecognition",
|
|
91
|
+
new H3.SmartForm.ViewAction("ContinueRecognition", "继续执行", "")
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## 主表与子表控件取值
|
|
98
|
+
|
|
99
|
+
- 生成后端主表或子表控件的取值、赋值或类型转换代码前,必须先查阅氚专开发文档:https://h3yunpro.github.io/docs/bo-set-get/ 。
|
|
100
|
+
- 字符串、人员/部门单选、关联表单、人员/部门多选、关联表单多选、日期、数字、复选框、是/否、地址、位置、附件和图片的值类型及读写方式,必须以该文档为准,不得猜测类型。
|
|
101
|
+
- 从业务对象读取子表控件值时,必须转换为 `H3.DataModel.BizObject[]`,例如:`H3.DataModel.BizObject[] detailRows = thisBo["子表控件编码"] as H3.DataModel.BizObject[];`。
|
|
102
|
+
- 禁止将子表控件值转换为 `List<H3.DataModel.BizObject>`;平台返回的子表值类型为数组,错误转换会导致读取结果为空或运行异常。
|
|
103
|
+
- 使用子表数据前先判断数组是否为 `null` 或长度为 `0`,再用 `foreach` 遍历每一行。
|
|
104
|
+
|
|
105
|
+
## 自定义类
|
|
106
|
+
|
|
107
|
+
- 同一应用的后端会共同编译。新增辅助类必须以当前完整表单编码为前缀:`表单编码_类名`。
|
|
108
|
+
- 第一次引用辅助类时,在上一行标注:`// 类名类及方法名方法定义于【表单名称】表单设计页后端代码中`。
|
|
109
|
+
- 类内只定义字段,禁止使用 `{ get; set; }` 自动属性;氚云后端编译器不支持自动属性。
|
|
110
|
+
- 每个字段定义时必须同时给出默认值;对象类型字段可默认为 null。例如:`public string name = "";`、`public decimal qty = 0;`、`public List<string> ids = null;`。
|
|
111
|
+
|
|
112
|
+
## 方法复用
|
|
113
|
+
|
|
114
|
+
- 封装方法内部需要使用 `H3.IEngine` 时,必须将 `H3.IEngine engine` 作为方法入参传入,不直接使用 `this.Engine`,以提高方法复用性。
|
|
115
|
+
- 封装方法内部需要使用 `H3.DataModel.BizObject` 时,必须将业务对象作为方法入参传入,不直接使用 `this.Request.BizObject`,以提高方法复用性。
|
|
116
|
+
|
|
117
|
+
## 编码注释
|
|
118
|
+
|
|
119
|
+
- 表单编码旁标注表单名称;字段编码旁标注“表单名称-字段名称”。
|
|
120
|
+
- 新增封装方法上方添加一句中文注释说明处理目标。
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# 氚专开发文档按需读取路由
|
|
2
|
+
|
|
3
|
+
处理氚云需求时,先读取本表;仅在需求命中对应主题、用户要求按氚专开发文档实现,或现有规则无法确认 API 签名、参数、返回值、事件时机或控件类型时,再读取对应开发文档。不要为简单需求加载全部文档。
|
|
4
|
+
|
|
5
|
+
| 需求意图或关键词 | 必读开发文档 |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| 按钮编码、自定义按钮、`actionName`、动态增删表单按钮、`response.Actions` | 读取技能内置 `FORM_ACTIONS_GUIDELINES.md`;动态按钮不再依赖旧版官方网页 |
|
|
8
|
+
| 自动化、业务规则与代码的执行顺序或副作用 | [代码跟自动化混用](https://h3yunpro.github.io/docs/automation/) |
|
|
9
|
+
| 创建、加载、查询、提交、删除 `BizObject` | [业务对象](https://h3yunpro.github.io/docs/biz-object/) |
|
|
10
|
+
| 后端读取或设置主表、子表控件值 | [业务对象之控件的取值与赋值](https://h3yunpro.github.io/docs/bo-set-get/) |
|
|
11
|
+
| 获取应用、表单、子表、控件编码 | [应用、表单、控件编码查看](https://h3yunpro.github.io/docs/check-code/) |
|
|
12
|
+
| 后端 API 用法、`Engine`、常用 C# 示例 | [后端常用实例](https://h3yunpro.github.io/docs/cs-instance/);必要时参考 [后端代码示例](https://h3yunpro.github.io/docs/cs-example/) |
|
|
13
|
+
| JSON 序列化或反序列化 | [后端 JSON 序列化与反序列化](https://h3yunpro.github.io/docs/cs-json/) |
|
|
14
|
+
| 数据库系统表、字段、附件表、流程表 | [数据库表结构详解](https://h3yunpro.github.io/docs/database/) |
|
|
15
|
+
| 开发环境、编辑器、调试工具 | [开发工具](https://h3yunpro.github.io/docs/dev-tools/) |
|
|
16
|
+
| 后端 SQL 查询、参数化 SQL | [后端执行 SQL](https://h3yunpro.github.io/docs/exec-sql/) |
|
|
17
|
+
| 平台报错、现象或 FAQ 类问题 | [常见问题解答](https://h3yunpro.github.io/docs/faq/) |
|
|
18
|
+
| 表单后端或前端事件、提交时机 | [表单事件](https://h3yunpro.github.io/docs/form-events/) |
|
|
19
|
+
| 表单前端 API、控件操作 | [表单前端 API](https://h3yunpro.github.io/docs/form-js-api/);控件取值和赋值另读 [表单前端 GetValue 与 SetValue](https://h3yunpro.github.io/docs/form-js-set-get/) |
|
|
20
|
+
| 表单或列表前后端请求、`PostForm`、`ListView.Post` | [前后端交互示例](https://h3yunpro.github.io/docs/interactive-example/);必要时参考 [纯净代码示例](https://h3yunpro.github.io/docs/pure-example/) |
|
|
21
|
+
| 表单前端完整示例、事件代码示例 | [前端代码示例](https://h3yunpro.github.io/docs/js-example/);常见组合场景参考 [前端常用实例](https://h3yunpro.github.io/docs/js-instance/) |
|
|
22
|
+
| .NET/C#、JavaScript、浏览器兼容性 | [技术栈](https://h3yunpro.github.io/docs/language/) |
|
|
23
|
+
| 列表前端或后端事件 | [列表事件](https://h3yunpro.github.io/docs/list-events/);列表删除获取选中数据时,另读 [后端代码示例-列表删除时获得用户选择的数据](https://h3yunpro.github.io/docs/cs-example/#%E5%88%97%E8%A1%A8-%E5%88%A0%E9%99%A4%E6%97%B6%E8%8E%B7%E5%BE%97%E7%94%A8%E6%88%B7%E9%80%89%E6%8B%A9%E7%9A%84%E6%95%B0%E6%8D%AE) |
|
|
24
|
+
| 列表前端 API、选中行、刷新、列表按钮 | [列表前端 API](https://h3yunpro.github.io/docs/list-js-api/) |
|
|
25
|
+
| 短信、通知、提醒 | [消息通知与提醒](https://h3yunpro.github.io/docs/notification/) |
|
|
26
|
+
| 平台术语、状态或对象含义 | [常见名词](https://h3yunpro.github.io/docs/noun/) |
|
|
27
|
+
| 外部系统调用氚云 | [第三方调用氚云 OpenAPI](https://h3yunpro.github.io/docs/open-api/) |
|
|
28
|
+
| 人员、部门、角色、组织关系 | [部门、人员与角色管理](https://h3yunpro.github.io/docs/organization/) |
|
|
29
|
+
| 氚云后端调用第三方 HTTP 接口 | [氚云调用 WebAPI](https://h3yunpro.github.io/docs/req-api/) |
|
|
30
|
+
| 氚云后端调用第三方 WebService | [氚云调用 WebService](https://h3yunpro.github.io/docs/req-ws/) |
|
|
31
|
+
| SQL 高级数据源 | [SQL 高级数据源](https://h3yunpro.github.io/docs/sql-dashboard/) |
|
|
32
|
+
| SQL 报表 | [SQL 报表](https://h3yunpro.github.io/docs/sql-report/) |
|
|
33
|
+
| SQL 查询范例 | [数据库操作示例](https://h3yunpro.github.io/docs/sql-example/) |
|
|
34
|
+
| 平台编程规范或代码风格 | [编程规范](https://h3yunpro.github.io/docs/standard/) |
|
|
35
|
+
| 计划、周期、延时执行 | [定时器](https://h3yunpro.github.io/docs/timer/) |
|
|
36
|
+
| 流程实例、节点、发起、审批、撤回、作废 | [流程操作](https://h3yunpro.github.io/docs/workflow/) |
|
|
37
|
+
| 常用工具函数 | [工具方法合集](https://h3yunpro.github.io/docs/tool-function/) |
|
|
38
|
+
|
|
39
|
+
无法归类或开发文档目录新增主题时,读取总索引:https://h3yunpro.github.io/llms.txt 。
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# 氚云表单按钮规则
|
|
2
|
+
|
|
3
|
+
本文件整理自氚云官方“表单按钮”开发说明,用于在官方旧文档下架后继续指导表单按钮开发。
|
|
4
|
+
|
|
5
|
+
## 适用范围
|
|
6
|
+
|
|
7
|
+
- 表单后端 `OnLoad` 中删除、替换、动态增加按钮。
|
|
8
|
+
- 表单后端 `OnSubmit` 中按按钮编码处理业务。
|
|
9
|
+
- 根据当前业务对象字段值决定按钮是否显示。
|
|
10
|
+
|
|
11
|
+
## 关键对象
|
|
12
|
+
|
|
13
|
+
- `response.Actions`:表单加载响应中的按钮字典,键为按钮编码,值为 `H3.SmartForm.ViewAction`。
|
|
14
|
+
- `H3.SmartForm.ViewAction`:按钮定义,构造参数为按钮编码、显示名称和说明文本。
|
|
15
|
+
- `actionName`:表单后端 `OnSubmit` 的按钮编码。
|
|
16
|
+
- `this.Request.BizObject`:当前表单业务对象,可读取字段值决定是否动态添加按钮。
|
|
17
|
+
|
|
18
|
+
## 基本规则
|
|
19
|
+
|
|
20
|
+
- `OnLoad` 必须先调用 `base.OnLoad(response)`。
|
|
21
|
+
- 操作 `response.Actions` 前先判断其不为 `null`。
|
|
22
|
+
- 删除按钮前先使用 `response.Actions.ContainsKey("按钮编码")` 判断。
|
|
23
|
+
- 新增按钮前同样判断编码不存在,避免重复添加异常。
|
|
24
|
+
- 动态添加的按钮编码必须与 `OnSubmit` 中判断的 `actionName` 完全一致。
|
|
25
|
+
- 自定义按钮编码只使用英文字母和数字,避免中文、空格及特殊字符。
|
|
26
|
+
- 不通过前端 DOM 操作新增或隐藏表单头部按钮,应由后端 `response.Actions` 决定。
|
|
27
|
+
|
|
28
|
+
## 删除与新增按钮
|
|
29
|
+
|
|
30
|
+
```csharp
|
|
31
|
+
protected override void OnLoad(H3.SmartForm.LoadSmartFormResponse response)
|
|
32
|
+
{
|
|
33
|
+
base.OnLoad(response);
|
|
34
|
+
|
|
35
|
+
if(response.Actions == null)
|
|
36
|
+
{
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if(response.Actions.ContainsKey("Save"))
|
|
41
|
+
{
|
|
42
|
+
response.Actions.Remove("Save");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if(!response.Actions.ContainsKey("TestAction"))
|
|
46
|
+
{
|
|
47
|
+
response.Actions.Add(
|
|
48
|
+
"TestAction",
|
|
49
|
+
new H3.SmartForm.ViewAction("TestAction", "测试按钮", "")
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## 按字段状态动态添加按钮
|
|
56
|
+
|
|
57
|
+
```csharp
|
|
58
|
+
protected override void OnLoad(H3.SmartForm.LoadSmartFormResponse response)
|
|
59
|
+
{
|
|
60
|
+
base.OnLoad(response);
|
|
61
|
+
|
|
62
|
+
if(response.Actions == null)
|
|
63
|
+
{
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 示例表单-处理状态:F0000001
|
|
68
|
+
string processStatus = this.Request.BizObject["F0000001"] + string.Empty;
|
|
69
|
+
if("待处理".Equals(processStatus) && !response.Actions.ContainsKey("TestAction"))
|
|
70
|
+
{
|
|
71
|
+
response.Actions.Add(
|
|
72
|
+
"TestAction",
|
|
73
|
+
new H3.SmartForm.ViewAction("TestAction", "测试按钮", "")
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## OnSubmit 处理动态按钮
|
|
80
|
+
|
|
81
|
+
```csharp
|
|
82
|
+
protected override void OnSubmit(
|
|
83
|
+
string actionName,
|
|
84
|
+
H3.SmartForm.SmartFormPostValue postValue,
|
|
85
|
+
H3.SmartForm.SubmitSmartFormResponse response)
|
|
86
|
+
{
|
|
87
|
+
try
|
|
88
|
+
{
|
|
89
|
+
if("TestAction".Equals(actionName))
|
|
90
|
+
{
|
|
91
|
+
// 处理测试按钮业务。
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch(Exception ex)
|
|
95
|
+
{
|
|
96
|
+
response.Errors.Add(ex.Message);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
base.OnSubmit(actionName, postValue, response);
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## 系统按钮编码
|
|
104
|
+
|
|
105
|
+
| 名称 | 编码 |
|
|
106
|
+
| --- | --- |
|
|
107
|
+
| 暂存 | `Save` |
|
|
108
|
+
| 提交/同意 | `Submit` |
|
|
109
|
+
| 删除 | `Remove` |
|
|
110
|
+
| 撤回 | `RetrieveInstance` |
|
|
111
|
+
| 不同意 | `Reject` |
|
|
112
|
+
| 作废 | `CancelInstance` |
|
|
113
|
+
| 打印 | `Print` |
|
|
114
|
+
| 关闭 | `Close` |
|
|
115
|
+
|
|
116
|
+
`Submit` 同时可能表示发起提交和流程审批同意。需要区分时,必须结合表单数据状态或流程节点编码判断。
|