openxiangda-cli 2.0.0-alpha.98 → 2.0.0
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/LICENSE +21 -0
- package/README.md +4 -8
- package/bin/run.js +2 -11
- package/dist/base.d.ts +30 -6
- package/dist/base.d.ts.map +1 -1
- package/dist/base.js +120 -22
- package/dist/base.js.map +1 -1
- package/dist/commands/accept.d.ts +28 -0
- package/dist/commands/accept.d.ts.map +1 -0
- package/dist/commands/accept.js +21 -0
- package/dist/commands/accept.js.map +1 -0
- package/dist/commands/admin.d.ts +32 -0
- package/dist/commands/admin.d.ts.map +1 -0
- package/dist/commands/admin.js +27 -0
- package/dist/commands/admin.js.map +1 -0
- package/dist/commands/auth.d.ts +30 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +13 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/cancel.d.ts +30 -0
- package/dist/commands/cancel.d.ts.map +1 -0
- package/dist/commands/cancel.js +12 -0
- package/dist/commands/cancel.js.map +1 -0
- package/dist/commands/check.d.ts +12 -3
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +21 -4
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/context.d.ts +27 -0
- package/dist/commands/context.d.ts.map +1 -0
- package/dist/commands/context.js +10 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/create.d.ts +20 -3
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +396 -22
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/deploy.d.ts +13 -3
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +17 -16
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.d.ts +10 -3
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +7 -4
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/docs.d.ts +30 -0
- package/dist/commands/docs.d.ts.map +1 -0
- package/dist/commands/docs.js +20 -0
- package/dist/commands/docs.js.map +1 -0
- package/dist/commands/login.d.ts +8 -3
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +1 -1
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/logs.d.ts +10 -3
- package/dist/commands/logs.d.ts.map +1 -1
- package/dist/commands/logs.js +7 -3
- package/dist/commands/logs.js.map +1 -1
- package/dist/commands/retry.d.ts +30 -0
- package/dist/commands/retry.d.ts.map +1 -0
- package/dist/commands/retry.js +12 -0
- package/dist/commands/retry.js.map +1 -0
- package/dist/commands/rollback.d.ts +11 -3
- package/dist/commands/rollback.d.ts.map +1 -1
- package/dist/commands/rollback.js +9 -3
- package/dist/commands/rollback.js.map +1 -1
- package/dist/commands/skill.d.ts +9 -3
- package/dist/commands/skill.d.ts.map +1 -1
- package/dist/commands/skill.js +9 -2
- package/dist/commands/skill.js.map +1 -1
- package/dist/commands/spec.d.ts +42 -0
- package/dist/commands/spec.d.ts.map +1 -0
- package/dist/commands/spec.js +123 -0
- package/dist/commands/spec.js.map +1 -0
- package/dist/commands/start.d.ts +28 -0
- package/dist/commands/start.d.ts.map +1 -0
- package/dist/commands/start.js +18 -0
- package/dist/commands/start.js.map +1 -0
- package/dist/commands/status.d.ts +9 -3
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +10 -3
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/stop.d.ts +28 -0
- package/dist/commands/stop.d.ts.map +1 -0
- package/dist/commands/stop.js +18 -0
- package/dist/commands/stop.js.map +1 -0
- package/dist/create-workspace.d.ts +36 -0
- package/dist/create-workspace.d.ts.map +1 -1
- package/dist/create-workspace.js +366 -8
- package/dist/create-workspace.js.map +1 -1
- package/dist/studio-events.d.ts +9 -0
- package/dist/studio-events.d.ts.map +1 -0
- package/dist/studio-events.js +30 -0
- package/dist/studio-events.js.map +1 -0
- package/package.json +15 -6
- package/template/AGENTS.md +25 -20
- package/template/README.md +31 -12
- package/template/apps/web/e2e/README.md +7 -0
- package/template/apps/web/package.json +4 -2
- package/template/apps/web/playwright.config.ts +4 -3
- package/template/apps/web/src/document.css +5 -0
- package/template/apps/web/src/main.tsx +48 -3
- package/template/apps/web/src/user-end/tailwind.css +5 -0
- package/template/apps/web/test/loopback.test.ts +8 -2
- package/template/apps/web/test/standard-user-surfaces.typecheck.tsx +27 -0
- package/template/apps/web/vite.config.ts +5 -2
- package/template/appspec/app.md +56 -0
- package/template/openxiangda.config.ts +50 -25
- package/template/package.json +4 -4
- package/template/packages/contracts/package.json +1 -1
- package/template/packages/contracts/src/generated.ts +143 -2
- package/template/apps/server/Dockerfile +0 -20
- package/template/apps/server/package.json +0 -27
- package/template/apps/server/src/app.module.ts +0 -43
- package/template/apps/server/src/main.ts +0 -5
- package/template/apps/server/test/smoke.test.ts +0 -16
- package/template/apps/server/tsconfig.json +0 -9
- package/template/apps/web/data-api-live.e2e.html +0 -15
- package/template/apps/web/e2e/client-fixture.ts +0 -5
- package/template/apps/web/e2e/data-api-live-fixture.tsx +0 -146
- package/template/apps/web/e2e/data-api-live.spec.ts +0 -138
- package/template/apps/web/e2e/field-protocol-fixture.tsx +0 -347
- package/template/apps/web/e2e/field-protocol.spec.ts +0 -186
- package/template/apps/web/e2e/resources.spec.ts +0 -285
- package/template/apps/web/e2e/workflow.spec.ts +0 -329
- package/template/apps/web/field-protocol.e2e.html +0 -15
- package/template/apps/web/scripts/check.mjs +0 -43
- package/template/apps/web/test/contracts.test.ts +0 -99
- package/template/scripts/verify-template-budget.mjs +0 -49
package/template/README.md
CHANGED
|
@@ -1,26 +1,45 @@
|
|
|
1
|
-
# OpenXiangda 2.0
|
|
1
|
+
# OpenXiangda 2.0 应用
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
本项目使用精确版本的 openxiangda 根包。默认包含 Web 和共享契约,普通 CRUD、标准审批与通知由平台执行;真实业务动作才按需启用 Nest。
|
|
4
|
+
|
|
5
|
+
## 开始
|
|
4
6
|
|
|
5
7
|
```bash
|
|
8
|
+
pnpm install --frozen-lockfile
|
|
9
|
+
pnpm openxiangda context --json
|
|
6
10
|
pnpm openxiangda dev
|
|
7
11
|
```
|
|
8
12
|
|
|
9
|
-
|
|
13
|
+
依赖已安装时无需重复安装。开发连接已绑定的远端测试数据,终端和页面显示实际环境。缺少账号或绑定时按命令错误提示处理;不要调用旧版全局 CLI。
|
|
14
|
+
|
|
15
|
+
## 阅读资料
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm openxiangda docs
|
|
19
|
+
pnpm openxiangda docs development
|
|
20
|
+
pnpm openxiangda docs application-foundation
|
|
21
|
+
pnpm openxiangda docs frontend
|
|
22
|
+
pnpm openxiangda docs data-authz
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
资料来自当前根包;长主题支持 --section <章节ID>。AGENTS.md 的平台段说明基本约定,项目自有内容保留在该段之外。AI Skill 按任务读取同源资料。
|
|
26
|
+
|
|
27
|
+
## 声明与页面
|
|
28
|
+
|
|
29
|
+
业务模型和页面选择保存在 modules,应用根配置负责装配。模型不隐含导航或写授权。字段与标准 CRUD 使用平台组件;PC 控件使用 antd,移动使用 openxiangda/mobile。应用负责最小文档重置和局部样式,不直接导入 antd-mobile 全局重置。
|
|
30
|
+
|
|
31
|
+
apps/server 仅在启用后端时初始化。后续 check/dev 保留业务源码,普通 CRUD 不增加 controller。
|
|
10
32
|
|
|
11
|
-
|
|
33
|
+
## 检查与发布
|
|
12
34
|
|
|
13
|
-
|
|
35
|
+
只检查时运行 `pnpm openxiangda check`;需要发布测试环境时直接运行 `pnpm openxiangda deploy`,它已包含检查、测试和构建。纯前端无需 Docker,启用后端时才构建官方 Dockerfile 镜像。
|
|
14
36
|
|
|
15
|
-
|
|
37
|
+
运行 status/logs 确认平台结果并完成真实角色验收。生产使用 `pnpm openxiangda deploy --environment production --from <成功测试运行ID>`;`--dry-run` 只预览。完整发布、取消、恢复和回滚说明使用 `pnpm openxiangda docs delivery`。
|
|
16
38
|
|
|
17
|
-
|
|
39
|
+
## MCP
|
|
18
40
|
|
|
19
41
|
```bash
|
|
20
|
-
pnpm openxiangda
|
|
21
|
-
pnpm openxiangda deploy
|
|
22
|
-
pnpm openxiangda status
|
|
23
|
-
pnpm openxiangda logs
|
|
42
|
+
pnpm exec openxiangda --mcp-stdio --cwd <项目绝对路径>
|
|
24
43
|
```
|
|
25
44
|
|
|
26
|
-
|
|
45
|
+
先读 workspace_context,再按需读取 docs_read 和 contract_describe;升级包后重启连接。登录、创建与长期开发进程继续使用 CLI。
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# 本应用的浏览器验收
|
|
2
|
+
|
|
3
|
+
在此目录按 AppSpec 的实际验收条件添加 `*.spec.ts`。模板没有预先通过的业务用例;尚未编写用例时,`pnpm test:e2e` 会提示未发现测试。
|
|
4
|
+
|
|
5
|
+
先启动完整连接开发环境,确认目标是测试环境,再通过 `OPENXIANGDA_E2E_BASE_URL` 指定它的 Web 地址并运行 `pnpm test:e2e`。使用平台正式提供的测试身份,分别验证允许与拒绝路径、PC 与移动页面、操作后数据及历史记录。登录信息不提交 Git。
|
|
6
|
+
|
|
7
|
+
测试页面与接口属于当前应用。模拟响应可用于局部交互回归,应明确记录范围;它不能证明平台权限或真实业务通过。工具链维护者的通用组件夹具不会复制到这里。
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"dev": "vite",
|
|
8
|
-
"check": "tsc -p tsconfig.json --noEmit
|
|
8
|
+
"check": "tsc -p tsconfig.json --noEmit",
|
|
9
9
|
"test": "tsx --test test/*.test.ts",
|
|
10
10
|
"test:e2e": "playwright test",
|
|
11
11
|
"build": "tsc -p tsconfig.json --noEmit && vite build && node scripts/verify-build.mjs"
|
|
@@ -14,17 +14,19 @@
|
|
|
14
14
|
"@ant-design/icons": "6.2.3",
|
|
15
15
|
"@refinedev/core": "5.0.12",
|
|
16
16
|
"antd": "6.4.3",
|
|
17
|
-
"openxiangda": "2.0.0
|
|
17
|
+
"openxiangda": "2.0.0",
|
|
18
18
|
"react": "19.2.8",
|
|
19
19
|
"react-dom": "19.2.8",
|
|
20
20
|
"react-router-dom": "7.8.2"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@playwright/test": "1.62.1",
|
|
24
|
+
"@tailwindcss/vite": "4.3.3",
|
|
24
25
|
"@types/node": "24.13.3",
|
|
25
26
|
"@types/react": "19.2.14",
|
|
26
27
|
"@types/react-dom": "19.2.3",
|
|
27
28
|
"@vitejs/plugin-react": "4.7.0",
|
|
29
|
+
"tailwindcss": "4.3.3",
|
|
28
30
|
"tsx": "4.23.12",
|
|
29
31
|
"typescript": "5.9.3",
|
|
30
32
|
"vite": "7.1.3"
|
|
@@ -6,7 +6,8 @@ const baseURL =
|
|
|
6
6
|
|
|
7
7
|
export default defineConfig({
|
|
8
8
|
testDir: './e2e',
|
|
9
|
-
|
|
9
|
+
// 在 e2e/ 中添加本应用的真实业务验收,测试账户与环境由测试代码明确选择。
|
|
10
|
+
workers: 1,
|
|
10
11
|
use: { baseURL, trace: 'retain-on-failure' },
|
|
11
12
|
webServer: process.env.OPENXIANGDA_E2E_BASE_URL
|
|
12
13
|
? undefined
|
|
@@ -16,7 +17,7 @@ export default defineConfig({
|
|
|
16
17
|
reuseExistingServer: false,
|
|
17
18
|
env: {
|
|
18
19
|
OPENXIANGDA_WEB_PORT: String(e2ePort),
|
|
19
|
-
OPENXIANGDA_ENVIRONMENT_KEY: '
|
|
20
|
+
OPENXIANGDA_ENVIRONMENT_KEY: 'preproduction',
|
|
20
21
|
OPENXIANGDA_DEV_PROXY:
|
|
21
22
|
process.env.OPENXIANGDA_E2E_PLATFORM_URL ||
|
|
22
23
|
'http://127.0.0.1:7001',
|
|
@@ -30,7 +31,7 @@ function resolveE2ePort() {
|
|
|
30
31
|
const port = Number(configured);
|
|
31
32
|
if (!Number.isInteger(port) || port < 1024 || port > 65535) {
|
|
32
33
|
throw new Error(
|
|
33
|
-
'OPENXIANGDA_E2E_PORT
|
|
34
|
+
'OPENXIANGDA_E2E_PORT 必须是 1024 到 65535 之间的整数'
|
|
34
35
|
);
|
|
35
36
|
}
|
|
36
37
|
return port;
|
|
@@ -1,21 +1,66 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import ReactDOM from 'react-dom/client';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
defineApplicationContributions,
|
|
5
|
+
DefaultDesktopApplicationLoginSurface,
|
|
6
|
+
DefaultMobileApplicationLoginSurface,
|
|
7
|
+
OpenXiangdaApplication,
|
|
8
|
+
type ApplicationLoginSurfaceProps,
|
|
9
|
+
} from 'openxiangda/react';
|
|
4
10
|
import {
|
|
5
11
|
appCode,
|
|
6
12
|
appName,
|
|
13
|
+
appPerspectives,
|
|
14
|
+
appRoutes,
|
|
15
|
+
routeManifest,
|
|
16
|
+
authenticationSurfaces,
|
|
17
|
+
anonymousPublicAccess,
|
|
18
|
+
adminNavigation,
|
|
19
|
+
adminAccess,
|
|
20
|
+
adminPages,
|
|
7
21
|
resourceDefinitions,
|
|
8
|
-
|
|
22
|
+
workflowDefinitions,
|
|
9
23
|
} from '../../../packages/contracts/src/generated.js';
|
|
24
|
+
import './document.css';
|
|
25
|
+
import './user-end/tailwind.css';
|
|
10
26
|
import 'openxiangda/react/styles.css';
|
|
11
27
|
|
|
28
|
+
const ApplicationHome = () => <main>OpenXiangda 应用</main>;
|
|
29
|
+
const ApplicationHomeMobile = () => <main>OpenXiangda 移动应用</main>;
|
|
30
|
+
const BrandedDesktopLogin = (props: ApplicationLoginSurfaceProps) => (
|
|
31
|
+
<DefaultDesktopApplicationLoginSurface {...props} />
|
|
32
|
+
);
|
|
33
|
+
const BrandedMobileLogin = (props: ApplicationLoginSurfaceProps) => (
|
|
34
|
+
<DefaultMobileApplicationLoginSurface {...props} />
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const applicationContributions = defineApplicationContributions(
|
|
38
|
+
{ routes: appRoutes, authenticationSurfaces },
|
|
39
|
+
{
|
|
40
|
+
pages: {
|
|
41
|
+
applicationHome: ApplicationHome,
|
|
42
|
+
applicationHomeMobile: ApplicationHomeMobile,
|
|
43
|
+
},
|
|
44
|
+
authentication: {
|
|
45
|
+
applicationLogin: BrandedDesktopLogin,
|
|
46
|
+
applicationLoginMobile: BrandedMobileLogin,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
);
|
|
12
50
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
13
51
|
<React.StrictMode>
|
|
14
52
|
<OpenXiangdaApplication
|
|
15
53
|
appCode={appCode}
|
|
16
54
|
appName={appName}
|
|
55
|
+
adminAccess={adminAccess}
|
|
56
|
+
adminNavigation={adminNavigation}
|
|
57
|
+
adminPages={adminPages}
|
|
58
|
+
routeManifest={routeManifest}
|
|
59
|
+
contributions={applicationContributions}
|
|
60
|
+
publicAccess={anonymousPublicAccess}
|
|
61
|
+
perspectives={appPerspectives}
|
|
17
62
|
resourceDefinitions={resourceDefinitions}
|
|
18
|
-
workflows={
|
|
63
|
+
workflows={workflowDefinitions}
|
|
19
64
|
/>
|
|
20
65
|
</React.StrictMode>,
|
|
21
66
|
);
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
2
|
import { spawn } from 'node:child_process';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
3
4
|
import { createServer } from 'node:net';
|
|
4
5
|
import { networkInterfaces } from 'node:os';
|
|
5
|
-
import { resolve } from 'node:path';
|
|
6
|
+
import { dirname, join, resolve } from 'node:path';
|
|
6
7
|
import test from 'node:test';
|
|
7
8
|
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
|
|
8
11
|
test('Vite is reachable on loopback and not on a LAN interface', async t => {
|
|
9
12
|
const port = await freePort();
|
|
10
13
|
const root = resolve(import.meta.dirname, '..');
|
|
11
|
-
const
|
|
14
|
+
const vitePackagePath = require.resolve('vite/package.json');
|
|
15
|
+
const vitePackage = require(vitePackagePath) as { bin: { vite: string } };
|
|
16
|
+
const viteBin = join(dirname(vitePackagePath), vitePackage.bin.vite);
|
|
17
|
+
const child = spawn(process.execPath, [viteBin, '--port', String(port), '--strictPort'], {
|
|
12
18
|
cwd: root,
|
|
13
19
|
stdio: 'ignore',
|
|
14
20
|
env: {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineApplicationContributions,
|
|
3
|
+
type StandardApplicationTodoCenterProps,
|
|
4
|
+
type StandardUserPageFrameProps,
|
|
5
|
+
type StandardUserSurfaceContributions,
|
|
6
|
+
} from 'openxiangda/react';
|
|
7
|
+
|
|
8
|
+
function Frame({ children }: StandardUserPageFrameProps) {
|
|
9
|
+
return <>{children}</>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function Todo({ items }: StandardApplicationTodoCenterProps) {
|
|
13
|
+
return <>{items.length}</>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const standardUserSurfaces = {
|
|
17
|
+
frame: { desktop: Frame, mobile: Frame },
|
|
18
|
+
applicationTodoCenter: { desktop: Todo, mobile: Todo },
|
|
19
|
+
} satisfies StandardUserSurfaceContributions;
|
|
20
|
+
|
|
21
|
+
const contributions = defineApplicationContributions({}, {
|
|
22
|
+
pages: {},
|
|
23
|
+
standardUserSurfaces,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const exportedFrame = contributions.standardUserSurfaces?.frame.desktop;
|
|
27
|
+
void exportedFrame;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import react from '@vitejs/plugin-react';
|
|
2
|
+
import tailwindcss from '@tailwindcss/vite';
|
|
2
3
|
import { defineConfig } from 'vite';
|
|
3
4
|
|
|
4
5
|
const connectedProxy =
|
|
@@ -6,8 +7,11 @@ const connectedProxy =
|
|
|
6
7
|
const port = Number(process.env.OPENXIANGDA_WEB_PORT || 5173);
|
|
7
8
|
|
|
8
9
|
export default defineConfig({
|
|
9
|
-
plugins: [react()],
|
|
10
|
+
plugins: [react(), tailwindcss()],
|
|
10
11
|
base: './',
|
|
12
|
+
optimizeDeps: {
|
|
13
|
+
entries: ['index.html'],
|
|
14
|
+
},
|
|
11
15
|
server: {
|
|
12
16
|
host: '127.0.0.1',
|
|
13
17
|
port,
|
|
@@ -24,7 +28,6 @@ export default defineConfig({
|
|
|
24
28
|
manualChunks: {
|
|
25
29
|
react: ['react', 'react-dom', 'react-router-dom'],
|
|
26
30
|
refine: ['@refinedev/core'],
|
|
27
|
-
antd: ['antd', '@ant-design/icons'],
|
|
28
31
|
},
|
|
29
32
|
},
|
|
30
33
|
},
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema: openxiangda.appspec/app/v1
|
|
3
|
+
app: "openxiangda-application"
|
|
4
|
+
title: "OpenXiangda 应用"
|
|
5
|
+
status: active
|
|
6
|
+
---
|
|
7
|
+
# OpenXiangda 应用
|
|
8
|
+
|
|
9
|
+
本文件是当前有效设计的总纲与目录。新应用先完成产品发现、详细设计与实际确认,形成评审基线后再制定实施计划;按 docs product-design 在 AppSpec 中建立相关材料。正式变更关联 ChangeSpec;测试发布前核对设计与验收计划,生产晋级前核对实际验收。资源、字段、权限和动作的实现以
|
|
10
|
+
`openxiangda.config.ts` 与实时编译合同为准。
|
|
11
|
+
|
|
12
|
+
## 业务目标
|
|
13
|
+
|
|
14
|
+
<!-- 用业务语言说明应用解决的问题和可观察结果。 -->
|
|
15
|
+
|
|
16
|
+
## 角色
|
|
17
|
+
|
|
18
|
+
<!-- 只记录业务角色及其目标;稳定 role code 在能力规格中引用。 -->
|
|
19
|
+
|
|
20
|
+
## 范围
|
|
21
|
+
|
|
22
|
+
### 包含
|
|
23
|
+
|
|
24
|
+
### 不包含
|
|
25
|
+
|
|
26
|
+
## 设计资料目录
|
|
27
|
+
|
|
28
|
+
<!-- 引用产品、旅程、页面、视觉、权限、架构与评审的稳定 ID;详细规则各有唯一归属。 -->
|
|
29
|
+
|
|
30
|
+
## 能力目录
|
|
31
|
+
|
|
32
|
+
<!-- 复杂业务再在 capabilities/ 下增加 CAP-*;简单应用的稳定规则直接写在本文件。 -->
|
|
33
|
+
|
|
34
|
+
## 术语
|
|
35
|
+
|
|
36
|
+
## 跨能力约束
|
|
37
|
+
|
|
38
|
+
## 架构与数据关系
|
|
39
|
+
|
|
40
|
+
<!-- 写明领域边界、核心模型关系、平台能力与自定义动作的归属、状态转换与不变量。 -->
|
|
41
|
+
|
|
42
|
+
## 业务任务与页面
|
|
43
|
+
|
|
44
|
+
<!-- 按用户任务规划管理端、PC、移动入口与数据关系;辅助表不必有页面。 -->
|
|
45
|
+
|
|
46
|
+
## 权限矩阵与确认
|
|
47
|
+
|
|
48
|
+
<!-- 记录角色、页面、操作、行范围、字段和多角色组合;写明需求依据与未明确的边界。 -->
|
|
49
|
+
|
|
50
|
+
## 性能与容量预算
|
|
51
|
+
|
|
52
|
+
<!-- 记录数据量及增长、并发、分页与索引、请求次数、延迟目标和测量口径;区分估算、目标与实测。 -->
|
|
53
|
+
|
|
54
|
+
## 未确认问题
|
|
55
|
+
|
|
56
|
+
- 无。
|
|
@@ -1,30 +1,55 @@
|
|
|
1
|
-
import { defineOpenXiangdaApp } from 'openxiangda/config';
|
|
1
|
+
import { defineAdminNavigation, defineOpenXiangdaApp } from 'openxiangda/config';
|
|
2
2
|
|
|
3
|
-
/**
|
|
3
|
+
/** Start with business modules; select standard CRUD only for the models that need it. */
|
|
4
4
|
export default defineOpenXiangdaApp({
|
|
5
|
-
schemaVersion: 3,
|
|
6
5
|
app: { code: 'openxiangda-application', name: 'OpenXiangda 应用' },
|
|
7
|
-
frontend: {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
6
|
+
frontend: {
|
|
7
|
+
root: 'apps/web',
|
|
8
|
+
devicePolicy: {
|
|
9
|
+
kind: 'viewport-family',
|
|
10
|
+
mobileMaxWidthPx: 900,
|
|
11
|
+
desktopMinWidthPx: 901,
|
|
12
|
+
},
|
|
13
|
+
routes: [
|
|
14
|
+
{
|
|
15
|
+
code: 'application-home',
|
|
16
|
+
path: '/home',
|
|
17
|
+
label: '应用首页',
|
|
18
|
+
surface: 'user',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
code: 'application-home-mobile',
|
|
22
|
+
path: '/m/home',
|
|
23
|
+
label: '移动首页',
|
|
24
|
+
surface: 'user',
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
authentication: {
|
|
28
|
+
accountMode: 'existing-platform-users-only',
|
|
29
|
+
registration: { mode: 'reject' },
|
|
30
|
+
methods: [
|
|
31
|
+
{
|
|
32
|
+
code: 'password',
|
|
33
|
+
type: 'password',
|
|
34
|
+
label: '账号密码登录',
|
|
35
|
+
presentation: 'primary',
|
|
36
|
+
required: true,
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
surfaces: {
|
|
40
|
+
desktop: {
|
|
41
|
+
routeCode: 'application-login',
|
|
42
|
+
path: '/login',
|
|
43
|
+
defaultRouteCode: 'application-home',
|
|
44
|
+
},
|
|
45
|
+
mobile: {
|
|
46
|
+
routeCode: 'application-login-mobile',
|
|
47
|
+
path: '/m/login',
|
|
48
|
+
defaultRouteCode: 'application-home-mobile',
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
admin: { navigation: defineAdminNavigation([]) },
|
|
17
53
|
},
|
|
18
|
-
|
|
19
|
-
authz: {
|
|
20
|
-
capabilities: [],
|
|
21
|
-
roles: [],
|
|
22
|
-
scopeDimensions: [],
|
|
23
|
-
scopeSources: [],
|
|
24
|
-
dataPolicies: [],
|
|
25
|
-
authorizationTransitions: [],
|
|
26
|
-
},
|
|
27
|
-
data: { resources: [] },
|
|
28
|
-
events: { schemas: [], subscriptions: [], timers: [], dateTriggers: [] },
|
|
29
|
-
workflows: { definitions: [], bindings: [], activations: [], providers: [], editableParameters: [] },
|
|
54
|
+
modules: [],
|
|
30
55
|
});
|
package/template/package.json
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"name": "openxiangda-application",
|
|
3
3
|
"version": "0.1.0",
|
|
4
4
|
"private": true,
|
|
5
|
+
"type": "module",
|
|
5
6
|
"packageManager": "pnpm@10.15.1",
|
|
6
7
|
"engines": {
|
|
7
8
|
"node": ">=22.12"
|
|
@@ -11,18 +12,17 @@
|
|
|
11
12
|
"prepare": "pnpm workspace:build",
|
|
12
13
|
"workspace:build": "pnpm --filter @app/contracts build",
|
|
13
14
|
"predev": "pnpm workspace:build",
|
|
14
|
-
"dev": "
|
|
15
|
+
"dev": "openxiangda dev",
|
|
15
16
|
"dev:web": "pnpm --filter @app/web dev",
|
|
16
|
-
"dev:server": "pnpm --filter @app/server dev",
|
|
17
17
|
"precheck": "pnpm workspace:build",
|
|
18
|
-
"check": "pnpm --recursive check
|
|
18
|
+
"check": "pnpm --recursive check",
|
|
19
19
|
"pretest": "pnpm workspace:build",
|
|
20
20
|
"test": "pnpm --recursive test",
|
|
21
21
|
"test:e2e": "pnpm --filter @app/web test:e2e",
|
|
22
22
|
"build": "pnpm --recursive build"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"openxiangda": "2.0.0
|
|
25
|
+
"openxiangda": "2.0.0",
|
|
26
26
|
"typescript": "5.9.3"
|
|
27
27
|
}
|
|
28
28
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// Generated by OpenXiangda 2.0 Native compiler. Do not edit by hand.
|
|
2
|
-
export const compilerContractVersion = "native-
|
|
2
|
+
export const compilerContractVersion = "native-4" as const;
|
|
3
3
|
export const appCode = "openxiangda-application" as const;
|
|
4
4
|
export const appName = "OpenXiangda 应用" as const;
|
|
5
|
+
export const appPerspectives = [] as const;
|
|
5
6
|
export const resourceCodes = [] as const;
|
|
6
7
|
export const resourceSurfaces = {} as const;
|
|
7
8
|
export const resourceDefinitions = {} as const;
|
|
@@ -15,7 +16,132 @@ export const capabilities = [
|
|
|
15
16
|
"app:super-admin:manage"
|
|
16
17
|
] as const;
|
|
17
18
|
export const appOperations = {} as const;
|
|
18
|
-
export const appRoutes = {
|
|
19
|
+
export const appRoutes = {
|
|
20
|
+
applicationHome: {
|
|
21
|
+
"code": "application-home",
|
|
22
|
+
"path": "/home",
|
|
23
|
+
"label": "应用首页",
|
|
24
|
+
"surface": "user",
|
|
25
|
+
"tabPersistence": "session",
|
|
26
|
+
"keepAlive": "none"
|
|
27
|
+
},
|
|
28
|
+
applicationHomeMobile: {
|
|
29
|
+
"code": "application-home-mobile",
|
|
30
|
+
"path": "/m/home",
|
|
31
|
+
"label": "移动首页",
|
|
32
|
+
"surface": "user",
|
|
33
|
+
"tabPersistence": "session",
|
|
34
|
+
"keepAlive": "none"
|
|
35
|
+
}
|
|
36
|
+
} as const;
|
|
37
|
+
export const authenticationMethods = [
|
|
38
|
+
{
|
|
39
|
+
"code": "password",
|
|
40
|
+
"type": "password",
|
|
41
|
+
"label": "账号密码登录",
|
|
42
|
+
"presentation": "primary",
|
|
43
|
+
"required": true
|
|
44
|
+
}
|
|
45
|
+
] as const;
|
|
46
|
+
export const authenticationSurfaces = {
|
|
47
|
+
applicationLogin: {
|
|
48
|
+
"device": "desktop",
|
|
49
|
+
"routeCode": "application-login",
|
|
50
|
+
"path": "/login",
|
|
51
|
+
"defaultRouteCode": "application-home"
|
|
52
|
+
},
|
|
53
|
+
applicationLoginMobile: {
|
|
54
|
+
"device": "mobile",
|
|
55
|
+
"routeCode": "application-login-mobile",
|
|
56
|
+
"path": "/m/login",
|
|
57
|
+
"defaultRouteCode": "application-home-mobile"
|
|
58
|
+
}
|
|
59
|
+
} as const;
|
|
60
|
+
export const applicationAuthentication = {
|
|
61
|
+
"accountMode": "existing-platform-users-only",
|
|
62
|
+
"registration": {
|
|
63
|
+
"mode": "reject"
|
|
64
|
+
},
|
|
65
|
+
"methods": [
|
|
66
|
+
{
|
|
67
|
+
"code": "password",
|
|
68
|
+
"type": "password",
|
|
69
|
+
"label": "账号密码登录",
|
|
70
|
+
"presentation": "primary",
|
|
71
|
+
"required": true
|
|
72
|
+
}
|
|
73
|
+
],
|
|
74
|
+
"surfaces": {
|
|
75
|
+
"desktop": {
|
|
76
|
+
"routeCode": "application-login",
|
|
77
|
+
"path": "/login",
|
|
78
|
+
"defaultRouteCode": "application-home"
|
|
79
|
+
},
|
|
80
|
+
"mobile": {
|
|
81
|
+
"routeCode": "application-login-mobile",
|
|
82
|
+
"path": "/m/login",
|
|
83
|
+
"defaultRouteCode": "application-home-mobile"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
} as const;
|
|
87
|
+
export const anonymousPublicAccess = undefined;
|
|
88
|
+
export const platformAuthManifest = {
|
|
89
|
+
"schemaVersion": "openxiangda.platform-auth-manifest/v2",
|
|
90
|
+
"appCode": "openxiangda-application",
|
|
91
|
+
"protectedUserRouteCount": 2,
|
|
92
|
+
"methods": [
|
|
93
|
+
{
|
|
94
|
+
"code": "password",
|
|
95
|
+
"type": "password",
|
|
96
|
+
"label": "账号密码登录",
|
|
97
|
+
"presentation": "primary",
|
|
98
|
+
"required": true
|
|
99
|
+
}
|
|
100
|
+
],
|
|
101
|
+
"surfaces": [
|
|
102
|
+
{
|
|
103
|
+
"device": "desktop",
|
|
104
|
+
"routeCode": "application-login",
|
|
105
|
+
"path": "/login",
|
|
106
|
+
"defaultRouteCode": "application-home"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"device": "mobile",
|
|
110
|
+
"routeCode": "application-login-mobile",
|
|
111
|
+
"path": "/m/login",
|
|
112
|
+
"defaultRouteCode": "application-home-mobile"
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
} as const;
|
|
116
|
+
export const adminAccess = undefined;
|
|
117
|
+
export const routeManifest = {
|
|
118
|
+
"schemaVersion": "openxiangda.application-route-manifest/v3",
|
|
119
|
+
"appCode": "openxiangda-application",
|
|
120
|
+
"devicePolicy": {
|
|
121
|
+
"kind": "viewport-family",
|
|
122
|
+
"mobileMaxWidthPx": 900,
|
|
123
|
+
"desktopMinWidthPx": 901
|
|
124
|
+
},
|
|
125
|
+
"rootEntry": {
|
|
126
|
+
"code": "application-home",
|
|
127
|
+
"desktop": "/home",
|
|
128
|
+
"mobile": "/m/home"
|
|
129
|
+
},
|
|
130
|
+
"authentication": {
|
|
131
|
+
"desktop": {
|
|
132
|
+
"routeCode": "application-login",
|
|
133
|
+
"path": "/login"
|
|
134
|
+
},
|
|
135
|
+
"mobile": {
|
|
136
|
+
"routeCode": "application-login-mobile",
|
|
137
|
+
"path": "/m/login"
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
"routes": [],
|
|
141
|
+
"digest": "2c01252ac4415de49a2fe58bf21df94a77f8c235d739ec7a6af583fc695ba0cf"
|
|
142
|
+
} as const;
|
|
143
|
+
export const adminPages = [] as const;
|
|
144
|
+
export const adminNavigation = [] as const;
|
|
19
145
|
export const eventTypes = [] as const;
|
|
20
146
|
export const eventSubscriptionCodes = [] as const;
|
|
21
147
|
export const eventHandlerManifest = {
|
|
@@ -26,15 +152,30 @@ export const eventHandlerManifest = {
|
|
|
26
152
|
export const eventHandlers = {} as const;
|
|
27
153
|
export const eventSchemas = [] as const;
|
|
28
154
|
export const workflowCodes = [] as const;
|
|
155
|
+
export const workflowDefinitions = [] as const;
|
|
156
|
+
export const notificationTemplateCodes = {
|
|
157
|
+
applicationInformational: "application.informational.standard",
|
|
158
|
+
} as const;
|
|
29
159
|
|
|
30
160
|
export type ResourceCode = (typeof resourceCodes)[number];
|
|
31
161
|
export type Capability = (typeof capabilities)[number];
|
|
32
162
|
export type AppOperation = (typeof appOperations)[keyof typeof appOperations];
|
|
33
163
|
export type AppRoute = (typeof appRoutes)[keyof typeof appRoutes];
|
|
164
|
+
export type AppRouteCode = AppRoute["code"];
|
|
165
|
+
export type ApplicationAuthenticationMethod = (typeof authenticationMethods)[number];
|
|
166
|
+
export type ApplicationAuthenticationSurface = (typeof authenticationSurfaces)[keyof typeof authenticationSurfaces];
|
|
167
|
+
export type ApplicationAuthenticationSurfaceCode = ApplicationAuthenticationSurface["routeCode"];
|
|
168
|
+
export type AnonymousPublicAccess = typeof anonymousPublicAccess;
|
|
169
|
+
export type AdminAccess = typeof adminAccess;
|
|
170
|
+
export type RouteManifest = typeof routeManifest;
|
|
171
|
+
export type AdminPage = (typeof adminPages)[number];
|
|
172
|
+
export type AdminNavigationGroup = (typeof adminNavigation)[number];
|
|
173
|
+
export type AppPerspective = (typeof appPerspectives)[number];
|
|
34
174
|
export type EventType = (typeof eventTypes)[number];
|
|
35
175
|
export type EventSubscriptionCode = (typeof eventSubscriptionCodes)[number];
|
|
36
176
|
export type EventTypesForSubscription<TCode extends EventSubscriptionCode> = never;
|
|
37
177
|
export type WorkflowCode = (typeof workflowCodes)[number];
|
|
178
|
+
export type NotificationTemplateCode = (typeof notificationTemplateCodes)[keyof typeof notificationTemplateCodes];
|
|
38
179
|
|
|
39
180
|
export interface OpenXiangdaCloudEvent<TType extends EventType = EventType, TData extends Record<string, unknown> = Record<string, unknown>> {
|
|
40
181
|
specversion: "1.0";
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
FROM node:22-alpine AS build
|
|
2
|
-
WORKDIR /workspace
|
|
3
|
-
RUN corepack enable
|
|
4
|
-
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml tsconfig.base.json ./
|
|
5
|
-
COPY apps/server/package.json apps/server/package.json
|
|
6
|
-
COPY packages/contracts/package.json packages/contracts/package.json
|
|
7
|
-
RUN node -e "const fs=require('node:fs'); const p=JSON.parse(fs.readFileSync('package.json','utf8')); delete p.devDependencies; fs.writeFileSync('package.json', JSON.stringify(p));"
|
|
8
|
-
RUN pnpm --filter @app/server --filter @app/contracts install --no-frozen-lockfile --ignore-scripts
|
|
9
|
-
COPY apps/server apps/server
|
|
10
|
-
COPY packages/contracts packages/contracts
|
|
11
|
-
RUN pnpm --filter @app/server... build
|
|
12
|
-
RUN pnpm --filter @app/server deploy --prod --legacy --ignore-scripts /output
|
|
13
|
-
|
|
14
|
-
FROM node:22-alpine AS runtime
|
|
15
|
-
ENV NODE_ENV=production
|
|
16
|
-
WORKDIR /app
|
|
17
|
-
COPY --from=build --chown=1000:1000 /output /app
|
|
18
|
-
USER 1000:1000
|
|
19
|
-
EXPOSE 3000
|
|
20
|
-
CMD ["node", "dist/main.js"]
|