# TBOSS OA 模块 — 产品需求文档

> 版本：v1.1 | 日期：2026-06-27 | 状态：待评审

---

## 1. 产品背景

### 1.1 业务背景

公司已有成熟的 ERP 系统（多套部署实例），支撑企业日常运营。ERP 覆盖了用户管理、组织架构、客户主数据、审批引擎、消息通知等核心能力。现有 Android/iOS App 已嵌入 ERP 相关功能。

**当前痛点**：

- **OA 流程依赖 PC 或线下**：费用报销、费用申请、加班、用车等审批流程缺乏统一的移动端入口
- **外勤管理无数字化手段**：业务员外出拜访缺少定位打卡和现场拍照核验机制
- **公告触达不可追踪**：行政通知发布后无法确认哪些员工已读、哪些未读
- **数据孤岛**：各业务单据分散在不同系统，缺少统一的个人/部门报表视图

### 1.2 产品目标

在已有 ERP App 内嵌入 OA 功能模块（Flutter 技术栈），为员工、审批人、财务人员、管理员提供移动端 OA 办公能力。

**核心原则**：不重复造轮子。ERP 已有的用户体系、审批引擎、消息推送、预算系统、项目主数据通过适配器复用。V1 聚焦于 8 个核心模块的"填单→审批→查看"闭环。

### 1.3 市场定位

| | 通用平台（钉钉/企微） | 专业 OA（蓝凌/泛微） | **TBOSS OA** |
|---|---|---|---|
| 定位 | 独立 SaaS 平台 | 独立 OA 产品 | **ERP 移动端外挂** |
| 优势 | 生态丰富 | 管控深度 | **无缝嵌入已有系统** |
| 劣势 | 不可定制 | 移动端体验弱 | 依赖 ERP 能力边界 |

TBOSS OA 的核心差异化：**不是独立产品，而是 ERP 的移动 OA 外挂**——用户无需切换 App、无需维护两套账号、直接调用原生能力（相机/通讯录/地图/GPS）。

### 1.4 成功指标

| 指标 | 目标值 | 衡量方式 |
|------|--------|---------|
| 报销全流程耗时 | 从填单到付款到账 ≤3 个工作日 | 系统记录 CreateTime → PaymentStatus=paid 时间差 |
| 移动端 OA 覆盖率 | 80% 以上 OA 单据通过移动端发起 | 移动端提交量 / 总提交量 |
| 公告触达率 | 发布后 24h 内 ≥85% 员工已读 | AnnouncementReadLog.IsRead=1 / 应达总人数（发布时固定快照） |
| 财务核销效率 | 单笔核销操作 ≤2 分钟 | 详情页打开 → 归档完成时间差 |
| 系统可用性 | 99.5%（月度） | 监控平台 |

### 1.5 明确不做的事（V1 边界）

以下功能有意识排除，避免范围蔓延：

| 不做 | 理由 |
|------|------|
| 审批链可视化配置 | 审批链在 ERP 端维护，OA 只消费 |
| 自定义表单模板 | V1 固定表单字段 |
| 在线支付 / 银企直连 | 财务线下打款后录入流水号 |
| OA 端创建用户 / 部门 | 用户和组织架构由 ERP 管理 |
| 独立的消息推送通道 | 复用 .NET 服务端已有推送 |
| 电子签章 | — |
| PC Web 端 | V1 仅移动端（Flutter） |
| 预付/借款冲抵 | V1 暂不支持（OffsetAmount 字段保留为 0） |
| 跨部门费用承担 | V1 费用承担部门自动等于申请人所属部门 |

---

## 2. 用户画像

### 2.1 普通员工（Employee）

| 属性 | 描述 |
|------|------|
| **身份** | 公司基层员工，归属某个部门 |
| **日常场景** | 出差前提交费用申请（可含多费用类型，关联项目+预算）→ 审批通过 → 回来提交费用报销（支持外币折算）→ 加班/用车/外勤日志 |
| **核心诉求** | 快速填单、拍发票、随时查看审批进度、草稿暂存 |
| **使用频率** | 每周 2-5 次 |
| **移动端偏好** | 表单简洁、支持拍照上传、操作反馈及时（撤回/编辑/查看状态） |

### 2.2 审批人 / 部门经理（Approver）

| 属性 | 描述 |
|------|------|
| **身份** | 部门主管/有审批职责的人员，通常直接管理 5-20 名下属 |
| **日常场景** | 收到审批待办通知 → 查看单据详情 + 风控参考 → 同意/拒绝；查看部门报表；在外勤日志下点评打分 |
| **核心诉求** | 快速了解单据全貌（申请人历史、预算消耗）、批量处理待办、移动端审批操作流畅 |
| **使用频率** | 每天 3-10 次 |
| **移动端偏好** | 待办红点醒目、卡片信息密度高、支持左滑快捷操作、拒绝时要求填写原因 |

### 2.3 财务人员（Finance）

| 属性 | 描述 |
|------|------|
| **身份** | 财务核销专员，负责发票合规查验和线下打款确认 |
| **日常场景** | 查看全公司已通过待付款的报销单 → 核验发票影像 → 手动勾选三项合规检查 → 线下打款 → 录入电汇流水号和记账凭证号归档 |
| **核心诉求** | 发票影像清晰可查验、合规检查项手动确认、连续核销不退回列表、数据可导出 |
| **使用频率** | 每天 5-20 笔核销 |
| **移动端偏好** | 发票大图预览、合规复选框联动锁定、一键全部通过、凭证归档表单、导出 Excel |

### 2.4 系统管理员（Admin）

| 属性 | 描述 |
|------|------|
| **身份** | IT 管理员 / HR 管理员 |
| **日常场景** | 为员工分配 OA 权限角色；发布/管理公司公告；DING 催办未读员工；查看公告触达率 |
| **核心诉求** | 权限配置灵活（套餐 + 单点调整）、公告富文本编辑、触达审计可视化 |
| **使用频率** | 每周 2-5 次 |
| **移动端偏好** | 搜索员工快捷、权限勾选直观、公告发布有预览 |

---

## 3. 核心功能列表

### 3.1 功能模块总览

