diff --git a/.cursor/cursorrules-mobile.md b/.cursor/cursorrules-mobile.md
index f2bbb71e..fbab21de 100644
--- a/.cursor/cursorrules-mobile.md
+++ b/.cursor/cursorrules-mobile.md
@@ -358,6 +358,51 @@ export default {
## 5. 🎨 图标使用规范 (SVG Data URI 方案)
+### 🚨 强制性图标使用规范(2025-08-06 新增)
+
+**⚠️ 重要说明**: 移动端项目已全面禁用 uview-plus 的 u-icon 组件,强制使用 iconfont 图标系统!
+
+#### 📋 强制要求
+
+1. **🚫 禁止使用 u-icon 组件**
+ ```vue
+
+
+
+
+ ```
+
+2. **✅ 必须使用 iconfont 图标**
+ ```vue
+
+
+
+
+ ```
+
+3. **🔄 动态图标类名**
+ ```vue
+
+
+ ```
+
+#### 🎯 替换映射表
+
+| u-icon name | iconfont class | 说明 |
+|-------------|----------------|------|
+| `search` | `icon-search` | 搜索图标 |
+| `account` | `icon-account-circle` | 用户账户图标 |
+| `close` | `icon-close` | 关闭图标 |
+| `email` | `icon-email` | 邮件图标 |
+| `arrow-right` | `icon-arrow-right` | 右箭头图标 |
+| `arrow-down` | `icon-arrow-down` | 下箭头图标 |
+| `home` | `icon-home` | 首页图标 |
+| `setting` | `icon-setting` | 设置图标 |
+
### 背景说明
由于 uview-plus 的 u-icon 组件在某些环境下存在显示异常(显示为横线),项目采用 **SVG Data URI** 方案实现图标系统,确保跨平台兼容性和显示稳定性。
@@ -369,6 +414,7 @@ export default {
- ✅ **加载稳定**: 无 404 错误,无网络请求,性能优异
- ✅ **样式灵活**: 通过 CSS filter 实现颜色自定义
- ✅ **维护简单**: 基于 Material Design,图标清晰美观
+- ✅ **显示稳定**: 彻底解决 u-icon 组件显示异常问题
### 图标配置文件
@@ -1784,3 +1830,152 @@ npm run lint:fix
**📝 维护**: 移动端开发规范文档
**🔄 更新**: 2025-01-03 (新增登录状态管理规范)
**📁 文件**: .cursor/cursorrules-mobile.md
+
+# uview-plus 图标组件规范(2025-07-01 新增)
+- 移动端UI开发中,图标组件统一使用 (uview-plus 3.x 官方标准),禁止使用 或 。
+- 推荐通过 easycom 自动全局注册,无需手动 import。
+- 标准用法示例:
+ ```vue
+
+
+ ```
+- 图标名称请参考 uview-plus 官方文档:https://v3.uviewui.com/components/icon.html
+
+## uview-plus 图标字体配置规范(2025-08-06 更新)
+
+### 📦 版本升级经验总结
+
+**升级记录**: uview-plus 3.4.45 → 3.4.73 (+28个版本)
+**升级时间**: 2025-08-06
+**配置优化**: 采用官方推荐的字体加载机制
+
+### 🔧 官方推荐配置方案
+
+#### 方案1: 自托管字体配置(官方推荐 ⭐)
+
+**参考文档**: https://uview-plus.jiangruyi.com/components/icon.html#自定义默认字体图标自托管资源
+
+```javascript
+// main.js - 官方推荐配置方式
+app.use(uviewPlus, () => {
+ return {
+ options: {
+ config: {
+ // 🔧 自托管字体图标资源地址,避免CDN依赖问题
+ iconUrl: '/static/fonts/uview-plus-official.ttf'
+ }
+ }
+ }
+});
+```
+
+#### 方案2: 手动加载字体(官方兼容性方案)
+
+**参考文档**: https://uview-plus.jiangruyi.com/components/icon.html#新版本全局加载兼容性
+
+```javascript
+// App.vue - 如果在小程序等场景出现图标无法全局加载
+import { fontUtil } from 'uview-plus';
+
+export default {
+ onLaunch: function () {
+ // 官方推荐:手动加载uview-plus字体图标
+ fontUtil.loadFont();
+ }
+};
+```
+
+#### 方案3: CSS备用方案(平台兼容)
+
+```css
+/* App.vue - 其他平台如抖音/QQ/百度小程序的备用方案 */
+@font-face {
+ font-family: 'uicon-iconfont';
+ src: url('/static/fonts/uview-plus-official.ttf') format('truetype');
+}
+```
+
+### 🏆 最佳实践配置
+
+```javascript
+// main.js - 完整配置示例
+import { createSSRApp } from 'vue';
+import App from './App.vue';
+import uviewPlus from 'uview-plus';
+import 'uview-plus/index.scss';
+import '@/static/fonts/iconfont.css'; // 自定义SVG图标库
+
+export function createApp() {
+ const app = createSSRApp(App);
+
+ // 🔧 官方推荐:uview-plus配置
+ app.use(uviewPlus, () => {
+ return {
+ options: {
+ config: {
+ iconUrl: '/static/fonts/uview-plus-official.ttf'
+ }
+ }
+ }
+ });
+
+ return { app };
+}
+```
+
+```javascript
+// App.vue - 兼容性配置
+import { fontUtil } from 'uview-plus';
+
+export default {
+ onLaunch: function () {
+ // 🔧 官方推荐:手动加载字体图标
+ try {
+ fontUtil.loadFont();
+ console.log('✅ 手动加载uview-plus字体成功');
+ } catch (error) {
+ console.error('❌ 手动加载uview-plus字体失败:', error);
+ }
+ }
+};
+```
+
+#### 混合图标使用策略
+- **uview-plus图标**: 使用 `` 系统通用图标
+- **项目iconfont**: 使用 `` 业务专用图标
+- **优先级**: 项目iconfont > uview-plus图标 > CDN字体
+
+### ⚠️ 重要注意事项
+
+#### 升级前检查
+1. **字体文件**: 确保本地字体文件存在且可访问
+2. **网络环境**: 测试CDN地址访问性
+3. **版本兼容**: 检查字体版本与组件版本匹配
+
+#### 最佳实践
+1. **优先级**: 项目iconfont > 本地字体 > CDN字体
+2. **测试覆盖**: 在不同网络环境下测试图标显示
+3. **回退机制**: 准备备用图标方案
+4. **文档记录**: 及时更新字体配置变更
+
+### 📚 相关文件位置
+- **字体配置**: `mobile/src/main.js`
+- **测试页面**: `mobile/src/pages/demo/uview-icon-test/index.vue`
+- **字体文件**: `mobile/src/static/fonts/`
+- **项目图标**: `mobile/src/static/fonts/iconfont.css`
+
+### 🧪 测试验证
+```bash
+# 启动开发服务器
+npm run dev:h5
+
+# 访问测试页面
+http://localhost:3000/#/pages/demo/uview-icon-test/index
+```
+
+### 📖 参考文档
+- uview-plus官方文档: https://uview-plus.jiangruyi.com/
+- 字体图标组件: https://uview-plus.jiangruyi.com/components/icon.html
+- 自托管配置: https://uview-plus.jiangruyi.com/components/icon.html#自定义默认字体图标自托管资源
+- 兼容性配置: https://uview-plus.jiangruyi.com/components/icon.html#新版本全局加载兼容性
+- 项目图标文档: `docs/开发文档/API/u-icon替换映射表.md`
diff --git a/.cursorignore b/.cursorignore
index 4535580f..cc9e232e 100644
--- a/.cursorignore
+++ b/.cursorignore
@@ -1,3 +1,2 @@
# Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv)
-temp/
-test/
+
diff --git a/.gitignore b/.gitignore
index 37bad9ff..5d722a07 100644
--- a/.gitignore
+++ b/.gitignore
@@ -161,4 +161,11 @@ coverage/
*.crt
*.p12
*.keystore/xgxt/
-excel/
\ No newline at end of file
+excel/
+
+# 吴自龙注释
+test/
+script/
+stgl-api-test.js
+update/
+本地临时文件目录/
\ No newline at end of file
diff --git a/backend/ruoyi-admin/src/main/resources/add_batch_id.sql b/backend/ruoyi-admin/src/main/resources/add_batch_id.sql
deleted file mode 100644
index fab91af0..00000000
--- a/backend/ruoyi-admin/src/main/resources/add_batch_id.sql
+++ /dev/null
@@ -1,2 +0,0 @@
--- 添加批次ID字段
-ALTER TABLE xljk_consultation_schedule ADD COLUMN batch_id VARCHAR(50) DEFAULT NULL COMMENT '批次ID,用于标识同一批次创建的预约时段';
\ No newline at end of file
diff --git a/backend/ruoyi-admin/src/main/resources/application-dev.yml b/backend/ruoyi-admin/src/main/resources/application-dev.yml
index e246ed56..9bb468c0 100644
--- a/backend/ruoyi-admin/src/main/resources/application-dev.yml
+++ b/backend/ruoyi-admin/src/main/resources/application-dev.yml
@@ -128,7 +128,7 @@ spring.data:
# 端口,默认为6379
port: 16379
# 数据库索引
- database: 0
+ database: 8
# redis 密码必须配置
password: Hzdyjs@123456
# 连接超时时间
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-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java b/backend/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java
index 1d5c3c9d..8c690e34 100644
--- a/backend/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java
+++ b/backend/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java
@@ -119,6 +119,12 @@ public class PageQuery implements Serializable {
return (pageNum - 1) * pageSize;
}
+ /**
+ * 无参构造器
+ */
+ public PageQuery() {
+ }
+
public PageQuery(Integer pageSize, Integer pageNum) {
this.pageSize = pageSize;
this.pageNum = pageNum;
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..aebb3a0e
--- /dev/null
+++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/config/DbInitializer.java
@@ -0,0 +1,35 @@
+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);
+ // 这里选择记录错误但不中断应用启动,因为可能是表结构已经更新过
+ // 如果需要严格控制,可以抛出异常中断应用启动
+ }
+ }
+}
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