# 出国申请管理 API 接口文档 ## 📋 文档说明 ### 适用场景 - 出国申请业务流程管理 - 出国申请审批流程控制 - 出国记录管理和跟踪 - 为 Cursor AI 提供完整的接口开发上下文 ### 模块特色 - **完整审批流程**:三级审批(部门主管→分管领导→校领导) - **状态自动流转**:申请提交→审批通过→自动创建记录 - **权限细分控制**:申请、审批、记录管理分离 - **数据完整追踪**:审批记录、状态变更历史完整保存 --- ## 🔌 出国申请管理 API 接口文档 ### 模块概述 **模块名称**:overseas / 出国申请管理 **基础路径**:`/api/rs/overseas` **版本**:v1.1 **负责人**:weizzzz **更新时间**:2025-10-28 ### 🆕 最近更新 **v1.1 (2025-10-28) - 分级审批权限升级** - ✅ **审批权限升级为五级审批制度** - 新增 `rs:overseas:approve1-5` 五个分级审批权限 - 保留 `rs:overseas:approve` 作为通用审批权限 - ✅ **Controller层采用OR模式权限验证** - 用户拥有任一审批权限即可访问审批接口 - 解决部门负责人403权限错误问题 - ✅ **Service层增加审批级别验证** - 新增 `validateApproverLevel()` 方法 - 精确验证用户权限是否匹配申请的审批级别 - 防止越级审批 - ✅ **前端按钮权限优化** - 审批按钮支持多级权限显示 - 改善用户体验,按钮显示逻辑更合理 ### 模块功能 出国申请管理模块提供完整的出国申请业务流程管理功能,包括申请提交、多级审批、记录管理等核心功能。支持学术交流、培训学习、合作项目等多种出国目的,提供完整的审批流程控制和状态追踪。 ### 权限说明 #### 出国申请权限 - **权限前缀**:`rs:overseas:application:` - **权限列表**: - `rs:overseas:application:list` - 查询申请列表权限 - `rs:overseas:application:query` - 查询申请详情权限 - `rs:overseas:application:add` - 新增申请权限 - `rs:overseas:application:edit` - 编辑申请权限 - `rs:overseas:application:remove` - 删除申请权限 - `rs:overseas:application:export` - 导出申请权限 - `rs:overseas:application:submit` - 提交申请权限 - `rs:overseas:application:cancel` - 取消申请权限 - `rs:overseas:application:approve` - 审批申请权限 #### 出国记录权限 - **权限前缀**:`rs:overseas:record:` - **权限列表**: - `rs:overseas:record:list` - 查询记录列表权限 - `rs:overseas:record:query` - 查询记录详情权限 - `rs:overseas:record:add` - 新增记录权限 - `rs:overseas:record:edit` - 编辑记录权限 - `rs:overseas:record:remove` - 删除记录权限 - `rs:overseas:record:export` - 导出记录权限 - `rs:overseas:record:create` - 创建记录权限 - `rs:overseas:record:complete` - 完成记录权限 #### 出国审批权限(🆕 2025-10-28 更新为五级审批) - **权限前缀**:`rs:overseas:` - **审批记录查看权限**: - `rs:overseas:approval:list` - 查询审批记录列表权限 - `rs:overseas:approval:query` - 查询审批记录详情权限 - `rs:overseas:approval:export` - 导出审批记录权限 - **分级审批权限**(五级审批制度): - `rs:overseas:approve` - 通用审批权限(超级管理员/高级审批者,可审批任意级别) - `rs:overseas:approve1` - 第1级:部门负责人审批权限 - `rs:overseas:approve2` - 第2级:人事处审批权限 - `rs:overseas:approve3` - 第3级:纪委审批权限 - `rs:overseas:approve4` - 第4级:学院分管领导审批权限 - `rs:overseas:approve5` - 第5级:学院党委书记审批权限 - **数据返回顺序**:审批人下拉接口(`/system/user/list-by-permission`)默认按用户名(`user_name`)升序排序,确保审批人选择器顺序一致(2025-11-13) - **权限使用说明**: - 审批接口采用 **OR模式**,用户拥有以上任一权限即可调用接口 - 具体审批权限在业务层验证,必须匹配申请的 `approval_level` - 部门负责人通过 `rs:overseas:approve1` 权限识别,自动应用部门数据过滤 ### 🔐 数据权限控制 #### 权限分级说明 出国申请系统采用**基于角色和部门的多级数据权限控制**,确保用户只能访问其权限范围内的数据。 #### 角色权限矩阵 | 角色 | 角色标识 | 数据访问范围 | 可见申请范围 | 备注 | |------|----------|------------|-------------|------| | **超级管理员** | `admin` | 全局数据 | 所有申请 | 不受任何限制 | | **人事管理员** | `rs-admin` | 全局数据 | 所有申请 | 仅当不是部门负责人时 | | **申请人** | `rs-Applicant` | 个人数据 | 仅自己的申请 | 只能查看和管理自己的申请 | | **部门负责人** | 具有`rs:overseas:approve1`权限 | 本部门数据 | 本部门所有申请 | **优先级最高** | | **普通审批人** | 其他审批权限 | 分配数据 | 分配给自己的审批 | 只能看到分配的审批任务 | #### 部门权限过滤规则(🆕 2025-01-28) **核心原则**:部门负责人**只能看到本部门成员提交的申请**,不能看到其他部门的申请。 **实现逻辑**: 1. **判断用户是否为部门负责人**(三种方式任选其一): - 具有 `rs:overseas:approve1` 权限 - 具有 `dept_head_role` 角色 - 具有 `overseas:application:dept_head:approve` 权限(兼容旧版) 2. **应用部门过滤**: - 查询条件:`申请人的dept_id = 当前用户的dept_id` - SQL实现:通过 `sys_user` 表关联,筛选申请人所属部门 - **重要**:不限制审批人(approverId),只限制申请人部门(deptId) 3. **权限优先级**(从高到低): ``` 超级管理员 > 申请人(自己的数据)> 部门负责人(本部门)> 人事管理员(全局)> 普通审批人(分配数据) ``` **示例场景**: 假设有以下用户和部门: - **张三**:`dept_id = 001`(测试部门A),角色:申请人 - **李四**:`dept_id = 001`(测试部门A),角色:部门负责人 - **王五**:`dept_id = 002`(测试部门B),角色:部门负责人 - **赵六**:`dept_id = 003`(测试部门C),角色:部门负责人 **张三提交申请后:** - ✅ **李四**(同部门):可以看到张三的申请 - ❌ **王五**(不同部门):不能看到张三的申请 - ❌ **赵六**(不同部门):不能看到张三的申请 **技术实现**: ```java // Service层权限过滤逻辑 private void applyDeptPermissionFilter(RsOverseasApprovalQuery query) { // 1. 判断是否为部门负责人 boolean isDeptApprover = isDeptApprover(currentUserId); // 2. 如果是部门负责人,只设置部门过滤 if (isDeptApprover) { query.setDeptId(currentDeptId); // 只应用部门过滤 return; // 不设置approverId,避免过度限制 } } ``` ```xml AND EXISTS ( SELECT 1 FROM sys_user u WHERE u.user_id = app.applicant_id AND u.dept_id = #{deptId} ) ``` **相关文档**: - 测试指南:`docs/测试文档/出国申请部门权限过滤测试指南.md` - 验证脚本:`部门权限过滤验证脚本.sql` - 代码实现:`RsOverseasApprovalServiceImpl.java` --- ## 📊 接口概览 ### 接口分类 #### 出国申请基础 CRUD 接口 | 接口名称 | HTTP 方法 | 路径 | 权限要求 | 功能描述 | | ---------------- | --------- | --------------------------------------- | ------------------------------------ | -------------------------- | | 查询申请列表 | GET | `/rs/overseas/application/list` | `rs:overseas:application:list` | 分页查询出国申请列表 | | 查询申请详情 | GET | `/rs/overseas/application/{id}` | `rs:overseas:application:query` | 根据ID查询出国申请详情 | | 新增申请 | POST | `/rs/overseas/application` | `rs:overseas:application:add` | 创建新的出国申请 | | 修改申请 | PUT | `/rs/overseas/application` | `rs:overseas:application:edit` | 更新出国申请信息 | | 删除申请 | DELETE | `/rs/overseas/application/{ids}` | `rs:overseas:application:remove` | 批量删除出国申请 | | 导出申请 | POST | `/rs/overseas/application/export` | `rs:overseas:application:export` | 导出出国申请数据 | #### 出国申请业务逻辑接口 | 接口名称 | HTTP 方法 | 路径 | 权限要求 | 功能描述 | | ------------------ | --------- | ----------------------------------------- | ------------------------------------ | -------------------------- | | 查询我的申请列表 | GET | `/rs/overseas/application/my/list` | `rs:overseas:application:list` | 查询当前用户的申请列表(排除已通过的申请) | | 查询待审批申请 | GET | `/rs/overseas/application/pending/list` | `rs:overseas:application:approve` | 查询待当前用户审批的申请 | | 按状态查询申请 | GET | `/rs/overseas/application/status/{status}` | `rs:overseas:application:list` | 根据状态查询申请列表 | | 提交申请 | PUT | `/rs/overseas/application/submit/{id}` | `rs:overseas:application:submit` | 提交出国申请进入审批流程 | | 重新提交申请 | PUT | `/rs/overseas/application/resubmit/{id}` | `rs:overseas:application:edit` | 重新提交已拒绝的出国申请 | | 取消申请 | PUT | `/rs/overseas/application/cancel/{id}` | `rs:overseas:application:cancel` | 取消出国申请 | | 审批申请 | POST | `/rs/overseas/application/approve` | `rs:overseas:application:approve` | 审批出国申请(通过/拒绝) | | 修复记录 | POST | `/rs/overseas/application/fix/records` | `rs:overseas:application:approve` | 修复已审批通过但未创建记录的申请 | | **下载党籍保留PDF模板** | GET | `/rs/overseas/record/downloadPartyRetention` | `rs:overseas:query` | **新增**:下载党籍保留申请PDF空白模板(线下手工填写)(2025-10-17) | #### 出国记录管理接口 | 接口名称 | HTTP 方法 | 路径 | 权限要求 | 功能描述 | | ------------------ | --------- | --------------------------------------- | -------------------------------- | ---------------------------- | | 查询记录列表 | GET | `/rs/overseas/record/list` | `rs:overseas:record:list` | 分页查询出国记录列表 | | 查询我的记录列表 | GET | `/rs/overseas/record/my/list` | `rs:overseas:record:list` | 查询当前用户的记录列表 | | 根据申请查询记录 | GET | `/rs/overseas/record/application/{id}` | `rs:overseas:record:query` | 根据申请ID查询出国记录 | | 查询记录详情 | GET | `/rs/overseas/record/{id}` | `rs:overseas:record:query` | 根据ID查询出国记录详情 | | 新增记录 | POST | `/rs/overseas/record` | `rs:overseas:record:add` | 创建新的出国记录 | | 修改记录 | PUT | `/rs/overseas/record` | `rs:overseas:record:edit` | 更新出国记录信息 | | 删除记录 | DELETE | `/rs/overseas/record/{ids}` | `rs:overseas:record:remove` | 批量删除出国记录 | | 导出记录 | POST | `/rs/overseas/record/export` | `rs:overseas:record:export` | 导出出国记录数据 | | 创建记录 | POST | `/rs/overseas/record/create/{id}` | `rs:overseas:record:create` | 基于申请创建出国记录 | | 更新完成状态 | PUT | `/rs/overseas/record/complete` | `rs:overseas:record:complete` | 更新出国记录完成状态 | #### 出国审批记录接口 | 接口名称 | HTTP 方法 | 路径 | 权限要求 | 功能描述 | | -------------------- | --------- | --------------------------------------------------- | -------------------------------- | ---------------------------- | | 查询审批记录列表 | GET | `/rs/overseas/approval/list` | `rs:overseas:approval:list` | 分页查询审批记录列表 | | 查询我的审批记录 | GET | `/rs/overseas/approval/my/list` | `rs:overseas:approval:list` | 查询当前用户的审批记录 | | 根据申请查询审批记录 | GET | `/rs/overseas/approval/application/{id}` | `rs:overseas:approval:query` | 根据申请ID查询审批记录 | | 查询审批记录详情 | GET | `/rs/overseas/approval/{id}` | `rs:overseas:approval:query` | 根据ID查询审批记录详情 | | 查询申请审批历史 | GET | `/rs/overseas/application/approval/history/{id}` | `rs:overseas:application:query` | 查询指定申请的完整审批历史 | | 导出审批记录 | POST | `/rs/overseas/approval/export` | `rs:overseas:approval:export` | 导出审批记录数据 | --- ## 📄 数据结构定义 ### 出国申请对象 (OverseasApplication) ```typescript interface OverseasApplicationVO { applicationId: number | string; // 申请ID applicantId: number; // 申请人ID applicantName: string; // 申请人姓名 department: string; // 所属部门 position: string; // 职位 contactPhone: string; // 联系电话 contactEmail: string; // 联系邮箱 purpose: string; // 出国目的编码 purposeDetail: string; // 目的详细说明 destination: string; // 目的地国家 destinationCity: string; // 目的地城市 startDate: string; // 开始日期 endDate: string; // 结束日期 duration: number; // 出国天数 budget: number; // 预算金额 budgetSource: string; // 经费来源编码 invitation: string; // 邀请函信息 itinerary: string; // 行程安排 accommodationPlan: string; // 住宿安排 transportationPlan: string; // 交通安排 status: string; // 申请状态编码 approvalStatus: string; // 工作流状态编码 currentApprover: string; // 当前审批人 approvalLevel: string; // 审批级别 applyTime: string; // 申请时间 approvalTime: string; // 审批完成时间 adminRemark: string; // 管理员备注 applicantRemark: string; // 申请人备注 createTime: string; // 创建时间 updateTime: string; // 更新时间 // 显示字段 statusText: string; // 状态文本 approvalStatusText: string; // 工作流状态文本 purposeText: string; // 目的文本 budgetSourceText: string; // 经费来源文本 approvalLevelText: string; // 审批级别文本 } ``` ### 出国记录对象 (OverseasRecord) ```typescript interface OverseasRecordVO { recordId: number | string; // 记录ID applicationId: number; // 关联申请ID applicantId: number; // 申请人ID applicantName: string; // 申请人姓名 department: string; // 所属部门 purpose: string; // 出国目的 destination: string; // 目的地国家 actualStartDate: string; // 实际开始日期 actualEndDate: string; // 实际结束日期 actualDuration: number; // 实际出国天数 actualBudget: number; // 实际花费金额 completionStatus: string; // 完成状态编码 achievement: string; // 出国成果 reportContent: string; // 总结报告内容 certificatePath: string; // 证书文件路径 createTime: string; // 创建时间 updateTime: string; // 更新时间 // 显示字段 purposeText: string; // 目的文本 completionStatusText: string; // 完成状态文本 } ``` ### 出国审批记录对象 (OverseasApproval) ```typescript interface OverseasApprovalVO { approvalId: number | string; // 审批ID applicationId: number; // 关联申请ID approverId: number; // 审批人ID approverName: string; // 审批人姓名(优先返回昵称,缺省时回退账号) approvalLevel: string; // 审批级别编码 approvalStatus: string; // 审批状态编码 approvalOpinion: string; // 审批意见 approvalTime: string; // 审批时间 createTime: string; // 创建时间 updateTime: string; // 更新时间 // 显示字段 approvalLevelText: string; // 审批级别文本 approvalStatusText: string; // 审批状态文本 } ``` > 🆕 **2025-11-28 更新**:`approverName` 字段统一返回审批人的昵称,若用户未设置昵称则自动回退到账号,确保审批记录界面展示真实姓名。 --- ## 🔧 核心接口详细说明 ### 1. 查询出国申请列表 **接口地址**:`GET /rs/overseas/application/list` **权限要求**:`rs:overseas:application:list` #### 请求参数 ```typescript interface OverseasApplicationQuery { pageNum: number; // 页码(必须) pageSize: number; // 每页大小(必须) applicantName?: string; // 申请人姓名 department?: string; // 所属部门 purpose?: string; // 出国目的 destination?: string; // 目的地 status?: string; // 申请状态 approvalLevel?: string; // 审批级别 currentApprover?: string; // 当前审批人 applicantId?: number; // 申请人ID budgetMin?: number; // 预算最小值 budgetMax?: number; // 预算最大值 applyTimeBegin?: string; // 申请开始时间 applyTimeEnd?: string; // 申请结束时间 startDateBegin?: string; // 出国开始日期范围 startDateEnd?: string; // 出国结束日期范围 } ``` #### 响应示例 ```json { "code": 200, "msg": "查询成功", "rows": [ { "applicationId": 1, "applicantId": 1, "applicantName": "张三", "department": "计算机学院", "position": "副教授", "contactPhone": "13800138001", "contactEmail": "zhangsan@example.com", "purpose": "academic_exchange", "purposeDetail": "参加国际人工智能学术交流会议,发表研究论文并进行学术交流", "destination": "美国", "destinationCity": "纽约", "startDate": "2025-03-15", "endDate": "2025-03-25", "duration": 11, "budget": 25000.00, "budgetSource": "school", "invitation": "已收到会议邀请函", "itinerary": "3月15日飞往纽约,参加为期5天的会议,会后进行实验室访问", "status": "1", "currentApprover": "admin", "approvalLevel": "2", "applyTime": "2025-01-28 10:00:00", "approvalTime": "2025-01-28 15:30:00", "applicantRemark": "期待能够带回先进的研究成果", "createTime": "2025-01-28 10:00:00", "updateTime": "2025-01-28 15:30:00", "statusText": "已通过", "purposeText": "学术交流", "budgetSourceText": "学校经费", "approvalLevelText": "分管领导" } ], "total": 1 } ``` ### 2. 新增出国申请 **接口地址**:`POST /rs/overseas/application` **权限要求**:`rs:overseas:application:add` #### 请求参数 ```typescript interface OverseasApplicationForm { department: string; // 所属部门(必须) position?: string; // 职位 contactPhone: string; // 联系电话(必须) contactEmail: string; // 联系邮箱(必须) purpose: string; // 出国目的(必须) purposeDetail: string; // 目的详细说明(必须) destination: string; // 目的地国家(必须) destinationCity?: string; // 目的地城市 startDate: string; // 开始日期(必须) endDate: string; // 结束日期(必须) budget: number; // 预算金额(必须) budgetSource: string; // 经费来源(必须) invitation?: string; // 邀请函信息 itinerary: string; // 行程安排(必须) accommodationPlan?: string; // 住宿安排 transportationPlan?: string; // 交通安排 applicantRemark?: string; // 申请人备注 } ``` #### 请求示例 ```json { "department": "计算机学院", "position": "副教授", "contactPhone": "13800138001", "contactEmail": "zhangsan@example.com", "purpose": "academic_exchange", "purposeDetail": "参加国际人工智能学术交流会议,发表研究论文并进行学术交流", "destination": "美国", "destinationCity": "纽约", "startDate": "2025-03-15", "endDate": "2025-03-25", "budget": 25000.00, "budgetSource": "school", "invitation": "已收到会议邀请函", "itinerary": "3月15日飞往纽约,参加为期5天的会议,会后进行实验室访问", "accommodationPlan": "入住会议指定酒店", "transportationPlan": "往返机票+当地交通", "applicantRemark": "期待能够带回先进的研究成果" } ``` #### 响应示例 ```json { "code": 200, "msg": "新增成功", "data": null } ``` ### 3. 重新提交被拒绝的出国申请 **接口地址**: `PUT /rs/overseas/application/resubmit/{id}` **权限要求**: `rs:overseas:application:edit` **功能说明**: 允许用户重新提交已被拒绝的出国申请,支持修改申请内容后重新进入审批流程。 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | | ------ | ---- | ---- | ---- | | id | Long | 是 | 申请ID | **请求体**: ```json { "destination": "美国", "purpose": "1", "purposeDetail": "参加学术会议", "destinationCity": "纽约", "startDate": "2025-02-01", "endDate": "2025-02-10", "budget": 15000, "budgetSource": "科研经费", "itinerary": "详细行程安排", "applicantRemark": "申请备注" } ``` **响应数据**: ```json { "code": 200, "msg": "操作成功", "data": null } ``` **业务逻辑**: - 只有状态为"已拒绝"(status=2)的申请才能重新提交 - 重新提交后状态重置为"待审核"(status=0) - 自动重置审批级别和当前审批人信息 - 自动重新计算出国天数 - 更新申请时间为当前时间 ### 4. 审批出国申请 **接口地址**:`POST /rs/overseas/application/approve` **权限要求**:`rs:overseas:approve` (通用) 或 `rs:overseas:approve1-5` (分级) - **支持多级审批权限(2025-10-28更新)** #### 🔴 权限说明(重要更新) **分级审批权限体系**(OR模式,满足任一权限即可访问接口): - `rs:overseas:approve` - 通用审批权限(超级管理员/高级审批者) - `rs:overseas:approve1` - 第1级:部门负责人审批权限 - `rs:overseas:approve2` - 第2级:人事处审批权限 - `rs:overseas:approve3` - 第3级:纪委审批权限 - `rs:overseas:approve4` - 第4级:学院分管领导审批权限 - `rs:overseas:approve5` - 第5级:学院党委书记审批权限 **权限验证机制**: 1. **接口层验证**:Controller使用 `@SaCheckPermission(value={...}, mode=SaMode.OR)`,满足任一权限即可调用接口 2. **业务层验证**:Service层根据申请的 `approval_level` 字段验证用户是否拥有对应级别的审批权限 3. **权限不匹配处理**:如果用户审批级别与申请要求不符,返回业务异常"您没有第X级审批权限" **示例场景**: - 部门负责人(拥有 `approve1`)只能审批 `approval_level=1` 的申请 - 尝试审批 `approval_level=2` 的申请将被拒绝 - 拥有 `rs:overseas:approve` 通用权限的用户可以审批任意级别 #### 请求参数 ```typescript interface ApprovalRequest { applicationId: number | string; // 申请ID(必须) approvalStatus: string; // 审批状态:1-通过,2-拒绝(必须) approvalOpinion?: string; // 审批意见 taskId?: string; // 🔴 工作流任务ID(可选,支持标准工作流模式) } ``` #### 请求示例 ```json { "applicationId": 1, "approvalStatus": "1", "approvalOpinion": "同意申请,项目具有重要学术价值,注意安全" } ``` #### 响应示例 ```json { "code": 200, "msg": "审批成功", "data": null } ``` ### 4. 修复已审批通过但未创建记录的申请 **接口地址**:`POST /rs/overseas/application/fix/records` **权限要求**:`rs:overseas:application:approve` #### 功能说明 修复已审批通过但由于系统异常未自动创建出国记录的申请。该接口会: 1. 查询所有状态为"已通过"的申请 2. 检查是否存在对应的出国记录 3. 为缺失记录的申请自动创建出国记录 #### 请求参数 无需参数,直接POST请求即可。 #### 请求示例 ```bash POST /rs/overseas/application/fix/records Authorization: Bearer {token} Content-Type: application/json clientid: ruoyi-web referer: http://localhost ``` #### 响应示例 ```json { "code": 200, "msg": "修复完成,共修复 3 条记录", "data": "修复完成,共修复 3 条记录" } ``` #### 使用场景 - 系统升级后的数据修复 - 审批流程异常导致的记录缺失修复 - 管理员手动数据修复 ### 5. 查询申请审批历史 **接口地址**:`GET /rs/overseas/application/approval/history/{applicationId}` **权限要求**:`rs:overseas:application:query` #### 功能说明 查询指定出国申请的完整审批历史记录,按审批级别升序、审批时间降序排列。主要用于审批历史追溯和流程透明化。 #### 路径参数 | 参数名 | 类型 | 必须 | 说明 | | ------------- | ------ | ---- | -------- | | applicationId | number | 是 | 申请ID | #### 请求示例 ```bash GET /rs/overseas/application/approval/history/1 Authorization: Bearer {token} Content-Type: application/json clientid: ruoyi-web referer: http://localhost ``` #### 响应示例 ```json { "code": 200, "msg": "查询成功", "data": [ { "approvalId": 1, "applicationId": 1, "approverId": 2, "approverName": "李部长", "approvalLevel": "1", "approvalStatus": "1", "approvalOpinion": "同意申请,项目具有重要学术价值", "approvalTime": "2025-01-28 14:30:00", "createTime": "2025-01-28 14:30:00", "updateTime": "2025-01-28 14:30:00", "approvalLevelText": "部门主管", "approvalStatusText": "通过" }, { "approvalId": 2, "applicationId": 1, "approverId": 3, "approverName": "王副校长", "approvalLevel": "2", "approvalStatus": "1", "approvalOpinion": "项目符合学校发展战略,建议通过", "approvalTime": "2025-01-28 16:15:00", "createTime": "2025-01-28 16:15:00", "updateTime": "2025-01-28 16:15:00", "approvalLevelText": "分管领导", "approvalStatusText": "通过" }, { "approvalId": 3, "applicationId": 1, "approverId": 4, "approverName": "张校长", "approvalLevel": "3", "approvalStatus": "1", "approvalOpinion": "同意出国,注意安全,及时汇报进展", "approvalTime": "2025-01-28 17:45:00", "createTime": "2025-01-28 17:45:00", "updateTime": "2025-01-28 17:45:00", "approvalLevelText": "校领导", "approvalStatusText": "通过" } ] } ``` #### 响应字段说明 | 字段名 | 类型 | 说明 | | ------------------- | ------ | --------------------------------------- | | approvalId | number | 审批记录ID | | applicationId | number | 关联的申请ID | | approverId | number | 审批人用户ID | | approverName | string | 审批人姓名 | | approvalLevel | string | 审批级别编码(1-部门主管,2-分管领导,3-校领导) | | approvalStatus | string | 审批状态编码(0-待审核,1-通过,2-拒绝) | | approvalOpinion | string | 审批意见 | | approvalTime | string | 审批时间 | | approvalLevelText | string | 审批级别中文描述 | | approvalStatusText | string | 审批状态中文描述 | #### 特殊说明 - 返回数据按 `approvalLevel` 升序、`approvalTime` 降序排列 - 如果申请还未产生任何审批记录,返回空数组 - 只返回与指定申请ID相关的审批历史 - 审批历史记录一旦创建不可删除,确保流程可追溯 #### 错误响应 ```json { "code": 500, "msg": "申请ID不存在" } ``` --- ## 📝 字典码值说明 ### 申请状态 (status) | 编码 | 含义 | 描述 | | ---- | ------ | ------------------------ | | 0 | 待审核 | 申请已提交,等待审批 | | 1 | 已通过 | 申请审批通过 | | 2 | 已拒绝 | 申请审批被拒绝 | | 3 | 已取消 | 申请被申请人主动取消 | | 4 | 已完成 | 出国活动已完成 | ### 工作流状态 (approvalStatus) | 编码 | 含义 | 描述 | | ----------- | ------ | ------------------------ | | cancel | 已撤销 | 申请人主动撤销申请 | | draft | 草稿 | 申请尚未提交,处于编辑状态 | | waiting | 待审核 | 申请已提交,正在审批流程中 | | finish | 已完成 | 所有审批节点都已通过,流程结束 | | invalid | 已作废 | 申请因某种原因被标记为无效 | | back | 已退回 | 审批被拒绝,退回给申请人修改 | | termination | 已终止 | 流程被管理员或系统强制终止 | > **📝 说明**:工作流状态由 Warm-Flow 工作流引擎自动管理,用于总体流程监听和状态跟踪。 ### 出国目的 (purpose) | 编码 | 含义 | 描述 | | ----------------- | -------- | ------------------------ | | academic_exchange | 学术交流 | 参加学术交流活动 | | conference | 学术会议 | 参加学术会议 | | training | 培训学习 | 参加培训课程 | | cooperation | 合作项目 | 开展合作研究项目 | | research | 科研访问 | 科研访问学者 | | other | 其他 | 其他出国目的 | ### 经费来源 (budgetSource) | 编码 | 含义 | 描述 | | ----------- | -------- | ------------------------ | | school | 学校经费 | 学校提供经费支持 | | government | 政府资助 | 政府资助项目 | | project | 项目经费 | 科研项目经费 | | self | 个人承担 | 个人承担费用 | | cooperation | 合作单位 | 合作单位提供经费 | | other | 其他 | 其他经费来源 | ### 审批级别 (approvalLevel) | 编码 | 含义 | 描述 | | ---- | -------- | ------------------------ | | 1 | 部门主管 | 第一级审批:部门主管 | | 2 | 分管领导 | 第二级审批:分管领导 | | 3 | 校领导 | 第三级审批:校领导 | ### 审批记录状态 (审批记录表中的 approvalStatus) | 编码 | 含义 | 描述 | | ---- | ------ | ------------------------ | | 0 | 待审核 | 等待审批 | | 1 | 通过 | 审批通过 | | 2 | 拒绝 | 审批拒绝 | > **⚠️ 注意**:此状态是审批记录表中的状态,与申请表中的工作流状态(approvalStatus)是不同的字段。 ### 完成状态 (completionStatus) > **⚠️ 重要说明**:完成状态(completionStatus)与申请状态(status)是两个不同的概念! > - **申请状态(status)**:表示审批流程的状态(待审核/已通过/已拒绝) > - **完成状态(completionStatus)**:表示出国任务执行的状态(未完成/进行中/已完成/已取消) | 编码 | 含义 | 描述 | 使用场景 | | ---- | -------- | ------------------------ | -------- | | 0 | 未完成 | 出国活动尚未开始 | 已通过审批但尚未出发 | | 1 | 进行中 | 出国活动正在进行 | 已出发但未返回(默认值) | | 2 | 已完成 | 出国活动已完成 | 已返回并完成回执 | | 3 | 已取消 | 出国活动取消 | 计划取消 | **字段关系示例**: - 出国记录中的申请若已通过审批(`status='1'`),默认完成状态为"进行中"(`completionStatus='1'`) - 出国记录列表显示"完成状态"字段,详情弹窗显示"申请状态"字段 - 所有出国记录的申请状态都应该是"已通过"(`status='1'`),因为只有通过审批的申请才会出现在记录中 --- ## 🔄 业务流程说明 ### 申请流程 1. **提交申请**:用户填写申请信息并提交,状态为"待审核",审批级别为"部门主管" 2. **部门主管审批**:部门主管审批通过后,审批级别自动流转到"分管领导" 3. **分管领导审批**:分管领导审批通过后,审批级别自动流转到"校领导" 4. **校领导审批**:校领导审批通过后,申请状态变为"已通过",自动创建出国记录 5. **完成出国**:申请人完成出国活动后,更新记录状态为"已完成",申请状态同步更新 ### 状态流转 ``` 草稿状态 → 待审核 → 已通过/已拒绝 ↓ 已取消 ↓ 已完成 ``` ### 权限控制 - **申请人**:可以查看自己的申请,编辑草稿状态的申请,提交/取消申请 - **审批人**:可以查看待审批的申请,进行审批操作 - **管理员**:拥有所有权限,可以管理所有申请和记录 --- ## 🧪 测试说明 ### 测试环境 - **基础URL**:`http://localhost:8080` - **认证方式**:Bearer Token - **必需Header**: ``` Authorization: Bearer {token} Content-Type: application/json clientid: ruoyi-web referer: http://localhost ``` ### 测试用例 1. **申请管理测试**:创建申请→提交申请→审批流程→状态验证 2. **权限测试**:不同角色的权限边界测试 3. **业务流程测试**:完整的申请到完成流程测试 4. **数据校验测试**:参数验证、业务规则验证 ### 注意事项 - 测试前确保数据库表结构已创建 - 测试用户需要有相应的权限 - 审批测试需要使用不同级别的审批人账号 - 时间字段格式为:`YYYY-MM-DD HH:mm:ss` --- ## 📝 更新记录 | 版本 | 更新时间 | 更新内容 | 更新人 | | ---- | ---------- | -------------------------------------- | ------ | | v1.0 | 2025-01-28 | 初始版本,包含完整的API接口文档 | weizzzz | | v1.1 | 2025-08-03 | 新增查询申请审批历史接口,完善审批流程追溯功能 | weizzzz | | v1.2 | 2025-01-09 | 新增修复记录接口,修复审批通过后记录创建问题 | weizzzz | | v1.3 | 2025-01-08 | 调整我的申请列表接口,显示完整申请历史(包括已通过申请) | weizzzz | | v1.4 | 2025-01-08 | 修正我的申请列表接口,排除已通过申请(已通过申请显示在出国记录中) | weizzzz | | v1.5 | 2025-01-28 | 新增盖章文件上传功能,用户需上传盖章文件后才能提交申请 | weizzzz | ### 8. 上传登记备案表盖章文件 **接口地址**:`POST /rs/overseas/application/uploadStampedFile` **权限要求**:`rs:overseas:add` #### 功能说明 用于上传填写并加盖公章后的登记备案表文件。支持PDF、JPG、PNG格式,文件大小不超过10MB。上传成功后,用户才能提交出国申请。 #### 请求参数 **Content-Type**: `multipart/form-data` | 参数名 | 类型 | 必填 | 说明 | |---------------|--------|------|--------------------------------| | file | File | 是 | 盖章文件,支持PDF、JPG、PNG格式 | | applicationId | Long | 是 | 申请ID | #### 请求示例 ```bash POST /rs/overseas/application/uploadStampedFile Content-Type: multipart/form-data Authorization: Bearer {token} clientid: ruoyi-web referer: http://localhost form-data: file: [选择的盖章文件] applicationId: 1 ``` #### 响应示例 **成功响应**: ```json { "code": 200, "msg": "盖章文件上传成功", "data": { "filePath": "overseas/stamped/stamped_1_1704076800000.pdf", "fileName": "出国境登记表_张三.pdf", "uploadTime": "2025-01-28 14:30:00" } } ``` **失败响应**: ```json { "code": 500, "msg": "不支持的文件格式,请上传PDF、JPG或PNG文件" } ``` #### 业务规则 - 文件格式:只支持PDF、JPG、PNG格式 - 文件大小:不超过10MB - 数量限制:每个申请只能上传一个盖章文件 - 上传成功后,系统自动更新申请记录的盖章文件信息 - 必须先上传盖章文件,才能提交出国申请 #### 数据库更新 上传成功后,系统会自动更新以下字段: - `stamped_registration_file_path`: 文件存储路径 - `stamped_registration_file_name`: 原始文件名 - `stamped_file_upload_time`: 上传时间 - `stamped_file_upload_status`: 上传状态(设为'1') --- ### 21. 下载党籍保留申请PDF空白模板 **接口路径**:`GET /rs/overseas/record/downloadPartyRetention` #### 功能说明 生成标准的党籍保留申请PDF空白模板,用户下载后完全通过线下手工填写所有内容。 #### 权限要求 - **权限标识**:`rs:overseas:query` - **权限说明**:需要有查询出国记录的权限 #### 请求参数 **无需参数**:接口不需要任何参数,直接下载空白模板。 #### 请求示例 ```http GET /api/rs/overseas/record/downloadPartyRetention Authorization: Bearer {token} clientid: {clientid} referer: {referer} ``` #### 响应说明 **成功响应**: - **Content-Type**: `application/pdf` - **Content-Disposition**: `attachment; filename="党籍保留申请_yyyyMMdd.pdf"` - **Body**: PDF文件二进制流 **失败响应**: ```json { "code": 500, "msg": "PDF生成失败: 错误信息" } ``` #### PDF模板内容 PDF模板包含以下空白填写区域(完全手工填写): 1. **标题**:关于因私赴 ______________ __________保留党籍的申请 2. **支部信息**:______________________ 支部: 3. **申请人信息**: - 本人 _______________(____________________),现任 _______________ - 计划于 ____________________ 4. **出国信息**: - 至 ____________________ 偕配偶 _______________ - 因私赴 _______________(国家 或地区)探望 在 - _________________________ 的工作(留学、定居)的 __________ - 在外停留 __________ 天(含路、抵返国境当 5. **申请内容**:日)。现申请保留期间暂缓籍。 6. **结束语**:特此申请,望予批准。 7. **签名区域**: - 申请人:_______________ - 申请日期: ____________________ #### 使用流程 **方式一:申请表单中下载** 1. 用户在新增/编辑申请时,系统检测到是党员且出国天数>30天 2. 系统显示党籍保留附件上传区域 3. 用户点击"下载党籍保留申请PDF模板"按钮 4. 系统生成并下载空白PDF模板 5. 用户线下手工填写所有内容、打印、盖章 6. 扫描成PDF后上传到系统 **方式二:申请详情中下载** 1. 用户查看申请详情(党员且出国天数>30天) 2. 详情弹窗底部显示"下载党籍保留申请"按钮 3. 点击按钮下载空白PDF模板 4. 线下手工填写、打印、盖章后上传 #### 技术实现 - 使用 **iText 5.5.13** PDF生成库 - 支持中文字体(STSong-Light) - A4纸张大小,标准页边距 - 所有内容区域使用下划线标识,无预填充数据 - 标准模板格式,适用于所有党籍保留申请场景 #### 注意事项 - **模板为完全空白**,所有内容需线下手工填写 - 不需要申请ID或记录ID,任何时候都可以下载 - 下载后需打印、手写填写、盖章后扫描上传 - 适用于中共党员且出国天数>30天的情况 - **按钮显示位置**: - 申请表单:党员且出国天数>30天时自动显示 - 申请详情:符合条件时在弹窗底部显示绿色按钮 - **前端实现**:必须使用 `request` 工具而不是 `fetch` API,以确保包含所有必要的认证请求头 - **附件格式**:自 2025-11-27 起,党籍保留附件支持任意文件类型上传,仅保留 10MB 单文件大小限制 #### 错误处理 | HTTP状态码 | 错误信息 | 处理建议 | | ---------- | ---------------------- | ---------------------------- | | 500 | PDF生成失败 | 检查服务器日志,联系技术支持 | --- **📞 技术支持**:如有疑问请联系开发团队 **📚 相关文档**:参考项目API文档基础规范和开发规范 **🆕 更新日志**: - 2025-10-17 新增党籍保留PDF空白模板下载功能(线下手工填写) - 接口路径调整:从 `/application/downloadPartyRetentionTemplate/{id}` 改为 `/record/downloadPartyRetention` - 模板类型变更:从预填充模板改为完全空白模板 --- ## 附录:工作流集成说明 ### 动态审批人方案 #### 背景 出国申请业务集成了Warm-Flow工作流引擎,审批流程为:**发起人 → 部门领导 → 校领导**。审批人根据申请人所属部门动态确定,系统需要实时查询当前待审批人信息。 #### 方案设计 ##### 1. 数据库字段说明 `rs_overseas_application` 表中的 `current_approver_id` 字段: - **字段类型**:`bigint(20)` - **字段作用**:**冗余展示字段**(非核心业务字段) - **维护方式**:不再由后端业务代码维护 - **数据来源**:前端通过工作流API实时查询获取 - **设计原因**: - 审批人由工作流引擎动态确定,工作流表 `flow_node_user` 是唯一数据源 - 避免数据冗余和不一致问题 - 简化后端业务逻辑,降低维护成本 ##### 2. 后端实现 **核心原则**:后端不维护 `current_approver_id` 字段 ```java // OverseasApplicationServiceImpl.java // ✅ 新增申请:不设置 current_approver_id public Long addOverseasApplication(OverseasApplicationBo bo) { // ... 业务逻辑 // 不再包含:application.setCurrentApproverId(xxx); } // ✅ 提交审批:不设置 current_approver_id public R submitApplication(Long id) { // ... 提交工作流 // 不再包含:application.setCurrentApproverId(xxx); } // ✅ 审批通过:不设置 current_approver_id public R approve(Long id, String remark) { // ... 审批逻辑 // 不再包含:application.setCurrentApproverId(xxx); } ``` **代码清理**: - 所有 `setCurrentApproverId()` 调用已移除 - 相关查询逻辑已删除 - 字段仅作为数据库表结构保留(历史兼容) ##### 3. 前端实现 **核心方案**:通过工作流API实时查询当前审批人 ```typescript // overseasApplication/index.vue // 方案一:列表查询时批量获取审批人(推荐) const getList = async () => { const response = await listOverseasApplication(queryParams.value); dataList.value = response.rows; total.value = response.total; // 批量查询审批人信息 await batchGetCurrentApproverNames(); }; // 批量获取审批人姓名 const batchGetCurrentApproverNames = async () => { const promises = dataList.value .filter(item => item.status === 'processing') .map(async item => { try { // 调用工作流API查询当前审批人 const approvers = await getCurrentApproversForTask(item.flowProcessId); item.currentApproverName = approvers.map(u => u.name).join('、'); } catch (error) { item.currentApproverName = '-'; } }); await Promise.all(promises); }; // 方案二:详情查询时单独获取审批人 const handleViewDetail = async (row: OverseasApplicationVo) => { currentRow.value = row; // 如果流程进行中,查询当前审批人 if (row.flowProcessId && row.status === 'processing') { try { const approvers = await getCurrentApproversForTask(row.flowProcessId); currentRow.value.currentApproverName = approvers.map(u => u.name).join('、'); } catch (error) { currentRow.value.currentApproverName = '查询失败'; } } detailDialogVisible.value = true; }; ``` **工作流API调用**: ```typescript // 查询当前审批人(从工作流引擎获取) import { getCurrentApproversForTask } from '@/api/workflow/common'; // API 路径:/api/workflow/instance/{processId}/current-approvers // 返回格式: { "code": 200, "msg": "操作成功", "data": [ { "userId": 1001, "userName": "张三", "nodeId": 2, "nodeName": "部门领导审批" } ] } ``` ##### 4. 接口改造 相关API接口响应中的 `current_approver_id` 字段说明: | 接口 | 字段说明 | 使用建议 | |------|---------|---------| | 查询列表 | 返回null或历史值 | 忽略,通过工作流API获取 | | 查询详情 | 返回null或历史值 | 忽略,通过工作流API获取 | | 新增/修改 | 不处理该字段 | 前端不传递 | | 提交审批 | 不处理该字段 | 前端不传递 | ##### 5. 优势分析 **✅ 数据一致性** - 工作流表是唯一数据源,避免双写不一致 - 审批人变更(如转办、委派)自动生效 **✅ 降低维护成本** - 后端无需在多个业务方法中同步更新 - 减少代码复杂度和潜在Bug **✅ 实时性** - 前端每次查询都获取最新审批人 - 支持工作流引擎的高级功能(并行审批、会签等) **✅ 扩展性** - 审批流程调整时,前端无需修改 - 支持更复杂的审批场景(多人会签、或签等) #### 实施步骤 1. ✅ **后端改造**:移除所有 `current_approver_id` 字段的赋值代码 2. ✅ **前端改造**:实现基于工作流API的动态查询 3. ⏳ **测试验证**:确保列表、详情页面正确显示审批人 4. ⏳ **文档更新**:同步更新API文档和技术文档 #### 注意事项 - 历史数据中的 `current_approver_id` 字段可能存在脏数据,前端应忽略 - 工作流API调用失败时,前端应显示友好提示(如 "-" 或 "查询失败") - 性能优化:列表页建议使用批量查询,避免N+1查询问题 - 缓存策略:同一个流程实例的审批人信息可短时间缓存(如30秒) --- **更新时间**:2025-10-28 **维护人员**:开发团队 **变更记录**:新增工作流动态审批人方案说明 --- ### 2025-11-28 更新:出国记录回执下载 - **前端**:`web/src/views/rs/overseas/index.vue` 中的 “下载因私出国(境)情况回执” 按钮改为直接基于 Word 模板生成文件,摆脱后端 PDF 下载接口依赖。 - **模板**:`web/public/chuguoqingkuanghuizhi.docx`,占位符 `{{applicantName}}`、`{{startYear}}`、`{{startMonth}}`、`{{startDay}}`、`{{endYear}}`、`{{endMonth}}`、`{{endDay}}`、`{{destinationText}}`、`{{exitReasonText}}`、`{{durationDays}}`、`{{expenseType}}`。 - **数据映射**:优先使用记录中的实际出行信息(`actualStartDate`、`actualEndDate`、`destinationCountry`、`exitReason` 等),缺省时回落到申请单数据,行程天数自动根据起止日期计算,默认费用说明为“费用自理”。 - **交互提示**:增加起止日期缺失时的前端校验,模板生成失败会提示用户检查模板或稍后再试。 ### 2025-11-28 更新:审批监控页面审批表导出 - **前端**:`web/src/views/rs/overseas/monitor/index.vue` 的 “下载审批表” 按钮从 PDF 调整为 Word 导出,保持统一的 docxtemplater + image module 生成逻辑。 - **模板**:`web/public/chuguojingshenpibiao.docx`,覆盖个人信息、行程信息、申请人签名及五级审批节点(部门负责人、人事处、纪委、学院分管领导、学院党委书记)的签名、意见、日期占位符,支持 `{{%signature}}` 形式的图片变量。 - **数据来源**:优先取 `currentDetail` 详情接口返回值;审批节点数据由 `approvalHistory` 根据 `approvalLevel` 映射,若无签名/日期则留空。详情接口新增 `signature` 字段(申请人手写签名 base64),供模板直接使用。 - **交互提示**:仅在审批完成(状态为 `1`)时显示导出按钮,生成失败时给出友好提示并引导检查模板。