```
模块              子功能                       角色
─────────────────────────────────────────────────────
📋 费用申请    │  表单/列表/详情              │ 全员
💰 费用报销    │  表单/列表/详情/财务核销     │ 全员 + 财务
⏰ 加班申请    │  表单/列表/详情              │ 全员
🚗 用车申请    │  表单/列表/详情/还车登记     │ 全员
📍 外勤日志    │  创建/列表/详情/点评         │ 业务员 + 经理
📢 公告管理    │  列表/详情/发布/触达审计     │ 全员 + 管理员
📊 数据报表    │  5 大明细报表               │ 全员（按角色范围）
🔧 权限管理    │  用户权限配置                │ 管理员
🏠 工作台      │  首页/金刚区/报表/消息/个人中心 │ 全员
```

### 3.2 工作台（Home）

工作台是登录后的默认首页，内容按角色权限动态变化。权限判定依据 OaUserPermission，优先级 Admin > Finance > Approver > Employee，高优先级角色叠加低优先级角色的基础区块。

#### 所有角色共有

| 区块 | 说明 |
|------|------|
| 轮播图 | 顶部 Banner 轮播（3s 自动切换），数据来源 OA 本地 SysBanner 表。管理员可在后台维护。点击有链接则跳转，无链接则全屏预览 |
| 功能金刚区 | 4 列宫格，分三个区域。**【发起】**第一行：费用申请/费用报销/用车申请/加班申请。有 `oa.announcement.publish` 权限的用户额外显示：发布公告。**【记录】**第二行：申请记录/报销记录/加班记录/用车记录，第三行：外勤日志/公司公告。**【报表】**第四行：费用申请报表/费用报销报表/加班报表/用车报表，第五行：外勤日志报表。每个报表点击直接进入对应报表页 |

#### 员工版

在共有基础上增加快捷看板，两张数据卡片仅展示**本人**数据：

| 卡片 | 内容 | 点击跳转 | 数据来源 |
|------|------|---------|---------|
| 本月累计报销 | 本月本人已付款报销总额（¥） | 报销记录列表 | .NET 预计算：SUM(Expense.BaseAmount WHERE ApplicantId=本人 AND PaymentStatus=paid) |
| 本月已提单据 | 本月本人提交的全部单据数（笔） | 申请记录列表 | .NET 预计算：COUNT(所有业务表 WHERE ApplicantId=本人 AND 本月) |

数据缓存 5 分钟，下拉强制刷新。

#### 经理/老板版

在员工版基础上叠加两个区块：

**待审批卡片**：金刚区下方醒目的红色角标卡片，展示**本人**待审批单据数量和最新摘要。数据来源 .NET → ERP 审批引擎。点击跳转消息列表（筛选审批待办）。

**快捷看板升级**：三张卡片的数据范围从"本人"升级为**本部门**汇总：

| 卡片 | 内容 | 点击跳转 | 数据来源 |
|------|------|---------|---------|
| 本月累计报销 | 本部门已付款报销总额（¥） | 报销记录列表（部门视图） | .NET 预计算：SUM(Expense.BaseAmount WHERE DeptId=本部门 AND PaymentStatus=paid) |
| 本月已提单据 | 本部门全部单据数（笔） | 申请记录列表（部门视图） | .NET 预计算：COUNT(所有业务表 WHERE DeptId=本部门 AND 本月) |
| 部门在途单据 | 本部门审批中的单据数（笔） | 申请记录列表（部门视图，筛选审批中） | .NET → ERP 审批引擎：COUNT(pending WHERE DeptId=本部门) |

如果用户同时拥有经理和员工权限，显示经理版（高优先级叠加）。

#### 财务版

在员工版基础上，将快捷看板替换为财务看盘，三张大数字卡片展示**全公司**数据：

| 卡片 | 内容 | 数据来源 |
|------|------|---------|
| 本月已支付总额 | 全公司本月 PaymentStatus=paid 的报销总额（¥） | .NET 预计算：SUM(Expense.BaseAmount WHERE PaymentStatus=paid AND 本月) |
| 待付款总额 | 全公司当前已通过但未付款的报销总额（¥） | .NET 预计算：SUM(Expense.BaseAmount WHERE Status=approved AND PaymentStatus=unpaid) |
| 本周异常退回 | 本周财务退回修改的报销单数量（笔） | .NET 预计算：COUNT(Expense WHERE 本周被退回) |

数据缓存 5 分钟，下拉强制刷新。财务看盘替代快捷看板，财务不展示个人快捷看板。

#### 管理员版

在员工版基础上叠加：金刚区发起区域有 `oa.announcement.publish` 权限的用户额外显示"发布公告"入口。快捷看板展示**全公司**数据（与财务版相同，三张大数字卡片：本月已支付总额 / 待付款总额 / 本周异常退回）。数据来源 .NET 全公司聚合查询，缓存 5 分钟。

---

### 3.3 费用申请（Expense Application）

| 功能 | 优先级 | 说明 |
|------|--------|------|
| 新建申请 | P0 | 多费用类型（可同时勾选差旅+办公等）。关联项目（.NET → ERP ProjectService）→ 加载科目（.NET → ERP SubjectService）→ 查预算余额。预估明细行金额，附件上传 |
| 预算查看 | P0 | 选完项目+科目后实时展示可用余额（BudgetService）。余额不足时金额标红并触发超支审批链。ERP 无预算数据时跳过校验，不影响提交。预算服务不可用时隐藏预算信息，允许正常提交 |
| 草稿暂存 | P0 | 草稿可多次编辑后提交。草稿仅创建者本人可见（管理员也不例外），所有查询入口排除草稿 |
| 提交审批 | P0 | 校验必填项+预算 → 提交至 ERP 审批引擎。超预算时允许提交但携带 `budget_exceeded` 标签，由 ERP 决定是否进入特批链路 |
| 列表筛选 | P0 | 全部/草稿/审批中/已通过/已拒绝/已撤回/已归档 |
| 详情查看 | P0 | 状态横幅 + 费用明细 + 附件 + 审批时间线 |
| 撤回申请 | P1 | pending 状态可撤回（任何审批阶段均可撤，ERP 最终裁决） |
| 删除草稿 | P1 | draft 状态可删除（仅本人），删除后不可恢复 |
| 终态归档 | P0 | 当 UsageStatus=fully_used 时，Status 自动流转为 archived，不可再被引用 |

### 3.4 费用报销（Expense Reimbursement）

