版本:v1.1 | 日期:2026-06-27 | 基于 PRD v1.0 + 需求讨论确认
| 项目 | 规格 |
|---|---|
| 数据库 | Microsoft SQL Server 2019+ |
| 后端 | .NET Framework 4.8 + ADO.NET |
| 移动端 | Flutter 3.38.10(通过 HTTP API 交互,不直连数据库) |
| 字符集 | Chinese_PRC_CI_AS |
| 范畴 | 规范 | 示例 |
|---|---|---|
| 表名 | PascalCase,单数 | Expense, OaPermission |
| 列名 | PascalCase | Id, ApprovalInstanceId |
| 主键 | BIGINT IDENTITY(1,1) |
Id BIGINT IDENTITY(1,1) PRIMARY KEY |
| ERP 引用列 | 存储 ERP ID(NVARCHAR),非 FK 约束 | ApplicantId NVARCHAR(30)(注释标注来源 ERP) |
| 约束名 | PK_表名 / UX_表名_列名 |
PK_Expense, UX_Expense_ExpenseNo |
| 索引名 | IX_表名_用途 |
IX_Expense_List |
每张 OA 自管表必须包含:
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
CreateTime |
DATETIME |
NOT NULL DEFAULT GETDATE() |
服务器授时,行创建时间,不可修改 |
UpdateTime |
DATETIME |
NULL |
最后修改时间,应用层 UPDATE 时显式赋 GETDATE() |
IsDeleted |
BIT |
NOT NULL DEFAULT 0 |
软删除标记,0=正常,1=已删除。例外:OaPermissionChangeLog(审计日志不可删除)、ExpenseApplyMapping(纯关联表,随主表级联清理)、AnnouncementTarget / AnnouncementReadLog(随公告级联清理) |
| 类型 | 精度 | 示例 |
|---|---|---|
| 金额 | DECIMAL(28,8) |
TotalAmount |
| 经纬度 | DECIMAL(10,6) |
CheckInLongitude |
| 税率 | DECIMAL(5,4) |
TaxRate |
| 工时 | DECIMAL(4,1) |
NetOtHours |
所有状态/类型字段使用 VARCHAR(20) 存储英文标识,前端国际化映射。完整取值见第 6 节。
格式 {前缀}-{YYYYMMDD}-{序号},序号按天重置高位补零至 3 位。.NET 服务端使用 sp_getapplock 原子生成。
| 业务 | 前缀 | 示例 |
|---|---|---|
| 费用申请 | BXSQ | BXSQ-20260603-001 |
| 费用报销 | BX | BX-20260603-001 |
| 加班申请 | JB | JB-20260603-001 |
| 用车申请 | YC | YC-20260603-001 |
| 外勤日志 | VST | VST-20260603-001 |
┌─────────────────────────────┐
│ OA 权限体系(独立) │
│ OaPermission │
│ OaUserPermission │
│ OaPermissionChangeLog │
└─────────────────────────────┘
[ERP 用户] ◄──── 所有业务表的 ApplicantId / SalespersonId
[ERP 部门] ◄──── 所有业务表的 DeptId
[ERP 客户] ◄──── OutingLog.CustomerId
[ERP 审批] ◄──── ApprovalInstanceId(审批类主表)
[ERP 预算] ◄──── BudgetService 适配器(余额/冻结/释放)
[ERP 项目] ◄──── ProjectService 适配器(列表/级联)
[ERP 科目] ◄──── SubjectService 适配器(预算科目)
┌──────────────────────────────────────────────────────┐
│ OA 自管业务表 │
│ │
│ ExpenseApply ──(1:N)── ExpenseApplyDetail │
│ │ (明细行) │
│ │ │
│ ExpenseApplyMapping ── 申请↔报销多对多 │
│ ├──(N:1)── Expense │
│ │ (费用明细, 含币种) │
│ │ └──(1:N)── ExpenseDetail │
│ └──(N:1)── ExpenseApply │
│ │
│ Overtime │
│ │
│ Vehicle ──(1:N)── VehiclePassenger │
│ │ │
│ └──(N:1)── SysVehicle (OA 自管车辆池) │
│ │
│ OutingLog ──(1:N)── OutingLogComment │
│ │
│ Announcement ──(1:N)── AnnouncementTarget │
│ └──(1:N)── AnnouncementReadLog │
│ │
│ Attachment ── 统一附件表 │
│ BizType: expense_apply/expense/outing_log/announcement │
└──────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ OA 自管基础表 │
│ SysVehicle — 车辆池 │
│ SysBanner — 工作台轮播图 │
│ SysCostCategory — 费用类别字典 │
└──────────────────────────────────────────────────────┘
OA 独立权限系统的权限点定义表,存储所有可用权限编码及中文名称。权限点按模块分组,管理员通过快捷套餐或手动勾选为用户赋权。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| Id | BIGINT | ✅ | 主键,IDENTITY(1,1) |
| PermissionCode | VARCHAR(80) | ✅ | 权限点唯一编码,如 oa.expense.apply。UX_OaPermission_PermissionCode UNIQUE 约束 |
| PermissionName | NVARCHAR(50) | ✅ | 权限点中文名称,如"发起报销",用于管理界面展示 |
| Module | VARCHAR(30) | ✅ | 所属功能模块,用于分组展示:expense / expense_apply / overtime / vehicle / outing_log / announcement / report / admin |
| SortOrder | INT | ✅ | 管理界面排序权重,值越小越靠前,默认 0 |
| IsActive | BIT | ✅ | 权限点全局启用/停用标记,1=启用,0=停用,默认 1。停用后所有用户无法使用该权限 |
本表为字典表,不包含 CreateTime/UpdateTime/IsDeleted。权限点预置后一般不增删。
用户与权限点的多对多关联表。每个用户可拥有多个权限点,通过 IsDeleted 软删除实现权限的授予与回收。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| Id | BIGINT | ✅ | 主键,IDENTITY(1,1) |
| UserId | NVARCHAR(30) | ✅ | ERP 用户 ID(非 FK 约束,关联 ERP 用户主数据) |
| PermissionId | BIGINT | ✅ | 权限点 ID,FK → OaPermission.Id |
| GrantedBy | NVARCHAR(30) | ✅ | 授权操作人 ERP 用户 ID,用于审计追溯 |
| CreateTime | DATETIME | ✅ | DEFAULT GETDATE(),授权时间 |
| UpdateTime | DATETIME | 最后变更时间 | |
| IsDeleted | BIT | ✅ | DEFAULT 0。移除权限时设为 1(软删除,保留历史记录),重新授权时置回 0 |
索引:
UX_OaUserPermission_UserId_PermissionId UNIQUE (UserId, PermissionId) WHERE IsDeleted = 0 — 防止同一权限重复授权IX_UserPermission_UserId (UserId, IsDeleted) INCLUDE (PermissionId) — 按用户查权限列表记录管理员每次修改用户权限的完整快照。本表仅追加不修改(无 UpdateTime),不可软删除。用于变更追溯和合规审计。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| Id | BIGINT | ✅ | 主键,IDENTITY(1,1) |
| TargetUserId | NVARCHAR(30) | ✅ | 被操作的目标用户 ERP 用户 ID |
| OperatorId | NVARCHAR(30) | ✅ | 执行操作的管理员 ERP 用户 ID |
| ChangeType | VARCHAR(20) | ✅ | 变更类型:assign(赋权) / revoke(收权) / toggle_active(启停) |
| BeforeSnapshot | NVARCHAR(MAX) | ✅ | 变更前权限 JSON 快照,格式 {"permissions": ["oa.expense.apply"], "isActive": true} |
| AfterSnapshot | NVARCHAR(MAX) | ✅ | 变更后权限 JSON 快照,格式同上 |
| CreateTime | DATETIME | ✅ | DEFAULT GETDATE(),审计时间戳 |
本表无 UpdateTime 和 IsDeleted —— 审计日志不可修改、不可删除。
索引:IX_PermChangeLog_Target (TargetUserId, CreateTime DESC) INCLUDE (OperatorId, ChangeType)
员工在费用发生前提交的预估申请。支持多费用类型、关联项目与预算科目、多行预估明细。审批通过后可被多张报销单分批引用。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| Id | BIGINT | ✅ | 主键,IDENTITY(1,1) |
| ExpenseApplyNo | NVARCHAR(40) | ✅ | 单据唯一编号,格式 BXSQ-YYYYMMDD-XXX,提交时由 .NET 服务端原子生成 |
| ExpenseApplyDate | DATETIME | 业务申请时间(提交时写入) | |
| ApplicantId | NVARCHAR(30) | ✅ | 申请人 ERP 用户 ID |
| ApplicantName | NVARCHAR(100) | ✅ | 申请人姓名冗余,提交时从 ERP 用户主数据写入 |
| DeptId | NVARCHAR(30) | ✅ | 申请人所属部门 ERP 部门 ID,提交时写入 |
| DeptName | NVARCHAR(200) | ✅ | 部门名称冗余,提交时写入 |
| EstimatedAmount | DECIMAL(28,8) | ✅ | 预估申请总金额 = 所有明细行预估金额汇总,只读不手动修改,默认 0 |
| Urgency | VARCHAR(10) | ✅ | 紧急程度:normal(普通)/urgent(紧急)/critical(特急),默认 normal。枚举见 §6.3 |
| Purpose | NVARCHAR(500) | ✅ | 费用申请事由,限制 500 字 |
| Remark | NVARCHAR(500) | 备注,补充说明 | |
| EffectiveDate | DATETIME | 生效时间 | |
| AuditorId | NVARCHAR(30) | 审核人 ERP 用户 ID | |
| Status | VARCHAR(20) | ✅ | 业务状态:draft(草稿)/pending(审批中)/approved(已通过)/rejected(已拒绝)/withdrawn(已撤回)/archived(已归档)。审批状态通过 .NET → ERP 实时查询。当 UsageStatus=fully_used 时自动流转为 archived |
| UsageStatus | VARCHAR(20) | ✅ | 被报销引用的状态:unused(未引用)/partially_used(部分引用)/fully_used(已用完)。由报销单提交/删除时自动重算,默认 unused。在非 approved/archived 状态下始终为 unused |
| ValidUntil | DATE | 申请有效期截止日。到期后定时 Job 自动释放冻结预算,UsageStatus 标记为 fully_used,Status 流转为 archived,不可再被报销引用 | |
| ReferenceNo | NVARCHAR(100) | 关联的外部单号(合同号/询价单号),用于采购类申请追溯,非必填 | |
| ApprovalInstanceId | VARCHAR(50) | ERP 审批实例 ID,提交审批时由 .NET → ERP 创建后回写。撤回/驳回重新提交时覆盖为新值 | |
| PreviousInstanceIds | VARCHAR(MAX) | 历史审批实例 ID JSON 数组,撤回/驳回重新提交时追加旧 ApprovalInstanceId,用于追溯完整审批历史 | |
| Version | BIGINT | ✅ | 乐观锁并发控制字段,每次更新时 +1,默认 1 |
| CreateTime | DATETIME | ✅ | DEFAULT GETDATE() |
| UpdateTime | DATETIME | ||
| IsDeleted | BIT | ✅ | DEFAULT 0 |
索引:
IX_ExpenseApply_List (ApplicantId, CreateTime DESC) INCLUDE (ExpenseApplyNo, EstimatedAmount, Purpose, UsageStatus)IX_ExpenseApply_Import (ApplicantId, UsageStatus) WHERE UsageStatus IN ('unused', 'partially_used') — 报销导入时快速过滤可用申请费用申请的逐行预估明细。每行包含费用类别及预估金额,汇总金额写入主表 EstimatedAmount。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| Id | BIGINT | ✅ | 主键,IDENTITY(1,1) |
| ExpenseApplyId | BIGINT | ✅ | 关联主表,FK → ExpenseApply.Id(主表软删除时级联软删除) |
| ExpenseCategory | VARCHAR(30) | ✅ | 费用类型编码,值来源于 SysCostCategory.CategoryCode |
| Purpose | NVARCHAR(500) | ✅ | 费用事由,限制 500 字 |
| ProjectId | NVARCHAR(60) | 关联 ERP 项目代号 | |
| ProjectName | NVARCHAR(200) | 关联 ERP 项目名称,提交时写入 | |
| CostDeptId | NVARCHAR(30) | 费用承担部门 ERP 部门 ID,V1 中自动等于申请人所在 DeptId | |
| CostDeptName | NVARCHAR(200) | 费用承担部门名称冗余,V1 中自动等于申请人所在 DeptName | |
| AcctSubjectId | NVARCHAR(50) | 关联 ERP 会计科目代号,选择费用类型时会自动带出,值来源于 SysCostCategory.AcctSubjectId | |
| AcctSubjectName | NVARCHAR(400) | 关联 ERP 会计科目名称冗余,选择费用类型时会自动带出,值来源于 SysCostCategory.AcctSubjectName | |
| EstimatedStartDate | DATE | 预计开始日期,所有费用类型可见。选填 | |
| EstimatedEndDate | DATE | 预计结束日期,所有费用类型可见。选填。校验:必须 ≥ StartDate | |
| EstimatedAmount | DECIMAL(28,8) | ✅ | 单项预估金额 |
| Remark | NVARCHAR(500) | 明细项说明备注 | |
| SortOrder | INT | ✅ | 排序号,默认 1,控制移动端渲染顺序 |
| CreateTime | DATETIME | ✅ | DEFAULT GETDATE() |
| UpdateTime | DATETIME | ||
| IsDeleted | BIT | ✅ | DEFAULT 0 |
索引:IX_ExpenseApplyDetail_AppId (ExpenseApplyId, SortOrder) INCLUDE (ExpenseCategory, EstimatedAmount, Remark)
一张费用报销单可导入多张费用申请,一张费用申请也可被多张费用报销单分批引用。每行记录一次导入关联,保存导入金额用于 UsageStatus 计算。应用层约束:单次导入的多张申请必须属于同一项目和同一预算科目(ProjectId + AcctSubjectId 一致),以保证预算扣减的准确性。ImportedAmount 总和 ≤ 对应申请 EstimatedAmount - 已报总额。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| Id | BIGINT | ✅ | 主键,IDENTITY(1,1) |
| ExpenseId | BIGINT | ✅ | 报销单 ID,FK → Expense.Id |
| ExpenseApplyId | BIGINT | ✅ | 费用申请 ID,FK → ExpenseApply.Id |
| ImportedAmount | DECIMAL(28,8) | ✅ | 本次报销从该申请导入的金额。∑ImportedAmount 不能超过申请 EstimatedAmount。仅 Status IN ('pending', 'approved', 'archived') 的报销单计入占用总额,withdrawn 和 IsDeleted=1 的报销单不计入 |
| CreateTime | DATETIME | ✅ | DEFAULT GETDATE() |
约束:UX_ExpenseApplyMapping_ExpenseId_ExpenseApplyId UNIQUE (ExpenseId, ExpenseApplyId) — 同一张报销单不能重复导入同一张申请
索引:IX_ExpenseApplyMapping_AppId (ExpenseApplyId) INCLUDE (ExpenseId, ImportedAmount) — 查某申请的被引用情况
员工费用报销的核心主表。支持从费用申请拼单导入或直接新建,包含收款银行信息、财务核销标记。审批通过后由财务核销归档。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| Id | BIGINT | ✅ | 主键,IDENTITY(1,1) |
| ExpenseNo | VARCHAR(40) | ✅ | 单据唯一编号,格式 BX-YYYYMMDD-XXX,UX_Expense_ExpenseNo UNIQUE 约束 |
| ExpenseDate | DATE | 费用报销日期(提交时写入) | |
| ApplicantId | NVARCHAR(30) | ✅ | 报销人 ERP 用户 ID |
| ApplicantName | NVARCHAR(100) | ✅ | 报销人姓名冗余,提交时从 ERP 用户主数据写入 |
| DeptId | NVARCHAR(30) | ✅ | 报销人所属部门 ERP 部门 ID,提交时写入 |
| DeptName | NVARCHAR(200) | ✅ | 部门名称冗余,提交时写入 |
| CurrencyCode | VARCHAR(10) | 币别代码,默认 CNY。选外币时从 .NET → ERP ExchangeRateService 获取汇率填入明细行 | |
| IsGenerateVoucher | BIT | ✅ | 是否产生会计凭证:0=不产生,1=产生,DEFAULT 0 |
| VoucherNo | VARCHAR(50) | 财务核销时录入的金蝶/财务系统记账凭证号 | |
| ApprovedAmount | DECIMAL(28,8) | ✅ | 核准金额合计 = 所有明细行 ApprovedAmount 汇总,只读不手动修改,默认 0 |
| TotalAmount | DECIMAL(28,8) | ✅ | 报销总金额 = 所有明细行 TotalAmount 汇总,只读不手动修改,默认 0 |
| Purpose | NVARCHAR(500) | ✅ | 报销事由说明 |
| Remark | NVARCHAR(500) | 明细项说明备注 | |
| PaymentMethod | NVARCHAR(20) | 支付方式,员工填单时选择:bankTransfer(银行转账)/cash(现金)/alipay(支付宝)/wechat(微信) | |
| IsInvoiceVerified | BIT | ✅ | 财务核销标记一:发票已在全国增值税发票查验平台验真,默认 0 |
| IsTaxIdMatched | BIT | ✅ | 财务核销标记二:发票抬头与公司税号一致,默认 0 |
| IsCategoryCompliant | BIT | ✅ | 财务核销标记三:报销类目与发票项目匹配合规,默认 0 |
| BankTransferNo | VARCHAR(50) | 财务核销时录入的银行电汇流水号 | |
| PaymentStatus | VARCHAR(20) | ✅ | 付款状态:unpaid(待付款)/paid(已付款)。仅 Status=approved 时允许流转,默认 unpaid |
| Status | VARCHAR(20) | ✅ | 业务状态:draft/pending/approved/rejected/withdrawn/archived(已归档)。审批状态通过 .NET → ERP 实时查询。PaymentStatus=paid 且业务完结后自动流转为 archived |
| ApprovalInstanceId | VARCHAR(50) | ERP 审批实例 ID,提交审批时由 .NET → ERP 创建后回写 | |
| PreviousInstanceIds | VARCHAR(MAX) | 历史审批实例 ID JSON 数组 | |
| EffectiveDate | DATETIME | 生效时间 | |
| AuditorId | NVARCHAR(30) | 审核人 ERP 用户 ID | |
| CreateTime | DATETIME | ✅ | DEFAULT GETDATE() |
| UpdateTime | DATETIME | ||
| IsDeleted | BIT | ✅ | DEFAULT 0 |
索引:IX_Expense_List (ApplicantId, CreateTime DESC) INCLUDE (TotalAmount, ExpenseNo, PaymentStatus)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| Id | BIGINT | ✅ | 主键,IDENTITY(1,1) |
| ExpenseId | BIGINT | ✅ | 关联报销主表,FK → Expense.Id(主表软删除时级联软删除) |
| ExpenseApplyId | BIGINT | 关联费用申请,FK → ExpenseApply.Id。导入申请时写入,直接新建报销时为 NULL | |
| ExpenseApplyNo | NVARCHAR(40) | 费用申请单号冗余,便于列表展示免 JOIN | |
| ExpenseApplyDate | DATETIME | 费用申请日期冗余 | |
| ExpenseCategory | VARCHAR(30) | ✅ | 费用类型编码,值来源于 SysCostCategory.CategoryCode |
| Purpose | NVARCHAR(500) | ✅ | 费用事由,限制 500 字 |
| Remark | NVARCHAR(500) | 明细项说明备注 | |
| ProjectId | NVARCHAR(60) | 关联 ERP 项目代号 | |
| ProjectName | NVARCHAR(200) | 关联 ERP 项目名称,提交时写入 | |
| CostDeptId | NVARCHAR(30) | 费用承担部门 ERP 部门 ID,V1 中自动等于申请人所在 DeptId | |
| CostDeptName | NVARCHAR(200) | 费用承担部门名称冗余,V1 中自动等于申请人所在 DeptName | |
| AcctSubjectId | NVARCHAR(50) | 关联 ERP 会计科目代号,选择费用类型时会自动带出,值来源于 SysCostCategory.AcctSubjectId | |
| AcctSubjectName | NVARCHAR(400) | 关联 ERP 会计科目名称冗余,选择费用类型时会自动带出,值来源于 SysCostCategory.AcctSubjectName | |
| Amount | DECIMAL(28,8) | ✅ | 原币不含税金额。多币种时为原币金额 |
| TaxRate | DECIMAL(5,4) | 税率,如 0.0600 / 0.0900 / 0.1300 | |
| TaxAmount | DECIMAL(28,8) | ✅ | 进项税额,无发票时为 0,默认 0 |
| TotalAmount | DECIMAL(28,8) | ✅ | 原币价税合计 = Amount + TaxAmount |
| CurrencyCode | VARCHAR(10) | 原币币种代码,默认 CNY。选外币(如 USD/EUR/JPY)时从 .NET → ERP ExchangeRateService 获取当日汇率自动填入 ExchangeRate | |
| ExchangeRate | DECIMAL(10,4) | 原币→本币汇率,CNY 时为 1.0000。由 .NET 服务端从 ERP 汇率表查询后填入 | |
| BaseAmount | DECIMAL(28,8) | 折算后的本币金额 = TotalAmount × ExchangeRate。预算校验和报表统计均以本币为准 | |
| ApprovedAmount | DECIMAL(28,8) | ✅ | 该行核准金额,默认=TotalAmount(审批直接通过时),审批人可修改。当 ApprovedAmount 发生变化时,应以 ApprovedAmount 为基准重新计算本币金额:BaseAmount = ApprovedAmount × ExchangeRate。财务核销时只读不可修改 |
| CustomerVendorId | NVARCHAR(30) | 客户或供应商 ID,用于采购/招待类费用追溯。客户和供应商均可使用 | |
| CustomerVendorName | NVARCHAR(300) | 客户或供应商名称,用于采购/招待类费用追溯 | |
| OffsetAmount | DECIMAL(28,8) | ✅ | 已冲账金额(如预付/借款冲抵),V1 暂不使用,值始终为 0,默认 0。预留 V2 扩展 |
| BankName | NVARCHAR(100) | ✅ | 收款银行全称,前端支持下拉联想(数据源 .NET 字典 API),也可自由输入 |
| BankAccountName | NVARCHAR(50) | ✅ | 收款开户户名,默认填入当前用户姓名,可修改 |
| BankAccount | VARCHAR(50) | ✅ | 收款银行账号,前端校验 16-19 位数字格式 |
| SortOrder | INT | ✅ | 排序号,默认 1 |
| CreateTime | DATETIME | ✅ | DEFAULT GETDATE() |
| UpdateTime | DATETIME | ||
| IsDeleted | BIT | ✅ | DEFAULT 0 |
索引:IX_ExpenseDetail_ExpenseId (ExpenseId, SortOrder) INCLUDE (Amount, TotalAmount, ApprovedAmount, CustomerVendorName, OffsetAmount)
员工加班申请,支持三种加班类型和三种补偿方式。后端自动扣除午休/晚餐盲区计算净工时。审批通过后可用于调休或加班费结算。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| Id | BIGINT | ✅ | 主键,IDENTITY(1,1) |
| ApplicationNo | VARCHAR(30) | ✅ | 单据唯一编号,格式 JB-YYYYMMDD-XXX,UX_Overtime_ApplicationNo UNIQUE 约束 |
| ApplicantId | NVARCHAR(30) | ✅ | 申请人 ERP 用户 ID |
| DeptId | NVARCHAR(30) | ✅ | 所属部门 ERP 部门 ID |
| OtType | VARCHAR(10) | ✅ | 加班类型:workday(工作日加班)/weekend(休息日加班)/holiday(法定节假日加班) |
| CompensationType | VARCHAR(20) | ✅ | 补偿方式:overtime_pay(全部结算加班费)/comp_leave(全部转为调休)/mixed(部分调休+部分加班费) |
| CompLeaveRatio | DECIMAL(3,2) | 混合模式下转为调休的比例,范围 0.10~0.90,仅 CompensationType=mixed 时生效。如 0.30=30%调休+70%加班费 | |
| StartTime | DATETIME | 加班开始时间,提交时必填。校验:必须早于 EndTime | |
| EndTime | DATETIME | 加班结束时间,提交时必填 | |
| NetOtHours | DECIMAL(4,1) | ✅ | 实际净工时。由 .NET 服务端自动扣除午餐(12:00-13:00)和晚餐(18:00-18:30)盲区后计算,前端只读展示。≤0 时提交按钮置灰 |
| Reason | NVARCHAR(500) | ✅ | 加班原因说明 |
| Status | VARCHAR(20) | ✅ | 业务状态:draft/pending/approved/rejected/withdrawn/archived(已归档)。审批状态通过 .NET → ERP 实时查询。业务完结(加班费已发放或调休已确认)后自动流转为 archived |
| ApprovalInstanceId | VARCHAR(50) | ERP 审批实例 ID | |
| PreviousInstanceIds | VARCHAR(MAX) | 历史审批实例 ID JSON 数组 | |
| CreateTime | DATETIME | ✅ | DEFAULT GETDATE() |
| UpdateTime | DATETIME | ||
| IsDeleted | BIT | ✅ | DEFAULT 0 |
索引:IX_Overtime_List (ApplicantId, CreateTime DESC) INCLUDE (ApplicationNo, OtType, NetOtHours, CompensationType)
覆盖公车申请、审批、还车登记全生命周期。车辆池 OA 自管(SysVehicle),排期冲突检测、里程表记录和实际费用报账均在 OA 完成。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| Id | BIGINT | ✅ | 主键,IDENTITY(1,1) |
| ApplicationNo | VARCHAR(30) | ✅ | 单据唯一编号,格式 YC-YYYYMMDD-XXX,UX_Vehicle_ApplicationNo UNIQUE 约束 |
| ApplicantId | NVARCHAR(30) | ✅ | 申请人 ERP 用户 ID |
| DeptId | NVARCHAR(30) | ✅ | 所属部门 ERP 部门 ID |
| VehicleId | BIGINT | 申请调配的公车 ID,FK → SysVehicle.Id(OA 自管车辆池)。草稿时可空,提交时必填 | |
| Purpose | VARCHAR(20) | ✅ | 用车目的:reception(客户接待)/business(商务出行)/official(公务) |
| Reason | NVARCHAR(500) | ✅ | 用车事由详细说明 |
| Origin | NVARCHAR(200) | ✅ | 行程预计出发地地址 |
| OriginLongitude | DECIMAL(10,6) | 始发地经度(地图选点回填) | |
| OriginLatitude | DECIMAL(10,6) | 始发地纬度 | |
| Destination | NVARCHAR(200) | ✅ | 行程预计目的地地址 |
| DestLongitude | DECIMAL(10,6) | 目的地经度 | |
| DestLatitude | DECIMAL(10,6) | 目的地纬度 | |
| PassengerCount | INT | 同行总人数,默认 1 | |
| StartTime | DATETIME | 预计出车时间,提交时必填 | |
| EndTime | DATETIME | 预计还车时间,提交时必填。校验:必须晚于 StartTime | |
| — | — | — | 以下为还车登记字段(审批通过后填写) |
| ActualReturnTime | DATETIME | 还车登记:实际归还时间 | |
| StartOdometer | DECIMAL(10,2) | 还车登记:出车前里程表读数 | |
| EndOdometer | DECIMAL(10,2) | 还车登记:还车后里程表读数。校验:必须 > StartOdometer | |
| ActualCost | DECIMAL(28,8) | 还车登记:路桥费/停车费等实际费用总额 | |
| CostRemark | NVARCHAR(500) | 还车登记:费用明细备注 | |
| Status | VARCHAR(20) | ✅ | 业务状态:draft/pending/approved/rejected/withdrawn/returned(已还车)/archived(已归档)。审批状态通过 .NET → ERP 实时查询。还车登记完成(returned)后自动流转为 archived |
| ApprovalInstanceId | VARCHAR(50) | ERP 审批实例 ID | |
| PreviousInstanceIds | VARCHAR(MAX) | 历史审批实例 ID JSON 数组 | |
| CreateTime | DATETIME | ✅ | DEFAULT GETDATE() |
| UpdateTime | DATETIME | ||
| IsDeleted | BIT | ✅ | DEFAULT 0 |
索引:
IX_Vehicle_List (ApplicantId, CreateTime DESC) INCLUDE (ApplicationNo, VehicleId, Purpose, Origin, Destination, StartTime)IX_Vehicle_Collision (VehicleId, StartTime, EndTime) WHERE Status IN ('pending', 'approved') — 排期冲突检测| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| Id | BIGINT | ✅ | 主键,IDENTITY(1,1) |
| VehicleId | BIGINT | ✅ | 关联用车单主表,FK → Vehicle.Id |
| UserId | NVARCHAR(30) | 内部同事 ERP 用户 ID(可空,添加外部人员时为空) | |
| PassengerName | NVARCHAR(50) | ✅ | 同行人姓名文本(ERP 用户取 RealName,外部人员自由输入) |
| CreateTime | DATETIME | ✅ | DEFAULT GETDATE() |
| UpdateTime | DATETIME | ||
| IsDeleted | BIT | ✅ | DEFAULT 0 |
索引:IX_VehiclePassenger_VehicleId (VehicleId, IsDeleted) INCLUDE (UserId, PassengerName)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| Id | BIGINT | ✅ | 主键,IDENTITY(1,1) |
| LicensePlate | NVARCHAR(20) | ✅ | 车牌号(含中文字符如粤B12345),UX_SysVehicle_LicensePlate UNIQUE 约束 |
| VehicleType | VARCHAR(20) | ✅ | 车辆类型:sedan(轿车)/suv(SUV)/mpv(商务车)/van(面包车) |
| Brand | NVARCHAR(50) | 品牌型号 | |
| Seats | INT | 核定座位数 | |
| DriverName | NVARCHAR(50) | 默认驾驶员姓名 | |
| Status | VARCHAR(20) | ✅ | 车辆状态:idle(空闲可用)/in_use(使用中)/maintenance(维修中)。用车申请审批通过自动变更为 in_use,还车登记完成(Vehicle.Status=returned/archived)后恢复 idle。maintenance 状态下不可被申请 |
| IsActive | BIT | ✅ | 启用/停用标记,默认 1。停用的车辆不可被申请 |
| CreateTime | DATETIME | ✅ | DEFAULT GETDATE() |
| UpdateTime | DATETIME | ||
| IsDeleted | BIT | ✅ | DEFAULT 0 |
业务员外出拜访客户的工作记录。GPS 定位只读防篡改,照片强制拍摄并加水印。不走审批流,提交即完成。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| Id | BIGINT | ✅ | 主键,IDENTITY(1,1) |
| VisitNo | VARCHAR(30) | ✅ | 单据唯一编号,格式 VST-YYYYMMDD-XXX,UX_OutingLog_VisitNo UNIQUE 约束 |
| SalespersonId | NVARCHAR(30) | ✅ | 业务员 ERP 用户 ID(外勤日志使用 SalespersonId 而非 ApplicantId,强调销售角色属性) |
| DeptId | NVARCHAR(30) | ✅ | 所属部门 ERP 部门 ID |
| CustomerId | NVARCHAR(30) | 拜访客户 ERP 客户 ID(非 FK)。输入新客户名时,提交后由 .NET → ERP CustomerService 创建新客户并回填此字段 | |
| CustomerName | NVARCHAR(300) | ✅ | 客户公司全称冗余字段,前端输入时调 ERP 联想匹配,也支持自由输入 |
| ContactId | NVARCHAR(30) | 客户联系人 ERP ID | |
| CheckInLongitude | DECIMAL(10,6) | GPS 硬件定位经度,提交时必填。前端设为只读,不可手动修改 | |
| CheckInLatitude | DECIMAL(10,6) | GPS 硬件定位纬度 | |
| CheckInAddress | NVARCHAR(500) | 逆地理编码出的街道地址,前端设为只读,不可手动修改 | |
| VisitSummary | NVARCHAR(2000) | ✅ | 今日工作核心总结 |
| NextPlan | NVARCHAR(500) | 后续推进计划 | |
| Status | VARCHAR(20) | ✅ | 状态:draft(草稿)/completed(已提交)。不走审批流,提交即完成,提交后锁定不可再编辑 |
| LastViewedTime | DATETIME | 员工最后一次查看详情页的时间(仅打开详情页时更新),用于判断是否有新点评(红点逻辑:MAX(CreateTime) > LastViewedTime) | |
| CreateTime | DATETIME | ✅ | DEFAULT GETDATE(),服务器授时(用于防伪水印) |
| UpdateTime | DATETIME | ||
| IsDeleted | BIT | ✅ | DEFAULT 0(仅管理员可软删除已提交日志) |
索引:
IX_OutingLog_Sales (SalespersonId, CreateTime DESC) INCLUDE (CustomerId, CustomerName, CheckInAddress)IX_OutingLog_Dept (DeptId, CreateTime DESC) INCLUDE (SalespersonId, CustomerId, CustomerName, CheckInAddress, Status)经理在外勤日志详情页的星级评分和文字点评,以气泡样式展示。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| Id | BIGINT | ✅ | 主键,IDENTITY(1,1) |
| LogId | BIGINT | ✅ | 关联外勤日志,FK → OutingLog.Id(主表软删除时级联软删除) |
| CommenterId | NVARCHAR(30) | ✅ | 点评人(经理)ERP 用户 ID |
| RatingStars | INT | 星级评分 1-5,CHECK 约束 (RatingStars IS NULL OR RatingStars >= 1 AND RatingStars <= 5) | |
| CommentText | NVARCHAR(1000) | ✅ | 点评指导意见文字内容 |
| CreateTime | DATETIME | ✅ | DEFAULT GETDATE()(红点判定基于此字段) |
| UpdateTime | DATETIME | (修改点评不更新 CreateTime,不触发新红点) | |
| IsDeleted | BIT | ✅ | DEFAULT 0 |
索引:IX_OutingLogComment_LogId (LogId, IsDeleted) INCLUDE (CommenterId, RatingStars, CommentText, CreateTime)
管理员发布的公司公告。支持按全员/部门/指定用户三种范围发布,可置顶、设有效期。管理员可查看已读/未读名单并 DING 催办。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| Id | BIGINT | ✅ | 主键,IDENTITY(1,1) |
| Title | NVARCHAR(200) | ✅ | 公告标题 |
| Content | NVARCHAR(MAX) | ✅ | 公告正文,支持 HTML/Markdown 格式 |
| Type | VARCHAR(20) | ✅ | 公告分类:notice(通知公告)/policy(人事与制度)/activity(放假与活动) |
| Status | VARCHAR(20) | ✅ | 发布状态:draft(草稿,仅创建者和管理员可见)/published(已发布,全员可见) |
| PublisherId | NVARCHAR(30) | ✅ | 发布管理员 ERP 用户 ID |
| PublishTime | DATETIME | 实际发布时间,草稿时为 NULL,发布时写入 GETDATE() | |
| IsTop | BIT | ✅ | 是否全局置顶:1=置顶优先展示,0=正常排序,默认 0 |
| PrivateLevel | INT | ✅ | 可见范围级别:0=全员,1=按部门,2=按指定用户。默认 0 |
| ExpiryDate | DATETIME | 自动下架失效时间,不填则永不过期。过期后列表置灰标注"已过期" | |
| CreateTime | DATETIME | ✅ | DEFAULT GETDATE() |
| UpdateTime | DATETIME | ||
| IsDeleted | BIT | ✅ | DEFAULT 0 |
索引:IX_Announcement_List (Type, IsTop DESC, PublishTime DESC) INCLUDE (Title, PublisherId, Status, ExpiryDate) WHERE IsDeleted = 0 AND Status = 'published'
当公告按部门或按指定用户发布时,记录目标范围。多态外键设计(SQL Server 不支持多态 FK,由应用层校验引用完整性)。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| Id | BIGINT | ✅ | 主键,IDENTITY(1,1) |
| AnnouncementId | BIGINT | ✅ | 关联公告主表,FK → Announcement.Id |
| TargetType | VARCHAR(10) | ✅ | 目标实体类型:all(全员)/dept(按部门)/user(按指定个人)。PrivateLevel=0 时写入一条 all 记录 |
| TargetId | NVARCHAR(30) | ✅ | 多态外键:TargetType=all 时为 'ALL';TargetType=dept 时指向 ERP 部门 ID;TargetType=user 时指向 ERP 用户 ID |
| CreateTime | DATETIME | ✅ | DEFAULT GETDATE() |
| UpdateTime | DATETIME |
记录每个员工对每条公告的已读/未读状态。发布时根据 PrivateLevel 批量初始化 IsRead=0 记录,停留 ≥2 秒标记已读。管理员通过此表查看触达率并 DING 催办。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| Id | BIGINT | ✅ | 主键,IDENTITY(1,1) |
| AnnouncementId | BIGINT | ✅ | 关联公告,FK → Announcement.Id |
| UserId | NVARCHAR(30) | ✅ | 被触达员工 ERP 用户 ID |
| IsRead | BIT | ✅ | 已读标记:0=未读,1=已读。停留 ≥2 秒自动标记,默认 0 |
| ReadTime | DATETIME | 员工实际阅读时间 | |
| IsUrged | BIT | ✅ | 是否已被管理员 DING 催办过,默认 0(催办后仍可再次催办,更新 LastUrgeTime) |
| LastUrgeTime | DATETIME | 最后一次 DING 催办时间 | |
| CreateTime | DATETIME | ✅ | DEFAULT GETDATE() |
| UpdateTime | DATETIME |
约束:UX_AnnouncementReadLog_AnnouncementId_UserId UNIQUE (AnnouncementId, UserId) — 每个员工对每条公告仅有一条触达记录
索引:IX_Announcement_Ding (AnnouncementId, IsRead, IsUrged) INCLUDE (UserId, ReadTime)
费用申请(支撑材料)、费用报销(发票影像)、外勤日志(现场照片)、公告(附件)四种业务附件合并为一张聚合表。通过 BizType 区分业务类型,DetailId 可绑定报销明细行(仅报销使用),SortOrder 用于外勤照片墙排序。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| Id | BIGINT | ✅ | 主键,IDENTITY(1,1) |
| BizType | VARCHAR(30) | ✅ | 业务类型:expense_apply(费用申请)/expense(费用报销)/outing_log(外勤日志)/announcement(公告) |
| BizId | BIGINT | ✅ | 父表 ID(多态,与 BizType 联合路由到具体业务表) |
| DetailId | BIGINT | 仅 BizType='expense' 使用,绑定 ExpenseDetail.Id,实现发票与明细行的关联 | |
| FileName | NVARCHAR(200) | ✅ | 原始文件名 |
| FileUrl | VARCHAR(500) | ✅ | 云端对象存储(OSS)绝对 URL |
| FileType | VARCHAR(20) | ✅ | 文件类型:image(通用图片)/pdf/doc/xls/sign_in_photo(签到照)/visit_photo(现场照)/other |
| FileSize | BIGINT | 文件字节数,用于上传大小校验 | |
| SortOrder | INT | 排序号,默认 0。外勤照片墙按此排序展示 | |
| CreateTime | DATETIME | ✅ | DEFAULT GETDATE() |
| UpdateTime | DATETIME | ||
| IsDeleted | BIT | ✅ | DEFAULT 0 |
应用层约束(前端 + .NET 校验):
索引:IX_Attachment_Biz (BizType, BizId, IsDeleted) INCLUDE (FileName, FileUrl, FileType, FileSize, DetailId, SortOrder)
费用报销和费用申请表单中"费用类别"下拉的数据源。通过 BizScope 控制适用范围(事前/报销/通用)。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| Id | BIGINT | ✅ | 主键,IDENTITY(1,1) |
| CategoryCode | VARCHAR(30) | ✅ | 类别编码,UX_SysCostCategory_CategoryCode UNIQUE 约束 |
| CategoryName | NVARCHAR(200) | ✅ | 类别中文名称,如"交通费" |
| ParentId | BIGINT | 上级类别 ID,FK → SysCostCategory.Id(自引用)。NULL 为一级分类,非 NULL 为叶子节点。仅叶子节点可用于明细录入 | |
| AcctSubjectId | NVARCHAR(50) | 关联 ERP 会计科目代号 | |
| AcctSubjectName | NVARCHAR(400) | 关联 ERP 会计科目名称冗余 | |
| Remark | NVARCHAR(500) | 说明备注 | |
| SortOrder | INT | ✅ | 排序权重,默认 0 |
| IsActive | BIT | ✅ | 启用/停用标记:1=启用,0=停用 |
| DisableDate | DATETIME | 停用日期,到期后该类别不可被新单据引用 | |
| IsAttachmentRequired | BIT | ✅ | 是否需要附件:0=不需要,1=必须上传附件,DEFAULT 0 |
| BizScope | VARCHAR(20) | ✅ | 适用业务范围:expense_apply(仅费用申请)/expense(仅报销)/both(通用),默认 both。费用申请下拉仅显示 expense_apply 和 both;报销下拉仅显示 expense 和 both;导入报销时也不豁免 |
| CreateTime | DATETIME | ✅ | DEFAULT GETDATE() |
| UpdateTime | DATETIME | ||
| IsDeleted | BIT | ✅ | DEFAULT 0 |
索引:
IX_CostCategory_Parent (ParentId, SortOrder) INCLUDE (CategoryName, CategoryCode) WHERE IsActive = 1IX_CostCategory_Scope (BizScope, ParentId, SortOrder) INCLUDE (CategoryName, CategoryCode) WHERE IsActive = 1 AND IsDeleted = 0工作台顶部轮播图组件的数据源。管理员可维护图片、标题、跳转链接和排序。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| Id | BIGINT | ✅ | 主键,IDENTITY(1,1) |
| ImageUrl | VARCHAR(500) | ✅ | 轮播图片云端存储绝对 URL |
| Title | NVARCHAR(100) | 图片标题/alt 文本 | |
| LinkUrl | VARCHAR(500) | 点击跳转链接。为空时不可点击,仅全屏预览图片 | |
| SortOrder | INT | ✅ | 排序权重,值越小越靠前,默认 0 |
| IsActive | BIT | ✅ | 启用/停用标记:1=启用,0=停用 |
| CreateTime | DATETIME | ✅ | DEFAULT GETDATE() |
| UpdateTime | DATETIME | ||
| IsDeleted | BIT | ✅ | DEFAULT 0 |
索引:IX_Banner_Active (IsActive, SortOrder) INCLUDE (ImageUrl, Title, LinkUrl) WHERE IsDeleted = 0
所有 OA 自管表统一使用 IsDeleted 软删除。SELECT/JOIN 必须加 WHERE IsDeleted = 0。
例外:OaPermissionChangeLog(审计日志不可修改不可删除)、ExpenseApplyMapping(纯关联表,随主表级联清理)、AnnouncementTarget / AnnouncementReadLog(随公告级联清理)。
审批数据不在 OA 本地存储。审批状态、审批时间线、待办列表均通过 .NET → ERP 实时查询。业务表仅存两个字段用于关联和追溯:
| 字段 | 作用 | 更新时机 |
|---|---|---|
ApprovalInstanceId |
关联 ERP 审批实例 | 提交审批时写入;撤回/驳回重新提交时覆盖 |
PreviousInstanceIds |
追溯历史审批 | 撤回/驳回重新提交时追加旧 instanceId |
状态同步流程:
列表页 → 实时调 .NET → ERP 查询审批状态
详情页 → 实时调 .NET → ERP 查询审批状态和审批时间线
审批操作 → OA 调 .NET → ERP 执行审批动作
消息通知 → .NET 服务端消息模块在 ERP 审批事件后触发推送
ERP 创建审批实例失败处理:保持 draft 状态,提示"提交审批失败,请稍后重试",用户可重新提交。
ERP 创建成功但 OA 回写失败处理:采用异步补偿机制,后台定时任务扫描"创建成功但未回写"的事务日志,补偿回写 ApprovalInstanceId。
UsageStatus 由 .NET 服务端在报销单状态变更时自动重算:
| 已报总额(仅计入 Status IN ('pending', 'approved', 'archived') 的报销单) | UsageStatus | Status 联动 |
|---|---|---|
| SUM(ExpenseApplyMapping.ImportedAmount) = 0 | unused | 保持当前状态 |
| > 0 且 < EstimatedAmount | partially_used | 保持当前状态 |
| ≥ EstimatedAmount | fully_used | 自动流转为 archived |
注意:
Status='withdrawn'和IsDeleted=1的报销单不计入已报总额。
| 主表 | 子表 |
|---|---|
| ExpenseApply | ExpenseApplyDetail, Attachment (BizType='expense_apply') |
| Expense | ExpenseDetail, ExpenseApplyMapping, Attachment (BizType='expense') |
| Vehicle | VehiclePassenger |
| OutingLog | OutingLogComment, Attachment (BizType='outing_log') |
| Announcement | AnnouncementTarget, AnnouncementReadLog, Attachment (BizType='announcement') |
.NET 服务端使用 sp_getapplock + SELECT MAX(ApplicationNo) 原子生成,保证高并发下按天不重复。
-- 费用申请列表
CREATE NONCLUSTERED INDEX IX_ExpenseApply_List
ON ExpenseApply (ApplicantId, CreateTime DESC)
INCLUDE (ExpenseApplyNo, EstimatedAmount, Purpose, UsageStatus);
-- 费用申请一键导入(仅已通过且尚未用完的)
CREATE NONCLUSTERED INDEX IX_ExpenseApply_Import
ON ExpenseApply (ApplicantId, UsageStatus)
WHERE UsageStatus IN ('unused', 'partially_used');
-- 费用报销列表
CREATE NONCLUSTERED INDEX IX_Expense_List
ON Expense (ApplicantId, CreateTime DESC)
INCLUDE (TotalAmount, ExpenseNo, PaymentStatus);
-- 申请↔报销多对多
CREATE NONCLUSTERED INDEX IX_ExpenseApplyMapping_AppId
ON ExpenseApplyMapping (ExpenseApplyId)
INCLUDE (ExpenseId, ImportedAmount);
-- 加班列表
CREATE NONCLUSTERED INDEX IX_Overtime_List
ON Overtime (ApplicantId, CreateTime DESC)
INCLUDE (ApplicationNo, OtType, NetOtHours, CompensationType);
-- 用车列表 + 排期冲突检测
CREATE NONCLUSTERED INDEX IX_Vehicle_List
ON Vehicle (ApplicantId, CreateTime DESC)
INCLUDE (ApplicationNo, VehicleId, Purpose, Origin, Destination, StartTime);
CREATE NONCLUSTERED INDEX IX_Vehicle_Collision
ON Vehicle (VehicleId, StartTime, EndTime)
WHERE Status IN ('pending', 'approved');
-- 外勤日志-业务员
CREATE NONCLUSTERED INDEX IX_OutingLog_Sales
ON OutingLog (SalespersonId, CreateTime DESC)
INCLUDE (CustomerId, CustomerName, CheckInAddress);
-- 外勤日志-部门
CREATE NONCLUSTERED INDEX IX_OutingLog_Dept
ON OutingLog (DeptId, CreateTime DESC)
INCLUDE (SalespersonId, CustomerId, CustomerName, CheckInAddress, Status);
-- 公告列表 + 触达审计
CREATE NONCLUSTERED INDEX IX_Announcement_List
ON Announcement (Type, IsTop DESC, PublishTime DESC)
INCLUDE (Title, PublisherId, Status, ExpiryDate)
WHERE IsDeleted = 0 AND Status = 'published';
CREATE NONCLUSTERED INDEX IX_Announcement_Ding
ON AnnouncementReadLog (AnnouncementId, IsRead, IsUrged)
INCLUDE (UserId, ReadTime);
-- 统一附件
CREATE NONCLUSTERED INDEX IX_Attachment_Biz
ON Attachment (BizType, BizId, IsDeleted)
INCLUDE (FileName, FileUrl, FileType, FileSize, DetailId, SortOrder);
-- OA 权限
CREATE UNIQUE NONCLUSTERED INDEX UX_OaUserPermission_UserId_PermissionId
ON OaUserPermission (UserId, PermissionId) WHERE IsDeleted = 0;
CREATE NONCLUSTERED INDEX IX_UserPermission_UserId
ON OaUserPermission (UserId, IsDeleted) INCLUDE (PermissionId);
CREATE NONCLUSTERED INDEX IX_PermChangeLog_Target
ON OaPermissionChangeLog (TargetUserId, CreateTime DESC)
INCLUDE (OperatorId, ChangeType);
-- 基础字典
CREATE NONCLUSTERED INDEX IX_CostCategory_Parent
ON SysCostCategory (ParentId, SortOrder)
INCLUDE (CategoryName, CategoryCode) WHERE IsActive = 1;
CREATE NONCLUSTERED INDEX IX_CostCategory_Scope
ON SysCostCategory (BizScope, ParentId, SortOrder)
INCLUDE (CategoryName, CategoryCode) WHERE IsActive = 1 AND IsDeleted = 0;
CREATE NONCLUSTERED INDEX IX_Banner_Active
ON SysBanner (IsActive, SortOrder)
INCLUDE (ImageUrl, Title, LinkUrl) WHERE IsDeleted = 0;
-- 外勤点评 + 用车同行人 + 报销明细 + 事前明细
CREATE NONCLUSTERED INDEX IX_OutingLogComment_LogId
ON OutingLogComment (LogId, IsDeleted)
INCLUDE (CommenterId, RatingStars, CommentText, CreateTime);
CREATE NONCLUSTERED INDEX IX_VehiclePassenger_VehicleId
ON VehiclePassenger (VehicleId, IsDeleted)
INCLUDE (UserId, PassengerName);
CREATE NONCLUSTERED INDEX IX_ExpenseDetail_ExpenseId
ON ExpenseDetail (ExpenseId, SortOrder)
INCLUDE (Amount, TotalAmount, ApprovedAmount, CustomerVendorName, OffsetAmount);
CREATE NONCLUSTERED INDEX IX_ExpenseApplyDetail_AppId
ON ExpenseApplyDetail (ExpenseApplyId, SortOrder)
INCLUDE (ExpenseCategory, EstimatedAmount, Remark);
审批状态(draft/pending/approved/rejected/withdrawn)仅 draft 由 OA 本地直接写入,其余状态由 ERP 审批引擎管理,OA 通过 .NET → ERP 实时查询。archived 为业务完结终态,由 OA 本地根据业务规则自动流转。
| 值 | 含义 | 适用表 | 写入方 | 触发条件 |
|---|---|---|---|---|
draft |
草稿 | 全部业务表 | OA 本地 | 用户存草稿 |
pending |
待审批 | ExpenseApply, Expense, Overtime, Vehicle | OA 写入后 ERP 接管 | 提交审批 |
approved |
已通过 | 同上 | ERP 管理 | 审批全部通过 |
rejected |
已拒绝 | 同上 | ERP 管理 | 审批人拒绝 |
withdrawn |
已撤回 | 同上 | OA 本地 | 员工撤回 |
returned |
已还车 | Vehicle(独有) | OA 本地 | 还车登记完成 |
completed |
已提交 | OutingLog(不走审批流) | OA 本地 | 提交日志 |
published |
已发布 | Announcement(不走审批流) | OA 本地 | 发布公告 |
archived |
已归档 | ExpenseApply, Expense, Overtime, Vehicle | OA 本地 | 业务完结:ExpenseApply.UsageStatus=fully_used;Expense.PaymentStatus=paid;Overtime 业务完结;Vehicle 还车登记完成 |
| 值 | 含义 |
|---|---|
unpaid |
待付款 |
paid |
已付款 |
| 值 | 含义 |
|---|---|
normal |
普通 |
urgent |
紧急 |
critical |
特急 |
| 值 | 含义 |
|---|---|
unused |
未被报销引用 |
partially_used |
部分金额已报销 |
fully_used |
全部金额已报销,不可再被引用。触发 Status 流转为 archived |
| 值 | 含义 |
|---|---|
workday |
工作日加班 |
weekend |
休息日加班 |
holiday |
法定节假日加班 |
| 值 | 含义 |
|---|---|
overtime_pay |
全部结算加班费 |
comp_leave |
全部转为调休 |
mixed |
混合模式(按 CompLeaveRatio 分配) |
| 值 | 含义 |
|---|---|
reception |
客户接待 |
business |
商务出行 |
official |
公务 |
| 值 | 含义 |
|---|---|
sedan |
轿车 |
suv |
SUV |
mpv |
商务车 |
van |
面包车 |
| 值 | 含义 |
|---|---|
idle |
空闲可用 |
in_use |
使用中 |
maintenance |
维修中(不可申请) |
| 值 | 含义 |
|---|---|
notice |
通知公告 |
policy |
人事与制度 |
activity |
放假与活动 |
| PrivateLevel | 含义 | TargetType | TargetId |
|---|---|---|---|
0 |
全员 | all |
'ALL' |
1 |
按部门 | dept |
ERP 部门 ID |
2 |
按用户 | user |
ERP 用户 ID |
| 值 | 含义 |
|---|---|
expense_apply |
仅费用申请端使用 |
expense |
仅费用报销端使用 |
both |
两端通用 |
| 值 | 含义 | 适用 BizType |
|---|---|---|
image |
通用图片(JPG/PNG/WebP) | expense_apply / expense / announcement |
pdf |
PDF 文档 | expense_apply / expense / announcement |
doc |
Word 文档 | expense_apply / expense / announcement |
xls |
Excel 表格 | expense_apply / expense / announcement |
sign_in_photo |
外勤签到照(水印含授时+GPS) | outing_log |
visit_photo |
外勤拜访现场照 | outing_log |
other |
其他附件 | outing_log |
| 字段 | 0 的含义 | 1 的含义 |
|---|---|---|
IsDeleted |
正常 | 已软删除 |
IsActive |
停用/禁用 | 启用 |
IsRead |
未读 | 已读 |
IsTop |
普通 | 置顶 |
IsUrged |
未催办 | 已催办 |
IsInvoiceVerified |
未查验 | 已查验 |
IsTaxIdMatched |
未匹配 | 已匹配 |
IsCategoryCompliant |
不合规 | 合规 |
| 值 | 对应权限点前缀 |
|---|---|
expense |
oa.expense.* |
expense_apply |
oa.expense_apply.* |
overtime |
oa.overtime.* |
vehicle |
oa.vehicle.* |
outing_log |
oa.outing_log.* |
announcement |
oa.announcement.* |
report |
oa.report.* |
admin |
oa.admin.* |
| 值 | 含义 |
|---|---|
assign |
赋予权限 |
revoke |
移除权限 |
toggle_active |
启用/禁用(权限点全局) |
文档版本:v1.1 | 日期:2026-06-27