TBOSS OA 模块 — 系统架构设计
版本:v1.0 | 日期:2026-06-03 | 基于 tboss-oa-product-strategy.md
1. 整体分层架构
graph TB
subgraph 客户端层["客户端层 — Flutter 3.38.10"]
direction LR
A1[Android 宿主<br/>Java/Kotlin]
A2[iOS 宿主<br/>OC / Xcode 14.2]
A3[Flutter OA Module<br/>业务表单 / 公告 / 报表 / 权限管理]
A1 --- A3
A2 --- A3
end
subgraph 通道层["Platform Channel"]
B1[MethodChannel<br/>相机 / 相册 / 通讯录<br/>地图选点 / GPS 定位]
end
subgraph 服务端层[".NET Framework 4.8 服务端"]
direction TB
C1["OA API Controller<br/>RESTful HTTP API"]
subgraph 业务服务层
C2["权限服务<br/>OaPermission / ACL"]
C3["单据服务<br/>报销 / 事前 / 加班 / 用车 / 外勤"]
C4["公告服务<br/>发布 / 触达审计 / DING"]
C5["报表服务<br/>5 大明细报表 / 导出"]
end
subgraph 集成适配层
C6["审批适配器<br/>ErpAAdapter / ErpBAdapter"]
C7["消息适配器<br/>App Push / 站内消息"]
C8["数据适配器<br/>用户 / 部门 / 客户查询"]
end
C1 --> C2
C1 --> C3
C1 --> C4
C1 --> C5
C2 --> C8
C3 --> C6
C3 --> C8
C4 --> C7
end
subgraph 数据层["数据层"]
D1[("SQL Server 2019+<br/>OA 业务库<br/>单据 / 公告 / 权限 / 车辆")]
end
subgraph 外部系统["外部系统"]
direction LR
E1["ERP-A<br/>审批引擎 + 主数据"]
E2["ERP-B<br/>审批引擎 + 主数据"]
E3["ERP-C<br/>审批引擎 + 主数据"]
end
A3 -->|"HTTP REST"| C1
A3 -.->|"MethodChannel"| B1
C3 --> D1
C4 --> D1
C2 --> D1
C6 -->|"审批 API<br/>(签名不同)"| E1
C6 --> E2
C6 --> E3
C8 -->|"主数据查询"| E1
C8 --> E2
C8 --> E3
C7 -->|"App Push"| A3
分层职责
| 层 |
职责 |
不负责 |
| 客户端层 |
UI 渲染、表单校验、本地缓存、原生能力调用 |
审批逻辑、权限判定、数据聚合 |
| 通道层 |
桥接 Flutter 与原生 SDK(相机/GPS/通讯录/地图) |
业务逻辑 |
| 服务端层 |
REST API、业务逻辑、多 ERP 适配、消息推送触发 |
最终审批执行(由 ERP 完成) |
| 数据层 |
OA 业务数据持久化 |
用户/组织/客户/审批数据存储 |
| 外部系统 |
审批引擎、用户主数据、客户主数据 |
OA 特有的业务表单 |
2. 技术选型
| 层 |
技术 |
版本 |
选型理由 |
| 移动端框架 |
Flutter |
3.38.10 |
跨平台代码复用;已有 Flutter Module 嵌入 Android/iOS 宿主 |
| Android 宿主 |
Java/Kotlin |
— |
现有 ERP App 技术栈,MethodChannel 互调已验证 |
| iOS 宿主 |
Objective-C |
Xcode 14.2 |
现有 ERP App 技术栈;Flutter 3.38.10 兼容已验证 |
| 后端框架 |
.NET Framework 4.8 |
4.8 |
现有服务端技术栈;团队熟悉;已有消息/审批代理模块可复用 |
| 数据访问 |
Dapper |
— |
轻量高效;复杂 SQL 手写场景优于 EF;与现有服务端一致 |
| 数据库 |
SQL Server 2019+ |
2019+ |
现有基础设施;与 ERP 同生态 |
| HTTP 客户端 |
Dio |
— |
Flutter 端标准 HTTP 库;拦截器/超时/重试 |
| 路由 |
GoRouter |
— |
声明式路由;Deep Link 支持 |
| 状态管理 |
Provider / Riverpod |
— |
轻量;全局角色/权限状态注入 |
| UI 组件库 |
TDesign Flutter |
— |
企业级组件库;项目已统一采用 |
| 图表 |
fl_chart |
— |
报表趋势图/柱状图 |
| 原生互调 |
MethodChannel |
— |
Flutter ↔ 原生双向通信 |
不选型的理由
| 未选 |
理由 |
| 独立用户体系 |
ERP 已有用户/组织数据,避免双写和同步 |
| 自建审批引擎 |
ERP 审批引擎成熟且多套在用,OA 只做消费端 |
| 自建消息推送 |
.NET 服务端已有完整推送链路 |
| Flutter 独立 App |
必须是 App 内嵌模块,不能是独立应用 |
| Entity Framework |
团队偏好 Dapper;现有服务端已是 Dapper |
3. 模块划分
tboss_oa_module/lib/
├── main.dart # 入口
├── app.dart # MaterialApp + GoRouter + Provider 注入
│
├── core/
│ ├── router/app_router.dart # 路由表 + Deep Link 处理
│ ├── network/
│ │ ├── api_client.dart # Dio 实例(base URL / 拦截器 / 超时)
│ │ ├── api_response.dart # 统一响应模型
│ │ └── api_exception.dart # 业务异常
│ ├── auth/
│ │ └── auth_service.dart # Token 管理 + 401 处理
│ ├── theme/
│ │ ├── app_colors.dart # 色彩 Token
│ │ └── app_theme.dart # ThemeData
│ ├── utils/
│ │ ├── date_utils.dart
│ │ ├── responsive.dart # 横竖屏适配
│ │ └── validators.dart # 表单校验
│ └── i18n/
│ ├── app_localizations.dart
│ └── locale_provider.dart
│
├── shared/
│ ├── models/
│ │ ├── user_model.dart # 用户模型(从 ERP API 映射)
│ │ ├── approval_status.dart # 审批状态枚举
│ │ └── pagination_model.dart # 分页模型
│ └── widgets/
│ ├── app_card.dart
│ ├── status_tag.dart # 状态标签组件
│ ├── status_banner.dart # 详情页状态横幅
│ ├── list_card.dart # 列表卡片
│ ├── action_bar.dart # 底部操作栏
│ ├── filter_tabs.dart # Chip 筛选条
│ ├── form_section.dart # 表单分组
│ ├── form_field_row.dart # 表单行
│ ├── section_card.dart # 分区卡片
│ ├── empty_state.dart # 空状态
│ ├── loading_widget.dart # 骨架屏/菊花
│ ├── approval_actions.dart # 审批操作栏(经理版)
│ ├── approval_timeline.dart # 审批时间线
│ ├── report_filter_bar.dart # 报表筛选
│ ├── message_item.dart # 消息卡片
│ ├── profile_menu_item.dart # 个人中心菜单项
│ └── pencil_nav_bar.dart # 底部导航栏
│
├── features/
│ ├── shell/
│ │ ├── app_shell.dart # Appshell(底部 Tab)
│ │ └── nav_bar_config.dart
│ ├── home/
│ │ ├── home_page.dart # 工作台
│ │ └── home_controller.dart
│ ├── messages/
│ │ ├── message_list_page.dart
│ │ ├── message_controller.dart
│ │ └── message_model.dart
│ ├── profile/
│ │ └── profile_page.dart
│ ├── expense_application/
│ │ ├── expense_application_api.dart
│ │ ├── expense_application_model.dart
│ │ ├── expense_application_apply_page.dart
│ │ ├── expense_application_list_page.dart
│ │ ├── expense_application_list_controller.dart
│ │ └── expense_application_detail_page.dart
│ ├── expense/
│ │ ├── expense_api.dart
│ │ ├── expense_model.dart
│ │ ├── expense_apply_page.dart
│ │ ├── expense_list_page.dart
│ │ ├── expense_list_controller.dart
│ │ └── expense_detail_page.dart
│ ├── overtime/
│ │ ├── overtime_api.dart
│ │ ├── overtime_model.dart
│ │ ├── overtime_apply_page.dart
│ │ ├── overtime_list_page.dart
│ │ ├── overtime_list_controller.dart
│ │ └── overtime_detail_page.dart
│ ├── vehicle/
│ │ ├── vehicle_api.dart
│ │ ├── vehicle_model.dart
│ │ ├── vehicle_apply_page.dart
│ │ ├── vehicle_list_page.dart
│ │ ├── vehicle_list_controller.dart
│ │ └── vehicle_detail_page.dart
│ ├── outing_log/
│ │ ├── outing_log_api.dart
│ │ ├── outing_log_model.dart
│ │ ├── outing_log_create_page.dart
│ │ ├── outing_log_list_page.dart
│ │ ├── outing_log_list_controller.dart
│ │ └── outing_log_detail_page.dart
│ ├── announcement/
│ │ ├── announcement_api.dart
│ │ ├── announcement_model.dart
│ │ ├── announcement_list_page.dart
│ │ ├── announcement_list_controller.dart
│ │ ├── announcement_detail_page.dart
│ │ └── announcement_create_page.dart
│ ├── report/
│ │ ├── expense_apply_detail_report_page.dart
│ │ ├── expense_detail_report_page.dart
│ │ ├── overtime_detail_report_page.dart
│ │ ├── vehicle_detail_report_page.dart
│ │ └── outing_log_report_page.dart
│ └── admin/
│ └── admin_permissions_page.dart
│
└── .gitkeep 文件(各模块目录占位)
模块依赖关系
graph LR
subgraph features["features/"]
home["home"]
expense["expense"]
expense_apply["expense_application"]
overtime["overtime"]
vehicle["vehicle"]
outing_log["outing_log"]
announcement["announcement"]
report["report"]
admin["admin"]
messages["messages"]
profile["profile"]
shell["shell"]
end
subgraph shared["shared/"]
widgets["widgets"]
models["models"]
end
subgraph core["core/"]
router["router"]
network["network"]
auth["auth"]
theme["theme"]
utils["utils"]
i18n["i18n"]
end
home --> shared
expense --> shared
announcement --> shared
report --> shared
admin --> shared
shell --> router
shared --> core
所有 feature 模块依赖 shared/ 和 core/。shell 依赖 router 管理 Tab 切换。模块之间不直接相互依赖,页面跳转通过 GoRouter 完成。
4. .NET 服务端模块划分
Tboss.OA.Server/
├── Controllers/
│ ├── OaPermissionController.cs # 权限管理 API
│ ├── OaExpenseApplyController.cs # 事前申请 API
│ ├── OaExpenseController.cs # 费用报销 API
│ ├── OaOvertimeController.cs # 加班申请 API
│ ├── OaVehicleController.cs # 用车申请 API
│ ├── OaOutingLogController.cs # 外勤日志 API
│ ├── OaAnnouncementController.cs # 公告管理 API
│ ├── OaReportController.cs # 报表 API
│ └── OaApprovalController.cs # 审批代理 API
│
├── Services/
│ ├── PermissionService.cs # 权限校验 + 套餐管理
│ ├── ExpenseApplyService.cs
│ ├── ExpenseService.cs
│ ├── OvertimeService.cs
│ ├── VehicleService.cs
│ ├── OutingLogService.cs
│ ├── AnnouncementService.cs
│ ├── ReportService.cs
│ └── ApplicationNoGenerator.cs # 单据编号原子生成
│
├── ErpIntegration/ # ERP 集成适配器(统一模式,共 9 个)
│ ├── IApprovalAdapter.cs # 审批:创建实例/查询状态/执行动作/撤回
│ ├── IBudgetAdapter.cs # 预算:余额查询/冻结/扣减/释放
│ ├── IProjectAdapter.cs # 项目:列表/级联查询
│ ├── ISubjectAdapter.cs # 预算科目:按项目查科目列表
│ ├── ICostCenterAdapter.cs # 成本中心:列表查询
│ ├── IStandardAdapter.cs # 费用标准:按类型/城市/级别查上限
│ ├── IExchangeRateAdapter.cs # 汇率:币种→汇率查询
│ ├── ICustomerAdapter.cs # 客户:搜索/创建
│ ├── ISupplierAdapter.cs # 供应商:搜索(对公付款选供应商)
│ ├── NullAdapters.cs # 空实现集合(ERP 无能力时返回空/不限)
│ └── ErpAdapterFactory.cs # 按配置选取实现
│
├── Data/
│ ├── Repositories/ # Dapper 数据访问
│ └── Sql/ # SQL 脚本 / 迁移
│
├── Models/
│ ├── Dto/ # 请求/响应 DTO
│ └── Entity/ # 数据库实体映射
│
└── Infrastructure/
├── ErpClient.cs # ERP HTTP 客户端封装
├── MessageClient.cs # 消息推送客户端
└── CacheManager.cs # 报表数据缓存
5. 核心接口清单
5.1 OA 权限
| 方法 |
URL |
说明 |
| GET |
/api/oa/permissions |
获取权限点列表 |
| GET |
/api/oa/user-permissions?userId= |
获取用户权限 |
| PUT |
/api/oa/user-permissions |
保存用户权限 |
| GET |
/api/oa/permission-changelog?userId=&page= |
权限变更审计日志 |
5.2 业务单据
| 方法 |
URL |
说明 |
| 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 |
软删除单据 |
5.3 外勤日志
| 方法 |
URL |
说明 |
| PUT |
/api/oa/outing-log/submit |
提交外勤日志 |
| PUT |
/api/oa/outing-log/:id/view |
标记已查看 |
| POST |
/api/oa/outing-log/:id/comment |
主管点评 |
5.4 审批代理
| 方法 |
URL |
说明 |
| 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= |
下属单据列表 |
5.5 公告
| 方法 |
URL |
说明 |
| POST |
/api/oa/announcement/publish |
发布/更新公告 |
| POST |
/api/oa/announcement/:id/read |
标记已读 |
| POST |
/api/oa/announcement/:id/ding |
DING 催办 |
| GET |
/api/oa/announcement/read-stats/:id |
已读/未读统计 |
5.6 报表
| 方法 |
URL |
说明 |
| GET |
/api/oa/report/expense-apply?range=&userId= |
事前申请报表 |
| GET |
/api/oa/report/expense?range=&userId= |
费用报销报表 |
| GET |
/api/oa/report/overtime?range=&userId= |
加班报表 |
| GET |
/api/oa/report/vehicle?range=&userId= |
用车报表 |
| GET |
/api/oa/report/outing-log?range=&userId= |
外勤日志报表 |
| POST |
/api/oa/report/expense/export |
导出费用报销 Excel |
5.7 基础数据
| 方法 |
URL |
说明 |
| GET |
/api/oa/vehicles |
车辆池列表 |
| POST |
/api/oa/vehicles |
添加车辆 |
| PUT |
/api/oa/vehicles/:id |
修改车辆 |
| GET |
/api/oa/banners |
轮播图列表 |
5.8 复用已有 API
| 方法 |
URL |
说明 |
| 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 |
费用类别 |
6. 第三方服务集成方案
6.1 ERP 审批引擎
集成方式:.NET 服务端 HTTP 调用
认证:ERP 内部 Token / Session(由 .NET 服务端维护)
容错:单套 ERP 超时 15s,失败不影响其他 ERP 查询
重试 3 次,间隔 2s/4s/8s 递增
6.2 消息推送
集成方式:复用 .NET 服务端已有推送模块
通道:App Push(原生通知栏)+ 站内消息(消息列表)
触发点:审批创建/完成/拒绝/转交、公告发布、DING 催办
OA 职责:调用 .NET 消息 API 构造消息体,推送执行由消息模块完成
6.3 原生能力(MethodChannel)
| 能力 |
平台 |
用途 |
| 相机 |
Android/iOS |
外勤拍照(防伪水印)、发票拍照、头像拍摄 |
| 相册 |
Android/iOS |
附件上传(图片/PDF/文件选择) |
| GPS 定位 |
Android/iOS |
外勤签到、用车始发地 |
| 通讯录 |
Android/iOS |
用车同行人选人、审批转交选人 |
| 地图选点 |
Android/iOS |
用车目的地经纬度选点 |
| 文件分享 |
Android/iOS |
Excel 导出 → 系统分享面板 |
| PDF 查看 |
Android/iOS |
附件 PDF 原生预览 |
6.4 支付
不涉及。OA 不做在线支付,财务核销是线下打款后录入电汇流水号和记账凭证号。
7. 部署架构
graph TB
subgraph 用户设备["用户设备"]
direction LR
M1["Android App<br/>含 Flutter OA Module"]
M2["iOS App<br/>含 Flutter OA Module"]
end
subgraph 内网["公司内网"]
direction TB
S1["IIS / Windows Server<br/>.NET 4.8 服务端"]
DB1[("SQL Server 2019+<br/>OA 业务库")]
subgraph ERP群["ERP 系统群"]
ERP1["ERP-A<br/>审批 + 主数据"]
ERP2["ERP-B<br/>审批 + 主数据"]
ERP3["ERP-C<br/>审批 + 主数据"]
end
end
M1 -->|"HTTPS"| S1
M2 -->|"HTTPS"| S1
S1 --> DB1
S1 -->|"内网 HTTP"| ERP1
S1 -->|"内网 HTTP"| ERP2
S1 -->|"内网 HTTP"| ERP3
S1 -.->|"App Push<br/>FCM / APNs"| M1
S1 -.->|"App Push<br/>FCM / APNs"| M2
部署要点
| 组件 |
部署位置 |
说明 |
| Flutter OA Module |
嵌入 Android/iOS App |
随 App 发版更新 |
| .NET 4.8 服务端 |
Windows Server + IIS |
新增 OA Controller 部署到现有服务端 |
| SQL Server |
现有数据库实例 |
新增 OA 库或扩展现有库(新建 OA 表) |
| ERP 系统 |
内网已有 |
OA 通过内网 HTTP 调用 |
| 推送服务 |
FCM (Android) / APNs (iOS) |
已有消息模块管理,OA 不直接对接 |
配置项
<!-- .NET 服务端 web.config 新增 -->
<appSettings>
<!-- ERP 适配器配置:每项可设为 ErpA/ErpB/Null/OaLocal -->
<add key="Oa:Adapter:Approval" value="ErpA" />
<add key="Oa:Adapter:Budget" value="ErpA" />
<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/" />
<add key="ErpB:BaseUrl" value="http://erp-b.internal/api/" />
<!-- 调用超时(毫秒) -->
<add key="Oa:ErpTimeout" value="15000" />
<!-- 报表缓存时间(分钟) -->
<add key="Oa:ReportCacheMinutes" value="5" />
</appSettings>
8. 安全设计
| 维度 |
方案 |
| 传输安全 |
HTTPS(移动端 → 服务端);内网 HTTP(服务端 → ERP) |
| 认证 |
复用宿主 App 的登录态;401 → 触发宿主重新登录 |
| 授权 |
OA 端:OaUserPermission ACL;服务端:每个 API 校验权限点 |
| 数据隔离 |
列表查询按 view_own/view_dept/view_all 权限控制数据范围 |
| SQL 注入 |
Dapper 参数化查询 |
| 审计 |
OaPermissionChangeLog 记录所有权限变更 |
| 软删除 |
所有业务表 IsDeleted=1,禁止物理删除 |
9. ERP 适配器接口定义
9.1 IApprovalAdapter
public interface IApprovalAdapter
{
// 创建审批实例,返回 ERP 实例 ID
Task<string> SubmitAsync(string bizType, long bizId, object formData, List<string> tags = null);
// tags: ["budget_exceeded", "standard_exceeded"] → ERP 自动插入特批节点
// 查询审批状态
Task<ApprovalStatus> GetStatusAsync(string instanceId);
// 查询审批时间线
Task<List<TimelineNode>> GetTimelineAsync(string bizType, long bizId, string instanceId);
// 执行审批动作
Task<ActionResult> ExecuteActionAsync(string instanceId, string action, string opinion, long? transferToUserId);
// 撤回
Task WithdrawAsync(string instanceId);
// 查询待办列表
Task<PendingList> GetPendingListAsync(long approverId, string bizType, int page, int pageSize);
}
9.2 IBudgetAdapter
public interface IBudgetAdapter
{
// 查余额(含冻结金额)
Task<BudgetBalance> GetBalanceAsync(long projectId, long subjectId);
// 冻结(提交时),返回新 Version
Task<long> FreezeAsync(long projectId, long subjectId, decimal amount);
// 扣减(审批通过时),携带 Version 乐观锁
Task DeductAsync(long projectId, long subjectId, decimal amount, long version);
// 释放(拒绝/撤回时)
Task ReleaseAsync(long projectId, long subjectId, decimal amount);
}
public class BudgetBalance
{
public bool HasBudget { get; set; } // false → OA 前端隐藏预算区块
public decimal AllocatedAmount { get; set; }
public decimal AvailableAmount { get; set; }
public decimal FrozenAmount { get; set; }
public decimal ActualAvailable => AvailableAmount - FrozenAmount;
}
9.3 IProjectAdapter / ISubjectAdapter / ICostCenterAdapter
public interface IProjectAdapter
{
Task<List<IdName>> GetProjectsAsync(); // 项目列表
Task<List<IdName>> GetSubjectsAsync(long projectId); // 某项目下的预算科目
}
public interface ISubjectAdapter
{
Task<List<IdName>> GetSubjectsAsync(long? projectId);
}
public interface ICostCenterAdapter
{
Task<List<IdName>> GetCostCentersAsync();
}
public class IdName { public long Id; public string Name; }
9.4 IStandardAdapter
public interface IStandardAdapter
{
Task<StandardCheckResult> CheckAsync(string expenseType, string cityLevel, string employeeLevel, decimal amount);
}
public class StandardCheckResult
{
public bool HasStandard { get; set; } // false → OA 不校验
public bool Pass { get; set; }
public decimal MaxAmount { get; set; }
public decimal ExceedAmount { get; set; }
}
9.5 IExchangeRateAdapter / ICustomerAdapter / ISupplierAdapter
public interface IExchangeRateAdapter
{
Task<ExchangeRate> GetRateAsync(string currencyCode);
// { CurrencyCode: "USD", Rate: 7.2456, Date: "2026-06-03" }
}
public interface ICustomerAdapter
{
Task<List<IdName>> SearchAsync(string keyword);
Task<long> CreateAsync(string customerName); // 外勤日志新客户自动创建
}
public interface ISupplierAdapter
{
Task<List<IdName>> SearchAsync(string keyword);
}
9.6 NullAdapter 模式
// ERP 无某能力时,使用 Null 实现——OA 前端自动隐藏对应区块
public class NullBudgetAdapter : IBudgetAdapter
{
public Task<BudgetBalance> GetBalanceAsync(long p, long s)
=> Task.FromResult(new BudgetBalance { HasBudget = false });
public Task<long> FreezeAsync(long p, long s, decimal a) => Task.FromResult(0L);
public Task DeductAsync(long p, long s, decimal a, long v) => Task.CompletedTask;
public Task ReleaseAsync(long p, long s, decimal a) => Task.CompletedTask;
}
10. 事前申请→报销:适配器调用链
10.1 事前申请提交流程
sequenceDiagram
participant OA as Flutter OA
participant NET as .NET 服务端
participant P as ProjectService
participant S as SubjectService
participant B as BudgetService
participant ST as StandardService
participant A as ApprovalService
OA->>NET: 打开表单
NET->>P: 项目列表
P-->>NET: [{id, name}]
NET->>S: 某项目下科目
S-->>NET: [{id, name}]
NET-->>OA: 级联数据
OA->>NET: 选完项目+科目
NET->>B: 查可用余额
B-->>NET: { hasBudget, available, frozen }
NET-->>OA: 预算余额 ¥XX
OA->>NET: 提交
NET->>ST: 校验费用标准
ST-->>NET: { pass, exceedAmount }
NET->>B: 冻结预算
B-->>NET: { version }
NET->>A: 创建审批实例(含 tags)
A-->>NET: { instanceId }
NET-->>OA: { approvalInstanceId, status }
10.2 费用报销提交流程
sequenceDiagram
participant OA as Flutter OA
participant NET as .NET 服务端
participant C as CostCenterService
participant E as ExchangeRateService
participant ST as StandardService
participant B as BudgetService
participant A as ApprovalService
OA->>NET: 打开表单
NET->>C: 成本中心列表
C-->>NET: [{id, name}]
NET-->>OA: 下拉数据
OA->>NET: 明细行选外币 USD
NET->>E: 查 USD 汇率
E-->>NET: { rate: 7.2456 }
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: [...] }
A-->>NET: { instanceId }
NET->>NET: 写 ExpenseApplicationMapping + LoanRepayment
NET-->>OA: { instanceId, status }
10.3 适配器覆盖矩阵
| 环节 |
Approval |
Budget |
Project |
Subject |
CostCenter |
Standard |
ExchRate |
Customer |
Supplier |
| 事前-填单 |
|
余额 |
✅ |
✅ |
|
|
|
|
|
| 事前-提交 |
✅ 创建 |
✅ 冻结 |
|
|
|
✅ 校验 |
|
|
|
| 事前-审批 |
✅ 流转 |
|
|
|
|
|
|
|
|
| 事前-通过 |
|
✅ 扣减 |
|
|
|
|
|
|
|
| 事前-变更 |
✅ 补充 |
✅ 追加冻结 |
|
|
|
✅ |
|
|
|
| 报销-填单 |
|
|
|
|
✅ |
|
✅ |
|
✅ |
| 报销-提交 |
✅ 创建 |
✅ 扣减 |
|
|
|
✅ 校验 |
|
|
|
| 报销-审批 |
✅ 流转 |
|
|
|
|
|
|
|
|
| 报销-撤回 |
✅ 撤回 |
✅ 释放 |
|
|
|
|
|
|
|
11. ERP 能力缺位时的 OA 自治扩展
当 ERP 暂无某项能力,后续 OA 需要自建时,扩展路径标准化:
11.1 三步扩展法
步骤 1: 建表
ERP 无预算 → OA 新建 SysProjectBudget 表(DDL 已就绪)
ERP 无标准 → OA 新建 ExpenseStandard 表 + 管理页
步骤 2: 写实现
新建 OaLocalBudgetAdapter : IBudgetAdapter
新建 OaLocalStandardAdapter : IStandardAdapter
步骤 3: 切配置
appSettings: Oa:Adapter:Budget 从 Null 切为 OaLocal
重启 .NET 服务端生效
11.2 影响范围
| 层面 |
改动 |
| 数据库 |
+1 张表(迁移脚本) |
| .NET 服务端 |
+1 个 Adapter 实现类(~50 行) |
| web.config |
改 1 行配置 |
| Flutter 前端 |
零改动(API 契约不变) |
| OA API |
零改动 |
11.3 预设可用性
| 能力 |
当前 |
NullAdapter 行为 |
如需自建 |
| 预算 |
取决于 ERP |
hasBudget=false → OA 隐藏余额 |
+SysProjectBudget 表 |
| 费用标准 |
取决于 ERP |
hasStandard=false → 不校验超标 |
+ExpenseStandard 表+管理页 |
| 汇率 |
取决于 ERP |
只返回 CNY (rate=1) |
+汇率表+维护页 |
| 项目 |
始终需要 |
空列表 → 无下拉选项 |
+SysProject 管理页 |
| 成本中心 |
可空 |
空列表 → 可空不强制选 |
+SysCostCenter 管理页 |
| 供应商 |
可空 |
空列表 → 自由文本输入 |
+SysSupplier 管理页 |
文档版本:v1.0 | 日期:2026-06-03