| 功能 | 优先级 | 说明 |
|------|--------|------|
| 新建报销 | P0 | 两种方式：①导入费用申请（项目/科目自动带入，填每张导入金额）；②直接新建（手动选项目→科目→查预算，与费用申请相同的链式操作）。均支持外币明细（自动汇率折算）、附件上传（图片/PDF/Word/Excel，表头附件+明细行附件）。支付方式（PaymentMethod）由员工在填单时选择 |
| 导入费用申请 | P0 | 从已通过且尚有额度的申请多选导入，每条填导入金额。导入后自动回填项目/科目，额度实时校验，超申请金额不可提交。严格按 BizScope 过滤，不可导入 expense_apply 专属类别 |
| 列表筛选 | P0 | 全部/草稿/审批中/已通过/待付款/已付款/已拒绝/已撤回/已归档 |
| 详情查看 | P0 | 状态横幅 + 费用明细 + 审批时间线 + 附件预览 |
| 撤回申请 | P1 | pending 状态可撤回（任何审批阶段均可撤，ERP 最终裁决），撤回后立即释放已占用的申请额度 |
| 删除草稿 | P1 | draft 状态可删除（仅本人） |
| 草稿/提交 | P0 | 支持存为草稿后继续编辑，提交时校验必填项+预算，提交至 ERP 审批引擎。从费用申请导入的报销单提交时不重复校验预算 |
| 财务核销 | P0 | 发票合规三字查验（验真/税号/类目）→ 三项均由财务人员手动勾选，提供"一键全部通过"快捷操作 → 录入电汇流水号+记账凭证号 → 归档。单笔 ≤200 元小额零星支出时系统自动勾选三项，财务仍可取消 |
| 连续核销 | P1 | 归档后跳转下一笔待付款（FIFO：CreateTime 最早优先） |
| 退回修改 | P1 | 退给员工重填 或 退给经理重审 |

### 3.5 加班申请（Overtime）

| 功能 | 优先级 | 说明 |
|------|--------|------|
| 新建申请 | P0 | 加班类型（工作日/休息日/节假日）、补偿方式（转调休/加班费/混合）、开始/结束时间、原因 |
| 净工时自动计算 | P0 | 扣除午休(12:00-13:00)和晚餐(18:00-18:30)盲区 |
| 混合模式 | P1 | 调休比例滑块(10%-90%，步长10%)，实时显示分配文案 |
| 列表筛选 | P0 | 全部/草稿/审批中/已通过/已拒绝/已撤回/已归档 |
| 详情查看 | P0 | 状态横幅 + 加班信息 + 审批时间线 |
| 撤回申请 | P1 | pending 状态可撤回（任何审批阶段均可撤，ERP 最终裁决） |
| 删除草稿 | P1 | draft 状态可删除（仅本人） |

### 3.6 用车申请（Vehicle）

| 功能 | 优先级 | 说明 |
|------|--------|------|
| 新建申请 | P0 | 车牌号选择（草稿时可空，提交时必填）、始发地/目的地（支持地图选点）、出车/还车时间、同行人（通讯录多选或手动输入外部人员） |
| 排期冲突检测 | P0 | 选好车牌+时间后实时防抖校验，冲突时警告并置灰提交按钮。草稿状态下也支持实时检测（但不锁车） |
| 列表筛选 | P0 | 全部/草稿/审批中/已通过/已拒绝/已撤回/已还车/已归档 |
| 详情查看 | P0 | 状态横幅 + 行程信息 + 审批时间线 + 地图标记 |
| 撤回申请 | P1 | pending 状态可撤回（任何审批阶段均可撤，ERP 最终裁决） |
| 删除草稿 | P1 | draft 状态可删除（仅本人） |
| 还车登记 | P0 | approved 状态可登记还车：实还时间、出车前/还车后里程表读数、路桥停车费。还车登记完成后 Status 自动流转为 returned，再自动流转为 archived，车辆状态恢复 idle |
| 车辆池管理 | P1 | 管理员维护车辆信息（车牌/类型/品牌/座位/驾驶员/状态）。maintenance 状态车辆不可被申请 |

### 3.7 外勤日志（Outing Log）

| 功能 | 优先级 | 说明 |
|------|--------|------|
| 新建日志 | P0 | GPS 自动定位（只读，不可手动修改）、客户名（自动联想 ERP 客户池）、工作总结、后续计划。草稿状态下不强制 GPS，提交时校验 |
| 现场拍照 | P0 | 强制调用相机（不可选相册），照片带防伪水印（服务器授时+经纬度），最少 1 张最多 9 张。区分签到照（sign_in_photo）和现场照（visit_photo） |
| 主管点评 | P0 | 经理在详情页底部星级评分 + 文字点评，气泡样式展示。红点基于 CreateTime，修改点评不触发新红点 |
| 列表筛选 | P0 | 全部/草稿/已完成 |
| 详情查看 | P0 | 定位地图 + 工作摘要 + 照片墙 + 点评历史 |
| 新点评标记 | P1 | 列表页橙色"新点评"标记（MAX(CreateTime) > LastViewedTime）。LastViewedTime 仅打开详情页时更新 |
| 客户动态创建 | P2 | 输入新客户名时提交后自动在 ERP 创建 |
| 提交即锁定 | P0 | 提交后 Status=completed，不可编辑。仅管理员可软删除 |

### 3.8 公告管理（Announcement）

| 功能 | 优先级 | 说明 |
|------|--------|------|
| 公告列表 | P0 | 置顶优先 → 未过期按发布时间倒序 → 已过期置灰；类型筛选；不支持搜索 |
| 公告详情 | P0 | 红头文件样式、HTML/Markdown 正文、附件下载；停留 ≥2s 自动标记已读 |
| 发布公告 | P0 | 富文本编辑、置顶开关、有效期、接收范围（全员/部门树/指定用户）、预览。全员时写入 TargetType='all' 记录 |
| 触达审计 | P1 | 管理员查看已读/未读员工列表。已离职员工在未读名单中标记灰色"已离职"，催办时自动过滤 |
| DING 催办 | P1 | 管理员在公告详情页查看已读/未读名单，对未读员工一键推送高优先级通知。可多次催办 |

### 3.9 数据报表（Reports）

报表入口、按角色数据范围、详细图表说明见 §3.11。此处仅列出优先级。

| 功能 | 优先级 | 说明 |
|------|--------|------|
| 费用申请明细报表 | P1 | 含数值卡片 + 趋势图，按角色控制数据范围 |
| 费用报销明细报表 | P1 | 同上 |
| 加班明细报表 | P1 | 同上 |
| 用车明细报表 | P1 | 同上 |
| 外勤日志明细报表 | P1 | 同上 |
| 数据导出 | P2 | 财务角色可导出 Excel |

