diff --git a/backend/ruoyi-modules/ruoyi-rs/pom.xml b/backend/ruoyi-modules/ruoyi-rs/pom.xml index d29f672e..2d6e79c6 100644 --- a/backend/ruoyi-modules/ruoyi-rs/pom.xml +++ b/backend/ruoyi-modules/ruoyi-rs/pom.xml @@ -116,6 +116,13 @@ itextpdf 5.5.13.3 + + + + com.itextpdf + itext-asian + 5.2.0 + 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 7b101631..df6df958 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 @@ -27,14 +27,23 @@ import org.dromara.common.excel.utils.ExcelUtil; import org.dromara.common.satoken.utils.LoginHelper; import cn.dev33.satoken.annotation.SaCheckPermission; import org.dromara.rs.domain.bo.RsOverseasApplicationBo; +import org.dromara.rs.domain.bo.GenerateRegistrationFormBo; import org.dromara.rs.domain.vo.RsOverseasApplicationVo; import org.dromara.rs.domain.vo.RsOverseasApprovalVo; +import org.dromara.rs.domain.vo.RegistrationFormVO; import org.dromara.rs.domain.query.RsOverseasApplicationQuery; import org.dromara.rs.service.IRsOverseasApplicationService; import org.dromara.rs.service.IRsOverseasApprovalService; +import org.dromara.rs.service.IRegistrationFormService; import java.util.Date; import java.util.Map; import java.util.HashMap; +import org.springframework.web.multipart.MultipartFile; +import org.dromara.common.oss.core.OssClient; +import org.dromara.common.oss.factory.OssFactory; +import org.dromara.common.oss.entity.UploadResult; +import java.io.IOException; +import java.text.SimpleDateFormat; /** * 出国申请Controller @@ -53,6 +62,9 @@ public class RsOverseasApplicationController extends BaseController { @Autowired private IRsOverseasApprovalService overseasApprovalService; + @Autowired + private IRegistrationFormService registrationFormService; + /** * 查询出国申请列表 */ @@ -139,11 +151,27 @@ public class RsOverseasApplicationController extends BaseController { ExcelUtil.exportExcel(list, "出国申请数据", RsOverseasApplicationVo.class, response); } + /** + * 下载登记备案表模板 + */ + @GetMapping("/downloadRegistrationTemplate") + public void downloadRegistrationTemplate(HttpServletResponse response) { + registrationFormService.downloadRegistrationTemplate(response); + } + + /** + * 下载保留党籍申请模板 + */ + @GetMapping("/downloadPartyMembershipTemplate") + public void downloadPartyMembershipTemplate(HttpServletResponse response) { + registrationFormService.downloadPartyMembershipTemplate(response); + } + /** * 获取出国申请详细信息 */ @SaCheckPermission("rs:overseas:query") - @GetMapping("/{applicationId}") + @GetMapping("/detail/{applicationId}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long applicationId) { return R.ok(applicationService.queryById(applicationId)); } @@ -155,6 +183,14 @@ public class RsOverseasApplicationController extends BaseController { @Log(title = "出国申请", businessType = BusinessType.INSERT) @PostMapping public R add(@Validated(AddGroup.class) @RequestBody RsOverseasApplicationBo bo) { + // 🔍 调试日志:检查接收到的关键字段 + System.out.println("========== Controller 接收到的数据 =========="); + System.out.println("emergencyContact: " + bo.getEmergencyContact()); + System.out.println("destination: " + bo.getDestination()); + System.out.println("idNumber: " + bo.getIdNumber()); + System.out.println("unitContact: " + bo.getUnitContact()); + System.out.println("==============================================="); + // 自动设置申请人信息 bo.setApplicantId(LoginHelper.getUserId()); bo.setApplicantName(LoginHelper.getUsername()); @@ -192,6 +228,48 @@ public class RsOverseasApplicationController extends BaseController { @Log(title = "提交出国申请", businessType = BusinessType.UPDATE) @PutMapping("/submit/{applicationId}") public R submit(@NotNull(message = "申请ID不能为空") @PathVariable Long applicationId) { + // 获取申请详情进行校验 + RsOverseasApplicationVo application = applicationService.queryById(applicationId); + if (application == null) { + return R.fail("申请不存在"); + } + + // 1. 校验出国登记前置条件(近3个月是否有已通过的出国登记) + java.util.Map registrationValidation = + applicationService.validateRegistrationPrerequisite(application.getApplicantId()); + Boolean registrationValid = (Boolean) registrationValidation.get("isValid"); + if (!registrationValid) { + String message = (String) registrationValidation.get("message"); + return R.fail(message); + } + + // 2. 校验党籍保留前置条件(中共党员且出国天数>30天) + java.util.Map retentionValidation = + applicationService.validatePartyMembershipRetentionPrerequisite( + application.getApplicantId(), + application.getPoliticalStatus(), + application.getDuration() + ); + Boolean retentionValid = (Boolean) retentionValidation.get("isValid"); + if (!retentionValid) { + String message = (String) retentionValidation.get("message"); + return R.fail(message); + } + + // 3. 校验是否上传了盖章文件 + if (application.getStampedFileUploadStatus() == null || + !"1".equals(application.getStampedFileUploadStatus())) { + return R.fail("请先下载登记备案附件填写盖章后上传,才能提交申请!"); + } + + // 4. 如果需要党籍保留且已通过,检查是否上传了保留党籍附件 + Boolean needRetention = (Boolean) retentionValidation.get("needRetention"); + if (needRetention && retentionValid) { + // TODO: 添加保留党籍附件上传校验(需要在数据库和实体类中添加字段) + // 当前版本暂不强制校验保留党籍附件 + } + + // 所有校验通过,执行提交操作 return toAjax(applicationService.submitApplication(applicationId)); } @@ -206,6 +284,35 @@ public class RsOverseasApplicationController extends BaseController { return toAjax(applicationService.resubmitApplication(applicationId, bo)); } + /** + * 校验出国申请前置条件 + * 用于前端在填写表单时实时提示用户 + */ + @SaCheckPermission("rs:overseas:query") + @GetMapping("/validatePrerequisites") + public R> validatePrerequisites( + @RequestParam(required = false) String politicalStatus, + @RequestParam(required = false) Integer duration) { + + Long applicantId = LoginHelper.getUserId(); + java.util.Map result = new java.util.HashMap<>(); + + // 1. 校验出国登记前置条件 + java.util.Map registrationValidation = + applicationService.validateRegistrationPrerequisite(applicantId); + result.put("registrationValidation", registrationValidation); + + // 2. 校验党籍保留前置条件(如果提供了政治面貌和出国天数) + if (politicalStatus != null && duration != null) { + java.util.Map retentionValidation = + applicationService.validatePartyMembershipRetentionPrerequisite( + applicantId, politicalStatus, duration); + result.put("retentionValidation", retentionValidation); + } + + return R.ok(result); + } + /** * 取消出国申请 */ @@ -298,4 +405,165 @@ public class RsOverseasApplicationController extends BaseController { this.approvalOpinion = approvalOpinion; } } + + // ==================== PDF生成相关接口 ==================== + + /** + * 生成出国申请登记表PDF + */ + @SaCheckPermission("rs:overseas:generate") + @PostMapping("/generateRegistrationForm") + @Log(title = "生成出国申请登记表", businessType = BusinessType.OTHER) + public R generateRegistrationForm(@RequestBody @Validated GenerateRegistrationFormBo bo) { + RegistrationFormVO result = registrationFormService.generateRegistrationFormPdf(bo); + return R.ok(result); + } + + /** + * 下载生成的PDF文档 + */ + @SaCheckPermission("rs:overseas:download") + @GetMapping("/downloadPdf/{documentId}") + public void downloadPdf(@PathVariable String documentId, HttpServletResponse response) { + registrationFormService.downloadPdf(documentId, response); + } + + /** + * 预览生成的PDF文档 + */ + @SaCheckPermission("rs:overseas:preview") + @GetMapping("/previewPdf/{documentId}") + public void previewPdf(@PathVariable String documentId, HttpServletResponse response) { + registrationFormService.downloadPdf(documentId, response); + } + + // ==================== 盖章文件上传接口 ==================== + + /** + * 上传登记备案表盖章文件 + */ + @SaCheckPermission("rs:overseas:add") + @PostMapping("/uploadStampedFile") + @Log(title = "上传盖章文件", businessType = BusinessType.OTHER) + 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) { + return R.fail("文件大小超过10MB限制"); + } + + // 生成文件名 + String originalFilename = file.getOriginalFilename(); + String fileName = "stamped_" + applicationId + "_" + System.currentTimeMillis() + + getFileExtension(originalFilename); + + // 上传到OSS + OssClient ossClient = OssFactory.instance(); + String filePath = "overseas/stamped/" + fileName; + UploadResult uploadResult = ossClient.upload(file.getInputStream(), filePath, + file.getSize(), file.getContentType()); + + // 更新申请记录 - 只更新盖章文件相关字段 + applicationService.updateStampedFileInfo(applicationId, uploadResult.getUrl(), + originalFilename, new Date(), "1"); + + // 返回结果 + Map result = new HashMap<>(); + result.put("filePath", uploadResult.getUrl()); + result.put("fileName", originalFilename); + result.put("uploadTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); + + return R.ok("盖章文件上传成功", result); + + } catch (IOException e) { + e.printStackTrace(); + return R.fail("文件上传失败:" + e.getMessage()); + } catch (Exception e) { + e.printStackTrace(); + return R.fail("系统异常:" + e.getMessage()); + } + } + + /** + * 上传党籍保留附件 + */ + @PostMapping("/uploadPartyRetentionFile") + @Log(title = "上传党籍保留附件", businessType = BusinessType.OTHER) + 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) { + return R.fail("文件大小超过10MB限制"); + } + + // 生成文件名 + String originalFilename = file.getOriginalFilename(); + String fileName = "party_retention_" + applicationId + "_" + System.currentTimeMillis() + + getFileExtension(originalFilename); + + // 上传到OSS + OssClient ossClient = OssFactory.instance(); + String filePath = "overseas/party-retention/" + fileName; + UploadResult uploadResult = ossClient.upload(file.getInputStream(), filePath, + file.getSize(), file.getContentType()); + + // 更新申请记录 - 只更新党籍保留附件相关字段 + applicationService.updatePartyRetentionFileInfo(applicationId, uploadResult.getUrl(), + originalFilename, new Date(), "1"); + + // 返回结果 + Map result = new HashMap<>(); + result.put("filePath", uploadResult.getUrl()); + result.put("fileName", originalFilename); + result.put("uploadTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); + + return R.ok("党籍保留附件上传成功", result); + + } catch (IOException e) { + e.printStackTrace(); + return R.fail("文件上传失败:" + e.getMessage()); + } catch (Exception e) { + e.printStackTrace(); + return R.fail("系统异常:" + e.getMessage()); + } + } + + /** + * 验证文件类型 + */ + 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"); + } + + /** + * 获取文件扩展名 + */ + private String getFileExtension(String filename) { + if (filename == null || !filename.contains(".")) { + return ".pdf"; + } + return filename.substring(filename.lastIndexOf(".")); + } + + } \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasRecordController.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasRecordController.java index f95d4cf0..97f620cf 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasRecordController.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasRecordController.java @@ -30,6 +30,11 @@ import org.dromara.rs.domain.bo.RsOverseasRecordBo; import org.dromara.rs.domain.vo.RsOverseasRecordVo; import org.dromara.rs.domain.query.RsOverseasRecordQuery; import org.dromara.rs.service.IRsOverseasRecordService; +import org.dromara.rs.util.OverseasReceiptPdfUtil; + +import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; /** * 出国记录Controller @@ -61,8 +66,16 @@ public class RsOverseasRecordController extends BaseController { @GetMapping("/my/list") public TableDataInfo myList(RsOverseasRecordQuery query) { Long userId = LoginHelper.getUserId(); + System.out.println("========== 出国记录Controller: 接收到查询请求 =========="); + System.out.println("当前用户ID: " + userId); + System.out.println("原始查询参数: " + query); query.setApplicantId(userId); - return recordService.queryMyRecordPageList(query); + System.out.println("设置申请人ID后: " + query); + + TableDataInfo result = recordService.queryMyRecordPageList(query); + System.out.println("Controller返回结果: total=" + result.getTotal() + ", rows=" + (result.getRows() != null ? result.getRows().size() : 0)); + System.out.println("========== 出国记录Controller: 查询完成 =========="); + return result; } /** @@ -147,6 +160,53 @@ public class RsOverseasRecordController extends BaseController { return toAjax(recordService.deleteWithValidByIds(recordIds, true)); } + /** + * 下载因私出国(境)情况回执PDF + */ + @SaCheckPermission("rs:overseas:query") + @Log(title = "下载出国情况回执", businessType = BusinessType.EXPORT) + @GetMapping("/downloadReceipt/{recordId}") + public void downloadReceipt(@NotNull(message = "记录ID不能为空") @PathVariable Long recordId, + HttpServletResponse response) { + try { + // 查询出国记录详情 + RsOverseasRecordVo record = recordService.queryById(recordId); + if (record == null) { + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().write("{\"code\":404,\"msg\":\"出国记录不存在\"}"); + return; + } + + // 生成PDF + byte[] pdfBytes = OverseasReceiptPdfUtil.generateReceiptPdf(record); + + // 设置响应头 + String fileName = String.format("因私出国情况回执_%s_%s.pdf", + record.getApplicantName(), + new java.text.SimpleDateFormat("yyyyMMdd").format(new java.util.Date())); + String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8).replace("+", "%20"); + + response.setContentType("application/pdf"); + response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedFileName); + response.setContentLength(pdfBytes.length); + + // 输出PDF + response.getOutputStream().write(pdfBytes); + response.getOutputStream().flush(); + + } catch (Exception e) { + try { + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().write("{\"code\":500,\"msg\":\"PDF生成失败: " + e.getMessage() + "\"}"); + } catch (IOException ioException) { + // 记录日志 + System.err.println("PDF生成失败: " + e.getMessage()); + } + } + } + /** * 初始化测试数据(临时接口,用于解决出国记录列表显示问题) */ diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsJob.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsJob.java index 35989e42..3f63e43a 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsJob.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsJob.java @@ -44,12 +44,6 @@ public class RsJob extends BaseEntity { @ExcelProperty(value = "部门") private String department; - /** - * 薪资范围 - */ - @ExcelProperty(value = "薪资范围") - private String salaryRange; - /** * 工作地点 */ 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 2f888b83..1ca04d15 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 @@ -35,6 +35,12 @@ public class RsOverseasApplication extends BaseEntity { @ExcelProperty(value = "申请ID") private Long applicationId; + /** + * 编号(自动生成的登记编号) + */ + @ExcelProperty(value = "编号") + private String serialNumber; + /** * 申请人ID */ @@ -48,13 +54,140 @@ public class RsOverseasApplication extends BaseEntity { private String applicantName; /** - * 所属部门 + * 性别:0-男,1-女 + */ + @ExcelProperty(value = "性别") + private String gender; + + /** + * 出生日期 + */ + @JsonFormat(pattern = "yyyy-MM-dd") + @ExcelProperty(value = "出生日期") + private Date birthDate; + + /** + * 照片地址(2寸免冠白色或淡蓝色底彩照) + */ + @ExcelProperty(value = "照片") + private String photo; + + /** + * 公民身份号码(18位身份证号) + */ + @ExcelProperty(value = "公民身份号码") + private String idNumber; + + /** + * 政治面貌 + */ + @ExcelProperty(value = "政治面貌") + private String politicalStatus; + + /** + * 户籍所在地(填写至县市区) + */ + @ExcelProperty(value = "户籍所在地") + private String registeredAddress; + + /** + * 所属部门(工作部门/教学系部) */ @ExcelProperty(value = "所属部门") private String department; /** - * 联系电话 + * 职务职称 + */ + @ExcelProperty(value = "职务职称") + private String position; + + /** + * 职称 + */ + @ExcelProperty(value = "职称") + private String professionalTitle; + + /** + * 行政职级 + */ + @ExcelProperty(value = "行政职级") + private String administrativeLevel; + + /** + * 人员类别代码(根据报备人员范围中的情形项填写) + */ + @ExcelProperty(value = "人员类别代码") + private String personnelCategory; + + /** + * 登记备案责任单位 + */ + @ExcelProperty(value = "登记备案责任单位") + private String registrationUnit; + + /** + * 登记备案责任单位负责人签名 + */ + @ExcelProperty(value = "登记备案责任单位负责人签名") + private String unitLeaderSignature; + + /** + * 登记备案责任单位联系人 + */ + @ExcelProperty(value = "登记备案责任单位联系人") + private String unitContact; + + /** + * 办公电话 + */ + @ExcelProperty(value = "办公电话") + private String officePhone; + + /** + * 申领证件名称 + */ + @ExcelProperty(value = "申领证件名称") + private String documentName; + + /** + * 证件号码 + */ + @ExcelProperty(value = "证件号码") + private String documentNumber; + + /** + * 前往国家 + */ + @ExcelProperty(value = "前往国家") + private String destinationCountry; + + /** + * 出境事由 + */ + @ExcelProperty(value = "出境事由") + private String exitReason; + + /** + * 探望对象姓名及与申请者关系 + */ + @ExcelProperty(value = "探望对象关系") + private String visitRelationship; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remarks; + + /** + * 移动电话 + */ + @ExcelProperty(value = "移动电话") + private String mobilePhone; + + /** + * 联系电话(保留原字段兼容性) */ @ExcelProperty(value = "联系电话") private String contactPhone; @@ -177,6 +310,68 @@ public class RsOverseasApplication extends BaseEntity { @ExcelProperty(value = "申请人备注") private String applicantRemark; + /** + * 应急联系人及电话 + */ + @ExcelProperty(value = "应急联系人及电话") + private String emergencyContact; + + /** + * 手写签字(base64格式) + */ + @ExcelProperty(value = "手写签字") + private String signature; + + /** + * 登记备案表盖章文件路径 + */ + @ExcelProperty(value = "登记备案表盖章文件路径") + private String stampedRegistrationFilePath; + + /** + * 登记备案表盖章文件名 + */ + @ExcelProperty(value = "登记备案表盖章文件名") + private String stampedRegistrationFileName; + + /** + * 盖章文件上传时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ExcelProperty(value = "盖章文件上传时间") + private Date stampedFileUploadTime; + + /** + * 盖章文件上传状态:0-未上传,1-已上传 + */ + @ExcelProperty(value = "盖章文件上传状态") + private String stampedFileUploadStatus; + + /** + * 党籍保留附件文件路径 + */ + @ExcelProperty(value = "党籍保留附件文件路径") + private String partyRetentionFilePath; + + /** + * 党籍保留附件文件名 + */ + @ExcelProperty(value = "党籍保留附件文件名") + private String partyRetentionFileName; + + /** + * 党籍保留附件上传时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ExcelProperty(value = "党籍保留附件上传时间") + private Date partyRetentionUploadTime; + + /** + * 党籍保留附件上传状态:0-未上传,1-已上传 + */ + @ExcelProperty(value = "党籍保留附件上传状态") + private String partyRetentionUploadStatus; + /** * 基础字段:创建者 */ diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsOverseasRecord.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsOverseasRecord.java index 837ebd8d..a8140ea2 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsOverseasRecord.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsOverseasRecord.java @@ -59,12 +59,24 @@ public class RsOverseasRecord extends BaseEntity { @ExcelProperty(value = "所属部门") private String department; + /** + * 职务 + */ + @ExcelProperty(value = "职务") + private String position; + /** * 联系电话 */ @ExcelProperty(value = "联系电话") private String contactPhone; + /** + * 联系邮箱 + */ + @ExcelProperty(value = "联系邮箱") + private String contactEmail; + /** * 出国目的 */ @@ -115,6 +127,12 @@ public class RsOverseasRecord extends BaseEntity { @ExcelProperty(value = "实际花费金额") private BigDecimal actualBudget; + /** + * 经费来源 + */ + @ExcelProperty(value = "经费来源") + private String budgetSource; + /** * 邀请方信息 */ diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsTrainingApplication.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsTrainingApplication.java index ea61e5cb..b3677f55 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsTrainingApplication.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsTrainingApplication.java @@ -155,11 +155,6 @@ public class RsTrainingApplication extends BaseEntity { */ private String tenantId; - /** - * 创建部门 - */ - private Long createDept; - /** * 拒绝时间 */ diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsTrainingRecord.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsTrainingRecord.java index 5de22922..a83bc55f 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsTrainingRecord.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsTrainingRecord.java @@ -116,7 +116,6 @@ public class RsTrainingRecord extends BaseEntity { private String feedback; private String tenantId; - private Long createDept; /** * 部门ID 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 a17152cb..b803b7e4 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 @@ -9,6 +9,7 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.DecimalMin; import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.Pattern; import lombok.Data; import lombok.EqualsAndHashCode; import com.fasterxml.jackson.annotation.JsonFormat; @@ -35,6 +36,11 @@ public class RsOverseasApplicationBo extends BaseEntity { @NotNull(message = "申请ID不能为空", groups = { EditGroup.class }) private Long applicationId; + /** + * 编号(自动生成的登记编号) + */ + private String serialNumber; + /** * 申请人ID */ @@ -44,28 +50,125 @@ public class RsOverseasApplicationBo extends BaseEntity { /** * 申请人姓名 */ + @NotBlank(message = "申请人姓名不能为空", groups = { AddGroup.class, EditGroup.class }) private String applicantName; + /** + * 性别:0-男,1-女 + */ + @NotBlank(message = "性别不能为空", groups = { AddGroup.class, EditGroup.class }) + private String gender; + + /** + * 出生日期 + */ + @NotNull(message = "出生日期不能为空", groups = { AddGroup.class, EditGroup.class }) + @JsonFormat(pattern = "yyyy-MM-dd") + private Date birthDate; + + /** + * 照片地址 + */ + private String photo; + + /** + * 公民身份号码(18位身份证号) + * 注意:此字段为可选字段,不进行格式校验(因为可能使用护照等其他证件) + */ + private String idNumber; + + /** + * 政治面貌 + */ + @NotBlank(message = "政治面貌不能为空", groups = { AddGroup.class, EditGroup.class }) + private String politicalStatus; + + /** + * 户籍所在地(填写至县市区) + */ + @NotBlank(message = "户籍所在地不能为空", groups = { AddGroup.class, EditGroup.class }) + private String registeredAddress; + /** * 所属部门 */ + @NotBlank(message = "所属部门不能为空", groups = { AddGroup.class, EditGroup.class }) private String department; /** - * 联系电话 + * 职务职称 + */ + @NotBlank(message = "职务职称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String position; + + /** + * 职称 + */ + private String professionalTitle; + + /** + * 行政职级 + */ + private String administrativeLevel; + + /** + * 人员类别代码(根据报备人员范围中的情形项填写) + */ + @NotBlank(message = "人员类别代码不能为空", groups = { AddGroup.class, EditGroup.class }) + private String personnelCategory; + + /** + * 登记备案责任单位 + */ + private String registrationUnit; + + /** + * 登记备案责任单位负责人签名 + */ + private String unitLeaderSignature; + + /** + * 登记备案责任单位联系人 + */ + @NotBlank(message = "登记备案责任单位联系人不能为空", groups = { AddGroup.class, EditGroup.class }) + private String unitContact; + + /** + * 办公电话 + */ + @Pattern(regexp = "^(\\d{3,4}-)?\\d{7,8}$", message = "办公电话格式不正确", groups = { AddGroup.class, EditGroup.class }) + private String officePhone; + + /** + * 申领证件名称 + */ + private String documentName; + + /** + * 证件号码 + */ + private String documentNumber; + + /** + * 移动电话 + */ + @NotBlank(message = "移动电话不能为空", groups = { AddGroup.class, EditGroup.class }) + @Pattern(regexp = "^1[3-9]\\d{9}$", message = "移动电话格式不正确", groups = { AddGroup.class, EditGroup.class }) + private String mobilePhone; + + /** + * 联系电话(保留兼容性) */ private String contactPhone; /** * 出国目的 */ - @NotBlank(message = "出国目的不能为空", groups = { AddGroup.class, EditGroup.class }) private String purpose; /** * 目的详细说明 */ - @NotBlank(message = "目的详细说明不能为空", groups = { AddGroup.class, EditGroup.class }) private String purposeDetail; /** @@ -74,6 +177,21 @@ public class RsOverseasApplicationBo extends BaseEntity { @NotBlank(message = "目的地国家不能为空", groups = { AddGroup.class, EditGroup.class }) private String destination; + /** + * 前往国家 + */ + private String destinationCountry; + + /** + * 出境事由 + */ + private String exitReason; + + /** + * 探望对象姓名及与申请者关系 + */ + private String visitRelationship; + /** * 开始日期 */ @@ -97,8 +215,6 @@ public class RsOverseasApplicationBo extends BaseEntity { /** * 预算金额 */ - @NotNull(message = "预算金额不能为空", groups = { AddGroup.class, EditGroup.class }) - @DecimalMin(value = "0.01", message = "预算金额必须大于0", groups = { AddGroup.class, EditGroup.class }) private BigDecimal budget; /** @@ -119,8 +235,6 @@ public class RsOverseasApplicationBo extends BaseEntity { @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date approvalTime; - - /** * 目的地 */ @@ -146,6 +260,22 @@ public class RsOverseasApplicationBo extends BaseEntity { */ private String applicantRemark; + /** + * 应急联系人及电话 + */ + @NotBlank(message = "应急联系人及电话不能为空") + private String emergencyContact; + + /** + * 手写签字(base64格式) + */ + private String signature; + + /** + * 备注 + */ + private String remarks; + /** * 管理员备注 */ @@ -165,4 +295,25 @@ public class RsOverseasApplicationBo extends BaseEntity { * 申请状态 */ private String status; + + /** + * 登记备案表盖章文件路径 + */ + private String stampedRegistrationFilePath; + + /** + * 登记备案表盖章文件名 + */ + private String stampedRegistrationFileName; + + /** + * 盖章文件上传时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date stampedFileUploadTime; + + /** + * 盖章文件上传状态:0-未上传,1-已上传 + */ + private String stampedFileUploadStatus; } \ No newline at end of file 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 d43c7f46..dd81fb6d 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 @@ -28,6 +28,11 @@ public class RsOverseasApplicationVo implements Serializable { */ private Long applicationId; + /** + * 编号(自动生成的登记编号) + */ + private String serialNumber; + /** * 申请人ID */ @@ -38,11 +43,117 @@ public class RsOverseasApplicationVo implements Serializable { */ private String applicantName; + /** + * 性别 + */ + private String gender; + + /** + * 性别文本 + */ + private String genderText; + + /** + * 出生日期 + */ + @JsonFormat(pattern = "yyyy-MM-dd") + private Date birthDate; + + /** + * 照片地址 + */ + private String photo; + + /** + * 公民身份号码 + */ + private String idNumber; + + /** + * 政治面貌 + */ + private String politicalStatus; + + /** + * 政治面貌文本 + */ + private String politicalStatusText; + + /** + * 户籍所在地 + */ + private String registeredAddress; + /** * 所属部门 */ private String department; + /** + * 职务职称 + */ + private String position; + + /** + * 职称 + */ + private String professionalTitle; + + /** + * 行政职级 + */ + private String administrativeLevel; + + /** + * 行政职级文本 + */ + private String administrativeLevelText; + + /** + * 人员类别代码 + */ + private String personnelCategory; + + /** + * 人员类别代码文本 + */ + private String personnelCategoryText; + + /** + * 登记备案责任单位 + */ + private String registrationUnit; + + /** + * 登记备案责任单位负责人签名 + */ + private String unitLeaderSignature; + + /** + * 登记备案责任单位联系人 + */ + private String unitContact; + + /** + * 办公电话 + */ + private String officePhone; + + /** + * 申领证件名称 + */ + private String documentName; + + /** + * 证件号码 + */ + private String documentNumber; + + /** + * 移动电话 + */ + private String mobilePhone; + /** * 联系电话 */ @@ -68,6 +179,21 @@ public class RsOverseasApplicationVo implements Serializable { */ private String destination; + /** + * 前往国家 + */ + private String destinationCountry; + + /** + * 出境事由 + */ + private String exitReason; + + /** + * 探望对象姓名及与申请者关系 + */ + private String visitRelationship; + /** * 目的地 */ @@ -157,11 +283,47 @@ public class RsOverseasApplicationVo implements Serializable { */ private String applicantRemark; + /** + * 应急联系人及电话 + */ + private String emergencyContact; + + /** + * 手写签字(base64格式) + */ + private String signature; + + /** + * 备注 + */ + private String remarks; + /** * 管理员备注 */ private String adminRemark; + /** + * 登记备案表盖章文件路径 + */ + private String stampedRegistrationFilePath; + + /** + * 登记备案表盖章文件名 + */ + private String stampedRegistrationFileName; + + /** + * 盖章文件上传时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date stampedFileUploadTime; + + /** + * 盖章文件上传状态:0-未上传,1-已上传 + */ + private String stampedFileUploadStatus; + /** * 租户编号 */ diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsOverseasRecordVo.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsOverseasRecordVo.java index edf94076..029400fd 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsOverseasRecordVo.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsOverseasRecordVo.java @@ -47,11 +47,89 @@ public class RsOverseasRecordVo implements Serializable { * 所属部门 */ private String department; + + /** + * 职位 + */ + private String position; /** * 联系电话 */ private String contactPhone; + + /** + * 联系邮箱 + */ + private String contactEmail; + + // ========== 以下字段来自关联的出国申请表 ========== + + /** + * 性别(来自申请表) + */ + private String gender; + + /** + * 性别文本(来自申请表) + */ + private String genderText; + + /** + * 出生日期(来自申请表) + */ + @JsonFormat(pattern = "yyyy-MM-dd") + private Date birthDate; + + /** + * 政治面貌(来自申请表) + */ + private String politicalStatus; + + /** + * 政治面貌文本(来自申请表) + */ + private String politicalStatusText; + + /** + * 职称(来自申请表) + */ + private String professionalTitle; + + /** + * 申领证件名称(来自申请表) + */ + private String documentName; + + /** + * 证件号码(来自申请表) + */ + private String documentNumber; + + /** + * 移动电话(来自申请表) + */ + private String mobilePhone; + + /** + * 前往国家(来自申请表) + */ + private String destinationCountry; + + /** + * 出境事由(来自申请表) + */ + private String exitReason; + + /** + * 应急联系人及电话(来自申请表) + */ + private String emergencyContact; + + /** + * 备注(来自申请表) + */ + private String remarks; /** * 出国目的 @@ -120,6 +198,16 @@ public class RsOverseasRecordVo implements Serializable { */ private String transportationPlan; + /** + * 申请状态(来自申请表) + */ + private String status; + + /** + * 申请状态文本(来自申请表) + */ + private String statusText; + /** * 完成状态 */ 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 c541aabd..fe9fa809 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 @@ -158,4 +158,52 @@ public interface IRsOverseasApplicationService { * @return 是否成功 */ Boolean resubmitApplication(Long applicationId, RsOverseasApplicationBo applicationData); + + /** + * 更新盖章文件信息 + * + * @param applicationId 申请ID + * @param filePath 文件路径 + * @param fileName 文件名 + * @param uploadTime 上传时间 + * @param uploadStatus 上传状态 + * @return 是否成功 + */ + Boolean updateStampedFileInfo(Long applicationId, String filePath, String fileName, + java.util.Date uploadTime, String uploadStatus); + + /** + * 更新党籍保留附件信息 + * + * @param applicationId 申请ID + * @param filePath 文件路径 + * @param fileName 文件名 + * @param uploadTime 上传时间 + * @param uploadStatus 上传状态 + * @return 是否成功 + */ + Boolean updatePartyRetentionFileInfo(Long applicationId, String filePath, String fileName, + java.util.Date uploadTime, String uploadStatus); + + /** + * 校验出国登记前置条件 + * 检查近3个月内是否有已通过的出国登记记录 + * + * @param applicantId 申请人ID + * @return 校验结果Map,包含 isValid(是否通过校验)和 message(提示信息) + */ + java.util.Map validateRegistrationPrerequisite(Long applicantId); + + /** + * 校验党籍保留前置条件 + * 检查是否为中共党员且出国天数>30天,如果是则需要检查党籍保留申请 + * + * @param applicantId 申请人ID + * @param politicalStatus 政治面貌 + * @param duration 出国天数 + * @return 校验结果Map,包含 isValid(是否通过校验)、needRetention(是否需要党籍保留)和 message(提示信息) + */ + java.util.Map validatePartyMembershipRetentionPrerequisite(Long applicantId, + String politicalStatus, + Integer duration); } \ No newline at end of file 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 9289895d..ebdb5f18 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 @@ -2,7 +2,10 @@ package org.dromara.rs.service.impl; import java.util.Arrays; import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Calendar; import org.dromara.common.core.utils.DateUtils; import org.dromara.common.core.utils.StringUtils; @@ -12,12 +15,17 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import org.dromara.rs.mapper.RsOverseasApplicationMapper; import org.dromara.rs.mapper.RsOverseasApprovalMapper; +import org.dromara.rs.mapper.RsOverseasRegistrationMapper; +import org.dromara.rs.mapper.RsPartyMembershipRetentionMapper; import org.dromara.rs.domain.RsOverseasApplication; import org.dromara.rs.domain.RsOverseasApproval; +import org.dromara.rs.domain.RsOverseasRegistration; +import org.dromara.rs.domain.RsPartyMembershipRetention; import org.dromara.rs.domain.bo.RsOverseasApplicationBo; import org.dromara.rs.domain.vo.RsOverseasApplicationVo; import org.dromara.rs.domain.query.RsOverseasApplicationQuery; @@ -47,6 +55,12 @@ public class RsOverseasApplicationServiceImpl implements IRsOverseasApplicationS @Autowired private IRsOverseasRecordService overseasRecordService; + @Autowired + private RsOverseasRegistrationMapper registrationMapper; + + @Autowired + private RsPartyMembershipRetentionMapper retentionMapper; + /** * 查询出国申请分页列表 */ @@ -198,7 +212,71 @@ public class RsOverseasApplicationServiceImpl implements IRsOverseasApplicationS update.setDuration(days); } validEntityBeforeSave(update); - return baseMapper.updateById(update) > 0; + + // 使用LambdaUpdateWrapper安全更新,只更新非null字段 + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(RsOverseasApplication::getApplicationId, update.getApplicationId()); + + // ==================== 基本信息 ==================== + if (update.getApplicantId() != null) updateWrapper.set(RsOverseasApplication::getApplicantId, update.getApplicantId()); + if (update.getApplicantName() != null) updateWrapper.set(RsOverseasApplication::getApplicantName, update.getApplicantName()); + if (update.getGender() != null) updateWrapper.set(RsOverseasApplication::getGender, update.getGender()); + if (update.getBirthDate() != null) updateWrapper.set(RsOverseasApplication::getBirthDate, update.getBirthDate()); + if (update.getPhoto() != null) updateWrapper.set(RsOverseasApplication::getPhoto, update.getPhoto()); + if (update.getIdNumber() != null) updateWrapper.set(RsOverseasApplication::getIdNumber, update.getIdNumber()); + if (update.getPoliticalStatus() != null) updateWrapper.set(RsOverseasApplication::getPoliticalStatus, update.getPoliticalStatus()); // 政治面貌 ⬅️ 关键修复 + if (update.getRegisteredAddress() != null) updateWrapper.set(RsOverseasApplication::getRegisteredAddress, update.getRegisteredAddress()); + + // ==================== 工作信息 ==================== + if (update.getDepartment() != null) updateWrapper.set(RsOverseasApplication::getDepartment, update.getDepartment()); + if (update.getPosition() != null) updateWrapper.set(RsOverseasApplication::getPosition, update.getPosition()); // 职务 + if (update.getProfessionalTitle() != null) updateWrapper.set(RsOverseasApplication::getProfessionalTitle, update.getProfessionalTitle()); // 职称 + if (update.getAdministrativeLevel() != null) updateWrapper.set(RsOverseasApplication::getAdministrativeLevel, update.getAdministrativeLevel()); + if (update.getPersonnelCategory() != null) updateWrapper.set(RsOverseasApplication::getPersonnelCategory, update.getPersonnelCategory()); + + // ==================== 单位和联系信息 ==================== + if (update.getRegistrationUnit() != null) updateWrapper.set(RsOverseasApplication::getRegistrationUnit, update.getRegistrationUnit()); + if (update.getUnitLeaderSignature() != null) updateWrapper.set(RsOverseasApplication::getUnitLeaderSignature, update.getUnitLeaderSignature()); + if (update.getUnitContact() != null) updateWrapper.set(RsOverseasApplication::getUnitContact, update.getUnitContact()); + if (update.getOfficePhone() != null) updateWrapper.set(RsOverseasApplication::getOfficePhone, update.getOfficePhone()); + if (update.getMobilePhone() != null) updateWrapper.set(RsOverseasApplication::getMobilePhone, update.getMobilePhone()); + if (update.getContactPhone() != null) updateWrapper.set(RsOverseasApplication::getContactPhone, update.getContactPhone()); + if (update.getEmergencyContact() != null) updateWrapper.set(RsOverseasApplication::getEmergencyContact, update.getEmergencyContact()); // 应急联系人 ⬅️ 关键修复 + + // ==================== 出国目的和行程 ==================== + if (update.getPurpose() != null) updateWrapper.set(RsOverseasApplication::getPurpose, update.getPurpose()); + if (update.getPurposeDetail() != null) updateWrapper.set(RsOverseasApplication::getPurposeDetail, update.getPurposeDetail()); + if (update.getDestination() != null) updateWrapper.set(RsOverseasApplication::getDestination, update.getDestination()); + if (update.getDestinationCountry() != null) updateWrapper.set(RsOverseasApplication::getDestinationCountry, update.getDestinationCountry()); + if (update.getDestinationCity() != null) updateWrapper.set(RsOverseasApplication::getDestinationCity, update.getDestinationCity()); + if (update.getStartDate() != null) updateWrapper.set(RsOverseasApplication::getStartDate, update.getStartDate()); + if (update.getEndDate() != null) updateWrapper.set(RsOverseasApplication::getEndDate, update.getEndDate()); + if (update.getDuration() != null) updateWrapper.set(RsOverseasApplication::getDuration, update.getDuration()); + if (update.getBudget() != null) updateWrapper.set(RsOverseasApplication::getBudget, update.getBudget()); + + // ==================== 证件和文档 ==================== + if (update.getInvitation() != null) updateWrapper.set(RsOverseasApplication::getInvitation, update.getInvitation()); + if (update.getDocumentName() != null) updateWrapper.set(RsOverseasApplication::getDocumentName, update.getDocumentName()); + if (update.getDocumentNumber() != null) updateWrapper.set(RsOverseasApplication::getDocumentNumber, update.getDocumentNumber()); + if (update.getItinerary() != null) updateWrapper.set(RsOverseasApplication::getItinerary, update.getItinerary()); + if (update.getAccommodationPlan() != null) updateWrapper.set(RsOverseasApplication::getAccommodationPlan, update.getAccommodationPlan()); + if (update.getTransportationPlan() != null) updateWrapper.set(RsOverseasApplication::getTransportationPlan, update.getTransportationPlan()); + + // ==================== 出境事由和备注 ==================== + if (update.getExitReason() != null) updateWrapper.set(RsOverseasApplication::getExitReason, update.getExitReason()); // 出境事由 ⬅️ 关键字段 + if (update.getVisitRelationship() != null) updateWrapper.set(RsOverseasApplication::getVisitRelationship, update.getVisitRelationship()); + if (update.getRemarks() != null) updateWrapper.set(RsOverseasApplication::getRemarks, update.getRemarks()); + if (update.getApplicantRemark() != null) updateWrapper.set(RsOverseasApplication::getApplicantRemark, update.getApplicantRemark()); + if (update.getAdminRemark() != null) updateWrapper.set(RsOverseasApplication::getAdminRemark, update.getAdminRemark()); // 管理员备注 ⬅️ 补充遗漏字段 + if (update.getSignature() != null) updateWrapper.set(RsOverseasApplication::getSignature, update.getSignature()); // 手写签名 ⬅️ 补充遗漏字段 + + // ==================== 盖章文件相关字段 ==================== + if (update.getStampedRegistrationFilePath() != null) updateWrapper.set(RsOverseasApplication::getStampedRegistrationFilePath, update.getStampedRegistrationFilePath()); + if (update.getStampedRegistrationFileName() != null) updateWrapper.set(RsOverseasApplication::getStampedRegistrationFileName, update.getStampedRegistrationFileName()); + if (update.getStampedFileUploadTime() != null) updateWrapper.set(RsOverseasApplication::getStampedFileUploadTime, update.getStampedFileUploadTime()); + if (update.getStampedFileUploadStatus() != null) updateWrapper.set(RsOverseasApplication::getStampedFileUploadStatus, update.getStampedFileUploadStatus()); + + return baseMapper.update(null, updateWrapper) > 0; } /** @@ -465,9 +543,7 @@ public class RsOverseasApplicationServiceImpl implements IRsOverseasApplicationS } } - if (entity.getBudget() != null && entity.getBudget().doubleValue() <= 0) { - throw new RuntimeException("预算金额必须大于0"); - } + // 预算金额校验已删除(业务不需要预算金额字段) } /** @@ -498,23 +574,66 @@ public class RsOverseasApplicationServiceImpl implements IRsOverseasApplicationS private RsOverseasApplication buildEntityFromBo(RsOverseasApplicationBo bo) { RsOverseasApplication entity = new RsOverseasApplication(); entity.setApplicationId(bo.getApplicationId()); + + // ==================== 基本信息 ==================== entity.setApplicantId(bo.getApplicantId()); entity.setApplicantName(bo.getApplicantName()); + entity.setGender(bo.getGender()); // 性别 + entity.setBirthDate(bo.getBirthDate()); // 出生日期 + entity.setPhoto(bo.getPhoto()); // 照片 + entity.setIdNumber(bo.getIdNumber()); // 公民身份号码 + entity.setPoliticalStatus(bo.getPoliticalStatus()); // 政治面貌 ⬅️ 关键修复 + entity.setRegisteredAddress(bo.getRegisteredAddress()); // 户籍所在地 + + // ==================== 工作信息 ==================== entity.setDepartment(bo.getDepartment()); - entity.setContactPhone(bo.getContactPhone()); + entity.setPosition(bo.getPosition()); // 职务 + entity.setProfessionalTitle(bo.getProfessionalTitle()); // 职称 + entity.setAdministrativeLevel(bo.getAdministrativeLevel()); // 行政职级 + entity.setPersonnelCategory(bo.getPersonnelCategory()); // 人员类别代码 + + // ==================== 单位和联系信息 ==================== + entity.setRegistrationUnit(bo.getRegistrationUnit()); // 登记备案责任单位 + entity.setUnitLeaderSignature(bo.getUnitLeaderSignature()); // 单位负责人签名 + entity.setUnitContact(bo.getUnitContact()); // 单位联系人 + entity.setOfficePhone(bo.getOfficePhone()); // 办公电话 + entity.setMobilePhone(bo.getMobilePhone()); // 移动电话 + entity.setContactPhone(bo.getContactPhone()); // 联系电话(兼容字段) + entity.setEmergencyContact(bo.getEmergencyContact()); // 应急联系人及电话 ⬅️ 关键修复 + + // ==================== 出国目的和行程 ==================== entity.setPurpose(bo.getPurpose()); entity.setPurposeDetail(bo.getPurposeDetail()); entity.setDestination(bo.getDestination()); + entity.setDestinationCountry(bo.getDestinationCountry()); // 前往国家 entity.setDestinationCity(bo.getDestinationCity()); entity.setStartDate(bo.getStartDate()); entity.setEndDate(bo.getEndDate()); entity.setDuration(bo.getDuration()); entity.setBudget(bo.getBudget()); + + // ==================== 证件和文档 ==================== entity.setInvitation(bo.getInvitation()); + entity.setDocumentName(bo.getDocumentName()); // 申领证件名称 + entity.setDocumentNumber(bo.getDocumentNumber()); // 证件号码 entity.setItinerary(bo.getItinerary()); entity.setAccommodationPlan(bo.getAccommodationPlan()); entity.setTransportationPlan(bo.getTransportationPlan()); - entity.setApplicantRemark(bo.getApplicantRemark()); + + // ==================== 出境事由和备注 ==================== + entity.setExitReason(bo.getExitReason()); // 出境事由 ⬅️ 关键字段 + entity.setVisitRelationship(bo.getVisitRelationship()); // 探望对象关系 + entity.setRemarks(bo.getRemarks()); // 备注 + entity.setApplicantRemark(bo.getApplicantRemark()); // 申请人备注 + entity.setAdminRemark(bo.getAdminRemark()); // 管理员备注 ⬅️ 补充遗漏字段 + entity.setSignature(bo.getSignature()); // 手写签名 ⬅️ 补充遗漏字段 + + // ==================== 盖章文件相关字段 ==================== + entity.setStampedRegistrationFilePath(bo.getStampedRegistrationFilePath()); + entity.setStampedRegistrationFileName(bo.getStampedRegistrationFileName()); + entity.setStampedFileUploadTime(bo.getStampedFileUploadTime()); + entity.setStampedFileUploadStatus(bo.getStampedFileUploadStatus()); + return entity; } @@ -543,4 +662,178 @@ public class RsOverseasApplicationServiceImpl implements IRsOverseasApplicationS validatedByIds(ids, isValid); return baseMapper.deleteBatchIds(ids) > 0; } + + /** + * 更新盖章文件信息 + */ + @Override + @Transactional + public Boolean updateStampedFileInfo(Long applicationId, String filePath, String fileName, + java.util.Date uploadTime, String uploadStatus) { + return baseMapper.update(null, + new LambdaUpdateWrapper() + .eq(RsOverseasApplication::getApplicationId, applicationId) + .set(RsOverseasApplication::getStampedRegistrationFilePath, filePath) + .set(RsOverseasApplication::getStampedRegistrationFileName, fileName) + .set(RsOverseasApplication::getStampedFileUploadTime, uploadTime) + .set(RsOverseasApplication::getStampedFileUploadStatus, uploadStatus) + ) > 0; + } + + /** + * 更新党籍保留附件信息 + */ + @Override + @Transactional + public Boolean updatePartyRetentionFileInfo(Long applicationId, String filePath, String fileName, + java.util.Date uploadTime, String uploadStatus) { + return baseMapper.update(null, + new LambdaUpdateWrapper() + .eq(RsOverseasApplication::getApplicationId, applicationId) + .set(RsOverseasApplication::getPartyRetentionFilePath, filePath) + .set(RsOverseasApplication::getPartyRetentionFileName, fileName) + .set(RsOverseasApplication::getPartyRetentionUploadTime, uploadTime) + .set(RsOverseasApplication::getPartyRetentionUploadStatus, uploadStatus) + ) > 0; + } + + /** + * 校验出国登记前置条件 + * 检查当前自然年内是否有已通过的出国登记记录 + */ + @Override + public Map validateRegistrationPrerequisite(Long applicantId) { + Map result = new HashMap<>(); + + // 计算当前自然年的开始日期(1月1日 00:00:00) + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.MONTH, Calendar.JANUARY); + calendar.set(Calendar.DAY_OF_MONTH, 1); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + Date currentYearStart = calendar.getTime(); + + int currentYear = calendar.get(Calendar.YEAR); + + // 查询当前自然年内的出国登记记录(使用createBy字段) + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.eq(RsOverseasRegistration::getCreateBy, applicantId) + .ge(RsOverseasRegistration::getCreateTime, currentYearStart) + .orderByDesc(RsOverseasRegistration::getCreateTime) + .last("LIMIT 1"); + + RsOverseasRegistration latestRegistration = registrationMapper.selectOne(queryWrapper); + + if (latestRegistration == null) { + // 当前自然年内没有登记记录 + result.put("isValid", false); + result.put("message", "请先提交出国登记申请,审核通过后再提交出国申请"); + log.warn("用户{}在{}年内没有出国登记记录", applicantId, currentYear); + return result; + } + + String status = latestRegistration.getStatus(); + if ("2".equals(status)) { + // 状态为已通过(审批通过)- 出国登记状态2表示已通过 + result.put("isValid", true); + result.put("message", "出国登记已通过审核"); + result.put("registrationId", latestRegistration.getRegistrationId()); + log.info("用户{}的出国登记已通过审核,registrationId: {}", applicantId, latestRegistration.getRegistrationId()); + } else if ("1".equals(status)) { + // 状态为待审核 + result.put("isValid", false); + result.put("message", "出国登记审核中,请审核通过后再提交出国申请"); + log.warn("用户{}的出国登记还在审核中,status: {}", applicantId, status); + } else if ("3".equals(status)) { + // 状态为已拒绝 + result.put("isValid", false); + result.put("message", "出国登记已被拒绝,请重新提交出国登记并审核通过后再提交出国申请"); + log.warn("用户{}的出国登记已被拒绝,status: {}", applicantId, status); + } else { + // 其他状态(草稿等) + result.put("isValid", false); + result.put("message", "出国登记状态异常,请先提交出国登记申请"); + log.warn("用户{}的出国登记状态异常,status: {}", applicantId, status); + } + + return result; + } + + /** + * 校验党籍保留前置条件 + * 检查是否为中共党员且出国天数>30天,如果是则需要检查当前自然年内的党籍保留申请 + */ + @Override + public Map validatePartyMembershipRetentionPrerequisite(Long applicantId, + String politicalStatus, + Integer duration) { + Map result = new HashMap<>(); + + // 判断是否需要党籍保留申请 + boolean needRetention = "中共党员".equals(politicalStatus) && duration != null && duration > 30; + result.put("needRetention", needRetention); + + if (!needRetention) { + // 不需要党籍保留申请 + result.put("isValid", true); + result.put("message", "无需党籍保留申请"); + return result; + } + + // 需要党籍保留申请,检查当前自然年内是否有党籍保留申请(使用createBy字段) + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.MONTH, Calendar.JANUARY); + calendar.set(Calendar.DAY_OF_MONTH, 1); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + Date currentYearStart = calendar.getTime(); + + int currentYear = calendar.get(Calendar.YEAR); + + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.eq(RsPartyMembershipRetention::getCreateBy, applicantId) + .ge(RsPartyMembershipRetention::getCreateTime, currentYearStart) + .orderByDesc(RsPartyMembershipRetention::getCreateTime) + .last("LIMIT 1"); + + RsPartyMembershipRetention latestRetention = retentionMapper.selectOne(queryWrapper); + + if (latestRetention == null) { + // 当前自然年内没有党籍保留申请 + result.put("isValid", false); + result.put("message", "您是中共党员且出国天数超过30天,请先提交出国党籍保留申请,审核通过后再提交出国申请"); + log.warn("用户{}需要党籍保留但在{}年内未提交申请", applicantId, currentYear); + return result; + } + + String status = latestRetention.getStatus(); + if ("2".equals(status)) { + // 状态为已通过(审批通过) + result.put("isValid", true); + result.put("message", "党籍保留申请已通过审核"); + result.put("retentionId", latestRetention.getRetentionId()); + log.info("用户{}的党籍保留申请已通过审核,retentionId: {}", applicantId, latestRetention.getRetentionId()); + } else if ("0".equals(status) || "1".equals(status)) { + // 状态为草稿或待审核 + result.put("isValid", false); + result.put("message", "党籍保留申请审核中,请审核通过后再提交出国申请"); + log.warn("用户{}的党籍保留申请还在审核中,status: {}", applicantId, status); + } else if ("3".equals(status)) { + // 状态为已拒绝 + result.put("isValid", false); + result.put("message", "党籍保留申请已被拒绝,请重新提交党籍保留申请并审核通过后再提交出国申请"); + log.warn("用户{}的党籍保留申请已被拒绝,status: {}", applicantId, status); + } else { + // 其他状态 + result.put("isValid", false); + result.put("message", "党籍保留申请状态异常,请重新提交党籍保留申请"); + log.warn("用户{}的党籍保留申请状态异常,status: {}", applicantId, status); + } + + return result; + } } \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsOverseasRecordServiceImpl.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsOverseasRecordServiceImpl.java index aaaeab70..ddf20490 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsOverseasRecordServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsOverseasRecordServiceImpl.java @@ -87,20 +87,36 @@ public class RsOverseasRecordServiceImpl implements IRsOverseasRecordService { */ @Override public TableDataInfo queryMyRecordPageList(RsOverseasRecordQuery query) { + log.info("============= 出国记录列表查询开始 ============="); + log.info("查询参数: {}", query); + log.info("申请人ID: {}", query.getApplicantId()); + log.info("分页参数: pageNum={}, pageSize={}", query.getPageNum(), query.getPageSize()); + // 使用联合查询方法,包含已通过审批的申请数据 List list = baseMapper.selectMyOverseasRecordList(query); + log.info("查询结果数量: {}", list != null ? list.size() : 0); + if (list != null && !list.isEmpty()) { + log.info("第一条记录: {}", list.get(0)); + } else { + log.warn("⚠️ 查询结果为空!请检查数据库中是否有数据"); + } + // 手动分页处理 int pageNum = query.getPageNum(); int pageSize = query.getPageSize(); int start = (pageNum - 1) * pageSize; int end = Math.min(start + pageSize, list.size()); + log.info("分页计算: start={}, end={}, totalSize={}", start, end, list.size()); + List pageList; if (start > list.size()) { pageList = Arrays.asList(); + log.warn("⚠️ 起始位置超出数据范围,返回空列表"); } else { pageList = list.subList(start, end); + log.info("分页后数据量: {}", pageList.size()); } // 构建分页对象 @@ -108,7 +124,11 @@ public class RsOverseasRecordServiceImpl implements IRsOverseasRecordService { page.setRecords(pageList); page.setTotal(list.size()); - return TableDataInfo.build(page); + TableDataInfo result = TableDataInfo.build(page); + log.info("返回结果: total={}, rows={}", result.getTotal(), result.getRows() != null ? result.getRows().size() : 0); + log.info("============= 出国记录列表查询结束 ============="); + + return result; } /** diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsJobMapper.xml b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsJobMapper.xml index 0f13c629..1a4e94ab 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsJobMapper.xml +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsJobMapper.xml @@ -8,7 +8,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - @@ -27,7 +26,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - select j.job_id, j.title, j.department, j.salary_range, j.location, j.description, j.requirements, + select j.job_id, j.title, j.department, j.location, j.description, j.requirements, j.is_active, j.view_count, j.tenant_id, j.create_by, j.create_time, j.update_by, j.update_time, j.remark, (select count(1) from rs_application a where a.job_id = j.job_id and a.deleted = '0' and a.tenant_id = j.tenant_id) as applicant_count from rs_job j 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 7f29b40a..11ced7e2 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 @@ -6,12 +6,33 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + + + + + + + + + + + + + + + + + + + + + @@ -31,6 +52,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + + + + + + @@ -43,15 +70,27 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + + + + - select oa.application_id, oa.applicant_id, oa.applicant_name, oa.department, oa.position, - oa.contact_phone, oa.contact_email, oa.purpose, oa.purpose_detail, oa.destination, oa.destination_city, + select oa.application_id, oa.serial_number, oa.applicant_id, oa.applicant_name, + oa.gender, oa.birth_date, oa.photo, oa.id_number, oa.political_status, oa.registered_address, + oa.department, oa.position, oa.professional_title, oa.administrative_level, oa.personnel_category, + oa.registration_unit, oa.unit_leader_signature, oa.unit_contact, oa.office_phone, oa.mobile_phone, + oa.contact_phone, oa.contact_email, oa.document_name, oa.document_number, + oa.destination_country, oa.exit_reason, oa.visit_relationship, oa.remarks, + oa.purpose, oa.purpose_detail, oa.destination, oa.destination_city, oa.start_date, oa.end_date, oa.duration, oa.budget, oa.invitation, oa.itinerary, oa.accommodation_plan, oa.transportation_plan, oa.status, oa.current_approver, oa.approval_level, - oa.apply_time, oa.approval_time, oa.admin_remark, oa.applicant_remark, oa.tenant_id, - oa.create_by, oa.create_time, oa.update_by, oa.update_time, oa.deleted, + oa.apply_time, oa.approval_time, oa.admin_remark, oa.applicant_remark, + oa.emergency_contact, oa.signature, + oa.stamped_registration_file_path, oa.stamped_registration_file_name, + oa.stamped_file_upload_time, oa.stamped_file_upload_status, + oa.tenant_id, oa.create_by, oa.create_time, oa.update_by, oa.update_time, oa.deleted, CASE WHEN oa.status IS NULL OR oa.status = '' THEN '草稿' WHEN oa.status = '0' THEN '待审核' @@ -75,7 +114,52 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" WHEN '2' THEN '分管领导' WHEN '3' THEN '校领导' ELSE '未知级别' - END as approval_level_text + END as approval_level_text, + CASE oa.gender + WHEN '0' THEN '男' + WHEN '1' THEN '女' + ELSE '未知' + END as gender_text, + CASE oa.political_status + WHEN '1' THEN '中共党员' + WHEN '2' THEN '中共预备党员' + WHEN '3' THEN '共青团员' + WHEN '4' THEN '民革党员' + WHEN '5' THEN '民盟盟员' + WHEN '6' THEN '民建会员' + WHEN '7' THEN '民进会员' + WHEN '8' THEN '农工党党员' + WHEN '9' THEN '致公党党员' + WHEN '10' THEN '九三学社社员' + WHEN '11' THEN '台盟盟员' + WHEN '12' THEN '无党派人士' + WHEN '13' THEN '群众' + ELSE oa.political_status + END as political_status_text, + CASE oa.administrative_level + WHEN '0' THEN '无' + WHEN '1' THEN '一级巡视员' + WHEN '2' THEN '二级巡视员' + WHEN '3' THEN '一级调研员' + WHEN '4' THEN '二级调研员' + WHEN '5' THEN '三级调研员' + WHEN '6' THEN '四级调研员' + WHEN '7' THEN '一级主任科员' + WHEN '8' THEN '二级主任科员' + WHEN '9' THEN '三级主任科员' + WHEN '10' THEN '四级主任科员' + WHEN '11' THEN '一级科员' + WHEN '12' THEN '二级科员' + ELSE oa.administrative_level + END as administrative_level_text, + CASE oa.personnel_category + WHEN '1' THEN '1-厅局级' + WHEN '2' THEN '2-县处级' + WHEN '3' THEN '3-乡科级' + WHEN '4' THEN '4-一般干部' + WHEN '5' THEN '5-其他重要岗位人员' + ELSE oa.personnel_category + END as personnel_category_text from rs_overseas_application oa @@ -216,4 +300,204 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" order by oa.apply_time asc + + + insert into rs_overseas_application + + serial_number, + applicant_id, + applicant_name, + gender, + birth_date, + photo, + id_number, + political_status, + registered_address, + department, + position, + professional_title, + administrative_level, + personnel_category, + registration_unit, + unit_leader_signature, + unit_contact, + office_phone, + mobile_phone, + contact_phone, + contact_email, + document_name, + document_number, + destination_country, + exit_reason, + visit_relationship, + remarks, + purpose, + purpose_detail, + destination, + destination_city, + start_date, + end_date, + duration, + budget, + invitation, + itinerary, + accommodation_plan, + transportation_plan, + status, + current_approver, + approval_level, + apply_time, + approval_time, + admin_remark, + applicant_remark, + emergency_contact, + signature, + stamped_registration_file_path, + stamped_registration_file_name, + stamped_file_upload_time, + stamped_file_upload_status, + tenant_id, + deleted, + create_by, + create_time, + update_by, + update_time, + + + #{serialNumber}, + #{applicantId}, + #{applicantName}, + #{gender}, + #{birthDate}, + #{photo}, + #{idNumber}, + #{politicalStatus}, + #{registeredAddress}, + #{department}, + #{position}, + #{professionalTitle}, + #{administrativeLevel}, + #{personnelCategory}, + #{registrationUnit}, + #{unitLeaderSignature}, + #{unitContact}, + #{officePhone}, + #{mobilePhone}, + #{contactPhone}, + #{contactEmail}, + #{documentName}, + #{documentNumber}, + #{destinationCountry}, + #{exitReason}, + #{visitRelationship}, + #{remarks}, + #{purpose}, + #{purposeDetail}, + #{destination}, + #{destinationCity}, + #{startDate}, + #{endDate}, + #{duration}, + #{budget}, + #{invitation}, + #{itinerary}, + #{accommodationPlan}, + #{transportationPlan}, + #{status}, + #{currentApprover}, + #{approvalLevel}, + #{applyTime}, + #{approvalTime}, + #{adminRemark}, + #{applicantRemark}, + #{emergencyContact}, + #{signature}, + #{stampedRegistrationFilePath}, + #{stampedRegistrationFileName}, + #{stampedFileUploadTime}, + #{stampedFileUploadStatus}, + #{tenantId}, + #{deleted}, + #{createBy}, + #{createTime}, + #{updateBy}, + #{updateTime}, + + + + + + update rs_overseas_application + + serial_number = #{serialNumber}, + applicant_id = #{applicantId}, + applicant_name = #{applicantName}, + gender = #{gender}, + birth_date = #{birthDate}, + photo = #{photo}, + id_number = #{idNumber}, + political_status = #{politicalStatus}, + registered_address = #{registeredAddress}, + department = #{department}, + position = #{position}, + professional_title = #{professionalTitle}, + administrative_level = #{administrativeLevel}, + personnel_category = #{personnelCategory}, + registration_unit = #{registrationUnit}, + unit_leader_signature = #{unitLeaderSignature}, + unit_contact = #{unitContact}, + office_phone = #{officePhone}, + mobile_phone = #{mobilePhone}, + contact_phone = #{contactPhone}, + contact_email = #{contactEmail}, + document_name = #{documentName}, + document_number = #{documentNumber}, + destination_country = #{destinationCountry}, + exit_reason = #{exitReason}, + visit_relationship = #{visitRelationship}, + remarks = #{remarks}, + purpose = #{purpose}, + purpose_detail = #{purposeDetail}, + destination = #{destination}, + destination_city = #{destinationCity}, + start_date = #{startDate}, + end_date = #{endDate}, + duration = #{duration}, + budget = #{budget}, + invitation = #{invitation}, + itinerary = #{itinerary}, + accommodation_plan = #{accommodationPlan}, + transportation_plan = #{transportationPlan}, + status = #{status}, + current_approver = #{currentApprover}, + approval_level = #{approvalLevel}, + apply_time = #{applyTime}, + approval_time = #{approvalTime}, + admin_remark = #{adminRemark}, + applicant_remark = #{applicantRemark}, + emergency_contact = #{emergencyContact}, + signature = #{signature}, + stamped_registration_file_path = #{stampedRegistrationFilePath}, + stamped_registration_file_name = #{stampedRegistrationFileName}, + stamped_file_upload_time = #{stampedFileUploadTime}, + stamped_file_upload_status = #{stampedFileUploadStatus}, + update_by = #{updateBy}, + update_time = #{updateTime}, + + where application_id = #{applicationId} + + + + + update rs_overseas_application set deleted = '1' where application_id = #{applicationId} + + + + + update rs_overseas_application set deleted = '1' where application_id in + + #{applicationId} + + + \ 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 f5ee6f0b..78427757 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 @@ -40,6 +40,22 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + + + + + + + + + + + + + + + + @@ -52,10 +68,74 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 'record' as data_source from rs_overseas_record ors + + + + select + ors.record_id, ors.application_id, ors.applicant_id, ors.applicant_name, ors.department, + ors.position, 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, + ors.completion_status, ors.achievement, ors.report_content, ors.certificate_path, + ors.tenant_id, ors.create_dept, ors.create_by, ors.create_time, ors.update_by, ors.update_time, ors.deleted, + -- 来自申请表的扩展字段 + oa.gender, + CASE oa.gender + WHEN '0' THEN '男' + WHEN '1' THEN '女' + ELSE oa.gender + END as gender_text, + oa.birth_date, + oa.political_status, + CASE oa.political_status + WHEN '1' THEN '中共党员' + WHEN '2' THEN '中共预备党员' + WHEN '3' THEN '共青团员' + WHEN '4' THEN '民革党员' + WHEN '5' THEN '民盟盟员' + WHEN '6' THEN '民建会员' + WHEN '7' THEN '民进会员' + WHEN '8' THEN '农工党党员' + WHEN '9' THEN '致公党党员' + WHEN '10' THEN '九三学社社员' + WHEN '11' THEN '台盟盟员' + WHEN '12' THEN '无党派人士' + WHEN '13' THEN '群众' + ELSE oa.political_status + END as political_status_text, + oa.professional_title, + oa.document_name, + oa.document_number, + oa.mobile_phone, + oa.destination_country, + oa.exit_reason, + oa.emergency_contact, + oa.remarks, + -- 出国目的文本转换 + CASE ors.purpose + WHEN 'academic_exchange' THEN '学术交流' + WHEN 'conference' THEN '学术会议' + WHEN 'training' THEN '培训学习' + WHEN 'cooperation' THEN '合作项目' + WHEN 'research' THEN '科研访问' + WHEN 'other' THEN '其他' + ELSE ors.purpose + END as purpose_text, + -- 完成状态文本转换 + CASE ors.completion_status + WHEN '0' THEN '未完成' + WHEN '1' THEN '进行中' + WHEN '2' THEN '已完成' + ELSE '未知状态' + END as completion_status_text, + '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' + - -- 查询已创建的出国记录 + -- 查询已创建的出国记录(关联申请表获取扩展字段) select ors.record_id, ors.application_id, ors.applicant_id, ors.applicant_name, ors.department, ors.position, ors.contact_phone, ors.contact_email, ors.purpose, ors.purpose_detail, ors.destination, ors.destination_city, @@ -64,8 +144,68 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ors.invitation, ors.itinerary, ors.accommodation_plan, ors.transportation_plan, ors.completion_status, ors.achievement, ors.report_content, ors.certificate_path, ors.tenant_id, ors.create_dept, ors.create_by, ors.create_time, ors.update_by, ors.update_time, ors.deleted, + -- 来自申请表的扩展字段 + oa.gender, + CASE oa.gender + WHEN '0' THEN '男' + WHEN '1' THEN '女' + ELSE oa.gender + END as gender_text, + oa.birth_date, + oa.political_status, + CASE oa.political_status + WHEN '1' THEN '中共党员' + WHEN '2' THEN '中共预备党员' + WHEN '3' THEN '共青团员' + WHEN '4' THEN '民革党员' + WHEN '5' THEN '民盟盟员' + WHEN '6' THEN '民建会员' + WHEN '7' THEN '民进会员' + WHEN '8' THEN '农工党党员' + WHEN '9' THEN '致公党党员' + WHEN '10' THEN '九三学社社员' + WHEN '11' THEN '台盟盟员' + WHEN '12' THEN '无党派人士' + WHEN '13' THEN '群众' + ELSE oa.political_status + END as political_status_text, + oa.professional_title, + oa.document_name, + oa.document_number, + oa.mobile_phone, + oa.destination_country, + oa.exit_reason, + oa.emergency_contact, + oa.remarks, + -- 申请状态(来自申请表) + oa.status, + -- 申请状态文本转换 + CASE oa.status + WHEN '0' THEN '待审核' + WHEN '1' THEN '已通过' + WHEN '2' THEN '已拒绝' + ELSE oa.status + END as status_text, + -- 出国目的文本转换 + CASE ors.purpose + WHEN 'academic_exchange' THEN '学术交流' + WHEN 'conference_participation' THEN '参加会议' + WHEN 'collaborative_research' THEN '合作研究' + WHEN 'short_term_training' THEN '短期培训' + WHEN 'other' THEN '其他' + ELSE ors.purpose + END as purpose_text, + -- 完成状态文本转换 + CASE ors.completion_status + WHEN '0' THEN '未完成' + WHEN '1' THEN '进行中' + WHEN '2' THEN '已完成' + WHEN '3' THEN '已取消' + ELSE ors.completion_status + END as completion_status_text, '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' where ors.deleted = '0' UNION ALL @@ -80,6 +220,59 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" oa.invitation, oa.itinerary, oa.accommodation_plan, oa.transportation_plan, '1' as completion_status, NULL as achievement, NULL as report_content, NULL as certificate_path, oa.tenant_id, NULL as create_dept, oa.create_by, oa.create_time, oa.update_by, oa.update_time, oa.deleted, + -- 来自申请表的字段(UNION ALL 第二部分直接从申请表获取) + oa.gender, + CASE oa.gender + WHEN '0' THEN '男' + WHEN '1' THEN '女' + ELSE oa.gender + END as gender_text, + oa.birth_date, + oa.political_status, + CASE oa.political_status + WHEN '1' THEN '中共党员' + WHEN '2' THEN '中共预备党员' + WHEN '3' THEN '共青团员' + WHEN '4' THEN '民革党员' + WHEN '5' THEN '民盟盟员' + WHEN '6' THEN '民建会员' + WHEN '7' THEN '民进会员' + WHEN '8' THEN '农工党党员' + WHEN '9' THEN '致公党党员' + WHEN '10' THEN '九三学社社员' + WHEN '11' THEN '台盟盟员' + WHEN '12' THEN '无党派人士' + WHEN '13' THEN '群众' + ELSE oa.political_status + END as political_status_text, + oa.professional_title, + oa.document_name, + oa.document_number, + oa.mobile_phone, + oa.destination_country, + oa.exit_reason, + oa.emergency_contact, + oa.remarks, + -- 申请状态(来自申请表) + oa.status, + -- 申请状态文本转换(已通过审批的申请) + CASE oa.status + WHEN '0' THEN '待审核' + WHEN '1' THEN '已通过' + WHEN '2' THEN '已拒绝' + ELSE oa.status + END as status_text, + -- 出国目的文本转换 + CASE oa.purpose + WHEN 'academic_exchange' THEN '学术交流' + WHEN 'conference_participation' THEN '参加会议' + WHEN 'collaborative_research' THEN '合作研究' + WHEN 'short_term_training' THEN '短期培训' + WHEN 'other' THEN '其他' + ELSE oa.purpose + END as purpose_text, + -- 完成状态文本(已通过审批未创建记录的状态为"进行中") + '进行中' as completion_status_text, 'approved_application' as data_source from rs_overseas_application oa where oa.deleted = '0' @@ -140,7 +333,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" diff --git a/docs/原型文件/人事管理/recruitment-prototype.html b/docs/原型文件/人事管理/recruitment-prototype.html index e530ed27..d36e96b1 100644 --- a/docs/原型文件/人事管理/recruitment-prototype.html +++ b/docs/原型文件/人事管理/recruitment-prototype.html @@ -612,18 +612,6 @@ -
-
- - -
-
@@ -813,7 +801,6 @@ 职位名称 部门 - 薪资范围 工作地点 发布日期 状态 @@ -826,7 +813,6 @@ 高级Java开发工程师 教学部门 - 15k-25k 杭州 2025-07-01 招聘中 @@ -911,11 +897,6 @@
-
- - -
-
diff --git a/docs/开发文档/API/API文档总览.md b/docs/开发文档/API/API文档总览.md index 06e458c5..c947f27d 100644 --- a/docs/开发文档/API/API文档总览.md +++ b/docs/开发文档/API/API文档总览.md @@ -143,6 +143,13 @@ ## 🔗 API文档导航 +### 👨‍💼 人事管理模块 + +| API文档 | 描述 | 状态 | 更新时间 | +|---------|------|------|----------| +| [出国申请管理API](./出国申请管理API.md) | 出国申请审批流程管理(三级审批) | ✅ 完成 | 2025-01-28 | +| **[出国登记管理API](./出国登记管理API.md)** | **出国登记信息管理(已优化字段)** | **✅ 完成** | **2025-09-30** | + ### 👥 学工管理模块 | API文档 | 描述 | 状态 | 更新时间 | diff --git a/docs/开发文档/API/人事招聘模块API.md b/docs/开发文档/API/人事招聘模块API.md index a593ccff..43edba16 100644 --- a/docs/开发文档/API/人事招聘模块API.md +++ b/docs/开发文档/API/人事招聘模块API.md @@ -21,7 +21,6 @@ | job_id | bigint(20) | 是 | 职位ID,主键 | | title | varchar(100) | 是 | 职位名称 | | department | varchar(50) | 是 | 部门 | -| salary_range | varchar(50) | 是 | 薪资范围 | | location | varchar(100) | 是 | 工作地点 | | description | text | 是 | 职位描述 | | requirements | text | 是 | 任职要求 | @@ -130,7 +129,6 @@ "jobId": 1, "title": "软件工程师", "department": "tech", - "salaryRange": "15k-25k", "location": "北京", "isActive": "0", "publishTime": "2025-07-01 10:00:00", @@ -181,7 +179,6 @@ "jobId": 1, "title": "软件工程师", "department": "tech", - "salaryRange": "15k-25k", "location": "北京", "description": "负责系统设计和开发...", "requirements": "本科及以上学历,3年以上相关经验...", @@ -208,7 +205,7 @@ |-------|------|------|------| | title | string | 是 | 职位名称 | | department | string | 是 | 部门 | -| salaryRange | string | 是 | 薪资范围 | + | location | string | 是 | 工作地点 | | description | string | 是 | 职位描述 | | requirements | string | 是 | 任职要求 | @@ -236,7 +233,6 @@ | jobId | long | 是 | 职位ID | | title | string | 是 | 职位名称 | | department | string | 是 | 部门 | -| salaryRange | string | 是 | 薪资范围 | | location | string | 是 | 工作地点 | | description | string | 是 | 职位描述 | | requirements | string | 是 | 任职要求 | @@ -382,7 +378,7 @@ rs/ 1. 进入职位管理页面 (`/rs/job`) 2. 点击"新增"按钮添加职位 -3. 填写职位信息,包括职位名称、部门、薪资范围等 +3. 填写职位信息,包括职位名称、部门、工作地点等 4. 点击"确定"保存职位信息 ### 申请管理页面 diff --git a/docs/开发文档/API/出国申请管理API.md b/docs/开发文档/API/出国申请管理API.md index dd9347ce..7f16f5af 100644 --- a/docs/开发文档/API/出国申请管理API.md +++ b/docs/开发文档/API/出国申请管理API.md @@ -639,11 +639,21 @@ referer: http://localhost ### 完成状态 (completionStatus) -| 编码 | 含义 | 描述 | -| ---- | -------- | ------------------------ | -| 0 | 进行中 | 出国活动正在进行 | -| 1 | 已完成 | 出国活动已完成 | -| 2 | 提前结束 | 出国活动提前结束 | +> **⚠️ 重要说明**:完成状态(completionStatus)与申请状态(status)是两个不同的概念! +> - **申请状态(status)**:表示审批流程的状态(待审核/已通过/已拒绝) +> - **完成状态(completionStatus)**:表示出国任务执行的状态(未完成/进行中/已完成/已取消) + +| 编码 | 含义 | 描述 | 使用场景 | +| ---- | -------- | ------------------------ | -------- | +| 0 | 未完成 | 出国活动尚未开始 | 已通过审批但尚未出发 | +| 1 | 进行中 | 出国活动正在进行 | 已出发但未返回(默认值) | +| 2 | 已完成 | 出国活动已完成 | 已返回并完成回执 | +| 3 | 已取消 | 出国活动取消 | 计划取消 | + +**字段关系示例**: +- 出国记录中的申请若已通过审批(`status='1'`),默认完成状态为"进行中"(`completionStatus='1'`) +- 出国记录列表显示"完成状态"字段,详情弹窗显示"申请状态"字段 +- 所有出国记录的申请状态都应该是"已通过"(`status='1'`),因为只有通过审批的申请才会出现在记录中 --- @@ -714,6 +724,78 @@ referer: http://localhost | 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') --- diff --git a/docs/开发文档/招聘门户系统最终完成报告.md b/docs/开发文档/招聘门户系统最终完成报告.md index 3a5dab9d..ba7a7316 100644 --- a/docs/开发文档/招聘门户系统最终完成报告.md +++ b/docs/开发文档/招聘门户系统最终完成报告.md @@ -97,7 +97,7 @@ rs_job # 招聘岗位表 ├── job_id # 岗位ID ├── title # 岗位名称 ├── department # 部门 -├── salary_range # 薪资范围 + ├── location # 工作地点 ├── description # 岗位描述 ├── requirements # 任职要求 diff --git a/docs/需求文档/人事管理/培训管理.md b/docs/需求文档/人事管理/培训管理.md index c368070c..b113d762 100644 --- a/docs/需求文档/人事管理/培训管理.md +++ b/docs/需求文档/人事管理/培训管理.md @@ -58,7 +58,7 @@ - 出国申请流程:申请提交 → 部门审核 → 领导审批 → 出国执行 #### 2. **详细字段需求** -- **招聘职位**:职位名称、部门、岗位要求、薪资范围、工作地点等 +- **招聘职位**:职位名称、部门、岗位要求、工作地点等 - **简历信息**:基本信息、教育背景、工作经历、技能证书、附件等 - **培训记录**:培训名称、培训时间、学时、培训类别、证书等 - **出国申请**:出国目的、目的地、时间、预算、行程安排等 diff --git a/docs/需求文档/人事管理/招聘页面需求.md b/docs/需求文档/人事管理/招聘页面需求.md index 5220c883..7106fe3a 100644 --- a/docs/需求文档/人事管理/招聘页面需求.md +++ b/docs/需求文档/人事管理/招聘页面需求.md @@ -9,11 +9,11 @@ cursor 2. 核心功能: - 应聘者能浏览公开职位 - - HR能发布新职位(含薪资范围/要求等字段) + - HR能发布新职位(含工作地点/要求等字段) - 响应式布局(支持移动端) 3. 数据字段参考: - - 职位:title, department, salary_range, location, description, requirements + - 职位:title, department, location, description, requirements - 简历:personal_info, education[], work_experience[], skills 4. 角色权限: @@ -73,7 +73,7 @@ cursor - /apply 简历表单(分教育/工作经历多步骤) 2. 主要功能组件: - - 职位搜索过滤器(部门/薪资范围) + - 职位搜索过滤器(部门/工作地点) - 简历PDF上传组件 - 申请状态跟踪面板 diff --git a/web/src/api/rs/overseas.ts b/web/src/api/rs/overseas.ts index 0ea5f2e1..0b16a2cf 100644 --- a/web/src/api/rs/overseas.ts +++ b/web/src/api/rs/overseas.ts @@ -24,29 +24,57 @@ export interface OverseasApplicationQuery { // 出国申请数据类型 export interface OverseasApplicationVO { applicationId: number | string; + serialNumber?: string; // 编号 applicantId: number; applicantName: string; + gender?: string; // 性别 + birthDate?: string; // 出生日期 + photo?: string; // 照片地址 + idNumber?: string; // 身份证号 + politicalStatus?: string; // 政治面貌 + registeredAddress?: string; // 户籍所在地 department: string; + position?: string; // 职务 + professionalTitle?: string; // 职称 + administrativeLevel?: string; // 行政职级 + personnelCategory?: string; // 人员类别代码 + registrationUnit?: string; // 登记备案责任单位 + unitLeaderSignature?: string; // 单位负责人签名 + unitContact?: string; // 单位联系人 + officePhone?: string; // 办公电话 + mobilePhone?: string; // 移动电话 contactPhone: string; + documentName?: string; // 申领证件名称 + documentNumber?: string; // 证件号码 + destinationCountry?: string; // 前往国家 + exitReason?: string; // 出境事由 + visitRelationship?: string; // 探望对象姓名及与申请者关系 + remarks?: string; // 备注 + emergencyContact?: string; // 应急联系人及电话 + signature?: string; // 手写签字 + stampedRegistrationFilePath?: string; // 登记备案表盖章文件路径 + stampedRegistrationFileName?: string; // 登记备案表盖章文件名 + stampedFileUploadTime?: string; // 盖章文件上传时间 + stampedFileUploadStatus?: string; // 盖章文件上传状态 purpose: string; purposeDetail: string; destination: string; - destinationCity: string; + destinationCity?: string; startDate: string; endDate: string; duration: number; budget: number; - invitation: string; + invitation?: string; itinerary: string; - accommodationPlan: string; - transportationPlan: string; + accommodationPlan?: string; + transportationPlan?: string; status: string; - currentApprover: string; + currentApprover?: string; approvalLevel: string; applyTime: string; - approvalTime: string; - adminRemark: string; - applicantRemark: string; + approvalTime?: string; + adminRemark?: string; + applicantRemark?: string; createTime: string; updateTime: string; createBy: string; @@ -55,15 +83,50 @@ export interface OverseasApplicationVO { statusText: string; purposeText: string; approvalLevelText: string; + genderText?: string; + politicalStatusText?: string; + administrativeLevelText?: string; + personnelCategoryText?: string; } // 出国申请表单类型 export interface OverseasApplicationForm { applicationId?: number | string; + serialNumber?: string; // 编号 applicantId?: number; applicantName?: string; - department?: string; // 自动匹配,前端不需要输入 - contactPhone?: string; // 联系电话 + gender?: string; // 性别 + birthDate?: string; // 出生日期 + photo?: string; // 照片地址 + idNumber?: string; // 身份证号 + politicalStatus?: string; // 政治面貌 + registeredAddress?: string; // 户籍所在地 + department?: string; // 所属部门/现工作部门 + position?: string; // 职务 + professionalTitle?: string; // 职称 + administrativeLevel?: string; // 行政职级 + personnelCategory?: string; // 人员类别代码 + unitLeaderSignature?: string; // 单位负责人签名 + unitContact?: string; // 单位联系人 + officePhone?: string; // 办公电话 + mobilePhone?: string; // 移动电话 + contactPhone?: string; // 联系电话(保留兼容性) + documentName?: string; // 申领证件名称 + documentNumber?: string; // 证件号码 + destinationCountry?: string; // 前往国家 + exitReason?: string; // 出境事由 + visitRelationship?: string; // 探望对象姓名及与申请者关系 + remarks?: string; // 备注 + emergencyContact?: string; // 应急联系人及电话 + signature?: string; // 手写签字(base64格式) + stampedRegistrationFilePath?: string; // 登记备案表盖章文件路径 + stampedRegistrationFileName?: string; // 登记备案表盖章文件名 + stampedFileUploadTime?: string; // 盖章文件上传时间 + stampedFileUploadStatus?: string; // 盖章文件上传状态:0-未上传,1-已上传 + partyRetentionFilePath?: string; // 党籍保留附件文件路径 + partyRetentionFileName?: string; // 党籍保留附件文件名 + partyRetentionUploadTime?: string; // 党籍保留附件上传时间 + partyRetentionUploadStatus?: string; // 党籍保留附件上传状态:0-未上传,1-已上传 purpose: string; purposeDetail: string; destination: string; @@ -111,7 +174,9 @@ export interface OverseasRecordVO { applicantId: number; applicantName: string; department: string; + position: string; contactPhone: string; + contactEmail: string; purpose: string; purposeDetail: string; destination: string; @@ -134,7 +199,23 @@ export interface OverseasRecordVO { updateBy: string; // 显示字段 purposeText: string; + status?: string; // 申请状态(来自申请表) + statusText?: string; // 申请状态文本(来自申请表) completionStatusText: string; + // 来自申请表的扩展字段 + gender?: string; + genderText?: string; + birthDate?: string; + politicalStatus?: string; + politicalStatusText?: string; + professionalTitle?: string; + documentName?: string; + documentNumber?: string; + mobilePhone?: string; + destinationCountry?: string; + exitReason?: string; + emergencyContact?: string; + remarks?: string; } // 出国记录表单类型 @@ -255,7 +336,7 @@ export function listOverseasApplicationByStatus(status: string) { // 查询出国申请详细 export function getOverseasApplication(applicationId: string | number) { return request({ - url: `/rs/overseas/application/${applicationId}`, + url: `/rs/overseas/application/detail/${applicationId}`, method: 'get' }); } @@ -295,6 +376,18 @@ export function resubmitOverseasApplication(applicationId: string | number, data }); } +// 校验出国申请前置条件 +export function validateApplicationPrerequisites(politicalStatus?: string, duration?: number) { + return request({ + url: '/rs/overseas/application/validatePrerequisites', + method: 'get', + params: { + politicalStatus, + duration + } + }); +} + // 取消出国申请 export function cancelOverseasApplication(applicationId: string | number, reason?: string) { return request({ @@ -520,3 +613,153 @@ export function fixApprovedOverseasRecords() { method: 'post' }); } + +// ==================== PDF生成相关接口 ==================== + +// 生成登记表请求参数类型 +export interface GenerateRegistrationFormRequest { + applicationId: number; + documentNumber?: string; + format?: string; +} + +// 登记表生成响应类型 +export interface RegistrationFormResponse { + documentId: string; + fileName: string; + downloadUrl: string; + previewUrl: string; + fileSize: number; + generateTime: string; + documentNumber: string; + applicationId: number; +} + +// 生成出国申请登记表PDF +export function generateRegistrationForm(data: GenerateRegistrationFormRequest) { + return request({ + url: '/rs/overseas/application/generateRegistrationForm', + method: 'post', + data: data + }); +} + +// 下载生成的PDF文档 +export function downloadRegistrationFormPdf(documentId: string) { + return request({ + url: `/rs/overseas/application/downloadPdf/${documentId}`, + method: 'get', + responseType: 'blob' + }); +} + +// 预览生成的PDF文档 +export function previewRegistrationFormPdf(documentId: string) { + return request({ + url: `/rs/overseas/application/previewPdf/${documentId}`, + method: 'get', + responseType: 'blob' + }); +} + +// 下载登记备案附件模板 +export function downloadRegistrationTemplate() { + return request({ + url: '/rs/overseas/application/downloadRegistrationTemplate', + method: 'get', + responseType: 'blob' + }); +} + +// 下载保留党籍申请模板 +export function downloadPartyMembershipTemplate() { + return request({ + url: '/rs/overseas/application/downloadPartyMembershipTemplate', + method: 'get', + responseType: 'blob' + }); +} + +// ==================== 出国申请附件相关API ==================== + +// 附件数据类型 +export interface OverseasAttachmentVO { + attachmentId: number | string; + applicationId: number; + attachmentTitle: string; + attachmentContent?: string; + fileName: string; + fileOriginalName: string; + filePath: string; + fileSize: number; + fileSizeFormatted?: string; + fileType?: string; + mimeType?: string; + uploadTime?: string; + uploadUserId?: number; + uploadUserName?: string; + createTime?: string; + updateTime?: string; +} + +// 附件BO +export interface OverseasAttachmentBo { + attachmentId?: number | string; + applicationId: number; + attachmentTitle: string; + attachmentContent?: string; +} + +// 查询出国申请的附件列表 +export function listOverseasAttachments(applicationId: string | number) { + return request({ + url: `/rs/overseas/attachment/list/${applicationId}`, + method: 'get' + }); +} + +// 查询附件详情 +export function getOverseasAttachment(attachmentId: string | number) { + return request({ + url: `/rs/overseas/attachment/${attachmentId}`, + method: 'get' + }); +} + +// 上传附件 +export function uploadOverseasAttachment(data: FormData) { + return request({ + url: '/rs/overseas/attachment/upload', + method: 'post', + headers: { + 'Content-Type': 'multipart/form-data' + }, + data: data + }); +} + +// 更新附件信息(标题和内容) +export function updateOverseasAttachment(data: OverseasAttachmentBo) { + return request({ + url: '/rs/overseas/attachment', + method: 'put', + data: data + }); +} + +// 删除附件 +export function deleteOverseasAttachment(attachmentId: string | number) { + return request({ + url: `/rs/overseas/attachment/${attachmentId}`, + method: 'delete' + }); +} + +// 批量删除附件 +export function deleteOverseasAttachments(attachmentIds: Array) { + return request({ + url: '/rs/overseas/attachment/batch', + method: 'delete', + data: attachmentIds + }); +} diff --git a/web/src/views/rs/index.vue b/web/src/views/rs/index.vue index 7a6b01b6..1b8b76e1 100644 --- a/web/src/views/rs/index.vue +++ b/web/src/views/rs/index.vue @@ -75,7 +75,6 @@ {{ getDepartmentLabel(scope.row.department) }} -