diff --git a/backend/ruoyi-admin/src/main/resources/application.yml b/backend/ruoyi-admin/src/main/resources/application.yml
index a4624da3..cc5fe455 100644
--- a/backend/ruoyi-admin/src/main/resources/application.yml
+++ b/backend/ruoyi-admin/src/main/resources/application.yml
@@ -92,6 +92,10 @@ spring:
deserialization:
# 允许对象忽略json中不存在的属性
fail_on_unknown_properties: false
+ # thymeleaf模板配置 weizhong
+ thymeleaf:
+ # 是否检查模板位置
+ check-template-location: false
# Sa-Token配置
sa-token:
@@ -124,6 +128,10 @@ security:
- /v3/api-docs/**
- /webjars/**
- /warm-flow-ui/config
+ # 招聘门户公开接口 weizhong
+ - /rs/job/public/**
+ - /rs/job/view/**
+ - /rs/application/submit
- /system/dict/data/type/*
# 多租户配置
diff --git a/backend/ruoyi-modules/ruoyi-rs/pom.xml b/backend/ruoyi-modules/ruoyi-rs/pom.xml
index 6c6d0014..d29f672e 100644
--- a/backend/ruoyi-modules/ruoyi-rs/pom.xml
+++ b/backend/ruoyi-modules/ruoyi-rs/pom.xml
@@ -104,6 +104,18 @@
ruoyi-workflow
+
+
+ org.dromara
+ ruoyi-common-satoken
+
+
+
+
+ com.itextpdf
+ itextpdf
+ 5.5.13.3
+
diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/config/DbInitializer.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/config/DbInitializer.java
new file mode 100644
index 00000000..196d9325
--- /dev/null
+++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/config/DbInitializer.java
@@ -0,0 +1,34 @@
+package org.dromara.rs.config;
+
+import org.dromara.rs.util.SqlScriptRunner;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.stereotype.Component;
+
+/**
+ * 数据库初始化器,在应用启动时执行SQL脚本
+ */
+@Component
+public class DbInitializer implements CommandLineRunner {
+
+ private static final Logger logger = LoggerFactory.getLogger(DbInitializer.class);
+
+ @Autowired
+ private SqlScriptRunner sqlScriptRunner;
+
+ @Override
+ public void run(String... args) throws Exception {
+ try {
+ logger.info("开始执行数据库升级脚本...");
+ // 执行升级脚本
+ sqlScriptRunner.executeScript("mapper/rs/update-rs-application-table.sql");
+ logger.info("数据库升级脚本执行完成");
+ } catch (Exception e) {
+ logger.error("执行数据库升级脚本失败", e);
+ // 这里选择记录错误但不中断应用启动,因为可能是表结构已经更新过
+ // 如果需要严格控制,可以抛出异常中断应用启动
+ }
+ }
+}
\ No newline at end of file
diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsApplicationController.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsApplicationController.java
new file mode 100644
index 00000000..05e3f256
--- /dev/null
+++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsApplicationController.java
@@ -0,0 +1,349 @@
+package org.dromara.rs.controller;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.common.core.utils.file.MimeTypeUtils;
+import org.dromara.common.core.utils.DateUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.core.exception.ServiceException;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.dev33.satoken.annotation.SaIgnore;
+import jakarta.servlet.http.HttpServletResponse;
+import org.dromara.rs.domain.RsApplication;
+import org.dromara.rs.service.IRsApplicationService;
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.itextpdf.text.BaseColor;
+import com.itextpdf.text.Chunk;
+import com.itextpdf.text.Document;
+import com.itextpdf.text.Element;
+import com.itextpdf.text.Font;
+import com.itextpdf.text.FontFactory;
+import com.itextpdf.text.PageSize;
+import com.itextpdf.text.Paragraph;
+import com.itextpdf.text.pdf.PdfWriter;
+
+/**
+ * 应聘申请Controller
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/rs/application")
+public class RsApplicationController extends BaseController {
+ @Autowired
+ private IRsApplicationService applicationService;
+
+ /**
+ * 查询应聘申请列表
+ */
+ @SaCheckPermission("rs:application:list")
+ @GetMapping("/list")
+ public TableDataInfo list(RsApplication application, PageQuery pageQuery) {
+ List list = applicationService.selectRsApplicationList(application);
+ return TableDataInfo.build(list);
+ }
+
+ /**
+ * 查询指定职位的应聘申请
+ */
+ @SaCheckPermission("rs:application:list")
+ @GetMapping("/list/{jobId}")
+ public TableDataInfo listByJobId(@PathVariable("jobId") Long jobId, RsApplication application, PageQuery pageQuery) {
+ application.setJobId(jobId);
+ List list = applicationService.selectRsApplicationList(application);
+ return TableDataInfo.build(list);
+ }
+
+ /**
+ * 导出应聘申请列表
+ */
+ @SaCheckPermission("rs:application:export")
+ @Log(title = "应聘申请", businessType = BusinessType.EXPORT)
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, RsApplication application) {
+ List list = applicationService.selectRsApplicationList(application);
+ ExcelUtil.exportExcel(list, "申请记录数据", RsApplication.class, response);
+ }
+
+ /**
+ * 导出简历为PDF
+ */
+ @SaCheckPermission("rs:application:export")
+ @Log(title = "简历导出", businessType = BusinessType.EXPORT)
+ @GetMapping("/exportPdf/{applicationId}")
+ public void exportPdf(@PathVariable("applicationId") Long applicationId, HttpServletResponse response) throws IOException {
+ // 获取应聘申请详情
+ RsApplication application = applicationService.selectRsApplicationById(applicationId);
+ if (application == null) {
+ throw new ServiceException("未找到指定的简历记录");
+ }
+
+ // 临时测试:导出文本文件而不是PDF
+ response.setContentType("text/plain");
+ String fileName = URLEncoder.encode("简历_" + application.getApplicantName() + "_" + System.currentTimeMillis(), "UTF-8");
+ response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".txt");
+
+ // 设置允许跨域
+ response.setHeader("Access-Control-Allow-Origin", "*");
+ response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
+ response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
+
+ try {
+ // 直接写入文本内容
+ response.getWriter().write("应聘者简历详情\n\n");
+
+ // 添加基本信息部分
+ response.getWriter().write("=== 基本信息 ===\n");
+ response.getWriter().write("姓名: " + application.getApplicantName() + "\n");
+ response.getWriter().write("应聘职位: " + application.getJobTitle() + "\n");
+ response.getWriter().write("联系电话: " + application.getContactPhone() + "\n");
+ response.getWriter().write("联系邮箱: " + application.getContactEmail() + "\n");
+
+ // 添加申请状态和时间
+ String status = "未知";
+ switch (application.getStatus()) {
+ case "0":
+ status = "待处理";
+ break;
+ case "1":
+ status = "已通过";
+ break;
+ case "2":
+ status = "已拒绝";
+ break;
+ default:
+ status = "未知";
+ }
+ response.getWriter().write("申请状态: " + status + "\n");
+ response.getWriter().write("申请时间: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(application.getApplyTime()) + "\n\n");
+
+ // 添加教育背景
+ response.getWriter().write("=== 教育背景 ===\n");
+ response.getWriter().write("最高学历: " + (application.getEducation() != null ? application.getEducation() : "未提供") + "\n");
+ if (application.getEducationBackground() != null) {
+ response.getWriter().write("教育经历: " + application.getEducationBackground() + "\n\n");
+ }
+
+ // 添加工作经验
+ response.getWriter().write("=== 工作经验 ===\n");
+ response.getWriter().write(application.getExperience() != null ? application.getExperience() : "未提供工作经验");
+ response.getWriter().write("\n\n");
+
+ // 添加自我介绍
+ response.getWriter().write("=== 自我介绍 ===\n");
+ response.getWriter().write(application.getSelfDescription() != null ? application.getSelfDescription() : "未提供自我介绍");
+ response.getWriter().write("\n\n");
+
+ // 添加生成日期
+ response.getWriter().write("\n生成日期:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
+ } catch (Exception e) {
+ throw new ServiceException("导出文件失败:" + e.getMessage());
+ }
+ }
+
+ /**
+ * 获取应聘申请详细信息
+ */
+ @SaCheckPermission("rs:application:query")
+ @GetMapping(value = "/{applicationId}")
+ public R getInfo(@PathVariable("applicationId") Long applicationId) {
+ return R.ok(applicationService.selectRsApplicationById(applicationId));
+ }
+
+ /**
+ * 新增应聘申请
+ */
+ @SaCheckPermission("rs:application:add")
+ @Log(title = "应聘申请", businessType = BusinessType.INSERT)
+ @PostMapping
+ public R add(@RequestBody RsApplication application) {
+ // 设置默认状态
+ if (StringUtils.isEmpty(application.getStatus())) {
+ application.setStatus("pending");
+ }
+ return toAjax(applicationService.insertRsApplication(application));
+ }
+
+ /**
+ * 上传附件
+ */
+ @SaCheckPermission("rs:application:edit")
+ @Log(title = "上传应聘附件", businessType = BusinessType.UPDATE)
+ @PostMapping("/attachment/{applicationId}")
+ public R