@oj-module/runtime 0.1.15 → 0.1.17

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.
@@ -0,0 +1,17 @@
1
+ import type { NoticeCategory, NoticeItem, PushBroadcastInput, PushInput } from "@oj-module/runtime/notify";
2
+ export type { NoticeCategory, NoticeItem, PushBroadcastInput, PushInput, };
3
+ /**
4
+ * 跨模块通知发射原语(前端侧 facade)。委托给已注册 notificationsApi provider 的
5
+ * 发射方法(经 HTTP 落到后端通知模块)。后端模块请改用 `api/src/_shared/notify`
6
+ * (直接 db 写入两表)——那是真正的服务端原语。
7
+ *
8
+ * 语义:原语不做任何业务判断(不判空间/角色/可见性),收件人过滤是发送方责任。
9
+ */
10
+ export declare const notify: {
11
+ /** 给指定用户推一条按人通知,返回命名空间 id "u:<id>" */
12
+ push(input: PushInput): Promise<string>;
13
+ /** 广播一条通知(全员可见,一行存储),返回命名空间 id "b:<id>" */
14
+ pushBroadcast(input: PushBroadcastInput): Promise<string>;
15
+ /** 删除通知:按人项校验归属(非本人删自己);广播项仅删本人回执 */
16
+ del(id: string, userId: string | number): Promise<void>;
17
+ };
package/dist/index.d.ts CHANGED
@@ -10,6 +10,7 @@
10
10
  * 由宿主的 importmap 提供,模块应直接 import 它们,不要从 runtime 转出。
11
11
  */
12
12
  import "./styles/index.css";
13
+ export { notify } from "./api/notify";
13
14
  export * from "./api/system/menu";
14
15
  export * from "./api/system/role";
15
16
  export * from "./api/user";
@@ -45,5 +46,6 @@ export { getAllExpandedKeys } from "./utils/get-all-expanded-keys";
45
46
  export { getAppInfo } from "./utils/get-app-info";
46
47
  export { getRedirectPath } from "./utils/get-redirect-path";
47
48
  export * from "./utils/tree";
49
+ export type { NoticeCategory, NoticeItem, PushBroadcastInput, PushInput, } from "@oj-module/runtime/notify";
48
50
  export { ErrorBoundary } from "react-error-boundary";
49
51
  export { z } from "zod";
@@ -1,9 +1,20 @@
1
+ import type { NoticeCategory } from "@oj-module/runtime/notify";
1
2
  export interface NotificationItem {
2
- /** 必填——单条已读/清空等写操作的前提(G4,破坏性变更,发版说明标注) */
3
- id: string | number;
3
+ /**
4
+ * 必填——单条已读/清空等写操作的前提(G4)。现为命名空间串:
5
+ * "u:<id>" 按人(私信/@我),"b:<id>" 广播(通知/公告)。破坏性变更:由
6
+ * string|number 改为 string,发版说明标注(写操作须透传命名空间 id)。
7
+ */
8
+ id: string;
9
+ /** 数据来源:user=按人项,broadcast=广播项 */
10
+ scope: "user" | "broadcast";
4
11
  avatar: string;
5
12
  date: string;
6
13
  isRead?: boolean;
7
14
  message: string;
8
15
  title: string;
16
+ /** 深链:前端点击跳转 */
17
+ link?: string;
18
+ /** 消息类别:notice=通知/广播,pm=私信,mention=@我(占位) */
19
+ category?: NoticeCategory;
9
20
  }