### 3.10 权限管理（Admin）

| 功能 | 优先级 | 说明 |
|------|--------|------|
| 用户搜索 | P0 | 支持按姓名或工号模糊搜索，输入过程中自动联想匹配 |
| 权限配置 | P0 | 快捷套餐 + 单点加减；无角色用户默认等同"员工"。快捷套餐权限点清单见附录 |
| 变更审计 | P1 | 权限变更历史时间线 |
| 自保护 | P0 | 管理员无法取消自己的 admin 角色；至少保留一名管理员 |

### 3.11 报表入口与权限

**入口**：唯一入口为工作台金刚区报表区域，点击任一报表直接进入对应独立页。五个报表各自独立路由、独立加载数据。页面内顶部为筛选条件，下方为数值卡片 + 趋势图。

**角色数据范围**：

同一报表页，不同角色看到的数据范围不同：

| 角色 | 数据范围 | 展示形式 | 可导出 |
|------|---------|---------|:--:|
| 员工 | 仅本人 | 数值卡片 + 个人趋势图 | ❌ |
| 经理/老板 | 本部门 | 数值卡片 + 部门横向对比柱状图 + 明细列表 | ❌ |
| 财务 | 全公司 | 数值卡片 + 全公司趋势图 + 明细列表 | ✅ Excel |
| 管理员 | 全公司 | 与财务相同 | ✅ |

**数据来源**：所有报表数据均来自 OA 本地业务表（费用申请/费用报销/加班/用车/外勤日志主表及明细表）。.NET 服务端按角色权限（本人/本部门/全公司）和时间范围聚合查询后返回。审批状态相关统计（如"待审批笔数"）通过 .NET → ERP 实时查询补充。数值卡片和趋势图共用同一数据源，仅展示维度不同。

**性能策略**：预置时间段（本月/本季/本年）数据由 .NET 预计算，缓存 5 分钟。自定义时间段实时查询。数值卡片数据同时受时间筛选和角色数据范围双重约束，无数据时显示 0 或 ¥0.00，仅做展示不可点击。

**各报表详细规格**：

| 报表 | 数值卡片 | 专属筛选 | 员工图表 | 经理图表 | 财务/管理员图表 |
|------|---------|---------|---------|---------|-------------|
| 费用申请明细 | ①累计申请总额（含草稿）②当月笔数 ③已通过金额（Status='approved'，含 fully_used）④待审批笔数 | 状态（全部/已通过/已拒绝/已撤回/已归档） | 个人近 12 月申请金额 vs 已通过金额双折线 | 本部门横向对比柱状图（每人两根柱：申请/已通过）+ 底部明细列表 | 全公司趋势图（同员工）+ 底部明细列表 |
| 费用报销明细 | ①累计核销总额（PaymentStatus='paid'）②当月笔数 ③待审批笔数 ④待付款笔数 | 状态 + 付款状态 | 个人近 12 月报销金额 vs 审批通过金额双折线 | 本部门横向对比柱状图（每人两根柱：报销/通过）+ 底部明细列表 | 全公司趋势图 + 底部明细列表 |
| 加班明细 | ①当月净工时 ②当月次数 ③累计调休小时（comp_leave 100%净工时，mixed 按 CompLeaveRatio 比例）④加班费次数 | 加班类型（工作日/休息日/节假日） | 个人近 12 月加班工时柱状图，按类型分色堆叠 | 本部门横向对比柱状图（每人汇总工时）+ 底部明细列表 | 全公司趋势图 + 底部明细列表 |
| 用车明细 | ①当月次数 ②累计里程（仅 Status='returned'，EndOdometer-StartOdometer）③路桥停车费 ④未还车数 | 车辆 + 用途 | 个人近 12 月用车次数折线 | 本部门横向对比柱状图（每人次数/费用）+ 底部明细列表 | 全公司趋势图（次数 vs 费用双轴）+ 底部明细列表 |
| 外勤日志明细 | ①当月拜访次数 ②拜访客户数（去重）③平均评分（至少一条点评，取最新点评，无点评显示"暂无点评"）④未点评数 | 客户名模糊搜索 | 个人近 12 月拜访次数 vs 评分双轴折线 | 本部门横向对比柱状图（每人次数/评分）+ 底部明细列表 | 全公司趋势图 + 底部明细列表 |

**数值卡片说明**：所有卡片数据受时间筛选（本月/本季/本年）和角色数据范围（本人/本部门/全公司）双重约束。无数据时显示 0 或 ¥0.00。卡片仅做展示，不可点击。

**经理版交互增强**：点击柱状图上代表某员工的柱子 → 下方列表联动过滤该员工本月单据 → 可穿透跳转对应详情页。

各角色工作台看板卡片与报表的关系：看板卡片（§3.2）展示的是关键汇总数字，点击卡片跳转到的是对应单据列表页；金刚区报表入口（本小节）展示的是完整的趋势图表分析页。两者互补：看板做决策提醒，报表做深度分析。

### 3.12 关键业务规则澄清

