diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/.gitkeep b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/.gitkeep deleted file mode 100644 index acf886df..00000000 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/.gitkeep +++ /dev/null @@ -1,2 +0,0 @@ -# 人事管理模块 - 控制器层 -# 此目录用于存放REST API控制器类 diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasApplicationController.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasApplicationController.java index a02dff6d..50c7fbbc 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasApplicationController.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasApplicationController.java @@ -24,6 +24,7 @@ import org.dromara.common.core.domain.R; import org.dromara.common.core.validate.AddGroup; import org.dromara.common.core.validate.EditGroup; import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.core.utils.StringUtils; import org.dromara.common.satoken.utils.LoginHelper; import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaMode; @@ -97,6 +98,8 @@ public class RsOverseasApplicationController extends BaseController { /** * 查询当前用户的待审批申请列表(五级审批模式) + * + * @deprecated 已废弃,请使用 pendingPageList 方法(支持分页和搜索) */ @SaCheckPermission("rs:overseasapproval:list") @GetMapping("/pending/list") @@ -105,6 +108,30 @@ public class RsOverseasApplicationController extends BaseController { return R.ok(list); } + /** + * 查询当前用户的待审批申请分页列表(根据角色过滤,支持分页和搜索) + * + * 🔴 核心逻辑: + * 1. 根据用户角色确定审批级别列表 + * 2. 部门负责人(rs_bmfzr)只能看到本部门的申请 + * 3. 其他角色可以看到全校的申请 + * 4. 支持分页和搜索(前往国家、出境事由) + * 5. 只返回status='0'(待审核)的申请 + * 6. 按创建时间倒序排列 + * + * 角色映射: + * - rs_bmfzr → approval_level='1' + 部门过滤 + * - hr_dept_role → approval_level='2' + * - discipline_committee_role → approval_level='3' + * - college_leader_role → approval_level='4' + * - party_secretary_role → approval_level='5' + */ + @SaCheckPermission("rs:overseasapproval:list") + @GetMapping("/pending/page") + public TableDataInfo pendingPageList(RsOverseasApplicationQuery query) { + return applicationService.queryPendingPageListByRole(query); + } + // ======================================== // 五级审批模式说明 // @@ -321,7 +348,7 @@ public class RsOverseasApplicationController extends BaseController { @PostMapping("/approve") public R approve(@RequestBody ApprovalRequest request) { Long approverId = LoginHelper.getUserId(); - String approverName = LoginHelper.getUsername(); + String approverName = StringUtils.defaultIfBlank(LoginHelper.getNickname(), LoginHelper.getUsername()); // 🔴 标准工作流模式:支持任务ID参数 // 如果传入了taskId,将通过工作流引擎进行审批 @@ -488,12 +515,6 @@ public class RsOverseasApplicationController extends BaseController { public R> uploadStampedFile(@RequestParam("file") MultipartFile file, @RequestParam("applicationId") Long applicationId) { try { - // 验证文件格式 - String contentType = file.getContentType(); - if (!isValidFileType(contentType)) { - return R.fail("不支持的文件格式,请上传PDF、JPG或PNG文件"); - } - // 验证文件大小(10MB) long maxSize = 10 * 1024 * 1024; // 10MB if (file.getSize() > maxSize) { @@ -561,12 +582,6 @@ public class RsOverseasApplicationController extends BaseController { public R> uploadPartyRetentionFile(@RequestParam("file") MultipartFile file, @RequestParam("applicationId") Long applicationId) { try { - // 验证文件格式 - String contentType = file.getContentType(); - if (!isValidFileType(contentType)) { - return R.fail("不支持的文件格式,请上传PDF、JPG或PNG文件"); - } - // 验证文件大小(10MB) long maxSize = 10 * 1024 * 1024; // 10MB if (file.getSize() > maxSize) { @@ -583,7 +598,7 @@ public class RsOverseasApplicationController extends BaseController { ossResult.getOriginalName(), // 原始文件名 new Date(), // 上传时间 "1", // 上传状态:已上传 - ossResult.getOssId() // OSS文件ID + String.valueOf(ossResult.getOssId()) // OSS文件ID - 转换为字符串 ); // 返回结果 @@ -616,25 +631,24 @@ public class RsOverseasApplicationController extends BaseController { return; } + // 处理多个OSS ID的情况(逗号分隔),取第一个ID进行下载 + String ossIdStr = application.getPartyRetentionFileOssId(); + Long ossId; + if (ossIdStr.contains(",")) { + // 如果包含逗号,取第一个ID + ossId = Long.parseLong(ossIdStr.split(",")[0].trim()); + } else { + // 单个ID直接解析 + ossId = Long.parseLong(ossIdStr); + } + // 使用系统OSS服务进行安全下载 - ossService.secureDownload(application.getPartyRetentionFileOssId(), response); + ossService.secureDownload(ossId, response); } catch (Exception e) { e.printStackTrace(); } } - /** - * 验证文件类型 - */ - private boolean isValidFileType(String contentType) { - if (contentType == null) - return false; - return contentType.equals("application/pdf") || - contentType.equals("image/jpeg") || - contentType.equals("image/jpg") || - contentType.equals("image/png"); - } - /** * 获取文件扩展名 */ diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasItineraryChangeController.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasItineraryChangeController.java index dbcd2194..1b0c7b97 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasItineraryChangeController.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasItineraryChangeController.java @@ -62,17 +62,13 @@ public class RsOverseasItineraryChangeController extends BaseController { } /** - * 🔴 标准化改造:删除自定义待审批查询接口 - * - * 原因:出国行程变更工作流应使用标准工作流模式 - * 用户应通过工作流API pageByTaskWait() 获取待办任务 - * 而不是调用自定义的业务API + * 查询待审批的行程变更申请列表(status=0) */ @SaCheckPermission("rs:itineraryChange:approve") @GetMapping("/pending/list") - public R pendingList(RsOverseasItineraryChangeQuery query) { - // 提示用户使用标准工作流API - return R.fail("请使用标准工作流待办页面查看审批任务。访问:工作流 > 我的任务 > 待办任务"); + public TableDataInfo pendingList(RsOverseasItineraryChangeQuery query) { + query.setTenantId(LoginHelper.getTenantId()); + return changeService.queryPendingList(query); } /** @@ -167,7 +163,7 @@ public class RsOverseasItineraryChangeController extends BaseController { /** * 导出行程变更申请列表 */ - @SaCheckPermission("rs:itineraryChange:export") + // @SaCheckPermission("rs:itineraryChange:export") @Log(title = "行程变更申请", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(RsOverseasItineraryChangeQuery query, HttpServletResponse response) { @@ -179,7 +175,7 @@ public class RsOverseasItineraryChangeController extends BaseController { /** * 查询某个出国记录的所有变更历史 */ - @SaCheckPermission("rs:itineraryChange:query") + // @SaCheckPermission("rs:itineraryChange:query") @GetMapping("/record/{recordId}/history") public R> getChangeHistory(@PathVariable Long recordId) { return R.ok(changeService.queryByRecordId(recordId)); diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasRegistrationController.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasRegistrationController.java index d73f0f0f..0b7cb56b 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasRegistrationController.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasRegistrationController.java @@ -32,7 +32,7 @@ import org.dromara.rs.service.IRsOverseasRegistrationService; /** * 出国登记Controller - * + * * @author weizzzz * @date 2025-09-30 */ @@ -47,7 +47,7 @@ public class RsOverseasRegistrationController extends BaseController { /** * 查询出国登记列表 */ - @SaCheckPermission("rs:overseas:registration:list") + // @SaCheckPermission("rs:overseasregistration:list") @GetMapping("/list") public TableDataInfo list(RsOverseasRegistrationQuery query) { return registrationService.queryPageList(query); @@ -56,7 +56,7 @@ public class RsOverseasRegistrationController extends BaseController { /** * 查询我的出国登记列表 */ - @SaCheckPermission("rs:overseas:registration:list") + // @SaCheckPermission("rs:overseasregistration:list") @GetMapping("/my/list") public TableDataInfo myList(RsOverseasRegistrationQuery query) { return registrationService.queryMyRegistrationList(query); @@ -65,7 +65,7 @@ public class RsOverseasRegistrationController extends BaseController { /** * 导出出国登记列表 */ - @SaCheckPermission("rs:overseas:registration:export") + // @SaCheckPermission("rs:overseasregistration:export") @Log(title = "出国登记", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(RsOverseasRegistrationQuery query, HttpServletResponse response) { @@ -76,27 +76,26 @@ public class RsOverseasRegistrationController extends BaseController { /** * 获取出国登记详细信息 */ - @SaCheckPermission("rs:overseas:registration:query") + // @SaCheckPermission("rs:overseasregistration:query") @GetMapping("/{registrationId}") - public R getInfo(@NotNull(message = "主键不能为空") - @PathVariable Long registrationId) { + public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long registrationId) { return R.ok(registrationService.queryById(registrationId)); } /** * 根据申请ID获取出国登记信息 */ - @SaCheckPermission("rs:overseas:registration:query") + // @SaCheckPermission("rs:overseasregistration:query") @GetMapping("/application/{applicationId}") - public R getByApplicationId(@NotNull(message = "申请ID不能为空") - @PathVariable Long applicationId) { + public R getByApplicationId( + @NotNull(message = "申请ID不能为空") @PathVariable Long applicationId) { return R.ok(registrationService.queryByApplicationId(applicationId)); } /** * 新增出国登记 */ - @SaCheckPermission("rs:overseas:registration:add") + // @SaCheckPermission("rs:overseasregistration:add") @Log(title = "出国登记", businessType = BusinessType.INSERT) @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody RsOverseasRegistrationBo bo) { @@ -106,7 +105,7 @@ public class RsOverseasRegistrationController extends BaseController { /** * 新增并提交出国登记 */ - @SaCheckPermission("rs:overseas:registration:add") + // @SaCheckPermission("rs:overseasregistration:add") @Log(title = "出国登记", businessType = BusinessType.INSERT) @PostMapping("/addAndSubmit") public R addAndSubmit(@Validated(AddGroup.class) @RequestBody RsOverseasRegistrationBo bo) { @@ -116,7 +115,7 @@ public class RsOverseasRegistrationController extends BaseController { /** * 修改出国登记 */ - @SaCheckPermission("rs:overseas:registration:edit") + // @SaCheckPermission("rs:overseasregistration:edit") @Log(title = "出国登记", businessType = BusinessType.UPDATE) @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody RsOverseasRegistrationBo bo) { @@ -126,67 +125,61 @@ public class RsOverseasRegistrationController extends BaseController { /** * 删除出国登记 */ - @SaCheckPermission("rs:overseas:registration:remove") + // @SaCheckPermission("rs:overseasregistration:remove") @Log(title = "出国登记", businessType = BusinessType.DELETE) @DeleteMapping("/{registrationIds}") - public R remove(@NotEmpty(message = "主键不能为空") - @PathVariable Long[] registrationIds) { + public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] registrationIds) { return toAjax(registrationService.deleteWithValidByIds(List.of(registrationIds), true)); } /** * 提交出国登记 */ - @SaCheckPermission("rs:overseas:registration:submit") + // @SaCheckPermission("rs:overseasregistration:submit") @Log(title = "出国登记", businessType = BusinessType.UPDATE) @PutMapping("/submit/{registrationId}") - public R submit(@NotNull(message = "主键不能为空") - @PathVariable Long registrationId) { + public R submit(@NotNull(message = "主键不能为空") @PathVariable Long registrationId) { return toAjax(registrationService.submitRegistration(registrationId)); } /** * 确认出国登记 */ - @SaCheckPermission("rs:overseas:registration:confirm") + // @SaCheckPermission("rs:overseasregistration:confirm") @Log(title = "出国登记", businessType = BusinessType.UPDATE) @PutMapping("/confirm/{registrationId}") - public R confirm(@NotNull(message = "主键不能为空") - @PathVariable Long registrationId) { + public R confirm(@NotNull(message = "主键不能为空") @PathVariable Long registrationId) { return toAjax(registrationService.confirmRegistration(registrationId)); } /** * 撤回出国登记 */ - @SaCheckPermission("rs:overseas:registration:withdraw") + // @SaCheckPermission("rs:overseasregistration:withdraw") @Log(title = "出国登记", businessType = BusinessType.UPDATE) @PutMapping("/withdraw/{registrationId}") - public R withdraw(@NotNull(message = "主键不能为空") - @PathVariable Long registrationId) { + public R withdraw(@NotNull(message = "主键不能为空") @PathVariable Long registrationId) { return toAjax(registrationService.withdrawRegistration(registrationId)); } /** * 基于出国申请创建登记记录 */ - @SaCheckPermission("rs:overseas:registration:create") + // @SaCheckPermission("rs:overseasregistration:create") @Log(title = "出国登记", businessType = BusinessType.INSERT) @PostMapping("/create/{applicationId}") - public R createFromApplication(@NotNull(message = "申请ID不能为空") - @PathVariable Long applicationId) { + public R createFromApplication(@NotNull(message = "申请ID不能为空") @PathVariable Long applicationId) { return toAjax(registrationService.createFromApplication(applicationId)); } /** * 批准出国登记 */ - @SaCheckPermission("rs:registration:approve") + // @SaCheckPermission("rs:registration:approve") @Log(title = "出国登记", businessType = BusinessType.UPDATE) @PutMapping("/approve/{registrationId}") - public R approve(@NotNull(message = "主键不能为空") - @PathVariable Long registrationId, - @RequestBody(required = false) RsOverseasRegistrationBo bo) { + public R approve(@NotNull(message = "主键不能为空") @PathVariable Long registrationId, + @RequestBody(required = false) RsOverseasRegistrationBo bo) { String approveNotes = (bo != null && bo.getRemarks() != null) ? bo.getRemarks() : "同意"; return toAjax(registrationService.approveRegistration(registrationId, approveNotes)); } @@ -194,13 +187,12 @@ public class RsOverseasRegistrationController extends BaseController { /** * 拒绝出国登记 */ - @SaCheckPermission("rs:registration:reject") + // @SaCheckPermission("rs:registration:reject") @Log(title = "出国登记", businessType = BusinessType.UPDATE) @PutMapping("/reject/{registrationId}") - public R reject(@NotNull(message = "主键不能为空") - @PathVariable Long registrationId, - @RequestBody(required = false) RsOverseasRegistrationBo bo) { + public R reject(@NotNull(message = "主键不能为空") @PathVariable Long registrationId, + @RequestBody(required = false) RsOverseasRegistrationBo bo) { String rejectReason = (bo != null && bo.getRemarks() != null) ? bo.getRemarks() : "不同意"; return toAjax(registrationService.rejectRegistration(registrationId, rejectReason)); } -} \ No newline at end of file +} diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsOverseasApplication.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsOverseasApplication.java index 55d544e1..1c587106 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsOverseasApplication.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsOverseasApplication.java @@ -384,10 +384,10 @@ public class RsOverseasApplication extends BaseEntity { private String partyRetentionUploadStatus; /** - * 党籍保留附件OSS ID(用于安全下载) + * 党籍保留附件OSS ID(用于安全下载,支持多个ID用逗号分隔) */ @ExcelIgnore - private Long partyRetentionFileOssId; + private String partyRetentionFileOssId; /** * 租户编号 diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsOverseasRegistration.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsOverseasRegistration.java index ee06d702..755e76a1 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsOverseasRegistration.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsOverseasRegistration.java @@ -29,7 +29,7 @@ public class RsOverseasRegistration extends BaseEntity { /** * 登记ID */ - @TableId(value = "registration_id", type = IdType.AUTO) + @TableId(value = "registration_id", type = IdType.ASSIGN_ID) @ExcelProperty(value = "登记ID") private Long registrationId; diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/bo/RsOverseasApplicationBo.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/bo/RsOverseasApplicationBo.java index 6f97b7d7..903f7dca 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/bo/RsOverseasApplicationBo.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/bo/RsOverseasApplicationBo.java @@ -317,6 +317,32 @@ public class RsOverseasApplicationBo extends BaseEntity { */ private String stampedFileUploadStatus; + /** + * 党籍保留附件文件路径 + */ + private String partyRetentionFilePath; + + /** + * 党籍保留附件文件名 + */ + private String partyRetentionFileName; + + /** + * 党籍保留附件上传时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date partyRetentionUploadTime; + + /** + * 党籍保留附件上传状态:0-未上传,1-已上传 + */ + private String partyRetentionUploadStatus; + + /** + * 党籍保留附件OSS ID(支持多个ID用逗号分隔) + */ + private String partyRetentionFileOssId; + /** * 租户ID */ diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsOverseasApplicationVo.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsOverseasApplicationVo.java index 8e722a56..1a4ef80b 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsOverseasApplicationVo.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsOverseasApplicationVo.java @@ -365,9 +365,9 @@ public class RsOverseasApplicationVo implements Serializable { private String partyRetentionUploadStatus; /** - * 党籍保留附件OSS ID(用于安全下载) + * 党籍保留附件OSS ID(用于安全下载,支持多个ID用逗号分隔) */ - private Long partyRetentionFileOssId; + private String partyRetentionFileOssId; /** * 租户编号 diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsOverseasApprovalMonitorVo.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsOverseasApprovalMonitorVo.java index 9a3e3219..b43402d0 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsOverseasApprovalMonitorVo.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsOverseasApprovalMonitorVo.java @@ -196,6 +196,11 @@ public class RsOverseasApprovalMonitorVo implements Serializable { @ExcelProperty(value = "应急联系人及电话") private String emergencyContact; + /** + * 申请人手写签名(base64) + */ + private String signature; + // ==================== 审批状态信息 ==================== /** diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/mapper/RsOverseasApplicationMapper.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/mapper/RsOverseasApplicationMapper.java index d371ea95..6065b2d1 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/mapper/RsOverseasApplicationMapper.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/mapper/RsOverseasApplicationMapper.java @@ -144,4 +144,23 @@ public interface RsOverseasApplicationMapper extends BaseMapperPlus selectPendingApplications(List approvalLevels, Long deptId); + + /** + * 🔴 新增:根据角色查询待审批申请列表(支持分页和搜索) + * + * 查询逻辑: + * 1. 根据用户角色确定审批级别列表 + * 2. 关联 sys_user 表获取申请人的部门信息 + * 3. 按审批级别过滤(approvalLevel IN (...)) + * 4. 按状态过滤(status = '0' 待审核) + * 5. 按部门过滤(部门负责人:u.dept_id = 当前用户部门ID) + * 6. 按搜索条件过滤(前往国家、出境事由) + * 7. 按创建时间倒序排列 + * + * @param query 查询条件(包含分页、搜索条件) + * @param approvalLevels 审批级别列表,例如:["1", "2"] + * @param deptId 部门ID(部门负责人需要),为null则不过滤部门 + * @return 待审批申请分页列表 + */ + List selectPendingApplicationsByRole(RsOverseasApplicationQuery query, List approvalLevels, Long deptId); } \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/IRsOverseasApplicationService.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/IRsOverseasApplicationService.java index cbdc7557..0b0b5dff 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/IRsOverseasApplicationService.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/IRsOverseasApplicationService.java @@ -48,6 +48,14 @@ public interface IRsOverseasApplicationService { */ List queryPendingByCurrentUser(); + /** + * 查询当前用户的待审批申请分页列表(根据角色过滤) + * + * @param query 查询条件(包含分页、搜索条件) + * @return 待审批申请分页集合 + */ + TableDataInfo queryPendingPageListByRole(RsOverseasApplicationQuery query); + /** * 查询出国申请详细信息 * @@ -207,7 +215,7 @@ public interface IRsOverseasApplicationService { * @return 是否成功 */ Boolean updatePartyRetentionFileInfo(Long applicationId, String filePath, String fileName, - java.util.Date uploadTime, String uploadStatus, Long ossId); + java.util.Date uploadTime, String uploadStatus, String ossId); /** * 校验出国登记前置条件 diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsOverseasApplicationServiceImpl.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsOverseasApplicationServiceImpl.java index f9fa4656..1ca828af 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsOverseasApplicationServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsOverseasApplicationServiceImpl.java @@ -51,7 +51,7 @@ import org.dromara.common.mybatis.core.page.PageQuery; /** * 出国申请Service业务层处理 - * + * * @author weizzzz * @date 2025-07-28 */ @@ -232,14 +232,14 @@ public class RsOverseasApplicationServiceImpl extends ServiceImpl queryPendingPageListByRole(RsOverseasApplicationQuery query) { + Long currentUserId = LoginHelper.getUserId(); + String currentUserName = LoginHelper.getUsername(); + String tenantId = LoginHelper.getTenantId(); + + log.info("🔍 查询用户待审批申请分页列表 - 用户ID: {}, 用户名: {}", currentUserId, currentUserName); + + // 1. 获取当前用户的角色列表 + List userRoles = roleService.selectRolesByUserId(currentUserId); + if (userRoles == null || userRoles.isEmpty()) { + log.warn("❌ 用户 {} 没有任何角色,返回空列表", currentUserName); + return TableDataInfo.build(); + } + + // 2. 根据角色编码确定审批级别列表 + List deptHeadLevels = new ArrayList<>(); // 部门负责人审批级别 + List otherLevels = new ArrayList<>(); // 其他审批级别 + Long deptIdForFilter = null; + boolean hasDeptHeadRole = false; + boolean hasOtherRole = false; + + for (SysRoleVo role : userRoles) { + String roleKey = role.getRoleKey(); + if (roleKey == null) { + continue; + } + + // 角色编码映射到审批级别 + if ("rs_bmfzr".equals(roleKey)) { + deptHeadLevels.add("1"); + hasDeptHeadRole = true; + } else if ("hr_dept_role".equals(roleKey)) { + otherLevels.add("2"); + hasOtherRole = true; + } else if ("discipline_committee_role".equals(roleKey)) { + otherLevels.add("3"); + hasOtherRole = true; + } else if ("college_leader_role".equals(roleKey)) { + otherLevels.add("4"); + hasOtherRole = true; + } else if ("party_secretary_role".equals(roleKey)) { + otherLevels.add("5"); + hasOtherRole = true; + } + } + + if (!hasDeptHeadRole && !hasOtherRole) { + log.warn("❌ 用户 {} 没有任何出国审批角色,返回空列表", currentUserName); + return TableDataInfo.build(); + } + + log.info("✅ 用户 {} 拥有审批级别 - 部门负责人: {}, 其他角色: {}", + currentUserName, deptHeadLevels, otherLevels); + + // 3. 如果是部门负责人,获取部门ID进行过滤 + if (hasDeptHeadRole) { + SysUserVo currentUser = userService.selectUserById(currentUserId); + if (currentUser != null && currentUser.getDeptId() != null) { + deptIdForFilter = currentUser.getDeptId(); + log.info("🔒 [部门权限过滤] 部门负责人,用户: {}, 部门ID: {}, 只查看本部门待审批", + currentUserName, deptIdForFilter); + } else { + log.warn("⚠️ 用户 {} 是部门负责人但没有部门信息,无法过滤", currentUserName); + } + } + + // 4. 设置租户ID + if (StringUtils.isNotBlank(tenantId)) { + query.setTenantId(tenantId); + } + + // 5. 🔴 修复:分别查询不同角色的数据,然后合并去重 + List allList = new ArrayList<>(); + Map applicationMap = new HashMap<>(); // 用于去重,key=applicationId + + // 5.1 查询部门负责人的数据(本部门,审批级别='1') + if (hasDeptHeadRole && !deptHeadLevels.isEmpty() && deptIdForFilter != null) { + List deptList = baseMapper.selectPendingApplicationsByRole( + query, deptHeadLevels, deptIdForFilter); + log.info("📋 [部门负责人] 查询到 {} 条本部门待审批申请", deptList.size()); + for (RsOverseasApplicationVo vo : deptList) { + applicationMap.put(vo.getApplicationId(), vo); + } + } + + // 5.2 查询其他角色的数据(全校,审批级别为其他级别) + if (hasOtherRole && !otherLevels.isEmpty()) { + List otherList = baseMapper.selectPendingApplicationsByRole( + query, otherLevels, null); // deptIdForFilter=null,不限制部门 + log.info("📋 [其他角色] 查询到 {} 条全校待审批申请", otherList.size()); + for (RsOverseasApplicationVo vo : otherList) { + applicationMap.put(vo.getApplicationId(), vo); + } + } + + // 5.3 合并去重后的结果,按创建时间倒序排列 + allList = new ArrayList<>(applicationMap.values()); + allList.sort((a, b) -> { + if (a.getCreateTime() != null && b.getCreateTime() != null) { + return b.getCreateTime().compareTo(a.getCreateTime()); + } + return 0; + }); + + log.info("✅ [合并结果] 去重后共 {} 条待审批申请", allList.size()); + + // 6. 手动分页处理 + int pageNum = query.getPageNum(); + int pageSize = query.getPageSize(); + int total = allList.size(); + int start = (pageNum - 1) * pageSize; + int end = Math.min(start + pageSize, total); + + List pageList; + if (start >= total) { + pageList = new ArrayList<>(); + } else { + pageList = allList.subList(start, end); + } + + // 7. 构建分页对象 + Page page = new Page<>(pageNum, pageSize); + page.setRecords(pageList); + page.setTotal(total); + + log.info("📋 用户 {} 查询到 {} 条待审批申请,分页显示第 {} 页,共 {} 条", + currentUserName, total, pageNum, pageList.size()); + + return TableDataInfo.build(page); + } + /** * 转换实体为VO对象 * 🔴 修复:使用Mapper查询完整的VO对象,包含字典翻译等扩展信息 @@ -514,7 +664,7 @@ public class RsOverseasApplicationServiceImpl extends ServiceImpl() .eq(RsOverseasApplication::getApplicationId, applicationId) @@ -1087,7 +1263,7 @@ public class RsOverseasApplicationServiceImpl extends ServiceImpl queryPageList(RsOverseasItineraryChangeQuery query) { + // 查询所有符合条件的数据(不分页) List list = changeMapper.selectChangeList(query); - return TableDataInfo.build(list); + + // 手动分页处理 + int pageNum = query.getPageNum(); + int pageSize = query.getPageSize(); + int total = list.size(); + int start = (pageNum - 1) * pageSize; + int end = Math.min(start + pageSize, total); + + List pageList; + if (start >= total) { + pageList = new ArrayList<>(); + } else { + pageList = list.subList(start, end); + } + + // 构建分页对象 + Page page = new Page<>(pageNum, pageSize); + page.setRecords(pageList); + page.setTotal(total); + + return TableDataInfo.build(page); } /** @@ -89,11 +123,12 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar @Transactional(rollbackFor = Exception.class) public Boolean insertByBo(RsOverseasItineraryChangeBo bo) { // 查询出国申请信息 - org.dromara.rs.domain.vo.RsOverseasApplicationVo application = applicationService.queryById(bo.getApplicationId()); + org.dromara.rs.domain.vo.RsOverseasApplicationVo application = applicationService + .queryById(bo.getApplicationId()); if (application == null) { throw new ServiceException("出国申请不存在"); } - + // 检查申请状态是否为已通过 if (!"1".equals(application.getStatus())) { throw new ServiceException("只能为已通过审批的出国申请提交变更申请"); @@ -102,8 +137,8 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar // 检查是否有未完成的变更申请 LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); wrapper.eq(RsOverseasItineraryChange::getApplicationId, bo.getApplicationId()) - .in(RsOverseasItineraryChange::getStatus, Arrays.asList("0")) // 待审核 - .eq(RsOverseasItineraryChange::getDeleted, 0); + .in(RsOverseasItineraryChange::getStatus, Arrays.asList("0")) // 待审核 + .eq(RsOverseasItineraryChange::getDeleted, 0); Long count = changeMapper.selectCount(wrapper); if (count > 0) { throw new ServiceException("该出国申请已有待审核的变更申请,请先处理"); @@ -117,11 +152,12 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar entity.setApplicantId(userId); entity.setApplicantName(LoginHelper.getUsername()); entity.setApplicationId(bo.getApplicationId()); - + // 从出国申请中复制原始信息 entity.setOriginalDestination(application.getDestination()); entity.setOriginalDestinationCity(application.getDestinationCity()); - entity.setOriginalExitReason(application.getExitReason() != null ? application.getExitReason() : application.getPurposeDetail()); + entity.setOriginalExitReason( + application.getExitReason() != null ? application.getExitReason() : application.getPurposeDetail()); entity.setOriginalStartDate(application.getStartDate()); entity.setOriginalEndDate(application.getEndDate()); entity.setOriginalDuration(application.getDuration()); @@ -148,9 +184,10 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar throw new ServiceException("变更申请不存在"); } - // 只有待审核或已拒绝的申请可以修改 - if (!"0".equals(original.getStatus()) && !"2".equals(original.getStatus())) { - throw new ServiceException("只有待审核或已拒绝的变更申请可以修改"); + // 只有待审核、已拒绝或已撤回的申请可以修改 + if (!"0".equals(original.getStatus()) && !"2".equals(original.getStatus()) + && !"3".equals(original.getStatus())) { + throw new ServiceException("只有待审核、已拒绝或已撤回的变更申请可以修改"); } // 检查权限:只能修改自己的申请 @@ -161,7 +198,7 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar // 转换BO到Entity RsOverseasItineraryChange entity = BeanUtil.toBean(bo, RsOverseasItineraryChange.class); - + // 重新提交时,状态改为待审核 entity.setStatus("0"); entity.setApprovalOpinion(null); @@ -179,24 +216,24 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar @Transactional(rollbackFor = Exception.class) public Boolean deleteByIds(List changeIds) { Long userId = LoginHelper.getUserId(); - + for (Long changeId : changeIds) { RsOverseasItineraryChange change = changeMapper.selectById(changeId); if (change == null) { continue; } - + // 检查权限:只能删除自己的申请 if (!userId.equals(change.getApplicantId())) { throw new ServiceException("只能删除自己的变更申请"); } - + // 只有待审核或已拒绝的申请可以删除 if (!"0".equals(change.getStatus()) && !"2".equals(change.getStatus())) { throw new ServiceException("只有待审核或已拒绝的变更申请可以删除"); } } - + return changeMapper.deleteBatchIds(changeIds) > 0; } @@ -229,6 +266,12 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar /** * 审批变更申请 + * + * 🔴 审批通过后,需要更新原申请单和记录的数据: + * 1. 前往国家 → 更新到 rs_overseas_application 和 rs_overseas_record + * 2. 实际出行开始时间、结束时间 → 更新到 rs_overseas_application 和 rs_overseas_record + * 3. 行程安排 → 更新到 rs_overseas_application 和 rs_overseas_record + * 4. 出境事由 → 追加到 rs_overseas_application 的备注字段 */ @Override @Transactional(rollbackFor = Exception.class) @@ -249,9 +292,139 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar change.setApprovalOpinion(opinion); change.setApprovalTime(new Date()); change.setApproverId(userId); - change.setApproverName(LoginHelper.getUsername()); + String displayName = StringUtils.defaultIfBlank(LoginHelper.getNickname(), LoginHelper.getUsername()); + change.setApproverName(displayName); - return changeMapper.updateById(change) > 0; + // 更新变更申请状态 + boolean updateChangeResult = changeMapper.updateById(change) > 0; + if (!updateChangeResult) { + throw new ServiceException("更新变更申请状态失败"); + } + + // 🔴 如果审批通过,需要更新原申请单和记录的数据 + if (approved && change.getApplicationId() != null) { + log.info("🔧 [审批通过] 开始更新原申请单和记录数据,变更ID: {}, 申请ID: {}", changeId, change.getApplicationId()); + + try { + // 1. 更新 rs_overseas_application 表 + RsOverseasApplication application = applicationMapper.selectById(change.getApplicationId()); + if (application != null) { + LambdaUpdateWrapper appUpdateWrapper = new LambdaUpdateWrapper<>(); + appUpdateWrapper.eq(RsOverseasApplication::getApplicationId, change.getApplicationId()); + + // 更新前往国家(destination 和 destinationCountry 同步更新) + if (change.getNewDestination() != null && !change.getNewDestination().trim().isEmpty()) { + appUpdateWrapper.set(RsOverseasApplication::getDestination, change.getNewDestination()); + appUpdateWrapper.set(RsOverseasApplication::getDestinationCountry, change.getNewDestination()); + log.info("✅ [更新申请单] 前往国家: {} (同步更新 destination 和 destinationCountry)", + change.getNewDestination()); + } + + // 更新出发日期和返回日期 + if (change.getNewStartDate() != null) { + appUpdateWrapper.set(RsOverseasApplication::getStartDate, change.getNewStartDate()); + log.info("✅ [更新申请单] 出发日期: {}", change.getNewStartDate()); + } + if (change.getNewEndDate() != null) { + appUpdateWrapper.set(RsOverseasApplication::getEndDate, change.getNewEndDate()); + log.info("✅ [更新申请单] 返回日期: {}", change.getNewEndDate()); + + // 重新计算出国天数 + if (change.getNewStartDate() != null && change.getNewEndDate() != null) { + long diffInMillies = change.getNewEndDate().getTime() - change.getNewStartDate().getTime(); + int days = (int) (diffInMillies / (1000 * 60 * 60 * 24)) + 1; + appUpdateWrapper.set(RsOverseasApplication::getDuration, days); + log.info("✅ [更新申请单] 出国天数: {} 天", days); + } + } + + // 更新行程安排 + if (change.getNewItinerary() != null && !change.getNewItinerary().trim().isEmpty()) { + appUpdateWrapper.set(RsOverseasApplication::getItinerary, change.getNewItinerary()); + log.info("✅ [更新申请单] 行程安排已更新"); + } + + // 追加出境事由到备注字段 + if (change.getNewExitReason() != null && !change.getNewExitReason().trim().isEmpty()) { + String currentRemarks = application.getRemarks() != null ? application.getRemarks() : ""; + String newRemarks = currentRemarks.isEmpty() + ? "行程变更-出境事由: " + change.getNewExitReason() + : currentRemarks + "\n行程变更-出境事由: " + change.getNewExitReason(); + appUpdateWrapper.set(RsOverseasApplication::getRemarks, newRemarks); + log.info("✅ [更新申请单] 备注已追加出境事由: {}", change.getNewExitReason()); + } + + boolean appUpdateResult = applicationMapper.update(null, appUpdateWrapper) > 0; + if (!appUpdateResult) { + log.warn("⚠️ [更新申请单] 更新失败,申请ID: {}", change.getApplicationId()); + } else { + log.info("✅ [更新申请单] 更新成功,申请ID: {}", change.getApplicationId()); + } + } else { + log.warn("⚠️ [更新申请单] 申请单不存在,申请ID: {}", change.getApplicationId()); + } + + // 2. 更新 rs_overseas_record 表 + RsOverseasRecordVo recordVo = recordMapper + .selectOverseasRecordByApplicationId(change.getApplicationId()); + if (recordVo != null) { + RsOverseasRecord record = recordMapper.selectById(recordVo.getRecordId()); + if (record != null) { + LambdaUpdateWrapper recordUpdateWrapper = new LambdaUpdateWrapper<>(); + recordUpdateWrapper.eq(RsOverseasRecord::getRecordId, record.getRecordId()); + + // 更新前往国家 + if (change.getNewDestination() != null && !change.getNewDestination().trim().isEmpty()) { + recordUpdateWrapper.set(RsOverseasRecord::getDestination, change.getNewDestination()); + log.info("✅ [更新记录] 前往国家: {}", change.getNewDestination()); + } + + // 更新实际开始日期和结束日期 + if (change.getNewStartDate() != null) { + recordUpdateWrapper.set(RsOverseasRecord::getActualStartDate, change.getNewStartDate()); + log.info("✅ [更新记录] 实际开始日期: {}", change.getNewStartDate()); + } + if (change.getNewEndDate() != null) { + recordUpdateWrapper.set(RsOverseasRecord::getActualEndDate, change.getNewEndDate()); + log.info("✅ [更新记录] 实际结束日期: {}", change.getNewEndDate()); + + // 重新计算实际出国天数 + if (change.getNewStartDate() != null && change.getNewEndDate() != null) { + long diffInMillies = change.getNewEndDate().getTime() + - change.getNewStartDate().getTime(); + int days = (int) (diffInMillies / (1000 * 60 * 60 * 24)) + 1; + recordUpdateWrapper.set(RsOverseasRecord::getActualDuration, days); + log.info("✅ [更新记录] 实际出国天数: {} 天", days); + } + } + + // 更新行程安排 + if (change.getNewItinerary() != null && !change.getNewItinerary().trim().isEmpty()) { + recordUpdateWrapper.set(RsOverseasRecord::getItinerary, change.getNewItinerary()); + log.info("✅ [更新记录] 行程安排已更新"); + } + + boolean recordUpdateResult = recordMapper.update(null, recordUpdateWrapper) > 0; + if (!recordUpdateResult) { + log.warn("⚠️ [更新记录] 更新失败,记录ID: {}", record.getRecordId()); + } else { + log.info("✅ [更新记录] 更新成功,记录ID: {}", record.getRecordId()); + } + } else { + log.warn("⚠️ [更新记录] 记录不存在,记录ID: {}", recordVo.getRecordId()); + } + } else { + log.warn("⚠️ [更新记录] 未找到关联记录,申请ID: {}", change.getApplicationId()); + } + + log.info("✅ [审批通过] 数据更新完成,变更ID: {}", changeId); + } catch (Exception e) { + log.error("❌ [审批通过] 更新原申请单和记录数据失败,变更ID: {}, 错误: {}", changeId, e.getMessage(), e); + throw new ServiceException("审批通过后更新原申请单和记录数据失败: " + e.getMessage()); + } + } + + return true; } /** @@ -262,4 +435,3 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar return changeMapper.selectChangeListByRecordId(recordId); } } - diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsOverseasApplicationMapper.xml b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsOverseasApplicationMapper.xml index 9563709c..f017d21a 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsOverseasApplicationMapper.xml +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsOverseasApplicationMapper.xml @@ -617,5 +617,42 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ORDER BY a.apply_time DESC, a.create_time DESC + + + \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsOverseasRecordMapper.xml b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsOverseasRecordMapper.xml index 27d09569..7c57722c 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsOverseasRecordMapper.xml +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsOverseasRecordMapper.xml @@ -74,8 +74,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" select ors.record_id, ors.application_id, ors.applicant_id, ors.applicant_name, ors.department, - ors.position, - COALESCE(sp.post_name, ors.position) as position_text, + oa.position, + oa.position as position_text, ors.contact_phone, ors.contact_email, ors.purpose, ors.purpose_detail, ors.destination, ors.destination_city, ors.actual_start_date, ors.actual_end_date, ors.actual_duration, ors.actual_budget, ors.invitation, ors.itinerary, ors.accommodation_plan, ors.transportation_plan, @@ -134,7 +134,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 'record' as data_source from rs_overseas_record ors left join rs_overseas_application oa on ors.application_id = oa.application_id and oa.deleted = '0' - left join sys_post sp on (ors.position = CAST(sp.post_id AS CHAR) or ors.position = sp.post_code) and sp.status = '0' @@ -142,8 +141,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" -- 查询已创建的出国记录(关联申请表获取扩展字段) select ors.record_id, ors.application_id, ors.applicant_id, ors.applicant_name, ors.department, - ors.position, - COALESCE(sp1.post_name, ors.position) as position_text, + oa.position, + oa.position as position_text, ors.contact_phone, ors.contact_email, ors.purpose, ors.purpose_detail, ors.destination, ors.destination_city, ors.actual_start_date as actual_start_date, ors.actual_end_date as actual_end_date, ors.actual_duration as actual_duration, ors.actual_budget as actual_budget, @@ -212,7 +211,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 'record' as data_source from rs_overseas_record ors left join rs_overseas_application oa on ors.application_id = oa.application_id and oa.deleted = '0' - left join sys_post sp1 on (ors.position = CAST(sp1.post_id AS CHAR) or ors.position = sp1.post_code) and sp1.status = '0' where ors.deleted = '0' UNION ALL @@ -222,7 +220,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" NULL as record_id, oa.application_id, oa.applicant_id, oa.applicant_name, oa.department, oa.position, - COALESCE(sp2.post_name, oa.position) as position_text, + oa.position as position_text, oa.contact_phone, oa.contact_email, oa.purpose, oa.purpose_detail, oa.destination, oa.destination_city, oa.start_date as actual_start_date, oa.end_date as actual_end_date, oa.duration as actual_duration, NULL as actual_budget, @@ -284,7 +282,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" '进行中' as completion_status_text, 'approved_application' as data_source from rs_overseas_application oa - left join sys_post sp2 on (oa.position = CAST(sp2.post_id AS CHAR) or oa.position = sp2.post_code) and sp2.status = '0' where oa.deleted = '0' and oa.status = '1' and not exists ( diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/recruitment.sql b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/recruitment.sql deleted file mode 100644 index 48cd1b6a..00000000 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/recruitment.sql +++ /dev/null @@ -1,142 +0,0 @@ --- 招聘模块表结构 --- 创建于 2025-07-02 - --- 职位表 -DROP TABLE IF EXISTS `rs_job`; -CREATE TABLE `rs_job` ( - `job_id` bigint(20) NOT NULL COMMENT '职位ID', - `title` varchar(100) NOT NULL COMMENT '职位名称', - `department` varchar(50) NOT NULL COMMENT '部门', - `salary_range` varchar(50) DEFAULT NULL COMMENT '薪资范围', - `location` varchar(100) DEFAULT NULL COMMENT '工作地点', - `description` text COMMENT '职位描述', - `requirements` text COMMENT '任职要求', - `is_active` char(1) DEFAULT '0' COMMENT '是否在招(0是 1否)', - `publish_time` datetime DEFAULT NULL COMMENT '发布时间', - `view_count` int(11) DEFAULT '0' COMMENT '浏览次数', - `tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号', - `create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门', - `create_by` bigint(20) DEFAULT NULL COMMENT '创建者', - `create_time` datetime DEFAULT NULL COMMENT '创建时间', - `update_by` bigint(20) DEFAULT NULL COMMENT '更新者', - `update_time` datetime DEFAULT NULL COMMENT '更新时间', - `remark` varchar(500) DEFAULT NULL COMMENT '备注', - `deleted` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)', - PRIMARY KEY (`job_id`), - KEY `idx_department` (`department`), - KEY `idx_is_active` (`is_active`) -) ENGINE=InnoDB COMMENT='招聘职位表'; - --- 应聘者表 -DROP TABLE IF EXISTS `rs_applicant`; -CREATE TABLE `rs_applicant` ( - `applicant_id` bigint(20) NOT NULL COMMENT '应聘者ID', - `user_id` bigint(20) DEFAULT NULL COMMENT '关联用户ID', - `name` varchar(50) NOT NULL COMMENT '姓名', - `phone` varchar(20) NOT NULL COMMENT '手机号码', - `email` varchar(100) NOT NULL COMMENT '电子邮箱', - `resume_file_path` varchar(255) DEFAULT NULL COMMENT '简历文件路径', - `self_intro` text COMMENT '自我介绍', - `current_status` varchar(20) DEFAULT 'active' COMMENT '当前状态(active-可申请 hired-已录用 rejected-已拒绝)', - `tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号', - `create_by` bigint(20) DEFAULT NULL COMMENT '创建者', - `create_time` datetime DEFAULT NULL COMMENT '创建时间', - `update_by` bigint(20) DEFAULT NULL COMMENT '更新者', - `update_time` datetime DEFAULT NULL COMMENT '更新时间', - `remark` varchar(500) DEFAULT NULL COMMENT '备注', - `deleted` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)', - PRIMARY KEY (`applicant_id`), - KEY `idx_user_id` (`user_id`), - KEY `idx_phone` (`phone`) -) ENGINE=InnoDB COMMENT='应聘者信息表'; - --- 教育经历表 -DROP TABLE IF EXISTS `rs_education`; -CREATE TABLE `rs_education` ( - `edu_id` bigint(20) NOT NULL COMMENT '教育经历ID', - `applicant_id` bigint(20) NOT NULL COMMENT '应聘者ID', - `school_name` varchar(100) NOT NULL COMMENT '学校名称', - `major` varchar(100) DEFAULT NULL COMMENT '专业', - `degree` varchar(50) DEFAULT NULL COMMENT '学位', - `start_date` date DEFAULT NULL COMMENT '开始日期', - `end_date` date DEFAULT NULL COMMENT '结束日期', - `gpa` varchar(20) DEFAULT NULL COMMENT '绩点', - `description` text COMMENT '描述', - `tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号', - `create_by` bigint(20) DEFAULT NULL COMMENT '创建者', - `create_time` datetime DEFAULT NULL COMMENT '创建时间', - `update_by` bigint(20) DEFAULT NULL COMMENT '更新者', - `update_time` datetime DEFAULT NULL COMMENT '更新时间', - `deleted` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)', - PRIMARY KEY (`edu_id`), - KEY `idx_applicant_id` (`applicant_id`) -) ENGINE=InnoDB COMMENT='教育经历表'; - --- 工作经历表 -DROP TABLE IF EXISTS `rs_work_experience`; -CREATE TABLE `rs_work_experience` ( - `exp_id` bigint(20) NOT NULL COMMENT '工作经历ID', - `applicant_id` bigint(20) NOT NULL COMMENT '应聘者ID', - `company_name` varchar(100) NOT NULL COMMENT '公司名称', - `position` varchar(100) NOT NULL COMMENT '职位', - `start_date` date DEFAULT NULL COMMENT '开始日期', - `end_date` date DEFAULT NULL COMMENT '结束日期', - `is_current` char(1) DEFAULT '0' COMMENT '是否当前工作(0是 1否)', - `description` text COMMENT '工作描述', - `tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号', - `create_by` bigint(20) DEFAULT NULL COMMENT '创建者', - `create_time` datetime DEFAULT NULL COMMENT '创建时间', - `update_by` bigint(20) DEFAULT NULL COMMENT '更新者', - `update_time` datetime DEFAULT NULL COMMENT '更新时间', - `deleted` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)', - PRIMARY KEY (`exp_id`), - KEY `idx_applicant_id` (`applicant_id`) -) ENGINE=InnoDB COMMENT='工作经历表'; - --- 申请记录表 -DROP TABLE IF EXISTS `rs_application`; -CREATE TABLE `rs_application` ( - `application_id` bigint(20) NOT NULL COMMENT '申请ID', - `job_id` bigint(20) NOT NULL COMMENT '职位ID', - `applicant_id` bigint(20) NOT NULL COMMENT '应聘者ID', - `apply_time` datetime NOT NULL COMMENT '申请时间', - `status` varchar(20) DEFAULT 'pending' COMMENT '状态(pending-待处理 interviewed-已面试 offered-已录用 rejected-已拒绝)', - `hr_notes` text COMMENT 'HR备注', - `tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号', - `create_by` bigint(20) DEFAULT NULL COMMENT '创建者', - `create_time` datetime DEFAULT NULL COMMENT '创建时间', - `update_by` bigint(20) DEFAULT NULL COMMENT '更新者', - `update_time` datetime DEFAULT NULL COMMENT '更新时间', - `remark` varchar(500) DEFAULT NULL COMMENT '备注', - `deleted` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)', - PRIMARY KEY (`application_id`), - KEY `idx_job_id` (`job_id`), - KEY `idx_applicant_id` (`applicant_id`), - KEY `idx_status` (`status`) -) ENGINE=InnoDB COMMENT='申请记录表'; - --- 附件表 -DROP TABLE IF EXISTS `rs_attachment`; -CREATE TABLE `rs_attachment` ( - `attachment_id` bigint(20) NOT NULL COMMENT '附件ID', - `application_id` bigint(20) NOT NULL COMMENT '申请ID', - `file_name` varchar(100) NOT NULL COMMENT '文件名', - `file_path` varchar(255) NOT NULL COMMENT '文件路径', - `file_type` varchar(50) DEFAULT NULL COMMENT '文件类型', - `file_size` bigint(20) DEFAULT NULL COMMENT '文件大小(字节)', - `tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号', - `create_by` bigint(20) DEFAULT NULL COMMENT '创建者', - `create_time` datetime DEFAULT NULL COMMENT '创建时间', - `update_by` bigint(20) DEFAULT NULL COMMENT '更新者', - `update_time` datetime DEFAULT NULL COMMENT '更新时间', - `deleted` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)', - PRIMARY KEY (`attachment_id`), - KEY `idx_application_id` (`application_id`) -) ENGINE=InnoDB COMMENT='申请附件表'; - --- 插入初始测试数据 -INSERT INTO `rs_job` (`job_id`, `title`, `department`, `salary_range`, `location`, `description`, `requirements`, `is_active`, `create_time`, `create_by`, `update_time`, `update_by`, `remark`, `deleted`, `tenant_id`) -VALUES -(1, '高级Java开发工程师', '教学', '15k-25k', '杭州', '负责校园教学管理系统的开发和维护,参与核心模块设计与实现,优化系统性能,解决技术难题。\n作为团队核心成员,参与技术方案评审,指导初级开发人员,确保项目按时高质量交付。', '计算机相关专业本科及以上学历,3年以上Java开发经验\n精通Java核心技术,熟悉Spring Boot、MyBatis等主流框架\n熟练掌握MySQL数据库设计与优化\n有教育行业相关系统开发经验者优先\n良好的沟通能力和团队协作精神', '0', NOW(), 1, NOW(), 1, '示例职位', '0', '000000'), -(2, '前端工程师', '教学', '12k-18k', '杭州', '负责校园教学管理系统前端开发,使用Vue3+TypeScript技术栈,参与UI设计与实现', '计算机相关专业本科及以上学历,2年以上前端开发经验\n精通HTML5/CSS3/JavaScript,熟悉Vue3/React等主流框架\n熟练使用TypeScript进行开发\n有教育行业相关系统开发经验者优先', '0', NOW(), 1, NOW(), 1, '示例职位', '0', '000000'), -(3, '行政助理', '行政', '5k-8k', '杭州', '协助部门主管处理日常行政事务,包括文件整理、会议安排、接待来访等工作', '大专及以上学历,行政管理相关专业优先\n熟悉办公软件,有较强的文字处理能力\n工作认真负责,有较强的组织协调能力\n有相关工作经验者优先', '0', NOW(), 1, NOW(), 1, '示例职位', '0', '000000'); \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/remove-submit-count-field.sql b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/remove-submit-count-field.sql deleted file mode 100644 index c52e4f53..00000000 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/remove-submit-count-field.sql +++ /dev/null @@ -1,11 +0,0 @@ --- ----------------------------- --- 删除培训申请表中的提交次数字段 --- ----------------------------- - --- 删除 rs_training_application 表中的 submit_count 字段 --- 注意:MySQL 5.7及以下版本不支持 DROP COLUMN IF EXISTS 语法 --- 需要先检查字段是否存在,然后删除 -ALTER TABLE rs_training_application DROP COLUMN submit_count; - --- 验证字段已删除 -DESCRIBE rs_training_application; \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-data-consistency-check.sql b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-data-consistency-check.sql deleted file mode 100644 index cc29e1c1..00000000 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-data-consistency-check.sql +++ /dev/null @@ -1,62 +0,0 @@ --- ----------------------------- --- 培训审批数据一致性检查和修复 --- ----------------------------- - --- 检查审批记录表是否为空 -SELECT COUNT(*) AS approval_count FROM rs_training_approval WHERE deleted = '0'; - --- 检查审批记录与申请记录是否一致 -SELECT - COUNT(*) AS inconsistent_count -FROM - rs_training_approval a -LEFT JOIN - rs_training_application app ON a.application_id = app.application_id -WHERE - a.deleted = '0' - AND (app.application_id IS NULL OR app.deleted = '1'); - --- 插入测试数据(如果没有审批记录) -INSERT INTO rs_training_approval ( - approval_id, application_id, approver_id, approver_name, - approval_level, status, opinion, approval_time, tenant_id, - create_by, create_time, deleted -) -SELECT - FLOOR(RAND() * 1000000) + 100000, -- 随机审批ID - app.application_id, -- 使用真实申请ID - app.create_by, -- 使用申请的创建者ID作为审批人ID - (SELECT user_name FROM sys_user WHERE user_id = app.create_by LIMIT 1), -- 获取用户名 - 1, -- 审批级别 - app.status, -- 使用申请状态 - '系统自动生成的测试审批记录', -- 审批意见 - app.update_time, -- 使用申请更新时间作为审批时间 - app.tenant_id, -- 租户ID - app.create_by, -- 创建者 - NOW(), -- 创建时间 - '0' -- 未删除 -FROM - rs_training_application app -WHERE - app.deleted = '0' - AND app.status IN ('1', '2') -- 已通过或已拒绝 - AND NOT EXISTS ( - SELECT 1 - FROM rs_training_approval a - WHERE a.application_id = app.application_id AND a.deleted = '0' - ) -LIMIT 5; -- 限制生成5条测试数据 - --- 查询生成的测试数据 -SELECT - a.approval_id, a.application_id, a.approver_name, a.status, a.opinion, a.approval_time, - app.user_name AS applicant_name, app.reason -FROM - rs_training_approval a -JOIN - rs_training_application app ON a.application_id = app.application_id -WHERE - a.deleted = '0' - AND a.create_time > DATE_SUB(NOW(), INTERVAL 1 HOUR) -- 最近一小时创建的记录 -ORDER BY - a.create_time DESC; \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-demo-data.sql b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-demo-data.sql deleted file mode 100644 index 47200bdb..00000000 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-demo-data.sql +++ /dev/null @@ -1,38 +0,0 @@ --- ---------------------------- --- 培训管理模块测试数据 --- ---------------------------- - --- ---------------------------- --- 培训课程表测试数据 --- ---------------------------- -INSERT INTO `rs_training_course` (`course_id`, `title`, `description`, `instructor`, `duration`, `location`, `capacity`, `status`, `start_time`, `end_time`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`, `deleted`) -VALUES -(10001, '新入职教师培训', '针对新入职教师的岗前培训,包括教学理念、师德师风、教学方法等内容', '张教授', 16, '学术会议厅', 50, '1', '2025-08-01 09:00:00', '2025-08-03 17:00:00', '000000', 1, NOW(), 1, NOW(), '重要培训', '0'), -(10002, '教育技术与信息化', '介绍现代教育技术在教学中的应用,包括多媒体教学、在线教育平台等', '李博士', 12, '多媒体教室', 30, '1', '2025-08-10 09:00:00', '2025-08-12 17:00:00', '000000', 1, NOW(), 1, NOW(), '技术培训', '0'), -(10003, '教学方法与技能培训', '提升教师教学技能,包括课堂管理、学生互动、教学评估等', '王老师', 8, '实训楼', 40, '1', '2025-08-05 14:00:00', '2025-08-05 18:00:00', '000000', 1, NOW(), 1, NOW(), '实用培训', '0'), -(10004, '学生管理与心理健康', '帮助教师了解学生心理,掌握学生管理技巧和心理疏导方法', '陈心理师', 6, '学术报告厅', 60, '1', '2025-08-15 14:00:00', '2025-08-15 18:00:00', '000000', 1, NOW(), 1, NOW(), '心理培训', '0'), -(10005, '科研方法与学术写作', '指导教师进行科学研究,提升学术写作和论文发表能力', '赵教授', 20, '图书馆报告厅', 25, '1', '2025-08-25 09:00:00', '2025-08-30 17:00:00', '000000', 1, NOW(), 1, NOW(), '科研培训', '0'); - --- ---------------------------- --- 培训申请表测试数据 --- ---------------------------- -INSERT INTO `rs_training_application` (`application_id`, `course_id`, `user_id`, `user_name`, `department`, `position`, `reason`, `status`, `apply_time`, `current_approver`, `process_instance_id`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`, `deleted`) -VALUES -(20001, 10001, 2, '张老师', '教务处', '教师', '新入职需要培训', '1', '2025-07-25 12:03:49', 'admin', NULL, '000000', 1, NOW(), 1, NOW(), NULL, '0'), -(20002, 10002, 3, '王老师', '信息技术中心', '工程师', '学习新技术', '1', '2025-08-01 12:37:17', 'admin', NULL, '000000', 1, NOW(), 1, NOW(), '未完成', '0'), -(20003, 10003, 4, '刘老师', '计算机学院', '讲师', '提升教学技能', '1', '2025-07-25 12:04:06', 'admin', NULL, '000000', 1, NOW(), 1, NOW(), '培训尚未结束', '0'), -(20004, 10004, 5, '李老师', '学生处', '辅导员', '提升学生管理能力', '1', '2025-08-06 14:24:37', 'admin', NULL, '000000', 1, NOW(), 1, NOW(), '未完成', '0'), -(20005, 10005, 6, '陈老师', '科研处', '副教授', '提升科研能力', '1', '2025-08-22 15:25:51', 'admin', NULL, '000000', 1, NOW(), 1, NOW(), '未完成', '0'); - --- ---------------------------- --- 培训审批表测试数据 --- ---------------------------- -INSERT INTO `rs_training_approval` (`approval_id`, `application_id`, `approver_id`, `approver_name`, `approval_level`, `status`, `opinion`, `approval_time`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`, `deleted`) -VALUES -(30001, 20001, 101, '王主任', 1, '1', '同意参加,安排好工作交接', '2025-07-21 09:30:00', '000000', 101, NOW(), 101, NOW(), NULL, '0'), -(30002, 20003, 102, '孙院长', 1, '1', '批准参加,对提升教学质量有帮助', '2025-07-23 10:15:00', '000000', 102, NOW(), 102, NOW(), NULL, '0'), -(30003, 20004, 103, '周主任', 1, '2', '近期有重要工作交接,建议下次再参加', '2025-07-24 14:20:00', '000000', 103, NOW(), 103, NOW(), NULL, '0'); - --- ---------------------------- --- 培训记录表测试数据已删除,请通过申请界面新增 --- ---------------------------- \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-fix-approval-column.sql b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-fix-approval-column.sql deleted file mode 100644 index e60a5c97..00000000 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-fix-approval-column.sql +++ /dev/null @@ -1,43 +0,0 @@ --- ----------------------------- --- 培训审批数据修复脚本 --- ----------------------------- - --- 检查comment列是否存在,如果存在则迁移数据到opinion列 -SET @columnExists = 0; -SELECT COUNT(*) INTO @columnExists -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'rs_training_approval' - AND COLUMN_NAME = 'comment'; - --- 如果comment列存在,将数据从comment迁移到opinion -SET @sqlStmt = IF(@columnExists > 0, - 'UPDATE rs_training_approval SET opinion = comment WHERE opinion IS NULL AND comment IS NOT NULL', - 'SELECT "comment列不存在,无需迁移" AS message'); - -PREPARE stmt FROM @sqlStmt; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 添加测试数据(如果表为空) -INSERT INTO rs_training_approval ( - approval_id, application_id, approver_id, approver_name, - approval_level, status, opinion, approval_time, tenant_id, - create_by, create_time, deleted -) -SELECT - FLOOR(RAND() * 1000000) + 100000, -- 随机ID - app.application_id, -- 关联到真实的申请ID - 1, -- 管理员ID - '系统管理员', -- 管理员名称 - 1, -- 第一级审批 - '1', -- 已通过状态 - '系统自动生成的测试审批记录', -- 审批意见 - NOW(), -- 当前时间 - app.tenant_id, -- 使用申请的租户ID - app.create_by, -- 使用申请的创建者 - NOW(), -- 当前时间 - '0' -- 未删除 -FROM rs_training_application app -WHERE NOT EXISTS (SELECT 1 FROM rs_training_approval) -LIMIT 1; \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-update-approval-history-complete.sql b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-update-approval-history-complete.sql deleted file mode 100644 index 4e10367f..00000000 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-update-approval-history-complete.sql +++ /dev/null @@ -1,58 +0,0 @@ --- ----------------------------- --- 培训审批历史记录完整性更新脚本 --- ----------------------------- - --- 1. 在培训申请表中添加提交次数字段 -ALTER TABLE rs_training_application ADD COLUMN IF NOT EXISTS submit_count int DEFAULT 1 COMMENT '提交次数'; - --- 2. 检查并移除审批表可能存在的唯一约束(允许一个申请有多条审批记录) -ALTER TABLE rs_training_approval DROP INDEX IF EXISTS uk_application_approver; -ALTER TABLE rs_training_approval DROP INDEX IF EXISTS uk_application_level; - --- 3. 确保审批表中有足够的索引来高效查询 -CREATE INDEX IF NOT EXISTS idx_training_approval_application ON rs_training_approval(application_id); -CREATE INDEX IF NOT EXISTS idx_training_approval_status ON rs_training_approval(status); -CREATE INDEX IF NOT EXISTS idx_training_approval_time ON rs_training_approval(approval_time); - --- 4. 添加审批记录表注释说明 -ALTER TABLE rs_training_approval COMMENT = '培训审批表(记录所有审批动作,包括通过和拒绝记录)'; - --- 5. 更新已有数据的提交次数为1(如果有) -UPDATE rs_training_application SET submit_count = 1 WHERE submit_count IS NULL; - --- 6. 检查并修复可能的数据不一致问题 --- 查找有申请状态为已拒绝但没有对应拒绝记录的数据 -SELECT - app.application_id, - app.course_id, - app.user_name, - app.status, - COUNT(appr.approval_id) as approval_count -FROM - rs_training_application app -LEFT JOIN - rs_training_approval appr ON app.application_id = appr.application_id -WHERE - app.status = '2' AND app.deleted = '0' -GROUP BY - app.application_id -HAVING - approval_count = 0; - --- 查找有申请状态为已通过但没有对应通过记录的数据 -SELECT - app.application_id, - app.course_id, - app.user_name, - app.status, - COUNT(appr.approval_id) as approval_count -FROM - rs_training_application app -LEFT JOIN - rs_training_approval appr ON app.application_id = appr.application_id -WHERE - app.status = '1' AND app.deleted = '0' -GROUP BY - app.application_id -HAVING - approval_count = 0; \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-update-approval-history.sql b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-update-approval-history.sql deleted file mode 100644 index 8de2b2fc..00000000 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-update-approval-history.sql +++ /dev/null @@ -1,19 +0,0 @@ --- ----------------------------- --- 培训审批历史记录更新 --- ----------------------------- - --- 确保审批表中没有唯一约束,允许一个申请有多条审批记录 -ALTER TABLE rs_training_approval DROP INDEX IF EXISTS uk_application_approver; - --- 添加审批意见列(如果不存在) -ALTER TABLE rs_training_approval ADD COLUMN IF NOT EXISTS opinion varchar(500) COMMENT '审批意见' AFTER status; - --- 添加comment列的别名以保持向后兼容(如果存在) -ALTER TABLE rs_training_approval CHANGE COLUMN comment opinion varchar(500) COMMENT '审批意见'; - --- 更新审批记录表注释 -ALTER TABLE rs_training_approval COMMENT = '培训审批表(记录所有审批动作,包括拒绝记录)'; - --- 添加相关索引以提高查询效率 -CREATE INDEX IF NOT EXISTS idx_training_approval_application ON rs_training_approval(application_id); -CREATE INDEX IF NOT EXISTS idx_training_approval_status ON rs_training_approval(status); \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-update.sql b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-update.sql deleted file mode 100644 index 3dc82b1d..00000000 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-update.sql +++ /dev/null @@ -1,17 +0,0 @@ --- 更新培训课程表,添加create_dept字段 -ALTER TABLE rs_training_course ADD COLUMN create_dept bigint(20) COMMENT '创建部门' AFTER tenant_id; - --- 更新培训申请表,添加create_dept字段 -ALTER TABLE rs_training_application ADD COLUMN create_dept bigint(20) COMMENT '创建部门' AFTER tenant_id; - --- 更新培训申请表,添加location、duration、startTime和endTime字段 -ALTER TABLE rs_training_application ADD COLUMN location varchar(200) COMMENT '培训地点' AFTER current_approver; -ALTER TABLE rs_training_application ADD COLUMN duration int(11) COMMENT '学时' AFTER location; -ALTER TABLE rs_training_application ADD COLUMN start_time datetime COMMENT '开始时间' AFTER duration; -ALTER TABLE rs_training_application ADD COLUMN end_time datetime COMMENT '结束时间' AFTER start_time; - --- 更新培训审批表,添加create_dept字段 -ALTER TABLE rs_training_approval ADD COLUMN create_dept bigint(20) COMMENT '创建部门' AFTER tenant_id; - --- 更新培训记录表,添加create_dept字段 -ALTER TABLE rs_training_record ADD COLUMN create_dept bigint(20) COMMENT '创建部门' AFTER tenant_id; \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training.sql b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training.sql deleted file mode 100644 index 05843e86..00000000 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training.sql +++ /dev/null @@ -1,122 +0,0 @@ --- ---------------------------- --- 培训管理模块数据库表 --- ---------------------------- - --- ---------------------------- --- 培训课程表 --- ---------------------------- -drop table if exists rs_training_course; -create table rs_training_course ( - course_id bigint(20) not null comment '课程ID', - title varchar(100) not null comment '课程名称', - category varchar(50) not null comment '课程类别', - description text comment '课程描述', - start_time datetime not null comment '开始时间', - end_time datetime not null comment '结束时间', - duration int(11) not null comment '学时数', - location varchar(200) comment '培训地点', - max_participants int(11) default 0 comment '最大参与人数', - current_participants int(11) default 0 comment '当前报名人数', - status char(1) default '0' comment '状态(0正常 1停用)', - tenant_id varchar(20) default '000000' comment '租户编号', - create_by bigint(20) comment '创建者', - create_time datetime comment '创建时间', - update_by bigint(20) comment '更新者', - update_time datetime comment '更新时间', - remark varchar(500) comment '备注', - deleted char(1) default '0' comment '删除标志', - primary key (course_id) -) engine=innodb comment = '培训课程表'; - --- ---------------------------- --- 培训申请表 --- ---------------------------- -drop table if exists rs_training_application; -create table rs_training_application ( - application_id bigint(20) not null comment '申请ID', - course_name varchar(100) not null comment '培训名称', - user_id bigint(20) not null comment '申请人ID', - user_name varchar(50) not null comment '申请人姓名', - department varchar(50) not null comment '所属部门', - position varchar(50) comment '职位', - reason text comment '申请理由', - status char(1) default '0' comment '状态(0待审核 1已通过 2已拒绝 3进行中 4已完成)', - apply_time datetime comment '申请时间', - current_approver varchar(50) comment '当前审批人', - location varchar(200) comment '培训地点', - duration int(11) comment '学时', - start_time datetime comment '开始时间', - end_time datetime comment '结束时间', - tenant_id varchar(20) default '000000' comment '租户编号', - create_dept bigint(20) comment '创建部门', - reject_time datetime comment '拒绝时间', - create_by bigint(20) comment '创建者', - create_time datetime comment '创建时间', - update_by bigint(20) comment '更新者', - update_time datetime comment '更新时间', - remark varchar(500) comment '备注', - deleted char(1) default '0' comment '删除标志', - primary key (application_id) -) engine=innodb comment = '培训申请表'; - --- ---------------------------- --- 培训审批表 --- ---------------------------- -drop table if exists rs_training_approval; -create table rs_training_approval ( - approval_id bigint(20) not null comment '审批ID', - application_id bigint(20) not null comment '申请ID', - approver_id bigint(20) not null comment '审批人ID', - approver_name varchar(50) not null comment '审批人姓名', - approval_level int(11) not null comment '审批级别(1部门主管 2分管领导 3校领导)', - status char(1) default '0' comment '状态(0待审核 1已通过 2已拒绝)', - opinion varchar(500) comment '审批意见', - approval_time datetime comment '审批时间', - tenant_id varchar(20) default '000000' comment '租户编号', - create_by bigint(20) comment '创建者', - create_time datetime comment '创建时间', - update_by bigint(20) comment '更新者', - update_time datetime comment '更新时间', - remark varchar(500) comment '备注', - deleted char(1) default '0' comment '删除标志', - primary key (approval_id) -) engine=innodb comment = '培训审批表'; - --- ---------------------------- --- 培训记录表 --- ---------------------------- -drop table if exists rs_training_record; -create table rs_training_record ( - record_id bigint(20) not null comment '记录ID', - application_id bigint(20) not null comment '申请ID', - course_id bigint(20) not null comment '课程ID', - user_id bigint(20) not null comment '用户ID', - user_name varchar(50) not null comment '用户姓名', - completion_status char(1) default '0' comment '完成状态(0进行中 1已完成 2未完成)', - completion_time datetime comment '完成时间', - score decimal(5,2) comment '成绩', - certificate_path varchar(255) comment '证书路径', - certificate_number varchar(50) comment '证书编号', - attachment_ids varchar(255) comment '证书附件ID(多个用逗号分隔)', - feedback text comment '培训反馈', - location varchar(255) comment '培训地点', - duration int(11) comment '学时', - apply_time datetime comment '申请时间', - status varchar(10) comment '申请状态', - course_name varchar(255) comment '课程名称', - department varchar(255) comment '所属部门', - position varchar(255) comment '职位', - reason varchar(255) comment '申请理由', - current_approver varchar(255) comment '当前审批人', - start_time datetime comment '开始时间', - end_time datetime comment '结束时间', - tenant_id varchar(20) default '000000' comment '租户编号', - create_dept bigint(20) comment '创建部门', - create_by bigint(20) comment '创建者', - create_time datetime comment '创建时间', - update_by bigint(20) comment '更新者', - update_time datetime comment '更新时间', - remark varchar(500) comment '备注', - deleted char(1) default '0' comment '删除标志', - primary key (record_id) -) engine=innodb comment = '培训记录表'; \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/update-custom-course-name.sql b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/update-custom-course-name.sql deleted file mode 100644 index af90c53a..00000000 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/update-custom-course-name.sql +++ /dev/null @@ -1,5 +0,0 @@ --- 添加自定义课程名称字段 --- 创建于 2025-07-24 - --- 在rs_training_application表中添加course_name列,用于存储自定义培训名称 -ALTER TABLE `rs_training_application` ADD COLUMN `course_name` varchar(100) DEFAULT NULL COMMENT '自定义课程名称' AFTER `course_id`; \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/update-rs-application-table.sql b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/update-rs-application-table.sql deleted file mode 100644 index 4407c026..00000000 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/update-rs-application-table.sql +++ /dev/null @@ -1,7 +0,0 @@ --- 为rs_application表添加附件相关字段 -ALTER TABLE `rs_application` -ADD COLUMN `attachment_path` varchar(255) DEFAULT NULL COMMENT '附件路径' AFTER `self_description`, -ADD COLUMN `attachment_name` varchar(255) DEFAULT NULL COMMENT '附件名称' AFTER `attachment_path`; - --- 更新表注释 -ALTER TABLE `rs_application` COMMENT '应聘申请记录表'; \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/sql/update_party_retention_file_oss_id.sql b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/sql/update_party_retention_file_oss_id.sql new file mode 100644 index 00000000..f6bb1ab1 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/sql/update_party_retention_file_oss_id.sql @@ -0,0 +1,15 @@ +-- ============================================= +-- 修改 rs_overseas_application 表结构 +-- 将 party_retention_file_oss_id 字段从 bigint 改为 varchar(500) +-- 以支持存储多个附件OSS ID(逗号分隔) +-- 创建时间: 2025-11-27 +-- ============================================= + +-- 修改字段类型(MySQL/MariaDB) +ALTER TABLE rs_overseas_application +MODIFY COLUMN party_retention_file_oss_id VARCHAR(500) COMMENT '党籍保留附件OSS ID(多个ID用逗号分隔)'; + +-- 注意: +-- 1. 此脚本会保留现有数据,bigint值会自动转换为字符串 +-- 2. 建议在执行前备份数据库 +-- 3. 如果字段当前有NOT NULL约束,会被保留 diff --git a/docs/开发文档/API/出国申请管理API.md b/docs/开发文档/API/出国申请管理API.md index 7cc420f4..e8044246 100644 --- a/docs/开发文档/API/出国申请管理API.md +++ b/docs/开发文档/API/出国申请管理API.md @@ -316,7 +316,7 @@ interface OverseasApprovalVO { approvalId: number | string; // 审批ID applicationId: number; // 关联申请ID approverId: number; // 审批人ID - approverName: string; // 审批人姓名 + approverName: string; // 审批人姓名(优先返回昵称,缺省时回退账号) approvalLevel: string; // 审批级别编码 approvalStatus: string; // 审批状态编码 approvalOpinion: string; // 审批意见 @@ -329,6 +329,8 @@ interface OverseasApprovalVO { } ``` +> 🆕 **2025-11-28 更新**:`approverName` 字段统一返回审批人的昵称,若用户未设置昵称则自动回退到账号,确保审批记录界面展示真实姓名。 + --- ## 🔧 核心接口详细说明 @@ -1048,6 +1050,7 @@ PDF模板包含以下空白填写区域(完全手工填写): - 申请表单:党员且出国天数>30天时自动显示 - 申请详情:符合条件时在弹窗底部显示绿色按钮 - **前端实现**:必须使用 `request` 工具而不是 `fetch` API,以确保包含所有必要的认证请求头 +- **附件格式**:自 2025-11-27 起,党籍保留附件支持任意文件类型上传,仅保留 10MB 单文件大小限制 #### 错误处理 @@ -1242,4 +1245,20 @@ import { getCurrentApproversForTask } from '@/api/workflow/common'; **更新时间**:2025-10-28 **维护人员**:开发团队 -**变更记录**:新增工作流动态审批人方案说明 \ No newline at end of file +**变更记录**:新增工作流动态审批人方案说明 + +--- + +### 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`)时显示导出按钮,生成失败时给出友好提示并引导检查模板。 \ No newline at end of file diff --git a/docs/开发文档/API/出国行程变更管理API.md b/docs/开发文档/API/出国行程变更管理API.md index 43d7cb6b..0f9599fb 100644 --- a/docs/开发文档/API/出国行程变更管理API.md +++ b/docs/开发文档/API/出国行程变更管理API.md @@ -144,6 +144,10 @@ } ``` +#### 字段补充说明(2025-11-28) + +- `approverName`:后台统一返回审批人的 **昵称**,若用户未设置昵称则自动回退到账号;前端无需再做用户名到昵称的手动映射。 + --- ### 3. 查询待审批列表 diff --git a/web/package.json b/web/package.json index 5e29e2d3..b636f0ca 100644 --- a/web/package.json +++ b/web/package.json @@ -34,9 +34,12 @@ "await-to-js": "3.0.0", "axios": "1.8.4", "crypto-js": "4.2.0", + "docx": "^9.5.1", + "docxtemplater": "^3.67.5", + "docxtemplater-image-module-free": "^1.1.1", "echarts": "^5.6.0", "element-plus": "2.9.8", - "file-saver": "2.0.5", + "file-saver": "^2.0.5", "highlight.js": "11.9.0", "html2canvas": "^1.4.1", "image-conversion": "2.1.1", @@ -46,6 +49,7 @@ "jspdf-font": "^1.0.7", "nprogress": "0.2.0", "pinia": "3.0.2", + "pizzip": "^3.2.0", "qrcode": "^1.5.4", "screenfull": "6.0.2", "vue": "3.5.13", diff --git a/web/public/baoliudangji.docx b/web/public/baoliudangji.docx new file mode 100644 index 00000000..d54fc870 Binary files /dev/null and b/web/public/baoliudangji.docx differ diff --git a/web/public/chuguodengji.docx b/web/public/chuguodengji.docx new file mode 100644 index 00000000..1aaccf43 Binary files /dev/null and b/web/public/chuguodengji.docx differ diff --git a/web/public/chuguojingshenpibiao.docx b/web/public/chuguojingshenpibiao.docx new file mode 100644 index 00000000..04e9e130 Binary files /dev/null and b/web/public/chuguojingshenpibiao.docx differ diff --git a/web/public/chuguoqingkuanghuizhi.docx b/web/public/chuguoqingkuanghuizhi.docx new file mode 100644 index 00000000..7c1e4e19 Binary files /dev/null and b/web/public/chuguoqingkuanghuizhi.docx differ diff --git a/web/public/zunshoudangjichengnuoshu.docx b/web/public/zunshoudangjichengnuoshu.docx new file mode 100644 index 00000000..ed40ebc9 Binary files /dev/null and b/web/public/zunshoudangjichengnuoshu.docx differ diff --git a/web/src/api/rs/overseas-registration.ts b/web/src/api/rs/overseas-registration.ts index b97c832c..fdc069ae 100644 --- a/web/src/api/rs/overseas-registration.ts +++ b/web/src/api/rs/overseas-registration.ts @@ -70,6 +70,7 @@ export interface OverseasRegistrationForm { officePhone?: string; // 办公电话 mobilePhone?: string; // 移动电话 remarks?: string; // 备注 + status?: string; // 登记状态:0-草稿,1-待审核,2-已通过,3-已拒绝 } // 登记历史查询参数类型 @@ -142,6 +143,17 @@ export function getOverseasRegistrationList(query: OverseasRegistrationQuery) { }); } +/** + * 查询我的出国登记列表(只查询当前登录人创建的记录) + */ +export function getMyOverseasRegistrationList(query: OverseasRegistrationQuery) { + return request({ + url: '/rs/overseas/registration/my/list', + method: 'get', + params: query + }); +} + /** * 查询出国登记详细 */ @@ -433,4 +445,4 @@ export function batchRejectOverseasRegistration(registrationIds: Array; } @@ -842,8 +853,8 @@ export interface ItineraryChangeVO { status: string; statusText: string; approvalOpinion?: string; - approvalBy?: number; - approvalByName?: string; + approverId?: number; + approverName?: string; approvalTime?: string; createTime: string; updateTime?: string; diff --git a/web/src/permission.ts b/web/src/permission.ts index 30dea85a..48d071f7 100644 --- a/web/src/permission.ts +++ b/web/src/permission.ts @@ -34,7 +34,7 @@ router.beforeEach(async (to, from, next) => { const [err] = await tos(useUserStore().getInfo()); if (err) { await useUserStore().logout(); - ElMessage.error(err); + // ElMessage.error(err.message || '获取用户信息失败'); next({ path: '/' }); } else { isRelogin.show = false; diff --git a/web/src/utils/request.ts b/web/src/utils/request.ts index 9559c259..7ae6f71b 100644 --- a/web/src/utils/request.ts +++ b/web/src/utils/request.ts @@ -204,8 +204,14 @@ service.interceptors.response.use( err.response = res; return Promise.reject(err); } else if (code !== HttpStatus.SUCCESS) { - ElNotification.error({ title: msg }); - const err = new Error(msg) as any; + // 确保错误消息不为空,避免只显示图标没有文字 + const errorMsg = msg || errorCode['default'] || '操作失败,请稍后重试'; + ElNotification.error({ + title: '错误提示', + message: errorMsg, + duration: 5000 + }); + const err = new Error(errorMsg) as any; err.response = res; return Promise.reject(err); } else { diff --git a/web/src/views/login.vue b/web/src/views/login.vue index c80e5f5e..73e68f6f 100644 --- a/web/src/views/login.vue +++ b/web/src/views/login.vue @@ -192,13 +192,29 @@ const getLoginData = () => { * 获取租户列表 */ const initTenantList = async () => { - const { data } = await getTenantList(false); - tenantEnabled.value = data.tenantEnabled === undefined ? true : data.tenantEnabled; - if (tenantEnabled.value) { - tenantList.value = data.voList; - if (tenantList.value != null && tenantList.value.length !== 0) { - loginForm.value.tenantId = tenantList.value[0].tenantId; + try { + const [err, res] = await to(getTenantList(false)); + if (err) { + // 静默处理租户列表获取失败,不影响登录功能 + console.warn('获取租户列表失败:', err); + // 设置默认值,确保登录功能正常 + tenantEnabled.value = true; + return; } + if (res && res.data) { + const { data } = res; + tenantEnabled.value = data.tenantEnabled === undefined ? true : data.tenantEnabled; + if (tenantEnabled.value) { + tenantList.value = data.voList; + if (tenantList.value != null && tenantList.value.length !== 0) { + loginForm.value.tenantId = tenantList.value[0].tenantId; + } + } + } + } catch (error) { + // 捕获其他可能的错误,静默处理 + console.warn('初始化租户列表时发生错误:', error); + tenantEnabled.value = true; } }; diff --git a/web/src/views/rs/overseas-registration/index-all.vue b/web/src/views/rs/overseas-registration/index-all.vue new file mode 100644 index 00000000..1c775028 --- /dev/null +++ b/web/src/views/rs/overseas-registration/index-all.vue @@ -0,0 +1,1138 @@ + + + + + diff --git a/web/src/views/rs/overseas-registration/index.vue b/web/src/views/rs/overseas-registration/index.vue index 1b898610..28ba279c 100644 --- a/web/src/views/rs/overseas-registration/index.vue +++ b/web/src/views/rs/overseas-registration/index.vue @@ -1,316 +1,171 @@ - \ No newline at end of file + diff --git a/web/src/views/rs/overseas/approval.vue b/web/src/views/rs/overseas/approval.vue index b8171c2c..6a5a326a 100644 --- a/web/src/views/rs/overseas/approval.vue +++ b/web/src/views/rs/overseas/approval.vue @@ -9,12 +9,7 @@ - + @@ -49,254 +44,317 @@
- - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + +
- + - - - {{ viewData.applicantName || '-' }} - {{ viewData.gender === '0' ? '男' : viewData.gender === '1' ? '女' : '-' }} - - {{ formatDate(viewData.birthDate) || '-' }} - {{ viewData.politicalStatusText || '-' }} - - - {{ viewData.department || '-' }} - {{ viewData.professionalTitle || '-' }} - - {{ viewData.position || '-' }} - {{ viewData.documentName || '-' }} - - - {{ viewData.documentNumber || '-' }} - {{ viewData.destinationCountry || viewData.destination || '-' }} - - - {{ viewData.exitReason || '-' }} - - - {{ viewData.visitRelationship || '-' }} - - - - {{ viewData.itinerary || '-' }} - {{ viewData.duration || 0 }}天 - - - {{ viewData.remarks }} - - - {{ formatDate(viewData.startDate) || '-' }} - {{ formatDate(viewData.endDate) || '-' }} - - - {{ viewData.mobilePhone || viewData.contactPhone || '-' }} - {{ viewData.emergencyContact || '-' }} - - - - {{ viewData.statusText }} - - {{ viewData.applyTime || '-' }} - - - {{ viewData.adminRemark }} - +
+ +
+
个人信息
+ + + + {{ viewData.applicantName || '-' }} + + + + + {{ viewData.gender === '0' ? '男' : viewData.gender === '1' ? '女' : '-' }} + + + + + {{ formatYearMonth(viewData.birthDate) || '-' }} + + + + + + + {{ viewData.politicalStatusText || '-' }} + + + + + {{ viewData.registeredAddress || '-' }} + + + + + {{ viewData.professionalTitle || '-' }} + + + +
- -
- -
-
- - 出国境登记表盖章文件 +
+
岗位与证件
+ + + + {{ viewData.position || '-' }} + + + + + {{ viewData.documentName || '-' }} + + + + + {{ viewData.documentNumber || '-' }} + + +
-
-
- - - {{ viewData.stampedRegistrationFileName }} - - - 上传时间: {{ viewData.stampedFileUploadTime }} - -
-
- +
出行信息
+ + + + {{ viewData.destinationCountry || viewData.destination || '-' }} + + + + + {{ viewData.exitReason || '-' }} + + + + + {{ viewData.visitRelationship || '-' }} + + + + + + + {{ viewData.itinerary && viewData.itinerary !== '未填写' ? viewData.itinerary : '-' }} + + + +
+ +
+
承诺声明
+ + +
+ 本人承诺,在出国(境)期间遵守当地法律法规和中国的法律法规,结束后如期回国并到学校报到,如逾期不归,同意作自动离职处理。 +
+
+
+
+ +
+
手写签字
+ + +
+ 手写签名 +
+
+
+
+ +
+
时间安排
+ + + + {{ formatDate(viewData.startDate) || '-' }} + + + + + {{ formatDate(viewData.endDate) || '-' }} + + + + + {{ viewData.duration || 0 }}天 + + + +
+ +
+
联系方式
+ + + + {{ viewData.mobilePhone || viewData.contactPhone || '-' }} + + + + + + {{ viewData.emergencyContact || '-' }} + + + +
+ +
+
状态信息
+ + + + {{ viewData.statusText || '-' }} + + + + + {{ viewData.applyTime || '-' }} + + + + + + + {{ viewData.remarks }} + + + + + {{ viewData.adminRemark }} + + + +
+ + +
+
+ + 审批流程信息 +
+
+ + + - 查看 - - - 下载 - +
+
+ {{ getApprovalLevelText(record) }} + + {{ record.approvalStatusText || getApprovalResultText(record.approvalStatus) }} + +
+
+ 审批人:{{ record.approverName || '-' }} + 审批时间:{{ parseTime(record.approvalTime) }} +
+
审批意见:{{ record.approvalOpinion }}
+
+
+
+
+
+ +
+
+ + 申请附件 +
+
+
+
+ +
+ + {{ getFileExt(file.name || '') }} +
+
+
+ {{ file.name || '附件' }} +
+
上传时间:{{ parseTime(file.uploadTime) }}
+
+ 下载 +
- - -
-
- - 党籍保留附件 -
-
-
- - - {{ viewData.partyRetentionFileName }} - - - 上传时间: {{ viewData.partyRetentionUploadTime }} - -
-
- - 查看 - - - 下载 - -
-
+ +
+
- - -
-
- - 其他申请附件 -
- - - - - - - - - - - - - -
- - -
- -
diff --git a/web/src/views/rs/overseas/itinerary-change-approval.vue b/web/src/views/rs/overseas/itinerary-change-approval.vue index a39dc44b..b2bcf37f 100644 --- a/web/src/views/rs/overseas/itinerary-change-approval.vue +++ b/web/src/views/rs/overseas/itinerary-change-approval.vue @@ -1,30 +1,6 @@ - - - 前往待办页面 - - - - + + @@ -190,7 +161,7 @@ - + @@ -522,7 +518,8 @@ import { ref, reactive, toRefs, onMounted, computed, watch } from 'vue'; import { ElMessage, ElMessageBox } from 'element-plus'; import animate from '@/animate'; import { parseTime } from '@/utils/ruoyi'; -import { +import { useUserStore } from '@/store/modules/user'; +import { listItineraryChange, listMyItineraryChange, getItineraryChange, @@ -535,6 +532,7 @@ import { getOverseasApplication, getItineraryChangeHistory, getOverseasRecordByApplication, + listOverseasApplication, type ItineraryChangeQuery, type ItineraryChangeVO, type ItineraryChangeForm, @@ -553,7 +551,7 @@ const formRef = ref(); const state = reactive({ // Tab切换 activeTab: 'all', - + // 全部变更列表 loading: false, showSearch: true, @@ -565,9 +563,10 @@ const state = reactive({ pageSize: 10, applicantName: '', destination: '', - status: '' + status: '', + applicantId: undefined as number | undefined } as ItineraryChangeQuery, - + // 我的变更列表 myLoading: false, showMySearch: true, @@ -577,9 +576,10 @@ const state = reactive({ pageNum: 1, pageSize: 10, destination: '', - status: '' + status: '', + applicantId: undefined as number | undefined } as ItineraryChangeQuery, - + // 对话框 dialog: { visible: false, @@ -591,21 +591,21 @@ const state = reactive({ historyDialog: { visible: false }, - + // 表单数据 form: {} as ItineraryChangeForm, viewData: null as ItineraryChangeVO | null, - + // 变更历史 historyLoading: false, historyList: [] as ItineraryChangeVO[], - + // 可用的出国申请列表(已审批通过) availableApplicationList: [] as OverseasApplicationVO[], - + // 变更类型(多选) changeTypes: [] as string[], - + // 表单验证规则 rules: { applicationId: [{ required: true, message: '请选择原出国申请', trigger: 'change' }], @@ -690,7 +690,7 @@ onMounted(() => { const handleTabClick = (tab: any) => { const tabName = tab.props?.name || tab.paneName; state.activeTab = tabName; - + if (tabName === 'all') { getList(); } else if (tabName === 'my') { @@ -698,18 +698,36 @@ const handleTabClick = (tab: any) => { } }; -// 查询变更申请列表(只显示待审核的数据) +// 查询变更申请列表(只显示当前登录人的待审核和已撤回的数据) const getList = async () => { state.loading = true; try { - // 如果用户没有手动选择状态,默认只显示待审核的申请 - const params = { ...state.queryParams }; - if (!params.status) { - params.status = '0'; // 默认只显示待审核状态 - } + const userStore = useUserStore(); + // 先获取所有当前登录人的数据(不分页),然后在前端过滤和分页 + const params = { + pageNum: 1, + pageSize: 10000, // 设置一个较大的值,获取所有数据 + applicantId: Number(userStore.userId), + applicantName: state.queryParams.applicantName, + destination: state.queryParams.destination + }; + const response = await listItineraryChange(params); - state.changeList = response.rows; - state.total = response.total; + + // 前端过滤:只显示状态为 0(待审核)和 3(已撤回)的数据 + const filteredRows = response.rows.filter((item: ItineraryChangeVO) => { + return item.status === '0' || item.status === '3'; + }); + + // 前端分页 + const pageNum = state.queryParams.pageNum; + const pageSize = state.queryParams.pageSize; + const total = filteredRows.length; + const start = (pageNum - 1) * pageSize; + const end = Math.min(start + pageSize, total); + + state.changeList = filteredRows.slice(start, end); + state.total = total; } catch (error) { console.error('获取变更行程列表失败:', error); } finally { @@ -717,13 +735,35 @@ const getList = async () => { } }; -// 查询我的变更记录列表(只显示已通过的变更申请) +// 查询我的变更记录列表(只显示当前登录人的已通过和已拒绝的数据) const getMyList = async () => { state.myLoading = true; try { - const response = await listMyItineraryChange(state.myQueryParams); - state.myChangeList = response.rows; - state.myTotal = response.total; + const userStore = useUserStore(); + // 先获取所有当前登录人的数据(不分页),然后在前端过滤和分页 + const params = { + pageNum: 1, + pageSize: 10000, // 设置一个较大的值,获取所有数据 + applicantId: Number(userStore.userId), + destination: state.myQueryParams.destination + }; + + const response = await listItineraryChange(params); + + // 前端过滤:只显示状态为 1(已通过)和 2(已拒绝)的数据 + const filteredRows = response.rows.filter((item: ItineraryChangeVO) => { + return item.status === '1' || item.status === '2'; + }); + + // 前端分页 + const pageNum = state.myQueryParams.pageNum; + const pageSize = state.myQueryParams.pageSize; + const total = filteredRows.length; + const start = (pageNum - 1) * pageSize; + const end = Math.min(start + pageSize, total); + + state.myChangeList = filteredRows.slice(start, end); + state.myTotal = total; } catch (error) { console.error('获取我的变更记录失败:', error); } finally { @@ -731,13 +771,25 @@ const getMyList = async () => { } }; -// 获取已审批通过的出国申请列表 +// 获取已审批通过的出国申请列表(只显示当前登录人的) const getApprovedApplicationList = async () => { try { - const response = await getApprovedOverseasApplications(); - state.availableApplicationList = response.data; + const userStore = useUserStore(); + // 使用listOverseasApplication查询当前登录人的已通过申请 + const response = await listOverseasApplication({ + pageNum: 1, + pageSize: 1000, // 设置一个较大的值,获取所有数据 + applicantId: Number(userStore.userId), // 只查询当前登录人的申请 + status: '1' // 只查询已通过的状态 + }); + + // 确保只返回已通过审批的申请 + state.availableApplicationList = response.rows.filter((item: OverseasApplicationVO) => { + return item.status === '1'; + }); } catch (error) { console.error('获取已审批通过的出国申请列表失败:', error); + ElMessage.error('获取已审批通过的出国申请列表失败'); } }; @@ -798,30 +850,30 @@ const handleApplicationChange = async (applicationId: number) => { // 1. 获取出国申请详情 const response = await getOverseasApplication(applicationId); const selectedApplication = response.data; - + // 2. 根据申请ID获取出国记录(包含recordId) const recordResponse = await getOverseasRecordByApplication(applicationId); const recordData = recordResponse.data; - + if (selectedApplication && recordData) { // 设置recordId(关键字段,必填) state.form.recordId = recordData.recordId; - + // 设置申请人ID(关键字段,必填) state.form.applicantId = selectedApplication.applicantId; - + // 填充本人基本情况 state.form.applicantName = selectedApplication.applicantName; state.form.gender = selectedApplication.gender; state.form.birthDate = selectedApplication.birthDate; // 政治面貌:将字典值转换为文本标签 - state.form.politicalStatus = getPoliticalStatusLabel(selectedApplication.politicalStatus); + state.form.politicalStatus = selectedApplication.politicalStatus ? getPoliticalStatusLabel(selectedApplication.politicalStatus) : ''; state.form.professionalTitle = selectedApplication.professionalTitle; state.form.position = selectedApplication.position; state.form.documentName = selectedApplication.documentName; state.form.documentNumber = selectedApplication.documentNumber; state.form.department = selectedApplication.department; - + // 填充申请行程情况 // 前往国家:优先使用destinationCountry,其次使用destination state.form.originalDestination = selectedApplication.destinationCountry || selectedApplication.destination; @@ -833,7 +885,7 @@ const handleApplicationChange = async (applicationId: number) => { state.form.originalStartDate = selectedApplication.startDate; state.form.originalEndDate = selectedApplication.endDate; state.form.originalDuration = selectedApplication.duration; - + // 默认将原信息复制到新信息 state.form.newDestination = selectedApplication.destinationCountry || selectedApplication.destination; state.form.newDestinationCity = selectedApplication.destinationCity; @@ -867,22 +919,24 @@ const handleCancel = (row: ItineraryChangeVO) => { confirmButtonText: '确定', cancelButtonText: '取消', inputPlaceholder: '请输入撤回原因' - }).then(async ({ value }) => { - try { - await cancelItineraryChange(row.changeId, value); - ElMessage.success('撤回成功'); - - // 根据当前Tab刷新列表 - if (state.activeTab === 'all') { - getList(); - } else if (state.activeTab === 'my') { - getMyList(); + }) + .then(async ({ value }) => { + try { + await cancelItineraryChange(row.changeId, value); + ElMessage.success('撤回成功'); + + // 根据当前Tab刷新列表 + if (state.activeTab === 'all') { + getList(); + } else if (state.activeTab === 'my') { + getMyList(); + } + } catch (error) { + console.error('撤回失败:', error); + ElMessage.error('撤回失败'); } - } catch (error) { - console.error('撤回失败:', error); - ElMessage.error('撤回失败'); - } - }).catch(() => {}); + }) + .catch(() => {}); }; // 重新提交 @@ -890,12 +944,12 @@ const handleResubmit = async (row: ItineraryChangeVO) => { try { const response = await getItineraryChange(row.changeId); Object.assign(state.form, response.data); - + // 解析变更类型 if (response.data.changeType) { state.changeTypes = response.data.changeType.split(','); } - + state.dialog.title = '重新提交行程变更申请'; state.dialog.visible = true; } catch (error) { @@ -910,30 +964,32 @@ const handleDelete = (row: ItineraryChangeVO) => { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' - }).then(async () => { - try { - await delItineraryChange(row.changeId); - ElMessage.success('删除成功'); - - // 根据当前Tab刷新列表(如果删除的是当前页最后一条且不是第1页,返回上一页) - if (state.activeTab === 'all') { - // 如果删除的是当前页最后一条且不是第1页,返回上一页 - if (state.changeList.length === 1 && state.queryParams.pageNum > 1) { - state.queryParams.pageNum--; + }) + .then(async () => { + try { + await delItineraryChange(row.changeId); + ElMessage.success('删除成功'); + + // 根据当前Tab刷新列表(如果删除的是当前页最后一条且不是第1页,返回上一页) + if (state.activeTab === 'all') { + // 如果删除的是当前页最后一条且不是第1页,返回上一页 + if (state.changeList.length === 1 && state.queryParams.pageNum > 1) { + state.queryParams.pageNum--; + } + getList(); + } else if (state.activeTab === 'my') { + // 如果删除的是当前页最后一条且不是第1页,返回上一页 + if (state.myChangeList.length === 1 && state.myQueryParams.pageNum > 1) { + state.myQueryParams.pageNum--; + } + getMyList(); } - getList(); - } else if (state.activeTab === 'my') { - // 如果删除的是当前页最后一条且不是第1页,返回上一页 - if (state.myChangeList.length === 1 && state.myQueryParams.pageNum > 1) { - state.myQueryParams.pageNum--; - } - getMyList(); + } catch (error) { + console.error('删除失败:', error); + ElMessage.error('删除失败'); } - } catch (error) { - console.error('删除失败:', error); - ElMessage.error('删除失败'); - } - }).catch(() => {}); + }) + .catch(() => {}); }; // 查看变更历史 @@ -963,15 +1019,14 @@ const submitForm = () => { if (state.form.newExitReason !== state.form.originalExitReason) { changeTypeList.push('出境事由变更'); } - if (state.form.newStartDate !== state.form.originalStartDate || - state.form.newEndDate !== state.form.originalEndDate) { + if (state.form.newStartDate !== state.form.originalStartDate || state.form.newEndDate !== state.form.originalEndDate) { changeTypeList.push('时间变更'); } if (state.form.newItinerary && state.form.newItinerary !== state.form.originalItinerary) { changeTypeList.push('行程变更'); } state.form.changeType = changeTypeList.length > 0 ? changeTypeList.join(',') : '其他变更'; - + state.submitLoading = true; try { if (state.form.changeId) { @@ -982,7 +1037,7 @@ const submitForm = () => { ElMessage.success('提交成功!变更申请已进入审批流程。'); } state.dialog.visible = false; - + // 根据当前Tab刷新列表(重置到第1页) if (state.activeTab === 'all') { state.queryParams.pageNum = 1; @@ -1056,11 +1111,11 @@ const getStatusTagType = (status: string): 'primary' | 'success' | 'info' | 'war const typeMap: Record = { '0': 'warning', // 待审核 '1': 'success', // 已通过 - '2': 'danger' // 已拒绝 + '2': 'danger', // 已拒绝 + '3': 'info' // 已撤回 }; return typeMap[status] || 'info'; }; - - - diff --git a/web/src/views/rs/overseas/monitor/index.vue b/web/src/views/rs/overseas/monitor/index.vue index 6dede570..7bcc83d5 100644 --- a/web/src/views/rs/overseas/monitor/index.vue +++ b/web/src/views/rs/overseas/monitor/index.vue @@ -49,24 +49,24 @@
- - - + + + - - + + @@ -350,8 +350,16 @@ -