@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.
Files changed (330) hide show
  1. package/devkit/SKILL.md +4 -4
  2. package/devkit/manual.md +79 -65
  3. package/package.json +2 -2
  4. package/shell-dist/assets/{index-e_Mp2mb5.js → index-CImwskAz.js} +24 -11
  5. package/shell-dist/assets/runtime.js +7 -4
  6. package/shell-dist/index.html +3 -3
  7. package/shell-dist/versions.json +3 -3
  8. package/src/__tests__/layout.test.ts +30 -1
  9. package/src/__tests__/templates-schema-columns.test.ts +99 -0
  10. package/src/__tests__/templates-tenant-scope.test.ts +3 -2
  11. package/src/alias-paths.ts +11 -12
  12. package/src/builtin.ts +10 -3
  13. package/src/config.ts +9 -0
  14. package/src/contract/__tests__/client-path.test.ts +30 -0
  15. package/src/contract/__tests__/emit-client.test.ts +20 -2
  16. package/src/contract/__tests__/handler-scan.test.ts +23 -0
  17. package/src/contract/__tests__/market-client.integration.test.ts +2 -2
  18. package/src/contract/__tests__/run-api.test.ts +2 -2
  19. package/src/contract/check.ts +11 -7
  20. package/src/contract/client-path.ts +30 -0
  21. package/src/contract/emit-client.ts +27 -6
  22. package/src/contract/handler-scan.ts +40 -3
  23. package/src/contract/run.ts +15 -5
  24. package/src/init.ts +29 -3
  25. package/src/layout.ts +72 -16
  26. package/src/lifecycle/__tests__/manifest-contract.test.ts +2 -2
  27. package/src/market/__tests__/bootstrap.test.ts +1 -1
  28. package/src/site.ts +1 -1
  29. package/templates/README.md +49 -43
  30. package/templates/init.include.yaml +31 -19
  31. package/templates/openapi.json +1488 -120
  32. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/users.ts +27 -0
  33. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/oplog.ts +4 -2
  34. package/templates/src/{_platform/api → app/api/_platform}/contacts/api.ts +29 -1
  35. package/templates/src/{_platform/api → app/api/_platform}/contacts/detail/api.ts +25 -1
  36. package/templates/src/app/api/_platform/migrations/0017__users_employee_no.mysql.sql +7 -0
  37. package/templates/src/app/api/_platform/migrations/0017__users_employee_no.pg.sql +4 -0
  38. package/templates/src/app/api/_platform/migrations/0017__users_employee_no.sql +8 -0
  39. package/templates/src/{_platform/api → app/api/_platform}/schema.yaml +5 -0
  40. package/templates/src/{_platform/api → app/api/_platform}/solutions/api.ts +60 -1
  41. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/replay.ts +9 -3
  42. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/tenant-admin.ts +18 -27
  43. package/templates/src/{auth/api → app/api/auth}/api.ts +59 -0
  44. package/templates/src/{auth/api → app/api/auth}/ldap-login/api.ts +37 -1
  45. package/templates/src/{auth/api → app/api/auth}/wecom/api.ts +23 -0
  46. package/templates/src/{notification/api → app/api/notification}/api.ts +17 -0
  47. package/templates/src/{notification/api → app/api/notification}/del/api.ts +2 -0
  48. package/templates/src/{system/api → app/api/system}/oplog/api.ts +5 -3
  49. package/templates/src/{system/api → app/api/system}/roles/api.ts +3 -0
  50. package/templates/src/{system/api → app/api/system}/roles/bindings/api.ts +3 -0
  51. package/templates/src/{system/api → app/api/system}/settings/api.ts +3 -0
  52. package/templates/src/{system/api → app/api/system}/users/forbidden/api.ts +3 -0
  53. package/templates/src/{system/api → app/api/system}/users/password/api.ts +3 -0
  54. package/templates/src/{system/api → app/api/system}/users/roles/api.ts +3 -0
  55. package/templates/src/app/api/web/get-async-routes/api.ts +23 -0
  56. package/templates/src/app/api/web/hello/api.ts +22 -0
  57. package/templates/src/{web/api → app/api/web}/user-info/api.ts +21 -1
  58. package/templates/src/{_platform/web → app/web/_platform}/__client/api.schemas.ts +99 -35
  59. package/templates/src/{_platform/web/__client/api.ts → app/web/_platform/__client/index.ts} +108 -61
  60. package/templates/src/{auth/web → app/web/auth}/__client/api.schemas.ts +133 -13
  61. package/templates/src/{auth/web/__client/api.ts → app/web/auth/__client/index.ts} +134 -33
  62. package/templates/src/app/web/entry.ts +77 -0
  63. package/templates/src/{home/web/__client/api.ts → app/web/home/__client/index.ts} +5 -2
  64. package/templates/src/app/web/home/routes.ts +41 -0
  65. package/templates/src/app/web/login/routes.ts +71 -0
  66. package/templates/src/{notification/web → app/web/notification}/__client/api.schemas.ts +14 -0
  67. package/templates/src/{notification/web/__client/api.ts → app/web/notification/__client/index.ts} +67 -20
  68. package/templates/src/{notification/web → app/web/notification}/pages/center/index.tsx +1 -1
  69. package/templates/src/app/web/notification/routes.ts +70 -0
  70. package/templates/src/{personal-center/web/__client/api.ts → app/web/personal-center/__client/index.ts} +5 -2
  71. package/templates/src/{personal-center/web → app/web/personal-center}/pages/my-profile/index.tsx +1 -1
  72. package/templates/src/app/web/personal-center/routes.ts +49 -0
  73. package/templates/src/{system/web → app/web/system}/__client/api.schemas.ts +29 -22
  74. package/templates/src/{system/web/__client/api.ts → app/web/system/__client/index.ts} +103 -58
  75. package/templates/src/{system/web → app/web/system}/locales/en-US.json +2 -3
  76. package/templates/src/{system/web → app/web/system}/locales/zh-CN.json +2 -3
  77. package/templates/src/{system/web → app/web/system}/pages/departments/index.tsx +2 -2
  78. package/templates/src/{system/web → app/web/system}/pages/login-methods/index.tsx +1 -1
  79. package/templates/src/{system/web → app/web/system}/pages/oplog/index.tsx +44 -19
  80. package/templates/src/{system/web → app/web/system}/pages/roles/index.tsx +5 -3
  81. package/templates/src/app/web/system/pages/roles/visible-roles.ts +14 -0
  82. package/templates/src/{system/web → app/web/system}/pages/settings/index.tsx +2 -2
  83. package/templates/src/app/web/system/pages/users/can-manage.ts +21 -0
  84. package/templates/src/{system/web → app/web/system}/pages/users/index.tsx +23 -15
  85. package/templates/src/{system/web → app/web/system}/pages/users/roles.tsx +1 -1
  86. package/templates/src/{system/web → app/web/system}/pages/wecom/index.tsx +12 -3
  87. package/templates/src/{system/web/entry.ts → app/web/system/routes.ts} +25 -36
  88. package/templates/src/app/web/web/__client/api.schemas.ts +36 -0
  89. package/templates/src/{web/web/__client/api.ts → app/web/web/__client/index.ts} +38 -11
  90. package/templates/src/oj-market/web/api/defaultClient.ts +1 -1
  91. package/templates/tests/auth-ldap.spec.ts +2 -2
  92. package/templates/tests/auth-session.spec.ts +3 -3
  93. package/templates/tests/auth-throttle.spec.ts +1 -1
  94. package/templates/tests/fixture.ts +6 -4
  95. package/templates/tests/guard.spec.ts +1 -1
  96. package/templates/tests/lifecycle/atomic-state.spec.ts +3 -3
  97. package/templates/tests/lifecycle/bootstrap.spec.ts +3 -3
  98. package/templates/tests/lifecycle/deploy-smoke.spec.ts +8 -8
  99. package/templates/tests/lifecycle/installer.spec.ts +10 -10
  100. package/templates/tests/lifecycle/market-api.spec.ts +14 -14
  101. package/templates/tests/lifecycle/migrate.spec.ts +6 -6
  102. package/templates/tests/lifecycle/preflight.spec.ts +3 -3
  103. package/templates/tests/lifecycle/register.spec.ts +1 -1
  104. package/templates/tests/lifecycle/semver.spec.ts +1 -1
  105. package/templates/tests/lifecycle/sha384.spec.ts +1 -1
  106. package/templates/tests/lifecycle/solutions-api.spec.ts +1 -1
  107. package/templates/tests/lifecycle/source-seq.spec.ts +1 -1
  108. package/templates/tests/lifecycle/steps-fs.spec.ts +9 -9
  109. package/templates/tests/lifecycle/text-edit.spec.ts +1 -1
  110. package/templates/tests/lifecycle/tgz-guard.spec.ts +2 -2
  111. package/templates/tests/lifecycle/trust.spec.ts +5 -5
  112. package/templates/tests/lifecycle/uninstall.spec.ts +7 -7
  113. package/templates/tests/lifecycle/verify-checks.spec.ts +5 -5
  114. package/templates/tests/lifecycle/verify-report.spec.ts +4 -4
  115. package/templates/tests/org-departments.spec.ts +10 -6
  116. package/templates/tests/org-read.spec.ts +7 -7
  117. package/templates/tests/query-utils.spec.ts +1 -1
  118. package/templates/tests/rbac-audit.spec.ts +1 -1
  119. package/templates/tests/rbac-contacts.spec.ts +2 -2
  120. package/templates/tests/rbac-guard.spec.ts +1 -1
  121. package/templates/tests/rbac-roles.spec.ts +10 -2
  122. package/templates/tests/rbac-seed.spec.ts +1 -1
  123. package/templates/tests/rbac-settings.spec.ts +77 -7
  124. package/templates/tests/rbac-solution-perms.spec.ts +1 -1
  125. package/templates/tests/rbac-tables.spec.ts +29 -0
  126. package/templates/tests/rbac-users.spec.ts +55 -3
  127. package/templates/tests/roles-page.spec.ts +40 -0
  128. package/templates/tests/seed-role-accounts.spec.ts +1 -1
  129. package/templates/tests/tenant-access-cache.spec.ts +1 -1
  130. package/templates/tests/tenant-forgery.spec.ts +5 -5
  131. package/templates/tests/tenant-isolation.spec.ts +5 -4
  132. package/templates/tests/username-namespace.spec.ts +3 -3
  133. package/templates/tests/users-page.spec.ts +39 -0
  134. package/templates/tests/wecom-admin.spec.ts +6 -6
  135. package/templates/tests/wecom-client-timeout.spec.ts +1 -1
  136. package/templates/tests/wecom-connector.spec.ts +9 -9
  137. package/templates/tests/wecom-corp.spec.ts +9 -9
  138. package/templates/tests/wecom-crypt.spec.ts +1 -1
  139. package/templates/tests/wecom-endpoints.spec.ts +2 -2
  140. package/templates/tests/wecom-landing.spec.ts +1 -1
  141. package/templates/tests/wecom-org-sync.spec.ts +7 -7
  142. package/templates/tests/wecom-sync.spec.ts +3 -3
  143. package/templates/tsconfig.paths.json +11 -11
  144. package/templates/vitest.config.ts +2 -2
  145. package/templates/web.config.ts +24 -15
  146. package/vendor/host-versions.json +3 -3
  147. package/templates/src/home/web/entry.ts +0 -49
  148. package/templates/src/login/web/entry.ts +0 -79
  149. package/templates/src/notification/web/entry.ts +0 -84
  150. package/templates/src/personal-center/web/entry.ts +0 -60
  151. package/templates/src/web/api/get-async-routes/api.ts +0 -8
  152. package/templates/src/web/api/hello/api.ts +0 -10
  153. package/templates/src/web/web/__client/api.schemas.ts +0 -8
  154. package/templates/src/{_platform/api → app/api/_platform}/README.md +0 -0
  155. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/atomic.ts +0 -0
  156. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/history.ts +0 -0
  157. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/installer.ts +0 -0
  158. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/limits.ts +0 -0
  159. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/lock.ts +0 -0
  160. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/semver.ts +0 -0
  161. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/sha384.ts +0 -0
  162. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/source-seq.ts +0 -0
  163. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/state.ts +0 -0
  164. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/deploy-web.ts +0 -0
  165. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/download.ts +0 -0
  166. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/extract.ts +0 -0
  167. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/migrate.ts +0 -0
  168. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/preflight.ts +0 -0
  169. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/register.ts +0 -0
  170. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/reload.ts +0 -0
  171. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/smoke.ts +0 -0
  172. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/verify.ts +0 -0
  173. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/tasks.ts +0 -0
  174. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/text-edit.ts +0 -0
  175. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/tgz-guard.ts +0 -0
  176. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/trust.ts +0 -0
  177. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/types.ts +0 -0
  178. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/uninstall.ts +0 -0
  179. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/verify-checks.ts +0 -0
  180. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/verify-report.ts +0 -0
  181. package/templates/src/{_platform/api → app/api/_platform}/_shared/respond.ts +0 -0
  182. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/contacts.ts +0 -0
  183. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/org-read.ts +0 -0
  184. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/org.ts +0 -0
  185. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/roles.ts +0 -0
  186. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/settings.ts +0 -0
  187. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/solution-perms.ts +0 -0
  188. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/access.ts +0 -0
  189. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/audit.ts +0 -0
  190. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/guard.ts +0 -0
  191. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/query.ts +0 -0
  192. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/rate.ts +0 -0
  193. package/templates/src/{_platform/api → app/api/_platform}/_shared/site-facade.ts +0 -0
  194. package/templates/src/{_platform/api → app/api/_platform}/fixtures/l1-test-user.sql +0 -0
  195. package/templates/src/{_platform/api → app/api/_platform}/manifest.yaml +0 -0
  196. package/templates/src/{_platform/api → app/api/_platform}/market/api.ts +0 -0
  197. package/templates/src/{_platform/api → app/api/_platform}/market/bootstrap.ts +0 -0
  198. package/templates/src/{_platform/api → app/api/_platform}/market/index-source.ts +0 -0
  199. package/templates/src/{_platform/api → app/api/_platform}/migrations/0001__create_users.mysql.sql +0 -0
  200. package/templates/src/{_platform/api → app/api/_platform}/migrations/0001__create_users.pg.sql +0 -0
  201. package/templates/src/{_platform/api → app/api/_platform}/migrations/0001__create_users.sql +0 -0
  202. package/templates/src/{_platform/api → app/api/_platform}/migrations/0002__add_users_avatar_base64.mysql.sql +0 -0
  203. package/templates/src/{_platform/api → app/api/_platform}/migrations/0002__add_users_avatar_base64.sql +0 -0
  204. package/templates/src/{_platform/api → app/api/_platform}/migrations/0003__users_source_display_name.mysql.sql +0 -0
  205. package/templates/src/{_platform/api → app/api/_platform}/migrations/0003__users_source_display_name.sql +0 -0
  206. package/templates/src/{_platform/api → app/api/_platform}/migrations/0004__sys_rbac.mysql.sql +0 -0
  207. package/templates/src/{_platform/api → app/api/_platform}/migrations/0004__sys_rbac.pg.sql +0 -0
  208. package/templates/src/{_platform/api → app/api/_platform}/migrations/0004__sys_rbac.sql +0 -0
  209. package/templates/src/{_platform/api → app/api/_platform}/migrations/0005__users_must_change.mysql.sql +0 -0
  210. package/templates/src/{_platform/api → app/api/_platform}/migrations/0005__users_must_change.sql +0 -0
  211. package/templates/src/{_platform/api → app/api/_platform}/migrations/0006__users_is_forbidden.mysql.sql +0 -0
  212. package/templates/src/{_platform/api → app/api/_platform}/migrations/0006__users_is_forbidden.sql +0 -0
  213. package/templates/src/{_platform/api → app/api/_platform}/migrations/0007__users_email.sql +0 -0
  214. package/templates/src/{_platform/api → app/api/_platform}/migrations/0008__sys_refresh_token.mysql.sql +0 -0
  215. package/templates/src/{_platform/api → app/api/_platform}/migrations/0008__sys_refresh_token.pg.sql +0 -0
  216. package/templates/src/{_platform/api → app/api/_platform}/migrations/0008__sys_refresh_token.sql +0 -0
  217. package/templates/src/{_platform/api → app/api/_platform}/migrations/0009__sys_throttle.mysql.sql +0 -0
  218. package/templates/src/{_platform/api → app/api/_platform}/migrations/0009__sys_throttle.pg.sql +0 -0
  219. package/templates/src/{_platform/api → app/api/_platform}/migrations/0009__sys_throttle.sql +0 -0
  220. package/templates/src/{_platform/api → app/api/_platform}/migrations/0010__sys_solution_route.mysql.sql +0 -0
  221. package/templates/src/{_platform/api → app/api/_platform}/migrations/0010__sys_solution_route.pg.sql +0 -0
  222. package/templates/src/{_platform/api → app/api/_platform}/migrations/0010__sys_solution_route.sql +0 -0
  223. package/templates/src/{_platform/api → app/api/_platform}/migrations/0011__sys_setting_group_label.mysql.sql +0 -0
  224. package/templates/src/{_platform/api → app/api/_platform}/migrations/0011__sys_setting_group_label.sql +0 -0
  225. package/templates/src/{_platform/api → app/api/_platform}/migrations/0012__market_history.mysql.sql +0 -0
  226. package/templates/src/{_platform/api → app/api/_platform}/migrations/0012__market_history.pg.sql +0 -0
  227. package/templates/src/{_platform/api → app/api/_platform}/migrations/0012__market_history.sql +0 -0
  228. package/templates/src/{_platform/api → app/api/_platform}/migrations/0013__users_profile.sql +0 -0
  229. package/templates/src/{_platform/api → app/api/_platform}/migrations/0014__reg_source_seq.mysql.sql +0 -0
  230. package/templates/src/{_platform/api → app/api/_platform}/migrations/0014__reg_source_seq.pg.sql +0 -0
  231. package/templates/src/{_platform/api → app/api/_platform}/migrations/0014__reg_source_seq.sql +0 -0
  232. package/templates/src/{_platform/api → app/api/_platform}/migrations/0015__tenant.mysql.sql +0 -0
  233. package/templates/src/{_platform/api → app/api/_platform}/migrations/0015__tenant.pg.sql +0 -0
  234. package/templates/src/{_platform/api → app/api/_platform}/migrations/0015__tenant.sql +0 -0
  235. package/templates/src/{_platform/api → app/api/_platform}/migrations/0016__org.mysql.sql +0 -0
  236. package/templates/src/{_platform/api → app/api/_platform}/migrations/0016__org.pg.sql +0 -0
  237. package/templates/src/{_platform/api → app/api/_platform}/migrations/0016__org.sql +0 -0
  238. package/templates/src/{_platform/api → app/api/_platform}/org/departments/api.ts +0 -0
  239. package/templates/src/{_platform/api → app/api/_platform}/org/is-member/api.ts +0 -0
  240. package/templates/src/{_platform/api → app/api/_platform}/org/members/api.ts +0 -0
  241. package/templates/src/{_platform/api → app/api/_platform}/org/picker/api.ts +0 -0
  242. package/templates/src/{_platform/api → app/api/_platform}/org/resolve/api.ts +0 -0
  243. package/templates/src/{_platform/api → app/api/_platform}/org/tree/api.ts +0 -0
  244. package/templates/src/{_platform/api → app/api/_platform}/org/user/api.ts +0 -0
  245. package/templates/src/{_platform/api → app/api/_platform}/seed.mysql.sql +0 -0
  246. package/templates/src/{_platform/api → app/api/_platform}/seed.pg.sql +0 -0
  247. package/templates/src/{_platform/api → app/api/_platform}/seed.sql +0 -0
  248. package/templates/src/{auth/api → app/api/auth}/_shared/ldap-login.ts +0 -0
  249. package/templates/src/{auth/api → app/api/auth}/_shared/ldap.ts +0 -0
  250. package/templates/src/{auth/api → app/api/auth}/_shared/login-throttle.ts +0 -0
  251. package/templates/src/{auth/api → app/api/auth}/_shared/session.ts +0 -0
  252. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/admin.ts +1 -1
  253. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/client.ts +0 -0
  254. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/config.ts +0 -0
  255. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/contacts.ts +0 -0
  256. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/crypt.ts +0 -0
  257. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/events.ts +1 -1
  258. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/login.ts +0 -0
  259. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/payload.ts +0 -0
  260. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/store.ts +0 -0
  261. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/sync.ts +0 -0
  262. package/templates/src/{auth/api → app/api/auth}/manifest.yaml +0 -0
  263. package/templates/src/{auth/api → app/api/auth}/migrations/0001__wecom.mysql.sql +0 -0
  264. package/templates/src/{auth/api → app/api/auth}/migrations/0001__wecom.pg.sql +0 -0
  265. package/templates/src/{auth/api → app/api/auth}/migrations/0001__wecom.sql +0 -0
  266. package/templates/src/{auth/api → app/api/auth}/migrations/0002__org_sync.mysql.sql +0 -0
  267. package/templates/src/{auth/api → app/api/auth}/migrations/0002__org_sync.pg.sql +0 -0
  268. package/templates/src/{auth/api → app/api/auth}/migrations/0002__org_sync.sql +0 -0
  269. package/templates/src/{auth/api → app/api/auth}/migrations/0003__credential_kind.mysql.sql +0 -0
  270. package/templates/src/{auth/api → app/api/auth}/migrations/0003__credential_kind.pg.sql +0 -0
  271. package/templates/src/{auth/api → app/api/auth}/migrations/0003__credential_kind.sql +0 -0
  272. package/templates/src/{auth/api → app/api/auth}/schema.yaml +0 -0
  273. package/templates/src/{auth/api → app/api/auth}/wecom/admin/api.ts +0 -0
  274. package/templates/src/{auth/api → app/api/auth}/wecom/admin/tenant-admin/api.ts +1 -1
  275. package/templates/src/{auth/api → app/api/auth}/wecom/admin/tenant-admin/password/api.ts +1 -1
  276. /package/templates/src/{auth/api → app/api/auth}/wecom/sync/api.ts +0 -0
  277. /package/templates/src/{home/api → app/api/home}/line/api.ts +0 -0
  278. /package/templates/src/{home/api → app/api/home}/manifest.yaml +0 -0
  279. /package/templates/src/{home/api → app/api/home}/migrations/0001__create_home_tables.sql +0 -0
  280. /package/templates/src/{home/api → app/api/home}/migrations/0002__tenant.mysql.sql +0 -0
  281. /package/templates/src/{home/api → app/api/home}/migrations/0002__tenant.sql +0 -0
  282. /package/templates/src/{home/api → app/api/home}/pie/api.ts +0 -0
  283. /package/templates/src/{home/api → app/api/home}/schema.yaml +0 -0
  284. /package/templates/src/{home/api → app/api/home}/seed.sql +0 -0
  285. /package/templates/src/{notification/api → app/api/notification}/_shared/notify.ts +0 -0
  286. /package/templates/src/{notification/api → app/api/notification}/manifest.yaml +0 -0
  287. /package/templates/src/{notification/api → app/api/notification}/migrations/0001__create_notifications.sql +0 -0
  288. /package/templates/src/{notification/api → app/api/notification}/migrations/0002__notifications_user.sql +0 -0
  289. /package/templates/src/{notification/api → app/api/notification}/migrations/0003__notifications_broadcast.sql +0 -0
  290. /package/templates/src/{notification/api → app/api/notification}/migrations/0004__notifications_broadcast_hidden.sql +0 -0
  291. /package/templates/src/{notification/api → app/api/notification}/migrations/0005__tenant.mysql.sql +0 -0
  292. /package/templates/src/{notification/api → app/api/notification}/migrations/0005__tenant.sql +0 -0
  293. /package/templates/src/{notification/api → app/api/notification}/schema.yaml +0 -0
  294. /package/templates/src/{notification/api → app/api/notification}/seed.sql +0 -0
  295. /package/templates/src/{personal-center/api → app/api/personal-center}/manifest.yaml +0 -0
  296. /package/templates/src/{personal-center/api → app/api/personal-center}/profile/api.ts +0 -0
  297. /package/templates/src/{personal-center/api → app/api/personal-center}/upload/api.ts +0 -0
  298. /package/templates/src/{system/api → app/api/system}/departments/api.ts +0 -0
  299. /package/templates/src/{system/api → app/api/system}/departments/detach/api.ts +0 -0
  300. /package/templates/src/{system/api → app/api/system}/departments/members/api.ts +0 -0
  301. /package/templates/src/{system/api → app/api/system}/departments/move/api.ts +0 -0
  302. /package/templates/src/{system/api → app/api/system}/login-methods/api.ts +0 -0
  303. /package/templates/src/{system/api → app/api/system}/manifest.yaml +0 -0
  304. /package/templates/src/{system/api → app/api/system}/permissions/api.ts +0 -0
  305. /package/templates/src/{system/api → app/api/system}/users/api.ts +0 -0
  306. /package/templates/src/{system/api → app/api/system}/users/profile/api.ts +0 -0
  307. /package/templates/src/{web/api → app/api/web}/manifest.yaml +0 -0
  308. /package/templates/src/{home/web → app/web/home}/__client/api.schemas.ts +0 -0
  309. /package/templates/src/{home/web → app/web/home}/locales/en-US.json +0 -0
  310. /package/templates/src/{home/web → app/web/home}/locales/zh-CN.json +0 -0
  311. /package/templates/src/{home/web → app/web/home}/pages/components/bar-chart.tsx +0 -0
  312. /package/templates/src/{home/web → app/web/home}/pages/components/card-list.tsx +0 -0
  313. /package/templates/src/{home/web → app/web/home}/pages/components/line-chart.tsx +0 -0
  314. /package/templates/src/{home/web → app/web/home}/pages/components/pie-chart.tsx +0 -0
  315. /package/templates/src/{home/web → app/web/home}/pages/index.tsx +0 -0
  316. /package/templates/src/{login/web → app/web/login}/auth-provider.ts +0 -0
  317. /package/templates/src/{login/web → app/web/login}/locales/en-US.json +0 -0
  318. /package/templates/src/{login/web → app/web/login}/locales/zh-CN.json +0 -0
  319. /package/templates/src/{login/web → app/web/login}/pages/change-password.tsx +0 -0
  320. /package/templates/src/{login/web → app/web/login}/pages/login.tsx +0 -0
  321. /package/templates/src/{login/web → app/web/login}/wecom.ts +0 -0
  322. /package/templates/src/{notification/web → app/web/notification}/locales/en-US.json +0 -0
  323. /package/templates/src/{notification/web → app/web/notification}/locales/zh-CN.json +0 -0
  324. /package/templates/src/{personal-center/web → app/web/personal-center}/__client/api.schemas.ts +0 -0
  325. /package/templates/src/{personal-center/web → app/web/personal-center}/locales/en-US.json +0 -0
  326. /package/templates/src/{personal-center/web → app/web/personal-center}/locales/zh-CN.json +0 -0
  327. /package/templates/src/{system/web → app/web/system}/pages/departments/sync-bar.tsx +0 -0
  328. /package/templates/src/{system/web → app/web/system}/pages/departments/tree.ts +0 -0
  329. /package/templates/src/{system/web → app/web/system}/pages/errors.ts +0 -0
  330. /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
  };