| # | 规则 | 说明 |
|----|------|------|
| **审批链配置** | 审批链在 ERP 端维护，OA 不存储不配置。OA 检测到超预算时，通过 `ApprovalService.SubmitAsync(tags: ["budget_exceeded"])` 告知 ERP，由 ERP 审批引擎决定是否插入特批审批节点 |
| **审批数据来源** | 审批状态、审批时间线、待办列表均通过 .NET → ERP 实时查询，OA 本地不存储。列表页和详情页每次打开均实时请求，保证数据始终最新 |
| **多 ERP 路由** | 宿主 App 登录时已确定用户所属 ERP 实例，OA 通过 .NET 服务端上下文获取，无需额外路由逻辑。一人跨多 ERP 场景由宿主 App 切换账套时重新初始化 |
| **附件存储** | 统一上传至云端对象存储（OSS），`Attachment.FileUrl` 存绝对 URL。访问通过 CDN 加速。上传限制：图片/PDF/Word/Excel ≤20MB。附件存储不设自动清理机制 |
| **无发票场景** | 单笔费用 ≤200 元且类别为办公费/交通卡充值等小额零星支出时，三项合规检查自动勾选，财务人员仍可手动取消勾选 |
| **申请过期处理** | `ValidUntil` 到期后，定时 Job 自动释放冻结预算（调 BudgetService.Release），`UsageStatus` 标记为 `fully_used`，`Status` 流转为 `archived`。通知申请人"申请已过期，预算已释放" |
| **报表数据权限** | 个人报表：仅本人可见。部门报表（经理）：按人汇总展示 + 可展开逐笔明细。全公司报表（财务/管理员）：展示全公司所有明细。数据范围由角色权限控制，详见 §3.11 |
| **审批撤回窗口** | `Status=pending` 即可撤回（不限制当前审批级别）。已审批通过的节点历史保留，流程终止。ERP 不支持撤回时，OA 按钮仍展示，调 ERP 后被拒则弹错误提示 |
| **预算、项目、科目** | 三个概念构成两级核算体系。**项目**是企业内按目标划分的独立核算单元。**科目**是费用类别在财务口径下的分类。**预算**挂在**项目×科目**的交叉点上。操作链：选项目 → 加载科目列表 → 选定科目后查询可用余额。三个字段均通过 .NET 适配器从 ERP 获取，ERP 无数据时对应下拉为空，后续预算校验自动跳过 |
| **外币/汇率** | 报销明细行默认币种 CNY。员工选择外币后，OA 调 .NET → ERP ExchangeRateService 获取当日汇率，自动填入 `ExchangeRate`。服务端计算 `BaseAmount = TotalAmount × ExchangeRate`，预算校验和报表均以本币（CNY）为准 |
| **费用申请状态** | **审批状态（Status）**：`draft`→可编辑/提交/删除；`pending`→可撤回；`approved`→可被报销引用；`rejected`→可重新编辑提交；`withdrawn`→仅查看；`archived`→已归档终态。**使用状态（UsageStatus）**：`unused`/`partially_used`/`fully_used`（仅 `approved` 后有意义）。当 `UsageStatus=fully_used` 时 `Status` 自动流转为 `archived` |
| **费用报销状态** | **审批状态（Status）**：`draft`→`pending`→`approved`/`rejected`/`withdrawn`/`archived`。**付款状态（PaymentStatus）**：仅 `approved` 后生效，`unpaid`→财务核销后变为 `paid`。`PaymentStatus=paid` 且业务完结后 `Status` 自动流转为 `archived` |
| **加班申请状态** | 同费用申请，审批状态：`draft`→`pending`→`approved`/`rejected`/`withdrawn`/`archived`。业务完结后自动归档 |
| **用车申请状态** | 审批状态同其他单据，额外终态：`returned`（已还车）→`archived`。完整流转：`draft`→`pending`→`approved`→`returned`→`archived`（还车登记完成，车辆恢复空闲） |
| **外勤日志状态** | 不走审批流，`draft` 草稿→`completed` 已提交。提交即完成且锁定不可编辑，仅管理员可软删除 |
| **公告状态** | 不走审批流，`draft` 草稿（仅创建者和管理员可见）→`published` 已发布（全员可见，触发触达记录写入） |
| **列表筛选** | 使用顶部 Tabs 选项卡按状态筛选，按时间倒序排列。Tabs 下方增加筛选栏（日期范围 + 业务专属维度）。各单据 Tabs：**事前/加班** 全部/草稿/审批中/已通过/已拒绝/已撤回/已归档；**报销** 全部/草稿/审批中/已通过/待付款/已付款/已拒绝/已撤回/已归档；**用车**额外含已还车；**外勤日志**全部/草稿/已完成。**公告**——**员工侧** Tabs 为公告类型（全部/通知公告/人事制度/放假活动），仅展示已发布公告；**管理员侧**在类型 Tabs 基础上，右上角多一个"我的草稿"入口，点开仅展示自己创建的草稿 |
| **列表搜索** | 各列表页在经理范围切换（如有）和状态 Tab 之间提供搜索框（`TDSearchBar`），客户端本地过滤。搜索栏位：**费用报销**：报销单号、申请人姓名；**费用申请**：申请单号、申请人姓名；**加班申请**：加班单号、申请人姓名；**用车申请**：用车单号、申请人姓名；**外勤日志**：客户名称、业务员姓名；**公告**：公告标题 |
| **经理列表范围** | 审批类单据列表页顶部状态 Tab 上方增加两个 `TDChip` 切换：`我的发起`（默认）和 `下属审批`（展示本部门下属提交的单据）。`下属审批` 模式下卡片显示申请人姓名和部门。`pending` 状态卡片左滑→`[一键同意]` |
| **财务列表操作** | 费用报销列表页，财务角色默认查看全公司数据。`[待付款]` Tab 中卡片点击进入详情页，完成三项合规查验后方可归档 |
| **预算冻结异常** | 提交时调 ERP BudgetService.Freeze() 可能因超时或版本冲突失败。失败时提示"预算扣减失败，请稍后重试"，不创建审批实例，用户可重新提交 |
| **草稿生命周期** | 存草稿→写入数据库（Status=draft）→返回列表页默认激活"草稿"Tab。继续编辑→打开表单页自动回填。草稿仅创建者本人可见（管理员也不例外），所有查询入口排除草稿。无过期时间，可长期保存 |
| **并发编辑** | 草稿仅创建者可见和编辑，不存在多人并发。报销单提交时使用乐观锁（Version 字段），冲突时提示"单据已被修改，请刷新后重试" |
| **设备兼容性** | 仅支持竖屏手机。平板可运行但不做布局优化。折叠屏展开态默认按手机竖屏布局居中显示 |
| **多语言** | V1 支持简体中文、繁体中文、英文。所有 UI 文案通过 Flutter 国际化（`AppLocalizations`）管理。数据库枚举值存储英文标识，前端负责翻译映射。默认跟随系统语言，用户可在个人中心手动切换 |
| **深色模式** | V1 支持浅色/深色主题切换。入口在"我的"→"语言"下方 TDSwitch。通过 `ThemeExtension` 实现所有组件自动适配两种模式 |

---

## 4. 业务流程图

### 4.1 工作台角色路由

