版本:v1.0 | 日期:2026-06-04 | Base URL:
https://{host}/api
Content-Type: application/json
Authorization: Bearer {token} ← 由宿主 App 注入
X-User-Id: {erpUserId} ← 当前登录用户 ERP ID
{
"code": 0,
"message": "success",
"data": { ... },
"timestamp": 1717401600
}
{
"code": 0,
"data": {
"items": [ ... ],
"total": 128,
"page": 1,
"pageSize": 20
}
}
| code | 含义 | 处理方式 |
|---|---|---|
| 0 | 成功 | — |
| 400 | 参数校验失败 | Toast 提示具体字段错误 |
| 401 | 未授权 / Token 过期 | 触发宿主重新登录 |
| 403 | 无 OA 操作权限 | Toast "无此操作权限" |
| 404 | 资源不存在 | TDEmpty 或 Toast |
| 409 | 并发冲突 (CONCURRENCY_CONFLICT / APPROVAL_CONFLICT) | TDDialog 提示刷新 |
| 422 | 业务校验失败 | 展示具体错误信息 |
| 500 | 服务器内部错误 | Toast "服务器繁忙,请稍后重试" |
| 504 | ERP 审批接口超时 | Toast "审批服务暂不可用,请稍后重试" |
所有状态/类型字段使用英文标识字符串,前端负责国际化映射。完整枚举取值见 tboss-oa-database.md 第 6 节。
GET /api/oa/permissions
响应:
{
"code": 0,
"data": {
"permissions": [
{
"id": 1,
"code": "oa.expense.apply",
"name": "发起报销",
"module": "expense",
"sortOrder": 1
},
{
"id": 2,
"code": "oa.expense.view_own",
"name": "查看自己的报销单",
"module": "expense",
"sortOrder": 2
}
]
}
}
GET /api/oa/user-permissions?userId={erpUserId}
响应:
{
"code": 0,
"data": {
"userId": 1001,
"permissions": ["oa.expense.apply", "oa.expense.view_own", "oa.announcement.view"],
"presetRoles": ["employee"]
}
}
PUT /api/oa/user-permissions
请求:
{
"userId": 1001,
"permissionCodes": [
"oa.expense.apply",
"oa.expense.view_dept",
"oa.expense.approve",
"oa.outing_log.comment"
]
}
响应:
{
"code": 0,
"message": "权限已更新"
}
错误:
| code | 场景 |
|------|------|
| 403 | 无 oa.admin.permissions 权限 |
| 422 | 试图移除自己的管理员权限 / 移除最后一名管理员 |
GET /api/oa/permission-changelog?userId={erpUserId}&page=1&pageSize=20
响应:
{
"code": 0,
"data": {
"items": [
{
"id": 1,
"targetUserId": 1001,
"operatorId": 2001,
"operatorName": "管理员王五",
"changeType": "assign",
"beforePermissions": ["oa.expense.apply", "oa.expense.view_own"],
"afterPermissions": ["oa.expense.apply", "oa.expense.view_own", "oa.expense.approve"],
"createTime": "2026-06-03T10:30:00"
}
],
"total": 5,
"page": 1
}
}
{bizType} 取值:expense-apply | expense | overtime | vehicle
POST /api/oa/{bizType}/submit
请求:
{
"id": null,
"urgency": "normal",
"expenseTypes": "travel,office",
"purpose": "北京客户拜访差旅费预估",
"projectId": 100,
"budgetSubjectId": 5,
"isTaxIncluded": false,
"validUntil": "2026-07-03",
"referenceNo": "HT-2026-0089",
"applicationDate": "2026-06-03",
"estimatedStartDate": "2026-06-10",
"estimatedEndDate": "2026-06-12",
"isOvernight": true,
"transportType": "high_speed_rail",
"entertainmentTarget": null,
"entertainmentLevel": null,
"guestCount": null,
"companionCount": null,
"venue": null,
"details": [
{
"expenseCategory": "transport",
"quantity": 2,
"unitPrice": 550.00,
"unit": "张",
"remark": "往返高铁票",
"sortOrder": 1
},
{
"expenseCategory": "hotel",
"quantity": 2,
"unitPrice": 400.00,
"unit": "晚",
"remark": "住宿",
"sortOrder": 2
}
],
"attachments": [
{ "fileName": "出差审批截图.png", "fileUrl": "https://oss/...", "fileType": "image", "fileSize": 204800 }
]
}
费用类型联动字段:仅当
expenseTypes含对应类型时前端展示并必填。travel时需estimatedStartDate/EndDate/isOvernight/transportType;entertainment时需entertainmentTarget/Level/guestCount/companionCount/venue(校验companionCount ≤ guestCount);meeting时需estimatedStartDate/EndDate/venue。数据库全部 NULL 允许,应用层按 expenseTypes 动态校验。
响应:
{
"code": 0,
"data": {
"id": 12345,
"applicationNo": "BXSQ-20260603-001",
"approvalInstanceId": "inst_erp_a_009832",
"previousInstanceIds": []
}
}
请求:
{
"id": null,
"applicationMappings": [
{ "applicationId": 12300, "importedAmount": 5500.00 }
],
"purpose": "北京客户拜访差旅费报销",
"projectId": 100,
"budgetSubjectId": 5,
"costCenterId": 10,
"applicationDate": "2026-06-03",
"bankName": "中国工商银行",
"accountName": "张三",
"bankAccount": "6222021234567890123",
"details": [
{
"expenseDate": "2026-06-10",
"expenseType": "transport",
"expenseDesc": "G123 北京南-上海虹桥",
"amount": 523.58,
"taxAmount": 26.42,
"currencyCode": "CNY",
"exchangeRate": 1.0000,
"invoiceNo": "12345678",
"invoiceCode": "011001900111",
"invoiceType": "special",
"taxRate": 0.0900,
"sortOrder": 1
}
],
"attachments": [
{ "fileName": "高铁票.jpg", "fileUrl": "https://oss/...", "fileType": "image", "fileSize": 307200, "detailIndex": 0 }
]
}
导入申请:
applicationMappings非必填,传入时项目/科目/成本中心自动从申请带入。直接新建时不传,需手动填写projectId/budgetSubjectId。costCenterId可选(ERP 无数据时不传)。currencyCode默认 CNY,选外币时exchangeRate由服务端从 ERP 查询后填入,前端只传currencyCode。totalAmount(价税合计)= amount + taxAmount 由服务端计算。baseAmount= totalAmount × exchangeRate 由服务端计算。
响应:
{
"code": 0,
"data": {
"id": 12348,
"reportNo": "BX-20260603-001",
"approvalInstanceId": "inst_erp_a_009834",
"previousInstanceIds": []
}
}
请求:
{
"id": null,
"otType": "workday",
"compensationType": "overtime_pay",
"compLeaveRatio": null,
"startTime": "2026-06-03T18:00:00",
"endTime": "2026-06-03T22:00:00",
"reason": "紧急上线支持,需加班完成部署"
}
混合模式:
compensationType=mixed时compLeaveRatio必填,范围 0.10~0.90(步长 0.10),如 0.30 表示 30% 调休 + 70% 加班费。NetOtHours由服务端自动扣除午休(12:00-13:00)和晚餐(18:00-18:30)盲区后计算。applicationDate由服务端填写提交当日。
响应:
{
"code": 0,
"data": {
"id": 12346,
"applicationNo": "JB-20260603-001",
"netOtHours": 3.5,
"approvalInstanceId": "inst_erp_a_009833",
"previousInstanceIds": []
}
}
请求:
{
"id": null,
"vehicleId": 3,
"purpose": "business",
"reason": "前往客户现场演示产品",
"origin": "公司总部",
"originLongitude": 121.473701,
"originLatitude": 31.230416,
"destination": "客户公司",
"destLongitude": 121.510000,
"destLatitude": 31.250000,
"startTime": "2026-06-05T09:00:00",
"endTime": "2026-06-05T17:00:00",
"passengerCount": 3,
"passengers": [
{ "userId": 1002, "passengerName": "李四" },
{ "userId": null, "passengerName": "客户代表" }
]
}
排期冲突检测:提交前应调
GET /api/oa/vehicle/check-collision异步校验。冲突时前端锁死提交按钮。applicationDate由服务端填写提交当日。
响应:同其他单据。
PUT /api/oa/{bizType}/draft
请求体与提交相同,id 为已有草稿 ID(编辑已有草稿)或 null(新建草稿)。不触发审批。
响应:
{
"code": 0,
"data": { "id": 12347 }
}
GET /api/oa/expense-apply/list?status=pending&page=1&pageSize=20
GET /api/oa/expense/list?status=all&page=1
GET /api/oa/overtime/list?status=approved&page=1
GET /api/oa/vehicle/list?status=all&page=1
参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| status | string | 否 | 审批单据:all / draft / pending / approved / rejected / withdrawn。报销额外:unpaid / paid。用车额外:returned |
| page | int | 否 | 默认 1 |
| pageSize | int | 否 | 默认 20 |
| scope | string | 否 | own(默认) / dept / all — 根据权限自动限定 |
响应(以报销列表为例):
{
"code": 0,
"data": {
"items": [
{
"id": 100,
"reportNo": "BX-20260603-001",
"totalAmount": 1580.00,
"purpose": "差旅费报销",
"applicationDate": "2026-06-03",
"status": "approved",
"paymentStatus": "unpaid",
"applicantName": "张三",
"deptName": "销售部",
"createTime": "2026-06-03T10:00:00"
}
],
"total": 42,
"page": 1,
"pageSize": 20
}
}
事前申请列表响应(增量字段):
{
"id": 200,
"applicationNo": "BXSQ-20260603-001",
"estimatedAmount": 1900.00,
"purpose": "差旅费预估",
"expenseTypes": "travel,office",
"applicationDate": "2026-06-03",
"status": "pending",
"usageStatus": "unused",
"applicantName": "张三",
"deptName": "销售部",
"createTime": "2026-06-03T09:00:00"
}
加班列表响应(增量字段):
{
"id": 300,
"applicationNo": "JB-20260603-001",
"otType": "workday",
"netOtHours": 3.5,
"compensationType": "overtime_pay",
"compLeaveRatio": null,
"reason": "紧急上线支持",
"applicationDate": "2026-06-03",
"status": "approved",
"applicantName": "张三",
"deptName": "销售部",
"createTime": "2026-06-03T18:30:00"
}
用车列表响应(增量字段):
{
"id": 400,
"applicationNo": "YC-20260603-001",
"vehicleId": 3,
"licensePlate": "粤B12345",
"purpose": "business",
"origin": "公司总部",
"destination": "客户公司",
"startTime": "2026-06-05T09:00:00",
"status": "approved",
"applicantName": "张三",
"deptName": "销售部",
"createTime": "2026-06-03T11:00:00"
}
GET /api/oa/expense-apply/detail/12345
GET /api/oa/expense/detail/100
GET /api/oa/overtime/detail/12346
GET /api/oa/vehicle/detail/200
事前申请详情响应:
{
"code": 0,
"data": {
"id": 12345,
"applicationNo": "BXSQ-20260603-001",
"applicantId": 1001,
"applicantName": "张三",
"deptId": 10,
"deptName": "销售部",
"applicationDate": "2026-06-03",
"urgency": "normal",
"expenseTypes": "travel,office",
"purpose": "北京客户拜访差旅费预估",
"projectId": 100,
"projectName": "华东市场拓展",
"budgetSubjectId": 5,
"budgetSubjectName": "差旅费",
"estimatedAmount": 1900.00,
"isTaxIncluded": false,
"validUntil": "2026-07-03",
"referenceNo": "HT-2026-0089",
"estimatedStartDate": "2026-06-10",
"estimatedEndDate": "2026-06-12",
"isOvernight": true,
"transportType": "high_speed_rail",
"entertainmentTarget": null,
"entertainmentLevel": null,
"guestCount": null,
"companionCount": null,
"venue": null,
"status": "pending",
"usageStatus": "unused",
"approvalInstanceId": "inst_erp_a_009832",
"previousInstanceIds": [],
"details": [
{
"id": 600,
"expenseCategory": "transport",
"expenseCategoryName": "交通费",
"quantity": 2,
"unitPrice": 550.00,
"unit": "张",
"estimatedAmount": 1100.00,
"remark": "往返高铁票",
"sortOrder": 1
},
{
"id": 601,
"expenseCategory": "hotel",
"expenseCategoryName": "住宿费",
"quantity": 2,
"unitPrice": 400.00,
"unit": "晚",
"estimatedAmount": 800.00,
"remark": "住宿",
"sortOrder": 2
}
],
"attachments": [
{ "id": 20, "fileName": "出差审批截图.png", "fileUrl": "https://oss/...", "fileType": "image", "fileSize": 204800 }
],
"createTime": "2026-06-03T09:00:00",
"updateTime": "2026-06-03T09:00:00"
}
}
费用报销详情响应:
{
"code": 0,
"data": {
"id": 100,
"reportNo": "BX-20260603-001",
"applicantId": 1001,
"applicantName": "张三",
"deptId": 10,
"deptName": "销售部",
"applicationDate": "2026-06-03",
"projectId": 100,
"projectName": "华东市场拓展",
"budgetSubjectId": 5,
"budgetSubjectName": "差旅费",
"costCenterId": 10,
"costCenterName": "销售部-华东区",
"totalAmount": 1580.00,
"purpose": "差旅费报销",
"bankName": "中国工商银行",
"accountName": "张三",
"bankAccount": "6222021234567890123",
"isInvoiceVerified": false,
"isTaxIdMatched": false,
"isCategoryCompliant": false,
"bankTransferNo": null,
"voucherNo": null,
"status": "approved",
"paymentStatus": "unpaid",
"approvalInstanceId": "inst_erp_a_009832",
"previousInstanceIds": [],
"applicationMappings": [
{
"applicationId": 12300,
"applicationNo": "BXSQ-20260520-003",
"importedAmount": 5500.00
}
],
"details": [
{
"id": 500,
"expenseDate": "2026-06-10",
"expenseType": "transport",
"expenseTypeName": "交通费",
"expenseDesc": "G123 高铁票",
"amount": 523.58,
"taxAmount": 26.42,
"totalAmount": 550.00,
"currencyCode": "CNY",
"exchangeRate": 1.0000,
"baseAmount": 550.00,
"invoiceNo": "12345678",
"invoiceCode": "011001900111",
"invoiceType": "special",
"taxRate": 0.0900,
"sortOrder": 1
}
],
"attachments": [
{ "id": 10, "fileName": "高铁票.jpg", "fileUrl": "https://oss/...", "fileType": "image", "fileSize": 307200, "detailId": 500 }
],
"createTime": "2026-06-03T10:00:00",
"updateTime": "2026-06-03T15:30:00"
}
}
加班详情响应:
{
"code": 0,
"data": {
"id": 12346,
"applicationNo": "JB-20260603-001",
"applicantId": 1001,
"applicantName": "张三",
"deptId": 10,
"deptName": "销售部",
"otType": "workday",
"compensationType": "overtime_pay",
"compLeaveRatio": null,
"startTime": "2026-06-03T18:00:00",
"endTime": "2026-06-03T22:00:00",
"netOtHours": 3.5,
"reason": "紧急上线支持,需加班完成部署",
"status": "approved",
"approvalInstanceId": "inst_erp_a_009833",
"previousInstanceIds": [],
"createTime": "2026-06-03T18:30:00",
"updateTime": "2026-06-03T18:30:00"
}
}
用车详情响应:
{
"code": 0,
"data": {
"id": 200,
"applicationNo": "YC-20260603-001",
"applicantId": 1001,
"applicantName": "张三",
"deptId": 10,
"deptName": "销售部",
"vehicleId": 3,
"licensePlate": "粤B12345",
"vehicleType": "sedan",
"brand": "丰田凯美瑞",
"purpose": "business",
"reason": "前往客户现场演示产品",
"origin": "公司总部",
"originLongitude": 121.473701,
"originLatitude": 31.230416,
"destination": "客户公司",
"destLongitude": 121.510000,
"destLatitude": 31.250000,
"passengerCount": 3,
"passengers": [
{ "userId": 1002, "passengerName": "李四" },
{ "userId": null, "passengerName": "客户代表" }
],
"startTime": "2026-06-05T09:00:00",
"endTime": "2026-06-05T17:00:00",
"actualReturnTime": null,
"startOdometer": null,
"endOdometer": null,
"actualCost": null,
"costRemark": null,
"status": "approved",
"approvalInstanceId": "inst_erp_a_009835",
"previousInstanceIds": [],
"createTime": "2026-06-03T11:00:00",
"updateTime": "2026-06-03T11:00:00"
}
}
DELETE /api/oa/expense/100
DELETE /api/oa/{bizType}/{id}
约束:仅 draft 状态可删除。软删除(IsDeleted=1)。
响应:
{ "code": 0, "message": "已删除" }
错误: | code | 场景 | |------|------| | 422 | 非 draft 状态不可删除 |
GET /api/oa/expense-apply/import-candidates?applicantId=1001
说明:费用报销表单中"导入已通过的事前申请"的数据源。返回当前用户已通过且尚有可用额度(UsageStatus IN ('unused', 'partially_used'))的申请列表。
响应:
{
"code": 0,
"data": {
"items": [
{
"id": 12300,
"applicationNo": "BXSQ-20260520-003",
"estimatedAmount": 10000.00,
"usedAmount": 4500.00,
"availableAmount": 5500.00,
"expenseTypes": "travel",
"purpose": "北京出差预估",
"projectId": 100,
"projectName": "华东市场拓展",
"budgetSubjectId": 5,
"budgetSubjectName": "差旅费"
}
]
}
}
GET /api/oa/vehicle/check-collision?vehicleId=3&startTime=2026-06-05T09:00:00&endTime=2026-06-05T17:00:00&excludeId=
参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| vehicleId | long | ✅ | 车牌 ID |
| startTime | datetime | ✅ | 出车时间 |
| endTime | datetime | ✅ | 还车时间 |
| excludeId | long | 否 | 编辑已有申请时排除自身 ID |
响应(无冲突):
{ "code": 0, "data": { "hasCollision": false } }
响应(有冲突):
{
"code": 0,
"data": {
"hasCollision": true,
"conflictApplicationNo": "YC-20260604-002",
"conflictTimeRange": "2026-06-05 08:00 ~ 2026-06-05 12:00"
}
}
POST /api/oa/vehicle/200/return
权限:申请人本人。
请求:
{
"actualReturnTime": "2026-06-05T16:30:00",
"startOdometer": 45678.50,
"endOdometer": 45712.30,
"actualCost": 85.00,
"costRemark": "停车费30元,高速通行费55元"
}
校验:
endOdometer ≥ startOdometerstatus=approved 可还车响应:
{
"code": 0,
"data": {
"status": "returned",
"mileage": 33.80,
"actualCost": 85.00
}
}
后端操作:更新 Vehicle 表还车字段 → Status='returned' → 车辆恢复 SysVehicle.Status='idle'。
PUT /api/oa/expense/100/write-off
权限:需要 oa.expense.mark_paid 权限。
请求:
{
"isInvoiceVerified": true,
"isTaxIdMatched": true,
"isCategoryCompliant": true,
"bankTransferNo": "BT20260603001",
"voucherNo": "PZ-2026-0603-0015"
}
校验:
bankTransferNo 和 voucherNo 必填status=approved 且 paymentStatus=unpaid 可核销响应:
{
"code": 0,
"data": {
"paymentStatus": "paid",
"message": "核销完成,已归档"
}
}
错误: | code | 场景 | |------|------| | 422 | 合规检查未全部勾选 / 流水号或凭证号为空 | | 422 | 单据状态不满足核销条件 |
POST /api/oa/expense/100/return-for-revision
权限:需要 oa.expense.mark_paid 权限。
请求:
{
"returnTo": "applicant",
"reason": "发票验真未通过,请核实后重新提交"
}
returnTo 取值:applicant(退回员工重填)/ approver(退回经理重审)。
响应:
{ "code": 0, "message": "已退回修改" }
GET /api/oa/outing-log/list?status=all&page=1&pageSize=20
参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| status | string | 否 | all / draft / completed |
| page | int | 否 | 默认 1 |
| pageSize | int | 否 | 默认 20 |
| scope | string | 否 | own(默认) / dept / all — 根据权限自动限定 |
响应:
{
"code": 0,
"data": {
"items": [
{
"id": 300,
"visitNo": "VST-20260603-001",
"salespersonId": 1001,
"salespersonName": "张三",
"deptName": "销售部",
"customerId": 5001,
"customerName": "北京科技有限公司",
"checkInAddress": "北京市朝阳区建国路88号",
"visitSummary": "与客户CTO进行了产品演示...",
"status": "completed",
"hasNewComment": true,
"createTime": "2026-06-03T14:00:00"
}
],
"total": 15,
"page": 1,
"pageSize": 20
}
}
hasNewComment:Comment.CreateTime > COALESCE(LastViewedTime, '1900-01-01')且评论者≠本人时为 true,前端展示橙色"新点评"标记。
GET /api/oa/outing-log/detail/300
响应:
{
"code": 0,
"data": {
"id": 300,
"visitNo": "VST-20260603-001",
"salespersonId": 1001,
"salespersonName": "张三",
"deptId": 10,
"deptName": "销售部",
"customerId": 5001,
"customerName": "北京科技有限公司",
"contactId": 6001,
"contactName": "王总",
"checkInLongitude": 116.407526,
"checkInLatitude": 39.904030,
"checkInAddress": "北京市朝阳区建国路88号",
"visitSummary": "与客户CTO进行了产品演示,讨论了定制化需求...",
"nextPlan": "下周三前送出详细方案和报价",
"status": "completed",
"attachments": [
{ "id": 30, "fileName": "现场_20260603_143021.jpg", "fileUrl": "https://oss/...", "fileType": "sign_in_photo", "fileSize": 2048000, "sortOrder": 1 }
],
"comments": [
{
"id": 400,
"commenterId": 2001,
"commenterName": "王经理",
"ratingStars": 4,
"commentText": "拜访记录详实,客户需求梳理清晰。建议补充技术方案的可行性分析。",
"createTime": "2026-06-03T16:00:00"
}
],
"createTime": "2026-06-03T14:00:00",
"updateTime": "2026-06-03T14:00:00"
}
}
PUT /api/oa/outing-log/draft
请求体与提交相同(见 §4.4),id 为已有草稿 ID 或 null。不校验 GPS 和照片。
响应:
{ "code": 0, "data": { "id": 301 } }
PUT /api/oa/outing-log/submit
请求:
{
"id": null,
"customerId": 5001,
"customerName": "北京科技有限公司",
"checkInLongitude": 116.407526,
"checkInLatitude": 39.904030,
"checkInAddress": "北京市朝阳区建国路88号",
"visitSummary": "与客户CTO进行了产品演示,讨论了定制化需求...",
"nextPlan": "下周三前送出详细方案和报价",
"attachments": [
{ "fileName": "现场_20260603_143021.jpg", "fileUrl": "https://oss/...", "fileType": "sign_in_photo", "fileSize": 2048000 }
]
}
响应:
{
"code": 0,
"data": {
"id": 300,
"visitNo": "VST-20260603-001",
"status": "completed"
}
}
校验:
PUT /api/oa/outing-log/300/view
无请求体。更新 LastViewedTime = NOW(),使列表页"新点评"红点消失。
POST /api/oa/outing-log/300/comment
请求:
{
"ratingStars": 4,
"commentText": "拜访记录详实,客户需求梳理清晰。建议补充技术方案的可行性分析。"
}
权限:需要 oa.outing_log.comment 权限。
响应:
{
"code": 0,
"data": {
"id": 400,
"commenterId": 2001,
"commenterName": "王经理",
"ratingStars": 4,
"commentText": "拜访记录详实,客户需求梳理清晰。建议补充技术方案的可行性分析。",
"createTime": "2026-06-03T16:00:00"
}
}
DELETE /api/oa/outing-log/{id}
约束:仅 draft 状态可删除。软删除(IsDeleted=1)。级联软删除附件。
响应:
{ "code": 0, "message": "已删除" }
GET /api/oa/approval/pending?userId=2001&bizType=&page=1&pageSize=20
参数: | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | userId | long | ✅ | 审批人 ERP 用户 ID | | bizType | string | 否 | 为空时返回所有类型 | | page | int | 否 | | | pageSize | int | 否 | |
响应:
{
"code": 0,
"data": {
"items": [
{
"bizType": "expense",
"bizId": 100,
"bizNo": "BX-20260603-001",
"amount": 1580.00,
"summary": "差旅费报销",
"submitTime": "2026-06-03T10:00:00",
"applicantId": 1001,
"applicantName": "张三",
"deptName": "销售部",
"approvalLevel": 1
},
{
"bizType": "overtime",
"bizId": 12346,
"bizNo": "JB-20260603-001",
"amount": 3.5,
"summary": "紧急上线支持",
"submitTime": "2026-06-03T18:30:00",
"applicantId": 1002,
"applicantName": "李四",
"deptName": "技术部",
"approvalLevel": 2
}
],
"total": 8,
"page": 1
}
}
GET /api/oa/approval/pending-count?userId=2001
响应:
{ "code": 0, "data": { "count": 8 } }
GET /api/oa/approval/timeline?bizType=expense&bizId=100
响应:
{
"code": 0,
"data": {
"bizType": "expense",
"bizId": 100,
"currentStatus": "pending",
"currentApproverName": "王经理",
"nodes": [
{
"level": 0,
"action": "submit",
"approverName": "张三",
"opinion": null,
"approvalTime": "2026-06-03T10:00:00",
"isValid": true
},
{
"level": 1,
"action": "pending",
"approverName": "王经理",
"opinion": null,
"approvalTime": null,
"isValid": true
}
]
}
}
POST /api/oa/approval/action
请求(同意):
{
"instanceId": "inst_erp_a_009832",
"action": "approve",
"opinion": "同意报销"
}
请求(拒绝):
{
"instanceId": "inst_erp_a_009832",
"action": "reject",
"opinion": "发票金额与申请金额不符,请核实后重新提交"
}
响应:
{
"code": 0,
"message": "审批成功",
"data": { "newStatus": "approved" }
}
校验:
action=reject 时 opinion 必填且 ≥5 个汉字action=transfer 时 transferToUserId 必填POST /api/oa/approval/withdraw
请求:
{
"bizType": "expense",
"bizId": 100,
"instanceId": "inst_erp_a_009832"
}
响应:
{ "code": 0, "message": "已撤回" }
后端操作:.NET 服务端调 ERP 撤回 → OA 更新 Status='withdrawn',旧 ApprovalInstanceId 追加到 PreviousInstanceIds。
GET /api/oa/approval/subordinates?approverId=2001&bizType=expense&status=pending&page=1
响应:结构与列表查询相同,额外返回申请人姓名/部门。
GET /api/oa/announcement/list?type=all&page=1&pageSize=20
参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| type | string | 否 | all / notice / policy / activity。管理员额外支持 draft(我的草稿) |
| page | int | 否 | 默认 1 |
| pageSize | int | 否 | 默认 20 |
响应:
{
"code": 0,
"data": {
"items": [
{
"id": 50,
"title": "关于2026年端午节放假安排的通知",
"type": "activity",
"status": "published",
"publisherId": 2001,
"publisherName": "管理员王五",
"deptName": "行政部",
"publishTime": "2026-06-03T14:00:00",
"isTop": true,
"isRead": false,
"isExpired": false,
"expiryDate": "2026-06-22T00:00:00"
}
],
"total": 12,
"page": 1,
"pageSize": 20
}
}
排序:
isTop优先 → 未过期按publishTime倒序 → 已过期置灰垫底。isRead来自AnnouncementReadLog.IsRead,isExpired=expiryDate < NOW()。
GET /api/oa/announcement/detail/50
响应:
{
"code": 0,
"data": {
"id": 50,
"title": "关于2026年端午节放假安排的通知",
"content": "<h2>放假时间</h2><p>6月19日(周五)至6月21日(周日)...</p>",
"type": "activity",
"status": "published",
"publisherId": 2001,
"publisherName": "管理员王五",
"deptName": "行政部",
"publishTime": "2026-06-03T14:00:00",
"isTop": true,
"privateLevel": 0,
"expiryDate": "2026-06-22T00:00:00",
"isExpired": false,
"attachments": [
{ "id": 40, "fileName": "放假安排.pdf", "fileUrl": "https://oss/...", "fileType": "pdf", "fileSize": 512000 }
],
"createTime": "2026-06-03T13:00:00",
"updateTime": "2026-06-03T14:00:00"
}
}
PUT /api/oa/announcement/draft
请求体与发布相同(见 §6.4),不校验必填项。Status='draft',仅创建者和管理员可见。
响应:
{ "code": 0, "data": { "id": 51 } }
POST /api/oa/announcement/publish
请求(新建发布):
{
"id": null,
"title": "关于2026年端午节放假安排的通知",
"content": "<h2>放假时间</h2><p>6月19日(周五)至6月21日(周日)...</p>",
"type": "activity",
"isTop": false,
"expiryDate": "2026-06-22T00:00:00",
"privateLevel": 0,
"targets": []
}
请求(按部门发布):
{
"id": null,
"title": "销售部Q2业绩考核通知",
"content": "<p>...</p>",
"type": "notice",
"isTop": true,
"privateLevel": 1,
"targets": [
{ "targetType": "dept", "targetId": 10 }
]
}
请求(按指定用户发布):
{
"id": null,
"title": "销售部Q3考核安排",
"content": "<p>...</p>",
"type": "notice",
"isTop": false,
"privateLevel": 2,
"targets": [
{ "targetType": "user", "targetId": 1001 },
{ "targetType": "user", "targetId": 1002 }
]
}
响应:
{
"code": 0,
"data": {
"id": 50,
"status": "published",
"publishTime": "2026-06-03T14:00:00",
"targetCount": 156
}
}
后端操作:发布时异步初始化 AnnouncementReadLog(IsRead=0)。
POST /api/oa/announcement/50/read
无请求体。停留 ≥2s 时调用。无权限要求。
POST /api/oa/announcement/50/ding
权限:需要 oa.announcement.manage 权限。
响应:
{
"code": 0,
"data": { "urgedCount": 5 }
}
后端操作:查 AnnouncementReadLog WHERE IsRead=0 → 更新 IsUrged=1 → 调消息模块推送。
GET /api/oa/announcement/read-stats/50
响应:
{
"code": 0,
"data": {
"readCount": 45,
"unreadCount": 5,
"readUsers": [
{ "userId": 1001, "userName": "张三", "deptName": "销售部", "readTime": "2026-06-03T14:05:00" }
],
"unreadUsers": [
{ "userId": 1005, "userName": "赵六", "deptName": "技术部" }
]
}
}
GET /api/oa/report/{type}?range=month&startDate=2026-06-01&endDate=2026-06-30&userId={erpUserId}&deptId={deptId}
通用参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| range | string | 否 | month / quarter / year。与 startDate+endDate 互斥,优先使用自定义日期 |
| startDate | string | 否 | 自定义开始日期 YYYY-MM-DD |
| endDate | string | 否 | 自定义结束日期 YYYY-MM-DD |
| userId | long | 否 | 个人报表时传入;部门/全公司报表时不传 |
| deptId | long | 否 | 部门报表时传入 |
| scope | string | 否 | own(默认) / dept / all — 根据角色权限自动限定 |
type 取值:expense-apply | expense | overtime | vehicle | outing-log
数据来源:数值卡片和图表均来自 OA 本地业务表,.NET 按角色权限 + 时间范围聚合。审批状态统计通过 ERP 实时查询补充。预置时间段(month/quarter/year)缓存 5 分钟,自定义日期实时查询。
通用响应结构:
{
"code": 0,
"data": {
"cards": { ... },
"chart": { ... },
"memberBreakdown": [ ... ],
"detailList": {
"items": [ ... ],
"total": 0,
"page": 1
}
}
}
memberBreakdown仅经理角色返回(部门横向对比柱状图数据)。detailList仅经理/财务/管理员返回(底部明细列表,分页)。
GET /api/oa/report/expense-apply?range=year&userId=1001
专属参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| status | string | 否 | all / approved / rejected / withdrawn |
响应:
{
"code": 0,
"data": {
"cards": {
"yearTotalAmount": 28500.00,
"monthCount": 3,
"approvedCount": 2,
"approvedAmount": 15000.00
},
"chart": {
"labels": ["2025-07", "2025-08", "...", "2026-06"],
"appliedAmounts": [1200, 3400, 0, 0, 5600, 2300, 4500, 1800, 2900, 0, 3800, 2000],
"approvedAmounts": [1200, 3000, 0, 0, 0, 2300, 4000, 1800, 2900, 0, 3800, 2000]
},
"memberBreakdown": [
{ "userId": 1001, "userName": "张三", "appliedAmount": 15000.00, "approvedAmount": 12000.00 },
{ "userId": 1002, "userName": "李四", "appliedAmount": 8000.00, "approvedAmount": 8000.00 }
],
"detailList": {
"items": [
{
"id": 12345,
"applicationNo": "BXSQ-20260603-001",
"applicantName": "张三",
"deptName": "销售部",
"estimatedAmount": 1900.00,
"expenseTypes": "travel,office",
"status": "pending",
"createTime": "2026-06-03T09:00:00"
}
],
"total": 42,
"page": 1
}
}
}
GET /api/oa/report/expense?range=year&userId=1001
专属参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| status | string | 否 | all / approved / rejected |
| paymentStatus | string | 否 | all / unpaid / paid |
响应:
{
"code": 0,
"data": {
"cards": {
"yearTotalPaid": 28500.00,
"monthCount": 3,
"pendingCount": 1,
"approvedUnpaidCount": 2
},
"chart": {
"labels": ["2025-07", "2025-08", "...", "2026-06"],
"appliedAmounts": [1200, 3400, 0, 0, 5600, 2300, 4500, 1800, 2900, 0, 3800, 2000],
"approvedAmounts": [1200, 3000, 0, 0, 5600, 2300, 4000, 1800, 2900, 0, 3800, 2000]
},
"memberBreakdown": [
{ "userId": 1001, "userName": "张三", "appliedAmount": 12000.00, "approvedAmount": 12000.00 }
],
"detailList": {
"items": [ { "id": 100, "reportNo": "BX-20260603-001", "...": "..." } ],
"total": 42,
"page": 1
}
}
}
GET /api/oa/report/overtime?range=year&deptId=10
专属参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| otType | string | 否 | all / workday / weekend / holiday |
响应:
{
"code": 0,
"data": {
"cards": {
"monthTotalHours": 42.5,
"monthCount": 8,
"compLeaveHours": 24.0,
"overtimePayCount": 3
},
"chart": {
"labels": ["2025-07", "...", "2026-06"],
"workdayHours": [10, 8, 12, 5, 0, 8, 15, 6, 0, 10, 4, 8],
"weekendHours": [5, 0, 8, 0, 0, 0, 0, 4, 0, 0, 8, 0],
"holidayHours": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
},
"memberBreakdown": [
{ "userId": 1001, "userName": "张三", "totalHours": 18.5 },
{ "userId": 1002, "userName": "李四", "totalHours": 24.0 }
],
"detailList": {
"items": [ { "id": 12346, "applicationNo": "JB-20260603-001", "...": "..." } ],
"total": 15,
"page": 1
}
}
}
GET /api/oa/report/vehicle?range=year&deptId=10
专属参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| vehicleId | long | 否 | 按车牌筛选 |
| purpose | string | 否 | all / reception / business / official |
响应:
{
"code": 0,
"data": {
"cards": {
"monthCount": 5,
"totalMileage": 1250.80,
"tollParkingCost": 385.00,
"unreturnedCount": 1
},
"chart": {
"labels": ["2025-07", "...", "2026-06"],
"tripCounts": [3, 2, 5, 1, 4, 2, 6, 3, 0, 2, 5, 4],
"costs": [120, 80, 250, 50, 180, 90, 300, 150, 0, 100, 220, 180]
},
"memberBreakdown": [
{ "userId": 1001, "userName": "张三", "tripCount": 8, "totalCost": 450.00 }
],
"detailList": {
"items": [ { "id": 200, "applicationNo": "YC-20260603-001", "...": "..." } ],
"total": 25,
"page": 1
}
}
}
GET /api/oa/report/outing-log?range=year&deptId=10
专属参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| customerName | string | 否 | 客户名模糊搜索 |
响应:
{
"code": 0,
"data": {
"cards": {
"monthVisitCount": 12,
"uniqueCustomerCount": 8,
"avgRatingStars": 4.2,
"uncommentedCount": 3
},
"chart": {
"labels": ["2025-07", "...", "2026-06"],
"visitCounts": [5, 8, 12, 6, 10, 7, 15, 9, 4, 11, 8, 12],
"avgRatings": [4.0, 4.5, 3.8, 4.2, 4.0, 4.3, 3.9, 4.5, 4.0, 4.1, 4.2, 4.2]
},
"memberBreakdown": [
{ "userId": 1001, "userName": "张三", "visitCount": 25, "avgRating": 4.0 }
],
"detailList": {
"items": [ { "id": 300, "visitNo": "VST-20260603-001", "...": "..." } ],
"total": 80,
"page": 1
}
}
}
POST /api/oa/report/{type}/export
权限:需要 oa.report.export 权限(财务/管理员角色)。
请求:
{
"range": "quarter",
"startDate": "2026-04-01",
"endDate": "2026-06-30",
"deptId": 10,
"status": "all",
"paymentStatus": "all"
}
参数与对应报表查询接口一致。
range与startDate+endDate互斥。
响应:Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,二进制文件流。Flutter 收到后通过 MethodChannel 唤起系统分享面板。
GET /api/oa/home/summary
说明:工作台首页的快捷看板数据。根据当前用户 OA 权限自动判定角色,返回对应数据范围(本人/本部门/全公司)。
权限:登录即可访问,数据范围按角色自动限定。
响应(员工版):
{
"code": 0,
"data": {
"role": "employee",
"banners": [
{ "id": 1, "imageUrl": "https://oss/...", "title": "Q2优秀员工表彰", "linkUrl": "tboss://oa/announcement/detail/50", "sortOrder": 1 }
],
"cards": {
"monthTotalPaid": 28500.00,
"monthSubmitCount": 5
}
}
}
响应(经理版):
{
"code": 0,
"data": {
"role": "manager",
"banners": [ "..." ],
"pendingApprovalCount": 8,
"cards": {
"monthTotalPaid": 128500.00,
"monthSubmitCount": 42,
"deptPendingCount": 8
}
}
}
响应(财务/管理员版):
{
"code": 0,
"data": {
"role": "finance",
"banners": [ "..." ],
"cards": {
"monthTotalPaid": 525000.00,
"unpaidTotal": 185000.00,
"weekRejectedCount": 3
}
}
}
数据缓存 5 分钟。管理员版额外在金刚区展示"发布公告"入口(由前端根据 role 或权限判断)。员工/经理/财务/管理员角色判定优先级见 PRD §3.2。
GET /api/oa/budget/balance?projectId=100&subjectId=5
响应:
{ "code": 0, "data": { "hasBudget": true, "availableAmount": 150000.00, "frozenAmount": 20000.00 } }
ERP 无预算时返回 { "hasBudget": false },OA 前端隐藏预算区块。
GET /api/oa/projects # 项目列表
GET /api/oa/subjects?projectId= # 某项目下的预算科目
GET /api/oa/cost-centers # 成本中心列表
GET /api/oa/exchange-rate?currencyCode=USD
响应:
{ "code": 0, "data": { "currencyCode": "USD", "rate": 7.2456, "date": "2026-06-03" } }
GET /api/oa/cost-categories?bizScope=both&expenseType=travel
参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| bizScope | string | 否 | expense_apply / expense / both(默认 both) |
| expenseType | string | 否 | 按费用大类过滤(travel/entertainment/office 等),不传返回全部 |
说明:事前申请和费用报销表单中"费用类别"下拉的数据源。返回启用的叶子节点(ParentId IS NOT NULL),ExpenseType 用于联动过滤(如事前申请选了"差旅费"大类,则仅返回绑定了 travel 的费用小类)。
响应:
{
"code": 0,
"data": {
"categories": [
{
"id": 10,
"categoryName": "交通费",
"categoryCode": "transport",
"parentId": 1,
"expenseType": "travel",
"sortOrder": 1
},
{
"id": 11,
"categoryName": "住宿费",
"categoryCode": "hotel",
"parentId": 1,
"expenseType": "travel",
"sortOrder": 2
},
{
"id": 20,
"categoryName": "办公用品",
"categoryCode": "office_supplies",
"parentId": 5,
"expenseType": "office",
"sortOrder": 1
}
]
}
}
GET /api/oa/vehicles # 列表
POST /api/oa/vehicles # 添加
PUT /api/oa/vehicles/3 # 修改
GET 响应:
{
"code": 0,
"data": {
"vehicles": [
{
"id": 1,
"licensePlate": "粤B12345",
"vehicleType": "sedan",
"brand": "丰田凯美瑞",
"seats": 5,
"driverName": "陈司机",
"status": "idle",
"isActive": true
}
]
}
}
GET /api/oa/banners
响应:
{
"code": 0,
"data": {
"banners": [
{
"id": 1,
"imageUrl": "https://oss/...",
"title": "2026年Q2优秀员工表彰",
"linkUrl": "tboss://oa/announcement/detail/50",
"sortOrder": 1
}
]
}
}
POST /api/oa/upload
Content-Type: multipart/form-data
说明:统一文件上传端点。文件上传至云端对象存储(OSS),返回绝对 URL。前端拿到 URL 后在提交表单时写入 attachments 数组。
参数:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| file | file | ✅ | 文件本体 |
| bizType | string | ✅ | expense_apply / expense / outing_log / announcement |
校验:
| bizType | 数量限制 | 类型限制 | 大小限制 |
|---|---|---|---|
| expense_apply | ≤9 | 图片/PDF | 图片≤10MB, PDF≤20MB |
| expense | ≤9 | 图片 | ≤10MB |
| outing_log | 1~9 | 仅图片 | ≤10MB |
| announcement | ≤5 | PDF/图片/Word/Excel | ≤20MB |
数量限制由前端在上传入口处控制(满额隐藏上传按钮),后端做二次校验。
响应:
{
"code": 0,
"data": {
"fileUrl": "https://oss.example.com/oa/2026/06/abc123.jpg",
"fileName": "现场_20260603_143021.jpg",
"fileType": "image",
"fileSize": 2048000
}
}
错误: | code | 场景 | |------|------| | 422 | 文件大小超限 / 类型不支持 / 数量超限 |
以下 API 为 .NET 服务端已有,OA 模块直接调用。此处仅列接口签名,详细文档见原系统。
| 方法 | URL | 说明 | 关键参数 |
|---|---|---|---|
| GET | /api/user/{id} |
用户信息 | id: ERP 用户 ID |
| GET | /api/user/search?q=&page= |
用户搜索 | q: 姓名/工号 |
| GET | /api/dept/tree |
部门树 | — |
| PUT | /api/user/avatar |
上传头像 | multipart/form-data |
| GET | /api/customer/search?q= |
客户联想 | q: 客户名称 |
| GET | /api/messages?page= |
消息列表 | page |
| GET | /api/messages/unread-count |
未读消息数 | — |
| PUT | /api/messages/:id/read |
标记已读 | id |
| POST | /api/messages/read-all |
全部已读 | — |
| DELETE | /api/messages/:id |
删除消息 | id |
| GET | /api/dict/banks |
银行列表 | — |
| GET | /api/dict/cost-categories |
费用类别 | — |
文档版本:v1.0 | 日期:2026-06-04