@@ -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 !== MODULE_PREFIX.slice(1)) {
32
- console.warn(`[ojm-api] 模块 "${ctx.module.name}" 正在认领 ${MODULE_PREFIX} 的 client——跨模块认领会共享 request 单槽、卸载互相耦合;应由 "${MODULE_PREFIX.slice(1)}" 模块在自身 onInit 创建,跨模块需求经 provider 暴露。`);
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<unknown> {
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<unknown>>();
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 unknown;
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 async function notificationNotificationsReadAllPost(): Promise<unknown> {
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<unknown>>();
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 unknown;
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 async function notificationNotificationsClearPost(): Promise<unknown> {
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<unknown>>();
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 unknown;
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<unknown> {
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<unknown>>();
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 unknown;
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<unknown> {
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<unknown>>();
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 unknown;
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<unknown> {
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<unknown>>();
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 unknown;
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/api";
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 !== MODULE_PREFIX.slice(1)) {
32
- console.warn(`[ojm-api] 模块 "${ctx.module.name}" 正在认领 ${MODULE_PREFIX} 的 client——跨模块认领会共享 request 单槽、卸载互相耦合;应由 "${MODULE_PREFIX.slice(1)}" 模块在自身 onInit 创建,跨模块需求经 provider 暴露。`);
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);
@@ -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/api";
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
- "systemUsersProfilePut": {
272
+ "systemUsersForbiddenPut": {
269
273
  query: z.object({
270
274
  "uid": z.number()
271
275
  }),
272
276
  body: z.object({
273
- "displayName": z.string().nullable().optional(),
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
- "systemUsersForbiddenPut": {
281
+ "systemUsersPasswordPut": {
282
282
  query: z.object({
283
283
  "uid": z.number()
284
284
  }),
285
285
  body: z.object({
286
- "forbidden": z.boolean()
286
+ "password": z.string().min(6)
287
287
  }),
288
+ data: z.null(),
288
289
  },
289
- "systemUsersPasswordPut": {
290
+ "systemUsersProfilePut": {
290
291
  query: z.object({
291
292
  "uid": z.number()
292
293
  }),
293
294
  body: z.object({
294
- "password": z.string().min(6)
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
  };