```mermaid
flowchart TD
    A[用户打开 App] --> B[.NET 加载 OaUserPermission]
    B --> C{权限判定}
    C -->|有 oa.admin.*| D[管理员版工作台]
    C -->|有 oa.expense.verify_invoice| E[财务版工作台]
    C -->|有 oa.*.approve 或 oa.report.view_dept| F[经理版工作台]
    C -->|其他| G[员工版工作台]

    D --> D1[轮播图 + 金刚区 + 发布公告入口 + 全公司财务看盘]
    E --> E1[轮播图 + 金刚区 + 全公司财务看盘]
    F --> F1[轮播图 + 金刚区 + 待审批卡片 + 部门快捷看板]
    G --> G1[轮播图 + 金刚区 + 个人快捷看板]

    D1 --> H[用户点击金刚区入口进入对应功能]
    E1 --> H
    F1 --> H
    G1 --> H
```

### 4.2 费用报销全流程

```mermaid
flowchart TD
    A[员工发起] --> B{有费用申请?}
    B -->|有| C[多选导入申请: 项目/科目自动带入, 填每张导入金额]
    B -->|无| D[手工选择项目→科目→查预算, 填报销单]
    C --> E[填发票明细: 币种/汇率/金额/发票号]
    D --> E
    E --> F{存草稿 还是 提交?}
    F -->|存草稿| G[草稿保存]
    G --> A
    F -->|提交审批| H[校验必填项 + .NET 调 ERP BudgetService 预算]
    H -->|超预算| I[金额标红警告, 允许提交带 budget_exceeded 标签]
    I --> K[调 ERP BudgetService 冻结预算]
    H -->|校验失败| J[ScrollTo 报错字段]
    J --> E
    H -->|校验通过| K
    K --> L[.NET → ERP 创建审批实例]
    L -->|创建失败| L1[保持 draft, 提示重试]
    L1 --> A
    L -->|创建成功| M[OA 存 ApprovalInstanceId, Status=pending]
    M --> N[.NET 消息推送审批人]

    N --> O[经理审批]
    O --> P{审批动作}
    P -->|同意| Q{是否最后一级?}
    Q -->|是| R[Status=approved, ERP 预算冻结转扣减]
    Q -->|否| S[流转下一级]
    S --> N
    P -->|拒绝| T[Status=rejected]
    M -.->|员工随时撤回| U[调 ERP 撤回 → 释放冻结预算]
    U --> V[Status=withdrawn, 审批终止]

    R --> W[.NET 通知申请人]
    W --> X{财务核销}
    X --> Y[查验: 手动勾选三项合规]
    Y --> Z{三项全勾?}
    Z -->|否| X
    Z -->|是| AA[录入电汇流水号+记账凭证号]
    AA --> AB[PaymentStatus=paid, 归档]
    AB --> AC{连续核销?}
    AC -->|是| AD[跳转下一笔待付款 FIFO]
    AD --> X
    AC -->|否| AE[返回列表]

    T --> AF[.NET 通知: 已拒绝]
    AF --> AG[员工重新编辑发起]
    AG --> A
```

### 4.3 费用申请全流程

```mermaid
flowchart TD
    A[员工发起] --> B[填写申请]
    B --> C[选择费用类型: 多选, 如差旅+办公]
    C --> D[选择项目 → 加载科目 → 查预算余额]
    D --> E[填预估明细行: 费用类别+预估金额, 支持多行]
    E --> F[上传支撑材料: 最多9张]
    F --> G{存草稿 还是 提交?}
    G -->|存草稿| H[草稿保存]
    H --> A
    G -->|提交审批| I[校验必填项 + 调 ERP BudgetService 查预算]
    I -->|超预算| J[金额标红警告, 允许提交]
    J --> L[调 ERP BudgetService 冻结预算, tags: budget_exceeded]
    I -->|校验失败| K[ScrollTo 报错字段]
    K --> E
    I -->|校验通过| L
    L --> L1[.NET → ERP 创建审批实例]
    L1 -->|创建失败| L2[保持 draft, 提示重试]
    L2 --> A
    L1 -->|创建成功| M[OA 存 ApprovalInstanceId, Status=pending]
    M --> N[.NET 消息推送审批人]

    N --> O[经理审批]
    O --> P{审批动作}
    P -->|同意| Q{是否最后一级?}
    Q -->|是| R[Status=approved, ERP 预算冻结转扣减]
    Q -->|否| T[流转下一级]
    T --> N
    P -->|拒绝| U[Status=rejected]

    N -.->|员工随时撤回| V[调 ERP 撤回 → 释放冻结预算]
    V --> W[Status=withdrawn, 审批终止]

    R --> X[.NET 通知申请人]
    X --> Y[UsageStatus=unused, 等待报销引用]
    Y --> Z{报销引用后 UsageStatus?}
    Z -->|fully_used| AA[Status 自动流转为 archived]
    U --> AB[.NET 通知: 已拒绝]
    AB --> AC[员工重新编辑发起]
    AC --> A
```

### 4.4 外勤日志全流程

```mermaid
flowchart TD
    A[业务员到达客户现场] --> B[打开 OA 创建外勤日志]
    B --> C[GPS 自动定位, 地址只读]
    C --> D{定位成功?}
    D -->|失败| E[提交按钮置灰, 提示检查定位权限]
    D -->|成功| F[输入/选择客户名称]
    F --> G[填写工作总结 + 后续计划]
    G --> H[强制拍照: 签到照1张 + 现场照1-8张]
    H --> I{至少 1 张照片?}
    I -->|否| J[提交按钮置灰]
    J --> H
    I -->|是| K[照片自动添加水印: 授时+GPS]
    K --> L{存草稿 还是 提交?}
    L -->|存草稿| M[保存草稿, 不校验GPS/照片]
    L -->|提交| N[校验GPS+照片, Status=completed, 写入数据库, 锁定不可编辑]

    N --> O[经理在详情页查看]
    O --> P[经理星级评分 + 文字点评]
    P --> Q[点评数据写入 OutingLogComment]
    Q --> R["列表页红点: MAX(CreateTime) > LastViewedTime"]
    R --> S[员工点击查看详情, 更新 LastViewedTime]
    S --> T[红点消失]

    N -.->|管理员| U[管理员可软删除已提交日志]
```

### 4.5 用车申请全流程

