chengc 7 uur geleden
bovenliggende
commit
6ffb6d1b36

File diff suppressed because it is too large
+ 1105 - 229
docs/superpowers/specs/tboss-oa-api.md


+ 28 - 39
docs/superpowers/specs/tboss-oa-architecture.md

@@ -1,6 +1,6 @@
 # TBOSS OA 模块 — 系统架构设计
 
-> 版本:v1.0 | 日期:2026-06-03 | 基于 `tboss-oa-product-strategy.md`
+> 版本:v1.0 | 日期:2026-06-04 | 基于 PRD v1.0
 
 ---
 
@@ -35,16 +35,23 @@ graph TB
         subgraph 集成适配层
             C6["审批适配器<br/>ErpAAdapter / ErpBAdapter"]
             C7["消息适配器<br/>App Push / 站内消息"]
-            C8["数据适配器<br/>用户 / 部门 / 客户查询"]
+            C8["预算适配器<br/>BudgetService"]
+            C9["项目/科目适配器<br/>ProjectService / SubjectService"]
+            C10["成本中心适配器<br/>CostCenterService"]
+            C11["汇率适配器<br/>ExchangeRateService"]
+            C12["客户适配器<br/>CustomerService"]
         end
 
         C1 --> C2
         C1 --> C3
         C1 --> C4
         C1 --> C5
-        C2 --> C8
+        C2 --> C12
         C3 --> C6
         C3 --> C8
+        C3 --> C9
+        C3 --> C10
+        C3 --> C11
         C4 --> C7
     end
 
@@ -303,16 +310,14 @@ Tboss.OA.Server/
 │   ├── ReportService.cs
 │   └── ApplicationNoGenerator.cs      # 单据编号原子生成
-├── ErpIntegration/                     # ERP 集成适配器(统一模式,共 9 个)
+├── ErpIntegration/                     # ERP 集成适配器(V1 共 7 个)
 │   ├── IApprovalAdapter.cs            # 审批:创建实例/查询状态/执行动作/撤回
 │   ├── IBudgetAdapter.cs              # 预算:余额查询/冻结/扣减/释放
 │   ├── IProjectAdapter.cs             # 项目:列表/级联查询
 │   ├── ISubjectAdapter.cs             # 预算科目:按项目查科目列表
 │   ├── ICostCenterAdapter.cs          # 成本中心:列表查询
-│   ├── IStandardAdapter.cs            # 费用标准:按类型/城市/级别查上限
 │   ├── IExchangeRateAdapter.cs        # 汇率:币种→汇率查询
 │   ├── ICustomerAdapter.cs            # 客户:搜索/创建
-│   ├── ISupplierAdapter.cs            # 供应商:搜索(对公付款选供应商)
 │   ├── NullAdapters.cs                # 空实现集合(ERP 无能力时返回空/不限)
 │   └── ErpAdapterFactory.cs           # 按配置选取实现
@@ -371,7 +376,7 @@ Tboss.OA.Server/
 | GET | `/api/oa/approval/pending?userId=&bizType=&page=` | 待审批列表 |
 | GET | `/api/oa/approval/pending-count?userId=` | 待审批数量 |
 | GET | `/api/oa/approval/timeline?bizType=&bizId=` | 审批时间线 |
-| POST | `/api/oa/approval/action` | 审批动作(同意/拒绝/转交) |
+| POST | `/api/oa/approval/action` | 审批动作(同意/拒绝) |
 | POST | `/api/oa/approval/withdraw` | 撤回申请 |
 | GET | `/api/oa/approval/my-history?userId=&page=` | 我的审批历史 |
 | GET | `/api/oa/approval/subordinates?approverId=&bizType=&page=` | 下属单据列表 |
@@ -440,7 +445,7 @@ Tboss.OA.Server/
 ```
 集成方式:复用 .NET 服务端已有推送模块
 通道:App Push(原生通知栏)+ 站内消息(消息列表)
-触发点:审批创建/完成/拒绝/转交、公告发布、DING 催办
+触发点:审批创建/完成/拒绝、公告发布、DING 催办
 OA 职责:调用 .NET 消息 API 构造消息体,推送执行由消息模块完成
 ```
 
@@ -518,10 +523,8 @@ graph TB
   <add key="Oa:Adapter:Project" value="ErpA" />
   <add key="Oa:Adapter:Subject" value="ErpA" />
   <add key="Oa:Adapter:CostCenter" value="ErpA" />
-  <add key="Oa:Adapter:Standard" value="Null" />       <!-- ERP 暂未维护费用标准 -->
   <add key="Oa:Adapter:ExchangeRate" value="ErpA" />
   <add key="Oa:Adapter:Customer" value="ErpA" />
-  <add key="Oa:Adapter:Supplier" value="Null" />       <!-- 对公供应商暂自由文本 -->
   
   <!-- ERP API 地址 -->
   <add key="ErpA:BaseUrl" value="http://erp-a.internal/api/" />
@@ -568,8 +571,8 @@ public interface IApprovalAdapter
     // 查询审批时间线
     Task<List<TimelineNode>> GetTimelineAsync(string bizType, long bizId, string instanceId);
 
-    // 执行审批动作
-    Task<ActionResult> ExecuteActionAsync(string instanceId, string action, string opinion, long? transferToUserId);
+    // 执行审批动作(action: approve / reject)
+    Task<ActionResult> ExecuteActionAsync(string instanceId, string action, string opinion);
 
     // 撤回
     Task WithdrawAsync(string instanceId);
@@ -693,7 +696,6 @@ sequenceDiagram
     participant P as ProjectService
     participant S as SubjectService
     participant B as BudgetService
-    participant ST as StandardService
     participant A as ApprovalService
 
     OA->>NET: 打开表单
@@ -709,8 +711,6 @@ sequenceDiagram
     NET-->>OA: 预算余额 ¥XX
 
     OA->>NET: 提交
-    NET->>ST: 校验费用标准
-    ST-->>NET: { pass, exceedAmount }
     NET->>B: 冻结预算
     B-->>NET: { version }
     NET->>A: 创建审批实例(含 tags)
@@ -726,7 +726,6 @@ sequenceDiagram
     participant NET as .NET 服务端
     participant C as CostCenterService
     participant E as ExchangeRateService
-    participant ST as StandardService
     participant B as BudgetService
     participant A as ApprovalService
 
@@ -741,34 +740,26 @@ sequenceDiagram
     NET-->>OA: 本币金额 = 原币 × 7.2456
 
     OA->>NET: 提交
-    NET->>ST: 逐行校验费用标准
-    ST-->>NET: [{ line, pass, exceedAmount }]
-    alt 有超标行
-        NET-->>OA: 超标行标红,tags=["standard_exceeded"]
-    end
-    NET->>OA: 查未还借款 → 弹窗确认冲销
-    OA->>NET: 确认冲销明细
     NET->>B: 扣减预算
     B-->>NET: ok
-    NET->>A: 创建审批实例 { tags: [...] }
+    NET->>A: 创建审批实例 { tags }
     A-->>NET: { instanceId }
