@microi.net/cli 5.6.2 → 5.6.4

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.
Files changed (45) hide show
  1. package/.codebuddy-plugin/marketplace.json +2 -2
  2. package/.codebuddy-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +2 -2
  4. package/.workbuddy-plugin/marketplace.json +2 -2
  5. package/.workbuddy-plugin/plugin.json +1 -1
  6. package/README.md +2 -2
  7. package/assets/build-meta.json +7 -6
  8. package/assets/feature-matrix.json +138 -129
  9. package/cordis.patch.yml +2 -2
  10. package/package.json +1 -1
  11. package/scripts/mcp-server.js +82 -80
  12. package/scripts/microi-cli.js +59 -58
  13. package/scripts/microi-skills.meta.json +225 -222
  14. package/skills/.microi-skills-version.json +3 -3
  15. package/skills/.progressive-disclosure-manifest.json +58 -58
  16. package/skills/app-store/SKILL.md +6 -0
  17. package/skills/email-engine/SKILL.md +3 -2
  18. package/skills/job-engine/SKILL.md +10 -3
  19. package/skills/microi-client-frontend/SKILL.md +5 -2
  20. package/skills/microi-codex/SKILL.md +59 -48
  21. package/skills/microi-codex-installer/SKILL.md +12 -3
  22. package/skills/microi-deployment/SKILL.md +2 -0
  23. package/skills/microi-form-engine/SKILL.md +18 -0
  24. package/skills/microi-form-engine/references/component-catalog.md +1 -1
  25. package/skills/page-engine/SKILL.md +11 -1
  26. package/skills/page-engine/examples/compact-dashboard.json +1444 -0
  27. package/skills/playwright-e2e/SKILL.md +8 -8
  28. package/skills/playwright-e2e/references/progressive-01-/345/205/250/350/207/252/345/212/250/347/231/273/345/275/225-/345/205/215/351/252/214/350/257/201/347/240/201-/344/275/206/344/270/215/345/205/215/345/257/206/347/240/201-/345/277/205/350/257/273.md +3 -3
  29. package/skills/playwright-e2e/references/progressive-02-/346/226/207/345/255/227/345/257/271/346/257/224/345/272/246/344/270/216/345/217/257/350/257/273/346/200/247/350/207/252/345/212/250/345/214/226/346/243/200/346/237/245-/345/277/205/345/201/232.md +2 -2
  30. package/skills/playwright-e2e/references/progressive-03-microi-helper-/346/250/241/346/235/277.md +3 -3
  31. package/skills/playwright-e2e/references/progressive-04-ci-/345/273/272/350/256/256.md +3 -3
  32. package/skills/system-observability/SKILL.md +62 -8
  33. package/skills/v8-explorer-tree/SKILL.md +228 -226
  34. package/skills/v8-file-upload/SKILL.md +26 -1
  35. package/skills/v8-file-upload/references/progressive-01-/345/205/254/346/234/211/346/241/266-vs-/347/247/201/346/234/211/346/241/266.md +1 -1
  36. package/skills/v8-file-upload/references/progressive-02-office-/346/226/207/344/273/266/345/234/250/347/272/277/347/274/226/350/276/221/347/211/210/346/234/254/345/217/267/350/247/204/345/210/231.md +3 -3
  37. package/skills/v8-http-integration/SKILL.md +3 -1
  38. package/skills/v8-security/SKILL.md +3 -1
  39. package/skills/v8-sql-query/SKILL.md +141 -133
  40. package/skills/v8-tcp-integration/SKILL.md +2 -2
  41. package/skills/v8-workflow/SKILL.md +12 -4
  42. package/skills/workspace-conventions/SKILL.md +8 -6
  43. package/skills/workspace-conventions/references/progressive-01-/347/211/210/346/234/254/346/233/264/346/226/260/346/227/245/345/277/227/344/277/235/346/212/244/350/247/204/345/210/231-/345/274/272/345/210/266.md +6 -6
  44. package/skills/workspace-conventions/references/progressive-02-microi-net-api-/346/234/254/345/234/260/345/220/257/345/212/250/347/272/246/345/256/232.md +21 -21
  45. package/skills/workspace-conventions/references/progressive-03-cli-/344/270/216-ide-/346/217/222/344/273/266/351/224/231/347/211/210/345/205/261/345/255/230/347/272/246/345/256/232.md +1 -1
@@ -1,28 +1,28 @@
1
1
  ---
2
2
  name: v8-sql-query
3
- description: Microi V8 安全 SQL 查询指南。用于选择 V8.FormEngine _Where 或 V8.Db.FromSql,处理参数化查询、联表、聚合并避免 SQL 注入。
3
+ description: Microi V8 安全 SQL 查询指南。用于选择 V8.FormEngine _Where 或 V8.Db.FromSql,处理参数化查询、联表、聚合并避免 SQL 注入。
4
4
  ---
5
5
 
6
6
  > **Codex 非阻塞自动更新:** 当前宿主为 Codex 时,吾码 CLI、Codex 插件与工作区 AI/MCP 由后台自动更新;需要诊断时读取 `../microi-codex-installer/SKILL.md`。更新失败、等待空闲或尚未重载均不得阻断当前、正在进行或新建任务。非 Codex 宿主跳过此项。
7
7
 
8
8
  # Microi V8 安全 SQL 查询
9
9
 