```mermaid
flowchart TD
    A[员工发起] --> B[选择车牌号: OA 本地 SysVehicle 车池]
    B --> C[填写用车事由 + 始发地/目的地]
    C --> D[地图选点: 回填经纬度]
    D --> E[选择出车/还车时间]
    E --> F[添加同行人: 通讯录多选/手动输入外部人员]
    F --> G[实时排期冲突检测: 防抖500ms]
    G -->|冲突| H[红色警告, 提交按钮置灰]
    G -->|无冲突| I{存草稿 还是 提交?}
    H --> B
    I -->|存草稿| J[草稿保存, VehicleId可空]
    I -->|提交审批| K[校验必填项 + VehicleId必填]
    K -->|校验失败| L[ScrollTo 报错字段]
    L --> C
    K -->|校验通过| M[.NET → ERP 创建审批实例]
    M -->|创建失败| M1[保持 draft, 提示重试]
    M1 --> A
    M -->|创建成功| N[OA 存 ApprovalInstanceId, Status=pending]
    N --> O[.NET 消息推送审批人]

    O --> P[经理审批]
    P --> Q{审批动作}
    Q -->|同意| R{是否最后一级?}
    R -->|是| S[Status=approved, 车辆状态更新为 in_use]
    R -->|否| T[流转下一级]
    T --> O
    Q -->|拒绝| U[Status=rejected, 车辆状态不变]

    N -.->|员工随时撤回| V[调 ERP 撤回]
    V --> W[Status=withdrawn, 车辆状态不变]

    S --> X[.NET 通知申请人]
    X --> Y[用车中, 等待还车登记]
    Y --> Z[员工点击确认还车]
    Z --> AA[填写: 实还时间 + 出车前里程 + 还车后里程 + 费用备注]
    AA --> AB{里程校验: 还车里程 >= 出车里程?}
    AB -->|否| AC[红色提示, 提交按钮置灰]
    AC --> AA
    AB -->|是| AD[确认提交]
    AD --> AE[Status=returned → archived, 车辆恢复 idle]
    U --> AF[.NET 通知: 已拒绝]
    AF --> AG[员工重新编辑发起]
    AG --> A
```

### 4.6 公告发布全流程

```mermaid
flowchart TD
    A[管理员进入公告发布页] --> B[填写标题 + 选择分类]
    B --> C[富文本编辑正文]
    C --> D[上传附件: 最多9个]
    D --> E[设置: 置顶开关 + 有效期可选]
    E --> F[选择接收范围]
    F --> G{范围类型}
    G -->|全员| H[PrivateLevel=0, 写入 TargetType='all']
    G -->|按部门| I[部门树多选, 实时统计覆盖人数, 预写 ReadLog]
    G -->|按用户| J[员工搜索多选, 实时统计覆盖人数, 预写 ReadLog]
    H --> K{操作}
    I --> K
    J --> K
    K -->|存草稿| L[Status=draft, 仅创建者和管理员可见]
    L --> M[后续可编辑后发布]
    K -->|预览| N[全屏模拟详情页展示效果]
    N --> K
    K -->|发布| O[确认弹窗: 确认向 N 名员工发布?]
    O -->|确认| P[Status=published, 批量写入 AnnouncementReadLog]
    P --> Q[.NET 消息推送范围内员工]

    P --> R[员工查看公告]
    R --> S[停留 >= 2秒 → 自动标记已读]
    S --> T[AnnouncementReadLog.IsRead=1, 列表红点消失]

    P -.->|管理员后续操作| U[查看触达统计: 已读/未读名单]
    U --> V[未读中标记已离职灰色标签]
    V --> W[DING 一键催办未读员工: 自动过滤已离职]
```

### 4.7 权限管理配置流程

```mermaid
flowchart TD
    A[管理员进入权限管理页] --> B[搜索员工: 姓名/工号]
    B --> C[点击员工卡片]
    C --> D[右侧滑出权限编辑抽屉]
    D --> E[展示当前权限状态]

    E --> F{选择操作}
    F -->|快捷套餐| G[点击套餐按钮: 员工/审批人/财务/管理员]
    F -->|手动勾选| H[逐项勾选/取消权限点]

    G --> J{套餐覆盖当前权限}
    H --> J

    J --> K{点击保存?}
    K -->|取消| L{有未保存修改?}
    L -->|是| M[确认弹窗: 放弃修改?]
    M -->|放弃| N[关闭抽屉]
    M -->|继续编辑| D
    L -->|否| N

    K -->|确认保存| O{校验}
    O -->|取消自己的admin| P[Toast: 无法移除自身管理员]
    P --> D
    O -->|移除最后admin| Q[后端拒绝: 至少保留一名管理员]
    Q --> D
    O -->|通过| R[写入 OaUserPermission + 审计日志 JSON快照]
    R --> S[Toast: 权限已更新]
    S --> N
```

---

## 5. 技术可行性分析

### 5.1 技术栈匹配

| 层 | 技术 | 成熟度 | 风险 |
|----|------|--------|------|
| 移动端 | Flutter 3.38.10 | ✅ 已验证 | 低 |
| Android 宿主 | Java/Kotlin | ✅ 已验证 | 低 |
| iOS 宿主 | OC, Xcode 14.2 | ✅ 已验证 | 中 |
| 后端 | .NET Framework 4.8 | ✅ 已验证 | 低 |
| 数据库 | SQL Server 2019+ | ✅ 已验证 | 低 |

### 5.2 关键风险与缓解

| 风险 | 影响 | 缓解措施 |
|------|------|---------|
| **多 ERP 审批 API 签名不一致** | 适配器层开发量不确定 | 先对接一套 ERP，验证适配器模式后再扩展；Flutter 端不感知差异 |
| **无 ERP 回调机制** | 审批状态同步延迟 | 列表页缓存 + 后台静默刷新 + 详情页实时校准 |
| **ERP 审批引擎能力边界未知** | 会签/或签等高级审批形态不可用 | 需求层面做最小假设（基础审批即可） |
| **iOS 编译兼容性** | Flutter 依赖可能不兼容 Xcode 14.2 | 每次新增依赖后在 iOS 端编译验证 |
| **预算/项目/科目依赖 ERP** | 适配器开发量不确定 | 统一适配器模式，ERP 无数据时返回空/不限，OA 隐藏对应功能区块 |

### 5.3 与现有系统的集成点

