openxiangda 1.0.92 → 1.0.94
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 +42 -0
- package/lib/cli.js +1211 -23
- package/lib/design-gates.js +449 -0
- package/openxiangda-skills/SKILL.md +23 -20
- package/openxiangda-skills/references/architecture-design.md +25 -0
- package/openxiangda-skills/references/resource-manifest-cheatsheet.md +36 -0
- package/openxiangda-skills/skills/openxiangda-architecture-design/SKILL.md +29 -0
- package/package.json +4 -1
- package/templates/openxiangda-react-spa/AGENTS.md +6 -0
- package/templates/sy-lowcode-app-workspace/AGENTS.md +7 -0
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
const DESIGN_GATE_HARD_RULE =
|
|
2
|
+
'架构类需求默认只规划、不实现;用户明确确认设计后,才允许改文件、写平台资源或发布。';
|
|
3
|
+
|
|
4
|
+
const DESIGN_GATE_TOPICS = [
|
|
5
|
+
{
|
|
6
|
+
code: 'new-app',
|
|
7
|
+
title: '新应用 / 大版本应用',
|
|
8
|
+
triggers: ['新应用', '从零搭建', '门户', '后台系统', '管理系统', '业务系统'],
|
|
9
|
+
mustAsk: [
|
|
10
|
+
'目标用户是谁,内部用户和外部用户是否都存在',
|
|
11
|
+
'核心对象和主流程是什么,哪些对象需要表单、data-view 或 App Function',
|
|
12
|
+
'是否需要登录注册、公开访问、组织内权限分层',
|
|
13
|
+
'首屏是工作台、列表、表单、公开页还是仪表盘',
|
|
14
|
+
'是否需要集成外部系统、通知、流程自动化',
|
|
15
|
+
],
|
|
16
|
+
recommendedDefaults: [
|
|
17
|
+
'React SPA runtime,路由用 src/resources/routes 声明',
|
|
18
|
+
'多资源正式开发走 src/resources/** + resource plan/publish',
|
|
19
|
+
'先输出应用地图、资源清单、权限矩阵、验收用例,再实现',
|
|
20
|
+
],
|
|
21
|
+
antiPatterns: [
|
|
22
|
+
'先创建页面再回头补权限和数据模型',
|
|
23
|
+
'把公开访问写成旧版 ?publicAccess=guest 链接',
|
|
24
|
+
'用页面本地状态模拟应落库的数据',
|
|
25
|
+
],
|
|
26
|
+
acceptance: [
|
|
27
|
+
'用户确认资源边界和权限策略',
|
|
28
|
+
'resource validate/plan 无错误',
|
|
29
|
+
'关键公开、登录、权限拒绝路径都有验收步骤',
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
code: 'complex-page',
|
|
34
|
+
title: '复杂页面 / 工作台 / Dashboard',
|
|
35
|
+
triggers: ['复杂页面', '看板', '工作台', '详情页', '列表页', '移动端', '大屏'],
|
|
36
|
+
mustAsk: [
|
|
37
|
+
'页面的主任务、最高频操作、首屏信息密度',
|
|
38
|
+
'数据来自表单直读、data-view 聚合、App Function 还是连接器',
|
|
39
|
+
'是否需要编辑、批量操作、导入导出、移动端适配',
|
|
40
|
+
'各角色能看哪些模块和字段',
|
|
41
|
+
'加载、空状态、失败重试和越权状态如何呈现',
|
|
42
|
+
],
|
|
43
|
+
recommendedDefaults: [
|
|
44
|
+
'读取类复杂列表优先用 data-view,跨表逻辑优先 App Function',
|
|
45
|
+
'管理后台采用紧凑、可扫描布局,避免营销式 hero',
|
|
46
|
+
'页面路由和菜单分离声明,页面权限单独验收',
|
|
47
|
+
],
|
|
48
|
+
antiPatterns: [
|
|
49
|
+
'直接在前端拼接跨表权限逻辑',
|
|
50
|
+
'宽泛授予表单全部数据再靠 UI 隐藏',
|
|
51
|
+
'移动端只靠桌面页面缩放',
|
|
52
|
+
],
|
|
53
|
+
acceptance: [
|
|
54
|
+
'不同角色打开页面看到的模块符合权限矩阵',
|
|
55
|
+
'无数据、错误、越权、加载状态可用',
|
|
56
|
+
'公开页面不依赖 PageProvider 私有页面上下文',
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
code: 'auth',
|
|
61
|
+
title: '登录 / 注册 / 账号绑定',
|
|
62
|
+
triggers: ['登录', '注册', '手机号', '验证码', '外部账号', '账号绑定', 'SSO'],
|
|
63
|
+
mustAsk: [
|
|
64
|
+
'用户来源是平台组织用户、应用自注册用户、外部账号还是混合',
|
|
65
|
+
'登录方式:密码、手机号验证码、第三方 SSO、免登 ticket',
|
|
66
|
+
'注册策略:开放注册、邀请码、管理员审核、禁止注册',
|
|
67
|
+
'账号和组织用户如何匹配:手机号、邮箱、工号、外部 ID',
|
|
68
|
+
'失败、冻结、解绑、找回密码的处理边界',
|
|
69
|
+
],
|
|
70
|
+
recommendedDefaults: [
|
|
71
|
+
'用 src/resources/auth/*.json 声明 auth-config',
|
|
72
|
+
'默认 registration.mode=reject,除非用户确认开放注册',
|
|
73
|
+
'验证码发送优先由显式 grant 的 App Function/连接器承接',
|
|
74
|
+
],
|
|
75
|
+
antiPatterns: [
|
|
76
|
+
'前端自行保存明文密码或验证码',
|
|
77
|
+
'未确认注册策略就开放外部用户写入',
|
|
78
|
+
'把登录页和公开访问页混为同一权限模型',
|
|
79
|
+
],
|
|
80
|
+
acceptance: [
|
|
81
|
+
'auth-config methods 与实际登录页参数一致',
|
|
82
|
+
'未登录访问内部路由会跳登录或拒绝',
|
|
83
|
+
'注册/绑定失败有明确错误码和文案',
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
code: 'public-access',
|
|
88
|
+
title: '公开访问 / 外部人员无需登录',
|
|
89
|
+
triggers: ['公开访问', '无需登录', '外部人员', '游客', '报名', '公开查询', '分享链接'],
|
|
90
|
+
mustAsk: [
|
|
91
|
+
'公开的是哪些 route,是否仅 /view/:appType/public/*',
|
|
92
|
+
'外部角色有哪些,是否只读、可提交表单、可调用函数或连接器',
|
|
93
|
+
'是否需要 ticket:一次性、有效期、绑定手机号/邮箱、签名来源',
|
|
94
|
+
'允许访问哪些 form、data-view、function、connector grant',
|
|
95
|
+
'是否需要限流、过期时间、审计字段和回收策略',
|
|
96
|
+
],
|
|
97
|
+
recommendedDefaults: [
|
|
98
|
+
'新 React SPA 仅使用 /view/:appType/public/* + routes + public-access policy',
|
|
99
|
+
'普通公开页用 scoped public session;敏感页用 ticket 模式',
|
|
100
|
+
'未显式 grant 的 form/dataView/function/connector 一律拒绝',
|
|
101
|
+
],
|
|
102
|
+
antiPatterns: [
|
|
103
|
+
'新应用继续使用 ?publicAccess=guest',
|
|
104
|
+
'给游客账号平台级宽权限',
|
|
105
|
+
'公开页读取内部 data-view 但没有 public-access grant',
|
|
106
|
+
],
|
|
107
|
+
acceptance: [
|
|
108
|
+
'无登录直接打开公开 route 成功获得 scoped public session',
|
|
109
|
+
'允许的 form/data-view/function/connector 可访问',
|
|
110
|
+
'未 grant 的内部路由和资源返回拒绝,ticket 缺失/过期也拒绝',
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
code: 'permissions',
|
|
115
|
+
title: '角色 / 页面权限 / 表单数据范围',
|
|
116
|
+
triggers: ['权限', '角色', '数据范围', '只看自己', '部门数据', '页面权限', '字段权限'],
|
|
117
|
+
mustAsk: [
|
|
118
|
+
'角色清单及成员来源:固定成员、组织部门、外部角色',
|
|
119
|
+
'页面、菜单、公开路由分别开放给哪些角色',
|
|
120
|
+
'每个表单的 submit/view/manage 操作和字段权限',
|
|
121
|
+
'数据范围是全部、本人、部门、自定义条件还是公开 grant',
|
|
122
|
+
'验收时需要模拟哪些角色和拒绝场景',
|
|
123
|
+
],
|
|
124
|
+
recommendedDefaults: [
|
|
125
|
+
'角色写 src/resources/roles,页面组写 permissions/page-groups',
|
|
126
|
+
'表单权限组写 permissions/form-groups,显式声明 dataScope/operations',
|
|
127
|
+
'公开访问不要复用内部管理员权限组',
|
|
128
|
+
],
|
|
129
|
+
antiPatterns: [
|
|
130
|
+
'只配菜单可见,不配后端数据权限',
|
|
131
|
+
'为了省事授予全部数据再在前端过滤',
|
|
132
|
+
'公开角色和内部角色混用',
|
|
133
|
+
],
|
|
134
|
+
acceptance: [
|
|
135
|
+
'permission audit 输出无高危缺口',
|
|
136
|
+
'内部角色允许项可用,未授权项拒绝',
|
|
137
|
+
'公开访问受 public-access grant 和权限组双重约束',
|
|
138
|
+
],
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
code: 'workflow-automation',
|
|
142
|
+
title: '流程 / 自动化 / App Function',
|
|
143
|
+
triggers: ['流程', '审批', '自动化', '定时', 'App Function', 'JS_CODE', '函数'],
|
|
144
|
+
mustAsk: [
|
|
145
|
+
'触发来源:表单提交、状态变化、定时、手动按钮、公开访问',
|
|
146
|
+
'节点输入输出、失败重试、幂等键和日志保留',
|
|
147
|
+
'函数需要访问哪些 forms/dataViews/connectors',
|
|
148
|
+
'是否需要人工审批、通知、回写表单或调用外部系统',
|
|
149
|
+
'测试用例包含成功、失败、重复触发、越权调用哪些场景',
|
|
150
|
+
],
|
|
151
|
+
recommendedDefaults: [
|
|
152
|
+
'函数源码放 src/functions/<code>/index.ts 或对应模板约定目录',
|
|
153
|
+
'definitionJson 中通过 resources 声明依赖,发布时自动绑定',
|
|
154
|
+
'公开调用函数必须通过 public-access grants.functions 显式开放',
|
|
155
|
+
],
|
|
156
|
+
antiPatterns: [
|
|
157
|
+
'函数里硬编码 formUuid 或外部密钥',
|
|
158
|
+
'忽略重复触发导致重复写入或重复通知',
|
|
159
|
+
'公开页面直接调用未 grant 的内部函数',
|
|
160
|
+
],
|
|
161
|
+
acceptance: [
|
|
162
|
+
'函数 invoke 测试 envelope 成功,失败码会被识别',
|
|
163
|
+
'自动化 executions/logs 可追踪',
|
|
164
|
+
'重复触发不会产生重复副作用',
|
|
165
|
+
],
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
code: 'connector-notification',
|
|
169
|
+
title: '连接器 / 通知 / 外部集成',
|
|
170
|
+
triggers: ['连接器', '第三方接口', '通知', '消息', 'Webhook', '短信', '钉钉'],
|
|
171
|
+
mustAsk: [
|
|
172
|
+
'外部接口域名、鉴权方式、超时、重试、脱敏要求',
|
|
173
|
+
'哪些 connector API 允许内部调用,哪些允许公开访问调用',
|
|
174
|
+
'通知类型、模板变量、渠道、接收人解析方式',
|
|
175
|
+
'失败降级:重试、人工补偿、只记日志还是阻断主流程',
|
|
176
|
+
'是否需要 preview/send/batch-send 的验收样例',
|
|
177
|
+
],
|
|
178
|
+
recommendedDefaults: [
|
|
179
|
+
'连接器写 src/resources/connectors,通知写 src/resources/notifications',
|
|
180
|
+
'复杂写入通过 App Function 包装,连接器只做外部通信',
|
|
181
|
+
'公开访问连接器必须显式 grants.connectors,并限制 API 粒度',
|
|
182
|
+
],
|
|
183
|
+
antiPatterns: [
|
|
184
|
+
'把密钥写入前端或公开 manifest',
|
|
185
|
+
'只看 HTTP 200,不检查 JSON envelope 错误码',
|
|
186
|
+
'通知模板变量和实际 send body 不一致',
|
|
187
|
+
],
|
|
188
|
+
acceptance: [
|
|
189
|
+
'connector invoke/download-test 请求 body 与 SDK DTO 一致',
|
|
190
|
+
'notification preview/send 能用真实模板变量',
|
|
191
|
+
'PUBLIC_GRANT_DENIED 等业务错误码被当作失败',
|
|
192
|
+
],
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
code: 'resource-maintenance',
|
|
196
|
+
title: '资源维护 / 小步修复',
|
|
197
|
+
triggers: ['改一个资源', '补路由', '调整权限', '修复菜单', '删除配置', '同步 manifest'],
|
|
198
|
+
mustAsk: [
|
|
199
|
+
'这是临时 live mutation,还是要同步到仓库 manifest',
|
|
200
|
+
'是否允许删除、覆盖、发送通知等高风险操作',
|
|
201
|
+
'影响哪些 profile/appType,是否覆盖 workspace binding',
|
|
202
|
+
'是否需要先 pull/plan/audit 再写入',
|
|
203
|
+
],
|
|
204
|
+
recommendedDefaults: [
|
|
205
|
+
'多资源正式变更仍走 resource plan/publish',
|
|
206
|
+
'直接 CLI 写平台资源时加 --write-manifest 防漂移',
|
|
207
|
+
'删除、发送、覆盖前需要 --force',
|
|
208
|
+
],
|
|
209
|
+
antiPatterns: [
|
|
210
|
+
'手写未知 HTTP 绕过 CLI',
|
|
211
|
+
'平台改了但 src/resources 没同步',
|
|
212
|
+
'不传 profile/app-type 就在错误应用上写资源',
|
|
213
|
+
],
|
|
214
|
+
acceptance: [
|
|
215
|
+
'dry-run 能展示 method/path/body',
|
|
216
|
+
'成功写入后 state 和 manifest 同步',
|
|
217
|
+
'commands --json 可发现相应资源命令',
|
|
218
|
+
],
|
|
219
|
+
},
|
|
220
|
+
];
|
|
221
|
+
|
|
222
|
+
function selectTopics(topicCode) {
|
|
223
|
+
if (!topicCode || topicCode === 'all') return DESIGN_GATE_TOPICS;
|
|
224
|
+
const wanted = new Set(String(topicCode).split(',').map(item => item.trim()).filter(Boolean));
|
|
225
|
+
return DESIGN_GATE_TOPICS.filter(topic => wanted.has(topic.code));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function getDesignGates(topicCode) {
|
|
229
|
+
return {
|
|
230
|
+
hardRule: DESIGN_GATE_HARD_RULE,
|
|
231
|
+
topics: selectTopics(topicCode),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function renderDesignGatesText(topicCode) {
|
|
236
|
+
const gates = getDesignGates(topicCode);
|
|
237
|
+
const lines = [gates.hardRule, ''];
|
|
238
|
+
for (const topic of gates.topics) {
|
|
239
|
+
lines.push(`# ${topic.code}: ${topic.title}`);
|
|
240
|
+
lines.push(`触发词: ${topic.triggers.join('、')}`);
|
|
241
|
+
lines.push(`必须提问: ${topic.mustAsk.join(';')}`);
|
|
242
|
+
lines.push(`推荐默认: ${topic.recommendedDefaults.join(';')}`);
|
|
243
|
+
lines.push(`反模式: ${topic.antiPatterns.join(';')}`);
|
|
244
|
+
lines.push(`验收: ${topic.acceptance.join(';')}`);
|
|
245
|
+
lines.push('');
|
|
246
|
+
}
|
|
247
|
+
return lines.join('\n').trimEnd();
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function renderDesignTemplate(topicCode) {
|
|
251
|
+
const gates = getDesignGates(topicCode);
|
|
252
|
+
const lines = [
|
|
253
|
+
'# OpenXiangda 架构设计确认稿',
|
|
254
|
+
'',
|
|
255
|
+
`> ${gates.hardRule}`,
|
|
256
|
+
'> 本稿确认前,只允许读取、快照、dry-run、提问和输出设计。',
|
|
257
|
+
'',
|
|
258
|
+
'## 1. 需求边界',
|
|
259
|
+
'- 目标用户:',
|
|
260
|
+
'- 内部/外部访问:',
|
|
261
|
+
'- 核心对象与流程:',
|
|
262
|
+
'- 明确不做:',
|
|
263
|
+
'',
|
|
264
|
+
'## 2. 推荐方案',
|
|
265
|
+
'- Runtime:React SPA',
|
|
266
|
+
'- 资源发布:src/resources/** + openxiangda resource plan/publish',
|
|
267
|
+
'- 公开访问:仅使用 /view/:appType/public/* + routes + public-access policy',
|
|
268
|
+
'',
|
|
269
|
+
'## 3. 待确认问题',
|
|
270
|
+
];
|
|
271
|
+
for (const topic of gates.topics) {
|
|
272
|
+
lines.push(`### ${topic.title}`);
|
|
273
|
+
for (const question of topic.mustAsk) {
|
|
274
|
+
lines.push(`- [ ] ${question}`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
lines.push(
|
|
278
|
+
'',
|
|
279
|
+
'## 4. 资源设计',
|
|
280
|
+
'- routes:',
|
|
281
|
+
'- public-access:',
|
|
282
|
+
'- auth-config:',
|
|
283
|
+
'- roles/page-groups/form-groups:',
|
|
284
|
+
'- forms/data-views/functions/connectors/notifications:',
|
|
285
|
+
'',
|
|
286
|
+
'## 5. 验收清单',
|
|
287
|
+
);
|
|
288
|
+
for (const topic of gates.topics) {
|
|
289
|
+
for (const item of topic.acceptance) {
|
|
290
|
+
lines.push(`- [ ] ${item}`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
lines.push('', '## 6. 用户确认', '- [ ] 用户确认后再进入实现。');
|
|
294
|
+
return lines.join('\n');
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const RESOURCE_EXPLAINS = {
|
|
298
|
+
route: {
|
|
299
|
+
dir: 'src/resources/routes/*.json',
|
|
300
|
+
minimalManifest: {
|
|
301
|
+
code: 'public.register',
|
|
302
|
+
title: '公开报名',
|
|
303
|
+
kind: 'page',
|
|
304
|
+
pathPattern: '/view/:appType/public/register',
|
|
305
|
+
publicAccess: 'guest',
|
|
306
|
+
publicPolicyCode: 'public_register',
|
|
307
|
+
},
|
|
308
|
+
commands: [
|
|
309
|
+
'openxiangda route upsert --json-file src/resources/routes/public_register.json --dry-run',
|
|
310
|
+
'openxiangda resource plan',
|
|
311
|
+
'openxiangda resource publish',
|
|
312
|
+
],
|
|
313
|
+
},
|
|
314
|
+
'public-access': {
|
|
315
|
+
dir: 'src/resources/public-access/*.json',
|
|
316
|
+
minimalManifest: {
|
|
317
|
+
code: 'public_register',
|
|
318
|
+
mode: 'guest',
|
|
319
|
+
routeCode: 'public.register',
|
|
320
|
+
externalRoleCodes: ['external_visitor'],
|
|
321
|
+
grants: {
|
|
322
|
+
forms: ['FORM_OR_FORM_CODE'],
|
|
323
|
+
dataViews: ['public_lookup'],
|
|
324
|
+
functions: [],
|
|
325
|
+
connectors: [],
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
commands: [
|
|
329
|
+
'openxiangda public-access upsert --json-file src/resources/public-access/public_register.json',
|
|
330
|
+
'openxiangda public-access session-test public_register --path /view/APP_XXX/public/register',
|
|
331
|
+
'openxiangda public-access grant-check public_register --form-code customer',
|
|
332
|
+
],
|
|
333
|
+
},
|
|
334
|
+
'auth-config': {
|
|
335
|
+
dir: 'src/resources/auth/*.json',
|
|
336
|
+
minimalManifest: {
|
|
337
|
+
code: 'default',
|
|
338
|
+
status: 'active',
|
|
339
|
+
configJson: {
|
|
340
|
+
methods: [{ type: 'password', enabled: true }],
|
|
341
|
+
registration: { mode: 'reject' },
|
|
342
|
+
binding: { mode: 'auto' },
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
commands: [
|
|
346
|
+
'openxiangda auth-config methods',
|
|
347
|
+
'openxiangda auth-config upsert --json-file src/resources/auth/default.json',
|
|
348
|
+
],
|
|
349
|
+
},
|
|
350
|
+
function: {
|
|
351
|
+
dir: 'src/resources/functions/*.json',
|
|
352
|
+
minimalManifest: {
|
|
353
|
+
code: 'summarize_customer',
|
|
354
|
+
definitionJson: {
|
|
355
|
+
kind: 'app_function',
|
|
356
|
+
version: 'function_v1',
|
|
357
|
+
runtimeMode: 'trusted_node',
|
|
358
|
+
sourceType: 'inline',
|
|
359
|
+
code: 'module.exports = async () => ({ ok: true })',
|
|
360
|
+
},
|
|
361
|
+
status: 'active',
|
|
362
|
+
},
|
|
363
|
+
commands: [
|
|
364
|
+
'openxiangda function upsert --json-file src/resources/functions/summarize_customer.json',
|
|
365
|
+
'openxiangda function invoke summarize_customer --body-json \'{"input":{}}\'',
|
|
366
|
+
],
|
|
367
|
+
},
|
|
368
|
+
connector: {
|
|
369
|
+
dir: 'src/resources/connectors/*.json',
|
|
370
|
+
minimalManifest: {
|
|
371
|
+
code: 'third_party',
|
|
372
|
+
name: '第三方服务',
|
|
373
|
+
protocol: 'https',
|
|
374
|
+
domain: 'api.example.com',
|
|
375
|
+
authType: 'none',
|
|
376
|
+
apis: [{ code: 'ping', path: '/ping', method: 'GET' }],
|
|
377
|
+
},
|
|
378
|
+
commands: [
|
|
379
|
+
'openxiangda connector upsert --json-file src/resources/connectors/third_party.json',
|
|
380
|
+
'openxiangda connector invoke third_party.ping --query-json query.json',
|
|
381
|
+
],
|
|
382
|
+
},
|
|
383
|
+
notification: {
|
|
384
|
+
dir: 'src/resources/notifications/*.json',
|
|
385
|
+
minimalManifest: {
|
|
386
|
+
templates: [{ code: 'reservation_reminder', name: '预约提醒', content: '{{title}}' }],
|
|
387
|
+
typeConfigs: [{ notificationType: 'reservation_reminder', templateCode: 'reservation_reminder', enabled: true }],
|
|
388
|
+
},
|
|
389
|
+
commands: [
|
|
390
|
+
'openxiangda notification template-upsert --json-file src/resources/notifications/reservation_reminder.json',
|
|
391
|
+
'openxiangda notification preview reservation_reminder --body-json \'{"variables":{"title":"测试"}}\'',
|
|
392
|
+
],
|
|
393
|
+
},
|
|
394
|
+
'data-view': {
|
|
395
|
+
dir: 'src/resources/data-views/*.json',
|
|
396
|
+
minimalManifest: {
|
|
397
|
+
code: 'customer_lookup',
|
|
398
|
+
base: { formCode: 'customer', alias: 'customer' },
|
|
399
|
+
select: [{ field: 'customer.form_instance_id', as: 'id' }],
|
|
400
|
+
refresh: { mode: 'manual' },
|
|
401
|
+
},
|
|
402
|
+
commands: [
|
|
403
|
+
'openxiangda data-view upsert --json-file src/resources/data-views/customer_lookup.json',
|
|
404
|
+
'openxiangda data-view query customer_lookup --query-json query.json',
|
|
405
|
+
],
|
|
406
|
+
},
|
|
407
|
+
permission: {
|
|
408
|
+
dir: 'src/resources/roles 与 src/resources/permissions/**',
|
|
409
|
+
minimalManifest: {
|
|
410
|
+
role: { code: 'manager', name: '管理员' },
|
|
411
|
+
pageGroup: { code: 'manager_pages', name: '管理员页面', roles: ['manager'], routeCodes: ['admin.home'] },
|
|
412
|
+
formGroup: { code: 'customer_view', formCode: 'customer', name: '客户查看', type: 'view', roles: ['manager'] },
|
|
413
|
+
},
|
|
414
|
+
commands: [
|
|
415
|
+
'openxiangda permission audit --json',
|
|
416
|
+
'openxiangda permission role-update manager --json-file role.json --write-manifest',
|
|
417
|
+
],
|
|
418
|
+
},
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
function getResourceExplain(type) {
|
|
422
|
+
const key = type || 'route';
|
|
423
|
+
return RESOURCE_EXPLAINS[key];
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function renderResourceExplain(type) {
|
|
427
|
+
const item = getResourceExplain(type);
|
|
428
|
+
if (!item) {
|
|
429
|
+
return `未知资源类型: ${type}`;
|
|
430
|
+
}
|
|
431
|
+
return [
|
|
432
|
+
`# ${type}`,
|
|
433
|
+
`目录: ${item.dir}`,
|
|
434
|
+
'最小 manifest:',
|
|
435
|
+
JSON.stringify(item.minimalManifest, null, 2),
|
|
436
|
+
'常用命令:',
|
|
437
|
+
...item.commands.map(command => `- ${command}`),
|
|
438
|
+
].join('\n');
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
module.exports = {
|
|
442
|
+
DESIGN_GATE_HARD_RULE,
|
|
443
|
+
DESIGN_GATE_TOPICS,
|
|
444
|
+
getDesignGates,
|
|
445
|
+
getResourceExplain,
|
|
446
|
+
renderDesignGatesText,
|
|
447
|
+
renderDesignTemplate,
|
|
448
|
+
renderResourceExplain,
|
|
449
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: openxiangda
|
|
3
|
-
description: "Use OpenXiangda for any private low-code platform or sy-lowcode-app-workspace work: architecture design, app creation, forms, pages, resources, workflows, automations, JS_CODE, permissions, data views, connectors, notifications, publishing, deployment, diagnosis, feedback, profile/login state, and CLI commands. Trigger on OpenXiangda,
|
|
3
|
+
description: "Use OpenXiangda for any private low-code platform or sy-lowcode-app-workspace work: architecture design, app creation, forms, pages, resources, workflows, automations, JS_CODE, permissions, data views, connectors, notifications, publishing, deployment, diagnosis, feedback, profile/login state, and CLI commands. Trigger on OpenXiangda, 享搭, 私有化低代码, 低代码平台, .openxiangda/state.json, app-workspace.config.ts, or openxiangda CLI usage."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# OpenXiangda
|
|
@@ -15,30 +15,31 @@ OpenXiangda supports two workspace modes. Classic `sy-lowcode-app-workspace` pub
|
|
|
15
15
|
|
|
16
16
|
### Routing — user intent → skill / command
|
|
17
17
|
|
|
18
|
-
| User says (zh / en)
|
|
19
|
-
|
|
20
|
-
| 发布 classic workspace / 上线旧模式
|
|
21
|
-
| 发布 React SPA / Phase 6 runtime
|
|
22
|
-
| 只发布改动 / 增量 / 单页 / 单表
|
|
23
|
-
| 架构设计 / 详细设计 / 新应用需求 / 需求文档转享搭应用
|
|
24
|
-
| 创建应用 / 新建 app / scaffold / 初始化工作区
|
|
25
|
-
| 绑定已有应用 / bind existing app
|
|
26
|
-
| 创建 / 修改表单字段 / 表单页 / schema
|
|
27
|
-
| 创建 / 修改自定义代码页 / portal / dashboard
|
|
28
|
-
| 审批流程 / workflow / 流程节点 / JS_CODE
|
|
29
|
-
| 自动化 / 定时任务 / 提交触发 / cron
|
|
30
|
-
| App Function / function_call / 可复用后端逻辑
|
|
31
|
-
| 应用登录 / Auth SDK / 手机号验证码 / CAS / 钉钉免登
|
|
32
|
-
| 角色 / 权限组 / 字段权限 / 数据范围
|
|
18
|
+
| User says (zh / en) | Skill | First command |
|
|
19
|
+
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
20
|
+
| 发布 classic workspace / 上线旧模式 | `openxiangda-core` | `openxiangda workspace publish --profile <name> --changed --dry-run` |
|
|
21
|
+
| 发布 React SPA / Phase 6 runtime | `openxiangda-core` | `openxiangda resource publish --profile <name>` then `openxiangda runtime deploy --profile <name>` |
|
|
22
|
+
| 只发布改动 / 增量 / 单页 / 单表 | `openxiangda-core` | `... --changed` / `--page <code>` / `--form <code>` / `--only pages/a,forms/b` |
|
|
23
|
+
| 架构设计 / 详细设计 / 新应用需求 / 需求文档转享搭应用 | `openxiangda-architecture-design` | read `references/architecture-design.md`; inspect workspace/app state before coding |
|
|
24
|
+
| 创建应用 / 新建 app / scaffold / 初始化工作区 | `openxiangda-app` | `openxiangda workspace init <dir> --profile <name> --app-name "..."` |
|
|
25
|
+
| 绑定已有应用 / bind existing app | `openxiangda-app` | `openxiangda workspace bind --profile <name> --app-type APP_XXX` |
|
|
26
|
+
| 创建 / 修改表单字段 / 表单页 / schema | `openxiangda-form` | edit `src/forms/<code>/{schema.ts,page.tsx}` → `workspace publish --form <code>` |
|
|
27
|
+
| 创建 / 修改自定义代码页 / portal / dashboard | `openxiangda-page` | edit `src/pages/<code>/` → `workspace publish --page <code>` |
|
|
28
|
+
| 审批流程 / workflow / 流程节点 / JS_CODE | `openxiangda-workflow-automation` | `openxiangda workflow validate / create / publish` |
|
|
29
|
+
| 自动化 / 定时任务 / 提交触发 / cron | `openxiangda-workflow-automation` | `openxiangda automation validate / create / publish / enable` |
|
|
30
|
+
| App Function / function_call / 可复用后端逻辑 | `openxiangda-workflow-automation` | declare `src/resources/functions/<code>.json` + `src/functions/<code>/index.ts` |
|
|
31
|
+
| 应用登录 / Auth SDK / 手机号验证码 / CAS / 钉钉免登 | `openxiangda-architecture-design` + `openxiangda-page` | design security gate first, then declare `src/resources/auth/<code>.json` and use `createAuthClient` / `LoginPage` |
|
|
32
|
+
| 角色 / 权限组 / 字段权限 / 数据范围 | `openxiangda-permission-settings` | `openxiangda permission ...` / `openxiangda settings ...` |
|
|
33
33
|
| 外部人员无需登录访问 / 公开报名 / 公开查询 / public page | `openxiangda-architecture-design` + `openxiangda-permission-settings` + `openxiangda-page` | 先确认公开范围、外部角色、ticket、grants;再声明 `routes` + `public-access`,并用 `OpenXiangdaProvider` + `OpenXiangdaPageProvider` + `PublicAccessGate` |
|
|
34
|
-
| 看应用结构 / 快照 / 对比 / 诊断 / 排查 / 报错
|
|
35
|
-
| 登录 / 切换平台 / profile / token / whoami
|
|
36
|
-
| 多表只读联表 / 固定口径统计 / 强实时复杂查询 / 看板指标
|
|
37
|
-
| 调外部 / 第三方 API / 钉钉 / 自建系统
|
|
34
|
+
| 看应用结构 / 快照 / 对比 / 诊断 / 排查 / 报错 | `openxiangda-inspect` | `openxiangda app snapshot <APP_XXX> --profile <name> --json` |
|
|
35
|
+
| 登录 / 切换平台 / profile / token / whoami | `openxiangda-core` | `openxiangda env --profile <name>` / `openxiangda auth status` |
|
|
36
|
+
| 多表只读联表 / 固定口径统计 / 强实时复杂查询 / 看板指标 | `openxiangda-form` (data view) | declare `src/resources/data-views/<code>.json` with `storageMode` → `resource publish` |
|
|
37
|
+
| 调外部 / 第三方 API / 钉钉 / 自建系统 | `openxiangda-page` (connector) | declare `src/resources/connectors/<code>.json` → `sdk.connector.invoke` |
|
|
38
38
|
|
|
39
39
|
### Hard rules — always
|
|
40
40
|
|
|
41
41
|
- ✅ Publish classic workspaces through `openxiangda workspace publish --profile <name>`; publish React SPA workspaces through `openxiangda resource publish` + `openxiangda runtime deploy`.
|
|
42
|
+
- ✅ Architecture-class requests are plan-gated by default. For 新应用、复杂页面、登录注册、公开访问、权限数据范围、流程自动化、连接器/通知、外部集成, first run `openxiangda doctor --json` when a workspace exists and `openxiangda design gates --topic <code> --json`; then ask the user to confirm the design. Before confirmation, only read files, inspect/snapshot, dry-run, ask questions, and write/output design docs. Do not edit source files, write platform resources, send notifications, publish, or deploy.
|
|
42
43
|
- ✅ `runtime deploy` defaults to staged multipart `dist/` uploads plus a final manifest. Use `--upload-mode legacy-json` only for old platform servers.
|
|
43
44
|
- ✅ User token lives in `~/.openxiangda/profiles.json`; project state in `.openxiangda/state.json` (IDs only).
|
|
44
45
|
- ✅ Each profile (dev / prod / ...) has its own `appType` and resource IDs; never copy a `formUuid` / `pageId` / `workflowId` / `automationId` across profiles.
|
|
@@ -47,6 +48,7 @@ OpenXiangda supports two workspace modes. Classic `sy-lowcode-app-workspace` pub
|
|
|
47
48
|
- ✅ For app login/auth requirements, run the auth security gate before coding: enabled methods, registration policy, identity matching keys, provider boundary, default roles, rate limits, audit fields, and third-party ownership must be confirmed.
|
|
48
49
|
- ✅ For public/no-login access requirements, run the public access design gate before coding: public routes, external role codes, guest vs ticket, form/dataView/function/connector grants, backend permission groups, and visible loading/error fallback states must be confirmed. New React SPA apps use `/view/:appType/public/*`, `src/resources/routes/`, `src/resources/public-access/`, and `PublicAccessGate`; route children that use Page SDK hooks must be inside `OpenXiangdaProvider` + `OpenXiangdaPageProvider`.
|
|
49
50
|
- ✅ Public/no-login validation must inspect the JSON envelope, not only HTTP status. HTTP 200 with `code: "PUBLIC_GRANT_DENIED"` is a denied request; success requires `code` `200`, `"200"`, or compatible `0`, and no `success: false`.
|
|
51
|
+
- ✅ Use first-class resource CLI commands for discovery, diagnosis, and small live fixes: `route`, `public-access`, `auth-config`, `function`, `connector`, `notification`, `data-view`, `menu`, and `permission`. For multi-resource formal development, still prefer `src/resources/**` + `openxiangda resource validate|plan|publish`. If a direct CLI write is intentionally used, add `--write-manifest` when the repository should stay the source of truth.
|
|
50
52
|
- ✅ For form-entry UX, pick components in this order: OpenXiangda platform form components → `antd` / `antd-mobile` wrappers → custom component only when neither exists.
|
|
51
53
|
- ✅ List pages, linked options, remote selectors, and report drill-downs must use paginated server-side queries with explicit searchable fields. Do not fetch a large page and filter in local state.
|
|
52
54
|
- ✅ Treat workflow forms as an exception. Ordinary ticket/order/task/asset lifecycles use status fields, state machines, action logs, permissions, and automations; workflows are for real approval tasks.
|
|
@@ -58,6 +60,7 @@ OpenXiangda supports two workspace modes. Classic `sy-lowcode-app-workspace` pub
|
|
|
58
60
|
- ❌ Asking the user for `AK` / `SK` / `appKey` / `appSecret`. The whole flow is normal-user token only.
|
|
59
61
|
- ❌ Searching the platform for a similar app name when `.openxiangda/state.json` has no binding — create a new app with `workspace init --app-name`.
|
|
60
62
|
- ❌ Using `openxiangda form create` / `form publish` / `page publish` as the normal page generation path. They are low-level repair commands.
|
|
63
|
+
- ❌ Implementing an architecture-class request before the user confirms the design. This includes creating files, mutating platform resources, publishing runtime, sending notifications, or calling live write/delete endpoints.
|
|
61
64
|
- ❌ Running a full publish after editing one file. Default to `--changed --dry-run` → `--changed`, or targeted `--page` / `--form`.
|
|
62
65
|
- ❌ Storing tokens, AK, SK, or third-party API secrets in project files. Shared env (`APP_OSS_*`, feedback robot) goes to `~/.openxiangda/.env`.
|
|
63
66
|
- ❌ Raw native HTML form controls in AI-authored workspace code (`<input>`, `<select>`, `<textarea>`, file inputs, hand-written pickers, hand-written upload controls). They are allowed only inside OpenXiangda SDK/platform component internals.
|
|
@@ -44,6 +44,16 @@ Do not insult the person. Criticize the proposal and explain the technical conse
|
|
|
44
44
|
|
|
45
45
|
## Architecture Gate Workflow
|
|
46
46
|
|
|
47
|
+
Before any implementation for architecture-class work, run the CLI gate when available:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
openxiangda doctor --profile <name> --json
|
|
51
|
+
openxiangda design gates --topic public-access --json
|
|
52
|
+
openxiangda design template --topic auth,public-access
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Until the user confirms the design, only read, inspect, snapshot, dry-run, ask questions, and write/output the architecture document. Do not edit app source, mutate platform resources, publish, deploy, send notifications, or invoke live write/delete endpoints.
|
|
56
|
+
|
|
47
57
|
1. **Read the input**: user prompt, uploaded requirements, current workspace files, app snapshots, and existing resources.
|
|
48
58
|
2. **Classify the app**: admin CRUD, portal, workbench, dashboard, workflow approval, status lifecycle, automation-heavy app, integration-heavy app, or mixed.
|
|
49
59
|
3. **Draft the resource map**:
|
|
@@ -60,6 +70,21 @@ Do not insult the person. Criticize the proposal and explain the technical conse
|
|
|
60
70
|
`docs/architecture/<app-slug>-openxiangda-design.md`.
|
|
61
71
|
8. **Create a development task table** that can be executed by OpenXiangda subskills.
|
|
62
72
|
|
|
73
|
+
## Design Gate Matrix
|
|
74
|
+
|
|
75
|
+
Use this matrix to decide what to ask. Ask in small rounds, and recommend the default option when the user is unsure.
|
|
76
|
+
|
|
77
|
+
| Topic | Trigger examples | Must confirm | Recommended default | Resources to design | Acceptance |
|
|
78
|
+
|---|---|---|---|---|---|
|
|
79
|
+
| 新应用 / 大版本 | 新应用、从零搭建、门户、管理系统 | actors, core objects, internal/external access, first screen, integrations | React SPA + `src/resources/**` + design doc first | forms, routes, menus, roles, dataViews, auth/public policies | user confirms resource map and permission matrix |
|
|
80
|
+
| 复杂页面 / 工作台 | 看板、工作台、详情页、移动端、大屏 | page task, data source, edits/batch actions, role visibility, empty/error states | dataView for repeated reads, App Function for backend orchestration | route/menu, page component, dataView/function, permissions | role-specific UI works; no PageProvider errors |
|
|
81
|
+
| 登录 / 注册 | 登录、注册、手机号验证码、SSO、账号绑定 | methods, registration policy, identity match keys, provider function, default roles, rate limits | `registration.mode=reject`; provider returns identity assertion only | `src/resources/auth`, login route/page, App Function provider | methods match SDK DTO; internal route requires auth |
|
|
82
|
+
| 公开访问 | 无需登录、外部人员、公开报名、分享链接 | public routes, external roles, guest vs ticket, grants, rate limit, audit/expiry | `/view/:appType/public/*` + route + public-access policy; sensitive lookup uses ticket | `src/resources/routes`, `src/resources/public-access`, external role permission groups, `PublicAccessGate` | no-login public route works; ungranted resources deny |
|
|
83
|
+
| 权限 / 数据范围 | 角色、只看自己、部门数据、字段权限 | roles, page/menu groups, form operations, data scope, field access, rejection tests | backend permission groups, not frontend-only filtering | roles, page-groups, form-groups, field access policy | `permission audit` clean; allowed/denied paths verified |
|
|
84
|
+
| 流程 / 自动化 / Function | 审批、状态流转、定时、JS_CODE、App Function | trigger, node IO, idempotency, resources, approval vs status machine, logs | normal status machine unless real approval; reusable logic as App Function | workflows, automations, functions, JS_CODE source | invoke/executions/logs verified; duplicate trigger safe |
|
|
85
|
+
| 连接器 / 通知 | 第三方接口、Webhook、短信、钉钉、消息 | auth, timeout/retry, secret owner, notification type/template, recipients, failure policy | connector for external HTTP; notification resources before sending | connectors, notifications, App Function wrapper if needed | `connector invoke` and notification preview/send DTOs pass |
|
|
86
|
+
| 资源维护 | 补路由、修权限、删除配置、同步 manifest | live mutation or repo source of truth, profile/appType, force/delete/send risk | `resource plan/publish` for formal changes; direct CLI with `--write-manifest` for small fixes | affected manifest and state mapping | dry-run path/body reviewed; no repo/platform drift |
|
|
87
|
+
|
|
63
88
|
## Anti-Patterns To Reject
|
|
64
89
|
|
|
65
90
|
| Anti-pattern | Why it is wrong | Required replacement |
|
|
@@ -13,14 +13,50 @@
|
|
|
13
13
|
|
|
14
14
|
## 命令
|
|
15
15
|
|
|
16
|
+
正式多资源开发仍推荐先写 `src/resources/**`,再走声明式发布:
|
|
17
|
+
|
|
16
18
|
```bash
|
|
17
19
|
openxiangda resource validate --profile <name> # 静态校验所有 manifest
|
|
18
20
|
openxiangda resource plan --profile <name> # diff:本地 vs. 平台
|
|
19
21
|
openxiangda resource publish --profile <name> # 非破坏性 upsert(不 prune)
|
|
20
22
|
openxiangda resource publish --profile <name> --prune # 删除 manifest 未声明的平台资源(谨慎)
|
|
21
23
|
openxiangda resource pull --profile <name> # 拉取平台资源回写到本地(用于初次同步)
|
|
24
|
+
openxiangda resource explain public-access --json # 查看资源目录、最小 manifest、验证命令
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
小步诊断、修复或 AI 自动发现能力时,可以用一等资源 CLI。写命令默认是 live mutation;需要让仓库继续作为来源时加 `--write-manifest`,复杂 DTO 用 `--json-file`,危险动作用 `--force`,先看请求就加 `--dry-run`。
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
openxiangda doctor --profile <name> --json
|
|
31
|
+
openxiangda design gates --topic public-access --json
|
|
32
|
+
|
|
33
|
+
openxiangda route upsert --json-file src/resources/routes/public_register.json --dry-run
|
|
34
|
+
openxiangda public-access upsert --json-file src/resources/public-access/public_register.json --write-manifest
|
|
35
|
+
openxiangda public-access ticket-create public_register --json-file ticket.json
|
|
36
|
+
openxiangda public-access session-test public_register --path /view/APP_XXX/public/register --json
|
|
37
|
+
openxiangda public-access grant-check public_register --form-code registration_form --json
|
|
38
|
+
|
|
39
|
+
openxiangda auth-config methods --json
|
|
40
|
+
openxiangda auth-config upsert --json-file src/resources/auth/default.json --write-manifest
|
|
41
|
+
|
|
42
|
+
openxiangda function upsert --json-file src/resources/functions/submit_public_registration.json
|
|
43
|
+
openxiangda function invoke submit_public_registration --body-json '{"input":{}}'
|
|
44
|
+
|
|
45
|
+
openxiangda connector upsert --json-file src/resources/connectors/sms.json
|
|
46
|
+
openxiangda connector invoke sms.sendCode --body-json '{"body":{"phone":"13800000000"}}'
|
|
47
|
+
|
|
48
|
+
openxiangda notification template-upsert --json-file src/resources/notifications/register.json
|
|
49
|
+
openxiangda notification type-upsert --json-file src/resources/notifications/register.json
|
|
50
|
+
openxiangda notification preview public_register_notice --body-json '{"variables":{"title":"测试"}}'
|
|
51
|
+
|
|
52
|
+
openxiangda data-view upsert --json-file src/resources/data-views/public_lookup.json
|
|
53
|
+
openxiangda menu update public_register --json-file src/resources/menus/public_register.json --write-manifest
|
|
54
|
+
openxiangda permission audit --json
|
|
55
|
+
openxiangda permission role-update external_visitor --json-file src/resources/roles/external_visitor.json --write-manifest
|
|
22
56
|
```
|
|
23
57
|
|
|
58
|
+
调用和测试类命令会检查 JSON envelope。HTTP 200 但 `code: "PUBLIC_GRANT_DENIED"`、`success: false` 或其他字符串错误码必须当失败,不能只看 HTTP status。
|
|
59
|
+
|
|
24
60
|
## 0. Auth — `src/resources/auth/<code>.json`
|
|
25
61
|
|
|
26
62
|
```json
|