10
- 你正在开发 Microi 吾码平台的 V8 引擎代码。数据库查询有两种方式:`V8.FormEngine`(推荐)和 `V8.Db`(原始 SQL)。必须遵守安全规范。
11
-
12
- ## 性能门禁(必须执行)
13
-
14
- 写接口引擎前必须先做数据访问计划,避免“循环套循环查数据库”:
15
-
16
- - 禁止在 `for` / `while` / `forEach` / `map` 循环内调用 `V8.FormEngine.GetFormData`、`GetTableData`、`V8.Db.FromSql`、`V8.ApiEngine.Run` 或 `V8.Http.*`。确实无法避免时,必须先说明原因,并加分页、缓存或限流。
17
- - 多条 Id、编码、外键查询必须一次性用 `_Where: [['Id','In', ids]]`、SQL `IN`、JOIN 或聚合查询取回,再用内存字典映射。
18
- - 需要父子、主从、用户/部门/角色名称映射时,先批量取关联表,只保留 `_SelectFields` 必要列,不要逐行查名称。
19
- - 统计、计数、汇总优先让数据库一次 `GROUP BY` / `COUNT` / `SUM` 完成,不要把大表全部拉到 V8 里循环统计。
20
- - 列表接口必须限制 `_PageSize`,管理端默认不要超过 100,导出或批处理必须显式分批。
21
- - 每次查询都要写 `_SelectFields` 或明确 SQL 字段列表,禁止 `SELECT *` 用在大表、接口列表、循环前置查询中。
22
- - 外部 HTTP、翻译、短信、AI 等慢调用不能放在数据库事务和大循环中;要么异步队列,要么批量预处理并设置超时。
23
- - 返回前自检一次:数据库访问次数应与数据量无关或近似常数级,不能随着行数线性增长为 N 次查询。
24
-
25
- ## 首选:V8.FormEngine + _Where(自动防注入)
10
+ 你正在开发 Microi 吾码平台的 V8 引擎代码。数据库查询有两种方式:`V8.FormEngine`(推荐)和 `V8.Db`(原始 SQL)。必须遵守安全规范。
11
+
12
+ ## 性能门禁(必须执行)
13
+
14
+ 写接口引擎前必须先做数据访问计划,避免“循环套循环查数据库”:
15
+
16
+ - 禁止在 `for` / `while` / `forEach` / `map` 循环内调用 `V8.FormEngine.GetFormData`、`GetTableData`、`V8.Db.FromSql`、`V8.ApiEngine.Run` 或 `V8.Http.*`。确实无法避免时,必须先说明原因,并加分页、缓存或限流。
17
+ - 多条 Id、编码、外键查询必须一次性用 `_Where: [['Id','In', ids]]`、SQL `IN`、JOIN 或聚合查询取回,再用内存字典映射。
18
+ - 需要父子、主从、用户/部门/角色名称映射时,先批量取关联表,只保留 `_SelectFields` 必要列,不要逐行查名称。
19
+ - 统计、计数、汇总优先让数据库一次 `GROUP BY` / `COUNT` / `SUM` 完成,不要把大表全部拉到 V8 里循环统计。
20
+ - 列表接口必须限制 `_PageSize`,管理端默认不要超过 100,导出或批处理必须显式分批。
21
+ - 每次查询都要写 `_SelectFields` 或明确 SQL 字段列表,禁止 `SELECT *` 用在大表、接口列表、循环前置查询中。
22
+ - 外部 HTTP、翻译、短信、AI 等慢调用不能放在数据库事务和大循环中;要么异步队列,要么批量预处理并设置超时。
23
+ - 返回前自检一次:数据库访问次数应与数据量无关或近似常数级,不能随着行数线性增长为 N 次查询。
24
+
25
+ ## 首选:V8.FormEngine + _Where(自动防注入)
26
26
 
27
27
  `_Where` 是参数化查询语法,自动防 SQL 注入,**永远优先使用**。
28
28
 
