@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
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { AppRouteRecordRaw, ModuleContext } from "@oj-module/runtime";
|
|
2
|
+
|
|
3
|
+
import { useAuthStore, useTenantStore } from "@oj-module/runtime";
|
|
4
|
+
import { lazy } from "react";
|
|
5
|
+
|
|
6
|
+
import { authProvider } from "./auth-provider";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 登录根 `/login`(+ 强制改密 `/change-password`)。
|
|
10
|
+
*
|
|
11
|
+
* shell 宿主只消费模块路由(`getRoutes()`),**不挂 runtime 的内置 baseRoutes**,
|
|
12
|
+
* 因此缺少该根时 `/login` 无路由可跳(登出/回跳登录会落空)。本根补上路由:
|
|
13
|
+
* 登录链路仍回落到 runtime 默认链(`fetchLogin` → 后端内置 `auth/login` +
|
|
14
|
+
* 守卫 `getUserInfo` → `web/user-info`),本根只负责「内容区」。
|
|
15
|
+
*
|
|
16
|
+
* `handle.login: true` 让框架识别为登录页(`resolveLoginRoute` 校验路径必须是
|
|
17
|
+
* `/login`);`handle.layout: "fullscreen"` 由框架注入全屏外壳。
|
|
18
|
+
*/
|
|
19
|
+
export const routes: AppRouteRecordRaw[] = [
|
|
20
|
+
{
|
|
21
|
+
path: "/login",
|
|
22
|
+
handle: {
|
|
23
|
+
layout: "fullscreen",
|
|
24
|
+
login: true,
|
|
25
|
+
hideInMenu: true,
|
|
26
|
+
title: "login:page.title",
|
|
27
|
+
},
|
|
28
|
+
children: [
|
|
29
|
+
{
|
|
30
|
+
index: true,
|
|
31
|
+
Component: lazy(() => import("./pages/login")),
|
|
32
|
+
handle: { title: "login:page.title" },
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
// 首登/重置后强制改密(must_change 悬挂期唯一可用写端);
|
|
38
|
+
// 管理面 403 msg=password change required 时也引导至此
|
|
39
|
+
path: "/change-password",
|
|
40
|
+
handle: {
|
|
41
|
+
layout: "fullscreen",
|
|
42
|
+
hideInMenu: true,
|
|
43
|
+
title: "login:page.changePwd.title",
|
|
44
|
+
},
|
|
45
|
+
children: [
|
|
46
|
+
{
|
|
47
|
+
index: true,
|
|
48
|
+
Component: lazy(() => import("./pages/change-password")),
|
|
49
|
+
handle: { title: "login:page.changePwd.title", hideInMenu: true },
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 认证 provider:仅接管「企微票据换会话」,其余方式原样委托内置链(见 auth-provider.ts)。
|
|
57
|
+
* 注册时机必须在任何登录动作之前——onInit 先于路由渲染。
|
|
58
|
+
*/
|
|
59
|
+
export async function onInit(ctx: ModuleContext) {
|
|
60
|
+
ctx.register.authProvider(authProvider);
|
|
61
|
+
// 登出必须清租户:否则下一个用户会带着上一个用户的租户头(跨租户请求 fail-closed 403,
|
|
62
|
+
// 但单租户部署会静默落到别人的 default 视图)。token 由非空转空即视为登出。
|
|
63
|
+
//
|
|
64
|
+
// 租户上下文的请求头由**运行时**注入(`runtime/store/tenant.ts`),本根不注册
|
|
65
|
+
// headerProvider——宿主 shell 的 hydrateUser / solution-routes 都在任何模块 onInit
|
|
66
|
+
// 之前就发请求,模块注册必然晚一步(2026-10-30 冷启动 e2e 实测两个 400)。
|
|
67
|
+
useAuthStore.subscribe((state, prev) => {
|
|
68
|
+
if (prev.token && !state.token)
|
|
69
|
+
useTenantStore.getState().clear();
|
|
70
|
+
});
|
|
71
|
+
}
|
|
@@ -21,6 +21,13 @@ export const schemas = {
|
|
|
21
21
|
body: z.object({
|
|
22
22
|
"id": z.string()
|
|
23
23
|
}),
|
|
24
|
+
data: z.null(),
|
|
25
|
+
},
|
|
26
|
+
"notificationNotificationsReadAllPost": {
|
|
27
|
+
data: z.null(),
|
|
28
|
+
},
|
|
29
|
+
"notificationNotificationsClearPost": {
|
|
30
|
+
data: z.null(),
|
|
24
31
|
},
|
|
25
32
|
"notificationPushPost": {
|
|
26
33
|
body: z.object({
|
|
@@ -30,6 +37,9 @@ export const schemas = {
|
|
|
30
37
|
"link": z.string().optional(),
|
|
31
38
|
"avatar": z.string().optional(),
|
|
32
39
|
"category": z.enum(["notice", "pm", "mention"]).optional()
|
|
40
|
+
}),
|
|
41
|
+
data: z.object({
|
|
42
|
+
"id": z.string()
|
|
33
43
|
}),
|
|
34
44
|
},
|
|
35
45
|
"notificationPushBroadcastPost": {
|
|
@@ -39,6 +49,9 @@ export const schemas = {
|
|
|
39
49
|
"link": z.string().optional(),
|
|
40
50
|
"avatar": z.string().optional(),
|
|
41
51
|
"category": z.enum(["notice", "pm", "mention"]).optional()
|
|
52
|
+
}),
|
|
53
|
+
data: z.object({
|
|
54
|
+
"id": z.string()
|
|
42
55
|
}),
|
|
43
56
|
},
|
|
44
57
|
"notificationDelPost": {
|
|
@@ -46,5 +59,6 @@ export const schemas = {
|
|
|
46
59
|
"id": z.string(),
|
|
47
60
|
"userId": z.union([z.string(), z.number()]).optional()
|
|
48
61
|
}),
|
|
62
|
+
data: z.null(),
|
|
49
63
|
},
|
|
50
64
|
};
|
package/templates/src/{notification/web/__client/api.ts → app/web/notification/__client/index.ts}
RENAMED
|
@@ -17,6 +17,9 @@ export const API_PREFIX = "/notification";
|
|
|
17
17
|
/** 认领身份前缀(契约 apiPrefix,字面等于模块目录名):跨模块认领护栏对比基准 */
|
|
18
18
|
export const MODULE_PREFIX = "/notification";
|
|
19
19
|
|
|
20
|
+
/** 认领方模块(web.config clientOwners 指定):跨模块认领护栏对比基准 */
|
|
21
|
+
export const MODULE_OWNER = "/app";
|
|
22
|
+
|
|
20
23
|
/**
|
|
21
24
|
* 模块 entry 的 onInit 里一行创建(构造即 install):完成 apiPrefix 登记 +
|
|
22
25
|
* scoped request 绑定,返回绑好前缀的端点函数集合。一模块一 client;
|
|
@@ -28,8 +31,8 @@ export function createNotificationClient(ctx: ModuleContext) {
|
|
|
28
31
|
// 单槽、卸载互相耦合;跨模块需求应由本模块创建后经 provider 暴露。
|
|
29
32
|
// 注意对比基准是 MODULE_PREFIX(目录名)而非 API_PREFIX——D-M8 映射时线上前缀
|
|
30
33
|
// 属于别的命名空间,认领身份仍按目录名。
|
|
31
|
-
if (import.meta.env.DEV && ctx.module.name !==
|
|
32
|
-
console.warn(`[ojm-api] 模块 "${ctx.module.name}" 正在认领 ${MODULE_PREFIX} 的 client——跨模块认领会共享 request 单槽、卸载互相耦合;应由 "${
|
|
34
|
+
if (import.meta.env.DEV && ctx.module.name !== MODULE_OWNER.slice(1)) {
|
|
35
|
+
console.warn(`[ojm-api] 模块 "${ctx.module.name}" 正在认领 ${MODULE_PREFIX} 的 client——跨模块认领会共享 request 单槽、卸载互相耦合;应由 "${MODULE_OWNER.slice(1)}" 模块在自身 onInit 创建,跨模块需求经 provider 暴露。`);
|
|
33
36
|
}
|
|
34
37
|
ctx.register.apiPrefix(API_PREFIX);
|
|
35
38
|
bindRequest(ctx.utils.request);
|
|
@@ -88,15 +91,22 @@ export async function notificationNotificationsGet(): Promise<NotificationNotifi
|
|
|
88
91
|
}
|
|
89
92
|
|
|
90
93
|
export type NotificationNotificationsReadPostBody = z.input<(typeof schemas)["notificationNotificationsReadPost"]["body"]>;
|
|
94
|
+
export type NotificationNotificationsReadPostData = z.infer<(typeof schemas)["notificationNotificationsReadPost"]["data"]>;
|
|
91
95
|
|
|
92
|
-
export async function notificationNotificationsReadPost(body: NotificationNotificationsReadPostBody): Promise<
|
|
96
|
+
export async function notificationNotificationsReadPost(body: NotificationNotificationsReadPostBody): Promise<NotificationNotificationsReadPostData> {
|
|
93
97
|
const client = ensureReq();
|
|
94
98
|
try {
|
|
95
|
-
const env = await client.post(`notification/notifications/read`, { json: body }).json<OjEnvelope<
|
|
99
|
+
const env = await client.post(`notification/notifications/read`, { json: body }).json<OjEnvelope<NotificationNotificationsReadPostData>>();
|
|
96
100
|
// 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
|
|
97
101
|
if (typeof env.code === "number" && env.code !== 0)
|
|
98
102
|
throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
|
|
99
|
-
const data = env.data as
|
|
103
|
+
const data = env.data as NotificationNotificationsReadPostData;
|
|
104
|
+
if (import.meta.env.DEV) {
|
|
105
|
+
const { schemas } = await import("./api.schemas");
|
|
106
|
+
const r = schemas["notificationNotificationsReadPost"].data.safeParse(data);
|
|
107
|
+
if (!r.success)
|
|
108
|
+
throw new ContractApiError(-1, `[契约违例] notificationNotificationsReadPost 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
|
|
109
|
+
}
|
|
100
110
|
return data;
|
|
101
111
|
}
|
|
102
112
|
catch (e) {
|
|
@@ -104,14 +114,22 @@ export async function notificationNotificationsReadPost(body: NotificationNotifi
|
|
|
104
114
|
}
|
|
105
115
|
}
|
|
106
116
|
|
|
107
|
-
export
|
|
117
|
+
export type NotificationNotificationsReadAllPostData = z.infer<(typeof schemas)["notificationNotificationsReadAllPost"]["data"]>;
|
|
118
|
+
|
|
119
|
+
export async function notificationNotificationsReadAllPost(): Promise<NotificationNotificationsReadAllPostData> {
|
|
108
120
|
const client = ensureReq();
|
|
109
121
|
try {
|
|
110
|
-
const env = await client.post(`notification/notifications/read-all`).json<OjEnvelope<
|
|
122
|
+
const env = await client.post(`notification/notifications/read-all`).json<OjEnvelope<NotificationNotificationsReadAllPostData>>();
|
|
111
123
|
// 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
|
|
112
124
|
if (typeof env.code === "number" && env.code !== 0)
|
|
113
125
|
throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
|
|
114
|
-
const data = env.data as
|
|
126
|
+
const data = env.data as NotificationNotificationsReadAllPostData;
|
|
127
|
+
if (import.meta.env.DEV) {
|
|
128
|
+
const { schemas } = await import("./api.schemas");
|
|
129
|
+
const r = schemas["notificationNotificationsReadAllPost"].data.safeParse(data);
|
|
130
|
+
if (!r.success)
|
|
131
|
+
throw new ContractApiError(-1, `[契约违例] notificationNotificationsReadAllPost 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
|
|
132
|
+
}
|
|
115
133
|
return data;
|
|
116
134
|
}
|
|
117
135
|
catch (e) {
|
|
@@ -119,14 +137,22 @@ export async function notificationNotificationsReadAllPost(): Promise<unknown> {
|
|
|
119
137
|
}
|
|
120
138
|
}
|
|
121
139
|
|
|
122
|
-
export
|
|
140
|
+
export type NotificationNotificationsClearPostData = z.infer<(typeof schemas)["notificationNotificationsClearPost"]["data"]>;
|
|
141
|
+
|
|
142
|
+
export async function notificationNotificationsClearPost(): Promise<NotificationNotificationsClearPostData> {
|
|
123
143
|
const client = ensureReq();
|
|
124
144
|
try {
|
|
125
|
-
const env = await client.post(`notification/notifications/clear`).json<OjEnvelope<
|
|
145
|
+
const env = await client.post(`notification/notifications/clear`).json<OjEnvelope<NotificationNotificationsClearPostData>>();
|
|
126
146
|
// 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
|
|
127
147
|
if (typeof env.code === "number" && env.code !== 0)
|
|
128
148
|
throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
|
|
129
|
-
const data = env.data as
|
|
149
|
+
const data = env.data as NotificationNotificationsClearPostData;
|
|
150
|
+
if (import.meta.env.DEV) {
|
|
151
|
+
const { schemas } = await import("./api.schemas");
|
|
152
|
+
const r = schemas["notificationNotificationsClearPost"].data.safeParse(data);
|
|
153
|
+
if (!r.success)
|
|
154
|
+
throw new ContractApiError(-1, `[契约违例] notificationNotificationsClearPost 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
|
|
155
|
+
}
|
|
130
156
|
return data;
|
|
131
157
|
}
|
|
132
158
|
catch (e) {
|
|
@@ -135,15 +161,22 @@ export async function notificationNotificationsClearPost(): Promise<unknown> {
|
|
|
135
161
|
}
|
|
136
162
|
|
|
137
163
|
export type NotificationPushPostBody = z.input<(typeof schemas)["notificationPushPost"]["body"]>;
|
|
164
|
+
export type NotificationPushPostData = z.infer<(typeof schemas)["notificationPushPost"]["data"]>;
|
|
138
165
|
|
|
139
|
-
export async function notificationPushPost(body: NotificationPushPostBody): Promise<
|
|
166
|
+
export async function notificationPushPost(body: NotificationPushPostBody): Promise<NotificationPushPostData> {
|
|
140
167
|
const client = ensureReq();
|
|
141
168
|
try {
|
|
142
|
-
const env = await client.post(`notification/push`, { json: body }).json<OjEnvelope<
|
|
169
|
+
const env = await client.post(`notification/push`, { json: body }).json<OjEnvelope<NotificationPushPostData>>();
|
|
143
170
|
// 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
|
|
144
171
|
if (typeof env.code === "number" && env.code !== 0)
|
|
145
172
|
throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
|
|
146
|
-
const data = env.data as
|
|
173
|
+
const data = env.data as NotificationPushPostData;
|
|
174
|
+
if (import.meta.env.DEV) {
|
|
175
|
+
const { schemas } = await import("./api.schemas");
|
|
176
|
+
const r = schemas["notificationPushPost"].data.safeParse(data);
|
|
177
|
+
if (!r.success)
|
|
178
|
+
throw new ContractApiError(-1, `[契约违例] notificationPushPost 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
|
|
179
|
+
}
|
|
147
180
|
return data;
|
|
148
181
|
}
|
|
149
182
|
catch (e) {
|
|
@@ -152,15 +185,22 @@ export async function notificationPushPost(body: NotificationPushPostBody): Prom
|
|
|
152
185
|
}
|
|
153
186
|
|
|
154
187
|
export type NotificationPushBroadcastPostBody = z.input<(typeof schemas)["notificationPushBroadcastPost"]["body"]>;
|
|
188
|
+
export type NotificationPushBroadcastPostData = z.infer<(typeof schemas)["notificationPushBroadcastPost"]["data"]>;
|
|
155
189
|
|
|
156
|
-
export async function notificationPushBroadcastPost(body: NotificationPushBroadcastPostBody): Promise<
|
|
190
|
+
export async function notificationPushBroadcastPost(body: NotificationPushBroadcastPostBody): Promise<NotificationPushBroadcastPostData> {
|
|
157
191
|
const client = ensureReq();
|
|
158
192
|
try {
|
|
159
|
-
const env = await client.post(`notification/push-broadcast`, { json: body }).json<OjEnvelope<
|
|
193
|
+
const env = await client.post(`notification/push-broadcast`, { json: body }).json<OjEnvelope<NotificationPushBroadcastPostData>>();
|
|
160
194
|
// 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
|
|
161
195
|
if (typeof env.code === "number" && env.code !== 0)
|
|
162
196
|
throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
|
|
163
|
-
const data = env.data as
|
|
197
|
+
const data = env.data as NotificationPushBroadcastPostData;
|
|
198
|
+
if (import.meta.env.DEV) {
|
|
199
|
+
const { schemas } = await import("./api.schemas");
|
|
200
|
+
const r = schemas["notificationPushBroadcastPost"].data.safeParse(data);
|
|
201
|
+
if (!r.success)
|
|
202
|
+
throw new ContractApiError(-1, `[契约违例] notificationPushBroadcastPost 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
|
|
203
|
+
}
|
|
164
204
|
return data;
|
|
165
205
|
}
|
|
166
206
|
catch (e) {
|
|
@@ -169,15 +209,22 @@ export async function notificationPushBroadcastPost(body: NotificationPushBroadc
|
|
|
169
209
|
}
|
|
170
210
|
|
|
171
211
|
export type NotificationDelPostBody = z.input<(typeof schemas)["notificationDelPost"]["body"]>;
|
|
212
|
+
export type NotificationDelPostData = z.infer<(typeof schemas)["notificationDelPost"]["data"]>;
|
|
172
213
|
|
|
173
|
-
export async function notificationDelPost(body: NotificationDelPostBody): Promise<
|
|
214
|
+
export async function notificationDelPost(body: NotificationDelPostBody): Promise<NotificationDelPostData> {
|
|
174
215
|
const client = ensureReq();
|
|
175
216
|
try {
|
|
176
|
-
const env = await client.post(`notification/del`, { json: body }).json<OjEnvelope<
|
|
217
|
+
const env = await client.post(`notification/del`, { json: body }).json<OjEnvelope<NotificationDelPostData>>();
|
|
177
218
|
// 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
|
|
178
219
|
if (typeof env.code === "number" && env.code !== 0)
|
|
179
220
|
throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
|
|
180
|
-
const data = env.data as
|
|
221
|
+
const data = env.data as NotificationDelPostData;
|
|
222
|
+
if (import.meta.env.DEV) {
|
|
223
|
+
const { schemas } = await import("./api.schemas");
|
|
224
|
+
const r = schemas["notificationDelPost"].data.safeParse(data);
|
|
225
|
+
if (!r.success)
|
|
226
|
+
throw new ContractApiError(-1, `[契约违例] notificationDelPost 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
|
|
227
|
+
}
|
|
181
228
|
return data;
|
|
182
229
|
}
|
|
183
230
|
catch (e) {
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
notificationNotificationsGet,
|
|
12
12
|
notificationNotificationsReadAllPost,
|
|
13
13
|
notificationNotificationsReadPost,
|
|
14
|
-
} from "../../__client
|
|
14
|
+
} from "../../__client";
|
|
15
15
|
|
|
16
16
|
type Item = Awaited<ReturnType<typeof notificationNotificationsGet>>[number];
|
|
17
17
|
type CatFilter = "all" | NoticeCategory;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { AppRouteRecordRaw, ModuleContext, NotificationsApiProvider } from "@oj-module/runtime";
|
|
2
|
+
|
|
3
|
+
import { BellOutlined } from "@ant-design/icons";
|
|
4
|
+
|
|
5
|
+
import { createElement, lazy } from "react";
|
|
6
|
+
|
|
7
|
+
import { createNotificationClient } from "./__client";
|
|
8
|
+
|
|
9
|
+
const NotificationCenter = lazy(() => import("./pages/center"));
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 通知中心页(N3):全量消息 + 类型 Tab,铃铛弹层「查看全部」跳转至此。
|
|
13
|
+
* hideInMenu——入口在铃铛,不占侧边导航。
|
|
14
|
+
* 布局挂父路由 handle(resolveRouteLayouts 只给「无 Component 有 children」的
|
|
15
|
+
* 父路由注入 layout,页面组件写父级上布局会被忽略、裸渲染无 chrome)。
|
|
16
|
+
*/
|
|
17
|
+
export const routes: AppRouteRecordRaw[] = [
|
|
18
|
+
{
|
|
19
|
+
path: "/notification-center",
|
|
20
|
+
handle: {
|
|
21
|
+
layout: "container",
|
|
22
|
+
title: "notification:menu.notificationCenter",
|
|
23
|
+
icon: createElement(BellOutlined),
|
|
24
|
+
hideInMenu: true,
|
|
25
|
+
},
|
|
26
|
+
children: [
|
|
27
|
+
{
|
|
28
|
+
index: true,
|
|
29
|
+
Component: NotificationCenter,
|
|
30
|
+
handle: {
|
|
31
|
+
title: "notification:menu.notificationCenter",
|
|
32
|
+
icon: createElement(BellOutlined),
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 通知聚合的唯一前端消费面(G5 模板形态)。
|
|
41
|
+
*
|
|
42
|
+
* onInit 全量接管通知 API(D9 + G4):把通知读/写收敛到本模块 `/notification`
|
|
43
|
+
* 前缀下的 oj 后端,通知铃的互动(单条已读/全部已读/清空)由此生效;发射端
|
|
44
|
+
* 三方法(push/pushBroadcast/del)使本模块同时作为跨模块通知原语的接收端
|
|
45
|
+
* (前端 facade 委托至此)。
|
|
46
|
+
*/
|
|
47
|
+
export async function onInit(ctx: ModuleContext) {
|
|
48
|
+
// 构造即接线:createNotificationClient 登记契约前缀 /notification
|
|
49
|
+
// (唯一真源在 __client/api.ts 的 API_PREFIX)+ 绑定圈好前缀的 scoped
|
|
50
|
+
// request(越界/穿越发请求前即拒绝,前缀收敛 D11),返回的 client
|
|
51
|
+
// 方法都自动带 /api/notification/ 前缀。
|
|
52
|
+
const notificationClient = createNotificationClient(ctx);
|
|
53
|
+
const provider: NotificationsApiProvider = {
|
|
54
|
+
// 端点函数名由 oj operationId 派生(camelCase 子路径 + 方法),
|
|
55
|
+
// 见 __client/api.ts;provider 方法名(fetchNotifications/markRead…)不变。
|
|
56
|
+
fetchNotifications: () => notificationClient.notificationNotificationsGet(),
|
|
57
|
+
// id 为命名空间串("u:<id>" / "b:<id>"),原样透传
|
|
58
|
+
markRead: id => notificationClient.notificationNotificationsReadPost({ id: String(id) }).then(() => {}),
|
|
59
|
+
markAllRead: () => notificationClient.notificationNotificationsReadAllPost().then(() => {}),
|
|
60
|
+
clearAll: () => notificationClient.notificationNotificationsClearPost().then(() => {}),
|
|
61
|
+
push: input => notificationClient
|
|
62
|
+
.notificationPushPost({ userId: input.userId, title: input.title, message: input.message, link: input.link, avatar: input.avatar, category: input.category })
|
|
63
|
+
.then(r => String((r as { id: string }).id)),
|
|
64
|
+
pushBroadcast: input => notificationClient
|
|
65
|
+
.notificationPushBroadcastPost({ title: input.title, message: input.message, link: input.link, avatar: input.avatar, category: input.category })
|
|
66
|
+
.then(r => String((r as { id: string }).id)),
|
|
67
|
+
del: (id, userId) => notificationClient.notificationDelPost({ id: String(id), userId }).then(() => {}),
|
|
68
|
+
};
|
|
69
|
+
ctx.register.notificationsApi(provider);
|
|
70
|
+
}
|
|
@@ -17,6 +17,9 @@ export const API_PREFIX = "/personal-center";
|
|
|
17
17
|
/** 认领身份前缀(契约 apiPrefix,字面等于模块目录名):跨模块认领护栏对比基准 */
|
|
18
18
|
export const MODULE_PREFIX = "/personal-center";
|
|
19
19
|
|
|
20
|
+
/** 认领方模块(web.config clientOwners 指定):跨模块认领护栏对比基准 */
|
|
21
|
+
export const MODULE_OWNER = "/app";
|
|
22
|
+
|
|
20
23
|
/**
|
|
21
24
|
* 模块 entry 的 onInit 里一行创建(构造即 install):完成 apiPrefix 登记 +
|
|
22
25
|
* scoped request 绑定,返回绑好前缀的端点函数集合。一模块一 client;
|
|
@@ -28,8 +31,8 @@ export function createPersonalCenterClient(ctx: ModuleContext) {
|
|
|
28
31
|
// 单槽、卸载互相耦合;跨模块需求应由本模块创建后经 provider 暴露。
|
|
29
32
|
// 注意对比基准是 MODULE_PREFIX(目录名)而非 API_PREFIX——D-M8 映射时线上前缀
|
|
30
33
|
// 属于别的命名空间,认领身份仍按目录名。
|
|
31
|
-
if (import.meta.env.DEV && ctx.module.name !==
|
|
32
|
-
console.warn(`[ojm-api] 模块 "${ctx.module.name}" 正在认领 ${MODULE_PREFIX} 的 client——跨模块认领会共享 request 单槽、卸载互相耦合;应由 "${
|
|
34
|
+
if (import.meta.env.DEV && ctx.module.name !== MODULE_OWNER.slice(1)) {
|
|
35
|
+
console.warn(`[ojm-api] 模块 "${ctx.module.name}" 正在认领 ${MODULE_PREFIX} 的 client——跨模块认领会共享 request 单槽、卸载互相耦合;应由 "${MODULE_OWNER.slice(1)}" 模块在自身 onInit 创建,跨模块需求经 provider 暴露。`);
|
|
33
36
|
}
|
|
34
37
|
ctx.register.apiPrefix(API_PREFIX);
|
|
35
38
|
bindRequest(ctx.utils.request);
|
package/templates/src/{personal-center/web → app/web/personal-center}/pages/my-profile/index.tsx
RENAMED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
import { BasicContent, FormAvatarItem, useUserStore } from "@oj-module/runtime";
|
|
8
8
|
import { App, Form, Input } from "antd";
|
|
9
9
|
|
|
10
|
-
import { personalCenterProfilePost } from "../../__client
|
|
10
|
+
import { personalCenterProfilePost } from "../../__client";
|
|
11
11
|
|
|
12
12
|
export default function Profile() {
|
|
13
13
|
const currentUser = useUserStore();
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { AppRouteRecordRaw, ModuleContext, UploadApiProvider } from "@oj-module/runtime";
|
|
2
|
+
import { ProfileCardIcon, RiAccountCircleLine, useAuthStore } from "@oj-module/runtime";
|
|
3
|
+
|
|
4
|
+
import { createElement, lazy } from "react";
|
|
5
|
+
|
|
6
|
+
import { createPersonalCenterClient } from "./__client";
|
|
7
|
+
|
|
8
|
+
const MyProfile = lazy(() => import("./pages/my-profile"));
|
|
9
|
+
|
|
10
|
+
/** 个人中心根 `/personal-center`:runtime 用户菜单固定导航 `/personal-center/my-profile`,缺则点击落空。 */
|
|
11
|
+
export const routes: AppRouteRecordRaw[] = [
|
|
12
|
+
{
|
|
13
|
+
path: "/personal-center",
|
|
14
|
+
handle: {
|
|
15
|
+
layout: "container",
|
|
16
|
+
order: 110,
|
|
17
|
+
title: "personal-center:menu.personalCenter",
|
|
18
|
+
icon: createElement(RiAccountCircleLine),
|
|
19
|
+
},
|
|
20
|
+
children: [
|
|
21
|
+
{
|
|
22
|
+
path: "/personal-center/my-profile",
|
|
23
|
+
Component: MyProfile,
|
|
24
|
+
handle: {
|
|
25
|
+
title: "personal-center:menu.profile",
|
|
26
|
+
icon: createElement(ProfileCardIcon),
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* P4-3:接管头像上传端点(D9)。antd Upload 直连 action,headers 在上传瞬间
|
|
35
|
+
* 取当前 token 注入 Bearer(token 存 runtime auth store,跨渲染期读 getState)。
|
|
36
|
+
*/
|
|
37
|
+
export async function onInit(ctx: ModuleContext) {
|
|
38
|
+
// 构造即接线:登记契约前缀 /personal-center + 绑定 scoped request,
|
|
39
|
+
// 页面直接 import 的 personalCenterProfilePost 才有可用的请求通道
|
|
40
|
+
createPersonalCenterClient(ctx);
|
|
41
|
+
const provider: UploadApiProvider = {
|
|
42
|
+
action: "/api/personal-center/upload",
|
|
43
|
+
headers: (): Record<string, string> => {
|
|
44
|
+
const token = useAuthStore.getState().token;
|
|
45
|
+
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
ctx.register.uploadApi(provider);
|
|
49
|
+
}
|
|
@@ -48,17 +48,6 @@ export const schemas = {
|
|
|
48
48
|
}),
|
|
49
49
|
data: z.object({
|
|
50
50
|
"ok": z.boolean()
|
|
51
|
-
}),
|
|
52
|
-
},
|
|
53
|
-
"systemDepartmentsMovePut": {
|
|
54
|
-
query: z.object({
|
|
55
|
-
"id": z.number()
|
|
56
|
-
}),
|
|
57
|
-
body: z.object({
|
|
58
|
-
"parentId": z.number().nullable()
|
|
59
|
-
}),
|
|
60
|
-
data: z.object({
|
|
61
|
-
"ok": z.boolean()
|
|
62
51
|
}),
|
|
63
52
|
},
|
|
64
53
|
"systemDepartmentsDetachPost": {
|
|
@@ -118,6 +107,17 @@ export const schemas = {
|
|
|
118
107
|
}),
|
|
119
108
|
data: z.object({
|
|
120
109
|
"ok": z.boolean()
|
|
110
|
+
}),
|
|
111
|
+
},
|
|
112
|
+
"systemDepartmentsMovePut": {
|
|
113
|
+
query: z.object({
|
|
114
|
+
"id": z.number()
|
|
115
|
+
}),
|
|
116
|
+
body: z.object({
|
|
117
|
+
"parentId": z.number().nullable()
|
|
118
|
+
}),
|
|
119
|
+
data: z.object({
|
|
120
|
+
"ok": z.boolean()
|
|
121
121
|
}),
|
|
122
122
|
},
|
|
123
123
|
"systemLoginMethodsGet": {
|
|
@@ -130,6 +130,7 @@ export const schemas = {
|
|
|
130
130
|
"action": z.string().optional(),
|
|
131
131
|
"username": z.string().optional(),
|
|
132
132
|
"module": z.string().optional(),
|
|
133
|
+
"page": z.number().min(1).optional(),
|
|
133
134
|
"limit": z.number().min(1).max(200).optional()
|
|
134
135
|
}),
|
|
135
136
|
data: z.object({
|
|
@@ -197,6 +198,7 @@ export const schemas = {
|
|
|
197
198
|
query: z.object({
|
|
198
199
|
"name": z.string().min(1)
|
|
199
200
|
}),
|
|
201
|
+
data: z.null(),
|
|
200
202
|
},
|
|
201
203
|
"systemRolesBindingsPut": {
|
|
202
204
|
query: z.object({
|
|
@@ -205,6 +207,7 @@ export const schemas = {
|
|
|
205
207
|
body: z.object({
|
|
206
208
|
"permissions": z.array(z.string())
|
|
207
209
|
}),
|
|
210
|
+
data: z.null(),
|
|
208
211
|
},
|
|
209
212
|
"systemSettingsGet": {
|
|
210
213
|
data: z.array(z.object({
|
|
@@ -228,6 +231,7 @@ export const schemas = {
|
|
|
228
231
|
"key": z.string().min(1),
|
|
229
232
|
"value": z.string()
|
|
230
233
|
}),
|
|
234
|
+
data: z.null(),
|
|
231
235
|
},
|
|
232
236
|
"systemUsersGet": {
|
|
233
237
|
data: z.array(z.object({
|
|
@@ -265,33 +269,35 @@ export const schemas = {
|
|
|
265
269
|
"ok": z.boolean()
|
|
266
270
|
}),
|
|
267
271
|
},
|
|
268
|
-
"
|
|
272
|
+
"systemUsersForbiddenPut": {
|
|
269
273
|
query: z.object({
|
|
270
274
|
"uid": z.number()
|
|
271
275
|
}),
|
|
272
276
|
body: z.object({
|
|
273
|
-
"
|
|
274
|
-
"email": z.string().nullable().optional(),
|
|
275
|
-
"position": z.string().nullable().optional()
|
|
276
|
-
}),
|
|
277
|
-
data: z.object({
|
|
278
|
-
"ok": z.boolean()
|
|
277
|
+
"forbidden": z.boolean()
|
|
279
278
|
}),
|
|
279
|
+
data: z.null(),
|
|
280
280
|
},
|
|
281
|
-
"
|
|
281
|
+
"systemUsersPasswordPut": {
|
|
282
282
|
query: z.object({
|
|
283
283
|
"uid": z.number()
|
|
284
284
|
}),
|
|
285
285
|
body: z.object({
|
|
286
|
-
"
|
|
286
|
+
"password": z.string().min(6)
|
|
287
287
|
}),
|
|
288
|
+
data: z.null(),
|
|
288
289
|
},
|
|
289
|
-
"
|
|
290
|
+
"systemUsersProfilePut": {
|
|
290
291
|
query: z.object({
|
|
291
292
|
"uid": z.number()
|
|
292
293
|
}),
|
|
293
294
|
body: z.object({
|
|
294
|
-
"
|
|
295
|
+
"displayName": z.string().nullable().optional(),
|
|
296
|
+
"email": z.string().nullable().optional(),
|
|
297
|
+
"position": z.string().nullable().optional()
|
|
298
|
+
}),
|
|
299
|
+
data: z.object({
|
|
300
|
+
"ok": z.boolean()
|
|
295
301
|
}),
|
|
296
302
|
},
|
|
297
303
|
"systemUsersRolesPut": {
|
|
@@ -301,5 +307,6 @@ export const schemas = {
|
|
|
301
307
|
body: z.object({
|
|
302
308
|
"roles": z.array(z.string())
|
|
303
309
|
}),
|
|
310
|
+
data: z.null(),
|
|
304
311
|
},
|
|
305
312
|
};
|