@oj-module/cli 0.2.53 → 0.2.56
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/devkit/SKILL.md +4 -4
- package/devkit/manual.md +79 -65
- package/package.json +2 -2
- package/shell-dist/assets/{index-e_Mp2mb5.js → index-CImwskAz.js} +24 -11
- package/shell-dist/assets/runtime.js +7 -4
- package/shell-dist/index.html +3 -3
- package/shell-dist/versions.json +3 -3
- package/src/__tests__/layout.test.ts +30 -1
- package/src/__tests__/templates-schema-columns.test.ts +99 -0
- package/src/__tests__/templates-tenant-scope.test.ts +3 -2
- package/src/alias-paths.ts +11 -12
- package/src/builtin.ts +10 -3
- package/src/config.ts +9 -0
- package/src/contract/__tests__/client-path.test.ts +30 -0
- package/src/contract/__tests__/emit-client.test.ts +20 -2
- package/src/contract/__tests__/handler-scan.test.ts +23 -0
- package/src/contract/__tests__/market-client.integration.test.ts +2 -2
- package/src/contract/__tests__/run-api.test.ts +2 -2
- package/src/contract/check.ts +11 -7
- package/src/contract/client-path.ts +30 -0
- package/src/contract/emit-client.ts +27 -6
- package/src/contract/handler-scan.ts +40 -3
- package/src/contract/run.ts +15 -5
- package/src/init.ts +29 -3
- package/src/layout.ts +72 -16
- package/src/lifecycle/__tests__/manifest-contract.test.ts +2 -2
- package/src/market/__tests__/bootstrap.test.ts +1 -1
- package/src/site.ts +1 -1
- package/templates/README.md +49 -43
- package/templates/init.include.yaml +31 -19
- package/templates/openapi.json +1488 -120
- package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/users.ts +27 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/oplog.ts +4 -2
- package/templates/src/{_platform/api → app/api/_platform}/contacts/api.ts +29 -1
- package/templates/src/{_platform/api → app/api/_platform}/contacts/detail/api.ts +25 -1
- package/templates/src/app/api/_platform/migrations/0017__users_employee_no.mysql.sql +7 -0
- package/templates/src/app/api/_platform/migrations/0017__users_employee_no.pg.sql +4 -0
- package/templates/src/app/api/_platform/migrations/0017__users_employee_no.sql +8 -0
- package/templates/src/{_platform/api → app/api/_platform}/schema.yaml +5 -0
- package/templates/src/{_platform/api → app/api/_platform}/solutions/api.ts +60 -1
- package/templates/src/{auth/api → app/api/auth}/_shared/wecom/replay.ts +9 -3
- package/templates/src/{auth/api → app/api/auth}/_shared/wecom/tenant-admin.ts +18 -27
- package/templates/src/{auth/api → app/api/auth}/api.ts +59 -0
- package/templates/src/{auth/api → app/api/auth}/ldap-login/api.ts +37 -1
- package/templates/src/{auth/api → app/api/auth}/wecom/api.ts +23 -0
- package/templates/src/{notification/api → app/api/notification}/api.ts +17 -0
- package/templates/src/{notification/api → app/api/notification}/del/api.ts +2 -0
- package/templates/src/{system/api → app/api/system}/oplog/api.ts +5 -3
- package/templates/src/{system/api → app/api/system}/roles/api.ts +3 -0
- package/templates/src/{system/api → app/api/system}/roles/bindings/api.ts +3 -0
- package/templates/src/{system/api → app/api/system}/settings/api.ts +3 -0
- package/templates/src/{system/api → app/api/system}/users/forbidden/api.ts +3 -0
- package/templates/src/{system/api → app/api/system}/users/password/api.ts +3 -0
- package/templates/src/{system/api → app/api/system}/users/roles/api.ts +3 -0
- package/templates/src/app/api/web/get-async-routes/api.ts +23 -0
- package/templates/src/app/api/web/hello/api.ts +22 -0
- package/templates/src/{web/api → app/api/web}/user-info/api.ts +21 -1
- package/templates/src/{_platform/web → app/web/_platform}/__client/api.schemas.ts +99 -35
- package/templates/src/{_platform/web/__client/api.ts → app/web/_platform/__client/index.ts} +108 -61
- package/templates/src/{auth/web → app/web/auth}/__client/api.schemas.ts +133 -13
- package/templates/src/{auth/web/__client/api.ts → app/web/auth/__client/index.ts} +134 -33
- package/templates/src/app/web/entry.ts +77 -0
- package/templates/src/{home/web/__client/api.ts → app/web/home/__client/index.ts} +5 -2
- package/templates/src/app/web/home/routes.ts +41 -0
- package/templates/src/app/web/login/routes.ts +71 -0
- package/templates/src/{notification/web → app/web/notification}/__client/api.schemas.ts +14 -0
- package/templates/src/{notification/web/__client/api.ts → app/web/notification/__client/index.ts} +67 -20
- package/templates/src/{notification/web → app/web/notification}/pages/center/index.tsx +1 -1
- package/templates/src/app/web/notification/routes.ts +70 -0
- package/templates/src/{personal-center/web/__client/api.ts → app/web/personal-center/__client/index.ts} +5 -2
- package/templates/src/{personal-center/web → app/web/personal-center}/pages/my-profile/index.tsx +1 -1
- package/templates/src/app/web/personal-center/routes.ts +49 -0
- package/templates/src/{system/web → app/web/system}/__client/api.schemas.ts +29 -22
- package/templates/src/{system/web/__client/api.ts → app/web/system/__client/index.ts} +103 -58
- package/templates/src/{system/web → app/web/system}/locales/en-US.json +2 -3
- package/templates/src/{system/web → app/web/system}/locales/zh-CN.json +2 -3
- package/templates/src/{system/web → app/web/system}/pages/departments/index.tsx +2 -2
- package/templates/src/{system/web → app/web/system}/pages/login-methods/index.tsx +1 -1
- package/templates/src/{system/web → app/web/system}/pages/oplog/index.tsx +44 -19
- package/templates/src/{system/web → app/web/system}/pages/roles/index.tsx +5 -3
- package/templates/src/app/web/system/pages/roles/visible-roles.ts +14 -0
- package/templates/src/{system/web → app/web/system}/pages/settings/index.tsx +2 -2
- package/templates/src/app/web/system/pages/users/can-manage.ts +21 -0
- package/templates/src/{system/web → app/web/system}/pages/users/index.tsx +23 -15
- package/templates/src/{system/web → app/web/system}/pages/users/roles.tsx +1 -1
- package/templates/src/{system/web → app/web/system}/pages/wecom/index.tsx +12 -3
- package/templates/src/{system/web/entry.ts → app/web/system/routes.ts} +25 -36
- package/templates/src/app/web/web/__client/api.schemas.ts +36 -0
- package/templates/src/{web/web/__client/api.ts → app/web/web/__client/index.ts} +38 -11
- package/templates/src/oj-market/web/api/defaultClient.ts +1 -1
- package/templates/tests/auth-ldap.spec.ts +2 -2
- package/templates/tests/auth-session.spec.ts +3 -3
- package/templates/tests/auth-throttle.spec.ts +1 -1
- package/templates/tests/fixture.ts +6 -4
- package/templates/tests/guard.spec.ts +1 -1
- package/templates/tests/lifecycle/atomic-state.spec.ts +3 -3
- package/templates/tests/lifecycle/bootstrap.spec.ts +3 -3
- package/templates/tests/lifecycle/deploy-smoke.spec.ts +8 -8
- package/templates/tests/lifecycle/installer.spec.ts +10 -10
- package/templates/tests/lifecycle/market-api.spec.ts +14 -14
- package/templates/tests/lifecycle/migrate.spec.ts +6 -6
- package/templates/tests/lifecycle/preflight.spec.ts +3 -3
- package/templates/tests/lifecycle/register.spec.ts +1 -1
- package/templates/tests/lifecycle/semver.spec.ts +1 -1
- package/templates/tests/lifecycle/sha384.spec.ts +1 -1
- package/templates/tests/lifecycle/solutions-api.spec.ts +1 -1
- package/templates/tests/lifecycle/source-seq.spec.ts +1 -1
- package/templates/tests/lifecycle/steps-fs.spec.ts +9 -9
- package/templates/tests/lifecycle/text-edit.spec.ts +1 -1
- package/templates/tests/lifecycle/tgz-guard.spec.ts +2 -2
- package/templates/tests/lifecycle/trust.spec.ts +5 -5
- package/templates/tests/lifecycle/uninstall.spec.ts +7 -7
- package/templates/tests/lifecycle/verify-checks.spec.ts +5 -5
- package/templates/tests/lifecycle/verify-report.spec.ts +4 -4
- package/templates/tests/org-departments.spec.ts +10 -6
- package/templates/tests/org-read.spec.ts +7 -7
- package/templates/tests/query-utils.spec.ts +1 -1
- package/templates/tests/rbac-audit.spec.ts +1 -1
- package/templates/tests/rbac-contacts.spec.ts +2 -2
- package/templates/tests/rbac-guard.spec.ts +1 -1
- package/templates/tests/rbac-roles.spec.ts +10 -2
- package/templates/tests/rbac-seed.spec.ts +1 -1
- package/templates/tests/rbac-settings.spec.ts +77 -7
- package/templates/tests/rbac-solution-perms.spec.ts +1 -1
- package/templates/tests/rbac-tables.spec.ts +29 -0
- package/templates/tests/rbac-users.spec.ts +55 -3
- package/templates/tests/roles-page.spec.ts +40 -0
- package/templates/tests/seed-role-accounts.spec.ts +1 -1
- package/templates/tests/tenant-access-cache.spec.ts +1 -1
- package/templates/tests/tenant-forgery.spec.ts +5 -5
- package/templates/tests/tenant-isolation.spec.ts +5 -4
- package/templates/tests/username-namespace.spec.ts +3 -3
- package/templates/tests/users-page.spec.ts +39 -0
- package/templates/tests/wecom-admin.spec.ts +6 -6
- package/templates/tests/wecom-client-timeout.spec.ts +1 -1
- package/templates/tests/wecom-connector.spec.ts +9 -9
- package/templates/tests/wecom-corp.spec.ts +9 -9
- package/templates/tests/wecom-crypt.spec.ts +1 -1
- package/templates/tests/wecom-endpoints.spec.ts +2 -2
- package/templates/tests/wecom-landing.spec.ts +1 -1
- package/templates/tests/wecom-org-sync.spec.ts +7 -7
- package/templates/tests/wecom-sync.spec.ts +3 -3
- package/templates/tsconfig.paths.json +11 -11
- package/templates/vitest.config.ts +2 -2
- package/templates/web.config.ts +24 -15
- package/vendor/host-versions.json +3 -3
- package/templates/src/home/web/entry.ts +0 -49
- package/templates/src/login/web/entry.ts +0 -79
- package/templates/src/notification/web/entry.ts +0 -84
- package/templates/src/personal-center/web/entry.ts +0 -60
- package/templates/src/web/api/get-async-routes/api.ts +0 -8
- package/templates/src/web/api/hello/api.ts +0 -10
- package/templates/src/web/web/__client/api.schemas.ts +0 -8
- package/templates/src/{_platform/api → app/api/_platform}/README.md +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/atomic.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/history.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/installer.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/limits.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/lock.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/semver.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/sha384.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/source-seq.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/state.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/deploy-web.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/download.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/extract.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/migrate.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/preflight.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/register.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/reload.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/smoke.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/verify.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/tasks.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/text-edit.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/tgz-guard.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/trust.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/types.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/uninstall.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/verify-checks.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/verify-report.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/respond.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/contacts.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/org-read.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/org.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/roles.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/settings.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/solution-perms.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/access.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/audit.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/guard.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/query.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/rate.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/site-facade.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/fixtures/l1-test-user.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/manifest.yaml +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/market/api.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/market/bootstrap.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/market/index-source.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0001__create_users.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0001__create_users.pg.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0001__create_users.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0002__add_users_avatar_base64.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0002__add_users_avatar_base64.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0003__users_source_display_name.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0003__users_source_display_name.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0004__sys_rbac.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0004__sys_rbac.pg.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0004__sys_rbac.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0005__users_must_change.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0005__users_must_change.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0006__users_is_forbidden.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0006__users_is_forbidden.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0007__users_email.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0008__sys_refresh_token.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0008__sys_refresh_token.pg.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0008__sys_refresh_token.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0009__sys_throttle.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0009__sys_throttle.pg.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0009__sys_throttle.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0010__sys_solution_route.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0010__sys_solution_route.pg.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0010__sys_solution_route.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0011__sys_setting_group_label.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0011__sys_setting_group_label.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0012__market_history.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0012__market_history.pg.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0012__market_history.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0013__users_profile.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0014__reg_source_seq.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0014__reg_source_seq.pg.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0014__reg_source_seq.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0015__tenant.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0015__tenant.pg.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0015__tenant.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0016__org.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0016__org.pg.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0016__org.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/org/departments/api.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/org/is-member/api.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/org/members/api.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/org/picker/api.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/org/resolve/api.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/org/tree/api.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/org/user/api.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/seed.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/seed.pg.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/seed.sql +0 -0
- package/templates/src/{auth/api → app/api/auth}/_shared/ldap-login.ts +0 -0
- package/templates/src/{auth/api → app/api/auth}/_shared/ldap.ts +0 -0
- package/templates/src/{auth/api → app/api/auth}/_shared/login-throttle.ts +0 -0
- package/templates/src/{auth/api → app/api/auth}/_shared/session.ts +0 -0
- package/templates/src/{auth/api → app/api/auth}/_shared/wecom/admin.ts +1 -1
- package/templates/src/{auth/api → app/api/auth}/_shared/wecom/client.ts +0 -0
- package/templates/src/{auth/api → app/api/auth}/_shared/wecom/config.ts +0 -0
- package/templates/src/{auth/api → app/api/auth}/_shared/wecom/contacts.ts +0 -0
- package/templates/src/{auth/api → app/api/auth}/_shared/wecom/crypt.ts +0 -0
- package/templates/src/{auth/api → app/api/auth}/_shared/wecom/events.ts +1 -1
- package/templates/src/{auth/api → app/api/auth}/_shared/wecom/login.ts +0 -0
- package/templates/src/{auth/api → app/api/auth}/_shared/wecom/payload.ts +0 -0
- package/templates/src/{auth/api → app/api/auth}/_shared/wecom/store.ts +0 -0
- package/templates/src/{auth/api → app/api/auth}/_shared/wecom/sync.ts +0 -0
- package/templates/src/{auth/api → app/api/auth}/manifest.yaml +0 -0
- package/templates/src/{auth/api → app/api/auth}/migrations/0001__wecom.mysql.sql +0 -0
- package/templates/src/{auth/api → app/api/auth}/migrations/0001__wecom.pg.sql +0 -0
- package/templates/src/{auth/api → app/api/auth}/migrations/0001__wecom.sql +0 -0
- package/templates/src/{auth/api → app/api/auth}/migrations/0002__org_sync.mysql.sql +0 -0
- package/templates/src/{auth/api → app/api/auth}/migrations/0002__org_sync.pg.sql +0 -0
- package/templates/src/{auth/api → app/api/auth}/migrations/0002__org_sync.sql +0 -0
- package/templates/src/{auth/api → app/api/auth}/migrations/0003__credential_kind.mysql.sql +0 -0
- package/templates/src/{auth/api → app/api/auth}/migrations/0003__credential_kind.pg.sql +0 -0
- package/templates/src/{auth/api → app/api/auth}/migrations/0003__credential_kind.sql +0 -0
- package/templates/src/{auth/api → app/api/auth}/schema.yaml +0 -0
- package/templates/src/{auth/api → app/api/auth}/wecom/admin/api.ts +0 -0
- package/templates/src/{auth/api → app/api/auth}/wecom/admin/tenant-admin/api.ts +1 -1
- package/templates/src/{auth/api → app/api/auth}/wecom/admin/tenant-admin/password/api.ts +1 -1
- /package/templates/src/{auth/api → app/api/auth}/wecom/sync/api.ts +0 -0
- /package/templates/src/{home/api → app/api/home}/line/api.ts +0 -0
- /package/templates/src/{home/api → app/api/home}/manifest.yaml +0 -0
- /package/templates/src/{home/api → app/api/home}/migrations/0001__create_home_tables.sql +0 -0
- /package/templates/src/{home/api → app/api/home}/migrations/0002__tenant.mysql.sql +0 -0
- /package/templates/src/{home/api → app/api/home}/migrations/0002__tenant.sql +0 -0
- /package/templates/src/{home/api → app/api/home}/pie/api.ts +0 -0
- /package/templates/src/{home/api → app/api/home}/schema.yaml +0 -0
- /package/templates/src/{home/api → app/api/home}/seed.sql +0 -0
- /package/templates/src/{notification/api → app/api/notification}/_shared/notify.ts +0 -0
- /package/templates/src/{notification/api → app/api/notification}/manifest.yaml +0 -0
- /package/templates/src/{notification/api → app/api/notification}/migrations/0001__create_notifications.sql +0 -0
- /package/templates/src/{notification/api → app/api/notification}/migrations/0002__notifications_user.sql +0 -0
- /package/templates/src/{notification/api → app/api/notification}/migrations/0003__notifications_broadcast.sql +0 -0
- /package/templates/src/{notification/api → app/api/notification}/migrations/0004__notifications_broadcast_hidden.sql +0 -0
- /package/templates/src/{notification/api → app/api/notification}/migrations/0005__tenant.mysql.sql +0 -0
- /package/templates/src/{notification/api → app/api/notification}/migrations/0005__tenant.sql +0 -0
- /package/templates/src/{notification/api → app/api/notification}/schema.yaml +0 -0
- /package/templates/src/{notification/api → app/api/notification}/seed.sql +0 -0
- /package/templates/src/{personal-center/api → app/api/personal-center}/manifest.yaml +0 -0
- /package/templates/src/{personal-center/api → app/api/personal-center}/profile/api.ts +0 -0
- /package/templates/src/{personal-center/api → app/api/personal-center}/upload/api.ts +0 -0
- /package/templates/src/{system/api → app/api/system}/departments/api.ts +0 -0
- /package/templates/src/{system/api → app/api/system}/departments/detach/api.ts +0 -0
- /package/templates/src/{system/api → app/api/system}/departments/members/api.ts +0 -0
- /package/templates/src/{system/api → app/api/system}/departments/move/api.ts +0 -0
- /package/templates/src/{system/api → app/api/system}/login-methods/api.ts +0 -0
- /package/templates/src/{system/api → app/api/system}/manifest.yaml +0 -0
- /package/templates/src/{system/api → app/api/system}/permissions/api.ts +0 -0
- /package/templates/src/{system/api → app/api/system}/users/api.ts +0 -0
- /package/templates/src/{system/api → app/api/system}/users/profile/api.ts +0 -0
- /package/templates/src/{web/api → app/api/web}/manifest.yaml +0 -0
- /package/templates/src/{home/web → app/web/home}/__client/api.schemas.ts +0 -0
- /package/templates/src/{home/web → app/web/home}/locales/en-US.json +0 -0
- /package/templates/src/{home/web → app/web/home}/locales/zh-CN.json +0 -0
- /package/templates/src/{home/web → app/web/home}/pages/components/bar-chart.tsx +0 -0
- /package/templates/src/{home/web → app/web/home}/pages/components/card-list.tsx +0 -0
- /package/templates/src/{home/web → app/web/home}/pages/components/line-chart.tsx +0 -0
- /package/templates/src/{home/web → app/web/home}/pages/components/pie-chart.tsx +0 -0
- /package/templates/src/{home/web → app/web/home}/pages/index.tsx +0 -0
- /package/templates/src/{login/web → app/web/login}/auth-provider.ts +0 -0
- /package/templates/src/{login/web → app/web/login}/locales/en-US.json +0 -0
- /package/templates/src/{login/web → app/web/login}/locales/zh-CN.json +0 -0
- /package/templates/src/{login/web → app/web/login}/pages/change-password.tsx +0 -0
- /package/templates/src/{login/web → app/web/login}/pages/login.tsx +0 -0
- /package/templates/src/{login/web → app/web/login}/wecom.ts +0 -0
- /package/templates/src/{notification/web → app/web/notification}/locales/en-US.json +0 -0
- /package/templates/src/{notification/web → app/web/notification}/locales/zh-CN.json +0 -0
- /package/templates/src/{personal-center/web → app/web/personal-center}/__client/api.schemas.ts +0 -0
- /package/templates/src/{personal-center/web → app/web/personal-center}/locales/en-US.json +0 -0
- /package/templates/src/{personal-center/web → app/web/personal-center}/locales/zh-CN.json +0 -0
- /package/templates/src/{system/web → app/web/system}/pages/departments/sync-bar.tsx +0 -0
- /package/templates/src/{system/web → app/web/system}/pages/departments/tree.ts +0 -0
- /package/templates/src/{system/web → app/web/system}/pages/errors.ts +0 -0
- /package/templates/src/{system/web → app/web/system}/pages/forbidden.tsx +0 -0
package/src/init.ts
CHANGED
|
@@ -240,6 +240,24 @@ export async function initProject(destDir: string, opts: InitOptions = {}): Prom
|
|
|
240
240
|
// 与 pnpm-workspace.yaml 同型,改由 init 现场生成
|
|
241
241
|
ensureGitignore(destDir, report);
|
|
242
242
|
|
|
243
|
+
// 5d) 旧布局冲突提示:0.2.54 起因/app 同时承载前端聚合模块与后端模块。
|
|
244
|
+
// init 只补缺不删除——老工程重跑 init 会同时留下旧目录:
|
|
245
|
+
// · src/<name>/web 与 src/app/web 同名路由注册两遍(菜单重复);
|
|
246
|
+
// · src/<name>/api 与 src/app/api/<name> 模块名重复(ojm 扫描直接报错)。
|
|
247
|
+
const mergedWeb = path.join(destDir, "src", "app", "web", "entry.ts");
|
|
248
|
+
const mergedApi = path.join(destDir, "src", "app", "api");
|
|
249
|
+
if (fs.existsSync(mergedWeb) || fs.existsSync(mergedApi)) {
|
|
250
|
+
const appModules = ["home", "login", "personal-center", "notification", "system", "_platform", "auth", "web"];
|
|
251
|
+
const oldWeb = appModules.filter(name => fs.existsSync(path.join(destDir, "src", name, "web", "entry.ts")));
|
|
252
|
+
if (oldWeb.length > 0)
|
|
253
|
+
report.warnings.push(`检测到旧布局的前端模块目录(${oldWeb.map(n => `src/${n}/web`).join("、")})与新聚合模块 src/app/web 并存——路由会重复注册。请手工删除这些旧 web 目录。`);
|
|
254
|
+
const oldApi = appModules.filter(name =>
|
|
255
|
+
fs.existsSync(path.join(destDir, "src", name, "api", "manifest.yaml"))
|
|
256
|
+
&& fs.existsSync(path.join(mergedApi, name, "manifest.yaml")));
|
|
257
|
+
if (oldApi.length > 0)
|
|
258
|
+
report.warnings.push(`检测到旧布局的后端模块目录(${oldApi.map(n => `src/${n}/api`).join("、")})与新位置 src/app/api/<name> 同名——模块名重复会让 ojm 扫描报错。请手工删除这些旧 api 目录。`);
|
|
259
|
+
}
|
|
260
|
+
|
|
243
261
|
console.log(`[ojm] init 完成:${destDir}`);
|
|
244
262
|
console.log(`[ojm] 新增 ${report.created.length} 项,跳过(已存在)${report.skipped.length} 项`);
|
|
245
263
|
for (const item of report.skipped)
|
|
@@ -400,14 +418,18 @@ function pruneUnincludedApiPaths(destDir: string, include: Set<string>, report:
|
|
|
400
418
|
// 只裁本次新建的 openapi.json:init 是幂等补缺,不改动既有工程文件
|
|
401
419
|
if (!report.created.includes("openapi.json"))
|
|
402
420
|
return;
|
|
403
|
-
//
|
|
421
|
+
// 白名单里的 api 侧条目决定「脚手架有哪些后端模块」:
|
|
422
|
+
// 平铺 `src/<name>/api` 与聚合容器 `src/app/api/<name>` 都要认。
|
|
404
423
|
const mods = new Set([...include]
|
|
405
|
-
.map(p => /^src\/([^/]+)\/api$/.exec(p)?.[1])
|
|
424
|
+
.map(p => /^src\/([^/]+)\/api$/.exec(p)?.[1] ?? /^src\/[^/]+\/api\/([^/]+)$/.exec(p)?.[1])
|
|
406
425
|
.filter((m): m is string => !!m));
|
|
407
426
|
if (mods.size === 0)
|
|
408
427
|
return;
|
|
409
428
|
const openapiPath = path.join(destDir, "openapi.json");
|
|
410
|
-
const doc = JSON.parse(fs.readFileSync(openapiPath, "utf-8")) as {
|
|
429
|
+
const doc = JSON.parse(fs.readFileSync(openapiPath, "utf-8")) as {
|
|
430
|
+
paths?: Record<string, unknown>
|
|
431
|
+
tags?: { name?: string }[]
|
|
432
|
+
};
|
|
411
433
|
const paths = doc.paths ?? {};
|
|
412
434
|
const dropped = Object.keys(paths).filter((p) => {
|
|
413
435
|
const mod = /^\/api\/([^/]+)\//.exec(p)?.[1];
|
|
@@ -417,6 +439,10 @@ function pruneUnincludedApiPaths(destDir: string, include: Set<string>, report:
|
|
|
417
439
|
return;
|
|
418
440
|
for (const p of dropped)
|
|
419
441
|
delete paths[p];
|
|
442
|
+
// 顶层 tags 也要同步:只裁路径会留下指向不存在模块的 tag(契约自相矛盾,
|
|
443
|
+
// oj openapi 重产时又会消失,属 drift 噪声)。
|
|
444
|
+
if (Array.isArray(doc.tags))
|
|
445
|
+
doc.tags = doc.tags.filter(t => typeof t.name !== "string" || mods.has(t.name));
|
|
420
446
|
fs.writeFileSync(openapiPath, `${JSON.stringify(doc, null, "\t")}\n`);
|
|
421
447
|
report.created.push(`openapi.json 裁掉未收录 api 模块端点 ${dropped.length} 条`);
|
|
422
448
|
}
|
package/src/layout.ts
CHANGED
|
@@ -6,13 +6,16 @@
|
|
|
6
6
|
*
|
|
7
7
|
* 三种布局(互斥):
|
|
8
8
|
* module-root <project>/src/<name>/{api,web} 新布局:一目录一模块(设计 v2)
|
|
9
|
+
* 也支持聚合容器:src/app/{api/<模块>,web}——app 的 web 侧是多根前端模块,
|
|
10
|
+
* api 侧每个子目录各自是独立后端模块(模块名 = 子目录名 = URL 前缀)
|
|
9
11
|
* new <project>/web/src/<name> 历史 uni-dev 布局
|
|
10
12
|
* flat <project>/web/<name> 框架根仓 dogfooding(产物落 build/)
|
|
11
13
|
*
|
|
12
14
|
* module-root 的判定只认「模块根标记文件」而不是单纯的 src/ 存在性:工程里
|
|
13
15
|
* 常有不属于模块的 src/(脚本、共享库),凭目录名猜会误判。
|
|
14
16
|
* `src/<x>/api/manifest.yaml` 与 `src/<x>/web/entry.ts` 是唯一真源(设计 C-6:
|
|
15
|
-
*
|
|
17
|
+
* 一侧可缺省,两侧皆无则不是模块)。聚合容器下后端模块根是 `src/app/api/<x>/manifest.yaml`
|
|
18
|
+
* (见 collectApiRoots)。
|
|
16
19
|
*
|
|
17
20
|
* 产物根(2026-10-22 起收进工程根单一 dist/,原 {web,api}/dist 与源码混树):
|
|
18
21
|
* dist/api oj 扁平 release 根(<name>-<version>/ 同层兄弟 + manifests.yaml),三种布局一致
|
|
@@ -31,7 +34,7 @@ export type LayoutKind = "module-root" | "new" | "flat";
|
|
|
31
34
|
/** 模块一侧的可缺省组合:两侧皆无不是模块(设计 C-6) */
|
|
32
35
|
export interface ModuleSides {
|
|
33
36
|
name: string
|
|
34
|
-
/**
|
|
37
|
+
/** 后端源码目录(`src/<name>/api` 或聚合容器 `src/app/api/<name>`;无此侧为 undefined) */
|
|
35
38
|
apiSrc?: string
|
|
36
39
|
/** 前端源码目录 `<project>/src/<name>/web`(无此侧为 undefined) */
|
|
37
40
|
webSrc?: string
|
|
@@ -82,13 +85,44 @@ function isFile(p: string): boolean {
|
|
|
82
85
|
* 坏目录只在 module-root 被判定为当前布局后才报错(见 resolveLayout),
|
|
83
86
|
* 否则一个恰好带 `src/utils/api/` 的历史工程会被误伤。
|
|
84
87
|
*/
|
|
88
|
+
/** 递归扫描时跳过的目录名(产物 / 依赖 / 生成物,均不可能承载模块根) */
|
|
89
|
+
const SCAN_SKIP_DIRS = new Set(["node_modules", "dist", "build", "__client", ".cache", ".git"]);
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* 收集一个容器目录下的后端模块根。
|
|
93
|
+
*
|
|
94
|
+
* 两种形态并存(oj 的 `--module-root` 支持任意深度):
|
|
95
|
+
* · `<container>/api/manifest.yaml` —— 模块根在 api 自身(src/<name>/api)
|
|
96
|
+
* · `<container>/api/<name>/manifest.yaml` —— 聚合容器下的子模块(src/app/api/system)
|
|
97
|
+
*
|
|
98
|
+
* 后者用于「一个应用目录承载多个业务域」:src/app 的 web 侧是多根聚合模块,
|
|
99
|
+
* api 侧按业务域分成 system / notification / … 各自仍是独立模块(模块名 = 目录名
|
|
100
|
+
* = URL 前缀,故不能并成一个 app 模块)。命中模块根即停,不再往 api 内部递归。
|
|
101
|
+
*/
|
|
102
|
+
function collectApiRoots(container: string, out: string[]): void {
|
|
103
|
+
const apiDir = path.join(container, "api");
|
|
104
|
+
if (isFile(path.join(apiDir, "manifest.yaml"))) {
|
|
105
|
+
out.push(apiDir);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (!isDir(apiDir))
|
|
109
|
+
return;
|
|
110
|
+
for (const ent of fs.readdirSync(apiDir, { withFileTypes: true })
|
|
111
|
+
.filter(e => e.isDirectory() && !e.name.startsWith(".") && !SCAN_SKIP_DIRS.has(e.name))
|
|
112
|
+
.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0))) {
|
|
113
|
+
if (isFile(path.join(apiDir, ent.name, "manifest.yaml")))
|
|
114
|
+
out.push(path.join(apiDir, ent.name));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
85
118
|
function scanModuleRoot(projectRoot: string): { modules: ModuleSides[], broken: string[] } | null {
|
|
86
119
|
const srcDir = path.join(projectRoot, "src");
|
|
87
120
|
if (!isDir(srcDir))
|
|
88
121
|
return null;
|
|
89
122
|
|
|
90
|
-
const
|
|
123
|
+
const byName = new Map<string, ModuleSides>();
|
|
91
124
|
const broken: string[] = [];
|
|
125
|
+
const apiOwners = new Map<string, string>();
|
|
92
126
|
// 按名字排序:readdir 的顺序取决于文件系统与创建历史(同一份源码在不同机器上可能不同),
|
|
93
127
|
// 而枚举顺序会进入构建顺序与产物内容——不排序则「同布局两次构建 sha256 相等」无法保证。
|
|
94
128
|
const entries = fs.readdirSync(srcDir, { withFileTypes: true })
|
|
@@ -96,22 +130,41 @@ function scanModuleRoot(projectRoot: string): { modules: ModuleSides[], broken:
|
|
|
96
130
|
.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
97
131
|
for (const ent of entries) {
|
|
98
132
|
const dir = path.join(srcDir, ent.name);
|
|
99
|
-
const apiDir = path.join(dir, "api");
|
|
100
133
|
const webDir = path.join(dir, "web");
|
|
101
|
-
const hasApi = isFile(path.join(apiDir, "manifest.yaml"));
|
|
102
134
|
const hasWeb = isFile(path.join(webDir, "entry.ts"));
|
|
103
|
-
|
|
135
|
+
const apiRoots: string[] = [];
|
|
136
|
+
collectApiRoots(dir, apiRoots);
|
|
137
|
+
if (!hasWeb && apiRoots.length === 0) {
|
|
104
138
|
// 只把「有 api/ 或 web/ 子目录但缺标记」算坏目录;纯共享库目录(无两者)静默跳过
|
|
105
|
-
if (isDir(
|
|
139
|
+
if (isDir(path.join(dir, "api")) || isDir(webDir))
|
|
106
140
|
broken.push(ent.name);
|
|
107
141
|
continue;
|
|
108
142
|
}
|
|
109
|
-
|
|
110
|
-
name: ent.name,
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
143
|
+
if (hasWeb)
|
|
144
|
+
byName.set(ent.name, { name: ent.name, webSrc: webDir });
|
|
145
|
+
for (const apiDir of apiRoots) {
|
|
146
|
+
// 模块名:平铺 src/system/api → system(api 的父目录);
|
|
147
|
+
// 聚合 src/app/api/system → system(api 下方那层目录本身)。
|
|
148
|
+
const name = path.basename(apiDir) === "api"
|
|
149
|
+
? path.basename(path.dirname(apiDir))
|
|
150
|
+
: path.basename(apiDir);
|
|
151
|
+
const prev = apiOwners.get(name);
|
|
152
|
+
if (prev) {
|
|
153
|
+
throw new Error(
|
|
154
|
+
`[ojm] 后端模块名重复:${name} 同时出现在 ${path.relative(projectRoot, prev)} 与 `
|
|
155
|
+
+ `${path.relative(projectRoot, apiDir)}。模块名即 URL 前缀,必须唯一——`
|
|
156
|
+
+ "删掉旧布局目录(或改名)后重试。",
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
apiOwners.set(name, apiDir);
|
|
160
|
+
const hit = byName.get(name);
|
|
161
|
+
if (hit)
|
|
162
|
+
hit.apiSrc = apiDir;
|
|
163
|
+
else
|
|
164
|
+
byName.set(name, { name, apiSrc: apiDir });
|
|
165
|
+
}
|
|
114
166
|
}
|
|
167
|
+
const modules = [...byName.values()].sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
115
168
|
return { modules, broken };
|
|
116
169
|
}
|
|
117
170
|
|
|
@@ -244,7 +297,7 @@ export function legacyApiModuleRoots(projectRoot: string): ApiModuleRoot[] {
|
|
|
244
297
|
* 为什么不直接 walk `apiModulesSrc(layout)`:module-root 布局的树根是 `src`,模块的
|
|
245
298
|
* api 侧在 `<src>/<name>/api`,中间那层 `api` 是**布局分隔符、不是路由段**。拿 `src`
|
|
246
299
|
* 当扫描根会双重出错——路由镜像多出 `api` 段(`system/api/users` ≠ 真实 `system/users`),
|
|
247
|
-
* 且 `src/<name>/web/__client/
|
|
300
|
+
* 且 `src/<name>/web/__client/index.ts` 这种前端生成物也会被当 handler 扫进来。
|
|
248
301
|
*
|
|
249
302
|
* module-root → 按 `<name>/api/manifest.yaml` 判模块(与 build 同源,name 取 manifest);
|
|
250
303
|
* 历史布局 → `<project>/api/src` 的一级目录即模块。
|
|
@@ -267,9 +320,12 @@ export function moduleDir(layout: ProjectLayout, name: string): string {
|
|
|
267
320
|
* 历史布局为 `<project>/api/src/<name>`——不在 modulesSrc 之下,必须走 projectRoot。
|
|
268
321
|
*/
|
|
269
322
|
export function apiSrcOf(layout: ProjectLayout, name: string): string {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
323
|
+
if (layout.kind !== "module-root")
|
|
324
|
+
return path.join(layout.projectRoot, "api", "src", name);
|
|
325
|
+
// 聚合容器(src/app/api/<name>)与平铺(src/<name>/api)并存,以扫描结果为准;
|
|
326
|
+
// 找不到时回落旧口径,保证报错信息里的路径仍指向「本该在的位置」。
|
|
327
|
+
const hit = scanModules(layout).find(m => m.name === name)?.apiSrc;
|
|
328
|
+
return hit ?? path.join(layout.modulesSrc, name, "api");
|
|
273
329
|
}
|
|
274
330
|
|
|
275
331
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { InstallContext } from "../../../templates/src/
|
|
1
|
+
import type { InstallContext } from "../../../templates/src/app/api/_platform/_lifecycle/steps/preflight";
|
|
2
2
|
|
|
3
3
|
// BDD:bundle 级 manifest.yaml 契约校验(P1-11,C2 规则构建列)。
|
|
4
4
|
// 规则真源:docs/prd/solution/202610171254-lifecycle-manifest-contract.md(C2-01~33 构建列,§4.1 矩阵);
|
|
@@ -13,7 +13,7 @@ import { describe, expect, it } from "vitest";
|
|
|
13
13
|
import { parse } from "yaml";
|
|
14
14
|
// 跨端引用(双端一致钉):被测实现同仓分居两运行时(计划 §1「两运行时取舍」),
|
|
15
15
|
// 测试直接互调,比「两套件各读同一夹具」更强——漂移即本文件红。
|
|
16
|
-
import { preflight } from "../../../templates/src/
|
|
16
|
+
import { preflight } from "../../../templates/src/app/api/_platform/_lifecycle/steps/preflight";
|
|
17
17
|
import { checkLocalesComplete, checkTablesAgainstSchema, parseManifest, RESERVED_NAMES, reservedNameSuggestions, validateContract } from "../../manifest-contract";
|
|
18
18
|
|
|
19
19
|
const FIXTURES = join(dirname(fileURLToPath(import.meta.url)), "fixtures");
|
|
@@ -68,7 +68,7 @@ describe("given 一个装了 oj 二进制的源工程 When 自举隔离站点",
|
|
|
68
68
|
|
|
69
69
|
const site = await bootstrapIsolatedSite({ projectRoot: src, starter });
|
|
70
70
|
|
|
71
|
-
expect(existsSync(join(site.root, "src", "
|
|
71
|
+
expect(existsSync(join(site.root, "src", "app", "api", "_platform", "seed.sql"))).toBe(true);
|
|
72
72
|
expect(existsSync(join(site.root, "bin", "oj"))).toBe(true);
|
|
73
73
|
expect(existsSync(join(site.root, "bin", "plugins", "auth.wasm"))).toBe(true);
|
|
74
74
|
expect(site.root).not.toBe(src);
|
package/src/site.ts
CHANGED
|
@@ -21,7 +21,7 @@ export function tenantHeaders(): Record<string, string> {
|
|
|
21
21
|
return { [TENANT_HEADER]: DEFAULT_TENANT };
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
/** 任务记录(线形状镜像;真源 templates/src/
|
|
24
|
+
/** 任务记录(线形状镜像;真源 templates/src/app/api/_platform/_lifecycle/types.ts:65,客户端不 import 模板内部) */
|
|
25
25
|
export interface TaskRecord {
|
|
26
26
|
/**
|
|
27
27
|
* 记录 schema 版本(P2-10 线形状镜像):缺省(undefined)=旧记录=v1 语义照常渲染;
|
package/templates/README.md
CHANGED
|
@@ -6,82 +6,88 @@
|
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
pnpm install
|
|
9
|
-
pnpm dev # 开发:前端 devServer + oj 后端(
|
|
9
|
+
pnpm dev # 开发:前端 devServer + oj 后端(src 下 api.ts 保存即热更)
|
|
10
10
|
pnpm build # 构建:oj build + 前端全站合并到 dist/web
|
|
11
11
|
pnpm preview # 预览:oj migrate + server(release 产物)
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
登录账号:`
|
|
14
|
+
登录账号:`superadmin` / `aA123456`(四个内置角色账号见 `src/app/api/_platform/seed.sql`)。
|
|
15
15
|
|
|
16
|
-
##
|
|
16
|
+
## 目录(一个 `src/app` 装下整个应用)
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
```
|
|
19
|
+
config.yaml 站点/数据库/租户配置(init 现场签发证书到 config/)
|
|
20
|
+
src/app/api/<name>/ 后端模块(模块名 = URL 前缀;目录镜像:<路径>/api.ts)
|
|
21
|
+
src/app/web/ 前端聚合模块(多根,一个 entry.ts 挂 /home /login /…)
|
|
22
|
+
<root>/ 每个根:routes.ts + pages/ + locales/ + __client/
|
|
23
|
+
src/oj-market/web/ 解决方案市场(第一方能力,独立模块,删目录即下线)
|
|
24
|
+
dist/api/ oj build 后端产物
|
|
25
|
+
dist/web/ 站点产物(shell + modules + modules.json)
|
|
26
|
+
bin/ oj 二进制(init 解压,不入库)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
- **后端**按业务域分模块:`_platform` / `auth` / `notification` / `personal-center` /
|
|
30
|
+
`system` / `web`,各自是独立 oj 模块(模块名即 `/api/<name>` 前缀),只是统一收在
|
|
31
|
+
`src/app/api` 下——顶层不再散落同名目录。
|
|
32
|
+
- **前端** `src/app/web` 是一个**多根模块**:一个 `entry.ts` 里挂六个顶层路由,各根按
|
|
33
|
+
`handle.order` 排菜单,页面按根分目录。新增业务页面时建议单独建模块
|
|
34
|
+
(`pnpm exec ojm new <name>`),不要往聚合模块里塞。
|
|
21
35
|
|
|
22
|
-
|
|
23
|
-
|
|
36
|
+
## 契约与 `__client`
|
|
37
|
+
|
|
38
|
+
前端调后端一律走 `ojm api` 从 `openapi.json` 生成的 `__client/`(随模板分发,开箱即用)。
|
|
39
|
+
改了 handler 的 `.schema`(入参)/ `.response`(出参)后刷新:
|
|
24
40
|
|
|
25
41
|
```bash
|
|
26
|
-
bin/oj openapi -c config.yaml -
|
|
27
|
-
pnpm exec ojm api
|
|
42
|
+
bin/oj openapi -c config.yaml --module-root src -b /api -o openapi.json # 重产契约
|
|
43
|
+
pnpm exec ojm api # 重生成 __client
|
|
28
44
|
```
|
|
29
45
|
|
|
30
|
-
|
|
31
|
-
|
|
46
|
+
生成物入口是 `__client/index.ts`(**不是 `api.ts`**:oj dev 会把模块目录下的
|
|
47
|
+
`api.ts` 当路由扫描)。页面里用 `from "../__client"` 导入。
|
|
32
48
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
bin/ oj 二进制(init 解压,不入库)
|
|
37
|
-
api/src/ 后端模块源码(目录镜像路由:api/src/<模块>/<路径>/api.ts)
|
|
38
|
-
dist/api/ oj build 产物
|
|
39
|
-
web/src/ 前端模块源码
|
|
40
|
-
dist/web/ 构建产物(完整站点)
|
|
41
|
-
```
|
|
49
|
+
多根聚合模块的 client 归属由 `web.config.ts` 的 `clientOwners` 决定(`system → app`),
|
|
50
|
+
生成物落 `src/app/web/system/__client`,与页面同级——页面相对导入不用改。
|
|
51
|
+
`ojm api --check` 可对账漂移(零漂移为发布门禁)。
|
|
42
52
|
|
|
43
53
|
## 注意
|
|
44
54
|
|
|
45
|
-
- 改 `
|
|
55
|
+
- 改 `src/app/api/**/api.ts` 保存即生效;**新增/删除模块目录、改
|
|
46
56
|
schema/migrations/config.yaml 需重启 `ojm dev`**(oj 热更边界)。
|
|
47
|
-
- 后端开发请用 `/oj-api-dev` skill(`.claude/skills/oj-api-dev
|
|
48
|
-
|
|
49
|
-
- `api/config.yaml` 的证书为 init 现场签发的本地 dev 证书,不构成信任边界;
|
|
57
|
+
- 后端开发请用 `/oj-api-dev` skill(`.claude/skills/oj-api-dev/`),手册按章节按需读。
|
|
58
|
+
- `config/` 下的证书为 init 现场签发的本地 dev 证书,不构成信任边界;
|
|
50
59
|
生产环境用 oj 仓库 `oj-cert gen` 全新签发并替换公钥。
|
|
51
60
|
|
|
52
61
|
## 排障 / 常见问题
|
|
53
62
|
|
|
54
|
-
- **`pnpm dev` / `pnpm build` 报 `Could not resolve "
|
|
55
|
-
|
|
56
|
-
`ojm api` 从 `openapi.json` 生成的产物。在工程根跑一次即可补齐:
|
|
63
|
+
- **`pnpm dev` / `pnpm build` 报 `Could not resolve "../../__client"`**
|
|
64
|
+
对应根的 `__client` 缺失——它是 `ojm api` 的生成物。在工程根跑一次即可补齐:
|
|
57
65
|
|
|
58
66
|
```bash
|
|
59
|
-
pnpm exec ojm api #
|
|
67
|
+
pnpm exec ojm api # dev 检测到 openapi.json 变化也会自动重生成
|
|
60
68
|
```
|
|
61
69
|
|
|
62
|
-
|
|
70
|
+
改完后端契约后先重产契约再刷新:
|
|
63
71
|
|
|
64
72
|
```bash
|
|
65
|
-
bin/oj openapi -c config.yaml -
|
|
73
|
+
bin/oj openapi -c config.yaml --module-root src -b /api -o openapi.json
|
|
66
74
|
pnpm exec ojm api
|
|
67
75
|
```
|
|
68
76
|
|
|
69
77
|
- **`__client/` 下的文件能直接改吗?**
|
|
70
|
-
不能手改、也不入库——它是生成物,下次 `ojm api`
|
|
71
|
-
|
|
72
|
-
|
|
78
|
+
不能手改、也不入库——它是生成物,下次 `ojm api` 会覆盖。改接口就改 handler 的
|
|
79
|
+
`.schema`/`.response`,再跑上面的命令刷新。
|
|
80
|
+
|
|
81
|
+
- **从旧版本升级(`__client/api.ts`)**
|
|
82
|
+
0.2.54 起入口改名 `index.ts`(`api.ts` 会被 oj dev 当路由扫描)。重跑一次
|
|
83
|
+
`pnpm exec ojm api` 即可:新文件写入、旧 `api.ts` 自动删除;页面导入改为
|
|
84
|
+
`from "../__client"`。
|
|
73
85
|
|
|
74
86
|
- **`pnpm dlx @oj-module/cli@latest init` 生成的脚手架缺模块 / 看起来很旧**
|
|
75
|
-
pnpm dlx 会按首次解析到的版本**缓存** CLI
|
|
76
|
-
|
|
77
|
-
模板偏旧(典型表现:`web/src` 下没有 `system` 目录)。
|
|
78
|
-
排查:`ls web/src` 看是否缺预期模块。
|
|
79
|
-
解决:清掉 dlx 缓存后重跑——
|
|
87
|
+
pnpm dlx 会按首次解析到的版本**缓存** CLI 可执行文件。排查:`ls src/app/api` 看是否缺预期模块。
|
|
88
|
+
解决:清掉 dlx 缓存后显式指定版本重跑——
|
|
80
89
|
|
|
81
90
|
```bash
|
|
82
91
|
rm -rf ~/Library/Caches/pnpm/dlx # macOS;其它平台见 pnpm 文档的 dlx-store 路径
|
|
83
|
-
|
|
84
|
-
pnpm dlx @oj-module/cli@<明确版本> init <工程名> --yes # 显式指定版本最稳
|
|
92
|
+
pnpm dlx @oj-module/cli@<明确版本> init <工程名> --yes
|
|
85
93
|
```
|
|
86
|
-
|
|
87
|
-
养成显式带版本号(如 `@oj-module/cli@0.2.4`)的习惯,可避免 `@latest` 缓存歧义。
|
|
@@ -40,7 +40,7 @@ include:
|
|
|
40
40
|
- src/styles.css
|
|
41
41
|
# 后端 typecheck 工程(package.json 的 typecheck:api 指向它);只收后端、排除 web
|
|
42
42
|
- src/tsconfig.json
|
|
43
|
-
# —— 后端:配置 / 种子 / 测试 / 默认模块(模块源码在 src
|
|
43
|
+
# —— 后端:配置 / 种子 / 测试 / 默认模块(模块源码在 src/app/api/<模块>)——
|
|
44
44
|
- config.yaml
|
|
45
45
|
- .ojm-api-exempt.json
|
|
46
46
|
- seed.mysql.sql
|
|
@@ -71,8 +71,12 @@ include:
|
|
|
71
71
|
- tests/rbac-solution-perms.spec.ts
|
|
72
72
|
- tests/rbac-tables.spec.ts
|
|
73
73
|
- tests/rbac-users.spec.ts
|
|
74
|
+
# 角色页展示层过滤 superadmin(后端 listRoles 仍返回;页面源码随 src/app/web/system 下发)
|
|
75
|
+
- tests/roles-page.spec.ts
|
|
74
76
|
- tests/seed-role-accounts.spec.ts
|
|
75
|
-
#
|
|
77
|
+
# 用户页行级门禁:只有 superadmin 能改 superadmin 账号(后端 assertCanManageTarget 的另一面)
|
|
78
|
+
- tests/users-page.spec.ts
|
|
79
|
+
# 企微连接器与组织同步:源码本就分发(src/app/api/auth),对应回归也必须跟着走,
|
|
76
80
|
# 否则用户工程升级后改配置/换套件时没有任何本地自检手段。
|
|
77
81
|
- tests/wecom-admin.spec.ts
|
|
78
82
|
- tests/wecom-client-timeout.spec.ts
|
|
@@ -95,20 +99,30 @@ include:
|
|
|
95
99
|
- tests/lifecycle/uninstall.spec.ts
|
|
96
100
|
- tests/lifecycle/verify-checks.spec.ts
|
|
97
101
|
- tests/lifecycle/verify-report.spec.ts
|
|
98
|
-
|
|
99
|
-
-
|
|
100
|
-
- src/
|
|
101
|
-
- src/
|
|
102
|
-
- src/
|
|
103
|
-
- src/
|
|
104
|
-
|
|
105
|
-
- src/
|
|
106
|
-
|
|
107
|
-
-
|
|
108
|
-
|
|
109
|
-
- src/
|
|
110
|
-
- src/
|
|
111
|
-
|
|
102
|
+
# 框架内置后端全部收在 src/app/api/<模块>(模块名 = URL 前缀,故仍是独立模块)。
|
|
103
|
+
# home 的 api 是 demo-only 不进脚手架(见下),其余全部下发。
|
|
104
|
+
- src/app/api/_platform
|
|
105
|
+
- src/app/api/auth
|
|
106
|
+
- src/app/api/notification
|
|
107
|
+
- src/app/api/personal-center
|
|
108
|
+
- src/app/api/system
|
|
109
|
+
- src/app/api/web
|
|
110
|
+
# —— web 前端模块 ——
|
|
111
|
+
# 纯 client 归属目录(无页面模块):_platform 的生成物被 oj-market 引用,
|
|
112
|
+
# auth / web 的是各自后端契约的配套产物,一并下发保证 ojm api --check 开箱即过。
|
|
113
|
+
- src/app/web/_platform
|
|
114
|
+
- src/app/web/auth
|
|
115
|
+
# 框架内置页面聚合为一个多根模块(首页/登录/个人中心/通知/系统管理)。
|
|
116
|
+
# home 只列 routes/locales/pages:其 api 模块是 demo-only 不进脚手架,__client
|
|
117
|
+
# 留着会指向不存在的端点(见顶部说明),故逐子路径收录而不是整棵 src/app/web。
|
|
118
|
+
- src/app/web/entry.ts
|
|
119
|
+
- src/app/web/home/routes.ts
|
|
120
|
+
- src/app/web/home/locales
|
|
121
|
+
- src/app/web/home/pages
|
|
122
|
+
- src/app/web/login
|
|
123
|
+
- src/app/web/notification
|
|
124
|
+
- src/app/web/personal-center
|
|
125
|
+
- src/app/web/system
|
|
112
126
|
# 解决方案市场:默认第一方能力。逐子路径收录——__tests__ 不进脚手架
|
|
113
127
|
# (用户工程无 web 侧 vitest 基建,拷了是死文件)
|
|
114
128
|
- src/oj-market/web/entry.ts
|
|
@@ -119,6 +133,4 @@ include:
|
|
|
119
133
|
- src/oj-market/web/locales
|
|
120
134
|
- src/oj-market/web/pages
|
|
121
135
|
- src/oj-market/web/utils
|
|
122
|
-
- src/
|
|
123
|
-
- src/system/web
|
|
124
|
-
- src/web/web
|
|
136
|
+
- src/app/web/web
|