@@ -68,66 +68,74 @@ var result = V8.FormEngine.GetTableData('SysUser', {
68
68
 
69
69
  ### 旧版 _Where 兼容(V8.Method.ParseWhere)
70
70
 
71
- 老版本前端可能传旧格式(`{Name, Value, Type, AndOr, Group}`),转换成新格式:
71
+ 老版本前端可能传对象格式(`{Name, Value, Type, AndOr, GroupStart, GroupEnd}`)。
72
+ `V8.Method.ParseWhere` 返回统一的 `DiyWhere` 对象集合;它不是“对象转数组”函数。
72
73
 
73
74
  ```javascript
74
- var newWhere = V8.Method.ParseWhere(V8.Param._Where);
75
- V8.FormEngine.GetTableData('Table', { _Where: newWhere });
75
+ var objectWhere = V8.Method.ParseWhere(V8.Param._Where);
76
+ V8.FormEngine.GetTableData('Table', { _Where: objectWhere });
76
77
  ```
77
78
 
78
- ## 次选:V8.Db.FromSql(仅 SQL 字符串 + AddInParameter)
79
-
80
- > **⚠️ FormEngine 优先原则:** 增删改操作(INSERT / UPDATE / DELETE)必须**优先**使用 `V8.FormEngine.AddFormData` / `UptFormData` / `UptFormDataByWhere` / `DelFormData` 等方法。**只有**多表 JOIN、复杂子查询、GROUP BY 聚合等 FormEngine 无法表达的场景才使用 `V8.Db.FromSql`。
81
-
82
- > **⚠️ FromSql 调用规则:** `V8.Db.FromSql` 在 V8 中只传 SQL 字符串,不要把动态值作为第二个或后续参数传给 `FromSql`。动态值必须用链式 `.AddInParameter('@p0', value)` 绑定;否则会生成平台不支持的调用签名。
83
-
84
- > **共享事务:** `V8.Db` 是主库会话,`V8.Db.FromSql` 不会自动加入接口引擎事务。需要与表单、其它 SQL 或流程共同提交/回滚的操作,必须使用 `V8.DbTrans.FromSql`;依赖本事务尚未提交结果的查询也一样。`V8.FormEngine`、`V8.ApiEngine.Run` 继续显式传第三参数 `V8.DbTrans`。禁止从安全代理取内部事务并自行提交。
85
-
86
- ```javascript
87
- // ❌ 错误形态:不要把动态值作为 FromSql 的第二个参数传入
88
-
89
- // ✅ 优先:改用 FormEngine(单表增删改查都优先这样写)
90
- V8.FormEngine.UptFormData('t', { Id: id, A: val1, B: val2 });
91
-
92
- // ✅ 必须用原生 SQL 时:FromSql 只传 SQL,参数用 AddInParameter
93
- V8.DbTrans.FromSql("UPDATE t SET A=@p0, B=@p1 WHERE Id=@p2")
94
- .AddInParameter("@p0", val1)
95
- .AddInParameter("@p1", val2)
96
- .AddInParameter("@p2", id)
97
- .ExecuteNonQuery();
98
- ```
79
+ 合并客户端筛选与接口固定条件时,必须先统一格式,禁止直接把数组条件追加到对象条件集合。
80
+ 部分部署按首项类型解析整个集合,混合格式会触发转换失败并退化为空条件,导致统计返回未筛选总数。
81
+ 需要兼容混合输入时,逐条调用 `ParseWhere(JSON.stringify([condition]))`,确认每条恰好解析出一个有效条件后,
82
+ 再合并为同一种格式;非空输入解析为空、非法操作符或括号不平衡必须返回失败,不能继续执行统计。
83
+ 存在顶层 OR 时,还须保证语义为“整个客户端筛选 AND 固定条件”,不能仅在原表达式末尾追加 AND。
84
+ 回归至少比较纯数组、纯对象、混合条件、OR/分组、零结果和非法输入。
85
+
86
+ ## 次选:V8.Db.FromSql(仅 SQL 字符串 + AddInParameter)
87
+
88
+ > **⚠️ FormEngine 优先原则:** 增删改操作(INSERT / UPDATE / DELETE)必须**优先**使用 `V8.FormEngine.AddFormData` / `UptFormData` / `UptFormDataByWhere` / `DelFormData` 等方法。**只有**多表 JOIN、复杂子查询、GROUP BY 聚合等 FormEngine 无法表达的场景才使用 `V8.Db.FromSql`。
89
+
90
+ > **⚠️ FromSql 调用规则:** `V8.Db.FromSql` 在 V8 中只传 SQL 字符串,不要把动态值作为第二个或后续参数传给 `FromSql`。动态值必须用链式 `.AddInParameter('@p0', value)` 绑定;否则会生成平台不支持的调用签名。
91
+
92
+ > **共享事务:** `V8.Db` 是主库会话,`V8.Db.FromSql` 不会自动加入接口引擎事务。需要与表单、其它 SQL 或流程共同提交/回滚的操作,必须使用 `V8.DbTrans.FromSql`;依赖本事务尚未提交结果的查询也一样。`V8.FormEngine`、`V8.ApiEngine.Run` 继续显式传第三参数 `V8.DbTrans`。禁止从安全代理取内部事务并自行提交。
93
+
94
+ ```javascript
95
+ // ❌ 错误形态:不要把动态值作为 FromSql 的第二个参数传入
96
+
97
+ // ✅ 优先:改用 FormEngine(单表增删改查都优先这样写)
98
+ V8.FormEngine.UptFormData('t', { Id: id, A: val1, B: val2 });
99
+
100
+ // ✅ 必须用原生 SQL 时:FromSql 只传 SQL,参数用 AddInParameter
101
+ V8.DbTrans.FromSql("UPDATE t SET A=@p0, B=@p1 WHERE Id=@p2")
102
+ .AddInParameter("@p0", val1)
103
+ .AddInParameter("@p1", val2)
104
+ .AddInParameter("@p2", id)
105
+ .ExecuteNonQuery();
106
+ ```
99
107
 
100
108
  当 `_Where` 无法满足复杂查询(多表 JOIN、子查询、聚合统计)时使用 `V8.Db`:
101
109
 
102
110
  ```javascript
103
- // ✅ 安全:使用 @p0, @p1 占位符,并用 AddInParameter 绑定
104
- var list = V8.Db.FromSql(
105
- 'SELECT a.Id, a.Name, b.OrderCount FROM Customer a LEFT JOIN (SELECT CustomerId, COUNT(*) OrderCount FROM OrderHeader GROUP BY CustomerId) b ON a.Id = b.CustomerId WHERE a.Status = @p0'
106
- ).AddInParameter("@p0", 1).ToArray();
107
-
108
- // ✅ 安全:多个参数
109
- var row = V8.Db.FromSql(
110
- 'SELECT * FROM SysUser WHERE Account = @p0 AND DeptId = @p1'
111
- ).AddInParameter("@p0", V8.Param.account)
112
- .AddInParameter("@p1", V8.Param.deptId)
113
- .First();
114
-
115
- // 统计
116
- var count = V8.Db.FromSql(
117
- 'SELECT COUNT(*) FROM OrderHeader WHERE Status = @p0 AND CreateTime >= @p1'
118
- ).AddInParameter("@p0", 1)
119
- .AddInParameter("@p1", V8.Param.startDate)
120
- .ToScalar();
121
-
122
- // 非查询(UPDATE / INSERT / DELETE)
123
- V8.DbTrans.FromSql(
124
- 'UPDATE SysUser SET LastLoginTime = @p0 WHERE Id = @p1'
125
- ).AddInParameter("@p0", DateNow('yyyy-MM-dd HH:mm:ss'))
126
- .AddInParameter("@p1", V8.CurrentUser.Id)
127
- .ExecuteNonQuery();
128
- ```
129
-
130
- > 老版本兼容:要把维护接口复制到旧部署时,不要假定 `DateNow` 或 `System.DateTime.Now.ToString(...)` 一定可用。若数据库类型固定,可直接使用该数据库的当前时间表达式(例如 MySQL 的 `NOW()`);跨数据库代码则应按数据库类型选择表达式或传入平台已确认支持的时间值。
111
+ // ✅ 安全:使用 @p0, @p1 占位符,并用 AddInParameter 绑定
112
+ var list = V8.Db.FromSql(
113
+ 'SELECT a.Id, a.Name, b.OrderCount FROM Customer a LEFT JOIN (SELECT CustomerId, COUNT(*) OrderCount FROM OrderHeader GROUP BY CustomerId) b ON a.Id = b.CustomerId WHERE a.Status = @p0'
114
+ ).AddInParameter("@p0", 1).ToArray();
115
+
116
+ // ✅ 安全:多个参数
117
+ var row = V8.Db.FromSql(
118
+ 'SELECT * FROM SysUser WHERE Account = @p0 AND DeptId = @p1'
119
+ ).AddInParameter("@p0", V8.Param.account)
120
+ .AddInParameter("@p1", V8.Param.deptId)
121
+ .First();
122
+
123
+ // 统计
124
+ var count = V8.Db.FromSql(
125
+ 'SELECT COUNT(*) FROM OrderHeader WHERE Status = @p0 AND CreateTime >= @p1'
126
+ ).AddInParameter("@p0", 1)
127
+ .AddInParameter("@p1", V8.Param.startDate)
128
+ .ToScalar();
129
+
130
+ // 非查询(UPDATE / INSERT / DELETE)
131
+ V8.DbTrans.FromSql(
132
+ 'UPDATE SysUser SET LastLoginTime = @p0 WHERE Id = @p1'
133
+ ).AddInParameter("@p0", DateNow('yyyy-MM-dd HH:mm:ss'))
134
+ .AddInParameter("@p1", V8.CurrentUser.Id)
135
+ .ExecuteNonQuery();
136
+ ```
137
+
138
+ > 老版本兼容:要把维护接口复制到旧部署时,不要假定 `DateNow` 或 `System.DateTime.Now.ToString(...)` 一定可用。若数据库类型固定,可直接使用该数据库的当前时间表达式(例如 MySQL 的 `NOW()`);跨数据库代码则应按数据库类型选择表达式或传入平台已确认支持的时间值。
131
139
 
132
140
  ### V8.Db 方法速查
133
141
 
@@ -142,52 +150,52 @@ V8.DbTrans.FromSql(
142
150
 
143
151
  ### 读写分离
144
152
 
145
- ```javascript
146
- V8.Db.FromSql(...) // 主库(读写)
147
- V8.DbRead.FromSql(...) // 从库(只读,适合报表和大量查询)
153
+ ```javascript
154
+ V8.Db.FromSql(...) // 主库(读写)
155
+ V8.DbRead.FromSql(...) // 从库(只读,适合报表和大量查询)
148
156
  // 未部署读写分离时 V8.DbRead 与 V8.Db 一致
149
157
  ```
150
158
 
151
- ### 跨应用查询(扩展数据库)
152
-
153
- ```javascript
154
- var list = V8.Dbs.OracleDB1.FromSql('SELECT * FROM Table WHERE Id = @p0')
155
- .AddInParameter("@p0", id)
156
- .ToArray();
157
-
158
- // 不写入 microi_database:创建仅当前请求使用的临时会话
159
- var tempDb = V8.Dbs.Open(
160
- 'SqlServer',
161
- 'Server=127.0.0.1,1433;Database=app;User Id=user;Password=***;TrustServerCertificate=True;'
162
- );
163
- var tempList = tempDb.FromSql('SELECT Id, Name FROM Customer WHERE Status = @p0')
164
- .AddInParameter('@p0', 1)
165
- .ToArray();
166
- ```
167
-
168
- `V8.Dbs.Open` 与保存连接均只支持 Dos.ORM 已认证的 `MySql`、`SqlServer`、`Oracle`、`PostgreSql`、`DaMeng`、`KingBase`。动态连接串只能来自可信服务端密钥或管理员代码,禁止使用 `V8.Param.ConnectionString`,禁止记录或返回。外部 SQL 的表名、列名、排序字段必须来自已校验元数据白名单;动态值继续使用 `AddInParameter`。
169
-
170
- MCP 结构发现使用 `microi_inspect_external_database`,安全抽样默认使用只读的 `microi_query_external_database`。当用户明确要求数据库管理级能力时,使用独立的 `microi_execute_external_database`:它只允许后端确认的 `Level >= 9999` 当前用户调用,显式确认后可执行目标数据库账号有权执行的任意 DML、DDL、存储过程、数据库原生命令或多语句。输出行数限制只保护 MCP 传输,不限制数据库副作用;审计只记录 SQL 哈希、长度、模式和结果,不得记录 SQL 正文、连接串或密码。
171
-
172
- 不要用 `microi_get_db_schema` 读取第三方库,也不要在对话中搬运全库数据;持续同步应创建参数化、分页、幂等的服务端任务。最高权限入口不等于跨租户,也不能超越目标数据库账号自身权限。
159
+ ### 跨应用查询(扩展数据库)
160
+
161
+ ```javascript
162
+ var list = V8.Dbs.OracleDB1.FromSql('SELECT * FROM Table WHERE Id = @p0')
163
+ .AddInParameter("@p0", id)
164
+ .ToArray();
165
+
166
+ // 不写入 microi_database:创建仅当前请求使用的临时会话
167
+ var tempDb = V8.Dbs.Open(
168
+ 'SqlServer',
169
+ 'Server=127.0.0.1,1433;Database=app;User Id=user;Password=***;TrustServerCertificate=True;'
170
+ );
171
+ var tempList = tempDb.FromSql('SELECT Id, Name FROM Customer WHERE Status = @p0')
172
+ .AddInParameter('@p0', 1)
173
+ .ToArray();
174
+ ```
175
+
176
+ `V8.Dbs.Open` 与保存连接均只支持 Dos.ORM 已认证的 `MySql`、`SqlServer`、`Oracle`、`PostgreSql`、`DaMeng`、`KingBase`。动态连接串只能来自可信服务端密钥或管理员代码,禁止使用 `V8.Param.ConnectionString`,禁止记录或返回。外部 SQL 的表名、列名、排序字段必须来自已校验元数据白名单;动态值继续使用 `AddInParameter`。
177
+
178
+ MCP 结构发现使用 `microi_inspect_external_database`,安全抽样默认使用只读的 `microi_query_external_database`。当用户明确要求数据库管理级能力时,使用独立的 `microi_execute_external_database`:它只允许后端确认的 `Level >= 9999` 当前用户调用,显式确认后可执行目标数据库账号有权执行的任意 DML、DDL、存储过程、数据库原生命令或多语句。输出行数限制只保护 MCP 传输,不限制数据库副作用;审计只记录 SQL 哈希、长度、模式和结果,不得记录 SQL 正文、连接串或密码。
179
+
180
+ 不要用 `microi_get_db_schema` 读取第三方库,也不要在对话中搬运全库数据;持续同步应创建参数化、分页、幂等的服务端任务。最高权限入口不等于跨租户,也不能超越目标数据库账号自身权限。
173
181
 
174
182
  ## 数据库事务
175
183
 
176
184
  ### 接口引擎事务(自动管理)
177
185
 
178
186
  ```javascript
179
- // 接口引擎创建 V8.DbTrans;V8.Db 是独立主库会话,不自动加入它:
187
+ // 接口引擎创建 V8.DbTrans;V8.Db 是独立主库会话,不自动加入它:
180
188
  // 返回 Code=1 → 自动提交事务
181
189
  // 返回 Code≠1 → 自动回滚事务
182
190
  // 手动调用 V8.DbTrans.Commit() 或 V8.DbTrans.Rollback() 均无效
183
- V8.DbTrans.FromSql('UPDATE Account SET Balance = Balance - @p0 WHERE Id = @p1')
184
- .AddInParameter("@p0", 100)
185
- .AddInParameter("@p1", fromId)
186
- .ExecuteNonQuery();
187
- V8.DbTrans.FromSql('UPDATE Account SET Balance = Balance + @p0 WHERE Id = @p1')
188
- .AddInParameter("@p0", 100)
189
- .AddInParameter("@p1", toId)
190
- .ExecuteNonQuery();
191
+ V8.DbTrans.FromSql('UPDATE Account SET Balance = Balance - @p0 WHERE Id = @p1')
192
+ .AddInParameter("@p0", 100)
193
+ .AddInParameter("@p1", fromId)
194
+ .ExecuteNonQuery();
195
+ V8.DbTrans.FromSql('UPDATE Account SET Balance = Balance + @p0 WHERE Id = @p1')
196
+ .AddInParameter("@p0", 100)
197
+ .AddInParameter("@p1", toId)
198
+ .ExecuteNonQuery();
191
199
 
192
200
  // V8.DbTrans 可传给 FormEngine 和 ApiEngine.Run 共享事务
193
201
  V8.FormEngine.UptFormData('Table1', { Id: 'x', Status: 1 }, V8.DbTrans);
@@ -198,16 +206,16 @@ V8.ApiEngine.Run('other-engine', { Id: 'x' }, V8.DbTrans);
198
206
 
199
207
  ```javascript
200
208
  // 扩展数据库需要手动管理事务
201
- var exTrans = V8.Dbs.OracleDB1.BeginTransaction();
202
- try {
203
- exTrans.FromSql('UPDATE t1 SET a = @p0 WHERE Id = @p1')
204
- .AddInParameter("@p0", 1)
205
- .AddInParameter("@p1", id1)
206
- .ExecuteNonQuery();
207
- exTrans.FromSql('UPDATE t2 SET b = @p0 WHERE Id = @p1')
208
- .AddInParameter("@p0", 2)
209
- .AddInParameter("@p1", id2)
210
- .ExecuteNonQuery();
209
+ var exTrans = V8.Dbs.OracleDB1.BeginTransaction();
210
+ try {
211
+ exTrans.FromSql('UPDATE t1 SET a = @p0 WHERE Id = @p1')
212
+ .AddInParameter("@p0", 1)
213
+ .AddInParameter("@p1", id1)
214
+ .ExecuteNonQuery();
215
+ exTrans.FromSql('UPDATE t2 SET b = @p0 WHERE Id = @p1')
216
+ .AddInParameter("@p0", 2)
217
+ .AddInParameter("@p1", id2)
218
+ .ExecuteNonQuery();
211
219
  exTrans.Commit();
212
220
  } catch (ex) {
213
221
  exTrans.Rollback();
@@ -226,11 +234,11 @@ V8.Db.FromSql(sql).ToArray(); // SQL 注入漏洞!
226
234
  // ❌ 禁止:动态拼接表名
227
235
  var sql = "SELECT * FROM " + V8.Param.tableName + " WHERE Id = @p0";
228
236
 
229
- // ✅ 正确做法:单表查询优先使用 FormEngine + _Where
230
- var result = V8.FormEngine.GetTableData('SysUser', {
231
- _Where: [['Account', '=', V8.Param.account]],
232
- _PageSize: 20
233
- });
237
+ // ✅ 正确做法:单表查询优先使用 FormEngine + _Where
238
+ var result = V8.FormEngine.GetTableData('SysUser', {
239
+ _Where: [['Account', '=', V8.Param.account]],
240
+ _PageSize: 20
241
+ });
234
242
  ```
235
243
 
236
244
  ## 常见查询模式
@@ -273,21 +281,21 @@ var result = V8.FormEngine.GetTableData('Customer', {
273
281
  ### 关联查询(SQL JOIN)
274
282
 
275
283
  ```javascript
276
- var list = V8.Db.FromSql(`
277
- SELECT o.Id, o.OrderNo, o.TotalAmount, c.Name AS CustomerName
278
- FROM OrderHeader o
279
- INNER JOIN Customer c ON o.CustomerId = c.Id
280
- WHERE o.Status = @p0 AND o.CreateTime >= @p1
281
- ORDER BY o.CreateTime DESC
282
- `).AddInParameter("@p0", 1)
283
- .AddInParameter("@p1", V8.Param.startDate)
284
- .ToArray();
285
- ```
286
-
287
- ## 注意事项
288
-
289
- - `V8.Db.FromSql` 只传 SQL 字符串,参数占位符从 `@p0` 开始递增,动态值用 `.AddInParameter("@p0", value)` 绑定
290
- - 服务端 `V8.FormEngine` 操作默认不触发表单 V8 事件;确需触发时在参数中加 `_InvokeType: 'Client'`
284
+ var list = V8.Db.FromSql(`
285
+ SELECT o.Id, o.OrderNo, o.TotalAmount, c.Name AS CustomerName
286
+ FROM OrderHeader o
287
+ INNER JOIN Customer c ON o.CustomerId = c.Id
288
+ WHERE o.Status = @p0 AND o.CreateTime >= @p1
289
+ ORDER BY o.CreateTime DESC
290
+ `).AddInParameter("@p0", 1)
291
+ .AddInParameter("@p1", V8.Param.startDate)
292
+ .ToArray();
293
+ ```
294
+
295
+ ## 注意事项
296
+
297
+ - `V8.Db.FromSql` 只传 SQL 字符串,参数占位符从 `@p0` 开始递增,动态值用 `.AddInParameter("@p0", value)` 绑定
298
+ - 服务端 `V8.FormEngine` 操作默认不触发表单 V8 事件;确需触发时在参数中加 `_InvokeType: 'Client'`
291
299
  - 查询结果数量较大时务必分页,`_PageSize` 默认最大 1000
292
300
  - `V8.DbRead` 适用于不需要实时性的报表查询
293
301
  - 接口引擎的事务由平台自动管理,**不要手动调用** `V8.DbTrans.Commit/Rollback`
@@ -9,7 +9,7 @@ description: Microi 后端 V8 TCP 原始字节集成规范。用于通过 V8.Tcp
9
9
 
10
10
  使用后端 `V8.Tcp` 完成一次性 TCP 连接、原始字节写入和可选的有界响应读取。典型场景是 RAW/JetDirect 9100 网络小票机、串口服务器和 PLC;具体协议帧、校验码与打印业务仍由接口引擎编排。
11
11
 
12
- 文档维护时,只在 `microi.doc/docs/doc/v8-engine/v8-server.md` 原位更新后端 API;不要新建重复页面,也不要手工维护英文文档。改变公共契约时同步后端源码/测试、Microi.Client Monaco 定义、Microi.VSCode 类型与生成知识、AI Skill 镜像。
12
+ 文档维护时,只在 `microi.doc/docs/doc/v8-engine/v8-server.md` 原位更新后端 API;不要新建重复页面,也不要手工维护英文文档。改变公共契约时同步后端源码/测试、Microi.Client Monaco 定义、Microi.Code 类型与生成知识、AI Skill 镜像。
13
13
 
14
14
  ## 先判断是否适用
15
15
 
@@ -142,6 +142,6 @@ var hex = result.Data.Hex;
142
142
 
143
143
  - 源码:`Microi.Server/Microi.V8Engine/Extend/Tcp/V8Tcp.cs` 与 `V8Extend.cs`。
144
144
  - 测试:真实 Jint 注入、Byte/Base64/Hex/Text、回环收发、超时/上限/非法参数。
145
- - 编辑器:`Microi.Client/.../v8-api-server-definitions.js` 与 `Microi.VSCode/src/editor/typingsManager.ts`。
145
+ - 编辑器:`Microi.Client/.../v8-api-server-definitions.js` 与 `Microi.Code/src/editor/typingsManager.ts`。
146
146
  - 文档与知识:官网中文后端 V8 页面、`microi.skills/README.md`、`v8-utilities` 索引、`Microi.AI/Resource` 镜像和向量资源列表。
147
147
  - 验收:目标测试、后端构建、精确重启与健康检查、独立 TCP 回环;真实打印机出纸必须单独报告,不能用回环测试替代。
@@ -42,7 +42,7 @@ description: Microi V8 工作流事件指南。用于编写审批流条件、节
42
42
 
43
43
  <!-- /microi-progressive:chunk -->
44
44
  <!-- microi-progressive:chunk id=v8-workflow-001 sha256=013233bfa935a24b668691318ea79c2f0a95787d05d0b033b82859a5999878ba -->
45
- ## 工作流物理表
45
+ ## 工作流物理表
46
46
 
47
47
  | 表名 | 说明 |
48
48
  |------|------|
@@ -51,9 +51,17 @@ description: Microi V8 工作流事件指南。用于编写审批流条件、节
51
51
  | `wf_line` | 节点间连线/条件 |
52
52
  | `wf_flow` | 流程实例(一次发起对应一行) |
53
53
  | `wf_work` | 待办/已办工作 |
54
- | `wf_history` | 审批历史(每次同意/拒绝/撤回的记录) |
55
-
56
- 直接 SQL 查询常用场景:
54
+ | `wf_history` | 审批历史(每次同意/拒绝/撤回的记录) |
55
+
56
+ ### 原生人员、自动结束与取消快照契约
57
+
58
+ - `wf_node.Users` 是 JSON 序列化的 `IdName` 对象数组,例如 `[{"Id":"<user-id>","Name":"审核人"}]`;不能保存为字符串 Id 数组。`Name` 仅供显示,业务授权仍须按当前主库用户、角色和实际待办接收人判断。前端 `V8.WF.ForceSelectUsers` 的字符串数组是另一份 API 契约,不能混用。
59
+ - 自动结束节点的 `NodeType` 使用 `AutoEnd`。它可能继承上一人工节点的 `ApprovalType=Agree`,并生成一条结束历史;核对独立审批人数时只计入明确配置的人工节点,不能把结束节点当成额外审核人。
60
+ - 原生 `CancelFlow` 可将请求中的 `FormData` 写回 `wf_flow.FormData`;未传时可能保存 `{}`。应用不能把取消后的实例 `FormData` 当成不可变的原始提交快照。需要恢复冻结业务单时,优先核对业务表保存的服务端快照摘要;若使用原生历史,必须限定同一实例、同一业务表/行、唯一原始开始节点及发起人、自动提交历史,并检查版本、金额、配置摘要和零待办。此分支只能恢复草稿或驳回状态,不能据此批准或过账。
61
+ - 当前 `CancelFlow(WFParam)` 使用独立事务,没有 `StartWork/SendWork` 的共享 `DbTrans` 重载。应用不得在持有实例、工作或业务行锁的接口事务内调用取消,也不能假设给它多传一个参数便会共享事务。微服务可顺序执行“服务端核验并返回真实待办坐标 → 原生取消 → 业务终态复核”,每个请求完成后释放其事务;网络结果未知时先复核终态,不能盲目重发取消或提前恢复业务状态。
62
+ - 验收至少覆盖真实取消时不传 `FormData`、伪造取消载荷、退回后直接重提被拒绝、取消后新实例重提、自动结束继承 `Agree`,以及普通角色不能修改取证用的流程历史。模拟正确 JSON 不替代原生 HTTP 验收。
63
+
64
+ 直接 SQL 查询常用场景:
57
65
 
58
66
  ```javascript
59
67
  // 我的待办
@@ -106,7 +106,7 @@ AI 在用户本机启动 Node.js、Vite、Webpack、dotnet build、Java、Docker
106
106
  - 本规则只约束 `Microi.Client/` 吾码框架前端源码。独立 MicroService、Web、UniApp 等应用源码仍按其交付 Skill 在发布前执行自身必要的构建;不得因为本规则跳过微服务正式产物生成。
107
107
 
108
108
  <!-- /microi-progressive:chunk -->
109
- <!-- microi-progressive:chunk id=workspace-conventions-004 sha256=aec3b6a1666c1f57d8fb208eb29c3d3f91bef5d4d502ed08aee9040168a7e9ec -->
109
+ <!-- microi-progressive:chunk id=workspace-conventions-004 sha256=533d4a6d674f5980fdf6e9ae376b240af20e3191126a20eae3d6235897f45a88 -->
110
110
  ## 临时文件与 AI 产物放置规则(强制)
111
111
 
112
112
  AI 在工作区任意任务中生成的**一次性临时脚本、诊断文件、测试截图、临时报告**,**严禁放在工作区根目录(`<workspace-root>/`)**,必须放在指定位置:
@@ -115,7 +115,7 @@ AI 在工作区任意任务中生成的**一次性临时脚本、诊断文件、
115
115
  |------|---------|
116
116
  | 一次性脚本(.py / .mjs / .ps1 / .sh) | `.tmp/` |
117
117
  | 诊断截图、调试图片 | `.tmp/screenshots/` |
118
- | E2E 测试产物(Microi.VSCode 插件生成) | `.microi-e2e/` |
118
+ | E2E 测试产物(Microi.Code 插件生成) | `.microi-e2e/` |
119
119
  | AI 一次性 E2E 脚本、截图、日志、报告 | `.tmp/`、`.tmp/screenshots/`、`.tmp/reports/` |
120
120
  | 性能测试 HTML 报告 | `.microi-performance/` |
121
121
  | 项目专属临时文件 | `<对应子项目目录>/` 内,不要写到根目录 |
@@ -129,10 +129,10 @@ AI 在工作区任意任务中生成的**一次性临时脚本、诊断文件、
129
129
 
130
130
  `.tmp/` 已在 `.gitignore` 中排除,可以随意创建临时文件。任务完成后如无保留价值可以不清理。
131
131
 
132
- **2026-06 强制补充**:AI 不得在任何子项目目录下放置一次性日志、自动化截图、接口回收文件或调试脚本。像 `Microi.Server/Microi.net.Api/.tmp-*.log`、`Microi.Client/*.png` 这类文件一律视为规范失败,必须移到 `<workspace-root>/.tmp/` 或 `<workspace-root>/.tmp/screenshots/`。正式 Playwright 工程由 Microi.VSCode 插件生成时可以继续使用 `.microi-e2e/`,但 AI 为某个任务手写的一次性 Playwright 脚本、报告和截图仍然必须放在 `.tmp/`。
132
+ **2026-06 强制补充**:AI 不得在任何子项目目录下放置一次性日志、自动化截图、接口回收文件或调试脚本。像 `Microi.Server/Microi.net.Api/.tmp-*.log`、`Microi.Client/*.png` 这类文件一律视为规范失败,必须移到 `<workspace-root>/.tmp/` 或 `<workspace-root>/.tmp/screenshots/`。正式 Playwright 工程由 Microi.Code 插件生成时可以继续使用 `.microi-e2e/`,但 AI 为某个任务手写的一次性 Playwright 脚本、报告和截图仍然必须放在 `.tmp/`。
133
133
 
134
134
  <!-- /microi-progressive:chunk -->
135
- <!-- microi-progressive:chunk id=workspace-conventions-005 sha256=ac8e3c7085af4cbde3a5784f65ecb3f8cafa961a2a0e13f5103fa259852773e7 -->
135
+ <!-- microi-progressive:chunk id=workspace-conventions-005 sha256=c48d857c8e3086b29f99ab14d362f99c60d7dd00f9617709ea4f4dcad370611e -->
136
136
  ## Microi 源码路径速查(工作区根相对路径)
137
137
 
138
138
  当用户提到“吾码后端源码”“吾码前端源码”“表单引擎源码”“官网源码”等简称时,默认按下列路径定位;如果当前工作区缺少对应目录,再用 `rg --files` 或目录搜索确认实际位置。
@@ -140,9 +140,9 @@ AI 在工作区任意任务中生成的**一次性临时脚本、诊断文件、
140
140
  | 用户常用说法 | 默认路径 |
141
141
  |--------------|----------|
142
142
  | 吾码 MCP 前端源码 | `microi.mcp/` |
143
- | 吾码 MCP 后端源码 | `Microi.Server/Microi.net.Api/Controllers/V8EngineController.cs` |
143
+ | 吾码 MCP 后端源码 | `Microi.Server/Microi.MCP/`;HTTP 接口声明保留在 `Microi.Server/Microi.net.Api/Controllers/V8EngineController.cs` |
144
144
  | 吾码 skills / 知识库 | `microi.skills/` |
145
- | 吾码 VS Code 插件项目 | `Microi.VSCode/` |
145
+ | 吾码 VS Code 插件项目 | `Microi.Code/` |
146
146
  | 吾码低代码平台后台系统前端源码 | `Microi.Client/` |
147
147
  | 吾码后台系统前端移动端自适应源码 | `Microi.Client/src/views/mobile/` |
148
148
  | 吾码低代码后端源码 | `Microi.Server/` |
@@ -158,6 +158,8 @@ AI 在工作区任意任务中生成的**一次性临时脚本、诊断文件、
158
158
  | 吾码官方网站 / 文档源码 | `microi.doc/` |
159
159
  | 吾码 AI 应用及应用商城发行源码 | 默认位于 `Microi-V8-Engine/{系统名称} ({ApiBase域名})/{OsClient}.{OsClientType}.{OsClientNetwork}/AI应用/{appKey}/`;受审计的独立源码仓库必须由发布契约显式指定 |
160
160
 
161
+ `Microi.MCP` 与 `Microi.AI` 一样使用独立内部 Git 仓库和统一 DLL 混淆流程。`Microi.Anderson.sln` 加载源码,`Microi.net.sln` 消费同平台版本 NuGet;MCP 实现不得放回 Core 或 Controller,Core 仅保留非 MCP 入口也需要的通用原子。根公开仓必须排除 `Microi.Server/Microi.MCP/` 的普通文件和 gitlink,发布源码指纹须覆盖该独立仓库。
162
+
161
163
  以上路径只作为通用工作区相对路径规范,不写入具体本机盘符。跨仓库、空工作区或普通用户项目中,如果路径不存在,以插件生成的 `AGENTS.md`、MCP 配置和实际文件树为准。
162
164
 
163
165
  每个 `AI应用/{appKey}` 必须只有一个可编辑事实源,统一承载界面、微服务、Manifest、接口引擎、资源策略、测试、构建脚本与商城上传素材。普通应用默认使用当前连接下的 `Microi-V8-Engine` 目录;受审计的官方内置应用若由独立 Git 仓库维护,必须由版本管理的发布契约唯一指向该源码根,并让构建、跨工程测试和发行包共同读取契约。此时同名 `Microi-V8-Engine` 目录只是远端同步镜像,不得回退为构建源。禁止靠目录探测在多份副本之间自动择新,也禁止另建无契约的 `microi.apps/` 平行发行根。
@@ -139,10 +139,10 @@ AI 开始处理定制页面、弹窗、Web、UniApp、微服务或应用商城
139
139
  如果 MCP 返回登录过期,必须先修复或刷新目标 MCP 身份,再继续把 MCP 读取结果当作当前事实;不能因为读取失败就假设在线应用不存在并重复创建。
140
140
 
141
141
  <!-- /microi-progressive:chunk -->
142
- <!-- microi-progressive:chunk id=workspace-conventions-020 sha256=4d3aef6042f7e43be7c2bf61430ce461bfb75bfb853b1ca000b0f5c5836eff7f -->
142
+ <!-- microi-progressive:chunk id=workspace-conventions-020 sha256=64ab3dbde08488e8cf9f320bf74d97c938507170723022efeb6ab4f96380f9ca -->
143
143
  ## VS Code 插件空目录生成规则
144
144
 
145
- Microi.VSCode 面向普通用户时,用户本地可能只是一个空工作区。插件生成 AI 指令文件时不能假设用户已经有 `microi.skills/`、`Microi-V8-Engine/`、`AI-Project/` 或某个固定前端项目目录。
145
+ Microi.Code 面向普通用户时,用户本地可能只是一个空工作区。插件生成 AI 指令文件时不能假设用户已经有 `microi.skills/`、`Microi-V8-Engine/`、`AI-Project/` 或某个固定前端项目目录。
146
146
 
147
147
  强制要求:
148
148
  - 插件的“初始化AI配置”必须能在空目录生成 `microi.skills/`、`.github/copilot-instructions.md`、`AGENTS.md`、`CLAUDE.md`、`.cursorrules`、`.cursor/rules/microi-skills.mdc`、类型提示、`jsconfig.json` 和 MCP 配置。
@@ -151,14 +151,14 @@ Microi.VSCode 面向普通用户时,用户本地可能只是一个空工作区
151
151
  - 插件升级时应继续保护用户本地修改过的 skill 文件,只覆盖插件曾生成且用户未改过的文件。
152
152
 
153
153
  <!-- /microi-progressive:chunk -->
154
- <!-- microi-progressive:chunk id=workspace-conventions-021 sha256=d6da1a47b1f8ed4b1ea863b052c5e1fa7767e2809c39abe4813ea000d9a30db6 -->
154
+ <!-- microi-progressive:chunk id=workspace-conventions-021 sha256=cee19f2fffffce3b4bcd9347a50ecf27add4be0c919e1cf5d00df98d487d29b8 -->
155
155
  ## Microi 版本号规则
156
156
 
157
157
  Microi 通用版本号采用 `主版本.次版本.修订版本` 三段数字格式,从 `1.0.0` 开始。每次发布时最后一位加 1;当某一位超过 `9` 时向前一位进位并将当前位归 `0`,例如 `1.0.9 -> 1.1.0`、`1.9.9 -> 2.0.0`、`9.9.9 -> 10.0.0`。
158
158
 
159
159
  接口引擎代码头、表单/工作流 V8 事件代码头、前端微服务 `sys_microiservice.BuildVersion` 与 `sys_microiservice_page.BuildVersion` 这类业务发布版本统一使用带 `v` 前缀的格式:`v1.0.0 -> v1.0.1 -> v1.0.9 -> v1.1.0 -> v1.9.9 -> v2.0.0 -> v9.9.9 -> v10.0.0`。禁止使用时间戳、随机串或日期作为 BuildVersion;前端微服务上传到分布式存储的目录也必须使用同一个 BuildVersion 分段,便于回溯与 CDN 缓存隔离。
160
160
 
161
- `Microi.VSCode` 发布时会通过 `bump-version.js` 自动自增插件版本,并把 `microi.skills/.microi-skills-version.json` 中的 skills 发布版本写成同一个插件版本号;skills 不再独立自增。`.microi-skills-version.json` 只用于记录 skills 包版本和提示用户当前来源,不能单独作为覆盖依据。
161
+ `Microi.Code` 发布时会通过 `bump-version.js` 自动自增插件版本,并把 `microi.skills/.microi-skills-version.json` 中的 skills 发布版本写成同一个插件版本号;skills 不再独立自增。`.microi-skills-version.json` 只用于记录 skills 包版本和提示用户当前来源,不能单独作为覆盖依据。
162
162
 
163
163
  插件初始化或升级同步 `microi.skills/` 时,必须以 `.microi-skills-manifest.json` 的逐文件 hash 判断是否可覆盖:本地文件不存在则写入;本地文件与旧 manifest hash 一致说明用户未改,可自动升级;本地文件已被用户修改、或本地版本比插件捆绑版本更新时,必须保留用户版本并提示差异。不能因为插件版本号更高或更低,就粗暴覆盖本地 skills。创始人本地随时修改 skills 的工作区尤其要保护;普通用户未修改过的旧 skills 才应该被最新插件覆盖升级。
164
164
 
@@ -185,7 +185,7 @@ if (string.IsNullOrWhiteSpace(tableName)) { return; }
185
185
  如果方法内部只通过 `DynamicHelper` 读取对象字段,方法参数优先声明为 `object`,不要声明为 `dynamic`。这样可以减少 C# 运行时动态绑定进入普通字符串工具链的机会。
186
186
 
187
187
  <!-- /microi-progressive:chunk -->
188
- <!-- microi-progressive:chunk id=workspace-conventions-023 sha256=3ebfed66fbaabf345188326688f195186d6f33f72252e2ce19041b33a5eef403 -->
188
+ <!-- microi-progressive:chunk id=workspace-conventions-023 sha256=1dfe4286cfee04571a3d24a32af15365b9da6abffe9acfd92a75539e6160f2bd -->
189
189
  ## 根目录保留文件说明
190
190
 
191
191
  根目录只允许存在以下类型的文件和目录:
@@ -202,7 +202,7 @@ if (string.IsNullOrWhiteSpace(tableName)) { return; }
202
202
  | `AI-Project/` | 各租户/项目 | 否 |
203
203
  | `switch-env.ps1` | 本地环境切换工具 | 否(有用) |
204
204
  | `.tmp/` | AI 临时文件(gitignored) | 可删整个目录 |
205
- | `.microi-e2e/` | Microi.VSCode 插件 E2E 产物 | 可定期清理 |
205
+ | `.microi-e2e/` | Microi.Code 插件 E2E 产物 | 可定期清理 |
206
206
  | `.microi-performance/` | 性能测试报告 | 可定期清理 |
207
207
 
208
208
  <!-- /microi-progressive:chunk -->