-    NET->>NET: 写 ExpenseApplicationMapping + LoanRepayment
+    NET->>NET: 写 ExpenseApplicationMapping
     NET-->>OA: { instanceId, status }
 ```
 
 ### 10.3 适配器覆盖矩阵
 
-| 环节 | Approval | Budget | Project | Subject | CostCenter | Standard | ExchRate | Customer | Supplier |
-|------|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|
-| 事前-填单 | | 余额 | ✅ | ✅ | | | | | |
-| 事前-提交 | ✅ 创建 | ✅ 冻结 | | | | ✅ 校验 | | | |
-| 事前-审批 | ✅ 流转 | | | | | | | | |
-| 事前-通过 | | ✅ 扣减 | | | | | | | |
-| 事前-变更 | ✅ 补充 | ✅ 追加冻结 | | | | ✅ | | | |
-| 报销-填单 | | | | | ✅ | | ✅ | | ✅ |
-| 报销-提交 | ✅ 创建 | ✅ 扣减 | | | | ✅ 校验 | | | |
-| 报销-审批 | ✅ 流转 | | | | | | | | |
-| 报销-撤回 | ✅ 撤回 | ✅ 释放 | | | | | | | |
+| 环节 | Approval | Budget | Project | Subject | CostCenter | ExchRate | Customer |
+|------|:--:|:--:|:--:|:--:|:--:|:--:|:--:|
+| 事前-填单 | | 余额 | ✅ | ✅ | | | |
+| 事前-提交 | ✅ 创建 | ✅ 冻结 | | | | | |
+| 事前-审批 | ✅ 流转 | | | | | | |
+| 事前-通过 | | ✅ 扣减 | | | | | |
+| 报销-填单 | | | | | ✅ | ✅ | |
+| 报销-提交 | ✅ 创建 | ✅ 扣减 | | | | | |
+| 报销-审批 | ✅ 流转 | | | | | | |
+| 报销-撤回 | ✅ 撤回 | ✅ 释放 | | | | | |
 
 
 ## 11. ERP 能力缺位时的 OA 自治扩展
@@ -806,13 +797,11 @@ sequenceDiagram
 | 能力 | 当前 | NullAdapter 行为 | 如需自建 |
 |------|------|-----------------|---------|
 | 预算 | 取决于 ERP | hasBudget=false → OA 隐藏余额 | +SysProjectBudget 表 |
-| 费用标准 | 取决于 ERP | hasStandard=false → 不校验超标 | +ExpenseStandard 表+管理页 |
 | 汇率 | 取决于 ERP | 只返回 CNY (rate=1) | +汇率表+维护页 |
 | 项目 | 始终需要 | 空列表 → 无下拉选项 | +SysProject 管理页 |
 | 成本中心 | 可空 | 空列表 → 可空不强制选 | +SysCostCenter 管理页 |
-| 供应商 | 可空 | 空列表 → 自由文本输入 | +SysSupplier 管理页 |
 
 
 ---
 
-> **文档版本**:v1.0 | 日期:2026-06-03
+> **文档版本**:v1.0 | 日期:2026-06-04

File diff suppressed because it is too large
+ 414 - 664
docs/superpowers/specs/tboss-oa-database.md


File diff suppressed because it is too large
+ 967 - 328
docs/superpowers/specs/tboss-oa-design.md


+ 0 - 605
docs/superpowers/specs/tboss-oa-interactions.md

@@ -1,605 +0,0 @@
-# TBOSS OA 模块 — 交互设计
-
-> 版本:v1.0 | 日期:2026-06-03 | 基于 `tboss-oa-product-strategy.md`
->
-> 覆盖 27 个页面的所有可交互元素及其操作逻辑。数据源标注遵循 v2.0 架构(审批走 ERP、用户/组织/客户走 ERP、消息走 .NET 服务端、权限走 OA ACL)。
-
----
-
-## 1. 全局数据源
-
-| 数据 | 来源 | 说明 |
-|------|------|------|
-| 用户信息(姓名/部门/岗位/头像) | .NET `GET /api/user/{id}` | 非本地表 |
-| 组织架构树 | .NET `GET /api/dept/tree` | 非本地表 |
-| 客户数据 | .NET `GET /api/customer/search?q=` | 非本地表 |
-| 审批流程(状态/时间线/待办) | .NET `GET/POST /api/oa/approval/*` | 服务端转发 ERP |
-| 消息通知 | .NET 消息模块 API | 服务端已有模块 |
-| OA 权限 | 本地 `OaUserPermission` JOIN `OaPermission` | OA 自管 |
-| 业务单据 | 本地 OA 业务表 | OA 自管 |
-| 车辆池 | 本地 `SysVehicle` | OA 自管 |
-| 费用类别 | 本地 `SysCostCategory` | OA 自管 |
-| 轮播图 | 本地 `SysBanner` | OA 自管 |
-| 银行列表 | .NET `GET /api/dict/banks` | 服务端字典 |
-
----
-
-## 2. 页面跳转关系总图
-
-```
-/ (Appshell)
-├─ /messages ──────────────────────────────────────────────────────
-│   └─ (点击) → /announcement/detail/:id
-│            → /expense-apply/detail/:id  /expense/detail/:id
-│            → /overtime/detail/:id       /vehicle/detail/:id
-│
-├─ /home ──────────────────────────────────────────────────────────
-│   ├─ (轮播图) → 公告/活动链接 或 全屏预览
-│   ├─ (金刚区-发起) → /expense-apply/apply  /expense/apply
-│   │                → /vehicle/apply         /overtime/apply
-│   ├─ (金刚区-记录) → /expense-apply/list    /expense/list
-│   │                → /outing-log/list       /announcement/list
-│   ├─ (看板) → /expense-apply/list  /report/expense-detail  /messages
-│
-└─ /profile
-    ├─ 审批历史 → (子页面) → 各详情页
-    ├─ 我的报表 → (子页面) → /report/.. (5 Tab)
-    └─ 关于 → (子页面)
-
-表单页 ──(存草稿/提交)──→ 列表页 ──(点击卡片)──→ 详情页
-    ↑                         │                    │
-    └──(左滑编辑/draft编辑)───┘  (rejected→重新编辑)┘
-
-/admin/permissions  (管理员)
-/announcement/create (管理员)
-```
-
----
-
-## 3. 页面详细交互
-
-### 页面 4.0:借款/备用金表单 (`/loan/apply`)
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| 借款类型 TDPicker | 选择 | 备用金/差旅借款 | Loan.LoanType |
-| 借款金额 TDInput | 输入 | 纯数字 | Loan.Amount |
-| 借款事由 TDTextarea | 输入 | | Loan.Purpose |
-| [存为草稿]/[提交审批] | 点击 | 复用标准逻辑,提交调 `POST /api/oa/loan/submit` | — |
-
-### 页面 4.0a:借款列表 (`/loan/list`)
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| TDChip(全部/草稿/审批中/已通过/已拒绝/已撤回) | 点击 | 标准筛选 | ApprovalStatus 缓存 |
-| 卡片额外展示 | — | 还款状态标签(未还/部分已还/已还清/已核销) | Loan.RepaymentStatus |
-| 已通过+未还清卡片左滑 | 手势 | `[登记还款]` → 选还款方式+金额 → `POST /api/oa/loan/repay` | — |
-
-### 页面 4.0b:借款详情 (`/loan/detail/:id`)
-
-已通过且未还清的借款底部展示还款记录列表(LoanRepayment 按时间倒序)。
-
----
-
-
-### 页面 1:Appshell 底部导航 (`/`)
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| 消息 Tab | 点击 | 路由 → `/messages`,图标变 `#00ABF3` | — |
-| 消息 Tab 角标 | 数据绑定 | 未读消息数;进入时拉取,30s 轮询或 Push 刷新 | .NET 消息模块 API |
-| 工作台 Tab | 点击 | 路由 → `/home` | — |
-| 我的 Tab | 点击 | 路由 → `/profile` | — |
-| 当前激活 Tab | 重复点击 | 若含列表 → ScrollToTop | — |
-
----
-
-### 页面 2:消息通知聚合页 (`/messages`)
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| 导航栏标题 | — | "消息通知" | — |
-| "全部已读" | 点击 | 仅未读>0 时显示;调用 API,红点淡出,按钮消失 | .NET 消息模块 API |
-| 消息卡片(公告类) | 点击 | → `/announcement/detail/:id` | `MsgType='announcement'` |
-| 消息卡片(审批待办) | 点击 | 按 BizType 路由 → 对应详情页,底部展审批操作栏 | `BizType` 决定路由 |
-| 消息卡片(审批结果) | 点击 | 按 BizType 路由 → 对应详情页 | `BizType` 决定路由 |
-| 已读消息卡片 | — | 透明度 0.6,无红点,不可左滑 | `IsRead=1` |
-| 未读卡片-左滑 | 手势 | 滑出 `[标记已读]`(蓝) + `[删除]`(红) | — |
-| `[标记已读]` | 点击 | 成功→红点淡出+透明度↓;失败→Toast"操作失败,请重试" | .NET 消息模块 API |
-| `[删除]` | 点击 | TDDialog 确认 → 卡片收起 | .NET 消息模块 API |
-| 列表 | 下拉/上拉 | 刷新 page=1 / page++ 追加 | .NET 消息模块 API |
-| 空列表 | — | TDEmpty + 铃铛:"暂无消息通知" | — |
-
----
-
-### 页面 3:工作台 (`/home`)
-
-**角色判定**(决定工作台变体):查 `OaUserPermission`:
-- 有 `oa.admin.*` → 管理员版
-- 无 admin 但有 `oa.expense.mark_paid` → 财务版
-- 无上述但有 `oa.*.approve` 或 `oa.*.view_dept` → 经理版
-- 其余 → 员工版
-
-#### 员工版
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| 轮播图 | 自动/点击 | 3s 切换;有 LinkUrl→跳转,无→全屏预览(双指缩放) | `SysBanner` (IsActive=1, IsDeleted=0) |
-| 金刚区-事前申请 | 点击 | → `/expense-apply/apply` | — |
-| 金刚区-费用报销 | 点击 | → `/expense/apply` | — |
-| 金刚区-用车申请 | 点击 | → `/vehicle/apply` | — |
-| 金刚区-加班申请 | 点击 | → `/overtime/apply` | — |
-| 金刚区-申请记录 | 点击 | → `/expense-apply/list`(默认"全部") | — |
-| 金刚区-报销记录 | 点击 | → `/expense/list` | — |
-| 金刚区-外勤日志 | 点击 | → `/outing-log/list` | — |
-| 金刚区-公司公告 | 点击 | → `/announcement/list` | — |
-| 看板-已提单据 | 点击 | → `/expense-apply/list` | .NET 预计算(缓存 5min) |
-| 看板-本月报销 | 点击 | → `/report/expense-detail` | .NET 预计算 |
-| 看板-待处理 | 点击 | → `/messages`(筛选 approval_notice) | .NET 预计算 |
-| 页面整体 | 下拉 | 强制穿透缓存刷新 | — |
-
-#### 经理版增量
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| 待审批卡片 TDBadge | — | 红色数字展示待办件数 | `GET /api/oa/approval/pending-count` |
-| 待审批卡片 | 点击 | → `/messages`(筛选 approval_notice) | — |
-
-#### 财务版增量
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| 财务看盘卡片 | — | 已支付流水/待付款总额/异常退回数 | .NET 全公司聚合查询 |
-
----
-
-### 页面 3.1:个人中心 (`/profile`)
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| 头像 TDAvatar | 点击 | 唤起相册/相机→裁剪(1:1,≤2MB)→上传→全局刷新 | `PUT /api/user/avatar` → .NET → ERP |
-| 个人信息卡片 | — | 姓名/部门/岗位 | `GET /api/user/{erpUserId}` |
-| 我的审批历史 | 点击 | → 子页面 | — |
-| 我的报表 | 点击 | → 子页面(5 Tab) | — |
-| 关于 | 点击 | → 版本号/用户协议/隐私政策 | — |
-
-#### 子页面:我的审批历史
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| 单据卡片-编号 | 点击 | 按 BizType 跳转详情页 | `GET /api/oa/approval/my-history` |
-| 列表 | 下拉/上拉 | 刷新/加载更多 | 同上 |
-| 空列表 | — | TDEmpty:"暂无审批记录" | — |
-
----
-
-### 页面 4:事前申请表单 (`/expense-apply/apply`)
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| 申请人姓名 | — | 只读 | `GET /api/user/{erpUserId}` → RealName |
-| 所属部门 | — | 只读 | `GET /api/user/{erpUserId}` → DeptName |
-| 申请日期 | — | 只读,DateTime.now(),YYYY-MM-DD | — |
-| 紧急程度 TDRadioGroup | 选择 | 普通/紧急/特急,默认"普通" | `ExpenseApplication.Urgency` |
-| 费用类型 TDCheckboxGroup | 多选 | 至少一项,联动过滤科目候选 | `ExpenseApplication.ExpenseTypes` |
-| 费用事由 TDTextarea | 输入 | ≤200 字 | `ExpenseApplication.Purpose` |
-| 关联项目 TDPicker | 级联 | 一级→ProjectService;二级→SubjectService | `ExpenseApplication.ProjectId` |
-| 预算科目 TDPicker | 选择 | 选定后调 BudgetService 加载可用余额 | `ExpenseApplication.BudgetSubjectId` |
-| 预算余额 | — | 只读 ¥XXXX.xx,ERP 无预算→隐藏区块 | .NET → ERP BudgetService |
-| 预估金额 TDInput | 输入 | 纯数字键盘;汇总超预算→变红+警告 | `ExpenseAppDetail.EstimatedAmount` |
-| [+ 添加费用明细] | 点击 | 展开新卡片,页面滚动聚焦 | — |
-| 明细卡片 ✕ | 点击 | 气泡确认→折叠消失 | — |
-| 附件上传 [+] | 点击 | 相册/相机/文件;≤10MB(图)/≤20MB(PDF) | `Attachment(BizType='expense_apply')` |
-| 附件缩略图 | 点击 | 黑底全屏预览,双指缩放(0.5x~3x),左右滑动 | — |
-| 附件 ⊖ | 点击 | 气泡确认→删除服务器文件 | — |
-| PDF 附件 | 点击 | 原生 PDF 查看器 | — |
-| [重置] | 点击 | 仅编辑草稿时可见;确认弹窗→清空 | — |
-| [存为草稿] | 点击 | Loading → PUT → Toast 绿→跳列表(激活"草稿"Chip) | `ExpenseApplication` (Status='draft') |
-| [提交审批] | 点击 | 校验→ScrollTo+红闪+TDMessage → Loading → POST → .NET → ERP 创建实例 → 存 ApprovalInstanceId → Toast → 跳列表 | .NET `POST /api/oa/expense-apply/submit` |
-| 返回(有未保存修改) | 点击 | TDDialog:"是否退出?" | — |
-
----
-
-### 页面 5:费用报销表单 (`/expense/apply`)
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| [导入事前申请] | 点击 | 半屏多选抽屉:approved 且尚有额度的申请,每条填导入金额;可追加多条;空→TDEmpty | .NET API → `ExpenseApplication` (Status='approved', UsageStatus IN unused/partially_used) |
-| 对公/对私 TDSwitch | 切换 | personal→收款人账户;corporate→供应商名称+对公账户 | `Expense.PaymentType` |
-| 报销事由 | 输入 | 可导入回填或手工 | `Expense.Purpose` |
-| 成本中心 TDPicker | 选择 | 可空 | .NET → ERP CostCenterService |
-| 报销总金额 | — | 只读,明细累加上浮动画 | `Expense.TotalAmount` |
-| 开户行/户名/账号 | 输入 | 对私展示;前端校验账号 16-19 位 | `Expense.BankName/AccountName/BankAccount` |
-| 供应商名称/账户 | 输入 | 对公展示 | `Expense.SupplierName` |
-| 币种 TDPicker | 选择 | 默认 CNY,选外币→自动从 ERP 填汇率 | `ExpenseDetail.CurrencyCode/ExchangeRate` |
-| 费用类别 TDPicker | 选择 | 叶子节点 | `SysCostCategory` |
-| 原币金额/本币金额 | 输入/只读 | 原币×汇率=本币(服务端算) | `ExpenseDetail.Amount/BaseAmount` |
-| 分摊设置 | 输入 | 比例+目标部门/项目;不分摊留空 | `ExpenseDetail.AllocationPercent/AllocationDeptId/AllocationProjectId` |
-| 借款冲销 | 提交时 | .NET 查未还借款→弹窗确认冲销明细 | `GET /api/oa/loan/outstanding` |
-| [+ 添加费用明细] | 点击 | 平滑展开新卡片 | — |
-| 明细行 ✕ | 点击 | 气泡确认→折叠 | — |
-| 发票上传 [+] | 点击 | 相册/相机,≤10MB,满 9 隐藏;可绑定明细行 | `Attachment(BizType='expense')` |
-| 发票缩略图 | 点击 | 黑底全屏预览 | — |
-| [存为草稿]/[提交审批] | 点击 | 复用页面 4 逻辑 | — |
-| 返回(有修改) | 点击 | 复用页面 4 逻辑 | — |
-
----
-
-### 页面 6/7:事前申请/费用报销列表页
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| 导航栏 | — | "申请记录" / "报销记录" | — |
-| TDChip(全部/草稿/审批中/已通过/已拒绝/已撤回) | 点击 | 高亮 `#00ABF3`,列表淡入淡出刷新;无数据→TDEmpty(文案随 Chip) | ApprovalStatus 缓存 |
-| 列表 | 下拉/上拉 | 刷新/追加 | 本地 OA 表 + 后台静默刷新 ApprovalStatus |
-| 卡片空白区 | 点击 | → 详情页 | — |
-| 卡片左滑-[编辑] | 点击 | 仅 draft/rejected → 表单编辑态 | — |
-| 卡片左滑-[删除] | 点击 | 仅 draft/rejected → TDDialog 确认 → 软删除 | — |
-| StatusTag | — | 仅展示(灰/橙/绿/红/灰) | — |
-
-**经理版增量**:
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| 范围标签 `[我的发起]` | 点击 | 员工版逻辑 | — |
-| 范围标签 `[下属审批]` | 点击 | 骨架屏→加载部门下属单据 | `GET /api/oa/approval/subordinates` |
-| 下属卡片左滑-[一键同意] | 点击 | 仅 pending;绿色按钮,就地审批 | `POST /api/oa/approval/action` |
-
-**财务版增量**(仅页面 7):
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| `[待付款]` Chip | 点击 | 筛选 Status='approved' AND PaymentStatus='unpaid' | 本地 Expense 表 |
-| 卡片左滑-[线下已付款] | 点击 | 标记 PaymentStatus='paid' | 本地 Expense 表 |
-
----
-
-### 页面 8/9:事前申请/费用报销详情页
-
-#### 员工版
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| 状态横幅 | — | 图标 + 状态中文 + 当前审批人姓名 | ApprovalStatus + 审批人姓名从 .NET API 获取 |
-| 提交时间 | — | YYYY-MM-DD HH:mm | CreateTime |
-| 费用明细"展开全部" | 点击 | >5 行时显示;平滑展开 | 本地子表 |
-| 附件缩略图 | 点击 | 黑底全屏预览 | 本地附件表 |
-| 审批时间线 | — | 纵向时间线渲染 | `GET /api/oa/approval/timeline?bizType=&bizId=` → .NET → ERP |
-| 时间线节点 | 点击 | 当前用户待审批→弹出审批操作;否则→展开详情 | — |
-| 审批人姓名/头像 | 点击 | TDDialog 展示基本信息卡片 | `GET /api/user/{approverId}` |
-| [编辑](draft) | 点击 | → 表单编辑态 | — |
-| [提交审批](draft) | 点击 | 校验→提交;失败→TDDialog 引导 | `POST /api/oa/approval/action?action=submit` |
-| [撤回申请](pending) | 点击 | TDDialog 确认 → Status='withdrawn' → 旧 instanceId 追加到 PreviousInstanceIds | `POST /api/oa/approval/withdraw` |
-| [重新编辑并发起](rejected) | 点击 | → 表单编辑态;提交后更新原记录+旧审批链失效 | — |
-
-#### 经理版底部操作栏(pending 状态 + 当前审批人=该经理)
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| [拒绝] | 点击 | TDDialog 输入理由(≥5 字解锁)→ POST | `POST /api/oa/approval/action` {action: reject} |
-| [转交] | 点击 | MethodChannel 唤起通讯录单选 → POST | `POST /api/oa/approval/action` {action: transfer} |
-| [同意] | 点击 | Loading → POST → 节点变绿 → 流转 | `POST /api/oa/approval/action` {action: approve} |
-| 风控参考看板 | — | 申请人本月同类单据数 + 预算消耗进度条 | 本地查询 + .NET 转发 ERP |
-
-#### 管理员版底部操作栏
-
-| 元素 | 事件 | 行为 |
-|------|------|------|
-| [强制终止] | 点击 | TDDialog 确认 → Status='withdrawn' + 写入终止原因 |
-| [查看审批链] | 点击 | 展开完整审批历史(含历史 instanceId 的流程) |
-
-#### 财务版增量(仅页面 9)
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| 合规复选框 ×3 | 勾选 | 全部勾选→解锁 [确认打款] 按钮 | `Expense.IsInvoiceVerified/IsTaxIdMatched/IsCategoryCompliant` |
-| [退回修改] | 点击 | 选退回节点(员工/经理)→状态倒流 | .NET API |
-| [确认已线下打款并归档] | 点击 | 解锁后;滑出凭证表单(电汇流水号+记账凭证号必填)→提交→Paid | `Expense.BankTransferNo` + `VoucherNo` |
-| [下一笔待付款] | 点击 | 归档后替换显示;跳转下一条 approved+unpaid;无→"已无待付款" | — |
-
----
-
-### 页面 10:加班申请表单 (`/overtime/apply`)
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| 加班类型 TDPicker | 选择 | 工作日/休息日/节假日,默认"工作日";节假日标注费率 | `Overtime.OtType` |
-| 补偿方式 TDRadio | 选择 | 转调休/结算加班费/混合,默认"转调休" | `Overtime.CompensationType` |
-| 混合模式 TDSlider | 拖动 | 10%~90%,步长 10%;右侧实时比例文案 | `Overtime.CompLeaveRatio` |
-| 开始/结束时间 | 点击 | TDDatePicker 年月日时分;确认后自动算净工时(扣除 12:00-13:00/18:00-18:30) | `Overtime.StartTime/EndTime` → `NetOtHours` |
-| 净工时卡片 | — | 只读大字;≤0→变红+提交按钮置灰 | `Overtime.NetOtHours` |
-| 开始>结束校验 | — | 边框变红+提示+提交按钮置灰 | — |
-| 加班原因 TDTextarea | 输入 | `Overtime.Reason` | — |
-| [存为草稿]/[提交审批] | 点击 | 复用页面 4 逻辑;提交时 .NET 创建 ERP 审批实例 | — |
-| 返回(有修改) | 点击 | 复用页面 4 逻辑 | — |
-
----
-
-### 页面 11:加班列表 / 页面 12:加班详情
-
-交互模式同页面 6/7 和 8/9。加班类型标签(工作日/休息日/节假日)、补偿方式展示(加班费/转调休/"X%调休+Y%结算")。员工端仅 `[撤回]`。
-
----
-
-### 页面 13:用车申请表单 (`/vehicle/apply`)
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| 车牌号 TDPicker | 选择 | 全公司车池;冲突车辆标红"冲突";无可用→TDEmpty | `SysVehicle` (IsDeleted=0) |
-| 车牌+出车/还车时间 | 失焦 | 异步排期冲突检测;冲突→红色文本+锁死提交按钮 | `Vehicle` (Status IN pending,approved) |
-| 用车事由 | 输入 | | `Vehicle.Reason` |
-| 始发地 | 定位/输入 | 原生定位匹配 | `Vehicle.Origin` + 经纬度 |
-| 目的地+地图图标 | 输入/点击 | MethodChannel 唤醒地图选点→回填地址+经纬度 | `Vehicle.Destination` + 经纬度 |
-| 出车/还车时间 | 点击 | TDDatePicker 年月日时分 | `Vehicle.StartTime/EndTime` |
-| 还车>出车校验 | — | 否则红色提示+按钮置灰 | — |
-| 同行总人数 | 输入 | 纯数字,0→1 | `Vehicle.PassengerCount` |
-| 随行同行人 [+] | 点击 | MethodChannel 唤醒通讯录多选→胶囊标签;取消→无变化 | `VehiclePassenger` |
-| 胶囊标签 x | 点击 | 剔除人员 | — |
-| [存为草稿]/[提交审批] | 点击 | 复用页面 4 逻辑 | — |
-
----
-
-### 页面 14:用车列表 / 页面 15:用车详情
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| TDChip(全部/草稿/审批中/已通过/已拒绝/已撤回/已还车) | 点击 | 标准逻辑 | ApprovalStatus 缓存 |
-| 卡片左滑-[编辑]/[删除] | 点击 | 仅 draft/rejected | — |
-| 卡片左滑-[确认还车] | 点击 | 仅 approved;拉起半屏核销抽屉 | — |
-| 微缩地图 | 点击 | 唤醒原生地图导航 | 本地经纬度 |
-| [确认还车并登记] | 点击 | 滑出核销抽屉:实还时间/出车前里程/还车后里程/费用备注 | `Vehicle` 还车字段 |
-| 还车里程校验 | — | 还车后 ≥ 出车前,否则按钮置灰+红提示 | — |
-| 确认提交还车 | 点击 | TDDialog 确认→Status='returned'→页面刷新 | — |
-
----
-
-### 页面 16:外勤日志创建 (`/outing-log/create`)
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| GPS 定位区 | 页面初始化 | 强制高精度 GPS → ReadOnly 展示地址;精度>100m→黄色警告 | MethodChannel → `OutingLog.CheckIn*` |
-| GPS 定位失败 | — | TDEmpty"无法获取当前位置";提交按钮置灰 | — |
-| 客户名称 TDInput | 输入 | 自动联想;无匹配→自由文本(提交时 .NET 自动创建 ERP 客户) | `GET /api/customer/search?q=` |
-| 工作总结 TDTextarea | 输入 | | `OutingLog.VisitSummary` |
-| 后续计划 | 输入 | | `OutingLog.NextPlan` |
-| 现场拍照 | 点击 | 跳过相册,直接相机;水印=服务器授时+GPS;最少1张最多4张 | Attachment(BizType='outing_log') |
-| 相机权限被拒 | — | TDDialog + "前往设置" | — |
-| [存为草稿]/[提交] | 点击 | 校验 GPS+照片≥1 | — |
-| 返回(有修改) | 点击 | 复用页面 4 逻辑 | — |
-
----
-
-### 页面 17:外勤日志列表 / 页面 18:外勤日志详情
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| TDChip(全部/本月) | 点击 | 切换刷新 | — |
-| 卡片"新点评" TDBadge | — | 橙色标记 | `OutingLogComment.CreateTime > COALESCE(LastViewedTime, '1900-01-01')` 且评论者≠本人 |
-| 卡片左滑-[编辑]/[删除] | 点击 | 仅 draft | — |
-| 微缩地图 | 点击 | 唤醒原生地图导航 | 本地经纬度 |
-| 照片墙 | 点击 | 全屏预览 | Attachment(BizType='outing_log') |
-| 页面进入 | — | 无感 PUT 更新 LastViewedTime,列表"新点评"消失 | `PUT /api/oa/outing-log/:id/view` |
-| 主管点评区(员工端) | — | 只读浏览,不可回复 | `OutingLogComment` |
-
-**经理版增量**:
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| TDRate 星级 | 点选 | 1-5 星 | `OutingLogComment.RatingStars` |
-| 点评文本框 | 输入 | 批示文字 | `OutingLogComment.CommentText` |
-| 发送按钮 | 点击 | 气泡追加+动画 | `POST /api/oa/outing-log/:id/comment` |
-
----
-
-### 页面 19:公告列表 (`/announcement/list`)
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| TDChip 类型筛选 | 点击 | 全部/通知公告/人事与制度/放假与活动/我的草稿(仅管理员) | `Announcement.Type` + `Status` |
-| 排序规则 | — | 置顶→未过期(PublishTime DESC)→已过期(PublishTime DESC) | `IsTop`, `PublishTime`, `ExpiryDate` |
-| 已过期卡片 | — | 整体置灰+标题末尾"已过期" | `ExpiryDate < NOW` |
-| 未读红点 | — | 返回列表后红点消失 | `AnnouncementReadLog.IsRead=0` |
-| 公告卡片 | 点击 | → `/announcement/detail/:id` | — |
-| 搜索 | — | 不支持 | — |
-
----
-
-### 页面 20:公告详情 (`/announcement/detail/:id`)
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| 正文 | — | HTML/Markdown 渲染 | `Announcement.Content` |
-| 已过期横幅 | — | 红色"该公告已于 YYYY-MM-DD HH:mm 过期" | `Announcement.ExpiryDate` |
-| 附件图标 | 点击 | 原生浏览器/下载管理器;失败→Toast | Attachment(BizType='announcement') |
-| 停留 ≥2s | — | 无感标记已读;<2s 返回不发送 | `POST /api/oa/announcement/:id/read` |
-
-**管理员版增量**:
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| [已读 N 人] Chip | 点击 | 展开已读员工列表(头像+部门) | `AnnouncementReadLog` (IsRead=1) + .NET 用户 API |
-| [未读 N 人] Chip | 点击 | 展开未读员工列表 | `AnnouncementReadLog` (IsRead=0) |
-| [一键 DING] | 点击 | 震动反馈→MethodChannel 强推 Push/短信 | `AnnouncementReadLog.IsUrged=1` |
-
----
-
-### 页面 21~25:五大明细报表 (`/report/..`)
-
-| 元素 | 事件 | 行为 |
-|------|------|------|
-| TDDropdownMenu(本月/本季/本年) | 选择 | 数值卡片+图表 300ms 渐变刷新 |
-| 数值卡片 | — | 只读;空→0/¥0.00 |
-| fl_chart 数据点 | 长按 | tooltip(日期+数值) |
-| fl_chart 图表 | 水平滑动 | 查看历史数据 |
-| 图表为空 | — | TDEmpty:"所选时间范围内暂无数据" |
-
-**经理版增量**:柱状图柱体点击 → 下方列表联动过滤该员工本月单据 → 可穿透跳详情页。
-
-**财务版增量**(仅页面 22):部门树/项目组级联筛选 + `[数据流水一键导出]` → 后端生成 Excel → MethodChannel 原生分享。
-
----
-
-### 页面 26:公告发布 (`/announcement/create`)
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| 公告标题 TDInput | 输入 | | `Announcement.Title` |
-| 分类下拉 | 选择 | notice/policy/activity | `Announcement.Type` |
-| 富文本编辑域 | 操作 | 加粗/斜体/下划线/列表/图片/链接/字号 | `Announcement.Content` |
-| 附件上传 [+] | 点击 | ≤5 个,PDF/图片/Word/Excel,≤20MB | Attachment(BizType='announcement') |
-| 置顶 TDSwitch | 切换 | 默认关闭 | `Announcement.IsTop` |
-| 有效期 TDDatePicker | 选择 | 非必填,不填永不过期 | `Announcement.ExpiryDate` |
-| 接收范围选择器 | 点击 | 右侧滑出部门树多选(Checkbox);默认全员;底部统计人数 | `AnnouncementTarget` |
-| [预览] | 点击 | TDDialog 全屏模拟详情页 | — |
-| [存为草稿] | 点击 | 保存(仅创建者+管理员可见) | `Announcement.Status='draft'` |
-| [发布] | 点击 | 确认弹窗 → 写入 + 异步初始化 AnnouncementReadLog → Toast | `Announcement.Status='published'` |
-| 返回(有修改) | 点击 | 复用页面 4 逻辑 | — |
-
----
-
-### 页面 27:权限管理 (`/admin/permissions`)
-
-| 元素 | 事件 | 行为 | 数据来源 |
-|------|------|------|---------|
-| TDSearchBar | 输入 | 300ms 防抖模糊搜索→骨架屏→刷新;无结果→TDEmpty | `GET /api/user/search?q=` |
-| 员工列表 | 下拉/上拉 | 刷新/每页 20 条 | 默认按部门+姓名排序 |
-| 员工卡片 | 点击 | 右侧滑出 TDDrawer 权限编辑抽屉 | — |
-| 快捷套餐按钮 ×4 | 点击 | 一键赋予标准权限集(员工/审批人/财务/管理员) | `OaUserPermission` |
-| 权限点复选框 | 勾选 | 逐项加减 | `OaUserPermission` |
-| 启用/禁用 TDSwitch | 切换 | 经理有待审批时警告确认 | OA 权限状态 |
-| [确认保存] | 点击 | Loading → PUT → Toast + 写审计日志 | `OaUserPermission` + `OaPermissionChangeLog` |
-| 【变更记录】折叠区 | 展开 | TDTimeline 展示最近 20 条 | `OaPermissionChangeLog` |
-| 抽屉关闭(有未保存修改) | — | 确认弹窗"是否放弃?" | — |
-| 自保护 | — | 无法取消自己的 admin 权限 → Toast | — |
-| 最后管理员保护 | — | 后端拒绝移除最后一个 admin | — |
-
----
-
-## 4. API 端点汇总
-
-### OA 业务 API(.NET 服务端新增)
-
-```
-# 权限
-GET    /api/oa/permissions
-GET    /api/oa/user-permissions?userId=
-PUT    /api/oa/user-permissions
-GET    /api/oa/permission-changelog?userId=&page=
-
-# 业务单据
-POST   /api/oa/expense-apply/submit
-POST   /api/oa/expense/submit
-POST   /api/oa/overtime/submit
-POST   /api/oa/vehicle/submit
-PUT    /api/oa/{bizType}/draft
-GET    /api/oa/{bizType}/list?status=&page=
-GET    /api/oa/{bizType}/detail/:id
-DELETE /api/oa/{bizType}/:id
-
-# 外勤日志
-PUT    /api/oa/outing-log/submit
-PUT    /api/oa/outing-log/:id/view
-POST   /api/oa/outing-log/:id/comment
-
-# 审批代理
-GET    /api/oa/approval/pending?userId=&bizType=&page=
-GET    /api/oa/approval/pending-count?userId=
-GET    /api/oa/approval/timeline?bizType=&bizId=
-POST   /api/oa/approval/action
-POST   /api/oa/approval/withdraw
-GET    /api/oa/approval/my-history?userId=&page=
-GET    /api/oa/approval/subordinates?approverId=&bizType=&page=
-
-# 公告
-POST   /api/oa/announcement/publish
-POST   /api/oa/announcement/:id/read
-POST   /api/oa/announcement/:id/ding
-GET    /api/oa/announcement/read-stats/:id
-
-# 报表
-GET    /api/oa/report/{type}?range=&userId=
-POST   /api/oa/report/expense/export
-
-# 车辆/轮播图
-GET    /api/oa/vehicles
-POST   /api/oa/vehicles
-PUT    /api/oa/vehicles/:id
-GET    /api/oa/banners
-```
-
-### 复用已有 API
-
-```
-GET    /api/user/{id}
-GET    /api/user/search?q=&page=
-GET    /api/dept/tree
-PUT    /api/user/avatar
-GET    /api/customer/search?q=
-GET    /api/messages?page=
-GET    /api/messages/unread-count
-PUT    /api/messages/:id/read
-POST   /api/messages/read-all
-DELETE /api/messages/:id
-GET    /api/dict/banks
-GET    /api/dict/cost-categories
-```
-
----
-
-## 5. 全局交互规范
-
-### 5.1 空状态
-
-| 场景 | 文案 |
-|------|------|
-| 消息列表为空 | "暂无消息通知" |
-| 申请列表为空 | "暂无记录,点击下方按钮发起申请" |
-| 审批列表为空 | "暂无待审批单据" |
-| 搜索无结果 | "未找到匹配的员工,试试其他关键词" |
-| 公告列表为空 | "暂无行政公告" |
-
-### 5.2 加载态
-
-| 场景 | 方式 |
-|------|------|
-| 页面首次加载 | TDSkeleton 骨架屏(最长 8s 超时转网络异常态) |
-| 下拉刷新 | RefreshIndicator |
-| 上拉加载更多 | 底部菊花 + "加载中..." |
-| 按钮提交中 | 按钮替换为 TDLoading 小菊花 + 禁用态 |
-| 附件上传中 | 缩略图叠加半透明蒙层 + 中心菊花 |
-
-### 5.3 网络异常态
-
-| 场景 | 方式 |
-|------|------|
-| 列表加载失败 | TDEmpty + 断网图标 + [点击重试] |
-| 提交失败 | TDToast(红)"提交失败,请稍后重试" |
-| 接口超时(>15s) | TDEmpty + [点击重试] |
-| 401 未授权 | 静默触发宿主登录流程 |
-
-### 5.4 全局交互
-
-- **横竖屏**:仅支持竖屏
-- **键盘**:聚焦时页面上移;金额类弹数字键盘;文本类弹默认键盘
-- **返回手势**:iOS 边缘右滑;Android 系统返回键;表单页有未保存修改时拦截确认
-- **Deep Link**:`tboss://oa/{path}` → GoRouter 路由
-- **并发冲突**:编辑/审批冲突 → TDDialog 提示 → 刷新
-- **字体缩放**:sp 单位,最小 11sp,导航栏标题不溢出
-
-### 5.5 设计 Token
-
-| Token | 值 | 用途 |
-|-------|------|------|
-| `--primary` | `#00ABF3` | 主按钮/激活态/高亮/链接 |
-| `--success` | `#00A870` | 已通过/同意/金额正面 |
-| `--warning` | `#E37318` | 审批中/超支警告/冲突提示 |
-| `--danger` | `#D54941` | 已拒绝/拒绝/删除 |
-| `--text-primary` | `#1A1A1A` | 标题/关键金额 |
-| `--text-secondary` | `#666666` | 摘要/辅助说明 |
-| `--bg-page` | `#F5F5F5` | 页面背景 |
-| `--bg-card` | `#FFFFFF` | 卡片/表单底色 |
-| `--border` | `#E7E7E7` | 分割线/描边 |
-
----
-
-> **文档版本**:v1.0 | 日期:2026-06-03

File diff suppressed because it is too large
+ 351 - 89
docs/superpowers/specs/tboss-oa-prd.md


+ 53 - 151
docs/superpowers/specs/tboss-oa-product-strategy.md

@@ -1,18 +1,18 @@
 # TBOSS OA 模块 — 产品思路
 
-> 版本:v1.0 | 日期:2026-06-03 | 状态:已确认
+> 版本:v1.0 | 日期:2026-06-04 | 基于 PRD v1.0 | 状态:已确认
 
 ---
 
 ## 1. 模块定位
 
-在已有 ERP App 内嵌入一个 OA 功能模块(Flutter),后端在现有 .NET Framework 4.8 服务端上新增 API。
+在已有 ERP App 内嵌入一个 OA 功能模块(Flutter),后端在现有 .NET Framework 4.8 服务端上新增 API。V1 实现 8 个核心模块:事前申请、费用报销、加班申请、用车申请、外勤日志、公告管理、数据报表、权限管理,加上工作台共 9 个功能页面组。
 
 **OA 只负责**:业务表单的录入与展示、公告管理、报表展示、OA 独立权限控制。
 
-**不复建的已有能力**:用户与组织架构、审批引擎、消息通知推送、客户主数据——均复用 ERP/.NET 服务端
+**不复建的已有能力**:用户与组织架构、审批引擎、消息通知推送、预算系统、项目主数据、成本中心、客户主数据、汇率——全部通过 .NET 适配器层从 ERP 复用
 
-**Why**:公司已有成熟 ERP 和多套审批 API,消息通知也在 .NET 服务端上跑着。OA 是一个移动端业务前端,不是独立系统。
+**Why**:公司已有成熟 ERP 和多套审批 API,OA 是一个移动端业务前端,不是独立系统。
 
 ---
 
@@ -31,13 +31,17 @@
 │  已有(复用):                                               │
 │  ├─ 用户/部门/客户数据 ── 来自多套 ERP 主数据                    │
 │  ├─ 消息通知推送 ────── App Push + 站内消息                    │
-│  └─ 审批操作代理 ────── 适配器层(每套 ERP 一个 Adapter)       │
+│  ├─ 审批引擎适配 ────── 适配器层(每套 ERP 一个 Adapter)       │
+│  ├─ 预算系统适配 ────── BudgetService(余额/冻结/扣减/释放)     │
+│  ├─ 项目/科目/成本 ──── ProjectService/SubjectService/CostCenterService │
+│  ├─ 汇率适配 ────────── ExchangeRateService                   │
+│  └─ 客户适配 ────────── CustomerService                       │
 │                                                              │
 │  新增 OA 能力:                                               │
 │  ├─ 权限管理 ── OA 独立权限点 + 快捷套餐                        │
 │  ├─ 业务单据 ── 报销/事前/加班/用车/外勤 CRUD + 审批操作入口      │
 │  ├─ 公告管理 ── 发布/浏览/已读追踪/DING 催办                    │
-│  └─ 报表数据 ── 5 大明细报表聚合查询               
+│  └─ 报表数据 ── 5 大明细报表聚合查询(按角色数据范围)
 └─────────────────────────────────────────────────────────────┘
 ```
 
@@ -53,14 +57,9 @@
 
 ### 3.1 设计背景
 
-**约束**:
-- ERP 采用 ACL 模型:按单据/模块给每个用户赋予增删改查权限,没有预设的"财务""采购""销售"角色
-- OA 端也需要审批单据(事前申请、报销单、用车申请、加班申请)
-- ERP 的权限和 OA 的权限是两套独立体系
+**约束**:ERP 采用 ACL 模型,没有预设的"财务""采购""销售"角色。OA 端也需要审批单据。ERP 的权限和 OA 的权限是两套独立体系。
 
-**讨论过程**:是否保留 4 角色?→ "审批人"和"财务"在 OA 端如何定义?→ 结论:角色是权限的快捷套餐,底层是 ACL。
-
-### 3.2 权限点清单(~20 个)
+### 3.2 权限点清单
 
 ```
 报销:
@@ -72,8 +71,7 @@
   oa.expense.mark_paid        标记已付款(财务核销)
 
 事前申请:
-  oa.expense_apply.apply
-  oa.expense_apply.view_own / view_dept / view_all / approve
+  oa.expense_apply.apply / view_own / view_dept / view_all / approve
 
 加班:
   oa.overtime.apply / view_own / view_dept / view_all / approve
@@ -89,7 +87,7 @@
 公告:
   oa.announcement.view        浏览公告
   oa.announcement.publish     发布公告
-  oa.announcement.manage      管理(编辑/删除/置顶)
+  oa.announcement.manage      管理(编辑/删除/置顶/DING
 
 报表:
   oa.report.view_own / view_dept / view_all
@@ -98,7 +96,6 @@
 管理:
   oa.admin.permissions        管理 OA 权限
   oa.admin.vehicles           管理车辆池
-  oa.admin.customers          管理客户池
   oa.admin.banners            管理工作台轮播图
 ```
 
@@ -107,179 +104,87 @@
 | 套餐 | 包含 | 适用 |
 |------|------|------|
 | **员工** | apply + view_own + create + announcement.view | 默认 |
-| **审批人** | 员工 + approve + view_dept + outing_log.comment | 部门主管 |
+| **审批人** | 员工 + approve + view_dept + outing_log.comment | 部门主管/老板 |
 | **财务** | view_all + expense.mark_paid + report.view_all + report.export | 核销人员 |
 | **管理员** | 全部权限 | IT/HR 管理员 |
 
 ### 3.4 "审批人"和"财务"的定义
 
-**审批人角色 ≠ 可以审批所有单据。** OA 端只是:
-- 解锁审批操作栏 UI(同意/拒绝/转交按钮)
-- 解锁部门级数据范围(列表页"下属审批"标签、部门报表)
-- 解锁外勤日志点评功能
-
-具体谁能审批哪个单据,**由 ERP 审批引擎的审批链配置决定**,OA 只是把操作入口和审批结果展示出来。
+**审批人角色 ≠ 可以审批所有单据。** OA 端只是:解锁审批操作栏 UI(同意/拒绝按钮)、解锁部门级数据范围(列表页"下属审批"标签、部门报表)、解锁外勤日志点评功能。具体谁能审批哪个单据,由 ERP 审批引擎的审批链配置决定。
 
 **财务角色同理**:解锁发票合规核销区 + 打款归档按钮 + 全公司数据 + 导出。不参与正常审批流。
 
-**Why**:ERP 已经有完整的审批引擎和审批链配置,OA 侧的角色本质上是功能可见性开关 + 数据范围边界,不是数据权限的最终裁判。
+**Why**:OA 侧的角色本质上是功能可见性开关 + 数据范围边界,不是数据权限的最终裁判。
 
 ---
 
 ## 4. 审批对接方案
 
-### 4.1 OA 不存审批配置和记录
-
-审批链配置、审批记录、审批流转全部由 ERP 审批引擎管理。
-
-**Why**:审批引擎是 ERP 核心能力,OA 是消费者而非替代者。OA 存一份审批数据会造成双写和一致性难题。
+### 4.1 数据存储
 
-### 4.2 业务表新增字段
-
-每张需要审批的业务主表(ExpenseApplication / Expense / Overtime / Vehicle):
+OA 不存审批配置和记录。每张审批类业务主表(ExpenseApplication / Expense / Overtime / Vehicle)新增两个字段:
 
 ```
 ApprovalInstanceId     VARCHAR(50)     -- 当前有效的 ERP 审批实例 ID
-ApprovalStatus         VARCHAR(20)     -- OA 本地缓存状态(列表页秒开用)
-PreviousInstanceIds    VARCHAR(MAX)    -- 历史实例 ID JSON 数组
-                                       -- 驳回重新发起时追加旧 ID
+PreviousInstanceIds    VARCHAR(MAX)    -- 历史实例 ID JSON 数组(驳回/撤回后追加)
 ```
 
-**Why 3 个字段**:
-
-- `ApprovalInstanceId` 是必须的,关联 ERP 审批实例
-- `ApprovalStatus` 是性能缓存:列表页 20 条单据如果每条都跨系统查 ERP 会卡。缓存状态下拉刷新时校准
-- `PreviousInstanceIds` 用于追溯驳回重新提交前的审批历史。如果 ERP 能按 bizType+bizId 直接查全部审批历史则不需要此字段(待确认)
+列表页、详情页的审批状态均通过 .NET → ERP 实时查询,不本地缓存。
 
-### 4.3 状态同步策略
+### 4.2 状态同步策略
 
-**约束**:ERP 审批完成后没有回调 OA 的机制。
+**约束**:ERP 审批完成后没有回调 OA 的机制。OA 不缓存审批状态。
 
 | 场景 | 策略 |
 |------|------|
-| 列表页 | 展示本地缓存 ApprovalStatus(秒开);后台静默批量刷新 |
-| 详情页 | 打开时实时从 ERP 刷新校准 |
-| 下拉刷新 | 强制穿透缓存 |
-| 审批操作后 | 立即写缓存 + 异步校准 |
+| 列表页 | 打开时实时调 .NET → ERP 查询每笔单据的审批状态,数据始终最新 |
+| 详情页 | 打开时实时从 ERP 刷新 |
+| 审批操作后 | 立即更新本地数据 |
 | 消息通知 | 由 .NET 服务端消息模块在审批完成后从 ERP 侧触发推送 |
 
-**Why**:没有回调的情况下,列表页每次全量跨系统查询太慢,缓存 + 按需刷新是代价最低的平衡方案
+**Why**:V1 用户量小(百人级),ERP 内网直连延迟低,实时查询的性能代价可接受。去掉缓存简化了数据模型和同步逻辑
 
-### 4.4 多 ERP 适配
+### 4.3 ERP 集成适配器清单
 
-公司有多套 ERP 系统,审批 API 接口签名不一致。适配工作全部在 .NET 服务端完成
+所有外部数据通过 .NET 适配器模式获取。ERP 无某能力时 Adapter 返回空,OA 隐藏对应功能区块
 
-```
-.NET 服务端
-  ┌──────────────────────────┐
-  │  OA ApprovalService      │  ← 统一接口(Flutter 调的)
-  ├──────────────────────────┤
-  │  ErpAAdapter             │  ← 翻译 ERP-A 的 API 格式
-  │  ErpBAdapter             │  ← 翻译 ERP-B 的 API 格式
-  │  ErpCAdapter             │  ← 翻译 ERP-C 的 API 格式
-  └──────────────────────────┘
-```
-
-每套 ERP 需要提供的能力:
-
-| OA 需要的 | ERP 审批 API 提供 |
-|----------|------------------|
-| 提交单据进入审批流 | 发起审批 |
-| 获取单据当前审批状态 | 查询实例状态 |
-| 获取审批时间线 | 查询流程节点历史 |
-| 获取某人的待审批列表 | 查询待办 |
-| 执行审批(同意/拒绝/转交) | 审批动作接口 |
-
-**Why**:Flutter 端只需要统一的 REST API,多 ERP 的差异由服务端适配器模式隔离,后续新增 ERP 只需增加一个 Adapter。
+| 适配器 | 用途 | ERP 无能力时 |
+|--------|------|------------|
+| ApprovalService | 审批引擎(提交/查询/审批/撤回) | —(必须) |
+| UserService | 用户信息(姓名/部门/岗位/头像) | —(必须) |
+| DepartmentService | 组织架构树 | —(必须) |
+| BudgetService | 预算余额/冻结/扣减/释放 | 隐藏预算区块 |
+| ProjectService | 项目下拉 | 隐藏项目选择器 |
+| SubjectService | 预算科目下拉 | 隐藏科目选择器 |
+| CostCenterService | 成本中心下拉 | 隐藏成本中心字段 |
+| ExchangeRateService | 汇率查询 | 仅支持 CNY |
+| CustomerService | 客户搜索/创建 | 手动输入客户名 |
 
 ---
 
 ## 5. 功能模块与数据范围
 
-### 5.1 模块总览
-
 | 模块 | 走审批 | 核心数据来源 |
 |------|--------|------------|
-| 事前申请 | ✅ ERP | OA 业务表 + ERP 审批实例 |
-| 费用报销 | ✅ ERP | OA 业务表 + ERP 审批实例 |
+| 事前申请 | ✅ ERP | OA 业务表 + ERP 审批实例 + ERP 预算 |
+| 费用报销 | ✅ ERP | OA 业务表 + ERP 审批实例 + ERP 预算/汇率 |
 | 加班申请 | ✅ ERP | OA 业务表 + ERP 审批实例 |
-| 用车申请 | ✅ ERP | 车辆池 OA 自管;审批走 ERP |
+| 用车申请 | ✅ ERP | 车辆池 OA 自管(SysVehicle);审批走 ERP |
 | 外勤日志 | ❌ | OA 自闭环;客户名关联 ERP 客户 ID |
-| 公告 | ❌ | OA 完全自闭环 |
-| 报表 | ❌ | OA 业务表 + ERP 审批状态 |
-
-### 5.2 数据所有权
-
-```
-OA 独立维护:
-  ├─ 所有业务单据及明细(报销/事前/加班/用车/外勤/公告)
-  ├─ 附件(支撑材料/发票影像/外勤照片/公告附件)
-  ├─ 外勤点评
-  ├─ 车辆池(ERP 不管公车)
-  ├─ 公告触达审计(已读/未读/DING)
-  ├─ OA 权限数据
-  └─ 轮播图配置
-
-从 ERP 查询(不持久化):
-  ├─ 用户信息(姓名/部门/岗位/头像)
-  ├─ 组织架构树
-  ├─ 客户数据
-  ├─ 审批流程数据(状态/时间线/待办)
-  └─ 项目/预算科目/成本中心
-```
+| 公告管理 | ❌ | OA 完全自闭环 |
+| 数据报表 | ❌ | OA 业务表 + ERP 审批状态 |
+| 权限管理 | ❌ | OA 独立权限表 |
+| 工作台 | ❌ | OA 本地 + .NET 聚合查询 + ERP 审批引擎 |
 
 ---
 
-## 6. 数据库表变更
-
-### 6.1 删除(不再由 OA 管理)
-
-| 表 | 原因 |
-|----|------|
-| SysUser | ERP 管理用户 |
-| SysRole / SysUserRole | 改为 OA 权限模型 |
-| SysRoleChangeLog | 改为 OaPermissionChangeLog |
-| SysDepartment | ERP 管理组织架构 |
-| SysCostCenter / SysProject / SysBudgetSubject | ERP 管理,通过 .NET 适配器查询 |
-| SysCustomer / SysCustomerContact | ERP 管理客户,通过 .NET 适配器查询 |
-| SysBank | .NET 服务端字典 |
-| ApprovalChain / ApprovalRecord | ERP 审批引擎管理 |
-| Message | .NET 服务端消息模块管理 |
-| SysProjectBudget / ExpenseStandard | ERP 管理,通过 BudgetService / StandardService 适配器 |
-
-### 6.2 新增
+## 6. 数据存储原则
 
-```
-OaPermission         -- 权限点字典
-OaUserPermission     -- 用户-权限关联(基于 ERP UserId)
-OaPermissionChangeLog -- 权限变更审计
-ExpenseApplicationMapping -- 申请↔报销多对多
-ExpenseApplicationChange -- 申请变更/追加记录
-Loan / LoanRepayment  -- 借款/备用金及还款
-```
-
-### 6.3 业务表变更
+**OA 自管**:业务单据(事前/报销/加班/用车/外勤/公告)、附件(Attachment 统一聚合)、权限(OaPermission)、车辆池(SysVehicle)、轮播图(SysBanner)、费用类别字典(SysCostCategory)。
 
-所有审批类主表(ExpenseApplication / Expense / Overtime / Vehicle)新增:
-- `ApprovalInstanceId VARCHAR(50)`
-- `ApprovalStatus VARCHAR(20)`
-- `PreviousInstanceIds VARCHAR(MAX)`
+**ERP 管理**:用户、部门、角色、客户、项目、预算科目、成本中心、审批链、审批记录、消息——全部通过适配器查询,OA 不持久化。
 
-引用 ERP 数据的字段改为存储 ERP ID(非 FK 约束):
-- `DeptId`、`ProjectId`、`BudgetSubjectId`、`CostCenterId` → ERP 对应实体 ID
-- `OutingLog.CustomerId` → ERP 客户 ID
-
-### 6.4 保留不变
-
-OA 自管的核心表保持原有结构:
-- 业务主表:ExpenseApplication, Expense, Overtime, Vehicle, OutingLog, Announcement
-- 业务子表:ExpenseAppDetail, ExpenseDetail, VehiclePassenger
-- 附件表:Attachment(统一聚合,BizType 区分)
-- 借款表:Loan, LoanRepayment
-- 申请↔报销关联:ExpenseApplicationMapping(多对多)
-- 申请变更:ExpenseApplicationChange
-- 互动/审计表:OutingLogComment, AnnouncementReadLog, AnnouncementTarget
-- 基础表:SysVehicle, SysBanner
+详细表结构见 `tboss-oa-database.md`。
 
 ---
 
@@ -293,13 +198,11 @@ OA 自管的核心表保持原有结构:
 | 预算控制 | 提醒级 | 冻结级 | 取决于 ERP 预算能力 |
 | 角色模型 | 链式主管 | 岗位/相对角色 | **权限点 ACL + 快捷套餐** |
 | 移动端深度 | 功能全但不可定制 | 偏 PC | **原生嵌入(相机/通讯录/地图/GPS)** |
-| 事前→报销 | 关联控件 | 一键转换+预算冻结 | **一键导入+三级追踪+分批报销** |
+| 事前→报销 | 关联控件 | 一键转换+预算冻结 | **拼单导入+三级追踪+分批报销** |
 | 审批形态 | 基础 | 会签/或签/加签 | 取决于 ERP 审批引擎 |
 
 **已有竞争力**:预算冻结+三级使用追踪、外勤防伪(GPS只读+水印)、公告触达审计+DING催办、复合角色+工作台动态变体。
 
-**差距(部分取决于 ERP 能力)**:会签/或签审批形态、OCR 发票识别、离线审批。
-
 ---
 
 ## 8. 待确认
@@ -307,6 +210,5 @@ OA 自管的核心表保持原有结构:
 | # | 事项 | 影响 |
 |---|------|------|
 | 1 | ERP 审批历史是按 instanceId 还是 bizType+bizId 查询? | 决定是否要 PreviousInstanceIds |
-| 2 | 各套 ERP 的具体 API 签名差异 | .NET 服务端适配器层开发量(审批+预算+项目+科目+成本+标准+客户+汇率) |
+| 2 | 各套 ERP 的具体 API 签名差异 | .NET 适配器层开发量 |
 | 3 | ERP 客户表的主键类型和查询 API | 外勤日志客户关联的实现方式 |
-| 4 | ERP 费用标准表结构 | StandardService 适配器的查询参数 |