| 集成点 | 方向 | 方式 |
|--------|------|------|
| 用户信息 | ERP → OA | .NET 服务端查询 ERP 主数据 |
| 组织架构 | ERP → OA | 同上 |
| 客户数据 | ERP → OA | .NET CustomerService，外勤日志输入时联想 |
| 预算 | ERP → OA | .NET BudgetService，余额查询/冻结/扣减/释放 |
| 项目 | ERP → OA | .NET ProjectService，费用申请表单级联下拉 |
| 科目 | ERP → OA | .NET SubjectService，依赖项目选定后加载 |
| 汇率 | ERP → OA | .NET ExchangeRateService，默认 CNY |
| 审批发起 | OA → ERP | .NET 服务端调用 ERP 审批 API |
| 审批状态查询 | OA → ERP | 同上 |
| 审批动作 | OA → ERP | 同上 |
| 消息推送 | ERP → 用户 | .NET 服务端消息模块触发推送 |
| 原生能力 | App → OA | MethodChannel：相机、相册、通讯录、地图、GPS |

### 5.4 性能考量

| 场景 | 策略 | 预期体验 |
|------|------|---------|
| 列表页加载 | OA 本地业务数据 + .NET → ERP 实时查询审批状态 | 首次 1-2 秒 |
| 详情页打开 | 实时调用 ERP 刷新审批状态 | 200-500ms |
| 附件上传 | 分片上传 + 缩略图预生成 | 图片 ≤20MB 3-5 秒 |
| 报表图表 | 后端预计算 + 5 分钟缓存 | 首次 1-2 秒 |
| 多 ERP 查询 | .NET 服务端并行调用 + 超时熔断 | 单 ERP 失败不影响其他 |

### 5.5 非功能需求

#### 5.5.1 性能

| 指标 | 目标 |
|------|------|
| 应用冷启动 | ≤3 秒 |
| 列表页首屏 | ≤1 秒（本地数据） |
| 详情页加载 | ≤2 秒（含 ERP 状态刷新） |
| 附件上传 | 图片 ≤5 秒（20MB） |
| API 响应（列表） | P95 ≤500ms |
| API 响应（ERP 查询） | P95 ≤3s（含 ERP 耗时） |

#### 5.5.2 可用性

| 指标 | 目标 |
|------|------|
| 月度可用率 | ≥99.5% |
| 单 ERP 不可用降级 | 其他 ERP 用户不受影响；受影响用户看到明确错误提示 |
| 数据持久性 | 已提交单据零丢失（SQL Server 主备） |

#### 5.5.3 兼容性

| 平台 | 最低版本 |
|------|---------|
| Android | 8.0 (API 26) |
| iOS | 14.0 |
| Flutter | 3.38.10 |
| SQL Server | 2019+ |

#### 5.5.4 安全

| 要求 | 说明 |
|------|------|
| 传输加密 | 全链路 HTTPS（客户端→服务端）；内网 HTTP（服务端→ERP） |
| 身份认证 | 复用宿主 App Token；401 自动触发宿主重登录 |
| 数据隔离 | 列表查询按 OA 权限严格控制数据范围 |
| 敏感数据脱敏 | 银行账号列表页展示后 4 位，详情页完整展示 |
| 审计 | 权限变更全量记录 OaPermissionChangeLog |

### 5.6 依赖与假设

| 依赖 | 说明 | 不可用时的降级 |
|------|------|-------------|
| 宿主 App Flutter 集成 | Flutter Module 已嵌入 Android/iOS 宿主 | OA 模块不可用 |
| ERP API 可用 | 审批/预算/项目/科目/汇率/客户接口 | 各 Adapter 返回空/不限，OA 隐藏对应功能区块 |
| 定时 Job 调度 | 申请过期自动释放预算 | 预算无法自动释放，需人工处理 |
| .NET 服务端消息模块 | 推送通知 | 站内消息仍可见（轮询） |
| MethodChannel | 相机/相册/通讯录/GPS/地图选点 | 对应功能不可用，按钮置灰+提示 |
| SQL Server | OA 业务数据持久化 | 全模块不可用 |

**假设**：
- 宿主 App 已完成用户登录认证，OA 启动时能从宿主获取 Token 和 ERP 用户 ID
- ERP 审批引擎支持基础审批动作（同意/拒绝）
- 每套 ERP 的审批 API 签名虽不同，但功能语义一致

---

## 附录

### 附录 A：快捷套餐权限点清单

| 套餐 | 包含权限点 |
|------|----------|
| **员工（默认）** | `oa.expense_apply.submit`、`oa.expense.submit`、`oa.overtime.submit`、`oa.vehicle.submit`、`oa.outing_log.submit`、`oa.announcement.view`、`oa.report.view_own` |
| **审批人（经理）** | 员工所有权限 + `oa.expense_apply.approve`、`oa.expense.approve`、`oa.overtime.approve`、`oa.vehicle.approve`、`oa.outing_log.comment`、`oa.report.view_dept` |
| **财务** | 员工所有权限 + `oa.expense.verify_invoice`、`oa.expense.pay`、`oa.report.view_all`、`oa.report.export` |
| **管理员** | 全部权限（含以上所有）+ `oa.announcement.publish`、`oa.announcement.ding`、`oa.admin.permission`、`oa.admin.vehicle`、`oa.admin.banner`、`oa.admin.cost_category` |

### 附录 B：术语表

| 术语 | 说明 |
|------|------|
| ERP | 企业资源计划系统，OA 模块的底层数据来源 |
| ACL | 访问控制列表，按用户逐个赋权 |
| 费用申请 | 费用发生前的预估申请，审批通过后可被报销单引用 |
| 审批实例 | ERP 审批引擎中一次审批流程的运行实例 |
| 审批时间线 | 审批流程各节点的操作历史 |
| 净工时 | 扣除午休和晚餐休息盲区后的实际加班小时数 |
| 核销 | 财务确认发票合规、线下打款、凭证归档的完整闭环 |
| DING 催办 | 管理员对未读公告的员工推送高优先级通知 |
| 快捷套餐 | 预设的权限组合模板 |
| ERP 适配器 | .NET 服务端统一接口层，每套 ERP 一个实现 |
| 预算控制 | 费用申请/报销关联项目预算，超支自动警告 |
| 预算科目 | 财务核算科目，预算挂在项目×科目的交叉点上 |
| 外币折算 | 报销明细选择外币后自动从 ERP 获取汇率，折算为本币 |
| 拼单导入 | 报销时从多张已通过的费用申请导入金额 |
| 排期冲突 | 用车申请选定车牌+时间后检测时段重叠 |
| 触达率 | 公告发布后已读人数占应达总人数的比例 |
| 乐观锁 | 使用 Version 字段控制并发更新冲突 |

---

> **文档版本**：v1.1 | 日期：2026-06-27
