From d42f47319c316af711c5fb978f4f4348457357fe Mon Sep 17 00:00:00 2001 From: pqcqaq <905739777@qq.com> Date: Sun, 26 Oct 2025 11:04:43 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BE=85=E5=8A=9E=E4=BA=8B=E9=A1=B9=E7=AE=A1?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/genernal-business/todo.md | 1404 +++++++++++++++++++++++++++++++++ 1 file changed, 1404 insertions(+) create mode 100644 src/genernal-business/todo.md diff --git a/src/genernal-business/todo.md b/src/genernal-business/todo.md new file mode 100644 index 0000000..0f81c6d --- /dev/null +++ b/src/genernal-business/todo.md @@ -0,0 +1,1404 @@ +# oak-general-business 待办事项功能文档 + +## 1. 功能概述 + +参考自:oak-general-business/src/entities/ToDo.ts, oak-general-business/src/entities/ReadRemark.ts, oak-general-business/src/triggers/toDo.ts + +### 1.1 功能目的 + +待办事项功能是一个通用的任务管理系统,用于在业务流程中自动创建和管理待处理任务。该功能主要用于: + +- **自动化任务分配**:当系统中某个实体的数据满足特定条件时,自动为有权限的用户创建待办任务 +- **协作管理**:支持多用户协作完成待办事项 +- **任务追踪**:跟踪待办事项的状态,从创建到完成的全生命周期 +- **权限控制**:基于用户权限自动分配待办任务给相关用户 + +### 1.2 应用场景 + +待办事项功能适用于以下场景: + +1. **审批流程**:需要审批的订单、申请等自动创建待办提醒审批人 +2. **任务分配**:需要特定角色处理的业务自动分配给相关用户 +3. **状态变更提醒**:当实体状态发生变化时,提醒相关人员处理 +4. **协作处理**:多人协作完成某项任务的场景 +5. **工作流引擎**:作为工作流引擎的底层支撑 + +### 1.3 核心价值 + +- **自动化**:无需手动创建任务,系统根据业务规则自动生成 +- **精准分配**:基于权限系统自动识别有权限的用户并分配任务 +- **状态同步**:待办任务与业务数据状态自动同步,任务完成自动标记 +- **通用性强**:可应用于任何实体的任何动作,具有高度的灵活性 +- **避免重复**:同一任务不会重复创建,避免用户困扰 + +### 1.4 与其他功能模块的关系 + +待办事项功能与以下模块紧密关联: + +- **用户系统**:依赖用户权限体系分配任务 +- **权限管理**:通过 UserRelation 确定任务协作者 +- **消息系统**:可配合消息推送提醒用户处理待办 +- **各业务实体**:作为各业务流程的辅助管理工具 + +--- + +## 2. 实体定义详解 + +参考自:oak-general-business/src/entities/ToDo.ts, oak-general-business/src/entities/ReadRemark.ts + +待办事项功能涉及两个核心实体:`ToDo`(待办事项)和 `ReadRemark`(已读标记)。 + +### 2.1 ToDo 实体(待办事项) + +`ToDo` 实体是待办事项功能的核心,用于记录需要用户处理的任务。 + +#### 2.1.1 字段定义 + +| 字段名 | 类型 | 说明 | 必填 | +|--------|------|------|------| +| `id` | `String<64>` | 待办事项唯一标识 | ✓ | +| `title` | `Text` | 待办标题,描述任务名称 | ✓ | +| `description` | `Text` | 待办描述,详细说明任务内容 | - | +| `targetEntity` | `String<32>` | 目标实体名称,如 'order'、'article' 等 | ✓ | +| `targetFilter` | `Object` | 目标实体的过滤条件,用于匹配需要处理的数据 | ✓ | +| `action` | `String<32>` | 需要执行的动作,如 'approve'、'reject' 等 | ✓ | +| `redirectTo` | `RedirectToProps` | 重定向页面配置 | ✓ | +| `entity` | `String<32>` | 关联对象的实体名称 | ✓ | +| `entityId` | `String<64>` | 关联对象的ID | ✓ | +| `iState` | `IState` | 待办状态:'active'(待办)或 'done'(已完成) | ✓ | +| `createdAt` | `Datetime` | 创建时间 | 自动 | +| `updatedAt` | `Datetime` | 更新时间 | 自动 | + +#### 2.1.2 RedirectToProps 类型 + +```typescript +export type RedirectToProps = { + batchPath: string; // 批量处理页面路径 + singlePath?: string; // 单个处理页面路径(可选) +}; +``` + +用于配置点击待办事项后跳转的页面。 + +#### 2.1.3 状态定义(IState) + +| 状态值 | 中文名 | 颜色 | 说明 | +|--------|--------|------|------| +| `active` | 待办 | 蓝色 (#0000FF) | 待处理的任务 | +| `done` | 已完成 | 绿色 (#008000) | 已完成的任务 | + +#### 2.1.4 动作定义(Action) + +| 动作 | 中文名 | 说明 | +|------|--------|------| +| `complete` | 完成 | 标记待办事项为已完成 | + +#### 2.1.5 关系定义(Relation) + +| 关系名 | 中文名 | 说明 | +|--------|--------|------| +| `collaborator` | 协作者 | 参与处理该待办事项的用户 | + +通过 `UserRelation` 表关联,支持多个用户协作处理同一个待办任务。 + +#### 2.1.6 状态转换图 + +```mermaid +stateDiagram-v2 + [*] --> active: 创建待办 + active --> done: complete (完成) + done --> [*] +``` + +### 2.2 ReadRemark 实体(已读标记) + +`ReadRemark` 实体用于标记用户对会话消息的阅读状态(注:当前版本该功能未完全实现)。 + +#### 2.2.1 字段定义 + +| 字段名 | 类型 | 说明 | 必填 | +|--------|------|------|------| +| `id` | `String<64>` | 唯一标识 | ✓ | +| `user` | `User` | 当前用户,关联 User 实体 | ✓ | +| `session` | `Session` | 关联的会话,关联 Session 实体 | ✓ | +| `createdAt` | `Datetime` | 创建时间(最后阅读时间) | 自动 | + +#### 2.2.2 用途说明 + +`ReadRemark` 用于记录用户最后一次阅读某个会话的时间,配合 `Session.lmst`(最后一条消息时间)可以判断是否有未读消息。 + +**注意**:根据代码注释,该功能在当前版本中未完全实现,存在于 `src/components/session/cell/index.ts` 中但被注释掉。 + +### 2.3 实体关系图 + +```mermaid +erDiagram + ToDo ||--o{ UserRelation : "collaborator" + ToDo { + string id PK + string title + text description + string targetEntity + object targetFilter + string action + object redirectTo + string entity + string entityId + enum iState + } + + UserRelation { + string id PK + string userId FK + string relationId FK + string entityId FK + } + + User ||--o{ UserRelation : has + + ReadRemark ||--|| User : belongs_to + ReadRemark ||--|| Session : refers_to + ReadRemark { + string id PK + string userId FK + string sessionId FK + datetime createdAt + } + + Session { + string id PK + datetime lmst + } +``` + +### 2.4 索引设计 + +#### ToDo 实体索引 + +为了提高查询效率,建议在以下字段上建立索引: + +- `targetEntity + targetFilter + action + iState`:用于快速查找特定实体的待办任务 +- `entity + entityId`:用于查找关联特定对象的待办 +- `iState`:用于筛选待办或已完成的任务 +- `createdAt`:用于按时间排序 + +#### ReadRemark 实体索引 + +- `userId + sessionId`:用于查找用户对特定会话的阅读记录 + +--- + +## 3. 业务逻辑说明 + +参考自:oak-general-business/src/triggers/toDo.ts + +待办事项功能的业务逻辑主要通过 Trigger(触发器)实现,提供了自动创建和完成待办任务的机制。 + +### 3.1 核心业务流程 + +#### 3.1.1 待办事项创建流程 + +```mermaid +flowchart TD + A[业务触发点] --> B{检查是否已存在
相同待办} + B -->|已存在| C[跳过创建,返回0] + B -->|不存在| D{是否指定用户列表?} + D -->|是| E[使用指定用户] + D -->|否| F[查询有权限的用户] + F --> G[获取UserRelation和UserEntity] + G --> H[合并去重用户列表] + E --> I[查询collaborator关系] + H --> I + I --> J[创建待办事项] + J --> K[为每个用户创建UserRelation] + K --> L[返回1] +``` + +#### 3.1.2 待办事项完成流程 + +```mermaid +flowchart TD + A[执行业务动作] --> B[查询匹配的待办事项] + B --> C{找到待办?} + C -->|否| D[抛出异常] + C -->|是| E[遍历待办列表] + E --> F{目标数据还存在?} + F -->|是| G[跳过该待办] + F -->|否| H[标记待办完成] + H --> I{还有待办?} + I -->|是| E + I -->|否| J[返回完成数量] + G --> I +``` + +### 3.2 Trigger(触发器)详解 + +#### 3.2.1 createToDo 函数 + +**位置**:`src/triggers/toDo.ts` + +**功能**:为满足特定条件的实体对象创建待办事项 + +**函数签名**: + +```typescript +async function createToDo>( + entity: T, // 目标实体名称 + filter: ED[T]['Filter'], // 过滤条件 + action: ED[T]['Action'], // 需要执行的动作 + context: Cxt, // 运行时上下文 + data: { + title: string, // 待办标题 + description?: string, // 待办描述 + redirectTo: RedirectToProps, // 重定向页面 + entity: any, // 关联对象实体 + entityId: string, // 关联对象ID + }, + userIds?: string[] // 可选:指定用户列表 +): Promise // 返回创建数量(0或1) +``` + +**执行逻辑**: + +1. **防重复检查**: + - 查询是否已存在相同的待办(同 targetEntity、targetFilter、action、entity、entityId 且状态为 active) + - 如已存在则返回 0,避免重复创建 + +2. **确定协作用户**: + - 如果传入了 `userIds` 参数,直接使用 + - 否则调用 `getUserRelationsByActions` 查询对该实体有该动作权限的所有用户 + - 合并 userRelations 和 userEntities 中的用户ID并去重 + +3. **创建待办事项**: + - 生成新的待办ID + - 设置 targetEntity、targetFilter、action 等字段 + - 状态设为 'active' + +4. **建立协作关系**: + - 查询 'collaborator' 关系的 relationId + - 为每个用户创建 UserRelation 记录 + - 通过 `userRelation$entity` 级联创建 + +**使用场景**: + +在业务实体的 trigger 中调用,例如: + +```typescript +// 在订单创建后触发 +export const orderTriggers = { + afterCreate: async (params, context) => { + const { data } = params; + + // 为需要审批的订单创建待办 + if (data.status === 'pending') { + await createToDo( + 'order', // 实体名称 + { id: data.id }, // 过滤条件 + 'approve', // 动作 + context, + { + title: '订单待审批', + description: `订单号:${data.orderNo}`, + redirectTo: { + batchPath: '/order/approve', + singlePath: '/order/detail' + }, + entity: 'order', + entityId: data.id + } + // 不传userIds,自动查询有approve权限的用户 + ); + } + } +}; +``` + +#### 3.2.2 completeToDo 函数 + +**位置**:`src/triggers/toDo.ts` + +**功能**:当业务动作执行后,自动完成相关的待办事项 + +**函数签名**: + +```typescript +async function completeToDo>( + entity: T, // 实体名称 + filter: ED[T]['Filter'], // 过滤条件 + action: ED[T]['Action'], // 动作 + context: Cxt // 运行时上下文 +): Promise // 返回完成的待办数量 +``` + +**执行逻辑**: + +1. **查询待办**: + - 根据 targetEntity、targetFilter、action 查询所有状态为 'active' 的待办 + - 断言必须找到至少一个待办,否则抛出异常 + +2. **验证目标数据**: + - 对每个待办,使用其 targetFilter 查询目标数据是否还存在 + - 如果目标数据不存在(即已被处理),则可以完成该待办 + +3. **完成待办**: + - 调用 `context.operate('toDo', { action: 'complete', ... })` + - 将待办状态从 'active' 更新为 'done' + +4. **返回统计**: + - 返回实际完成的待办数量 + +**重要说明**: + +- **必须在 after trigger 中调用**:因为需要在业务动作执行后才能完成待办 +- **filter 参数要注意**:传入的 filter 应该能匹配到已经执行了 action 后的数据状态 + +**使用场景**: + +```typescript +// 在订单审批后触发 +export const orderTriggers = { + afterOperate: async (params, context) => { + const { action, filter } = params; + + if (action === 'approve') { + // 完成相关的待办事项 + const count = await completeToDo( + 'order', + filter, // 使用操作时的filter + 'approve', + context + ); + console.log(`完成了 ${count} 个待办事项`); + } + } +}; +``` + +### 3.3 业务规则和约束 + +#### 3.3.1 待办唯一性 + +- 同一个实体对象、同一个动作、同一个关联对象,只能存在一个 active 状态的待办 +- 通过 `targetEntity + targetFilter + action + entity + entityId + iState` 确保唯一性 + +#### 3.3.2 权限控制 + +- 只有对目标实体有特定动作权限的用户才会收到待办任务 +- 通过 `getUserRelationsByActions` 自动识别有权限的用户 +- 也可以手动指定用户列表,适用于特殊场景 + +#### 3.3.3 数据一致性 + +- `completeToDo` 会验证目标数据是否还存在 +- 只有目标数据不存在时才会完成待办 +- 确保待办任务与业务数据状态同步 + +#### 3.3.4 错误处理 + +- 如果完成待办时找不到对应的待办记录,会抛出异常 +- 建议在 trigger 中使用 try-catch 包裹,避免影响主业务流程 + +### 3.4 时序图 + +```mermaid +sequenceDiagram + participant BL as 业务逻辑 + participant CT as createToDo + participant PA as 权限系统 + participant DB as 数据库 + participant User as 用户 + + BL->>CT: 调用创建待办 + CT->>DB: 检查是否已存在 + DB-->>CT: 返回检查结果 + alt 已存在 + CT-->>BL: 返回0 + else 不存在 + CT->>PA: 查询有权限用户 + PA-->>CT: 返回用户列表 + CT->>DB: 创建待办事项 + CT->>DB: 创建UserRelation + DB-->>CT: 创建成功 + CT-->>BL: 返回1 + DB->>User: 推送待办通知 + end +``` + +```mermaid +sequenceDiagram + participant BL as 业务逻辑 + participant CT as completeToDo + participant DB as 数据库 + participant User as 用户 + + BL->>BL: 执行业务动作 + BL->>CT: 调用完成待办 + CT->>DB: 查询相关待办 + DB-->>CT: 返回待办列表 + + loop 遍历待办 + CT->>DB: 检查目标数据是否存在 + DB-->>CT: 返回检查结果 + alt 数据不存在 + CT->>DB: 标记待办完成 + DB->>User: 更新用户待办列表 + end + end + + CT-->>BL: 返回完成数量 +``` + +### 3.5 Aspect 层 + +**注意**:待办事项功能目前**没有提供专门的 Aspect 类**,所有逻辑都通过 Trigger 在服务端自动执行。 + +如果需要前端查询待办列表,可以直接使用通用的实体查询方法: + +```typescript +// 查询当前用户的待办事项 +const todos = await context.select('toDo', { + data: { + id: 1, + title: 1, + description: 1, + targetEntity: 1, + action: 1, + redirectTo: 1, + iState: 1, + createdAt: 1 + }, + filter: { + iState: 'active', + 'userRelation.userId': currentUserId // 通过关系筛选 + }, + order: { + createdAt: 'desc' + } +}); +``` + +### 3.6 Feature 层 + +待办事项功能目前**没有专门的 Feature 类**。 + +--- + +## 4. 前端组件使用 + +参考自:项目组件目录搜索结果 + +### 4.1 组件概述 + +待办事项功能**当前版本没有提供专门的前端组件**。这是因为待办功能主要作为后端业务流程的辅助工具,通过 Trigger 自动管理。 + +### 4.2 前端集成方案 + +虽然没有专门的组件,但您可以通过以下方式在前端使用待办功能: + +#### 4.2.1 查询待办列表 + +使用通用的实体查询功能: + +```typescript +import { useEffect, useState } from 'react'; + +function ToDoList() { + const [todos, setTodos] = useState([]); + const { context } = useContext(DomainContext); + + useEffect(() => { + loadTodos(); + }, []); + + async function loadTodos() { + const result = await context.select('toDo', { + data: { + id: 1, + title: 1, + description: 1, + targetEntity: 1, + action: 1, + redirectTo: 1, + entity: 1, + entityId: 1, + iState: 1, + createdAt: 1 + }, + filter: { + iState: 'active', + // 通过 collaborator 关系筛选当前用户的待办 + 'relation.collaborator.userId': context.userId + }, + order: { + createdAt: 'desc' + } + }); + + setTodos(result.data); + } + + return ( +
+

我的待办 ({todos.length})

+
    + {todos.map(todo => ( +
  • + {todo.title} +

    {todo.description}

    + +
  • + ))} +
+
+ ); +} +``` + +#### 4.2.2 处理待办跳转 + +```typescript +function handleClick(todo) { + const { redirectTo, targetEntity, entityId } = todo; + + // 单个处理页面 + if (redirectTo.singlePath) { + // 跳转到单个处理页面,传递实体ID + navigate(redirectTo.singlePath, { + state: { + entity: targetEntity, + entityId: entityId + } + }); + } + // 批量处理页面 + else if (redirectTo.batchPath) { + navigate(redirectTo.batchPath, { + state: { + entity: targetEntity, + filter: todo.targetFilter + } + }); + } +} +``` + +#### 4.2.3 统计待办数量 + +用于显示待办角标: + +```typescript +function ToDoBadge() { + const [count, setCount] = useState(0); + const { context } = useContext(DomainContext); + + useEffect(() => { + loadCount(); + + // 设置定时刷新 + const timer = setInterval(loadCount, 60000); // 每分钟刷新 + return () => clearInterval(timer); + }, []); + + async function loadCount() { + const result = await context.count('toDo', { + filter: { + iState: 'active', + 'relation.collaborator.userId': context.userId + }, + count: 100 // 最多显示99+ + }); + + setCount(result); + } + + return ( +
+ 待办 + {count > 0 && ( + {count > 99 ? '99+' : count} + )} +
+ ); +} +``` + +#### 4.2.4 手动完成待办 + +虽然待办通常会自动完成,但也可以手动标记完成: + +```typescript +async function markToDoComplete(todoId: string) { + await context.operate('toDo', { + id: generateNewId(), + action: 'complete', + data: {}, + filter: { + id: todoId + } + }); + + // 刷新列表 + loadTodos(); +} +``` + +### 4.3 界面设计建议 + +#### 4.3.1 待办列表页面 + +建议包含以下元素: + +- **标题**:显示 `title` 字段 +- **描述**:显示 `description` 字段(如果有) +- **类型标签**:根据 `targetEntity` 显示不同颜色的标签 +- **时间**:显示 `createdAt`,计算相对时间(如"3小时前") +- **操作按钮**:跳转到处理页面 +- **状态筛选**:支持查看已完成的待办 + +#### 4.3.2 待办卡片示例 + +```tsx +interface ToDoCardProps { + todo: ToDoSchema; + onHandle: (todo: ToDoSchema) => void; +} + +function ToDoCard({ todo, onHandle }: ToDoCardProps) { + return ( +
+
+ + {getEntityName(todo.targetEntity)} + + + {formatRelativeTime(todo.createdAt)} + +
+ +

{todo.title}

+ + {todo.description && ( +

{todo.description}

+ )} + +
+ + 需要操作:{getActionName(todo.action)} + + +
+
+ ); +} +``` + +### 4.4 与消息通知集成 + +待办功能可以配合消息推送使用: + +```typescript +// 在创建待办后发送通知 +export const orderTriggers = { + afterCreate: async (params, context) => { + const { data } = params; + + if (data.status === 'pending') { + // 创建待办 + const created = await createToDo( + 'order', + { id: data.id }, + 'approve', + context, + { + title: '订单待审批', + description: `订单号:${data.orderNo}`, + redirectTo: { + batchPath: '/order/approve' + }, + entity: 'order', + entityId: data.id + } + ); + + // 发送消息通知 + if (created > 0) { + await context.aspects.message.send({ + type: 'todo', + title: '新的待办任务', + content: '您有新的订单需要审批', + data: { + todoId: data.id, + entity: 'order', + entityId: data.id + } + }); + } + } + } +}; +``` + +### 4.5 移动端适配 + +在小程序或移动端,可以使用类似的逻辑: + +```javascript +// 小程序示例 +Page({ + data: { + todos: [], + loading: false + }, + + onLoad() { + this.loadTodos(); + }, + + async loadTodos() { + this.setData({ loading: true }); + + const context = getApp().context; + const result = await context.select('toDo', { + data: { + id: 1, + title: 1, + description: 1, + redirectTo: 1, + createdAt: 1 + }, + filter: { + iState: 'active', + 'relation.collaborator.userId': context.userId + } + }); + + this.setData({ + todos: result.data, + loading: false + }); + }, + + handleTodo(e) { + const todo = e.currentTarget.dataset.todo; + const path = todo.redirectTo.singlePath || todo.redirectTo.batchPath; + + wx.navigateTo({ + url: path + '?todoId=' + todo.id + }); + } +}); +``` + +--- + +## 5. 接入指南 + +### 5.1 前置要求 + +待办事项功能依赖于以下模块: + +- **oak-domain**:提供实体定义和基础功能 +- **用户系统**:需要用户认证和权限管理 +- **关系系统**:使用 UserRelation 管理协作者 + +### 5.2 实体注册 + +待办事项相关实体已在模块中注册,无需额外配置。确保您的项目已正确引入 `oak-general-business`: + +这样 `toDo` 和 `readRemark` 实体就会自动可用。 + +### 5.3 在业务实体中集成待办功能 + +#### 5.3.1 导入触发器函数 + +```typescript +import { createToDo, completeToDo } from 'oak-general-business/lib/triggers/toDo'; +``` + +#### 5.3.2 在实体 Trigger 中使用 + +以订单审批为例: + +```typescript +// src/triggers/order.ts +import { createToDo, completeToDo } from 'oak-general-business/lib/triggers/toDo'; +import { Trigger } from 'oak-domain/lib/types/Trigger'; +import { EntityDict } from '../oak-app-domain'; + +export const orderTriggers: Trigger = { + // 订单创建后,如果需要审批则创建待办 + afterCreate: async (params, context) => { + const { data } = params; + + if (data.status === 'pendingApproval') { + await createToDo( + 'order', // 目标实体 + { id: data.id }, // 过滤条件 + 'approve', // 动作 + context, + { + title: `订单${data.orderNo}待审批`, + description: `客户:${data.customerName},金额:¥${data.amount}`, + redirectTo: { + batchPath: '/admin/order/approval', + singlePath: `/admin/order/${data.id}` + }, + entity: 'order', + entityId: data.id + } + ); + } + }, + + // 订单审批后,完成相关待办 + afterOperate: async (params, context) => { + const { action, filter, data } = params; + + if (action === 'approve' || action === 'reject') { + // 完成待办(注意 filter 需要匹配已审批的订单) + await completeToDo( + 'order', + filter, + 'approve', + context + ); + } + } +}; +``` + +#### 5.3.3 配置实体权限 + +确保相关角色有对应的动作权限: + +```typescript +// 在权限配置中 +export const orderPermissions = { + approve: ['admin', 'manager'], // 有审批权限的角色 + reject: ['admin', 'manager'] +}; +``` + +### 5.4 前端页面开发 + +#### 5.4.1 创建待办列表页面 + +```typescript +// pages/todo/index.tsx +import React, { useEffect, useState } from 'react'; +import { useContext } from 'react'; +import { DomainContext } from 'your-context-path'; + +interface ToDoItem { + id: string; + title: string; + description?: string; + targetEntity: string; + action: string; + redirectTo: { + batchPath: string; + singlePath?: string; + }; + iState: 'active' | 'done'; + createdAt: Date; +} + +export default function ToDoPage() { + const [todos, setTodos] = useState([]); + const [activeTab, setActiveTab] = useState<'active' | 'done'>('active'); + const { context } = useContext(DomainContext); + + useEffect(() => { + loadTodos(); + }, [activeTab]); + + async function loadTodos() { + const result = await context.select('toDo', { + data: { + id: 1, + title: 1, + description: 1, + targetEntity: 1, + action: 1, + redirectTo: 1, + iState: 1, + createdAt: 1 + }, + filter: { + iState: activeTab, + 'relation.collaborator.userId': context.userId + }, + order: { + createdAt: 'desc' + } + }); + + setTodos(result.data); + } + + function handleTodoClick(todo: ToDoItem) { + const path = todo.redirectTo.singlePath || todo.redirectTo.batchPath; + // 跳转逻辑 + window.location.href = path; + } + + return ( +
+
+ + +
+ +
+ {todos.map(todo => ( +
handleTodoClick(todo)} + > +

{todo.title}

+ {todo.description &&

{todo.description}

} +
+ {todo.targetEntity} + + {new Date(todo.createdAt).toLocaleString()} + +
+
+ ))} +
+
+ ); +} +``` + +#### 5.4.2 添加待办角标 + +在导航栏或菜单中显示待办数量: + +```typescript +// components/TodoBadge.tsx +import React, { useEffect, useState } from 'react'; +import { useContext } from 'react'; +import { DomainContext } from 'your-context-path'; + +export function TodoBadge() { + const [count, setCount] = useState(0); + const { context } = useContext(DomainContext); + + useEffect(() => { + loadCount(); + + // 每分钟刷新一次 + const timer = setInterval(loadCount, 60000); + return () => clearInterval(timer); + }, []); + + async function loadCount() { + const result = await context.count('toDo', { + filter: { + iState: 'active', + 'relation.collaborator.userId': context.userId + }, + count: 100 + }); + + setCount(result); + } + + if (count === 0) return null; + + return ( + + {count > 99 ? '99+' : count} + + ); +} +``` + +## 6. API 接口说明 + +参考自:oak-general-business/src/triggers/toDo.ts + +### 6.1 Trigger 函数接口 + +待办事项功能提供两个核心的 Trigger 函数,用于在业务逻辑中创建和完成待办。 + +#### 6.1.1 createToDo - 创建待办事项 + +**导入路径**: + +```typescript +import { createToDo } from 'oak-general-business/lib/triggers/toDo'; +// 或 ES 模块 +import { createToDo } from 'oak-general-business/es/triggers/toDo'; +``` + +**函数签名**: + +```typescript +function createToDo>( + entity: T, + filter: ED[T]['Filter'], + action: ED[T]['Action'], + context: Cxt, + data: { + title: string; + description?: string; + redirectTo: EntityDict['toDo']['OpSchema']['redirectTo']; + entity: any; + entityId: string; + }, + userIds?: string[] +): Promise +``` + +**参数说明**: + +| 参数 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `entity` | `T` | ✓ | 目标实体名称,如 'order'、'article' 等 | +| `filter` | `ED[T]['Filter']` | ✓ | 过滤条件,用于匹配需要处理的数据 | +| `action` | `ED[T]['Action']` | ✓ | 需要执行的动作,如 'approve'、'review' 等 | +| `context` | `Cxt` | ✓ | 运行时上下文对象 | +| `data` | `object` | ✓ | 待办详情数据,见下表 | +| `userIds` | `string[]` | - | 可选,手动指定协作用户列表 | + +**data 对象字段**: + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `title` | `string` | ✓ | 待办标题 | +| `description` | `string` | - | 待办描述 | +| `redirectTo` | `RedirectToProps` | ✓ | 跳转页面配置 | +| `entity` | `string` | ✓ | 关联对象的实体名称 | +| `entityId` | `string` | ✓ | 关联对象的ID | + +**redirectTo 对象结构**: + +```typescript +type RedirectToProps = { + batchPath: string; // 批量处理页面路径(必填) + singlePath?: string; // 单个处理页面路径(可选) +} +``` + +**返回值**: + +- `Promise`:返回创建的待办数量 + - `1`:成功创建 + - `0`:已存在相同待办,跳过创建 + +**使用示例**: + +```typescript +// 示例1:自动分配给有权限的用户 +const created = await createToDo( + 'order', + { id: orderId }, + 'approve', + context, + { + title: '订单待审批', + description: `订单号:${orderNo},金额:¥${amount}`, + redirectTo: { + batchPath: '/admin/order/approval', + singlePath: `/admin/order/${orderId}` + }, + entity: 'order', + entityId: orderId + } +); + +// 示例2:手动指定用户 +const created = await createToDo( + 'article', + { id: articleId }, + 'review', + context, + { + title: '文章待审核', + redirectTo: { + batchPath: '/admin/article/review' + }, + entity: 'article', + entityId: articleId + }, + ['user-id-1', 'user-id-2'] // 指定审核人 +); +``` + +#### 6.1.2 completeToDo - 完成待办事项 + +**导入路径**: + +```typescript +import { completeToDo } from 'oak-general-business/lib/triggers/toDo'; +// 或 ES 模块 +import { completeToDo } from 'oak-general-business/es/triggers/toDo'; +``` + +**函数签名**: + +```typescript +function completeToDo>( + entity: T, + filter: ED[T]['Filter'], + action: ED[T]['Action'], + context: Cxt +): Promise +``` + +**参数说明**: + +| 参数 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `entity` | `T` | ✓ | 目标实体名称 | +| `filter` | `ED[T]['Filter']` | ✓ | 过滤条件,应该与创建时一致(但考虑状态变化) | +| `action` | `ED[T]['Action']` | ✓ | 对应的动作 | +| `context` | `Cxt` | ✓ | 运行时上下文对象 | + +**返回值**: + +- `Promise`:返回完成的待办数量 + +**异常**: + +- 如果找不到对应的待办,会抛出 `AssertionError` 异常 + +**使用示例**: + +```typescript +// 在订单审批后调用 +export const orderTriggers = { + afterOperate: async (params, context) => { + const { action, filter } = params; + + if (action === 'approve') { + try { + const count = await completeToDo( + 'order', + filter, + 'approve', + context + ); + console.log(`完成了 ${count} 个待办事项`); + } catch (error) { + console.error('完成待办失败', error); + } + } + } +}; +``` + +### 6.2 通用实体操作接口 + +由于待办功能没有专门的 Aspect 类,可以使用通用的实体操作方法。 + +#### 6.2.1 查询待办列表 + +```typescript +const result = await context.select('toDo', { + data: { + id: 1, + title: 1, + description: 1, + targetEntity: 1, + targetFilter: 1, + action: 1, + redirectTo: 1, + entity: 1, + entityId: 1, + iState: 1, + createdAt: 1, + updatedAt: 1 + }, + filter: { + iState: 'active', // 或 'done' + 'relation.collaborator.userId': userId + }, + order: { + createdAt: 'desc' + }, + limit: 20, + offset: 0 +}); +``` + +#### 6.2.2 统计待办数量 + +```typescript +const count = await context.count('toDo', { + filter: { + iState: 'active', + 'relation.collaborator.userId': userId + }, + count: 100 // 最多统计100个 +}); +``` + +#### 6.2.3 查询单个待办 + +```typescript +const [todo] = await context.select('toDo', { + data: { + id: 1, + title: 1, + description: 1, + targetEntity: 1, + targetFilter: 1, + action: 1, + redirectTo: 1, + iState: 1 + }, + filter: { + id: todoId + } +}); +``` + +#### 6.2.4 手动完成待办 + +虽然通常使用 `completeToDo` 函数,但也可以直接操作: + +```typescript +await context.operate('toDo', { + id: generateNewId(), + action: 'complete', + data: {}, + filter: { + id: todoId + } +}); +``` + +### 6.3 过滤条件说明 + +#### 6.3.1 常用过滤条件 + +```typescript +// 按状态筛选 +filter: { + iState: 'active' // 'active' | 'done' +} + +// 按目标实体筛选 +filter: { + targetEntity: 'order' +} + +// 按关联对象筛选 +filter: { + entity: 'order', + entityId: 'order-123' +} + +// 按协作用户筛选 +filter: { + 'relation.collaborator.userId': userId +} + +// 组合条件 +filter: { + iState: 'active', + targetEntity: 'order', + 'relation.collaborator.userId': userId, + createdAt: { $gte: startDate, $lte: endDate } +} +``` + +#### 6.3.2 时间范围查询 + +```typescript +// 查询最近7天的待办 +const sevenDaysAgo = new Date(); +sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7); + +filter: { + iState: 'active', + createdAt: { $gte: sevenDaysAgo } +} +``` + +### 6.4 Endpoint 接口 + +待办事项功能**没有提供 RESTful Endpoint**,所有操作都通过内部 Trigger 或前端直接调用实体操作完成。 + +如果需要对外提供 API,可以自行在项目中创建 Endpoint + +### 6.5 类型定义 + +#### 6.5.1 ToDo Schema + +```typescript +interface ToDoSchema { + id: string; + title: string; + description?: string; + targetEntity: string; + targetFilter: object; + action: string; + redirectTo: { + batchPath: string; + singlePath?: string; + }; + entity: string; + entityId: string; + iState: 'active' | 'done'; + createdAt: Date; + updatedAt: Date; +} +``` + +#### 6.5.2 ReadRemark Schema + +```typescript +interface ReadRemarkSchema { + id: string; + user: UserSchema; + session: SessionSchema; + createdAt: Date; +} +``` + +--- + +## 7. 总结 + +待办事项功能是 oak-general-business 中一个轻量级但强大的任务管理工具。它的核心优势在于: + +1. **自动化**:与业务流程深度集成,自动创建和完成待办 +2. **权限驱动**:基于权限系统自动分配任务 +3. **灵活性强**:可应用于任何实体的任何动作 +4. **易于集成**:通过简单的 Trigger 调用即可接入 + +虽然当前版本功能相对简单,没有提供完整的前端组件和 Aspect 封装,但其设计思路清晰,扩展性强,能够满足大多数业务场景的待办管理需求。 + +在实际使用中,建议: +- 在关键业务节点集成待办功能 +- 配合消息推送提升用户体验 +- 定期清理历史待办数据 +- 根据业务需要自行封装前端组件 + +--- + +*文档版本:v1.0* +*最后更新:2025年10月26日* +*维护者:oak-general-business 开发团队*