@@ -0,0 +1 @@
1
+ export * from "./types.ts";
@@ -0,0 +1,7 @@
1
+ // `@oj-module/runtime/notify` 物理子路径入口(与 contract 子路径同构)。
2
+ //
3
+ // 仅导出共享契约类型 —— 服务端原语不在 runtime 内,而是落在各 oj 工程的
4
+ // api/src/_shared/notify(oj 后端按物理目录解析裸 specifier 且钳制在 api 项目根内,
5
+ // 不能 import 本项目外的包;详见 packages/README 或设计文档 §4.3)。前端经此 facade
6
+ // 取类型;后端直接 import 本地 _shared/notify 的实现。
7
+ export * from "./types.js";
@@ -0,0 +1,31 @@
1
+ /** 消息类别:notice=通知/广播,pm=私信(按人),mention=@我(占位) */
2
+ export type NoticeCategory = "notice" | "pm" | "mention";
3
+ /** 铃展示项。id 为命名空间串:"u:<id>" 按人 / "b:<id>" 广播 */
4
+ export interface NoticeItem {
5
+ id: string;
6
+ scope: "user" | "broadcast";
7
+ avatar: string;
8
+ date: string;
9
+ isRead?: boolean;
10
+ message: string;
11
+ title: string;
12
+ link?: string;
13
+ category?: NoticeCategory;
14
+ }
15
+ /** 给指定用户推一条按人通知 */
16
+ export interface PushInput {
17
+ userId: string | number;
18
+ title: string;
19
+ message: string;
20
+ link?: string;
21
+ avatar?: string;
22
+ category?: NoticeCategory;
23
+ }
24
+ /** 广播一条通知(全员可见,一行存储) */
25
+ export interface PushBroadcastInput {
26
+ title: string;
27
+ message: string;
28
+ link?: string;
29
+ avatar?: string;
30
+ category?: NoticeCategory;
31
+ }
@@ -0,0 +1,3 @@
1
+ // 跨模块通知原语的公共类型(runtime 契约层)。后端模块 import 这些类型 +
2
+ // api/src/_shared/notify(db 实现);前端模块经 @oj-module/runtime 的 notify facade。
3
+ export {};
package/dist/runtime.js CHANGED
@@ -122,6 +122,36 @@ function unregisterApiProviders(moduleName) {
122
122
  var systemCurrent, notificationsCurrent, uploadCurrent, routesCurrent;
123
123
  var init_api_provider = __esmMin((() => {}));
124
124
  //#endregion
125
+ //#region src/api/notify/index.ts
126
+ init_api_provider();
127
+ var NOT_REGISTERED = "NotificationsBackendNotRegistered";
128
+ function requirePushProvider() {
129
+ const p = getNotificationsApiProvider();
130
+ if (!p || typeof p.push !== "function" || typeof p.pushBroadcast !== "function" || typeof p.del !== "function") throw new Error(`[notify] ${NOT_REGISTERED}:未注册 notificationsApi provider 的发射方法(push/pushBroadcast/del)。`);
131
+ return p;
132
+ }
133
+ /**
134
+ * 跨模块通知发射原语(前端侧 facade)。委托给已注册 notificationsApi provider 的
135
+ * 发射方法(经 HTTP 落到后端通知模块)。后端模块请改用 `api/src/_shared/notify`
136
+ * (直接 db 写入两表)——那是真正的服务端原语。
137
+ *
138
+ * 语义:原语不做任何业务判断(不判空间/角色/可见性),收件人过滤是发送方责任。
139
+ */
140
+ var notify = {
141
+ /** 给指定用户推一条按人通知,返回命名空间 id "u:<id>" */
142
+ push(input) {
143
+ return requirePushProvider().push(input);
144
+ },
145
+ /** 广播一条通知(全员可见,一行存储),返回命名空间 id "b:<id>" */
146
+ pushBroadcast(input) {
147
+ return requirePushProvider().pushBroadcast(input);
148
+ },
149
+ /** 删除通知:按人项校验归属(非本人删自己);广播项仅删本人回执 */
150
+ del(id, userId) {
151
+ return requirePushProvider().del(id, userId);
152
+ }
153
+ };
154
+ //#endregion
125
155
  //#region src/router/extra-info/route-path.ts
126
156
  /**
127
157
  * 判断 pathname 是否为登录页(basename 感知,P0)。
@@ -225,10 +255,10 @@ function getAppInfo() {
225
255
  return {
226
256
  "pkg": {
227
257
  "name": "@oj-module/runtime",
228
- "version": "0.1.15",
258
+ "version": "0.1.17",
229
259
  "license": "MIT"
230
260
  },
231
- "lastBuildTime": "2026-10-12 19:08:32"
261
+ "lastBuildTime": "2026-10-13 22:01:51"
232
262
  };
233
263
  }
234
264
  var init_get_app_info = __esmMin((() => {}));
@@ -4429,6 +4459,7 @@ var init_notification = __esmMin((() => {
4429
4459
  const handleClick = (item) => {
4430
4460
  if (readOnly) return;
4431
4461
  onEventChange && onEventChange("read", item);
4462
+ if (item.link) window.open(item.link, "_blank", "noopener");
4432
4463
  };
4433
4464
  dot = useMemo(() => {
4434
4465
  return !!notifications?.filter((item) => !item?.isRead).length;
@@ -27416,4 +27447,4 @@ init_ri();
27416
27447
  init_menu_icons();
27417
27448
  init_tree();
27418
27449
  //#endregion
27419
- export { AccessControl, AccessControlRoles, AntdApp, BasicButton, BasicContent, BasicTable, EmbeddedIcon, ErrorBoundary, ExternalIcon, FormAvatarItem, FormTreeItem, Iframe, LayoutCenterIcon, LayoutEffects, LayoutLeftIcon, LayoutRightIcon, MixedNavigationIcon, OutsidePageIcon, PageError, ProfileCardIcon, RiAccountCircleLine, RiContrastFill, RiFullscreenExitLine, RiFullscreenLine, RiMailCheckLine, RiMoonIcon, RiReactjsLine, RiSunIcon, RiUserSettingsLine, ServerErrorIcon, SideNavigationIcon, TopNavigationIcon, TwoColumnNavigationIcon, accessControlCodes, defineModule, fetchAddMenuItem, fetchAddRoleItem, fetchAsyncRoutes, fetchDeleteMenuItem, fetchDeleteRoleItem, fetchLogin, fetchLogout, fetchMenuByRoleId, fetchMenuList, fetchRefreshToken, fetchRoleList, fetchRoleMenu, fetchUpdateMenuItem, fetchUpdateRoleItem, fetchUserInfo, filterTree, getAllExpandedKeys, getAppInfo, getBooleanOptions, getModule, getModules, getRedirectPath, getRegisteredApiPrefix, getRegisteredStore, getRoutes, getYesNoOptions, handleTree, loadAll, mapTree, menuIcons, permissionPrefix, setupI18n, traverseTreeValues, unloadModule, useAccess, useAuthStore, usePreferences, useSlotNodes, useTabsStore, useUserStore, external_exports as z };
27450
+ export { AccessControl, AccessControlRoles, AntdApp, BasicButton, BasicContent, BasicTable, EmbeddedIcon, ErrorBoundary, ExternalIcon, FormAvatarItem, FormTreeItem, Iframe, LayoutCenterIcon, LayoutEffects, LayoutLeftIcon, LayoutRightIcon, MixedNavigationIcon, OutsidePageIcon, PageError, ProfileCardIcon, RiAccountCircleLine, RiContrastFill, RiFullscreenExitLine, RiFullscreenLine, RiMailCheckLine, RiMoonIcon, RiReactjsLine, RiSunIcon, RiUserSettingsLine, ServerErrorIcon, SideNavigationIcon, TopNavigationIcon, TwoColumnNavigationIcon, accessControlCodes, defineModule, fetchAddMenuItem, fetchAddRoleItem, fetchAsyncRoutes, fetchDeleteMenuItem, fetchDeleteRoleItem, fetchLogin, fetchLogout, fetchMenuByRoleId, fetchMenuList, fetchRefreshToken, fetchRoleList, fetchRoleMenu, fetchUpdateMenuItem, fetchUpdateRoleItem, fetchUserInfo, filterTree, getAllExpandedKeys, getAppInfo, getBooleanOptions, getModule, getModules, getRedirectPath, getRegisteredApiPrefix, getRegisteredStore, getRoutes, getYesNoOptions, handleTree, loadAll, mapTree, menuIcons, notify, permissionPrefix, setupI18n, traverseTreeValues, unloadModule, useAccess, useAuthStore, usePreferences, useSlotNodes, useTabsStore, useUserStore, external_exports as z };
@@ -1,3 +1,4 @@
1
+ import type { PushBroadcastInput, PushInput } from "@oj-module/runtime/notify";
1
2
  import type { MenuItemType } from "../api/system/menu/types";
2
3
  import type { FetchAddRoleItemBody, FetchAddRoleItemData, FetchDeleteRoleItemBody, FetchDeleteRoleItemData, FetchMenuByRoleIdData, FetchMenuByRoleIdQuery, FetchRoleListData, FetchRoleListQuery, FetchRoleMenuData, FetchUpdateRoleItemBody, FetchUpdateRoleItemData } from "../api/system/role/client/api";
3
4
  import type { NotificationItem } from "../layout/widgets/notification/types";
@@ -26,14 +27,31 @@ export interface SystemApiProvider {
26
27
  }
27
28
  /**
28
29
  * 通知 provider(D9 + G4):模块经 ctx.register.notificationsApi 接管通知。
29
- * 四方法全必填——「接管通知 API」是全量接管(读 + 三个写),不做部分托管;
30
- * 老 bundle 只有 fetchNotifications 的版本漂移由消费点运行时防御(评审 P2-7)。
30
+ *
31
+ * 定位澄清(Part A):这是「铃的数据通道」——消费端(读 + 三个写),供前端通知铃
32
+ * 接管内置通知 API。**单一 provider 槽位、先到先得是「一个铃 / 一套端点」的事实,
33
+ * 不是模块竞争通知归属的特权**(与 authProvider 同规则:消费位独占合理)。
34
+ * 框架从未提供「给某用户发通知」的原语——跨模块发射请走 notify 发射方法(见下)
35
+ * 或后端 api/src/_shared/notify,而非各模块自建私有表抢这一个数据源。
36
+ *
37
+ * 消费端四方法全必填——「接管通知 API」是全量接管,不做部分托管;老 bundle 只有
38
+ * fetchNotifications 的版本漂移由消费点运行时防御(评审 P2-7)。
39
+ *
40
+ * 发射端三方法(跨模块原语):显式 userId,前端 provider 经 HTTP 落到后端通知模块;
41
+ * 后端模块请直接用 api/src/_shared/notify(db 直写,绕开 HTTP)。原语不做任何业务
42
+ * 判断(不判空间/角色/可见性),收件人过滤是发送方责任。
31
43
  */
32
44
  export interface NotificationsApiProvider {
33
45
  fetchNotifications: () => Promise<NotificationItem[]>;
34
46
  markRead: (id: string | number) => Promise<void>;
35
47
  markAllRead: () => Promise<void>;
36
48
  clearAll: () => Promise<void>;
49
+ /** 给指定用户推按人通知,返回命名空间 id "u:<id>" */
50
+ push: (input: PushInput) => Promise<string>;
51
+ /** 广播通知(全员可见,一行存储),返回命名空间 id "b:<id>" */
52
+ pushBroadcast: (input: PushBroadcastInput) => Promise<string>;
53
+ /** 删通知:按人项校验归属;广播项仅删本人回执 */
54
+ del: (id: string, userId: string | number) => Promise<void>;
37
55
  }
38
56
  /** 上传 provider(D9):模块经 ctx.register.uploadApi 接管头像/附件上传端点 */
39
57
  export interface UploadApiProvider {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@oj-module/runtime",
3
3
  "type": "module",
4
- "version": "0.1.15",
4
+ "version": "0.1.17",
5
5
  "description": "React Antd Admin 框架运行时,供模块工程消费",
6
6
  "license": "MIT",
7
7
  "imports": {
@@ -19,6 +19,10 @@
19
19
  "./contract/errors": {
20
20
  "types": "./dist/contract/errors.d.ts",
21
21
  "default": "./dist/contract/errors.js"
22
+ },
23
+ "./notify": {
24
+ "types": "./dist/notify/index.d.ts",
25
+ "default": "./dist/notify/index.js"
22
26
  }
23
27
  },
24
28
  "files": [
@@ -64,6 +68,6 @@
64
68
  "unplugin-icons": "^23.0.1"
65
69
  },
66
70
  "scripts": {
67
- "build": "vite build && tsc -p tsconfig.contract.json && tsc -p tsconfig.dts.json && node scripts/rewrite-dts-specifiers.mjs && node scripts/inline-css.mjs"
71
+ "build": "vite build && tsc -p tsconfig.contract.json && tsc -p tsconfig.notify.json && tsc -p tsconfig.dts.json && node scripts/rewrite-dts-specifiers.mjs && node scripts/inline-css.mjs"
68
72
  }
69
73
  }