Просмотр исходного кода

1.完成公益性岗位与岗位推荐模块
2.新增重点关注人员管理模块和见习人员管理模块的推送岗位功能
3.新增招聘会管理模块
4.新增小程序需要的接口

kk 4 дней назад
Родитель
Сommit
31dc8d9f57
100 измененных файлов с 3504 добавлено и 1069 удалено
  1. 37 0
      .docs/260603-公益性岗位管理与岗位推荐模块开发记录.md
  2. 44 1
      .docs/260603-重点关注人员管理模块开发记录.md
  3. 0 0
      .docs/260605-前端-公益性岗位管理模块完善.md
  4. 0 6
      jeecgboot-vue3/修改记录-见习人员模块改造.md
  5. 49 0
      .docs/260605-见习岗位管理_公益性岗位管理_岗位推荐模块完善开发记录.md
  6. 12 0
      .docs/260609-职业指导服务模块开发记录.md
  7. 165 0
      .docs/260612-招聘会管理模块开发记录.md
  8. 3 0
      .docs/sql/岗位推荐视图.sql
  9. 87 0
      .docs/sql/招聘会管理-关联表与字段补充.sql
  10. 31 0
      .docs/sql/招聘会管理-字典数据.sql
  11. 52 0
      .docs/sql/招聘会管理-建表.sql
  12. 37 0
      .docs/sql/招聘会管理-测试数据.sql
  13. 83 0
      .docs/sql/招聘会管理-菜单权限.sql
  14. 3 0
      .docs/sql/新增模块清理与重新执行.sql
  15. 20 1
      .docs/sql/职业指导服务-DDL修复.sql
  16. 59 11
      .docs/sql/职业指导服务-测试数据.sql
  17. 14 0
      .docs/sql/职业指导服务-补充字典数据.sql
  18. 26 0
      .docs/sql/职业指导服务-视图年龄修复.sql
  19. 1 1
      .docs/sql/职业指导服务-达梦表与视图.sql
  20. 14 0
      .docs/就业援助模块开发记录.md
  21. 44 0
      .docs/开发记录_见习岗位与见习人员管理模块.md
  22. 12 0
      .docs/模块开发说明-就业状态自动感知.md
  23. 12 0
      .docs/模块开发说明-政策推送和岗位信息变动推送.md
  24. BIN
      doc/参考ui页面.png
  25. 563 772
      doc/报错信息.txt
  26. 4 0
      jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/ShiroConfig.java
  27. 54 1
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/careerguidanceservice/controller/CareerGuidanceServiceController.java
  28. 13 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/careerguidanceservice/mapper/xml/CareerGuidanceRecordMapper.xml
  29. 1 1
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/deepAnalysis/absorbDifficultSubsidy/service/impl/DaAbsorbDifficultSubsidyServiceImpl.java
  30. 1 1
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/dictionary/service/IDictionaryItemService.java
  31. 1 1
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/dictionary/service/impl/DictionaryItemServiceImpl.java
  32. 2 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/employmentassistance/controller/EmploymentAssistanceController.java
  33. 31 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/employmentassistance/service/impl/EmploymentAssistanceServiceImpl.java
  34. 2 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/employmentregistration/controller/EmploymentRegistrationController.java
  35. 31 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/employmentregistration/service/impl/EmploymentRegistrationServiceImpl.java
  36. 61 1
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/focuspersonnel/controller/FocusPersonnelController.java
  37. 12 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/focuspersonnel/entity/FocusPersonnelPageVo.java
  38. 2 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/focuspersonnel/service/IFocusPersonnelService.java
  39. 73 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/focuspersonnel/service/impl/FocusPersonnelServiceImpl.java
  40. 71 1
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/internshippersonnel/controller/InternshipPersonnelController.java
  41. 19 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/internshippersonnel/entity/InternshipPersonnelPageVo.java
  42. 2 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/internshippersonnel/service/IInternshipPersonnelService.java
  43. 73 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/internshippersonnel/service/impl/InternshipPersonnelServiceImpl.java
  44. 17 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/jobrecommend/controller/JobRecommendController.java
  45. 30 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/jobrecommend/dto/AvailablePostVo.java
  46. 2 2
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/jobrecommend/entity/JobRecommendPageVo.java
  47. 11 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/jobrecommend/mapper/JobRecommendMapper.java
  48. 50 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/jobrecommend/mapper/xml/JobRecommendMapper.xml
  49. 5 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/jobrecommend/service/IJobRecommendService.java
  50. 16 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/jobrecommend/service/impl/JobRecommendServiceImpl.java
  51. 77 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/notification/controller/NotificationController.java
  52. 191 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/personal/controller/ResumeInfoController.java
  53. 1 1
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/personalstatus/controller/PersonalStatusController.java
  54. 2 1
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/personalstatus/entity/PersonalStatusPageVo.java
  55. 6 6
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/personalstatus/service/impl/PersonalStatusServiceImpl.java
  56. 2 2
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/policyfile/controller/PolicyCategoryController.java
  57. 10 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/policyfile/controller/PolicyFileController.java
  58. 4 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/policyfile/mapper/PolicyCategoryMapper.java
  59. 9 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/policyfile/mapper/xml/PolicyCategoryMapper.xml
  60. 4 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/policyfile/service/IPolicyCategoryService.java
  61. 11 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/policyfile/service/impl/PolicyCategoryServiceImpl.java
  62. 65 1
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/preliminaryeligible/controller/PreliminaryEligibleController.java
  63. 14 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/preliminaryeligible/entity/PreliminaryEligiblePageVo.java
  64. 3 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/preliminaryeligible/service/IPreliminaryEligibleService.java
  65. 77 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/preliminaryeligible/service/impl/PreliminaryEligibleServiceImpl.java
  66. 2 1
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/profilechange/controller/ProfileChangeController.java
  67. 3 2
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/profilechange/entity/ProfileChangePageVo.java
  68. 308 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/recruitmentfair/controller/RecruitmentFairController.java
  69. 124 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/recruitmentfair/entity/RecruitmentFair.java
  70. 43 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/recruitmentfair/entity/RecruitmentFairEnterprise.java
  71. 48 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/recruitmentfair/entity/RecruitmentFairPersonal.java
  72. 7 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/recruitmentfair/mapper/RecruitmentFairEnterpriseMapper.java
  73. 15 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/recruitmentfair/mapper/RecruitmentFairMapper.java
  74. 7 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/recruitmentfair/mapper/RecruitmentFairPersonalMapper.java
  75. 4 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/recruitmentfair/mapper/xml/RecruitmentFairEnterpriseMapper.xml
  76. 5 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/recruitmentfair/mapper/xml/RecruitmentFairMapper.xml
  77. 4 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/recruitmentfair/mapper/xml/RecruitmentFairPersonalMapper.xml
  78. 15 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/recruitmentfair/service/IRecruitmentFairService.java
  79. 19 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/recruitmentfair/service/impl/RecruitmentFairServiceImpl.java
  80. 36 2
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/welfarepost/controller/WelfarePostController.java
  81. 5 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/welfarepost/service/IWelfarePostService.java
  82. 45 1
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/welfarepost/service/impl/WelfarePostServiceImpl.java
  83. 88 0
      jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V20260612_1__menu_insert_RecruitmentFair.sql
  84. BIN
      jeecgboot-vue3/node_modules.7z
  85. 1 1
      jeecgboot-vue3/src/components/Application/src/AppLogo.vue
  86. 13 20
      jeecgboot-vue3/src/hooks/dictionary/useDict.ts
  87. 9 10
      jeecgboot-vue3/src/layouts/default/header/index.less
  88. 3 2
      jeecgboot-vue3/src/layouts/default/header/index.vue
  89. 2 1
      jeecgboot-vue3/src/settings/designSetting.ts
  90. 4 4
      jeecgboot-vue3/src/settings/projectSetting.ts
  91. 17 0
      jeecgboot-vue3/src/views/careerguidancedocument/CareerGuidanceDocument.data.ts
  92. 12 7
      jeecgboot-vue3/src/views/careerguidancedocument/CareerGuidanceDocumentList.vue
  93. 2 5
      jeecgboot-vue3/src/views/careerguidancedocument/components/CareerGuidanceDocumentForm.vue
  94. 30 147
      jeecgboot-vue3/src/views/careerguidanceservice/CareerGuidanceService.data.ts
  95. 31 19
      jeecgboot-vue3/src/views/careerguidanceservice/CareerGuidanceServiceList.vue
  96. 90 0
      jeecgboot-vue3/src/views/careerguidanceservice/components/CareerGuidanceServiceDetail.vue
  97. 68 32
      jeecgboot-vue3/src/views/careerguidanceservice/components/CareerGuidanceServiceModal.vue
  98. 8 2
      jeecgboot-vue3/src/views/employmentassistance/EmploymentAssistanceList.vue
  99. 8 2
      jeecgboot-vue3/src/views/employmentregistration/EmploymentRegistrationList.vue
  100. 0 0
      jeecgboot-vue3/src/views/focuspersonnel/FocusPersonnelList.vue

+ 37 - 0
.docs/260603-公益性岗位管理与岗位推荐模块开发记录.md

@@ -142,3 +142,40 @@
 7. 前端表单下拉框使用 `a-select` 硬编码选项
 8. Flyway SQL中INSERT语句每行一个,不多行VALUES
 9. 岗位推荐模块依赖视图 `v_job_recommend_list`,需先执行视图创建SQL
+
+---
+
+## 2026-06-12 导出字典翻译修复
+
+### 问题
+导出Excel时岗位类型、报名方式、发布状态、岗位状态等字典字段显示为原始编码。
+
+### 修复文件
+| 文件 | 修改 |
+|------|------|
+| `IWelfarePostService.java` | 新增 `translateDictFields()` 方法 |
+| `WelfarePostServiceImpl.java` | 注入 `IDictionaryItemService`,批量查询字典 → switch-case替换字段值为标签 |
+| `WelfarePostController.java` | 重写 `exportXls`:QueryGenerator查询 → 过滤选中 → 字典翻译 → 导出为 `.xlsx` |
+
+---
+
+## 2026-06-12 岗位推送功能实现
+
+### 问题
+重点关注人员管理和见习人员管理的"岗位推送"按钮为占位状态,需实现手动选择岗位推送功能。岗位推送本质是新增一条岗位推荐记录,复用现有 `job_recommend` 表。
+
+### 后端修改(JobRecommend 模块)
+| 文件 | 修改 |
+|------|------|
+| `jobrecommend/dto/AvailablePostVo.java` | **新增** — 可选岗位VO |
+| `jobrecommend/mapper/xml/JobRecommendMapper.xml` | **新增** — `queryAvailablePosts` SQL:UNION 查询三表(internship_post / welfare_post / post_info),仅查已发布岗位,支持 keyword 模糊搜索 |
+| `jobrecommend/mapper/JobRecommendMapper.java` | **新增** — `queryAvailablePosts()` 方法 |
+| `jobrecommend/service/IJobRecommendService.java` | **新增** — `queryAvailablePosts()` 接口 |
+| `jobrecommend/service/impl/JobRecommendServiceImpl.java` | **新增** — `queryAvailablePosts()` 实现 |
+| `jobrecommend/controller/JobRecommendController.java` | **新增** — `GET /jobRecommend/availablePosts` 端点(支持 postType + keyword 过滤) |
+
+### 前端修改
+| 文件 | 修改 |
+|------|------|
+| `jobrecommend/JobRecommend.api.ts` | **新增** — `listAvailablePosts()` + `pushJob()` API |
+| `jobrecommend/components/JobPushModal.vue` | **新增** — 通用岗位推送弹窗(岗位类型筛选、岗位名称搜索、单选表格、推荐意见输入),FocusPersonnel 和 InternshipPersonnel 共用 |

+ 44 - 1
.docs/260603-重点关注人员管理模块开发记录.md

@@ -366,4 +366,47 @@ import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectT
 ### 11.4 涉及文件清单
 
 1. `FocusPersonnelList.vue` — 添加组件导入
-2. `FocusPersonnelForm.vue` — 添加组件导入
+2. `FocusPersonnelForm.vue` — 添加组件导入
+---
+
+## 2026-06-12 导出字典翻译修复
+
+### 问题
+导出Excel时性别、学历、求职人员类别、求职状态等字典字段显示为原始编码(如"1"而非"男"),与页面显示不一致;导出格式为旧版.xls,日期显示异常。
+
+### 修复文件
+| 文件 | 修改 |
+|------|------|
+| `IFocusPersonnelService.java` | 新增 `translateDictFieldsForPageVo()` 方法 |
+| `FocusPersonnelServiceImpl.java` | 注入 `IDictionaryItemService`,批量查询字典 → switch-case替换字段值为标签 |
+| `FocusPersonnelController.java` | 重写 `exportXls`:查询 `FocusPersonnelPageVo` 数据 → 过滤选中 → 字典翻译 → 导出为 `.xlsx`(ExcelType.XSSF) |
+
+### 导出字段
+`fullName,gender,age,education,householdLocation,currentResidence,jobSeekerCategory,contactPhone,jobSearchStatus,majorTag,minorTag,customTag`
+
+---
+
+## 2026-06-12 岗位推送功能实现
+
+### 问题
+列表操作栏"岗位推送"按钮为占位状态,点击提示"岗位推送功能待开发",需实现手动选择岗位推送给重点关注人员。
+
+### 实现方案
+复用现有 `job_recommend` 表的 `POST /jobRecommend/add` 接口创建推荐记录,前端新增通用岗位推送弹窗供选择岗位。
+
+### 后端新增(JobRecommend 模块)
+| 文件 | 修改 |
+|------|------|
+| `jobrecommend/dto/AvailablePostVo.java` | **新增** — 可选岗位VO(postId/postName/companyName/postType/workLocation) |
+| `jobrecommend/mapper/xml/JobRecommendMapper.xml` | **新增** — `queryAvailablePosts` SQL:UNION 查询三表(internship_post / welfare_post / post_info),仅查已发布岗位 |
+| `jobrecommend/mapper/JobRecommendMapper.java` | **新增** — `queryAvailablePosts()` 方法 |
+| `jobrecommend/service/IJobRecommendService.java` | **新增** — `queryAvailablePosts()` 接口 |
+| `jobrecommend/service/impl/JobRecommendServiceImpl.java` | **新增** — `queryAvailablePosts()` 实现 |
+| `jobrecommend/controller/JobRecommendController.java` | **新增** — `GET /jobRecommend/availablePosts` 端点(支持 postType + keyword 过滤) |
+
+### 前端修改
+| 文件 | 修改 |
+|------|------|
+| `jobrecommend/components/JobPushModal.vue` | **新增** — 岗位推送弹窗(岗位类型筛选、名称搜索、单选表格、推荐意见) |
+| `jobrecommend/JobRecommend.api.ts` | **新增** — `listAvailablePosts()` + `pushJob()` API |
+| `focuspersonnel/FocusPersonnelList.vue` | **修改** — 替换 `handleJobPush` 占位函数,接入 JobPushModal 弹窗,点击打开弹窗选择岗位后调用 `/jobRecommend/add` 创建推荐记录 |

jeecgboot-vue3/修改记录-公益性岗位管理模块完善.md → .docs/260605-前端-公益性岗位管理模块完善.md


+ 0 - 6
jeecgboot-vue3/修改记录-见习人员模块改造.md

@@ -24,12 +24,6 @@
 10. **defineExpose 增加 `detail` 方法暴露**
 11. **宽度从800调整为900**,maxHeight从500px调整为700px
 
-### 关键代码变更
-- 模板中增加 `v-if="isDetailMode"` 条件渲染
-- 新增 `InternshipPersonnelDetail` 组件导入
-- 新增 `isDetailMode`、`currentRecord` 响应式变量
-- 新增 `detail(record)` 方法
-
 ---
 
 ## 文件2:InternshipPersonnelList.vue

+ 49 - 0
.docs/260605-见习岗位管理_公益性岗位管理_岗位推荐模块完善开发记录.md

@@ -185,3 +185,52 @@
 2. 视图 `v_job_recommend_list` 需包含这三个新字段(需手动更新视图定义)
 3. 字典表需配置 `recommend_type` 和 `recommend_status` 字典项(由flyway脚本自动执行)
 4. 所有实体类改为继承 `JeecgEntity` 后,数据库表需有对应的审计字段(由flyway脚本自动执行)
+
+---
+
+## 六、岗位推荐模块 Bug 修复与功能增强(2026-06-13)
+
+### 6.1 启动报错修复
+
+#### JobRecommendMapper.xml - XML 注释非法字符
+- 问题:`<!--update-begin---author:kk...` 中 `---` 含 `--`,违反 XML 1.0 规范,导致 MyBatis 解析失败
+- 修复:将 `---` 分隔符改为空格描述格式
+
+#### JobRecommendMapper.xml - MyBatis 参数绑定错误
+- 问题:`queryAvailablePosts` 中 `keyword` 未找到,Mapper 接口用 `@Param("params")` 包裹
+- 修复:全部 `keyword` → `params.keyword`
+
+### 6.2 字典数据修复
+
+#### useDict.ts - 模块级缓存空数组锁死
+- 问题:首次加载时 dict 数据未入库,缓存写入空数组;后续 reload 判定 `!== undefined` 跳过重新加载
+- 修复:早期返回增加 `length > 0` 条件;API 返回空时不写缓存
+
+#### DICTIONARY_ITEM.Code 字段错误
+- 问题:`recommend_type`/`recommend_status` 的 Code 存了中文名称,useDict 优先用 Code 作选项值 → 下拉 value=中文 → 搜索无效
+- 修复:`UPDATE DICTIONARY_ITEM SET Code = ''`
+
+### 6.3 v_job_recommend_list 视图缺列
+- 问题:视图未包含 `recommender`、`recommend_org`、`recommend_time`
+- 修复:`.docs/sql/岗位推荐视图.sql` 和清理脚本中视图添加三列
+
+### 6.4 后端 postType 过滤未生效
+- 问题:`queryAvailablePosts` XML 三表始终 UNION ALL
+- 修复:用 `<choose>/<when>/<otherwise>` 重写,指定 postType 时只查对应表
+
+### 6.5 JobPushModal UI 重构与功能增强
+- 推送对象信息卡片(姓名+学历,学历用 useDict 翻译)
+- 搜索栏:岗位类型选择/标签 + 岗位名称搜索 + 查询/重置按钮
+- 列表空状态提示、已选岗位 alert 提示、确认按钮禁用保护
+- 新增 `postType`/`education` props 支持按人员类型过滤
+
+### 6.6 调用方适配
+- `InternshipPersonnelList.vue`:传入 `postType="internship_post"`,见习人员仅显示见习岗位
+- `FocusPersonnelList.vue`:传入 `education`,不限 postType
+- 两模块 `handleJobPush` 均从 record 提取 `education`、`postType`
+
+### 6.7 JobRecommendModal.add() 修复
+- 增加 `disableSubmit.value = false`,修复先查看后新增时表单仍禁用
+
+### 6.8 RecruitmentFairModal.add() 同款修复
+- 同上,招聘会管理模块 `add()` 增加 `disableSubmit.value = false`

+ 12 - 0
.docs/260609-职业指导服务模块开发记录.md

@@ -177,3 +177,15 @@ LEFT JOIN ZJRS_JEECG_BOOT.PERSONAL_INFO pi ON cgs.PERSONAL_ID = pi.ID;
 
 ### 后端
 - 无修改
+
+---
+
+## 2026-06-12 导出日期格式修复
+
+### 问题
+导出Excel时日期格式异常,因 `ExportParams` 缺少 `ExcelType.XSSF`,默认使用旧版 `.xls` 格式。
+
+### 修复文件
+| 文件 | 修改 |
+|------|------|
+| `CareerGuidanceServiceController.java` | `ExportParams` 补 `ExcelType.XSSF` + import |

+ 165 - 0
.docs/260612-招聘会管理模块开发记录.md

@@ -0,0 +1,165 @@
+# 招聘会管理模块开发记录
+
+## 开发日期
+2026-06-12
+
+## 模块概述
+本次开发实现了"招聘会管理"模块的完整后端+前端代码,参考 InternshipPost(见习岗位管理)模块的代码模式,采用单表CRUD架构。
+
+## 数据来源
+1. 省系统:省一体化平台(粤就业小程序)数据回流
+2. 市系统:本地招聘会数据录入
+3. 其他:第三方数据导入或同步
+
+## 业务逻辑
+- 招聘会信息以省一体化平台回流为准,点击查看页面链接跳转到省一体化平台招聘会页面
+- 前端操作栏仅保留"查看"按钮(符合需求),后台管理端可进行新增操作
+
+---
+
+## 一、后端代码
+
+| 文件 | 路径 | 说明 |
+|------|------|------|
+| RecruitmentFair.java | org.jeecg.modules.zjrs.recruitmentfair.entity | 实体类,@TableName("recruitment_fair"),继承JeecgEntity,包含11个业务字段 |
+| RecruitmentFairMapper.java | org.jeecg.modules.zjrs.recruitmentfair.mapper | Mapper接口,继承BaseMapper(此表不需要自定义JOIN查询) |
+| RecruitmentFairMapper.xml | org.jeecg.modules.zjrs.recruitmentfair.mapper.xml | Mapper XML,基础配置(空mapper) |
+| IRecruitmentFairService.java | org.jeecg.modules.zjrs.recruitmentfair.service | Service接口,继承IService |
+| RecruitmentFairServiceImpl.java | org.jeecg.modules.zjrs.recruitmentfair.service.impl | Service实现类 |
+| RecruitmentFairController.java | org.jeecg.modules.zjrs.recruitmentfair.controller | Controller,继承JeecgController,使用QueryGenerator自动构建查询条件 |
+
+**关键设计:**
+- /list 接口使用 QueryGenerator 自动构建查询条件(不需要自定义queryPageList)
+- 权限前缀:recruitment_fair
+- 实体类继承 JeecgEntity(id, createBy, createTime, updateBy, updateTime)
+- 日期字段使用VARCHAR(需求文档标注为"短文本",因省平台回流数据日期格式不统一)
+
+### 1.1 实体字段设计
+
+| 字段 | Java属性 | DB列 | 类型 | 必填 | 字典 | 说明 |
+|------|---------|------|------|------|------|------|
+| 招聘会名称 | fairName | FAIR_NAME | String | 否 | — | — |
+| 招聘会类型 | fairType | FAIR_TYPE | String | 否 | — | — |
+| 招聘会主体 | fairSubject | FAIR_SUBJECT | String | 否 | — | — |
+| 举办方式 | holdMethod | HOLD_METHOD | String | 否 | fair_hold_method | 现场/网络 |
+| 线上举办日期 | onlineHoldDate | ONLINE_HOLD_DATE | String | 否 | — | 短文本格式 |
+| 线下举办日期 | offlineHoldDate | OFFLINE_HOLD_DATE | String | 否 | — | 短文本格式 |
+| 主办单位 | hostUnit | HOST_UNIT | String | 否 | — | — |
+| 举办地点 | holdLocation | HOLD_LOCATION | String | **是** | — | — |
+| 数据来源 | dataSource | DATA_SOURCE | String | 否 | fair_data_source | 省系统/市系统/其他 |
+| 状态 | status | STATUS | String | 否 | fair_status | 未开始/进行中/已结束 |
+| 省平台链接 | provinceLink | PROVINCE_LINK | String | 否 | — | 省平台详情页URL |
+
+### 1.2 Controller API
+
+| 方法 | URL | 权限 | 说明 |
+|------|-----|------|------|
+| GET | /recruitmentFair/list | — | 分页列表查询 |
+| POST | /recruitmentFair/add | recruitment_fair:add | 新增 |
+| PUT/POST | /recruitmentFair/edit | recruitment_fair:edit | 编辑 |
+| DELETE | /recruitmentFair/delete | recruitment_fair:delete | 删除 |
+| DELETE | /recruitmentFair/deleteBatch | recruitment_fair:deleteBatch | 批量删除 |
+| GET | /recruitmentFair/queryById | — | 通过id查询 |
+| GET | /recruitmentFair/exportXls | recruitment_fair:exportXls | 导出Excel |
+| POST | /recruitmentFair/importExcel | recruitment_fair:importExcel | 导入Excel |
+
+---
+
+## 二、前端代码
+
+| 文件 | 路径 | 说明 |
+|------|------|------|
+| RecruitmentFair.api.ts | views/recruitmentfair/ | API定义,基础路径 /recruitmentFair |
+| RecruitmentFair.data.ts | views/recruitmentfair/ | 列定义(7列)和高级查询配置(3个查询条件) |
+| RecruitmentFairList.vue | views/recruitmentfair/ | 列表页面 |
+| RecruitmentFairForm.vue | views/recruitmentfair/components/ | 表单组件,单卡片布局 |
+| RecruitmentFairModal.vue | views/recruitmentfair/components/ | 弹窗组件 |
+
+**关键设计:**
+- 查询区域:招聘会名称(a-input)、举办方式(a-select,字典fair_hold_method)、状态(a-select,字典fair_status)
+- 表单单卡片布局:包含11个业务字段,2列排列
+- 仅"举办地点"为必填验证字段
+- 列表字典翻译:holdMethod、status、dataSource
+- 操作栏仅"查看"按钮(符合需求文档功能范围:查看、新增)
+- 表格标题区:新增按钮(v-auth="recruitment_fair:add")、导出按钮(v-auth="recruitment_fair:exportXls")
+
+---
+
+## 三、SQL文件
+
+| 文件 | 路径 | 说明 |
+|------|------|------|
+| 招聘会管理-建表.sql | .docs/sql/ | DDL建表语句,达梦数据库 |
+| 招聘会管理-字典数据.sql | .docs/sql/ | 字典数据初始化(fair_hold_method、fair_status、fair_data_source) |
+| 招聘会管理-菜单权限.sql | .docs/sql/ | 菜单及按钮权限初始化,达梦数据库 |
+| 招聘会管理-测试数据.sql | .docs/sql/ | 6条测试数据,覆盖不同来源/方式/状态 |
+| V20260612_1__menu_insert_RecruitmentFair.sql | flyway/sql/mysql/ | Flyway迁移脚本,MySQL语法 |
+
+### 3.1 Flyway菜单结构
+- 一级菜单"招聘会管理":id=178060100000600, sort_no=2.30
+- 二级菜单"招聘会管理":id=178060100000610, component=recruitmentfair/RecruitmentFairList
+- 按钮权限:add(611), edit(612), delete(613), deleteBatch(614), exportXls(615), importExcel(616)
+- 角色授权(admin):ID从178060100000620开始
+
+### 3.2 字典数据
+| 字典编码 | 名称 | 字典项 |
+|---------|------|--------|
+| fair_hold_method | 举办方式 | 现场、网络 |
+| fair_status | 招聘会状态 | 未开始、进行中、已结束 |
+| fair_data_source | 数据来源 | 省系统、市系统、其他 |
+
+### 3.3 测试数据
+6条测试数据覆盖以下场景:
+- 3种数据来源:省系统(3条)、市系统(2条)、其他(1条)
+- 2种举办方式:现场(4条)、网络(2条)
+- 3种状态:进行中(2条)、未开始(2条)、已结束(2条)
+- 省系统数据附带省平台链接(provinceLink),市系统和其他数据无链接
+
+---
+
+## 四、部署步骤
+
+1. **数据库初始化(达梦DM8):**
+   ```sql
+   -- 依次执行
+   @招聘会管理-建表.sql
+   @招聘会管理-字典数据.sql
+   @招聘会管理-菜单权限.sql
+   @招聘会管理-测试数据.sql
+   ```
+
+2. **后端启动:**
+   - 重新编译并启动 jeecg-system-start 模块
+   - 验证 Swagger UI 中 /recruitmentFair 路径的API可正常调用
+
+3. **前端启动:**
+   - Vite自动发现 views/recruitmentfair/ 目录下的组件
+   - 菜单通过后台接口加载,无需前端额外配置
+
+4. **验证:**
+   - 登录管理后台,确认"招聘会管理"一级菜单显示
+   - 测试列表查询、新增、查看功能
+   - 测试Excel导出功能
+
+---
+
+## 五、避坑记录
+
+1. **日期字段类型选择:** 需求文档明确标注日期字段类型为"短文本",而非日期选择器。原因是省一体化平台回流的数据日期格式不统一(可能为"2026年6月"、"2026-06-01至2026-06-30"等格式),使用VARCHAR类型更灵活。
+
+2. **操作栏按钮限制:** 需求文档仅列出"查看"和"新增"功能,因此前端操作栏仅展示"查看"按钮,"新增"按钮放在表格标题区。后端仍提供完整CRUD能力以备后用。
+
+3. **省平台链接展示:** 仅省系统来源的数据显示省平台链接,市系统和其他来源不显示。详情页面可根据dataSource字段动态控制链接的展示。
+
+4. **菜单sort_no选择:** 招聘会管理在需求文档中列在第3.5节(就业指导之后、深度分析之前),因此sort_no设为2.30,位于两者之间。
+
+---
+
+## 五、Bug 修复(2026-06-13)
+
+### 5.1 RecruitmentFairModal.add() 表单禁用残留
+- 问题:先点击"查看"(`disableSubmit=true`)再点击"新增"(`add()` 未重置 `disableSubmit`),表单仍处于禁用状态无法输入
+- 修复:`add()` 方法开头增加 `disableSubmit.value = false;`
+
+### 5.2 RecruitmentFairForm.vue 表单验证
+- 验证规则 `validatorRules` 仅包含 `holdLocation` 必填,其他字段无校验

+ 3 - 0
.docs/sql/岗位推荐视图.sql

@@ -11,6 +11,9 @@ SELECT
     job_recommend.recommend_type,
     job_recommend.recommend_opinion,
     job_recommend.recommend_status,
+    job_recommend.recommender,
+    job_recommend.recommend_org,
+    job_recommend.recommend_time,
     personal_info.full_name,
     personal_info.gender,
     personal_info.contact_phone,

+ 87 - 0
.docs/sql/招聘会管理-关联表与字段补充.sql

@@ -0,0 +1,87 @@
+-- ============================================================
+-- 招聘会管理模块 - 达梦(DM)数据库变更脚本
+-- 日期:2026-06-13
+-- 说明:新增招聘会关联表、补充字段、字典数据
+-- ============================================================
+
+-- 1. 招聘会管理表新增字段
+ALTER TABLE RECRUITMENT_FAIR ADD COLUMN FAIR_DESCRIPTION TEXT;
+COMMENT ON COLUMN RECRUITMENT_FAIR.FAIR_DESCRIPTION IS '招聘会描述';
+ALTER TABLE RECRUITMENT_FAIR ADD COLUMN FAIR_IMAGE VARCHAR(500);
+COMMENT ON COLUMN RECRUITMENT_FAIR.FAIR_IMAGE IS '封面图URL';
+ALTER TABLE RECRUITMENT_FAIR ADD COLUMN REGISTRATION_DEADLINE VARCHAR(50);
+COMMENT ON COLUMN RECRUITMENT_FAIR.REGISTRATION_DEADLINE IS '报名截止时间';
+ALTER TABLE RECRUITMENT_FAIR ADD COLUMN MAX_ENTERPRISES INT;
+COMMENT ON COLUMN RECRUITMENT_FAIR.MAX_ENTERPRISES IS '最大参展企业数';
+ALTER TABLE RECRUITMENT_FAIR ADD COLUMN MAX_PARTICIPANTS INT;
+COMMENT ON COLUMN RECRUITMENT_FAIR.MAX_PARTICIPANTS IS '最大参与人数';
+ALTER TABLE RECRUITMENT_FAIR ADD COLUMN CONTACT_PERSON VARCHAR(100);
+COMMENT ON COLUMN RECRUITMENT_FAIR.CONTACT_PERSON IS '联系人';
+ALTER TABLE RECRUITMENT_FAIR ADD COLUMN CONTACT_PHONE VARCHAR(20);
+COMMENT ON COLUMN RECRUITMENT_FAIR.CONTACT_PHONE IS '联系电话';
+ALTER TABLE RECRUITMENT_FAIR ADD COLUMN FAIR_SCHEDULE TEXT;
+COMMENT ON COLUMN RECRUITMENT_FAIR.FAIR_SCHEDULE IS '日程安排';
+
+-- 2. 招聘会-企业关联表
+CREATE TABLE IF NOT EXISTS RECRUITMENT_FAIR_ENTERPRISE (
+    ID VARCHAR(36) NOT NULL,
+    CREATE_BY VARCHAR(50),
+    CREATE_TIME TIMESTAMP,
+    UPDATE_BY VARCHAR(50),
+    UPDATE_TIME TIMESTAMP,
+    SYS_ORG_CODE VARCHAR(64),
+    FAIR_ID VARCHAR(36) NOT NULL,
+    ENTERPRISE_ID VARCHAR(36) NOT NULL,
+    ENTERPRISE_NAME VARCHAR(200),
+    STATUS VARCHAR(2) DEFAULT '0',
+    PRIMARY KEY (ID)
+);
+COMMENT ON TABLE RECRUITMENT_FAIR_ENTERPRISE IS '招聘会-企业关联表';
+COMMENT ON COLUMN RECRUITMENT_FAIR_ENTERPRISE.FAIR_ID IS '招聘会ID';
+COMMENT ON COLUMN RECRUITMENT_FAIR_ENTERPRISE.ENTERPRISE_ID IS '企业ID';
+COMMENT ON COLUMN RECRUITMENT_FAIR_ENTERPRISE.ENTERPRISE_NAME IS '企业名称';
+COMMENT ON COLUMN RECRUITMENT_FAIR_ENTERPRISE.STATUS IS '参展状态:0-待审核,1-已通过,2-已拒绝';
+CREATE INDEX IDX_RFE_FAIR_ID ON RECRUITMENT_FAIR_ENTERPRISE(FAIR_ID);
+CREATE INDEX IDX_RFE_ENT_ID ON RECRUITMENT_FAIR_ENTERPRISE(ENTERPRISE_ID);
+
+-- 3. 招聘会-个人报名表
+CREATE TABLE IF NOT EXISTS RECRUITMENT_FAIR_PERSONAL (
+    ID VARCHAR(36) NOT NULL,
+    CREATE_BY VARCHAR(50),
+    CREATE_TIME TIMESTAMP,
+    UPDATE_BY VARCHAR(50),
+    UPDATE_TIME TIMESTAMP,
+    SYS_ORG_CODE VARCHAR(64),
+    FAIR_ID VARCHAR(36) NOT NULL,
+    PERSONAL_ID VARCHAR(36) NOT NULL,
+    PERSONAL_NAME VARCHAR(100),
+    CONTACT_PHONE VARCHAR(20),
+    STATUS VARCHAR(2) DEFAULT '0',
+    PRIMARY KEY (ID)
+);
+COMMENT ON TABLE RECRUITMENT_FAIR_PERSONAL IS '招聘会-个人报名表';
+COMMENT ON COLUMN RECRUITMENT_FAIR_PERSONAL.FAIR_ID IS '招聘会ID';
+COMMENT ON COLUMN RECRUITMENT_FAIR_PERSONAL.PERSONAL_ID IS '个人ID';
+COMMENT ON COLUMN RECRUITMENT_FAIR_PERSONAL.PERSONAL_NAME IS '个人姓名';
+COMMENT ON COLUMN RECRUITMENT_FAIR_PERSONAL.CONTACT_PHONE IS '联系电话';
+COMMENT ON COLUMN RECRUITMENT_FAIR_PERSONAL.STATUS IS '报名状态:0-已报名,1-已签到,2-已取消';
+CREATE INDEX IDX_RFP_FAIR_ID ON RECRUITMENT_FAIR_PERSONAL(FAIR_ID);
+CREATE INDEX IDX_RFP_PER_ID ON RECRUITMENT_FAIR_PERSONAL(PERSONAL_ID);
+
+-- 4. 字典数据 - 招聘会状态
+INSERT INTO SYS_DICT (ID, DICT_NAME, DICT_CODE, DESCRIPTION, DEL_FLAG, CREATE_BY, CREATE_TIME, TYPE, TENANT_ID) VALUES ('fair_status_dict', '招聘会状态', 'fair_status', '招聘会状态', 0, 'admin', CURRENT_TIMESTAMP, 0, NULL);
+INSERT INTO SYS_DICT_ITEM (ID, DICT_ID, ITEM_TEXT, ITEM_VALUE, DESCRIPTION, SORT_ORDER, STATUS, CREATE_BY, CREATE_TIME) VALUES ('fair_status_0', 'fair_status_dict', '未开始', '0', '', 1, 1, 'admin', CURRENT_TIMESTAMP);
+INSERT INTO SYS_DICT_ITEM (ID, DICT_ID, ITEM_TEXT, ITEM_VALUE, DESCRIPTION, SORT_ORDER, STATUS, CREATE_BY, CREATE_TIME) VALUES ('fair_status_1', 'fair_status_dict', '报名中', '1', '', 2, 1, 'admin', CURRENT_TIMESTAMP);
+INSERT INTO SYS_DICT_ITEM (ID, DICT_ID, ITEM_TEXT, ITEM_VALUE, DESCRIPTION, SORT_ORDER, STATUS, CREATE_BY, CREATE_TIME) VALUES ('fair_status_2', 'fair_status_dict', '进行中', '2', '', 3, 1, 'admin', CURRENT_TIMESTAMP);
+INSERT INTO SYS_DICT_ITEM (ID, DICT_ID, ITEM_TEXT, ITEM_VALUE, DESCRIPTION, SORT_ORDER, STATUS, CREATE_BY, CREATE_TIME) VALUES ('fair_status_3', 'fair_status_dict', '已结束', '3', '', 4, 1, 'admin', CURRENT_TIMESTAMP);
+
+-- 5. 字典数据 - 举办方式
+INSERT INTO SYS_DICT (ID, DICT_NAME, DICT_CODE, DESCRIPTION, DEL_FLAG, CREATE_BY, CREATE_TIME, TYPE, TENANT_ID) VALUES ('fair_hold_method_dict', '举办方式', 'fair_hold_method', '招聘会举办方式', 0, 'admin', CURRENT_TIMESTAMP, 0, NULL);
+INSERT INTO SYS_DICT_ITEM (ID, DICT_ID, ITEM_TEXT, ITEM_VALUE, DESCRIPTION, SORT_ORDER, STATUS, CREATE_BY, CREATE_TIME) VALUES ('fair_hold_0', 'fair_hold_method_dict', '线上', '0', '', 1, 1, 'admin', CURRENT_TIMESTAMP);
+INSERT INTO SYS_DICT_ITEM (ID, DICT_ID, ITEM_TEXT, ITEM_VALUE, DESCRIPTION, SORT_ORDER, STATUS, CREATE_BY, CREATE_TIME) VALUES ('fair_hold_1', 'fair_hold_method_dict', '线下', '1', '', 2, 1, 'admin', CURRENT_TIMESTAMP);
+INSERT INTO SYS_DICT_ITEM (ID, DICT_ID, ITEM_TEXT, ITEM_VALUE, DESCRIPTION, SORT_ORDER, STATUS, CREATE_BY, CREATE_TIME) VALUES ('fair_hold_2', 'fair_hold_method_dict', '线上+线下', '2', '', 3, 1, 'admin', CURRENT_TIMESTAMP);
+
+-- 6. 字典数据 - 数据来源
+INSERT INTO SYS_DICT (ID, DICT_NAME, DICT_CODE, DESCRIPTION, DEL_FLAG, CREATE_BY, CREATE_TIME, TYPE, TENANT_ID) VALUES ('fair_data_source_dict', '招聘会数据来源', 'fair_data_source', '招聘会数据来源', 0, 'admin', CURRENT_TIMESTAMP, 0, NULL);
+INSERT INTO SYS_DICT_ITEM (ID, DICT_ID, ITEM_TEXT, ITEM_VALUE, DESCRIPTION, SORT_ORDER, STATUS, CREATE_BY, CREATE_TIME) VALUES ('fair_source_0', 'fair_data_source_dict', '省系统', '0', '', 1, 1, 'admin', CURRENT_TIMESTAMP);
+INSERT INTO SYS_DICT_ITEM (ID, DICT_ID, ITEM_TEXT, ITEM_VALUE, DESCRIPTION, SORT_ORDER, STATUS, CREATE_BY, CREATE_TIME) VALUES ('fair_source_1', 'fair_data_source_dict', '市系统', '1', '', 2, 1, 'admin', CURRENT_TIMESTAMP);

+ 31 - 0
.docs/sql/招聘会管理-字典数据.sql

@@ -0,0 +1,31 @@
+-- ============================================================
+-- 湛江市智慧人社运营运维(2025-2027年)项目
+-- 就业一湛通服务平台 - 招聘会管理模块
+-- 数据字典初始化脚本
+--
+-- 数据库:达梦数据库 (DM8)
+-- 创建日期:2026-06-12
+-- ============================================================
+
+-- ============================================================
+-- 1. 举办方式 (fair_hold_method)
+-- ============================================================
+INSERT INTO "DICTIONARY" VALUES ('fair_hold_method', '举办方式', 1, 1, 0);
+INSERT INTO "DICTIONARY_ITEM" VALUES ('178060100000501', '', 'fair_hold_method', 1, '现场', 1, 1, 1, NULL);
+INSERT INTO "DICTIONARY_ITEM" VALUES ('178060100000502', '', 'fair_hold_method', 2, '网络', 2, 1, 1, NULL);
+
+-- ============================================================
+-- 2. 招聘会状态 (fair_status)
+-- ============================================================
+INSERT INTO "DICTIONARY" VALUES ('fair_status', '招聘会状态', 1, 1, 0);
+INSERT INTO "DICTIONARY_ITEM" VALUES ('178060100000511', '', 'fair_status', 1, '未开始', 1, 1, 1, NULL);
+INSERT INTO "DICTIONARY_ITEM" VALUES ('178060100000512', '', 'fair_status', 2, '进行中', 2, 1, 1, NULL);
+INSERT INTO "DICTIONARY_ITEM" VALUES ('178060100000513', '', 'fair_status', 3, '已结束', 3, 1, 1, NULL);
+
+-- ============================================================
+-- 3. 数据来源 (fair_data_source)
+-- ============================================================
+INSERT INTO "DICTIONARY" VALUES ('fair_data_source', '数据来源', 1, 1, 0);
+INSERT INTO "DICTIONARY_ITEM" VALUES ('178060100000521', '', 'fair_data_source', 1, '省系统', 1, 1, 1, NULL);
+INSERT INTO "DICTIONARY_ITEM" VALUES ('178060100000522', '', 'fair_data_source', 2, '市系统', 2, 1, 1, NULL);
+INSERT INTO "DICTIONARY_ITEM" VALUES ('178060100000523', '', 'fair_data_source', 3, '其他', 3, 1, 1, NULL);

+ 52 - 0
.docs/sql/招聘会管理-建表.sql

@@ -0,0 +1,52 @@
+-- ============================================================
+-- 湛江市智慧人社运营运维(2025-2027年)项目
+-- 就业一湛通服务平台 - 招聘会管理模块
+-- 达梦数据库建表脚本
+--
+-- 依据:《需求规格说明书》第3.5节 - 招聘会管理
+-- 数据库:达梦数据库 (DM8)
+-- 创建日期:2026-06-12
+-- ============================================================
+
+-- ============================================================
+-- 3.5 招聘会信息表
+-- ============================================================
+DROP TABLE IF EXISTS RECRUITMENT_FAIR;
+CREATE TABLE RECRUITMENT_FAIR
+(
+    ID                VARCHAR(36)  NOT NULL,
+    FAIR_NAME         VARCHAR(200)          COMMENT '招聘会名称',
+    FAIR_TYPE         VARCHAR(100)          COMMENT '招聘会类型',
+    FAIR_SUBJECT      VARCHAR(200)          COMMENT '招聘会主体',
+    HOLD_METHOD       VARCHAR(50)           COMMENT '举办方式(现场/网络)',
+    ONLINE_HOLD_DATE  VARCHAR(200)          COMMENT '线上举办日期',
+    OFFLINE_HOLD_DATE VARCHAR(200)          COMMENT '线下举办日期',
+    HOST_UNIT         VARCHAR(300)          COMMENT '主办单位',
+    HOLD_LOCATION     VARCHAR(500) NOT NULL COMMENT '举办地点',
+    DATA_SOURCE       VARCHAR(50)           COMMENT '数据来源(省系统/市系统/其他)',
+    STATUS            VARCHAR(50)           COMMENT '状态(not_started/in_progress/ended)',
+    PROVINCE_LINK     VARCHAR(500)          COMMENT '省平台链接',
+    CREATE_BY         VARCHAR(50)           COMMENT '创建人',
+    CREATE_TIME       DATETIME              COMMENT '创建时间',
+    UPDATE_BY         VARCHAR(50)           COMMENT '修改人',
+    UPDATE_TIME       DATETIME              COMMENT '修改时间',
+    PRIMARY KEY (ID)
+);
+
+COMMENT ON TABLE RECRUITMENT_FAIR IS '招聘会信息表';
+COMMENT ON COLUMN RECRUITMENT_FAIR.ID IS '主键ID';
+COMMENT ON COLUMN RECRUITMENT_FAIR.FAIR_NAME IS '招聘会名称';
+COMMENT ON COLUMN RECRUITMENT_FAIR.FAIR_TYPE IS '招聘会类型';
+COMMENT ON COLUMN RECRUITMENT_FAIR.FAIR_SUBJECT IS '招聘会主体';
+COMMENT ON COLUMN RECRUITMENT_FAIR.HOLD_METHOD IS '举办方式(现场/网络)';
+COMMENT ON COLUMN RECRUITMENT_FAIR.ONLINE_HOLD_DATE IS '线上举办日期';
+COMMENT ON COLUMN RECRUITMENT_FAIR.OFFLINE_HOLD_DATE IS '线下举办日期';
+COMMENT ON COLUMN RECRUITMENT_FAIR.HOST_UNIT IS '主办单位';
+COMMENT ON COLUMN RECRUITMENT_FAIR.HOLD_LOCATION IS '举办地点';
+COMMENT ON COLUMN RECRUITMENT_FAIR.DATA_SOURCE IS '数据来源(省系统/市系统/其他)';
+COMMENT ON COLUMN RECRUITMENT_FAIR.STATUS IS '状态(未开始/进行中/已结束)';
+COMMENT ON COLUMN RECRUITMENT_FAIR.PROVINCE_LINK IS '省平台链接';
+COMMENT ON COLUMN RECRUITMENT_FAIR.CREATE_BY IS '创建人';
+COMMENT ON COLUMN RECRUITMENT_FAIR.CREATE_TIME IS '创建时间';
+COMMENT ON COLUMN RECRUITMENT_FAIR.UPDATE_BY IS '修改人';
+COMMENT ON COLUMN RECRUITMENT_FAIR.UPDATE_TIME IS '修改时间';

+ 37 - 0
.docs/sql/招聘会管理-测试数据.sql

@@ -0,0 +1,37 @@
+-- ============================================================
+-- 就业一湛通服务平台 - 招聘会管理模块 测试数据
+-- 数据库:达梦数据库 (DM8) / MySQL 8.0 兼容
+-- 创建日期:2026-06-12
+-- 说明:覆盖不同数据来源、举办方式、状态的测试场景
+-- ============================================================
+
+-- 先清理旧数据
+DELETE FROM recruitment_fair;
+
+-- ============================================================
+-- 招聘会信息(RECRUITMENT_FAIR)
+-- ============================================================
+
+-- 1. 省平台回流 - 现场招聘会 - 进行中
+INSERT INTO recruitment_fair (ID, FAIR_NAME, FAIR_TYPE, FAIR_SUBJECT, HOLD_METHOD, ONLINE_HOLD_DATE, OFFLINE_HOLD_DATE, HOST_UNIT, HOLD_LOCATION, DATA_SOURCE, STATUS, PROVINCE_LINK, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME)
+VALUES ('178061200000401', '湛江市2026年"南粤春暖"现场招聘会', '综合招聘会', '高校毕业生', '现场', NULL, '2026-06-15 09:00-12:00', '湛江市人力资源和社会保障局', '湛江市赤坎区体育中心', '省系统', '进行中', 'https://yuejiuye.gd.gov.cn/fair/detail/FAIR-2026-00001', 'admin', '2026-06-12 10:00:00', NULL, NULL);
+
+-- 2. 省平台回流 - 网络招聘会 - 进行中
+INSERT INTO recruitment_fair (ID, FAIR_NAME, FAIR_TYPE, FAIR_SUBJECT, HOLD_METHOD, ONLINE_HOLD_DATE, OFFLINE_HOLD_DATE, HOST_UNIT, HOLD_LOCATION, DATA_SOURCE, STATUS, PROVINCE_LINK, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME)
+VALUES ('178061200000402', '湛江市2026年高校毕业生网络招聘会', '专场招聘会', '高校毕业生', '网络', '2026-06-01 至 2026-06-30', NULL, '湛江市就业服务中心', '粤就业小程序线上平台', '省系统', '进行中', 'https://yuejiuye.gd.gov.cn/fair/detail/FAIR-2026-00002', 'admin', '2026-06-12 10:00:00', NULL, NULL);
+
+-- 3. 省平台回流 - 现场招聘会 - 未开始
+INSERT INTO recruitment_fair (ID, FAIR_NAME, FAIR_TYPE, FAIR_SUBJECT, HOLD_METHOD, ONLINE_HOLD_DATE, OFFLINE_HOLD_DATE, HOST_UNIT, HOLD_LOCATION, DATA_SOURCE, STATUS, PROVINCE_LINK, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME)
+VALUES ('178061200000403', '湛江市2026年退役军人专场招聘会', '专场招聘会', '退役军人', '现场', NULL, '2026-07-10 09:00-16:00', '湛江市退役军人事务局', '湛江市霞山区人民广场', '省系统', '未开始', 'https://yuejiuye.gd.gov.cn/fair/detail/FAIR-2026-00003', 'admin', '2026-06-12 10:00:00', NULL, NULL);
+
+-- 4. 市系统录入 - 现场招聘会 - 已结束
+INSERT INTO recruitment_fair (ID, FAIR_NAME, FAIR_TYPE, FAIR_SUBJECT, HOLD_METHOD, ONLINE_HOLD_DATE, OFFLINE_HOLD_DATE, HOST_UNIT, HOLD_LOCATION, DATA_SOURCE, STATUS, PROVINCE_LINK, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME)
+VALUES ('178061200000404', '2026年麻章区就业援助月招聘会', '综合招聘会', '就业困难人员', '现场', NULL, '2026-05-20 09:00-12:00', '麻章区人力资源和社会保障局', '湛江市麻章区文化广场', '市系统', '已结束', NULL, 'admin', '2026-06-12 10:00:00', NULL, NULL);
+
+-- 5. 市系统录入 - 网络招聘会 - 未开始
+INSERT INTO recruitment_fair (ID, FAIR_NAME, FAIR_TYPE, FAIR_SUBJECT, HOLD_METHOD, ONLINE_HOLD_DATE, OFFLINE_HOLD_DATE, HOST_UNIT, HOLD_LOCATION, DATA_SOURCE, STATUS, PROVINCE_LINK, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME)
+VALUES ('178061200000405', '坡头区2026年民营企业招聘月网络招聘会', '专场招聘会', '社会人员', '网络', '2026-08-01 至 2026-08-31', NULL, '坡头区人力资源和社会保障局', '湛江就业在线平台', '市系统', '未开始', NULL, 'admin', '2026-06-12 10:00:00', NULL, NULL);
+
+-- 6. 其他来源 - 现场招聘会 - 已结束
+INSERT INTO recruitment_fair (ID, FAIR_NAME, FAIR_TYPE, FAIR_SUBJECT, HOLD_METHOD, ONLINE_HOLD_DATE, OFFLINE_HOLD_DATE, HOST_UNIT, HOLD_LOCATION, DATA_SOURCE, STATUS, PROVINCE_LINK, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME)
+VALUES ('178061200000406', '湛江经开区2026年校企对接招聘会', '综合招聘会', '高校毕业生', '现场', NULL, '2026-04-15 09:00-16:00', '湛江经济技术开发区管委会', '湛江市开发区会展中心', '其他', '已结束', NULL, 'admin', '2026-06-12 10:00:00', NULL, NULL);

+ 83 - 0
.docs/sql/招聘会管理-菜单权限.sql

@@ -0,0 +1,83 @@
+-- ============================================================
+-- 湛江市智慧人社运营运维(2025-2027年)项目
+-- 就业一湛通服务平台 - 招聘会管理模块
+-- 菜单及权限初始化脚本
+--
+-- 数据库:达梦数据库 (DM8)
+-- 创建日期:2026-06-12
+-- ============================================================
+
+-- ============================================================
+-- 1. 清理旧数据(兼容重复执行)
+-- ============================================================
+DELETE FROM sys_role_permission WHERE permission_id IN (
+    '178060100000600', '178060100000610', '178060100000611',
+    '178060100000612', '178060100000613', '178060100000614',
+    '178060100000615', '178060100000616'
+);
+DELETE FROM sys_permission WHERE id IN (
+    '178060100000610', '178060100000611', '178060100000612',
+    '178060100000613', '178060100000614', '178060100000615',
+    '178060100000616'
+);
+DELETE FROM sys_permission WHERE id = '178060100000600';
+
+-- ============================================================
+-- 2. 一级菜单:招聘会管理
+-- ============================================================
+INSERT INTO sys_permission (id, parent_id, "NAME", url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, "DESCRIPTION", create_by, create_time, update_by, update_time, del_flag, rule_flag, "STATUS", internal_or_external)
+VALUES ('178060100000600', '', '招聘会管理', '/recruitmentFair', 'layouts/default/index', 1, '', NULL, 0, NULL, '0', 2.30, 0, 'ant-design:calendar-outlined', 0, 0, 0, 0, NULL, 'admin', '2026-06-12 10:00:00', NULL, NULL, 0, 0, 1, 0);
+
+-- ============================================================
+-- 3. 二级菜单:招聘会管理页面
+-- ============================================================
+INSERT INTO sys_permission (id, parent_id, "NAME", url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, "DESCRIPTION", create_by, create_time, update_by, update_time, del_flag, rule_flag, "STATUS", internal_or_external)
+VALUES ('178060100000610', '178060100000600', '招聘会管理', '/recruitmentFair/recruitmentFairList', 'recruitmentfair/RecruitmentFairList', 1, '', NULL, 1, NULL, '0', 1.00, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2026-06-12 10:00:00', NULL, NULL, 0, 0, 1, 0);
+
+-- ============================================================
+-- 4. 按钮权限
+-- ============================================================
+INSERT INTO sys_permission (id, parent_id, "NAME", url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, "DESCRIPTION", create_by, create_time, update_by, update_time, del_flag, rule_flag, "STATUS", internal_or_external)
+VALUES ('178060100000611', '178060100000610', '添加招聘会信息', NULL, NULL, 0, '', NULL, 2, 'recruitment_fair:add', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2026-06-12 10:00:00', NULL, NULL, 0, 0, 1, 0);
+
+INSERT INTO sys_permission (id, parent_id, "NAME", url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, "DESCRIPTION", create_by, create_time, update_by, update_time, del_flag, rule_flag, "STATUS", internal_or_external)
+VALUES ('178060100000612', '178060100000610', '编辑招聘会信息', NULL, NULL, 0, '', NULL, 2, 'recruitment_fair:edit', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2026-06-12 10:00:00', NULL, NULL, 0, 0, 1, 0);
+
+INSERT INTO sys_permission (id, parent_id, "NAME", url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, "DESCRIPTION", create_by, create_time, update_by, update_time, del_flag, rule_flag, "STATUS", internal_or_external)
+VALUES ('178060100000613', '178060100000610', '删除招聘会信息', NULL, NULL, 0, '', NULL, 2, 'recruitment_fair:delete', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2026-06-12 10:00:00', NULL, NULL, 0, 0, 1, 0);
+
+INSERT INTO sys_permission (id, parent_id, "NAME", url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, "DESCRIPTION", create_by, create_time, update_by, update_time, del_flag, rule_flag, "STATUS", internal_or_external)
+VALUES ('178060100000614', '178060100000610', '批量删除招聘会信息', NULL, NULL, 0, '', NULL, 2, 'recruitment_fair:deleteBatch', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2026-06-12 10:00:00', NULL, NULL, 0, 0, 1, 0);
+
+INSERT INTO sys_permission (id, parent_id, "NAME", url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, "DESCRIPTION", create_by, create_time, update_by, update_time, del_flag, rule_flag, "STATUS", internal_or_external)
+VALUES ('178060100000615', '178060100000610', '导出excel_招聘会管理', NULL, NULL, 0, '', NULL, 2, 'recruitment_fair:exportXls', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2026-06-12 10:00:00', NULL, NULL, 0, 0, 1, 0);
+
+INSERT INTO sys_permission (id, parent_id, "NAME", url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, "DESCRIPTION", create_by, create_time, update_by, update_time, del_flag, rule_flag, "STATUS", internal_or_external)
+VALUES ('178060100000616', '178060100000610', '导入excel_招聘会管理', NULL, NULL, 0, '', NULL, 2, 'recruitment_fair:importExcel', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2026-06-12 10:00:00', NULL, NULL, 0, 0, 1, 0);
+
+-- ============================================================
+-- 5. 角色授权(admin角色)
+-- ============================================================
+INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip)
+VALUES ('178060100000620', 'f6817f48af4fb3af11b9e8bf182f618b', '178060100000600', NULL, '2026-06-12 10:00:00', '127.0.0.1');
+
+INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip)
+VALUES ('178060100000621', 'f6817f48af4fb3af11b9e8bf182f618b', '178060100000610', NULL, '2026-06-12 10:00:00', '127.0.0.1');
+
+INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip)
+VALUES ('178060100000622', 'f6817f48af4fb3af11b9e8bf182f618b', '178060100000611', NULL, '2026-06-12 10:00:00', '127.0.0.1');
+
+INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip)
+VALUES ('178060100000623', 'f6817f48af4fb3af11b9e8bf182f618b', '178060100000612', NULL, '2026-06-12 10:00:00', '127.0.0.1');
+
+INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip)
+VALUES ('178060100000624', 'f6817f48af4fb3af11b9e8bf182f618b', '178060100000613', NULL, '2026-06-12 10:00:00', '127.0.0.1');
+
+INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip)
+VALUES ('178060100000625', 'f6817f48af4fb3af11b9e8bf182f618b', '178060100000614', NULL, '2026-06-12 10:00:00', '127.0.0.1');
+
+INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip)
+VALUES ('178060100000626', 'f6817f48af4fb3af11b9e8bf182f618b', '178060100000615', NULL, '2026-06-12 10:00:00', '127.0.0.1');
+
+INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip)
+VALUES ('178060100000627', 'f6817f48af4fb3af11b9e8bf182f618b', '178060100000616', NULL, '2026-06-12 10:00:00', '127.0.0.1');

+ 3 - 0
.docs/sql/新增模块清理与重新执行.sql

@@ -69,6 +69,9 @@ SELECT
     job_recommend.recommend_type,
     job_recommend.recommend_opinion,
     job_recommend.recommend_status,
+    job_recommend.recommender,
+    job_recommend.recommend_org,
+    job_recommend.recommend_time,
     personal_info.full_name,
     personal_info.gender,
     personal_info.contact_phone,

+ 20 - 1
.docs/sql/职业指导服务-DDL修复.sql

@@ -1,8 +1,24 @@
 -- ============================================================
 -- 职业指导服务 - DDL 修复脚本
+-- 用途:修复视图年龄计算
 -- ============================================================
 
-ALTER TABLE ZJRS_JEECG_BOOT.CAREER_GUIDANCE_DOCUMENT MODIFY CATEGORY_ID VARCHAR(36) DEFAULT NULL;
+-- 视图年龄修复(YEARS_BETWEEN → MONTHS_BETWEEN/12)
+CREATE OR REPLACE VIEW V_CAREER_GUIDANCE_SERVICE AS
+SELECT
+    cgs.ID,
+    cgs.PERSONAL_ID,
+    cgs.APPLICATION_REASON,
+    cgs.APPLICATION_DATE,
+    cgs.IS_FOLLOWED_UP,
+    cgs.LAST_NOTIFY_TIME,
+    pi.FULL_NAME AS NAME,
+    pi.ID_NUMBER AS ID_CARD,
+    pi.GENDER,
+    FLOOR(MONTHS_BETWEEN(SYSDATE, pi.BIRTH_DATE) / 12) AS AGE,
+    pi.EDUCATION,
+    pi.HOUSEHOLD_LOCATION,
+    pi.CONTACT_PHONE,
+    cgs.CREATE_TIME
+FROM CAREER_GUIDANCE_SERVICE cgs
+LEFT JOIN ZJRS_JEECG_BOOT.PERSONAL_INFO pi ON cgs.PERSONAL_ID = pi.ID;

+ 59 - 11
.docs/sql/职业指导服务-测试数据.sql

@@ -1,36 +1,71 @@
 -- ============================================================
+-- 就业一湛通服务平台 - 职业指导服务 测试数据
+-- 数据库:达梦数据库 (DM8)
+-- 创建日期:2026-06-12
+-- 说明:关联 PERSONAL_INFO 表中已存在的测试数据 (178060100000200~178060100000205)
 -- ============================================================
 
+-- 清理旧数据
 DELETE FROM CAREER_GUIDANCE_RECORD;
 DELETE FROM CAREER_GUIDANCE_SERVICE;
 DELETE FROM CAREER_GUIDANCE_EXPERT;
 
+-- ============================================================
 -- 1. 职业指导专家库
-INSERT INTO CAREER_GUIDANCE_EXPERT (ID, NAME, CONTACT_PHONE, EXPERTISE_AREA, CREATE_BY, CREATE_TIME, SYS_ORG_CODE) 
+-- ============================================================
+INSERT INTO CAREER_GUIDANCE_EXPERT (ID, NAME, CONTACT_PHONE, EXPERTISE_AREA, CREATE_BY, CREATE_TIME, SYS_ORG_CODE)
 VALUES ('178060200000001', '王明', '13800138000', '职场生涯规划、简历深度优化、面试模拟辅导', 'admin', CURRENT_TIMESTAMP, 'A01');
-INSERT INTO CAREER_GUIDANCE_EXPERT (ID, NAME, CONTACT_PHONE, EXPERTISE_AREA, CREATE_BY, CREATE_TIME, SYS_ORG_CODE) 
+
+INSERT INTO CAREER_GUIDANCE_EXPERT (ID, NAME, CONTACT_PHONE, EXPERTISE_AREA, CREATE_BY, CREATE_TIME, SYS_ORG_CODE)
 VALUES ('178060200000002', '李红', '13900139000', '大学生创业政策咨询、职业心理调适、转型技能提升', 'admin', CURRENT_TIMESTAMP, 'A01');
 
+INSERT INTO CAREER_GUIDANCE_EXPERT (ID, NAME, CONTACT_PHONE, EXPERTISE_AREA, CREATE_BY, CREATE_TIME, SYS_ORG_CODE)
+VALUES ('178060200000003', '陈志强', '13700137000', '制造业技能培训、农民工就业指导、劳动法规咨询', 'admin', CURRENT_TIMESTAMP, 'A01');
+
+-- ============================================================
 -- 2. 职业指导服务申请
-INSERT INTO CAREER_GUIDANCE_SERVICE (ID, PERSONAL_ID, APPLICATION_REASON, APPLICATION_DATE, IS_FOLLOWED_UP, CREATE_BY, CREATE_TIME, SYS_ORG_CODE) 
-VALUES ('178060210000001', '178060100000200', '应届生求职方向迷茫,需要针对互联网行业的简历优化建议', '2026-06-08', '是', 'admin', CURRENT_TIMESTAMP, 'A01');
-INSERT INTO CAREER_GUIDANCE_SERVICE (ID, PERSONAL_ID, APPLICATION_REASON, APPLICATION_DATE, IS_FOLLOWED_UP, CREATE_BY, CREATE_TIME, SYS_ORG_CODE) 
-VALUES ('178060210000002', '178060100000201', '城镇失业人员,寻求职业技能培训推荐及转型指导', '2026-06-09', '否', 'admin', CURRENT_TIMESTAMP, 'A01');
-
-INSERT INTO CAREER_GUIDANCE_RECORD (ID, SERVICE_ID, RECORD_TYPE, TITLE, CONTENT, RECORD_TIME, OPERATOR, CREATE_BY, CREATE_TIME, SYS_ORG_CODE) 
-VALUES ('178060220000001', '178060210000001', 1, NULL, '已通过电话进行初步沟通。建议人员突出在校项目经历中的技术栈细节,并针对目标岗位调整关键词。', '2026-06-09 10:00:00', '王明(专家)', 'admin', CURRENT_TIMESTAMP, 'A01');
-INSERT INTO CAREER_GUIDANCE_RECORD (ID, SERVICE_ID, RECORD_TYPE, TITLE, CONTENT, RECORD_TIME, OPERATOR, CREATE_BY, CREATE_TIME, SYS_ORG_CODE) 
-VALUES ('178060220000002', '178060210000001', 2, '职业指导申请进度更新', '您的职业指导申请已由专家王明受理,第一阶段建议已发送,请查收。', '2026-06-09 10:30:00', '系统管理员', 'admin', CURRENT_TIMESTAMP, 'A01');
+-- ============================================================
+INSERT INTO CAREER_GUIDANCE_SERVICE (ID, PERSONAL_ID, APPLICATION_REASON, APPLICATION_DATE, IS_FOLLOWED_UP, LAST_NOTIFY_TIME, CREATE_BY, CREATE_TIME, SYS_ORG_CODE)
+VALUES ('178060210000001', '178060100000200', '应届生求职方向迷茫,需要针对互联网行业的简历优化建议', '2026-06-01', '是', '2026-06-09 10:00:00', 'admin', CURRENT_TIMESTAMP, 'A01');
+
+INSERT INTO CAREER_GUIDANCE_SERVICE (ID, PERSONAL_ID, APPLICATION_REASON, APPLICATION_DATE, IS_FOLLOWED_UP, LAST_NOTIFY_TIME, CREATE_BY, CREATE_TIME, SYS_ORG_CODE)
+VALUES ('178060210000002', '178060100000201', '失业半年以上,寻求职业技能培训推荐及转型指导', '2026-06-03', '是', '2026-06-08 15:30:00', 'admin', CURRENT_TIMESTAMP, 'A01');
+
+INSERT INTO CAREER_GUIDANCE_SERVICE (ID, PERSONAL_ID, APPLICATION_REASON, APPLICATION_DATE, IS_FOLLOWED_UP, LAST_NOTIFY_TIME, CREATE_BY, CREATE_TIME, SYS_ORG_CODE)
+VALUES ('178060210000003', '178060100000202', '脱贫人员希望找到稳定工作,需要就业指导和岗位推荐', '2026-06-05', '否', NULL, 'admin', CURRENT_TIMESTAMP, 'A01');
+
+INSERT INTO CAREER_GUIDANCE_SERVICE (ID, PERSONAL_ID, APPLICATION_REASON, APPLICATION_DATE, IS_FOLLOWED_UP, LAST_NOTIFY_TIME, CREATE_BY, CREATE_TIME, SYS_ORG_CODE)
+VALUES ('178060210000004', '178060100000203', '农学硕士寻求农业技术相关岗位,需要行业分析和面试辅导', '2026-06-07', '否', NULL, 'admin', CURRENT_TIMESTAMP, 'A01');
+
+INSERT INTO CAREER_GUIDANCE_SERVICE (ID, PERSONAL_ID, APPLICATION_REASON, APPLICATION_DATE, IS_FOLLOWED_UP, LAST_NOTIFY_TIME, CREATE_BY, CREATE_TIME, SYS_ORG_CODE)
+VALUES ('178060210000005', '178060100000204', '退役军人希望转业到物流行业,需要职业规划和技能培训建议', '2026-06-09', '是', '2026-06-11 09:20:00', 'admin', CURRENT_TIMESTAMP, 'A01');
+
+INSERT INTO CAREER_GUIDANCE_SERVICE (ID, PERSONAL_ID, APPLICATION_REASON, APPLICATION_DATE, IS_FOLLOWED_UP, LAST_NOTIFY_TIME, CREATE_BY, CREATE_TIME, SYS_ORG_CODE)
+VALUES ('178060210000006', '178060100000205', '零就业家庭成员,有会计证,寻求财务类岗位及面试指导', '2026-06-10', '否', NULL, 'admin', CURRENT_TIMESTAMP, 'A01');
+
+-- ============================================================
+-- 3. 服务跟进记录
+-- ============================================================
+-- 记录: 申请1 (178060210000001) — 王明专家跟进
+INSERT INTO CAREER_GUIDANCE_RECORD (ID, SERVICE_ID, RECORD_TYPE, TITLE, CONTENT, RECORD_TIME, OPERATOR, CREATE_BY, CREATE_TIME, SYS_ORG_CODE)
+VALUES ('178060220000001', '178060210000001', 1, '初次电话沟通', '已通过电话进行初步沟通。建议人员突出在校项目经历中的技术栈细节,并针对目标岗位调整关键词。', '2026-06-02 10:00:00', '王明', 'admin', CURRENT_TIMESTAMP, 'A01');
+
+INSERT INTO CAREER_GUIDANCE_RECORD (ID, SERVICE_ID, RECORD_TYPE, TITLE, CONTENT, RECORD_TIME, OPERATOR, CREATE_BY, CREATE_TIME, SYS_ORG_CODE)
+VALUES ('178060220000002', '178060210000001', 2, '简历优化完成', '协助完成简历优化,新增2个实习项目描述,调整技能标签为Java/Python/Spring Boot。', '2026-06-05 14:00:00', '王明', 'admin', CURRENT_TIMESTAMP, 'A01');
+
+INSERT INTO CAREER_GUIDANCE_RECORD (ID, SERVICE_ID, RECORD_TYPE, TITLE, CONTENT, RECORD_TIME, OPERATOR, CREATE_BY, CREATE_TIME, SYS_ORG_CODE)
+VALUES ('178060220000003', '178060210000001', 3, '推送岗位信息', '推送湛江本地3个Java开发岗位,人员表示会尽快投递。', '2026-06-09 10:00:00', '王明', 'admin', CURRENT_TIMESTAMP, 'A01');
+
+-- 记录: 申请2 (178060210000002) — 李红专家跟进
+INSERT INTO CAREER_GUIDANCE_RECORD (ID, SERVICE_ID, RECORD_TYPE, TITLE, CONTENT, RECORD_TIME, OPERATOR, CREATE_BY, CREATE_TIME, SYS_ORG_CODE)
+VALUES ('178060220000004', '178060210000002', 1, '需求评估', '人员有文秘经验,建议朝行政/人事方向发展。推荐参加办公软件高级应用培训。', '2026-06-04 09:30:00', '李红', 'admin', CURRENT_TIMESTAMP, 'A01');
+
+INSERT INTO CAREER_GUIDANCE_RECORD (ID, SERVICE_ID, RECORD_TYPE, TITLE, CONTENT, RECORD_TIME, OPERATOR, CREATE_BY, CREATE_TIME, SYS_ORG_CODE)
+VALUES ('178060220000005', '178060210000002', 2, '培训推荐', '推荐湛江市人社局免费职业技能培训(办公软件应用),人员已报名参加。', '2026-06-08 15:30:00', '李红', 'admin', CURRENT_TIMESTAMP, 'A01');
+
+-- 记录: 申请5 (178060210000005) — 陈志强专家跟进
+INSERT INTO CAREER_GUIDANCE_RECORD (ID, SERVICE_ID, RECORD_TYPE, TITLE, CONTENT, RECORD_TIME, OPERATOR, CREATE_BY, CREATE_TIME, SYS_ORG_CODE)
+VALUES ('178060220000006', '178060210000005', 1, '退役军人专项指导', '了解人员军队驾驶经验,建议考取危险品运输资格证,推荐物流公司司机岗位。', '2026-06-10 11:00:00', '陈志强', 'admin', CURRENT_TIMESTAMP, 'A01');
+
+INSERT INTO CAREER_GUIDANCE_RECORD (ID, SERVICE_ID, RECORD_TYPE, TITLE, CONTENT, RECORD_TIME, OPERATOR, CREATE_BY, CREATE_TIME, SYS_ORG_CODE)
+VALUES ('178060220000007', '178060210000005', 3, '岗位推荐反馈', '推送湛江港物流公司岗位,人员已投递简历并等待面试通知。', '2026-06-11 09:20:00', '陈志强', 'admin', CURRENT_TIMESTAMP, 'A01');

+ 14 - 0
.docs/sql/职业指导服务-补充字典数据.sql

@@ -0,0 +1,14 @@
+-- ============================================================
+-- 就业一湛通服务平台 - 职业指导服务模块 补充字典数据
+-- 数据库:达梦数据库 (DM8)
+-- 创建日期:2026-06-12
+-- 说明:消息中心通过 notification_module 字典映射 moduleType 到中文名称
+-- ============================================================
+
+INSERT INTO DICTIONARY_ITEM (DictionaryItemID, Code, DictionaryCode, Value, Name, OrderNo, RecordStatus, IsEditable)
+SELECT '100060100023', 'career_guidance', 'notification_module', 5, '职业指导', 5, 1, 0
+WHERE NOT EXISTS (SELECT 1 FROM DICTIONARY_ITEM WHERE DictionaryCode = 'notification_module' AND Code = 'career_guidance');
+
+INSERT INTO DICTIONARY_ITEM (DictionaryItemID, Code, DictionaryCode, Value, Name, OrderNo, RecordStatus, IsEditable)
+SELECT '100060100024', 'preliminary_eligible', 'notification_module', 6, '初步符合条件人员', 6, 1, 0
+WHERE NOT EXISTS (SELECT 1 FROM DICTIONARY_ITEM WHERE DictionaryCode = 'notification_module' AND Code = 'preliminary_eligible');

+ 26 - 0
.docs/sql/职业指导服务-视图年龄修复.sql

@@ -0,0 +1,26 @@
+-- ============================================================
+-- 就业一湛通服务平台 - 职业指导服务 视图年龄修复
+-- 数据库:达梦数据库 (DM8)
+-- 创建日期:2026-06-12
+-- 修复:年龄计算 YEARS_BETWEEN → FLOOR(MONTHS_BETWEEN/12)
+-- 原因:YEARS_BETWEEN 可能返回负数或不准确,导致列表年龄显示为负
+-- ============================================================
+
+CREATE OR REPLACE VIEW V_CAREER_GUIDANCE_SERVICE AS
+SELECT
+    cgs.ID,
+    cgs.PERSONAL_ID,
+    cgs.APPLICATION_REASON,
+    cgs.APPLICATION_DATE,
+    cgs.IS_FOLLOWED_UP,
+    cgs.LAST_NOTIFY_TIME,
+    pi.FULL_NAME AS NAME,
+    pi.ID_NUMBER AS ID_CARD,
+    pi.GENDER,
+    FLOOR(MONTHS_BETWEEN(SYSDATE, pi.BIRTH_DATE) / 12) AS AGE,
+    pi.EDUCATION,
+    pi.HOUSEHOLD_LOCATION,
+    pi.CONTACT_PHONE,
+    cgs.CREATE_TIME
+FROM CAREER_GUIDANCE_SERVICE cgs
+LEFT JOIN ZJRS_JEECG_BOOT.PERSONAL_INFO pi ON cgs.PERSONAL_ID = pi.ID;

+ 1 - 1
.docs/sql/职业指导服务-达梦表与视图.sql

@@ -93,7 +93,7 @@ SELECT
     pi.FULL_NAME AS NAME,
     pi.ID_NUMBER AS ID_CARD,
     pi.GENDER,
-    YEARS_BETWEEN(pi.BIRTH_DATE, SYSDATE) AS AGE,
+    FLOOR(MONTHS_BETWEEN(SYSDATE, pi.BIRTH_DATE) / 12) AS AGE,
     pi.EDUCATION,
     pi.HOUSEHOLD_LOCATION,
     pi.CONTACT_PHONE,

+ 14 - 0
.docs/就业援助模块开发记录.md

@@ -205,3 +205,17 @@
 
 ### 就业援助管理-列表页 aidStatus 列去除颜色样式
 - `aidStatus` 列原来使用彩色 `<a-tag>`(已通过=绿/待审核=橙/已驳回=红),改为纯文本 `{{ text || '-' }}`,与其他列保持一致
+
+---
+
+## 2026-06-12 初步符合条件人员导出字典翻译修复
+
+### 问题
+导出Excel时性别、学历、求职人员类别、求职状态等字典字段显示为原始编码。
+
+### 修复文件
+| 文件 | 修改 |
+|------|------|
+| `IPreliminaryEligibleService.java` | 新增 `translateDictFieldsForPageVo()` 方法 |
+| `PreliminaryEligibleServiceImpl.java` | 注入 `IDictionaryItemService`,批量查询字典 → switch-case替换字段值为标签 |
+| `PreliminaryEligibleController.java` | 重写 `exportXls`:查询 `PreliminaryEligiblePageVo` 数据 → 过滤选中 → 字典翻译 → 导出为 `.xlsx` |

+ 44 - 0
.docs/开发记录_见习岗位与见习人员管理模块.md

@@ -703,3 +703,47 @@ async function loadResidenceTreeChildren(treeNode) {
 初始误清空了 Code,后恢复为文本值(因 DB 存储的是 '见习中' 等文本,非数值):
 - `internship_status`: Code='见习中'/'已期满'/'已退出'
 - `audit_status`: Code='待审核'/'已通过'/'未通过'
+
+---
+
+## 2026-06-12 导出字典翻译修复
+
+### 问题
+导出Excel时性别、学历等字典字段显示为原始编码,日期格式异常。
+
+### 修复文件
+| 文件 | 修改 |
+|------|------|
+| `IInternshipPersonnelService.java` | 新增 `translateDictFieldsForPageVo()` 方法 |
+| `InternshipPersonnelServiceImpl.java` | 注入 `IDictionaryItemService`,批量查询字典 → switch-case替换字段值为标签 |
+| `InternshipPersonnelController.java` | 重写 `exportXls`:查询 `InternshipPersonnelPageVo` 数据 → 过滤选中 → 字典翻译 → 导出为 `.xlsx` |
+
+---
+
+## 2026-06-12 岗位推送功能实现
+
+### 问题
+列表操作栏"岗位推送"按钮为占位状态,点击提示"岗位推送功能待开发"。
+
+### 实现方案
+复用 `JobRecommend` 模块的 `/jobRecommend/add` 接口,前端新增通用 `JobPushModal` 弹窗供用户选择岗位后推送。
+
+### 后端修改(JobRecommend 模块)
+| 文件 | 修改 |
+|------|------|
+| `jobrecommend/dto/AvailablePostVo.java` | **新增** — 可选岗位VO |
+| `jobrecommend/mapper/xml/JobRecommendMapper.xml` | **新增** — UNION 查询三张岗位表,支持 keyword 模糊搜索 |
+| `jobrecommend/mapper/JobRecommendMapper.java` | **新增** — `queryAvailablePosts()` 方法 |
+| `jobrecommend/service/IJobRecommendService.java` | **新增** — `queryAvailablePosts()` 接口 |
+| `jobrecommend/service/impl/JobRecommendServiceImpl.java` | **新增** — `queryAvailablePosts()` 实现 |
+| `jobrecommend/controller/JobRecommendController.java` | **新增** — `GET /jobRecommend/availablePosts` 端点 |
+
+### 前端修改
+| 文件 | 修改 |
+|------|------|
+| `jobrecommend/JobRecommend.api.ts` | **新增** — `listAvailablePosts()` + `pushJob()` API |
+| `jobrecommend/components/JobPushModal.vue` | **新增** — 通用岗位推送弹窗(岗位类型筛选、名称搜索、单选表格、推荐意见),FocusPersonnel 和 InternshipPersonnel 共用 |
+| `internshippersonnel/InternshipPersonnelList.vue` | **修改** — 替换 `handleJobPush` 占位函数,接入 JobPushModal 弹窗 |
+
+### 交互流程
+点击"岗位推送" → 弹窗自动加载可选岗位列表 → 可筛选岗位类型(见习/公益/普通)+ 模糊搜索 → 单选岗位 → 填写推荐意见(选填)→ 确定 → 调用 `/jobRecommend/add` 创建推荐记录(recommendType=1, recommendStatus=0),后端自动填充推荐人/机构/时间

+ 12 - 0
.docs/模块开发说明-就业状态自动感知.md

@@ -349,3 +349,15 @@ notification_record          notification_target
 ### 表结构优化
 - `notification_record` 新增 `SENDER_ID VARCHAR(36)`(关联 `sys_user.id`),`sender` 存姓名冗余展示
 - `create_by` 为 JeecgBoot 标准字段(存 `sys_user.username`)
+
+---
+
+## 2026-06-12 导出日期格式修复
+
+### 问题
+导出Excel时日期格式异常,因 `ExportParams` 缺少 `ExcelType.XSSF`。
+
+### 修复文件
+| 文件 | 修改 |
+|------|------|
+| `PersonalStatusController.java` | `ExportParams` 补 `ExcelType.XSSF` |

+ 12 - 0
.docs/模块开发说明-政策推送和岗位信息变动推送.md

@@ -264,3 +264,15 @@ LEFT JOIN PERSONAL_STATUS_LOCAL psl ON pi.ID = psl.PERSONAL_ID;
 
 ### 政策公开文件
 - 无修改(纯文件管理模块,无需字典翻译)
+
+---
+
+## 2026-06-12 导出日期格式修复
+
+### 问题
+导出Excel时日期格式异常,因 `ExportParams` 缺少 `ExcelType.XSSF`。
+
+### 修复文件
+| 文件 | 修改 |
+|------|------|
+| `ProfileChangeController.java` | 补 `ExcelType` import + `ExportParams` 加 `ExcelType.XSSF` |

BIN
doc/参考ui页面.png


Разница между файлами не показана из-за своего большого размера
+ 563 - 772
doc/报错信息.txt


+ 4 - 0
jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/ShiroConfig.java

@@ -98,6 +98,10 @@ public class ShiroConfig {
         filterChainDefinitionMap.put("/sys/phoneLogin", "anon");//手机登录
         filterChainDefinitionMap.put("/zjrs/login/miniProgramPersonalLogin", "anon");//小程序个人登录
         filterChainDefinitionMap.put("/zjrs/login/miniProgramEnterpriseLogin", "anon");//小程序企业登录
+        filterChainDefinitionMap.put("/recruitmentFair/publicList", "anon");//小程序-招聘会公开列表
+        filterChainDefinitionMap.put("/recruitmentFair/detail", "anon");//小程序-招聘会详情
+        filterChainDefinitionMap.put("/policyfile/file/queryById", "anon");//小程序-政策文件详情
+        filterChainDefinitionMap.put("/policyfile/file/view", "anon");//小程序-政策文件在线查看
         filterChainDefinitionMap.put("/sys/user/checkOnlyUser", "anon");//校验用户是否存在
         filterChainDefinitionMap.put("/sys/user/register", "anon");//用户注册
         filterChainDefinitionMap.put("/sys/user/phoneVerification", "anon");//用户忘记密码验证手机号

+ 54 - 1
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/careerguidanceservice/controller/CareerGuidanceServiceController.java

@@ -13,6 +13,7 @@ import org.jeecgframework.poi.excel.ExcelImportUtil;
 import org.jeecgframework.poi.excel.def.NormalExcelConstants;
 import org.jeecgframework.poi.excel.entity.ExportParams;
 import org.jeecgframework.poi.excel.entity.ImportParams;
+import org.jeecgframework.poi.excel.entity.enmus.ExcelType;
 import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
 import org.jeecg.common.system.vo.LoginUser;
 import org.apache.shiro.SecurityUtils;
@@ -65,6 +66,13 @@ public class CareerGuidanceServiceController {
     @Autowired
     private IPersonalInfoService personalInfoService;
 
+    /** 导出字典字段映射 */
+    private static final Map<String, String> EXPORT_DICT_FIELD_MAP = new LinkedHashMap<>();
+    static {
+        EXPORT_DICT_FIELD_MAP.put("gender", "Gender");
+        EXPORT_DICT_FIELD_MAP.put("education", "Education");
+    }
+
     /**
      * 分页列表查询
      *
@@ -258,11 +266,56 @@ public class CareerGuidanceServiceController {
           pageList.add(vo);
       }
 
+      // Step.3.5 字典翻译(gender/education 从 PersonalInfo 获取的是字典码,翻译为中文)
+      if (!pageList.isEmpty()) {
+          List<String> dictCodes = new ArrayList<>(EXPORT_DICT_FIELD_MAP.values());
+          Map<String, List<Map<String, Object>>> dictData = dictionaryItemService.getDictItemsBatch(dictCodes);
+          Map<String, Map<String, String>> valueLabelMap = new HashMap<>();
+          for (Map.Entry<String, List<Map<String, Object>>> entry : dictData.entrySet()) {
+              Map<String, String> valueToLabel = new HashMap<>();
+              for (Map<String, Object> item : entry.getValue()) {
+                  valueToLabel.put(String.valueOf(item.get("value")), String.valueOf(item.get("label")));
+              }
+              valueLabelMap.put(entry.getKey(), valueToLabel);
+          }
+          for (CareerGuidanceServicePage item : pageList) {
+              if (item.getGender() != null && valueLabelMap.get("Gender") != null) {
+                  item.setGender(valueLabelMap.get("Gender").getOrDefault(item.getGender(), item.getGender()));
+              }
+              if (item.getEducation() != null && valueLabelMap.get("Education") != null) {
+                  item.setEducation(valueLabelMap.get("Education").getOrDefault(item.getEducation(), item.getEducation()));
+              }
+          }
+      }
+
+      // Step.3.6 XZQH code -> name translation for householdLocation
+      java.util.Set<String> xzqhCodes = new java.util.LinkedHashSet<>();
+      for (CareerGuidanceServicePage item : pageList) {
+          if (item.getHouseholdLocation() != null && !item.getHouseholdLocation().isEmpty())
+              xzqhCodes.add(item.getHouseholdLocation());
+      }
+      if (!xzqhCodes.isEmpty()) {
+          java.util.List<String> codeList = new java.util.ArrayList<>(xzqhCodes);
+          java.util.List<String> names = dictionaryItemService.loadDictItem("XZQH", String.join(",", codeList));
+          java.util.Map<String, String> xzqhMap = new java.util.HashMap<>();
+          for (int i = 0; i < codeList.size(); i++) {
+              xzqhMap.put(codeList.get(i), names.get(i));
+          }
+          for (CareerGuidanceServicePage item : pageList) {
+              if (item.getHouseholdLocation() != null && !item.getHouseholdLocation().isEmpty()) {
+                  String name = xzqhMap.get(item.getHouseholdLocation());
+                  if (name == null && item.getHouseholdLocation().length() >= 6)
+                      name = xzqhMap.get(item.getHouseholdLocation().substring(0, 6));
+                  if (name != null) item.setHouseholdLocation(name);
+              }
+          }
+      }
+
       // Step.4 AutoPoi 导出Excel
       ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
       mv.addObject(NormalExcelConstants.FILE_NAME, "职业指导服务列表");
       mv.addObject(NormalExcelConstants.CLASS, CareerGuidanceServicePage.class);
-      mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("职业指导服务数据", "导出人:"+sysUser.getRealname(), "职业指导服务"));
+      mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("职业指导服务数据", "导出人:"+sysUser.getRealname(), "职业指导服务", ExcelType.XSSF));
       mv.addObject(NormalExcelConstants.DATA_LIST, pageList);
 
       String exportFields = "name,gender,age,education,contactPhone,householdLocation,applicationReason,applicationDate,isFollowedUp,lastNotifyTime,createTime";

+ 13 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/careerguidanceservice/mapper/xml/CareerGuidanceRecordMapper.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.jeecg.modules.zjrs.careerguidanceservice.mapper.CareerGuidanceRecordMapper">
+
+    <select id="selectByMainId" resultType="org.jeecg.modules.zjrs.careerguidanceservice.entity.CareerGuidanceRecord">
+        SELECT * FROM CAREER_GUIDANCE_RECORD WHERE SERVICE_ID = #{mainId} ORDER BY RECORD_TIME DESC
+    </select>
+
+    <delete id="deleteByMainId" parameterType="java.lang.String">
+        DELETE FROM CAREER_GUIDANCE_RECORD WHERE SERVICE_ID = #{mainId}
+    </delete>
+
+</mapper>

+ 1 - 1
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/deepAnalysis/absorbDifficultSubsidy/service/impl/DaAbsorbDifficultSubsidyServiceImpl.java

@@ -26,7 +26,7 @@ public class DaAbsorbDifficultSubsidyServiceImpl extends ServiceImpl<DaAbsorbDif
     public List<DaAbsorbDifficultSubsidy> translateDictFields(List<DaAbsorbDifficultSubsidy> list, Map<String, String> fieldDictMap) {
         // 批量查询导出涉及的所有字典数据
         List<String> dictCodes = new ArrayList<>(fieldDictMap.values());
-        Map<String, List<Map<String, Object>>> dictData = dictionaryItemService.getDictItemsBat(dictCodes);
+        Map<String, List<Map<String, Object>>> dictData = dictionaryItemService.getDictItemsBatch(dictCodes);
 
         // 构建 value→label 快速查找映射: Map<字典编码, Map<value, label>>
         Map<String, Map<String, String>> valueLabelMap = new HashMap<>();

+ 1 - 1
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/dictionary/service/IDictionaryItemService.java

@@ -16,7 +16,7 @@ public interface IDictionaryItemService extends IService<DictionaryItem> {
     /**
      * 批量获取字典项,返回 Map,key 为字典编码,value 为 label/value 键值对列表
      */
-    Map<String, List<Map<String, String>>> getDictItemsBatch(List<String> dictionaryCodes);
+    Map<String, List<Map<String, Object>>> getDictItemsBatch(List<String> dictionaryCodes);
 
     /**
      * 根据字典编码获取树形字典项,返回带 children 的树形结构

+ 1 - 1
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/dictionary/service/impl/DictionaryItemServiceImpl.java

@@ -34,7 +34,7 @@ public class DictionaryItemServiceImpl extends ServiceImpl<DictionaryItemMapper,
     }
 
     @Override
-    public Map<String, List<Map<String, String>>> getDictItemsBatch(List<String> dictionaryCodes) {
+    public Map<String, List<Map<String, Object>>> getDictItemsBatch(List<String> dictionaryCodes) {
         // 一次查询所有字典编码的数据,避免 N+1 问题
         QueryWrapper<DictionaryItem> queryWrapper = new QueryWrapper<>();
         queryWrapper.in("DictionaryCode", dictionaryCodes);

+ 2 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/employmentassistance/controller/EmploymentAssistanceController.java

@@ -50,6 +50,8 @@ public class EmploymentAssistanceController extends JeecgController<EmploymentAs
     static {
         EXPORT_DICT_FIELD_MAP.put("aidType", "aid_type");
         EXPORT_DICT_FIELD_MAP.put("aidStatus", "aid_status");
+        EXPORT_DICT_FIELD_MAP.put("gender", "Gender");
+        EXPORT_DICT_FIELD_MAP.put("education", "Education");
     }
 
     /**

+ 31 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/employmentassistance/service/impl/EmploymentAssistanceServiceImpl.java

@@ -65,6 +65,37 @@ public class EmploymentAssistanceServiceImpl extends ServiceImpl<EmploymentAssis
                         if (vo.getAidStatus() != null)
                             vo.setAidStatus(mapping.getOrDefault(vo.getAidStatus(), vo.getAidStatus()));
                         break;
+                    case "gender":
+                        if (vo.getGender() != null)
+                            vo.setGender(mapping.getOrDefault(vo.getGender(), vo.getGender()));
+                        break;
+                    case "education":
+                        if (vo.getEducation() != null)
+                            vo.setEducation(mapping.getOrDefault(vo.getEducation(), vo.getEducation()));
+                        break;
+                }
+            }
+        }
+
+        // XZQH code -> name translation for householdLocation
+        java.util.Set<String> xzqhCodes = new java.util.LinkedHashSet<>();
+        for (EmploymentAssistancePageVo vo : list) {
+            if (vo.getHouseholdLocation() != null && !vo.getHouseholdLocation().isEmpty())
+                xzqhCodes.add(vo.getHouseholdLocation());
+        }
+        if (!xzqhCodes.isEmpty()) {
+            java.util.List<String> codeList = new java.util.ArrayList<>(xzqhCodes);
+            java.util.List<String> names = dictionaryItemService.loadDictItem("XZQH", String.join(",", codeList));
+            java.util.Map<String, String> xzqhMap = new java.util.HashMap<>();
+            for (int i = 0; i < codeList.size(); i++) {
+                xzqhMap.put(codeList.get(i), names.get(i));
+            }
+            for (EmploymentAssistancePageVo vo : list) {
+                if (vo.getHouseholdLocation() != null && !vo.getHouseholdLocation().isEmpty()) {
+                    String name = xzqhMap.get(vo.getHouseholdLocation());
+                    if (name == null && vo.getHouseholdLocation().length() >= 6)
+                        name = xzqhMap.get(vo.getHouseholdLocation().substring(0, 6));
+                    if (name != null) vo.setHouseholdLocation(name);
                 }
             }
         }

+ 2 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/employmentregistration/controller/EmploymentRegistrationController.java

@@ -51,6 +51,8 @@ public class EmploymentRegistrationController extends JeecgController<Employment
 
     public static final Map<String, String> EXPORT_DICT_FIELD_MAP = new LinkedHashMap<String, String>() {{
         put("registrationStatus", "registration_status");
+        put("gender", "Gender");
+        put("education", "Education");
     }};
 
     @Operation(summary = "就业一湛通-就业登记管理-分页列表查询")

+ 31 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/employmentregistration/service/impl/EmploymentRegistrationServiceImpl.java

@@ -61,6 +61,37 @@ public class EmploymentRegistrationServiceImpl extends ServiceImpl<EmploymentReg
                         if (vo.getRegistrationStatus() != null)
                             vo.setRegistrationStatus(mapping.getOrDefault(vo.getRegistrationStatus(), vo.getRegistrationStatus()));
                         break;
+                    case "gender":
+                        if (vo.getGender() != null)
+                            vo.setGender(mapping.getOrDefault(vo.getGender(), vo.getGender()));
+                        break;
+                    case "education":
+                        if (vo.getEducation() != null)
+                            vo.setEducation(mapping.getOrDefault(vo.getEducation(), vo.getEducation()));
+                        break;
+                }
+            }
+        }
+
+        // XZQH code -> name translation for householdLocation
+        java.util.Set<String> xzqhCodes = new java.util.LinkedHashSet<>();
+        for (EmploymentRegistrationPageVo vo : list) {
+            if (vo.getHouseholdLocation() != null && !vo.getHouseholdLocation().isEmpty())
+                xzqhCodes.add(vo.getHouseholdLocation());
+        }
+        if (!xzqhCodes.isEmpty()) {
+            java.util.List<String> codeList = new java.util.ArrayList<>(xzqhCodes);
+            java.util.List<String> names = dictionaryItemService.loadDictItem("XZQH", String.join(",", codeList));
+            java.util.Map<String, String> xzqhMap = new java.util.HashMap<>();
+            for (int i = 0; i < codeList.size(); i++) {
+                xzqhMap.put(codeList.get(i), names.get(i));
+            }
+            for (EmploymentRegistrationPageVo vo : list) {
+                if (vo.getHouseholdLocation() != null && !vo.getHouseholdLocation().isEmpty()) {
+                    String name = xzqhMap.get(vo.getHouseholdLocation());
+                    if (name == null && vo.getHouseholdLocation().length() >= 6)
+                        name = xzqhMap.get(vo.getHouseholdLocation().substring(0, 6));
+                    if (name != null) vo.setHouseholdLocation(name);
                 }
             }
         }

+ 61 - 1
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/focuspersonnel/controller/FocusPersonnelController.java

@@ -8,14 +8,23 @@ import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.shiro.SecurityUtils;
 import org.apache.shiro.authz.annotation.RequiresPermissions;
 import org.jeecg.common.api.vo.Result;
 import org.jeecg.common.aspect.annotation.AutoLog;
 import org.jeecg.common.system.base.controller.JeecgController;
+import org.jeecg.common.system.vo.LoginUser;
+import org.jeecg.common.util.oConvertUtils;
 import org.jeecg.modules.zjrs.focuspersonnel.entity.FocusPersonnel;
 import org.jeecg.modules.zjrs.focuspersonnel.entity.FocusPersonnelDetailVo;
 import org.jeecg.modules.zjrs.focuspersonnel.entity.FocusPersonnelPageVo;
 import org.jeecg.modules.zjrs.focuspersonnel.service.IFocusPersonnelService;
+import org.jeecgframework.poi.excel.ExcelImportUtil;
+import org.jeecgframework.poi.excel.def.NormalExcelConstants;
+import org.jeecgframework.poi.excel.entity.ExportParams;
+import org.jeecgframework.poi.excel.entity.ImportParams;
+import org.jeecgframework.poi.excel.entity.enmus.ExcelType;
+import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.servlet.ModelAndView;
@@ -40,6 +49,14 @@ public class FocusPersonnelController extends JeecgController<FocusPersonnel, IF
     @Autowired
     private IFocusPersonnelService focusPersonnelService;
 
+    private static final Map<String, String> EXPORT_DICT_FIELD_MAP = new HashMap<>();
+    static {
+        EXPORT_DICT_FIELD_MAP.put("gender", "Gender");
+        EXPORT_DICT_FIELD_MAP.put("education", "Education");
+        EXPORT_DICT_FIELD_MAP.put("jobSeekerCategory", "JobSeekerCategory");
+        EXPORT_DICT_FIELD_MAP.put("jobSearchStatus", "JobSeekerStatus");
+    }
+
     /**
      * 分页列表查询(关联个人信息表,返回个人信息字段)
      * 支持的查询参数:fullName/education/householdLocation/currentResidence/majorTag/minorTag/customTag/jobSearchStatus
@@ -145,7 +162,50 @@ public class FocusPersonnelController extends JeecgController<FocusPersonnel, IF
     @RequiresPermissions("focus_personnel:exportXls")
     @RequestMapping(value = "/exportXls")
     public ModelAndView exportXls(HttpServletRequest request, FocusPersonnel focusPersonnel) {
-        return super.exportXls(request, focusPersonnel, FocusPersonnel.class, "就业一湛通-重点关注人员管理");
+        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+
+        Map<String, String> params = new HashMap<>();
+        String fullName = request.getParameter("fullName");
+        String gender = request.getParameter("gender");
+        String education = request.getParameter("education");
+        String householdLocation = request.getParameter("householdLocation");
+        String currentResidence = request.getParameter("currentResidence");
+        String majorTag = request.getParameter("majorTag");
+        String minorTag = request.getParameter("minorTag");
+        String customTag = request.getParameter("customTag");
+        String jobSearchStatus = request.getParameter("jobSearchStatus");
+
+        if (fullName != null && !fullName.isEmpty()) params.put("fullName", fullName);
+        if (gender != null && !gender.isEmpty()) params.put("gender", gender);
+        if (education != null && !education.isEmpty()) params.put("education", education);
+        if (householdLocation != null && !householdLocation.isEmpty()) params.put("householdLocation", householdLocation);
+        if (currentResidence != null && !currentResidence.isEmpty()) params.put("currentResidence", currentResidence);
+        if (majorTag != null && !majorTag.isEmpty()) params.put("majorTag", majorTag);
+        if (minorTag != null && !minorTag.isEmpty()) params.put("minorTag", minorTag);
+        if (customTag != null && !customTag.isEmpty()) params.put("customTag", customTag);
+        if (jobSearchStatus != null && !jobSearchStatus.isEmpty()) params.put("jobSearchStatus", jobSearchStatus);
+
+        Page<FocusPersonnelPageVo> page = new Page<>(1, 999999);
+        Page<FocusPersonnelPageVo> pageList = focusPersonnelService.queryPageList(page, params);
+        List<FocusPersonnelPageVo> dataList = pageList.getRecords();
+
+        String selections = request.getParameter("selections");
+        if (oConvertUtils.isNotEmpty(selections)) {
+            List<String> selectionList = Arrays.asList(selections.split(","));
+            dataList = dataList.stream().filter(item -> selectionList.contains(item.getId())).collect(Collectors.toList());
+        }
+
+        focusPersonnelService.translateDictFieldsForPageVo(dataList, EXPORT_DICT_FIELD_MAP);
+
+        ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
+        mv.addObject(NormalExcelConstants.FILE_NAME, "就业一湛通-重点关注人员管理");
+        mv.addObject(NormalExcelConstants.CLASS, FocusPersonnelPageVo.class);
+        mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("重点关注人员管理", "导出人:" + sysUser.getRealname(), "重点关注人员管理", ExcelType.XSSF));
+        mv.addObject(NormalExcelConstants.DATA_LIST, dataList);
+
+        String exportFields = "fullName,gender,age,education,householdLocation,currentResidence,jobSeekerCategory,contactPhone,jobSearchStatus,majorTag,minorTag,customTag";
+        mv.addObject(NormalExcelConstants.EXPORT_FIELDS, exportFields);
+        return mv;
     }
 
     @RequiresPermissions("focus_personnel:importExcel")

+ 12 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/focuspersonnel/entity/FocusPersonnelPageVo.java

@@ -29,50 +29,62 @@ public class FocusPersonnelPageVo implements Serializable {
     private java.lang.String personalId;
 
     /**人员大类标签*/
+    @Excel(name = "人员大类", width = 15)
     @Schema(description = "人员大类标签")
     private java.lang.String majorTag;
 
     /**人员小类标签*/
+    @Excel(name = "人员小类", width = 15)
     @Schema(description = "人员小类标签")
     private java.lang.String minorTag;
 
     /**自定义标签*/
+    @Excel(name = "自定义标签", width = 20)
     @Schema(description = "自定义标签")
     private java.lang.String customTag;
 
     /**姓名(来自个人信息表)*/
+    @Excel(name = "姓名", width = 15)
     @Schema(description = "姓名")
     private java.lang.String fullName;
 
     /**性别(来自个人信息表)*/
+    @Excel(name = "性别", width = 8)
     @Schema(description = "性别")
     private java.lang.String gender;
 
     /**年龄(由出生日期计算)*/
+    @Excel(name = "年龄", width = 8)
     @Schema(description = "年龄")
     private java.lang.Integer age;
 
     /**学历(来自个人信息表)*/
+    @Excel(name = "学历", width = 12)
     @Schema(description = "学历")
     private java.lang.String education;
 
     /**户口所在地(来自个人信息表)*/
+    @Excel(name = "户口所在地", width = 20)
     @Schema(description = "户口所在地")
     private java.lang.String householdLocation;
 
     /**现居住地(来自个人信息表)*/
+    @Excel(name = "现居住地", width = 20)
     @Schema(description = "现居住地")
     private java.lang.String currentResidence;
 
     /**求职人员类别(来自个人信息表)*/
+    @Excel(name = "求职人员类别", width = 15)
     @Schema(description = "求职人员类别")
     private java.lang.String jobSeekerCategory;
 
     /**联系电话(来自个人信息表)*/
+    @Excel(name = "联系电话", width = 15)
     @Schema(description = "联系电话")
     private java.lang.String contactPhone;
 
     /**求职状态(来自个人信息表)*/
+    @Excel(name = "求职状态", width = 12)
     @Schema(description = "求职状态")
     private java.lang.String jobSearchStatus;
 }

+ 2 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/focuspersonnel/service/IFocusPersonnelService.java

@@ -73,4 +73,6 @@ public interface IFocusPersonnelService extends IService<FocusPersonnel> {
      * @param tags 标签名称列表
      */
     void batchUpdateCustomTag(List<String> ids, List<String> tags);
+
+    void translateDictFieldsForPageVo(List<FocusPersonnelPageVo> list, Map<String, String> fieldDictMap);
 }

+ 73 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/focuspersonnel/service/impl/FocusPersonnelServiceImpl.java

@@ -30,6 +30,9 @@ public class FocusPersonnelServiceImpl extends ServiceImpl<FocusPersonnelMapper,
     @Autowired
     private DictionaryItemMapper dictionaryItemMapper;
 
+    @Autowired
+    private org.jeecg.modules.zjrs.dictionary.service.IDictionaryItemService dictionaryItemService;
+
     @Override
     public Page<FocusPersonnelPageVo> queryPageList(Page<FocusPersonnelPageVo> page, Map<String, String> params) {
         return baseMapper.queryPageList(page, params);
@@ -167,4 +170,74 @@ public class FocusPersonnelServiceImpl extends ServiceImpl<FocusPersonnelMapper,
             baseMapper.updateById(person);
         }
     }
+
+    @Override
+    public void translateDictFieldsForPageVo(List<FocusPersonnelPageVo> list, Map<String, String> fieldDictMap) {
+        List<String> dictCodes = new ArrayList<>(fieldDictMap.values());
+        Map<String, List<Map<String, Object>>> dictData = dictionaryItemService.getDictItemsBatch(dictCodes);
+
+        Map<String, Map<String, String>> valueLabelMap = new HashMap<>();
+        for (Map.Entry<String, List<Map<String, Object>>> entry : dictData.entrySet()) {
+            Map<String, String> valueToLabel = new HashMap<>();
+            for (Map<String, Object> item : entry.getValue()) {
+                valueToLabel.put(String.valueOf(item.get("value")), String.valueOf(item.get("label")));
+            }
+            valueLabelMap.put(entry.getKey(), valueToLabel);
+        }
+
+        for (FocusPersonnelPageVo vo : list) {
+            for (Map.Entry<String, String> fieldEntry : fieldDictMap.entrySet()) {
+                String fieldName = fieldEntry.getKey();
+                String dictCode = fieldEntry.getValue();
+                Map<String, String> mapping = valueLabelMap.get(dictCode);
+                if (mapping == null) continue;
+
+                switch (fieldName) {
+                    case "gender":
+                        if (vo.getGender() != null) vo.setGender(mapping.getOrDefault(vo.getGender(), vo.getGender()));
+                        break;
+                    case "education":
+                        if (vo.getEducation() != null) vo.setEducation(mapping.getOrDefault(vo.getEducation(), vo.getEducation()));
+                        break;
+                    case "jobSeekerCategory":
+                        if (vo.getJobSeekerCategory() != null) vo.setJobSeekerCategory(mapping.getOrDefault(vo.getJobSeekerCategory(), vo.getJobSeekerCategory()));
+                        break;
+                    case "jobSearchStatus":
+                        if (vo.getJobSearchStatus() != null) vo.setJobSearchStatus(mapping.getOrDefault(vo.getJobSearchStatus(), vo.getJobSearchStatus()));
+                        break;
+                }
+            }
+        }
+
+        // XZQH code -> name translation for address fields
+        java.util.Set<String> xzqhCodes = new java.util.LinkedHashSet<>();
+        for (FocusPersonnelPageVo vo : list) {
+            if (vo.getHouseholdLocation() != null && !vo.getHouseholdLocation().isEmpty())
+                xzqhCodes.add(vo.getHouseholdLocation());
+            if (vo.getCurrentResidence() != null && !vo.getCurrentResidence().isEmpty())
+                xzqhCodes.add(vo.getCurrentResidence());
+        }
+        if (!xzqhCodes.isEmpty()) {
+            java.util.List<String> codeList = new java.util.ArrayList<>(xzqhCodes);
+            java.util.List<String> names = dictionaryItemService.loadDictItem("XZQH", String.join(",", codeList));
+            java.util.Map<String, String> xzqhMap = new java.util.HashMap<>();
+            for (int i = 0; i < codeList.size(); i++) {
+                xzqhMap.put(codeList.get(i), names.get(i));
+            }
+            for (FocusPersonnelPageVo vo : list) {
+                if (vo.getHouseholdLocation() != null && !vo.getHouseholdLocation().isEmpty()) {
+                    String name = xzqhMap.get(vo.getHouseholdLocation());
+                    if (name == null && vo.getHouseholdLocation().length() >= 6)
+                        name = xzqhMap.get(vo.getHouseholdLocation().substring(0, 6));
+                    if (name != null) vo.setHouseholdLocation(name);
+                }
+                if (vo.getCurrentResidence() != null && !vo.getCurrentResidence().isEmpty()) {
+                    String name = xzqhMap.get(vo.getCurrentResidence());
+                    if (name == null && vo.getCurrentResidence().length() >= 6)
+                        name = xzqhMap.get(vo.getCurrentResidence().substring(0, 6));
+                    if (name != null) vo.setCurrentResidence(name);
+                }
+            }
+        }
+    }
 }

+ 71 - 1
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/internshippersonnel/controller/InternshipPersonnelController.java

@@ -7,10 +7,17 @@ import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.shiro.SecurityUtils;
 import org.apache.shiro.authz.annotation.RequiresPermissions;
 import org.jeecg.common.api.vo.Result;
 import org.jeecg.common.aspect.annotation.AutoLog;
 import org.jeecg.common.system.base.controller.JeecgController;
+import org.jeecg.common.system.vo.LoginUser;
+import org.jeecg.common.util.oConvertUtils;
+import org.jeecgframework.poi.excel.def.NormalExcelConstants;
+import org.jeecgframework.poi.excel.entity.ExportParams;
+import org.jeecgframework.poi.excel.entity.enmus.ExcelType;
+import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
 import org.jeecg.modules.zjrs.dictionary.entity.DictionaryItem;
 import org.jeecg.modules.zjrs.internshippersonnel.entity.InternshipPersonnel;
 import org.jeecg.modules.zjrs.internshippersonnel.entity.InternshipPersonnelDetailVo;
@@ -40,6 +47,14 @@ public class InternshipPersonnelController extends JeecgController<InternshipPer
     @Autowired
     private IInternshipPersonnelService internshipPersonnelService;
 
+    private static final Map<String, String> EXPORT_DICT_FIELD_MAP = new HashMap<>();
+    static {
+        EXPORT_DICT_FIELD_MAP.put("gender", "Gender");
+        EXPORT_DICT_FIELD_MAP.put("education", "Education");
+        EXPORT_DICT_FIELD_MAP.put("jobSeekerCategory", "JobSeekerCategory");
+        EXPORT_DICT_FIELD_MAP.put("jobSearchStatus", "JobSeekerStatus");
+    }
+
     /**
      * 分页列表查询(关联个人信息表和见习岗位表,返回关联字段)
      */
@@ -149,7 +164,62 @@ public class InternshipPersonnelController extends JeecgController<InternshipPer
     @RequiresPermissions("internship_personnel:exportXls")
     @RequestMapping(value = "/exportXls")
     public ModelAndView exportXls(HttpServletRequest request, InternshipPersonnel internshipPersonnel) {
-        return super.exportXls(request, internshipPersonnel, InternshipPersonnel.class, "见习人员管理");
+        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+
+        Map<String, String> params = new HashMap<>();
+        String fullName = request.getParameter("fullName");
+        String internshipStatus = request.getParameter("internshipStatus");
+        String auditStatus = request.getParameter("auditStatus");
+        String postName = request.getParameter("postName");
+        String companyName = request.getParameter("companyName");
+        String gender = request.getParameter("gender");
+        String education = request.getParameter("education");
+        String householdLocation = request.getParameter("householdLocation");
+        String currentResidence = request.getParameter("currentResidence");
+        String majorTag = request.getParameter("majorTag");
+        String minorTag = request.getParameter("minorTag");
+        String customTag = request.getParameter("customTag");
+        String jobSearchStatus = request.getParameter("jobSearchStatus");
+        String ageBegin = request.getParameter("ageBegin");
+        String ageEnd = request.getParameter("ageEnd");
+
+        if (fullName != null && !fullName.isEmpty()) params.put("fullName", fullName);
+        if (internshipStatus != null && !internshipStatus.isEmpty()) params.put("internshipStatus", internshipStatus);
+        if (auditStatus != null && !auditStatus.isEmpty()) params.put("auditStatus", auditStatus);
+        if (postName != null && !postName.isEmpty()) params.put("postName", postName);
+        if (companyName != null && !companyName.isEmpty()) params.put("companyName", companyName);
+        if (gender != null && !gender.isEmpty()) params.put("gender", gender);
+        if (education != null && !education.isEmpty()) params.put("education", education);
+        if (householdLocation != null && !householdLocation.isEmpty()) params.put("householdLocation", householdLocation);
+        if (currentResidence != null && !currentResidence.isEmpty()) params.put("currentResidence", currentResidence);
+        if (majorTag != null && !majorTag.isEmpty()) params.put("majorTag", majorTag);
+        if (minorTag != null && !minorTag.isEmpty()) params.put("minorTag", minorTag);
+        if (customTag != null && !customTag.isEmpty()) params.put("customTag", customTag);
+        if (jobSearchStatus != null && !jobSearchStatus.isEmpty()) params.put("jobSearchStatus", jobSearchStatus);
+        if (ageBegin != null && !ageBegin.isEmpty()) params.put("ageBegin", ageBegin);
+        if (ageEnd != null && !ageEnd.isEmpty()) params.put("ageEnd", ageEnd);
+
+        Page<InternshipPersonnelPageVo> page = new Page<>(1, 999999);
+        Page<InternshipPersonnelPageVo> pageList = internshipPersonnelService.queryPageList(page, params);
+        List<InternshipPersonnelPageVo> dataList = pageList.getRecords();
+
+        String selections = request.getParameter("selections");
+        if (oConvertUtils.isNotEmpty(selections)) {
+            List<String> selectionList = Arrays.asList(selections.split(","));
+            dataList = dataList.stream().filter(item -> selectionList.contains(item.getId())).collect(Collectors.toList());
+        }
+
+        internshipPersonnelService.translateDictFieldsForPageVo(dataList, EXPORT_DICT_FIELD_MAP);
+
+        ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
+        mv.addObject(NormalExcelConstants.FILE_NAME, "就业一湛通-见习人员管理");
+        mv.addObject(NormalExcelConstants.CLASS, InternshipPersonnelPageVo.class);
+        mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("见习人员管理", "导出人:" + sysUser.getRealname(), "见习人员管理", ExcelType.XSSF));
+        mv.addObject(NormalExcelConstants.DATA_LIST, dataList);
+
+        String exportFields = "fullName,gender,age,education,householdLocation,currentResidence,contactPhone,jobSeekerCategory,jobSearchStatus,majorTag,minorTag,customTag,internshipStatus,postName,companyName,startDate,endDate";
+        mv.addObject(NormalExcelConstants.EXPORT_FIELDS, exportFields);
+        return mv;
     }
 
     @RequiresPermissions("internship_personnel:importExcel")

+ 19 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/internshippersonnel/entity/InternshipPersonnelPageVo.java

@@ -8,6 +8,8 @@ import lombok.experimental.Accessors;
 import java.io.Serializable;
 import java.util.Date;
 
+import org.jeecgframework.poi.excel.annotation.Excel;
+
 /**
  * @Description: 见习人员管理(列表页面VO,包含个人信息和岗位信息字段)
  * @Author: jeecg-boot
@@ -33,14 +35,17 @@ public class InternshipPersonnelPageVo implements Serializable {
     private java.lang.String internshipPostId;
 
     /**见习状态*/
+    @Excel(name = "见习状态", width = 12)
     @Schema(description = "见习状态")
     private java.lang.String internshipStatus;
 
     /**见习开始日期*/
+    @Excel(name = "见习开始日期", width = 15, format = "yyyy-MM-dd")
     @Schema(description = "见习开始日期")
     private Date startDate;
 
     /**见习结束日期*/
+    @Excel(name = "见习结束日期", width = 15, format = "yyyy-MM-dd")
     @Schema(description = "见习结束日期")
     private Date endDate;
 
@@ -53,26 +58,32 @@ public class InternshipPersonnelPageVo implements Serializable {
     private java.lang.String auditOpinion;
 
     /**人员大类标签*/
+    @Excel(name = "人员大类", width = 15)
     @Schema(description = "人员大类标签")
     private java.lang.String majorTag;
 
     /**人员小类标签*/
+    @Excel(name = "人员小类", width = 15)
     @Schema(description = "人员小类标签")
     private java.lang.String minorTag;
 
     /**姓名(来自个人信息表)*/
+    @Excel(name = "姓名", width = 15)
     @Schema(description = "姓名")
     private java.lang.String fullName;
 
     /**性别(来自个人信息表)*/
+    @Excel(name = "性别", width = 8)
     @Schema(description = "性别")
     private java.lang.String gender;
 
     /**联系电话(来自个人信息表)*/
+    @Excel(name = "联系电话", width = 15)
     @Schema(description = "联系电话")
     private java.lang.String contactPhone;
 
     /**学历(来自个人信息表)*/
+    @Excel(name = "学历", width = 12)
     @Schema(description = "学历")
     private java.lang.String education;
 
@@ -81,34 +92,42 @@ public class InternshipPersonnelPageVo implements Serializable {
     private java.lang.String idNumber;
 
     /**见习岗位(来自见习岗位表)*/
+    @Excel(name = "见习岗位", width = 20)
     @Schema(description = "见习岗位")
     private java.lang.String postName;
 
     /**见习单位(来自见习岗位表)*/
+    @Excel(name = "见习单位", width = 20)
     @Schema(description = "见习单位")
     private java.lang.String companyName;
 
     /**年龄(由出生日期计算)*/
+    @Excel(name = "年龄", width = 8)
     @Schema(description = "年龄")
     private java.lang.Integer age;
 
     /**户口所在地(来自个人信息表)*/
+    @Excel(name = "户口所在地", width = 20)
     @Schema(description = "户口所在地")
     private java.lang.String householdLocation;
 
     /**现居住地(来自个人信息表)*/
+    @Excel(name = "现居住地", width = 20)
     @Schema(description = "现居住地")
     private java.lang.String currentResidence;
 
     /**求职人员类别(来自个人信息表)*/
+    @Excel(name = "求职人员类别", width = 15)
     @Schema(description = "求职人员类别")
     private java.lang.String jobSeekerCategory;
 
     /**求职状态(来自个人信息表)*/
+    @Excel(name = "求职状态", width = 12)
     @Schema(description = "求职状态")
     private java.lang.String jobSearchStatus;
 
     /**自定义标签(逗号分隔)*/
+    @Excel(name = "自定义标签", width = 20)
     @Schema(description = "自定义标签")
     private java.lang.String customTag;
 }

+ 2 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/internshippersonnel/service/IInternshipPersonnelService.java

@@ -58,4 +58,6 @@ public interface IInternshipPersonnelService extends IService<InternshipPersonne
      * 批量更新自定义标签
      */
     void batchUpdateCustomTag(List<String> ids, List<String> tags);
+
+    void translateDictFieldsForPageVo(List<InternshipPersonnelPageVo> list, Map<String, String> fieldDictMap);
 }

+ 73 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/internshippersonnel/service/impl/InternshipPersonnelServiceImpl.java

@@ -32,6 +32,9 @@ public class InternshipPersonnelServiceImpl extends ServiceImpl<InternshipPerson
     @Autowired
     private DictionaryItemMapper dictionaryItemMapper;
 
+    @Autowired
+    private org.jeecg.modules.zjrs.dictionary.service.IDictionaryItemService dictionaryItemService;
+
     @Override
     public Page<InternshipPersonnelPageVo> queryPageList(Page<InternshipPersonnelPageVo> page, Map<String, String> params) {
         return baseMapper.queryPageList(page, params);
@@ -160,4 +163,74 @@ public class InternshipPersonnelServiceImpl extends ServiceImpl<InternshipPerson
             baseMapper.updateById(person);
         }
     }
+
+    @Override
+    public void translateDictFieldsForPageVo(List<InternshipPersonnelPageVo> list, Map<String, String> fieldDictMap) {
+        List<String> dictCodes = new ArrayList<>(fieldDictMap.values());
+        Map<String, List<Map<String, Object>>> dictData = dictionaryItemService.getDictItemsBatch(dictCodes);
+
+        Map<String, Map<String, String>> valueLabelMap = new HashMap<>();
+        for (Map.Entry<String, List<Map<String, Object>>> entry : dictData.entrySet()) {
+            Map<String, String> valueToLabel = new HashMap<>();
+            for (Map<String, Object> item : entry.getValue()) {
+                valueToLabel.put(String.valueOf(item.get("value")), String.valueOf(item.get("label")));
+            }
+            valueLabelMap.put(entry.getKey(), valueToLabel);
+        }
+
+        for (InternshipPersonnelPageVo vo : list) {
+            for (Map.Entry<String, String> fieldEntry : fieldDictMap.entrySet()) {
+                String fieldName = fieldEntry.getKey();
+                String dictCode = fieldEntry.getValue();
+                Map<String, String> mapping = valueLabelMap.get(dictCode);
+                if (mapping == null) continue;
+
+                switch (fieldName) {
+                    case "gender":
+                        if (vo.getGender() != null) vo.setGender(mapping.getOrDefault(vo.getGender(), vo.getGender()));
+                        break;
+                    case "education":
+                        if (vo.getEducation() != null) vo.setEducation(mapping.getOrDefault(vo.getEducation(), vo.getEducation()));
+                        break;
+                    case "jobSeekerCategory":
+                        if (vo.getJobSeekerCategory() != null) vo.setJobSeekerCategory(mapping.getOrDefault(vo.getJobSeekerCategory(), vo.getJobSeekerCategory()));
+                        break;
+                    case "jobSearchStatus":
+                        if (vo.getJobSearchStatus() != null) vo.setJobSearchStatus(mapping.getOrDefault(vo.getJobSearchStatus(), vo.getJobSearchStatus()));
+                        break;
+                }
+            }
+        }
+
+        // XZQH code -> name translation for address fields
+        java.util.Set<String> xzqhCodes = new java.util.LinkedHashSet<>();
+        for (InternshipPersonnelPageVo vo : list) {
+            if (vo.getHouseholdLocation() != null && !vo.getHouseholdLocation().isEmpty())
+                xzqhCodes.add(vo.getHouseholdLocation());
+            if (vo.getCurrentResidence() != null && !vo.getCurrentResidence().isEmpty())
+                xzqhCodes.add(vo.getCurrentResidence());
+        }
+        if (!xzqhCodes.isEmpty()) {
+            java.util.List<String> codeList = new java.util.ArrayList<>(xzqhCodes);
+            java.util.List<String> names = dictionaryItemService.loadDictItem("XZQH", String.join(",", codeList));
+            java.util.Map<String, String> xzqhMap = new java.util.HashMap<>();
+            for (int i = 0; i < codeList.size(); i++) {
+                xzqhMap.put(codeList.get(i), names.get(i));
+            }
+            for (InternshipPersonnelPageVo vo : list) {
+                if (vo.getHouseholdLocation() != null && !vo.getHouseholdLocation().isEmpty()) {
+                    String name = xzqhMap.get(vo.getHouseholdLocation());
+                    if (name == null && vo.getHouseholdLocation().length() >= 6)
+                        name = xzqhMap.get(vo.getHouseholdLocation().substring(0, 6));
+                    if (name != null) vo.setHouseholdLocation(name);
+                }
+                if (vo.getCurrentResidence() != null && !vo.getCurrentResidence().isEmpty()) {
+                    String name = xzqhMap.get(vo.getCurrentResidence());
+                    if (name == null && vo.getCurrentResidence().length() >= 6)
+                        name = xzqhMap.get(vo.getCurrentResidence().substring(0, 6));
+                    if (name != null) vo.setCurrentResidence(name);
+                }
+            }
+        }
+    }
 }

+ 17 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/jobrecommend/controller/JobRecommendController.java

@@ -16,6 +16,7 @@ import org.jeecg.common.system.base.controller.JeecgController;
 import org.jeecg.common.system.query.QueryGenerator;
 import org.jeecg.common.system.vo.LoginUser;
 import org.jeecg.common.util.oConvertUtils;
+import org.jeecg.modules.zjrs.jobrecommend.dto.AvailablePostVo;
 import org.jeecg.modules.zjrs.jobrecommend.entity.JobRecommend;
 import org.jeecg.modules.zjrs.jobrecommend.entity.JobRecommendPageVo;
 import org.jeecg.modules.zjrs.jobrecommend.service.IJobRecommendService;
@@ -49,6 +50,8 @@ public class JobRecommendController extends JeecgController<JobRecommend, IJobRe
     static {
         EXPORT_DICT_FIELD_MAP.put("recommendType", "recommend_type");
         EXPORT_DICT_FIELD_MAP.put("recommendStatus", "recommend_status");
+        EXPORT_DICT_FIELD_MAP.put("gender", "Gender");
+        EXPORT_DICT_FIELD_MAP.put("education", "Education");
     }
 
     /**
@@ -186,6 +189,20 @@ public class JobRecommendController extends JeecgController<JobRecommend, IJobRe
         return mv;
     }
 
+    //update-begin---author:kk ---date:2026-06-12  for:【信息智能匹配推送】岗位推送功能-查询可选岗位列表-----------
+    @Operation(summary = "查询可选岗位列表(用于岗位推送弹窗选择)")
+    @GetMapping(value = "/availablePosts")
+    public Result<List<AvailablePostVo>> availablePosts(
+            @RequestParam(name = "postType", required = false) String postType,
+            @RequestParam(name = "keyword", required = false) String keyword) {
+        Map<String, String> params = new HashMap<>();
+        if (postType != null && !postType.isEmpty()) params.put("postType", postType);
+        if (keyword != null && !keyword.isEmpty()) params.put("keyword", keyword);
+        List<AvailablePostVo> list = jobRecommendService.queryAvailablePosts(params);
+        return Result.OK(list);
+    }
+    //update-end---author:kk ---date:2026-06-12  for:【信息智能匹配推送】岗位推送功能-查询可选岗位列表-----------
+
     @RequiresPermissions("job_recommend:importExcel")
     @RequestMapping(value = "/importExcel", method = RequestMethod.POST)
     public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {

+ 30 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/jobrecommend/dto/AvailablePostVo.java

@@ -0,0 +1,30 @@
+package org.jeecg.modules.zjrs.jobrecommend.dto;
+
+import lombok.Data;
+
+/**
+ * @Description: 可选岗位VO
+ * @Author: jeecg-boot
+ * @Date:   2026-06-12
+ * @Version: V1.0
+ */
+//update-begin---author:kk ---date:2026-06-12  for:【信息智能匹配推送】岗位推送功能-可选岗位VO-----------
+@Data
+public class AvailablePostVo {
+
+    /** 岗位ID */
+    private String postId;
+
+    /** 岗位名称 */
+    private String postName;
+
+    /** 单位名称 */
+    private String companyName;
+
+    /** 岗位类型: internship_post / welfare_post / post_info */
+    private String postType;
+
+    /** 工作地点 */
+    private String workLocation;
+}
+//update-end---author:kk ---date:2026-06-12  for:【信息智能匹配推送】岗位推送功能-可选岗位VO-----------

+ 2 - 2
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/jobrecommend/entity/JobRecommendPageVo.java

@@ -62,8 +62,8 @@ public class JobRecommendPageVo implements Serializable {
     /**推荐时间*/
     @Excel(name = "推荐时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
     @Schema(description = "推荐时间")
-    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
-    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm")
     private java.util.Date recommendTime;
 
     /**姓名(来自个人信息表)*/

+ 11 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/jobrecommend/mapper/JobRecommendMapper.java

@@ -3,9 +3,11 @@ package org.jeecg.modules.zjrs.jobrecommend.mapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import org.apache.ibatis.annotations.Param;
+import org.jeecg.modules.zjrs.jobrecommend.dto.AvailablePostVo;
 import org.jeecg.modules.zjrs.jobrecommend.entity.JobRecommend;
 import org.jeecg.modules.zjrs.jobrecommend.entity.JobRecommendPageVo;
 
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -23,4 +25,13 @@ public interface JobRecommendMapper extends BaseMapper<JobRecommend> {
      * @return 包含个人信息和岗位信息字段的分页数据
      */
     Page<JobRecommendPageVo> queryPageList(Page<JobRecommendPageVo> page, @Param("params") Map<String, String> params);
+
+    //update-begin---author:kk ---date:2026-06-12  for:【信息智能匹配推送】岗位推送功能-查询可选岗位-----------
+    /**
+     * 查询可选岗位列表(三表UNION:internship_post / welfare_post / post_info)
+     * @param params 查询参数(keyword: 岗位名称模糊搜索)
+     * @return 可选岗位列表
+     */
+    List<AvailablePostVo> queryAvailablePosts(@Param("params") Map<String, String> params);
+    //update-end---author:kk ---date:2026-06-12  for:【信息智能匹配推送】岗位推送功能-查询可选岗位-----------
 }

+ 50 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/jobrecommend/mapper/xml/JobRecommendMapper.xml

@@ -31,4 +31,54 @@
         ORDER BY id DESC
     </select>
 
+    <!-- update-begin author:kk date:2026-06-12 for:信息智能匹配推送-岗位推送功能-查询可选岗位 -->
+    <!-- 从岗位表查询可选岗位列表,支持按 postType 过滤 -->
+    <select id="queryAvailablePosts" resultType="org.jeecg.modules.zjrs.jobrecommend.dto.AvailablePostVo">
+        <choose>
+            <when test="params.postType == 'internship_post'">
+                SELECT id AS postId, post_name AS postName, company_name AS companyName,
+                       'internship_post' AS postType, work_location AS workLocation
+                FROM internship_post
+                WHERE publish_status = '1'
+                <if test="params.keyword != null and params.keyword != ''">
+                    AND post_name LIKE CONCAT('%', #{params.keyword}, '%')
+                </if>
+            </when>
+            <when test="params.postType == 'welfare_post'">
+                SELECT id AS postId, post_name AS postName, company_name AS companyName,
+                       'welfare_post' AS postType, work_location AS workLocation
+                FROM welfare_post
+                WHERE publish_status = '1'
+                <if test="params.keyword != null and params.keyword != ''">
+                    AND post_name LIKE CONCAT('%', #{params.keyword}, '%')
+                </if>
+            </when>
+            <when test="params.postType == 'post_info'">
+                SELECT p.id AS postId, p.post_name AS postName, e.company_name AS companyName,
+                       'post_info' AS postType, p.work_location AS workLocation
+                FROM post_info p
+                LEFT JOIN enterprise_info e ON p.enterprise_id = e.id
+                WHERE 1=1
+                <if test="params.keyword != null and params.keyword != ''">
+                    AND p.post_name LIKE CONCAT('%', #{params.keyword}, '%')
+                </if>
+            </when>
+            <otherwise>
+                SELECT id AS postId, post_name AS postName, company_name AS companyName,
+                       'internship_post' AS postType, work_location AS workLocation
+                FROM internship_post WHERE publish_status = '1'
+                UNION ALL
+                SELECT id AS postId, post_name AS postName, company_name AS companyName,
+                       'welfare_post' AS postType, work_location AS workLocation
+                FROM welfare_post WHERE publish_status = '1'
+                UNION ALL
+                SELECT p.id AS postId, p.post_name AS postName, e.company_name AS companyName,
+                       'post_info' AS postType, p.work_location AS workLocation
+                FROM post_info p LEFT JOIN enterprise_info e ON p.enterprise_id = e.id
+            </otherwise>
+        </choose>
+        ORDER BY postId
+    </select>
+    <!-- update-end author:kk date:2026-06-12 for:信息智能匹配推送-岗位推送功能-查询可选岗位 -->
+
 </mapper>

+ 5 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/jobrecommend/service/IJobRecommendService.java

@@ -2,6 +2,7 @@ package org.jeecg.modules.zjrs.jobrecommend.service;
 
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
+import org.jeecg.modules.zjrs.jobrecommend.dto.AvailablePostVo;
 import org.jeecg.modules.zjrs.jobrecommend.entity.JobRecommend;
 import org.jeecg.modules.zjrs.jobrecommend.entity.JobRecommendPageVo;
 
@@ -35,4 +36,8 @@ public interface IJobRecommendService extends IService<JobRecommend> {
      * @param fieldDictMap 字段名→字典编码 的映射关系
      */
     void translateDictFieldsForPageVo(List<JobRecommendPageVo> list, Map<String, String> fieldDictMap);
+
+    //update-begin---author:kk ---date:2026-06-12  for:【信息智能匹配推送】岗位推送-查询可选岗位列表-----------
+    List<AvailablePostVo> queryAvailablePosts(Map<String, String> params);
+    //update-end---author:kk ---date:2026-06-12  for:【信息智能匹配推送】岗位推送-查询可选岗位列表-----------
 }

+ 16 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/jobrecommend/service/impl/JobRecommendServiceImpl.java

@@ -2,6 +2,7 @@ package org.jeecg.modules.zjrs.jobrecommend.service.impl;
 
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.jeecg.modules.zjrs.jobrecommend.dto.AvailablePostVo;
 import org.jeecg.modules.zjrs.jobrecommend.entity.JobRecommend;
 import org.jeecg.modules.zjrs.jobrecommend.entity.JobRecommendPageVo;
 import org.jeecg.modules.zjrs.jobrecommend.mapper.JobRecommendMapper;
@@ -59,6 +60,14 @@ public class JobRecommendServiceImpl extends ServiceImpl<JobRecommendMapper, Job
                         if (vo.getRecommendStatus() != null)
                             vo.setRecommendStatus(mapping.getOrDefault(vo.getRecommendStatus(), vo.getRecommendStatus()));
                         break;
+                    case "gender":
+                        if (vo.getGender() != null)
+                            vo.setGender(mapping.getOrDefault(vo.getGender(), vo.getGender()));
+                        break;
+                    case "education":
+                        if (vo.getEducation() != null)
+                            vo.setEducation(mapping.getOrDefault(vo.getEducation(), vo.getEducation()));
+                        break;
                 }
             }
         }
@@ -102,4 +111,11 @@ public class JobRecommendServiceImpl extends ServiceImpl<JobRecommendMapper, Job
         }
         return list;
     }
+
+    //update-begin---author:kk ---date:2026-06-12  for:【信息智能匹配推送】岗位推送-查询可选岗位列表-----------
+    @Override
+    public List<AvailablePostVo> queryAvailablePosts(Map<String, String> params) {
+        return baseMapper.queryAvailablePosts(params);
+    }
+    //update-end---author:kk ---date:2026-06-12  for:【信息智能匹配推送】岗位推送-查询可选岗位列表-----------
 }

+ 77 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/notification/controller/NotificationController.java

@@ -323,4 +323,81 @@ public class NotificationController {
         }
         return Result.OK(page);
     }
+
+    @Operation(summary = "查询当前用户收到的消息")
+    @GetMapping(value = "/receivedList")
+    public Result<Page<NotificationRecord>> receivedList(
+            @RequestParam(name = "keyword", required = false) String keyword,
+            @RequestParam(name = "moduleType", required = false) String moduleType,
+            @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
+            @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
+        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+        String username = sysUser.getUsername();
+
+        // 从username解析用户ID和类型
+        // 小程序登录格式:personal_{personalInfoId} 或 enterprise_{enterpriseInfoId}
+        // 管理端登录格式:普通username
+        String targetId = null;
+        String targetType = null;
+
+        if (username != null && username.startsWith("personal_")) {
+            targetId = username.substring("personal_".length());
+            targetType = "personal";
+        } else if (username != null && username.startsWith("enterprise_")) {
+            targetId = username.substring("enterprise_".length());
+            targetType = "enterprise";
+        } else {
+            // 管理端用户,使用sysUser.getId()
+            targetId = sysUser.getId();
+            targetType = "personal"; // 默认按个人查询
+        }
+
+        if (targetId == null || targetId.isEmpty()) {
+            Page<NotificationRecord> emptyPage = new Page<>(pageNo, pageSize);
+            emptyPage.setTotal(0);
+            emptyPage.setRecords(Collections.emptyList());
+            return Result.OK(emptyPage);
+        }
+
+        // 查询当前用户作为接收人的消息ID列表
+        QueryWrapper<NotificationTarget> tq = new QueryWrapper<>();
+        tq.eq("target_id", targetId);
+        if (targetType != null) {
+            tq.eq("target_type", targetType);
+        }
+        tq.select("notification_id");
+        List<NotificationTarget> targets = notificationTargetMapper.selectList(tq);
+
+        if (targets.isEmpty()) {
+            Page<NotificationRecord> emptyPage = new Page<>(pageNo, pageSize);
+            emptyPage.setTotal(0);
+            emptyPage.setRecords(Collections.emptyList());
+            return Result.OK(emptyPage);
+        }
+
+        Set<String> notificationIds = targets.stream().map(NotificationTarget::getNotificationId).collect(Collectors.toSet());
+
+        // 根据消息ID列表查询消息记录
+        QueryWrapper<NotificationRecord> query = new QueryWrapper<>();
+        query.in("id", notificationIds);
+        if (keyword != null && !keyword.isEmpty()) {
+            query.like("subject", keyword);
+        }
+        if (moduleType != null && !moduleType.isEmpty()) {
+            query.eq("module_type", moduleType);
+        }
+        query.orderByDesc("send_time");
+        Page<NotificationRecord> page = new Page<>(pageNo, pageSize);
+        page = notificationRecordService.page(page, query);
+
+        // 填充接收人信息
+        for (NotificationRecord record : page.getRecords()) {
+            QueryWrapper<NotificationTarget> ntq = new QueryWrapper<>();
+            ntq.eq("notification_id", record.getId());
+            List<NotificationTarget> recordTargets = notificationTargetMapper.selectList(ntq);
+            resolveTargetNames(recordTargets);
+            record.setTargets(recordTargets);
+        }
+        return Result.OK(page);
+    }
 }

+ 191 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/personal/controller/ResumeInfoController.java

@@ -263,4 +263,195 @@ public class ResumeInfoController extends JeecgController<ResumeInfo, IResumeInf
         List<ResumeStudyAbroad> list = resumeStudyAbroadMapper.selectByMainId(id);
         return Result.OK(list);
     }
+
+    // ==================== 子表CRUD接口(小程序端使用) ====================
+
+    /** 工作经历 - 新增 */
+    @AutoLog(value = "工作经历-新增")
+    @Operation(summary = "工作经历-新增")
+    @PostMapping(value = "/addWorkExp")
+    public Result<String> addWorkExp(@RequestBody ResumeWorkExp workExp) {
+        resumeWorkExpMapper.insert(workExp);
+        return Result.OK("添加成功!");
+    }
+
+    /** 工作经历 - 编辑 */
+    @AutoLog(value = "工作经历-编辑")
+    @Operation(summary = "工作经历-编辑")
+    @RequestMapping(value = "/editWorkExp", method = {RequestMethod.PUT, RequestMethod.POST})
+    public Result<String> editWorkExp(@RequestBody ResumeWorkExp workExp) {
+        resumeWorkExpMapper.updateById(workExp);
+        return Result.OK("编辑成功!");
+    }
+
+    /** 工作经历 - 删除 */
+    @AutoLog(value = "工作经历-删除")
+    @Operation(summary = "工作经历-删除")
+    @DeleteMapping(value = "/deleteWorkExp")
+    public Result<String> deleteWorkExp(@RequestParam(name = "id", required = true) String id) {
+        resumeWorkExpMapper.deleteById(id);
+        return Result.OK("删除成功!");
+    }
+
+    /** 教育经历 - 新增 */
+    @AutoLog(value = "教育经历-新增")
+    @Operation(summary = "教育经历-新增")
+    @PostMapping(value = "/addEducation")
+    public Result<String> addEducation(@RequestBody ResumeEducation education) {
+        resumeEducationMapper.insert(education);
+        return Result.OK("添加成功!");
+    }
+
+    /** 教育经历 - 编辑 */
+    @AutoLog(value = "教育经历-编辑")
+    @Operation(summary = "教育经历-编辑")
+    @RequestMapping(value = "/editEducation", method = {RequestMethod.PUT, RequestMethod.POST})
+    public Result<String> editEducation(@RequestBody ResumeEducation education) {
+        resumeEducationMapper.updateById(education);
+        return Result.OK("编辑成功!");
+    }
+
+    /** 教育经历 - 删除 */
+    @AutoLog(value = "教育经历-删除")
+    @Operation(summary = "教育经历-删除")
+    @DeleteMapping(value = "/deleteEducation")
+    public Result<String> deleteEducation(@RequestParam(name = "id", required = true) String id) {
+        resumeEducationMapper.deleteById(id);
+        return Result.OK("删除成功!");
+    }
+
+    /** 培训经历 - 新增 */
+    @AutoLog(value = "培训经历-新增")
+    @Operation(summary = "培训经历-新增")
+    @PostMapping(value = "/addTraining")
+    public Result<String> addTraining(@RequestBody ResumeTraining training) {
+        resumeTrainingMapper.insert(training);
+        return Result.OK("添加成功!");
+    }
+
+    /** 培训经历 - 编辑 */
+    @AutoLog(value = "培训经历-编辑")
+    @Operation(summary = "培训经历-编辑")
+    @RequestMapping(value = "/editTraining", method = {RequestMethod.PUT, RequestMethod.POST})
+    public Result<String> editTraining(@RequestBody ResumeTraining training) {
+        resumeTrainingMapper.updateById(training);
+        return Result.OK("编辑成功!");
+    }
+
+    /** 培训经历 - 删除 */
+    @AutoLog(value = "培训经历-删除")
+    @Operation(summary = "培训经历-删除")
+    @DeleteMapping(value = "/deleteTraining")
+    public Result<String> deleteTraining(@RequestParam(name = "id", required = true) String id) {
+        resumeTrainingMapper.deleteById(id);
+        return Result.OK("删除成功!");
+    }
+
+    /** 职业技能 - 新增 */
+    @AutoLog(value = "职业技能-新增")
+    @Operation(summary = "职业技能-新增")
+    @PostMapping(value = "/addSkill")
+    public Result<String> addSkill(@RequestBody ResumeSkill skill) {
+        resumeSkillMapper.insert(skill);
+        return Result.OK("添加成功!");
+    }
+
+    /** 职业技能 - 编辑 */
+    @AutoLog(value = "职业技能-编辑")
+    @Operation(summary = "职业技能-编辑")
+    @RequestMapping(value = "/editSkill", method = {RequestMethod.PUT, RequestMethod.POST})
+    public Result<String> editSkill(@RequestBody ResumeSkill skill) {
+        resumeSkillMapper.updateById(skill);
+        return Result.OK("编辑成功!");
+    }
+
+    /** 职业技能 - 删除 */
+    @AutoLog(value = "职业技能-删除")
+    @Operation(summary = "职业技能-删除")
+    @DeleteMapping(value = "/deleteSkill")
+    public Result<String> deleteSkill(@RequestParam(name = "id", required = true) String id) {
+        resumeSkillMapper.deleteById(id);
+        return Result.OK("删除成功!");
+    }
+
+    /** 职称情况 - 新增 */
+    @AutoLog(value = "职称情况-新增")
+    @Operation(summary = "职称情况-新增")
+    @PostMapping(value = "/addTitle")
+    public Result<String> addTitle(@RequestBody ResumeTitle title) {
+        resumeTitleMapper.insert(title);
+        return Result.OK("添加成功!");
+    }
+
+    /** 职称情况 - 编辑 */
+    @AutoLog(value = "职称情况-编辑")
+    @Operation(summary = "职称情况-编辑")
+    @RequestMapping(value = "/editTitle", method = {RequestMethod.PUT, RequestMethod.POST})
+    public Result<String> editTitle(@RequestBody ResumeTitle title) {
+        resumeTitleMapper.updateById(title);
+        return Result.OK("编辑成功!");
+    }
+
+    /** 职称情况 - 删除 */
+    @AutoLog(value = "职称情况-删除")
+    @Operation(summary = "职称情况-删除")
+    @DeleteMapping(value = "/deleteTitle")
+    public Result<String> deleteTitle(@RequestParam(name = "id", required = true) String id) {
+        resumeTitleMapper.deleteById(id);
+        return Result.OK("删除成功!");
+    }
+
+    /** 获奖情况/其他证书 - 新增 */
+    @AutoLog(value = "获奖情况-新增")
+    @Operation(summary = "获奖情况-新增")
+    @PostMapping(value = "/addAward")
+    public Result<String> addAward(@RequestBody ResumeAward award) {
+        resumeAwardMapper.insert(award);
+        return Result.OK("添加成功!");
+    }
+
+    /** 获奖情况/其他证书 - 编辑 */
+    @AutoLog(value = "获奖情况-编辑")
+    @Operation(summary = "获奖情况-编辑")
+    @RequestMapping(value = "/editAward", method = {RequestMethod.PUT, RequestMethod.POST})
+    public Result<String> editAward(@RequestBody ResumeAward award) {
+        resumeAwardMapper.updateById(award);
+        return Result.OK("编辑成功!");
+    }
+
+    /** 获奖情况/其他证书 - 删除 */
+    @AutoLog(value = "获奖情况-删除")
+    @Operation(summary = "获奖情况-删除")
+    @DeleteMapping(value = "/deleteAward")
+    public Result<String> deleteAward(@RequestParam(name = "id", required = true) String id) {
+        resumeAwardMapper.deleteById(id);
+        return Result.OK("删除成功!");
+    }
+
+    /** 留学经历 - 新增 */
+    @AutoLog(value = "留学经历-新增")
+    @Operation(summary = "留学经历-新增")
+    @PostMapping(value = "/addStudyAbroad")
+    public Result<String> addStudyAbroad(@RequestBody ResumeStudyAbroad studyAbroad) {
+        resumeStudyAbroadMapper.insert(studyAbroad);
+        return Result.OK("添加成功!");
+    }
+
+    /** 留学经历 - 编辑 */
+    @AutoLog(value = "留学经历-编辑")
+    @Operation(summary = "留学经历-编辑")
+    @RequestMapping(value = "/editStudyAbroad", method = {RequestMethod.PUT, RequestMethod.POST})
+    public Result<String> editStudyAbroad(@RequestBody ResumeStudyAbroad studyAbroad) {
+        resumeStudyAbroadMapper.updateById(studyAbroad);
+        return Result.OK("编辑成功!");
+    }
+
+    /** 留学经历 - 删除 */
+    @AutoLog(value = "留学经历-删除")
+    @Operation(summary = "留学经历-删除")
+    @DeleteMapping(value = "/deleteStudyAbroad")
+    public Result<String> deleteStudyAbroad(@RequestParam(name = "id", required = true) String id) {
+        resumeStudyAbroadMapper.deleteById(id);
+        return Result.OK("删除成功!");
+    }
 }

+ 1 - 1
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/personalstatus/controller/PersonalStatusController.java

@@ -163,7 +163,7 @@ public class PersonalStatusController {
         ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
         mv.addObject(NormalExcelConstants.FILE_NAME, "个人就业状态感知");
         mv.addObject(NormalExcelConstants.CLASS, PersonalStatusPageVo.class);
-        mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("个人就业状态感知数据", "导出人:" + sysUser.getRealname(), "个人就业状态感知"));
+        mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("个人就业状态感知数据", "导出人:" + sysUser.getRealname(), "个人就业状态感知", ExcelType.XSSF));
         mv.addObject(NormalExcelConstants.DATA_LIST, list);
 
         String exportFields = "idNumber,fullName,householdAreaName,gender,education,age,jobSearchStatus,employmentStatus,employRegCount,unemployRegCount,socialInsuranceStatus,customTags,lastNoticeTime";

+ 2 - 1
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/personalstatus/entity/PersonalStatusPageVo.java

@@ -31,6 +31,7 @@ public class PersonalStatusPageVo implements Serializable {
     @Schema(description = "户口所属区县")
     private java.lang.String householdDistrict;
 
+    @Excel(name = "户口所属区县名称", width = 15)
     @Schema(description = "户口所属区县名称")
     private java.lang.String householdAreaName;
 
@@ -70,7 +71,7 @@ public class PersonalStatusPageVo implements Serializable {
     @Schema(description = "自定义标签")
     private java.lang.String customTags;
 
-    @Excel(name = "最后通知时间", width = 18)
+    @Excel(name = "最后通知时间", width = 18, format = "yyyy-MM-dd HH:mm:ss")
     @Schema(description = "最后通知时间")
     private Date lastNoticeTime;
 

+ 6 - 6
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/personalstatus/service/impl/PersonalStatusServiceImpl.java

@@ -38,18 +38,18 @@ public class PersonalStatusServiceImpl extends ServiceImpl<PersonalStatusLocalMa
         Set<String> dictCodes = new HashSet<>(fieldDictMap.values());
 
         // Batch fetch all dict data
-        Map<String, List<Map<String, String>>> dictItemsMap = dictionaryItemService.getDictItemsBatch(new ArrayList<>(dictCodes));
+        Map<String, List<Map<String, Object>>> dictItemsMap = dictionaryItemService.getDictItemsBatch(new ArrayList<>(dictCodes));
 
         // Build value -> label mapping for each dict code
         Map<String, Map<String, String>> dictLabelMap = new HashMap<>();
-        for (Map.Entry<String, List<Map<String, String>>> entry : dictItemsMap.entrySet()) {
+        for (Map.Entry<String, List<Map<String, Object>>> entry : dictItemsMap.entrySet()) {
             String dictCode = entry.getKey();
-            List<Map<String, String>> items = entry.getValue();
+            List<Map<String, Object>> items = entry.getValue();
             Map<String, String> valueLabelMap = new HashMap<>();
             if (items != null) {
-                for (Map<String, String> item : items) {
-                    String value = item.get("value");
-                    String label = item.get("label");
+                for (Map<String, Object> item : items) {
+                    String value = item.get("value").toString();
+                    String label = item.get("label").toString();
                     if (value != null && label != null) {
                         valueLabelMap.put(value, label);
                     }

+ 2 - 2
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/policyfile/controller/PolicyCategoryController.java

@@ -38,10 +38,10 @@ public class PolicyCategoryController {
         return Result.OK(pageList);
     }
 
-    @Operation(summary = "树形列表(全部)")
+    @Operation(summary = "树形列表(全部,按排序号排序)")
     @GetMapping(value = "/treeList")
     public Result<List<PolicyCategory>> queryTreeList() {
-        List<PolicyCategory> list = policyCategoryService.list();
+        List<PolicyCategory> list = policyCategoryService.queryTreeList();
         return Result.OK(list);
     }
 

+ 10 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/policyfile/controller/PolicyFileController.java

@@ -82,6 +82,16 @@ public class PolicyFileController {
     }
     //update-end---author:kk ---date:2026-06-11  for:【政策公开文件】递归获取所有子目录ID-----------
 
+    @Operation(summary = "根据ID查询政策文件")
+    @GetMapping(value = "/queryById")
+    public Result<PolicyFile> queryById(@RequestParam(name = "id") String id) {
+        PolicyFile policyFile = policyFileService.getById(id);
+        if (policyFile == null) {
+            return Result.error("未找到对应数据");
+        }
+        return Result.OK(policyFile);
+    }
+
     @Operation(summary = "添加")
     @PostMapping(value = "/add")
     public Result<String> add(@RequestBody PolicyFile policyFile) {

+ 4 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/policyfile/mapper/PolicyCategoryMapper.java

@@ -3,5 +3,9 @@ package org.jeecg.modules.zjrs.policyfile.mapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import org.jeecg.modules.zjrs.policyfile.entity.PolicyCategory;
 
+import java.util.List;
+
 public interface PolicyCategoryMapper extends BaseMapper<PolicyCategory> {
+
+    List<PolicyCategory> queryTreeList();
 }

+ 9 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/policyfile/mapper/xml/PolicyCategoryMapper.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.jeecg.modules.zjrs.policyfile.mapper.PolicyCategoryMapper">
+
+    <select id="queryTreeList" resultType="org.jeecg.modules.zjrs.policyfile.entity.PolicyCategory">
+        SELECT * FROM policy_category ORDER BY sort_no ASC
+    </select>
+
+</mapper>

+ 4 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/policyfile/service/IPolicyCategoryService.java

@@ -3,5 +3,9 @@ package org.jeecg.modules.zjrs.policyfile.service;
 import org.jeecg.modules.zjrs.policyfile.entity.PolicyCategory;
 import com.baomidou.mybatisplus.extension.service.IService;
 
+import java.util.List;
+
 public interface IPolicyCategoryService extends IService<PolicyCategory> {
+
+    List<PolicyCategory> queryTreeList();
 }

+ 11 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/policyfile/service/impl/PolicyCategoryServiceImpl.java

@@ -4,8 +4,19 @@ import org.jeecg.modules.zjrs.policyfile.entity.PolicyCategory;
 import org.jeecg.modules.zjrs.policyfile.mapper.PolicyCategoryMapper;
 import org.jeecg.modules.zjrs.policyfile.service.IPolicyCategoryService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.util.List;
+
 @Service
 public class PolicyCategoryServiceImpl extends ServiceImpl<PolicyCategoryMapper, PolicyCategory> implements IPolicyCategoryService {
+
+    @Autowired
+    private PolicyCategoryMapper policyCategoryMapper;
+
+    @Override
+    public List<PolicyCategory> queryTreeList() {
+        return policyCategoryMapper.queryTreeList();
+    }
 }

+ 65 - 1
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/preliminaryeligible/controller/PreliminaryEligibleController.java

@@ -7,21 +7,30 @@ import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.shiro.SecurityUtils;
 import org.apache.shiro.authz.annotation.RequiresPermissions;
 import org.jeecg.common.api.vo.Result;
 import org.jeecg.common.aspect.annotation.AutoLog;
 import org.jeecg.common.system.base.controller.JeecgController;
+import org.jeecg.common.system.vo.LoginUser;
+import org.jeecg.common.util.oConvertUtils;
 import org.jeecg.modules.zjrs.preliminaryeligible.entity.PreliminaryEligible;
 import org.jeecg.modules.zjrs.preliminaryeligible.entity.PreliminaryEligibleDetailVo;
 import org.jeecg.modules.zjrs.preliminaryeligible.entity.PreliminaryEligiblePageVo;
 import org.jeecg.modules.zjrs.preliminaryeligible.service.IPreliminaryEligibleService;
+import org.jeecgframework.poi.excel.def.NormalExcelConstants;
+import org.jeecgframework.poi.excel.entity.ExportParams;
+import org.jeecgframework.poi.excel.entity.enmus.ExcelType;
+import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.servlet.ModelAndView;
 
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * @Description: 就业一湛通-初步符合条件人员
@@ -37,6 +46,14 @@ public class PreliminaryEligibleController extends JeecgController<PreliminaryEl
     @Autowired
     private IPreliminaryEligibleService preliminaryEligibleService;
 
+    private static final Map<String, String> EXPORT_DICT_FIELD_MAP = new HashMap<>();
+    static {
+        EXPORT_DICT_FIELD_MAP.put("gender", "Gender");
+        EXPORT_DICT_FIELD_MAP.put("education", "Education");
+        EXPORT_DICT_FIELD_MAP.put("jobSeekerCategory", "JobSeekerCategory");
+        EXPORT_DICT_FIELD_MAP.put("jobSearchStatus", "JobSeekerStatus");
+    }
+
     /**
      * 分页列表查询(关联个人信息表,返回个人信息字段)
      */
@@ -139,7 +156,54 @@ public class PreliminaryEligibleController extends JeecgController<PreliminaryEl
     @RequiresPermissions("preliminary_eligible:exportXls")
     @RequestMapping(value = "/exportXls")
     public ModelAndView exportXls(HttpServletRequest request, PreliminaryEligible preliminaryEligible) {
-        return super.exportXls(request, preliminaryEligible, PreliminaryEligible.class, "就业一湛通-初步符合条件人员");
+        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+
+        Map<String, String> params = new HashMap<>();
+        String fullName = request.getParameter("fullName");
+        String gender = request.getParameter("gender");
+        String education = request.getParameter("education");
+        String householdLocation = request.getParameter("householdLocation");
+        String currentResidence = request.getParameter("currentResidence");
+        String ageBegin = request.getParameter("ageBegin");
+        String ageEnd = request.getParameter("ageEnd");
+        String jobSeekerCategory = request.getParameter("jobSeekerCategory");
+        String jobSearchStatus = request.getParameter("jobSearchStatus");
+        String appliedAid = request.getParameter("appliedAid");
+        String dataSource = request.getParameter("dataSource");
+
+        if (fullName != null && !fullName.isEmpty()) params.put("fullName", fullName);
+        if (gender != null && !gender.isEmpty()) params.put("gender", gender);
+        if (education != null && !education.isEmpty()) params.put("education", education);
+        if (householdLocation != null && !householdLocation.isEmpty()) params.put("householdLocation", householdLocation);
+        if (currentResidence != null && !currentResidence.isEmpty()) params.put("currentResidence", currentResidence);
+        if (ageBegin != null && !ageBegin.isEmpty()) params.put("ageBegin", ageBegin);
+        if (ageEnd != null && !ageEnd.isEmpty()) params.put("ageEnd", ageEnd);
+        if (jobSeekerCategory != null && !jobSeekerCategory.isEmpty()) params.put("jobSeekerCategory", jobSeekerCategory);
+        if (jobSearchStatus != null && !jobSearchStatus.isEmpty()) params.put("jobSearchStatus", jobSearchStatus);
+        if (appliedAid != null && !appliedAid.isEmpty()) params.put("appliedAid", appliedAid);
+        if (dataSource != null && !dataSource.isEmpty()) params.put("dataSource", dataSource);
+
+        Page<PreliminaryEligiblePageVo> page = new Page<>(1, 999999);
+        Page<PreliminaryEligiblePageVo> pageList = preliminaryEligibleService.queryPageList(page, params);
+        List<PreliminaryEligiblePageVo> dataList = pageList.getRecords();
+
+        String selections = request.getParameter("selections");
+        if (oConvertUtils.isNotEmpty(selections)) {
+            List<String> selectionList = Arrays.asList(selections.split(","));
+            dataList = dataList.stream().filter(item -> selectionList.contains(item.getId())).collect(Collectors.toList());
+        }
+
+        preliminaryEligibleService.translateDictFieldsForPageVo(dataList, EXPORT_DICT_FIELD_MAP);
+
+        ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
+        mv.addObject(NormalExcelConstants.FILE_NAME, "就业一湛通-初步符合条件人员");
+        mv.addObject(NormalExcelConstants.CLASS, PreliminaryEligiblePageVo.class);
+        mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("就业一湛通-初步符合条件人员", "导出人:" + sysUser.getRealname(), "就业一湛通-初步符合条件人员", ExcelType.XSSF));
+        mv.addObject(NormalExcelConstants.DATA_LIST, dataList);
+
+        String exportFields = "fullName,gender,age,education,householdLocation,currentResidence,jobSeekerCategory,contactPhone,jobSearchStatus,appliedAid,dataSource,lastNoticeTime";
+        mv.addObject(NormalExcelConstants.EXPORT_FIELDS, exportFields);
+        return mv;
     }
 
     @RequiresPermissions("preliminary_eligible:importExcel")

+ 14 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/preliminaryeligible/entity/PreliminaryEligiblePageVo.java

@@ -8,6 +8,8 @@ import lombok.experimental.Accessors;
 import java.io.Serializable;
 import java.util.Date;
 
+import org.jeecgframework.poi.excel.annotation.Excel;
+
 /**
  * @Description: 就业一湛通-初步符合条件人员(列表页面VO,包含个人信息字段)
  * @Author: jeecg-boot
@@ -29,50 +31,62 @@ public class PreliminaryEligiblePageVo implements Serializable {
     private java.lang.String personalId;
 
     /**是否已申请援助*/
+    @Excel(name = "是否已申请援助", width = 15)
     @Schema(description = "是否已申请援助")
     private java.lang.String appliedAid;
 
     /**最后通知时间*/
+    @Excel(name = "最后通知时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
     @Schema(description = "最后通知时间")
     private Date lastNoticeTime;
 
     /**数据来源*/
+    @Excel(name = "数据来源", width = 12)
     @Schema(description = "数据来源")
     private java.lang.String dataSource;
 
     /**姓名(来自个人信息表)*/
+    @Excel(name = "姓名", width = 15)
     @Schema(description = "姓名")
     private java.lang.String fullName;
 
     /**性别(来自个人信息表)*/
+    @Excel(name = "性别", width = 8)
     @Schema(description = "性别")
     private java.lang.String gender;
 
     /**年龄(由出生日期计算)*/
+    @Excel(name = "年龄", width = 8)
     @Schema(description = "年龄")
     private java.lang.Integer age;
 
     /**学历(来自个人信息表)*/
+    @Excel(name = "学历", width = 12)
     @Schema(description = "学历")
     private java.lang.String education;
 
     /**户口所在地(来自个人信息表)*/
+    @Excel(name = "户口所在地", width = 20)
     @Schema(description = "户口所在地")
     private java.lang.String householdLocation;
 
     /**现居住地(来自个人信息表)*/
+    @Excel(name = "现居住地", width = 20)
     @Schema(description = "现居住地")
     private java.lang.String currentResidence;
 
     /**求职人员类别(来自个人信息表)*/
+    @Excel(name = "求职人员类别", width = 15)
     @Schema(description = "求职人员类别")
     private java.lang.String jobSeekerCategory;
 
     /**联系电话(来自个人信息表)*/
+    @Excel(name = "联系电话", width = 15)
     @Schema(description = "联系电话")
     private java.lang.String contactPhone;
 
     /**求职状态(来自个人信息表)*/
+    @Excel(name = "求职状态", width = 12)
     @Schema(description = "求职状态")
     private java.lang.String jobSearchStatus;
 }

+ 3 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/preliminaryeligible/service/IPreliminaryEligibleService.java

@@ -7,6 +7,7 @@ import org.jeecg.modules.zjrs.preliminaryeligible.entity.PreliminaryEligible;
 import org.jeecg.modules.zjrs.preliminaryeligible.entity.PreliminaryEligibleDetailVo;
 import org.jeecg.modules.zjrs.preliminaryeligible.entity.PreliminaryEligiblePageVo;
 
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -28,4 +29,6 @@ public interface IPreliminaryEligibleService extends IService<PreliminaryEligibl
      * @return 包含个人信息全部字段的详情VO
      */
     PreliminaryEligibleDetailVo queryDetailById(String id);
+
+    void translateDictFieldsForPageVo(List<PreliminaryEligiblePageVo> list, Map<String, String> fieldDictMap);
 }

+ 77 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/preliminaryeligible/service/impl/PreliminaryEligibleServiceImpl.java

@@ -7,8 +7,12 @@ import org.jeecg.modules.zjrs.preliminaryeligible.entity.PreliminaryEligibleDeta
 import org.jeecg.modules.zjrs.preliminaryeligible.entity.PreliminaryEligiblePageVo;
 import org.jeecg.modules.zjrs.preliminaryeligible.mapper.PreliminaryEligibleMapper;
 import org.jeecg.modules.zjrs.preliminaryeligible.service.IPreliminaryEligibleService;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -20,6 +24,9 @@ import java.util.Map;
 @Service
 public class PreliminaryEligibleServiceImpl extends ServiceImpl<PreliminaryEligibleMapper, PreliminaryEligible> implements IPreliminaryEligibleService {
 
+    @Autowired
+    private org.jeecg.modules.zjrs.dictionary.service.IDictionaryItemService dictionaryItemService;
+
     @Override
     public Page<PreliminaryEligiblePageVo> queryPageList(Page<PreliminaryEligiblePageVo> page, Map<String, String> params) {
         return baseMapper.queryPageList(page, params);
@@ -29,4 +36,74 @@ public class PreliminaryEligibleServiceImpl extends ServiceImpl<PreliminaryEligi
     public PreliminaryEligibleDetailVo queryDetailById(String id) {
         return baseMapper.queryDetailById(id);
     }
+
+    @Override
+    public void translateDictFieldsForPageVo(List<PreliminaryEligiblePageVo> list, Map<String, String> fieldDictMap) {
+        List<String> dictCodes = new ArrayList<>(fieldDictMap.values());
+        Map<String, List<Map<String, Object>>> dictData = dictionaryItemService.getDictItemsBatch(dictCodes);
+
+        Map<String, Map<String, String>> valueLabelMap = new HashMap<>();
+        for (Map.Entry<String, List<Map<String, Object>>> entry : dictData.entrySet()) {
+            Map<String, String> valueToLabel = new HashMap<>();
+            for (Map<String, Object> item : entry.getValue()) {
+                valueToLabel.put(String.valueOf(item.get("value")), String.valueOf(item.get("label")));
+            }
+            valueLabelMap.put(entry.getKey(), valueToLabel);
+        }
+
+        for (PreliminaryEligiblePageVo vo : list) {
+            for (Map.Entry<String, String> fieldEntry : fieldDictMap.entrySet()) {
+                String fieldName = fieldEntry.getKey();
+                String dictCode = fieldEntry.getValue();
+                Map<String, String> mapping = valueLabelMap.get(dictCode);
+                if (mapping == null) continue;
+
+                switch (fieldName) {
+                    case "gender":
+                        if (vo.getGender() != null) vo.setGender(mapping.getOrDefault(vo.getGender(), vo.getGender()));
+                        break;
+                    case "education":
+                        if (vo.getEducation() != null) vo.setEducation(mapping.getOrDefault(vo.getEducation(), vo.getEducation()));
+                        break;
+                    case "jobSeekerCategory":
+                        if (vo.getJobSeekerCategory() != null) vo.setJobSeekerCategory(mapping.getOrDefault(vo.getJobSeekerCategory(), vo.getJobSeekerCategory()));
+                        break;
+                    case "jobSearchStatus":
+                        if (vo.getJobSearchStatus() != null) vo.setJobSearchStatus(mapping.getOrDefault(vo.getJobSearchStatus(), vo.getJobSearchStatus()));
+                        break;
+                }
+            }
+        }
+
+        // XZQH code -> name translation for address fields
+        java.util.Set<String> xzqhCodes = new java.util.LinkedHashSet<>();
+        for (PreliminaryEligiblePageVo vo : list) {
+            if (vo.getHouseholdLocation() != null && !vo.getHouseholdLocation().isEmpty())
+                xzqhCodes.add(vo.getHouseholdLocation());
+            if (vo.getCurrentResidence() != null && !vo.getCurrentResidence().isEmpty())
+                xzqhCodes.add(vo.getCurrentResidence());
+        }
+        if (!xzqhCodes.isEmpty()) {
+            java.util.List<String> codeList = new java.util.ArrayList<>(xzqhCodes);
+            java.util.List<String> names = dictionaryItemService.loadDictItem("XZQH", String.join(",", codeList));
+            java.util.Map<String, String> xzqhMap = new java.util.HashMap<>();
+            for (int i = 0; i < codeList.size(); i++) {
+                xzqhMap.put(codeList.get(i), names.get(i));
+            }
+            for (PreliminaryEligiblePageVo vo : list) {
+                if (vo.getHouseholdLocation() != null && !vo.getHouseholdLocation().isEmpty()) {
+                    String name = xzqhMap.get(vo.getHouseholdLocation());
+                    if (name == null && vo.getHouseholdLocation().length() >= 6)
+                        name = xzqhMap.get(vo.getHouseholdLocation().substring(0, 6));
+                    if (name != null) vo.setHouseholdLocation(name);
+                }
+                if (vo.getCurrentResidence() != null && !vo.getCurrentResidence().isEmpty()) {
+                    String name = xzqhMap.get(vo.getCurrentResidence());
+                    if (name == null && vo.getCurrentResidence().length() >= 6)
+                        name = xzqhMap.get(vo.getCurrentResidence().substring(0, 6));
+                    if (name != null) vo.setCurrentResidence(name);
+                }
+            }
+        }
+    }
 }

+ 2 - 1
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/profilechange/controller/ProfileChangeController.java

@@ -16,6 +16,7 @@ import org.jeecg.modules.zjrs.profilechange.entity.ProfileChangePageVo;
 import org.jeecg.modules.zjrs.profilechange.service.IProfileChangeService;
 import org.jeecgframework.poi.excel.def.NormalExcelConstants;
 import org.jeecgframework.poi.excel.entity.ExportParams;
+import org.jeecgframework.poi.excel.entity.enmus.ExcelType;
 import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
@@ -149,7 +150,7 @@ public class ProfileChangeController {
         ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
         mv.addObject(NormalExcelConstants.FILE_NAME, "画像变动管理");
         mv.addObject(NormalExcelConstants.CLASS, ProfileChangePageVo.class);
-        mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("画像变动管理数据", "导出人:" + sysUser.getRealname(), "画像变动管理"));
+        mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("画像变动管理数据", "导出人:" + sysUser.getRealname(), "画像变动管理", ExcelType.XSSF));
         mv.addObject(NormalExcelConstants.DATA_LIST, list);
 
         String exportFields = "idNumber,fullName,householdAreaName,gender,education,age,profileStatus,statusDesc,dataDate,customTags,lastNoticeTime";

+ 3 - 2
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/profilechange/entity/ProfileChangePageVo.java

@@ -31,6 +31,7 @@ public class ProfileChangePageVo implements Serializable {
     @Schema(description = "户口所属区县")
     private java.lang.String householdDistrict;
 
+    @Excel(name = "户口所属区县名称", width = 15)
     @Schema(description = "户口所属区县名称")
     private java.lang.String householdAreaName;
 
@@ -54,7 +55,7 @@ public class ProfileChangePageVo implements Serializable {
     @Schema(description = "情况说明")
     private java.lang.String statusDesc;
 
-    @Excel(name = "数据日期", width = 18)
+    @Excel(name = "数据日期", width = 18, format = "yyyy-MM-dd")
     @Schema(description = "数据日期")
     private Date dataDate;
 
@@ -62,7 +63,7 @@ public class ProfileChangePageVo implements Serializable {
     @Schema(description = "自定义标签")
     private java.lang.String customTags;
 
-    @Excel(name = "最后通知时间", width = 18)
+    @Excel(name = "最后通知时间", width = 18, format = "yyyy-MM-dd HH:mm:ss")
     @Schema(description = "最后通知时间")
     private Date lastNoticeTime;
 }

+ 308 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/recruitmentfair/controller/RecruitmentFairController.java

@@ -0,0 +1,308 @@
+//update-begin---author:kk ---date:2026-06-12  for:【招聘会管理】新增招聘会Controller-----------
+package org.jeecg.modules.zjrs.recruitmentfair.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.jeecg.common.api.vo.Result;
+import org.jeecg.common.aspect.annotation.AutoLog;
+import org.jeecg.common.system.base.controller.JeecgController;
+import org.jeecg.common.system.query.QueryGenerator;
+import org.jeecg.modules.zjrs.recruitmentfair.entity.RecruitmentFair;
+import org.jeecg.modules.zjrs.recruitmentfair.entity.RecruitmentFairEnterprise;
+import org.jeecg.modules.zjrs.recruitmentfair.entity.RecruitmentFairPersonal;
+import org.jeecg.modules.zjrs.recruitmentfair.mapper.RecruitmentFairEnterpriseMapper;
+import org.jeecg.modules.zjrs.recruitmentfair.mapper.RecruitmentFairPersonalMapper;
+import org.jeecg.modules.zjrs.recruitmentfair.service.IRecruitmentFairService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.servlet.ModelAndView;
+
+import java.util.Arrays;
+
+/**
+ * @Description: 招聘会管理
+ * @Author: jeecg-boot
+ * @Date: 2026-06-12
+ * @Version: V1.0
+ */
+@Tag(name = "招聘会管理")
+@RestController
+@RequestMapping("/recruitmentFair")
+@Slf4j
+public class RecruitmentFairController extends JeecgController<RecruitmentFair, IRecruitmentFairService> {
+    @Autowired
+    private IRecruitmentFairService recruitmentFairService;
+    @Autowired
+    private RecruitmentFairEnterpriseMapper fairEnterpriseMapper;
+    @Autowired
+    private RecruitmentFairPersonalMapper fairPersonalMapper;
+
+    /**
+     * 分页列表查询
+     */
+    @Operation(summary = "招聘会管理-分页列表查询")
+    @GetMapping(value = "/list")
+    public Result<IPage<RecruitmentFair>> queryPageList(RecruitmentFair recruitmentFair,
+                                                        @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
+                                                        @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
+                                                        HttpServletRequest req) {
+        QueryWrapper<RecruitmentFair> queryWrapper = QueryGenerator.initQueryWrapper(recruitmentFair, req.getParameterMap());
+        Page<RecruitmentFair> page = new Page<>(pageNo, pageSize);
+        IPage<RecruitmentFair> pageList = recruitmentFairService.page(page, queryWrapper);
+        return Result.OK(pageList);
+    }
+
+    @AutoLog(value = "招聘会管理-添加")
+    @Operation(summary = "招聘会管理-添加")
+    @RequiresPermissions("recruitment_fair:add")
+    @PostMapping(value = "/add")
+    public Result<String> add(@RequestBody RecruitmentFair recruitmentFair) {
+        recruitmentFairService.save(recruitmentFair);
+        return Result.OK("添加成功!");
+    }
+
+    @AutoLog(value = "招聘会管理-编辑")
+    @Operation(summary = "招聘会管理-编辑")
+    @RequiresPermissions("recruitment_fair:edit")
+    @RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
+    public Result<String> edit(@RequestBody RecruitmentFair recruitmentFair) {
+        recruitmentFairService.updateById(recruitmentFair);
+        return Result.OK("编辑成功!");
+    }
+
+    @AutoLog(value = "招聘会管理-通过id删除")
+    @Operation(summary = "招聘会管理-通过id删除")
+    @RequiresPermissions("recruitment_fair:delete")
+    @DeleteMapping(value = "/delete")
+    public Result<String> delete(@RequestParam(name = "id", required = true) String id) {
+        recruitmentFairService.removeById(id);
+        return Result.OK("删除成功!");
+    }
+
+    @AutoLog(value = "招聘会管理-批量删除")
+    @Operation(summary = "招聘会管理-批量删除")
+    @RequiresPermissions("recruitment_fair:deleteBatch")
+    @DeleteMapping(value = "/deleteBatch")
+    public Result<String> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
+        this.recruitmentFairService.removeByIds(Arrays.asList(ids.split(",")));
+        return Result.OK("批量删除成功!");
+    }
+
+    @Operation(summary = "招聘会管理-通过id查询")
+    @GetMapping(value = "/queryById")
+    public Result<RecruitmentFair> queryById(@RequestParam(name = "id", required = true) String id) {
+        RecruitmentFair recruitmentFair = recruitmentFairService.getById(id);
+        if (recruitmentFair == null) {
+            return Result.error("未找到对应数据");
+        }
+        return Result.OK(recruitmentFair);
+    }
+
+    @RequiresPermissions("recruitment_fair:exportXls")
+    @RequestMapping(value = "/exportXls")
+    public ModelAndView exportXls(HttpServletRequest request, RecruitmentFair recruitmentFair) {
+        return super.exportXls(request, recruitmentFair, RecruitmentFair.class, "招聘会管理");
+    }
+
+    @RequiresPermissions("recruitment_fair:importExcel")
+    @RequestMapping(value = "/importExcel", method = RequestMethod.POST)
+    public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
+        return super.importExcel(request, response, RecruitmentFair.class);
+    }
+
+    /**
+     * 小程序-招聘会公开列表(无需管理端权限,按状态筛选)
+     */
+    @Operation(summary = "小程序-招聘会公开列表")
+    @GetMapping(value = "/publicList")
+    public Result<IPage<RecruitmentFair>> publicList(
+            @RequestParam(name = "status", required = false) String status,
+            @RequestParam(name = "fairName", required = false) String fairName,
+            @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
+            @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
+        Page<RecruitmentFair> page = new Page<>(pageNo, pageSize);
+        QueryWrapper<RecruitmentFair> queryWrapper = new QueryWrapper<>();
+        // 只查询已发布的招聘会(状态不为空)
+        queryWrapper.isNotNull("status");
+        queryWrapper.ne("status", "");
+        if (status != null && !status.isEmpty()) {
+            queryWrapper.eq("status", status);
+        }
+        if (fairName != null && !fairName.isEmpty()) {
+            queryWrapper.like("fair_name", fairName);
+        }
+        queryWrapper.orderByDesc("create_time");
+        IPage<RecruitmentFair> pageList = recruitmentFairService.page(page, queryWrapper);
+        return Result.OK(pageList);
+    }
+
+    /**
+     * 小程序-招聘会详情(含参展企业列表和报名人数)
+     */
+    @Operation(summary = "小程序-招聘会详情")
+    @GetMapping(value = "/detail")
+    public Result<?> detail(@RequestParam(name = "id", required = true) String id) {
+        RecruitmentFair fair = recruitmentFairService.getById(id);
+        if (fair == null) {
+            return Result.error("未找到对应数据");
+        }
+        // 查询已通过审核的参展企业
+        QueryWrapper<RecruitmentFairEnterprise> entWrapper = new QueryWrapper<>();
+        entWrapper.eq("fair_id", id);
+        entWrapper.eq("status", "1");
+        entWrapper.orderByDesc("create_time");
+        java.util.List<RecruitmentFairEnterprise> enterpriseList = fairEnterpriseMapper.selectList(entWrapper);
+
+        // 查询报名人数(非取消状态)
+        QueryWrapper<RecruitmentFairPersonal> perWrapper = new QueryWrapper<>();
+        perWrapper.eq("fair_id", id);
+        perWrapper.ne("status", "2");
+        Long participantCount = fairPersonalMapper.selectCount(perWrapper);
+
+        // 组装结果
+        java.util.Map<String, Object> result = new java.util.HashMap<>();
+        result.put("fair", fair);
+        result.put("enterpriseList", enterpriseList);
+        result.put("participantCount", participantCount);
+        return Result.OK(result);
+    }
+
+    /**
+     * 小程序-企业报名参展
+     */
+    @AutoLog(value = "招聘会-企业报名参展")
+    @Operation(summary = "小程序-企业报名参展")
+    @PostMapping(value = "/applyEnterprise")
+    public Result<String> applyEnterprise(@RequestBody RecruitmentFairEnterprise fairEnterprise) {
+        // 检查是否已报名
+        QueryWrapper<RecruitmentFairEnterprise> wrapper = new QueryWrapper<>();
+        wrapper.eq("fair_id", fairEnterprise.getFairId());
+        wrapper.eq("enterprise_id", fairEnterprise.getEnterpriseId());
+        Long count = fairEnterpriseMapper.selectCount(wrapper);
+        if (count > 0) {
+            return Result.error("该企业已报名此招聘会");
+        }
+        fairEnterprise.setStatus("0"); // 待审核
+        fairEnterpriseMapper.insert(fairEnterprise);
+        return Result.OK("报名成功,等待审核!");
+    }
+
+    /**
+     * 小程序-个人报名参加
+     */
+    @AutoLog(value = "招聘会-个人报名参加")
+    @Operation(summary = "小程序-个人报名参加")
+    @PostMapping(value = "/applyPersonal")
+    public Result<String> applyPersonal(@RequestBody RecruitmentFairPersonal fairPersonal) {
+        // 检查是否已报名
+        QueryWrapper<RecruitmentFairPersonal> wrapper = new QueryWrapper<>();
+        wrapper.eq("fair_id", fairPersonal.getFairId());
+        wrapper.eq("personal_id", fairPersonal.getPersonalId());
+        wrapper.ne("status", "2"); // 排除已取消的
+        Long count = fairPersonalMapper.selectCount(wrapper);
+        if (count > 0) {
+            return Result.error("您已报名此招聘会");
+        }
+        fairPersonal.setStatus("0"); // 已报名
+        fairPersonalMapper.insert(fairPersonal);
+        return Result.OK("报名成功!");
+    }
+
+    /**
+     * 小程序-企业取消参展
+     */
+    @AutoLog(value = "招聘会-企业取消参展")
+    @Operation(summary = "小程序-企业取消参展")
+    @DeleteMapping(value = "/cancelEnterprise")
+    public Result<String> cancelEnterprise(
+            @RequestParam(name = "fairId", required = true) String fairId,
+            @RequestParam(name = "enterpriseId", required = true) String enterpriseId) {
+        QueryWrapper<RecruitmentFairEnterprise> wrapper = new QueryWrapper<>();
+        wrapper.eq("fair_id", fairId);
+        wrapper.eq("enterprise_id", enterpriseId);
+        fairEnterpriseMapper.delete(wrapper);
+        return Result.OK("取消参展成功!");
+    }
+
+    /**
+     * 小程序-个人取消报名
+     */
+    @AutoLog(value = "招聘会-个人取消报名")
+    @Operation(summary = "小程序-个人取消报名")
+    @DeleteMapping(value = "/cancelPersonal")
+    public Result<String> cancelPersonal(
+            @RequestParam(name = "fairId", required = true) String fairId,
+            @RequestParam(name = "personalId", required = true) String personalId) {
+        QueryWrapper<RecruitmentFairPersonal> wrapper = new QueryWrapper<>();
+        wrapper.eq("fair_id", fairId);
+        wrapper.eq("personal_id", personalId);
+        RecruitmentFairPersonal record = fairPersonalMapper.selectOne(wrapper);
+        if (record != null) {
+            record.setStatus("2"); // 已取消
+            fairPersonalMapper.updateById(record);
+        }
+        return Result.OK("取消报名成功!");
+    }
+
+    /**
+     * 小程序-查询我的招聘会(企业端)
+     */
+    @Operation(summary = "小程序-查询我的招聘会(企业端)")
+    @GetMapping(value = "/myFairs")
+    public Result<IPage<RecruitmentFair>> myFairs(
+            @RequestParam(name = "enterpriseId", required = true) String enterpriseId,
+            @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
+            @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
+        // 先查询该企业参加的招聘会ID列表
+        QueryWrapper<RecruitmentFairEnterprise> entWrapper = new QueryWrapper<>();
+        entWrapper.eq("enterprise_id", enterpriseId);
+        entWrapper.ne("status", "2"); // 排除已拒绝的
+        java.util.List<RecruitmentFairEnterprise> entList = fairEnterpriseMapper.selectList(entWrapper);
+        if (entList.isEmpty()) {
+            return Result.OK(new Page<>(pageNo, pageSize));
+        }
+        java.util.List<String> fairIds = entList.stream().map(RecruitmentFairEnterprise::getFairId).collect(java.util.stream.Collectors.toList());
+        // 查询招聘会列表
+        Page<RecruitmentFair> page = new Page<>(pageNo, pageSize);
+        QueryWrapper<RecruitmentFair> queryWrapper = new QueryWrapper<>();
+        queryWrapper.in("id", fairIds);
+        queryWrapper.orderByDesc("create_time");
+        IPage<RecruitmentFair> pageList = recruitmentFairService.page(page, queryWrapper);
+        return Result.OK(pageList);
+    }
+
+    /**
+     * 小程序-查询我报名的招聘会(个人端)
+     */
+    @Operation(summary = "小程序-查询我报名的招聘会(个人端)")
+    @GetMapping(value = "/mySignedFairs")
+    public Result<IPage<RecruitmentFair>> mySignedFairs(
+            @RequestParam(name = "personalId", required = true) String personalId,
+            @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
+            @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
+        // 先查询该个人报名的招聘会ID列表
+        QueryWrapper<RecruitmentFairPersonal> perWrapper = new QueryWrapper<>();
+        perWrapper.eq("personal_id", personalId);
+        perWrapper.ne("status", "2"); // 排除已取消的
+        java.util.List<RecruitmentFairPersonal> perList = fairPersonalMapper.selectList(perWrapper);
+        if (perList.isEmpty()) {
+            return Result.OK(new Page<>(pageNo, pageSize));
+        }
+        java.util.List<String> fairIds = perList.stream().map(RecruitmentFairPersonal::getFairId).collect(java.util.stream.Collectors.toList());
+        // 查询招聘会列表
+        Page<RecruitmentFair> page = new Page<>(pageNo, pageSize);
+        QueryWrapper<RecruitmentFair> queryWrapper = new QueryWrapper<>();
+        queryWrapper.in("id", fairIds);
+        queryWrapper.orderByDesc("create_time");
+        IPage<RecruitmentFair> pageList = recruitmentFairService.page(page, queryWrapper);
+        return Result.OK(pageList);
+    }
+}
+//update-end---author:kk ---date:2026-06-12  for:【招聘会管理】新增招聘会Controller-----------

+ 124 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/recruitmentfair/entity/RecruitmentFair.java

@@ -0,0 +1,124 @@
+//update-begin---author:kk ---date:2026-06-12  for:【招聘会管理】新增招聘会实体类-----------
+package org.jeecg.modules.zjrs.recruitmentfair.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import org.jeecg.common.aspect.annotation.Dict;
+import org.jeecg.common.system.base.entity.JeecgEntity;
+import org.jeecgframework.poi.excel.annotation.Excel;
+
+/**
+ * @Description: 招聘会管理
+ * @Author: jeecg-boot
+ * @Date: 2026-06-12
+ * @Version: V1.0
+ */
+@Data
+@TableName("recruitment_fair")
+@Accessors(chain = true)
+@EqualsAndHashCode(callSuper = true)
+@Schema(description = "招聘会管理")
+public class RecruitmentFair extends JeecgEntity {
+
+    /**招聘会名称*/
+    @Excel(name = "招聘会名称", width = 15)
+    @Schema(description = "招聘会名称")
+    private java.lang.String fairName;
+
+    /**招聘会类型*/
+    @Excel(name = "招聘会类型", width = 15)
+    @Schema(description = "招聘会类型")
+    private java.lang.String fairType;
+
+    /**招聘会主体*/
+    @Excel(name = "招聘会主体", width = 15)
+    @Schema(description = "招聘会主体")
+    private java.lang.String fairSubject;
+
+    /**举办方式*/
+    @Dict(dicCode = "fair_hold_method")
+    @Excel(name = "举办方式", width = 15)
+    @Schema(description = "举办方式")
+    private java.lang.String holdMethod;
+
+    /**线上举办日期*/
+    @Excel(name = "线上举办日期", width = 15)
+    @Schema(description = "线上举办日期")
+    private java.lang.String onlineHoldDate;
+
+    /**线下举办日期*/
+    @Excel(name = "线下举办日期", width = 15)
+    @Schema(description = "线下举办日期")
+    private java.lang.String offlineHoldDate;
+
+    /**主办单位*/
+    @Excel(name = "主办单位", width = 15)
+    @Schema(description = "主办单位")
+    private java.lang.String hostUnit;
+
+    /**举办地点*/
+    @Excel(name = "举办地点", width = 15)
+    @Schema(description = "举办地点")
+    private java.lang.String holdLocation;
+
+    /**数据来源*/
+    @Dict(dicCode = "fair_data_source")
+    @Excel(name = "数据来源", width = 15)
+    @Schema(description = "数据来源")
+    private java.lang.String dataSource;
+
+    /**状态*/
+    @Dict(dicCode = "fair_status")
+    @Excel(name = "状态", width = 15)
+    @Schema(description = "状态")
+    private java.lang.String status;
+
+    /**省平台链接*/
+    @Excel(name = "省平台链接", width = 15)
+    @Schema(description = "省平台链接")
+    private java.lang.String provinceLink;
+
+    /**招聘会描述*/
+    @Excel(name = "招聘会描述", width = 15)
+    @Schema(description = "招聘会描述")
+    private java.lang.String fairDescription;
+
+    /**封面图URL*/
+    @Excel(name = "封面图URL", width = 15)
+    @Schema(description = "封面图URL")
+    private java.lang.String fairImage;
+
+    /**报名截止时间*/
+    @Excel(name = "报名截止时间", width = 15)
+    @Schema(description = "报名截止时间")
+    private java.lang.String registrationDeadline;
+
+    /**最大参展企业数*/
+    @Excel(name = "最大参展企业数", width = 15)
+    @Schema(description = "最大参展企业数")
+    private java.lang.Integer maxEnterprises;
+
+    /**最大参与人数*/
+    @Excel(name = "最大参与人数", width = 15)
+    @Schema(description = "最大参与人数")
+    private java.lang.Integer maxParticipants;
+
+    /**联系人*/
+    @Excel(name = "联系人", width = 15)
+    @Schema(description = "联系人")
+    private java.lang.String contactPerson;
+
+    /**联系电话*/
+    @Excel(name = "联系电话", width = 15)
+    @Schema(description = "联系电话")
+    private java.lang.String contactPhone;
+
+    /**日程安排(富文本)*/
+    @Excel(name = "日程安排", width = 15)
+    @Schema(description = "日程安排")
+    private java.lang.String fairSchedule;
+}
+//update-end---author:kk ---date:2026-06-12  for:【招聘会管理】新增招聘会实体类-----------

+ 43 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/recruitmentfair/entity/RecruitmentFairEnterprise.java

@@ -0,0 +1,43 @@
+package org.jeecg.modules.zjrs.recruitmentfair.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import org.jeecg.common.system.base.entity.JeecgEntity;
+import org.jeecgframework.poi.excel.annotation.Excel;
+
+/**
+ * @Description: 招聘会-企业关联表
+ * @Author: jeecg-boot
+ * @Date: 2026-06-13
+ * @Version: V1.0
+ */
+@Data
+@TableName("recruitment_fair_enterprise")
+@Accessors(chain = true)
+@EqualsAndHashCode(callSuper = true)
+@Schema(description = "招聘会-企业关联表")
+public class RecruitmentFairEnterprise extends JeecgEntity {
+
+    /**招聘会ID*/
+    @Excel(name = "招聘会ID", width = 15)
+    @Schema(description = "招聘会ID")
+    private java.lang.String fairId;
+
+    /**企业ID*/
+    @Excel(name = "企业ID", width = 15)
+    @Schema(description = "企业ID")
+    private java.lang.String enterpriseId;
+
+    /**企业名称*/
+    @Excel(name = "企业名称", width = 15)
+    @Schema(description = "企业名称")
+    private java.lang.String enterpriseName;
+
+    /**参展状态:0-待审核,1-已通过,2-已拒绝*/
+    @Excel(name = "参展状态", width = 15)
+    @Schema(description = "参展状态:0-待审核,1-已通过,2-已拒绝")
+    private java.lang.String status;
+}

+ 48 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/recruitmentfair/entity/RecruitmentFairPersonal.java

@@ -0,0 +1,48 @@
+package org.jeecg.modules.zjrs.recruitmentfair.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import org.jeecg.common.system.base.entity.JeecgEntity;
+import org.jeecgframework.poi.excel.annotation.Excel;
+
+/**
+ * @Description: 招聘会-个人报名表
+ * @Author: jeecg-boot
+ * @Date: 2026-06-13
+ * @Version: V1.0
+ */
+@Data
+@TableName("recruitment_fair_personal")
+@Accessors(chain = true)
+@EqualsAndHashCode(callSuper = true)
+@Schema(description = "招聘会-个人报名表")
+public class RecruitmentFairPersonal extends JeecgEntity {
+
+    /**招聘会ID*/
+    @Excel(name = "招聘会ID", width = 15)
+    @Schema(description = "招聘会ID")
+    private java.lang.String fairId;
+
+    /**个人ID*/
+    @Excel(name = "个人ID", width = 15)
+    @Schema(description = "个人ID")
+    private java.lang.String personalId;
+
+    /**个人姓名*/
+    @Excel(name = "个人姓名", width = 15)
+    @Schema(description = "个人姓名")
+    private java.lang.String personalName;
+
+    /**联系电话*/
+    @Excel(name = "联系电话", width = 15)
+    @Schema(description = "联系电话")
+    private java.lang.String contactPhone;
+
+    /**报名状态:0-已报名,1-已签到,2-已取消*/
+    @Excel(name = "报名状态", width = 15)
+    @Schema(description = "报名状态:0-已报名,1-已签到,2-已取消")
+    private java.lang.String status;
+}

+ 7 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/recruitmentfair/mapper/RecruitmentFairEnterpriseMapper.java

@@ -0,0 +1,7 @@
+package org.jeecg.modules.zjrs.recruitmentfair.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.jeecg.modules.zjrs.recruitmentfair.entity.RecruitmentFairEnterprise;
+
+public interface RecruitmentFairEnterpriseMapper extends BaseMapper<RecruitmentFairEnterprise> {
+}

+ 15 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/recruitmentfair/mapper/RecruitmentFairMapper.java

@@ -0,0 +1,15 @@
+//update-begin---author:kk ---date:2026-06-12  for:【招聘会管理】新增招聘会Mapper接口-----------
+package org.jeecg.modules.zjrs.recruitmentfair.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.jeecg.modules.zjrs.recruitmentfair.entity.RecruitmentFair;
+
+/**
+ * @Description: 招聘会管理
+ * @Author: jeecg-boot
+ * @Date: 2026-06-12
+ * @Version: V1.0
+ */
+public interface RecruitmentFairMapper extends BaseMapper<RecruitmentFair> {
+}
+//update-end---author:kk ---date:2026-06-12  for:【招聘会管理】新增招聘会Mapper接口-----------

+ 7 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/recruitmentfair/mapper/RecruitmentFairPersonalMapper.java

@@ -0,0 +1,7 @@
+package org.jeecg.modules.zjrs.recruitmentfair.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.jeecg.modules.zjrs.recruitmentfair.entity.RecruitmentFairPersonal;
+
+public interface RecruitmentFairPersonalMapper extends BaseMapper<RecruitmentFairPersonal> {
+}

+ 4 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/recruitmentfair/mapper/xml/RecruitmentFairEnterpriseMapper.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.jeecg.modules.zjrs.recruitmentfair.mapper.RecruitmentFairEnterpriseMapper">
+</mapper>

+ 5 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/recruitmentfair/mapper/xml/RecruitmentFairMapper.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.jeecg.modules.zjrs.recruitmentfair.mapper.RecruitmentFairMapper">
+
+</mapper>

+ 4 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/recruitmentfair/mapper/xml/RecruitmentFairPersonalMapper.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.jeecg.modules.zjrs.recruitmentfair.mapper.RecruitmentFairPersonalMapper">
+</mapper>

+ 15 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/recruitmentfair/service/IRecruitmentFairService.java

@@ -0,0 +1,15 @@
+//update-begin---author:kk ---date:2026-06-12  for:【招聘会管理】新增招聘会Service接口-----------
+package org.jeecg.modules.zjrs.recruitmentfair.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.jeecg.modules.zjrs.recruitmentfair.entity.RecruitmentFair;
+
+/**
+ * @Description: 招聘会管理
+ * @Author: jeecg-boot
+ * @Date: 2026-06-12
+ * @Version: V1.0
+ */
+public interface IRecruitmentFairService extends IService<RecruitmentFair> {
+}
+//update-end---author:kk ---date:2026-06-12  for:【招聘会管理】新增招聘会Service接口-----------

+ 19 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/recruitmentfair/service/impl/RecruitmentFairServiceImpl.java

@@ -0,0 +1,19 @@
+//update-begin---author:kk ---date:2026-06-12  for:【招聘会管理】新增招聘会Service实现-----------
+package org.jeecg.modules.zjrs.recruitmentfair.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.jeecg.modules.zjrs.recruitmentfair.entity.RecruitmentFair;
+import org.jeecg.modules.zjrs.recruitmentfair.mapper.RecruitmentFairMapper;
+import org.jeecg.modules.zjrs.recruitmentfair.service.IRecruitmentFairService;
+import org.springframework.stereotype.Service;
+
+/**
+ * @Description: 招聘会管理
+ * @Author: jeecg-boot
+ * @Date: 2026-06-12
+ * @Version: V1.0
+ */
+@Service
+public class RecruitmentFairServiceImpl extends ServiceImpl<RecruitmentFairMapper, RecruitmentFair> implements IRecruitmentFairService {
+}
+//update-end---author:kk ---date:2026-06-12  for:【招聘会管理】新增招聘会Service实现-----------

+ 36 - 2
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/welfarepost/controller/WelfarePostController.java

@@ -8,18 +8,26 @@ import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.shiro.SecurityUtils;
 import org.apache.shiro.authz.annotation.RequiresPermissions;
 import org.jeecg.common.api.vo.Result;
 import org.jeecg.common.aspect.annotation.AutoLog;
 import org.jeecg.common.system.base.controller.JeecgController;
 import org.jeecg.common.system.query.QueryGenerator;
+import org.jeecg.common.system.vo.LoginUser;
+import org.jeecg.common.util.oConvertUtils;
 import org.jeecg.modules.zjrs.welfarepost.entity.WelfarePost;
 import org.jeecg.modules.zjrs.welfarepost.service.IWelfarePostService;
+import org.jeecgframework.poi.excel.def.NormalExcelConstants;
+import org.jeecgframework.poi.excel.entity.ExportParams;
+import org.jeecgframework.poi.excel.entity.enmus.ExcelType;
+import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.servlet.ModelAndView;
 
-import java.util.Arrays;
+import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * @Description: 就业一湛通-公益性岗位管理
@@ -35,6 +43,14 @@ public class WelfarePostController extends JeecgController<WelfarePost, IWelfare
     @Autowired
     private IWelfarePostService welfarePostService;
 
+    private static final Map<String, String> EXPORT_DICT_FIELD_MAP = new HashMap<>();
+    static {
+        EXPORT_DICT_FIELD_MAP.put("postType", "welfare_post_type");
+        EXPORT_DICT_FIELD_MAP.put("applyMethod", "apply_method");
+        EXPORT_DICT_FIELD_MAP.put("publishStatus", "publish_status");
+        EXPORT_DICT_FIELD_MAP.put("postStatus", "welfare_post_status");
+    }
+
     /**
      * 分页列表查询(使用QueryGenerator自动构建查询条件)
      */
@@ -149,7 +165,25 @@ public class WelfarePostController extends JeecgController<WelfarePost, IWelfare
     @RequiresPermissions("welfare_post:exportXls")
     @RequestMapping(value = "/exportXls")
     public ModelAndView exportXls(HttpServletRequest request, WelfarePost welfarePost) {
-        return super.exportXls(request, welfarePost, WelfarePost.class, "就业一湛通-公益性岗位管理");
+        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+
+        QueryWrapper<WelfarePost> queryWrapper = QueryGenerator.initQueryWrapper(welfarePost, request.getParameterMap());
+        List<WelfarePost> dataList = welfarePostService.list(queryWrapper);
+
+        String selections = request.getParameter("selections");
+        if (oConvertUtils.isNotEmpty(selections)) {
+            List<String> selectionList = Arrays.asList(selections.split(","));
+            dataList = dataList.stream().filter(item -> selectionList.contains(item.getId())).collect(Collectors.toList());
+        }
+
+        welfarePostService.translateDictFields(dataList, EXPORT_DICT_FIELD_MAP);
+
+        ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
+        mv.addObject(NormalExcelConstants.FILE_NAME, "就业一湛通-公益性岗位管理");
+        mv.addObject(NormalExcelConstants.CLASS, WelfarePost.class);
+        mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("就业一湛通-公益性岗位管理", "导出人:" + sysUser.getRealname(), "公益性岗位管理", ExcelType.XSSF));
+        mv.addObject(NormalExcelConstants.DATA_LIST, dataList);
+        return mv;
     }
 
     @RequiresPermissions("welfare_post:importExcel")

+ 5 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/welfarepost/service/IWelfarePostService.java

@@ -3,6 +3,9 @@ package org.jeecg.modules.zjrs.welfarepost.service;
 import com.baomidou.mybatisplus.extension.service.IService;
 import org.jeecg.modules.zjrs.welfarepost.entity.WelfarePost;
 
+import java.util.List;
+import java.util.Map;
+
 /**
  * @Description: 就业一湛通-公益性岗位管理
  * @Author: jeecg-boot
@@ -29,4 +32,6 @@ public interface IWelfarePostService extends IService<WelfarePost> {
      * @param postStatus 岗位状态(招聘中/已满/已关闭)
      */
     void updatePostStatus(String id, String postStatus);
+
+    List<WelfarePost> translateDictFields(List<WelfarePost> list, Map<String, String> fieldDictMap);
 }

+ 45 - 1
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/welfarepost/service/impl/WelfarePostServiceImpl.java

@@ -3,13 +3,14 @@ package org.jeecg.modules.zjrs.welfarepost.service.impl;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import org.apache.shiro.SecurityUtils;
 import org.jeecg.common.system.vo.LoginUser;
+import org.jeecg.modules.zjrs.dictionary.service.IDictionaryItemService;
 import org.jeecg.modules.zjrs.welfarepost.entity.WelfarePost;
 import org.jeecg.modules.zjrs.welfarepost.mapper.WelfarePostMapper;
 import org.jeecg.modules.zjrs.welfarepost.service.IWelfarePostService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
-import java.util.Date;
+import java.util.*;
 
 /**
  * @Description: 就业一湛通-公益性岗位管理
@@ -23,6 +24,9 @@ public class WelfarePostServiceImpl extends ServiceImpl<WelfarePostMapper, Welfa
     @Autowired
     private WelfarePostMapper welfarePostMapper;
 
+    @Autowired
+    private IDictionaryItemService dictionaryItemService;
+
     @Override
     public void publish(String id) {
         WelfarePost post = this.getById(id);
@@ -71,4 +75,44 @@ public class WelfarePostServiceImpl extends ServiceImpl<WelfarePostMapper, Welfa
         post.setPostStatus(postStatus);
         this.updateById(post);
     }
+
+    @Override
+    public List<WelfarePost> translateDictFields(List<WelfarePost> list, Map<String, String> fieldDictMap) {
+        List<String> dictCodes = new ArrayList<>(fieldDictMap.values());
+        Map<String, List<Map<String, Object>>> dictData = dictionaryItemService.getDictItemsBatch(dictCodes);
+
+        Map<String, Map<String, String>> valueLabelMap = new HashMap<>();
+        for (Map.Entry<String, List<Map<String, Object>>> entry : dictData.entrySet()) {
+            Map<String, String> valueToLabel = new HashMap<>();
+            for (Map<String, Object> item : entry.getValue()) {
+                valueToLabel.put(String.valueOf(item.get("value")), String.valueOf(item.get("label")));
+            }
+            valueLabelMap.put(entry.getKey(), valueToLabel);
+        }
+
+        for (WelfarePost entity : list) {
+            for (Map.Entry<String, String> fieldEntry : fieldDictMap.entrySet()) {
+                String fieldName = fieldEntry.getKey();
+                String dictCode = fieldEntry.getValue();
+                Map<String, String> mapping = valueLabelMap.get(dictCode);
+                if (mapping == null) continue;
+
+                switch (fieldName) {
+                    case "postType":
+                        if (entity.getPostType() != null) entity.setPostType(mapping.getOrDefault(entity.getPostType(), entity.getPostType()));
+                        break;
+                    case "applyMethod":
+                        if (entity.getApplyMethod() != null) entity.setApplyMethod(mapping.getOrDefault(entity.getApplyMethod(), entity.getApplyMethod()));
+                        break;
+                    case "publishStatus":
+                        if (entity.getPublishStatus() != null) entity.setPublishStatus(mapping.getOrDefault(entity.getPublishStatus(), entity.getPublishStatus()));
+                        break;
+                    case "postStatus":
+                        if (entity.getPostStatus() != null) entity.setPostStatus(mapping.getOrDefault(entity.getPostStatus(), entity.getPostStatus()));
+                        break;
+                }
+            }
+        }
+        return list;
+    }
 }

+ 88 - 0
jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V20260612_1__menu_insert_RecruitmentFair.sql

@@ -0,0 +1,88 @@
+-- 注意:该页面对应的前台目录为views/recruitmentfair文件夹下
+-- 如果你想更改到其他目录,请修改sql中component字段对应的值
+
+-- ============================================================
+-- 就业一湛通服务平台 - 菜单权限SQL - 招聘会管理
+-- 菜单层级:招聘会管理(一级菜单)
+-- MySQL语法,兼容达梦数据库
+-- ============================================================
+
+-- ============================================================
+-- 1. 清理旧数据(兼容重复执行)
+-- ============================================================
+DELETE FROM sys_role_permission WHERE permission_id IN (
+    '178060100000600', '178060100000610', '178060100000611',
+    '178060100000612', '178060100000613', '178060100000614',
+    '178060100000615', '178060100000616'
+);
+DELETE FROM sys_permission WHERE id IN (
+    '178060100000610', '178060100000611', '178060100000612',
+    '178060100000613', '178060100000614', '178060100000615',
+    '178060100000616'
+);
+DELETE FROM sys_permission WHERE id = '178060100000600';
+
+-- ============================================================
+-- 2. 一级菜单:招聘会管理
+-- ============================================================
+INSERT INTO sys_permission (id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('178060100000600', '', '招聘会管理', '/recruitmentFair', 'layouts/default/index', 1, '', NULL, 0, NULL, '0', 2.30, 0, 'ant-design:calendar-outlined', 0, 0, 0, 0, NULL, 'admin', '2026-06-12 10:00:00', NULL, NULL, 0, 0, NULL, 0);
+
+-- ============================================================
+-- 3. 二级菜单:招聘会管理页面
+-- ============================================================
+INSERT INTO sys_permission (id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('178060100000610', '178060100000600', '招聘会管理', '/recruitmentFair/recruitmentFairList', 'recruitmentfair/RecruitmentFairList', 1, '', NULL, 1, NULL, '0', 1.00, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2026-06-12 10:00:00', NULL, NULL, 0, 0, NULL, 0);
+
+-- ============================================================
+-- 4. 按钮权限:新增
+-- ============================================================
+INSERT INTO sys_permission (id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('178060100000611', '178060100000610', '添加招聘会信息', NULL, NULL, 0, '', NULL, 2, 'recruitment_fair:add', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2026-06-12 10:00:00', NULL, NULL, 0, 0, NULL, 0);
+
+-- 按钮权限:编辑
+INSERT INTO sys_permission (id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('178060100000612', '178060100000610', '编辑招聘会信息', NULL, NULL, 0, '', NULL, 2, 'recruitment_fair:edit', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2026-06-12 10:00:00', NULL, NULL, 0, 0, NULL, 0);
+
+-- 按钮权限:删除
+INSERT INTO sys_permission (id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('178060100000613', '178060100000610', '删除招聘会信息', NULL, NULL, 0, '', NULL, 2, 'recruitment_fair:delete', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2026-06-12 10:00:00', NULL, NULL, 0, 0, NULL, 0);
+
+-- 按钮权限:批量删除
+INSERT INTO sys_permission (id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('178060100000614', '178060100000610', '批量删除招聘会信息', NULL, NULL, 0, '', NULL, 2, 'recruitment_fair:deleteBatch', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2026-06-12 10:00:00', NULL, NULL, 0, 0, NULL, 0);
+
+-- 按钮权限:导出excel
+INSERT INTO sys_permission (id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('178060100000615', '178060100000610', '导出excel_招聘会管理', NULL, NULL, 0, '', NULL, 2, 'recruitment_fair:exportXls', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2026-06-12 10:00:00', NULL, NULL, 0, 0, NULL, 0);
+
+-- 按钮权限:导入excel
+INSERT INTO sys_permission (id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('178060100000616', '178060100000610', '导入excel_招聘会管理', NULL, NULL, 0, '', NULL, 2, 'recruitment_fair:importExcel', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2026-06-12 10:00:00', NULL, NULL, 0, 0, NULL, 0);
+
+-- ============================================================
+-- 5. 角色授权(admin角色 f6817f48af4fb3af11b9e8bf182f618b)
+-- ============================================================
+INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip)
+VALUES ('178060100000620', 'f6817f48af4fb3af11b9e8bf182f618b', '178060100000600', NULL, '2026-06-12 10:00:00', '127.0.0.1');
+
+INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip)
+VALUES ('178060100000621', 'f6817f48af4fb3af11b9e8bf182f618b', '178060100000610', NULL, '2026-06-12 10:00:00', '127.0.0.1');
+
+INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip)
+VALUES ('178060100000622', 'f6817f48af4fb3af11b9e8bf182f618b', '178060100000611', NULL, '2026-06-12 10:00:00', '127.0.0.1');
+
+INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip)
+VALUES ('178060100000623', 'f6817f48af4fb3af11b9e8bf182f618b', '178060100000612', NULL, '2026-06-12 10:00:00', '127.0.0.1');
+
+INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip)
+VALUES ('178060100000624', 'f6817f48af4fb3af11b9e8bf182f618b', '178060100000613', NULL, '2026-06-12 10:00:00', '127.0.0.1');
+
+INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip)
+VALUES ('178060100000625', 'f6817f48af4fb3af11b9e8bf182f618b', '178060100000614', NULL, '2026-06-12 10:00:00', '127.0.0.1');
+
+INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip)
+VALUES ('178060100000626', 'f6817f48af4fb3af11b9e8bf182f618b', '178060100000615', NULL, '2026-06-12 10:00:00', '127.0.0.1');
+
+INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip)
+VALUES ('178060100000627', 'f6817f48af4fb3af11b9e8bf182f618b', '178060100000616', NULL, '2026-06-12 10:00:00', '127.0.0.1');

BIN
jeecgboot-vue3/node_modules.7z


+ 1 - 1
jeecgboot-vue3/src/components/Application/src/AppLogo.vue

@@ -38,7 +38,7 @@
   const { getCollapsedShowTitle } = useMenuSetting();
   const userStore = useUserStore();
   const { title, shortTitle } = useGlobSetting();
-  
+
   const go = useGo();
 
   const getAppLogoClass = computed(() => [prefixCls, props.theme, { 'collapsed-show-title': unref(getCollapsedShowTitle) }]);

+ 13 - 20
jeecgboot-vue3/src/hooks/dictionary/useDict.ts

@@ -8,23 +8,15 @@ let loadPromise: Promise<void> | null = null;
 
 export function useDict(dictCodes: string[]) {
   const loading = ref(isLoading);
+  const codes = [...new Set(dictCodes.filter(Boolean))];
 
   async function fetchDicts(): Promise<void> {
-    const codes = [...new Set(dictCodes.filter(Boolean))];
     if (!codes.length) return;
-    // 如果当前所有字典都已缓存,直接返回
-    if (codes.every((code) => dictCache[code] !== undefined)) {
-      return;
-    }
-    // 如果已有请求在途中,等待它完成后再检查是否还缺失字典
+    // 如果已有请求在途中,等待它完成
     if (isLoading && loadPromise) {
       loading.value = true;
       await loadPromise;
       loading.value = false;
-      // 等待完毕后再次检查,如果仍缺失则继续执行后续请求
-      if (codes.every((code) => dictCache[code] !== undefined)) {
-        return;
-      }
     }
     isLoading = true;
     loading.value = true;
@@ -35,15 +27,14 @@ export function useDict(dictCodes: string[]) {
           data: codes,
         });
         for (const code of codes) {
-          const items = result[code] || [];
-          // 后端已返回 {label, value} 格式,直接存入缓存供 a-select 使用
-          dictCache[code] = items.map((item) => ({
-            // Code 字段用于存储非数值类字典的实际值(如文本),Value 为 INT 仅作排序
-            // 优先使用 Code 作为选项值,确保 a-select 的 v-model 与数据库文本值匹配
-            value: item.code ? item.code : String(item.value),
-            label: item.label,
-            code: item.code || String(item.value),
-          }));
+          const items = result[code];
+          if (items && items.length > 0) {
+            dictCache[code] = items.map((item) => ({
+              value: item.code ? item.code : String(item.value),
+              label: item.label,
+              code: item.code || String(item.value),
+            }));
+          }
         }
       } catch (e) {
         console.error('字典加载失败:', e);
@@ -55,7 +46,7 @@ export function useDict(dictCodes: string[]) {
     await loadPromise;
   }
 
-  // 根据字典编码和值获取对应的文本(优先value匹配,其次code匹配)
+  // 根据字典编码和值获取对应的文本
   function getDictText(code: string, value: any): string {
     const items = dictCache[code];
     if (!items) return value;
@@ -72,6 +63,8 @@ export function useDict(dictCodes: string[]) {
   }
 
   onMounted(() => {
+    // 每次挂载都强制刷新当前请求的字典,确保数据最新
+    codes.forEach((code) => delete dictCache[code]);
     fetchDicts();
   });
 

+ 9 - 10
jeecgboot-vue3/src/layouts/default/header/index.less

@@ -176,12 +176,10 @@
 
   &--dark {
     background-color: @header-dark-bg-color !important;
-    // border-bottom: 1px solid @border-color-base;
-    // update-begin--author:liaozhiyang---date:20250818---for:【issues/8709】LayoutContent样式多出1px
-    // border-left: 1px solid @border-color-base;
-    // update-end--author:liaozhiyang---date:20250818---for:【issues/8709】LayoutContent样式多出1px
 
     .@{header-prefix-cls}-logo {
+      color: rgba(255, 255, 255, 1);
+
       &:hover {
         background-color: @header-dark-bg-hover-color;
       }
@@ -189,21 +187,22 @@
 
     .@{header-prefix-cls}-action {
       &__item {
+        color: rgba(255, 255, 255, 1);
+
         .app-iconify {
           padding: 0 10px;
           font-size: 16px !important;
         }
 
-        .ant-badge {
-          span {
-            color: @white;
-          }
-        }
-
         &:hover {
           background-color: @header-dark-bg-hover-color;
         }
       }
+
+      &-icon,
+      span[role='img'] {
+        color: rgba(255, 255, 255, 1);
+      }
     }
   }
 }

+ 3 - 2
jeecgboot-vue3/src/layouts/default/header/index.vue

@@ -155,8 +155,9 @@
         if (!unref(getIsMixMode) || unref(getIsMobile)) {
           return {};
         }
-        const width = unref(getMenuWidth) < 180 ? 180 : unref(getMenuWidth);
-        return { width: `${width}px` };
+        // logo区域宽度:logo 48px + 间距 10px + 标题文字约200px + padding
+        const width = 320;
+        return { width: `${width}px`, minWidth: `${width}px` };
       });
 
       const getSplitType = computed(() => {

+ 2 - 1
jeecgboot-vue3/src/settings/designSetting.ts

@@ -50,9 +50,10 @@ export const SIDE_BAR_BG_COLOR_LIST: string[] = [
   // '#344058',
   '#e74c3c',
   '#383f45',
+  '#e6eef9',
 ];
 
-// sider logo line preset color [logo����ɫ]
+// sider logo line preset color [logo背景色]
 export const SIDER_LOGO_BG_COLOR_LIST: string[] = [
   'linear-gradient(180deg, #000000, #021d37)',
   // 'linear-gradient(180deg, #000000, #282828)',

+ 4 - 4
jeecgboot-vue3/src/settings/projectSetting.ts

@@ -1,5 +1,5 @@
 import type { ProjectConfig } from '/#/config';
-import { MenuTypeEnum, MenuModeEnum, TriggerEnum, MixSidebarTriggerEnum } from '/@/enums/menuEnum';
+import { MenuTypeEnum, TriggerEnum, MixSidebarTriggerEnum } from '/@/enums/menuEnum';
 import { CacheTypeEnum } from '/@/enums/cacheEnum';
 import {
   ContentEnum,
@@ -111,7 +111,7 @@ const setting: ProjectConfig = {
     // Whether to show dom
     hidden: false,
     // 菜单宽度
-    menuWidth: 210,
+    menuWidth: 260,
     // 菜单模式
     mode,
     // 菜单类型
@@ -130,7 +130,7 @@ const setting: ProjectConfig = {
     accordion: true,
     // 在路由切换的时候关闭左侧混合菜单展开菜单
     closeMixSidebarOnChange: false,
-    // 左侧混合菜单模块切换触发方式 ‘click’ |'hover'
+    // 左侧混合菜单模块切换触发方式 'click' |'hover'
     mixSideTrigger: MixSidebarTriggerEnum.CLICK,
     // 是否固定左侧混合菜单
     mixSideFixed: false,
@@ -195,7 +195,7 @@ const setting: ProjectConfig = {
   // 切换界面的时候是否删除未关闭的message及notify
   closeMessageOnSwitch: true,
 
-  // 切换界面的时候是否取消已经发送但是未响应的http请求。
+  // 切换界面的时候是否取消已经发送的http请求。
   // 如果开启,想对单独接口覆盖。可以在单独接口设置
   removeAllHttpPending: false,
 };

+ 17 - 0
jeecgboot-vue3/src/views/careerguidancedocument/CareerGuidanceDocument.data.ts

@@ -24,6 +24,12 @@ export const searchFormSchema: FormSchema[] = [
 ];
 
 export const formSchema: FormSchema[] = [
+  {
+    label: '所属目录',
+    field: 'categoryId',
+    component: 'Input',
+    ifShow: false,
+  },
   {
     label: '文件名称',
     field: 'name',
@@ -36,7 +42,18 @@ export const formSchema: FormSchema[] = [
     component: 'JUpload',
     componentProps: {
       fileType: 'pdf',
+      maxCount: 1,
+      removeConfirm: true,
+      beforeUpload: (file: File) => {
+        const isPdf = file.type === 'application/pdf' || file.name?.toLowerCase().endsWith('.pdf');
+        if (!isPdf) {
+          import('ant-design-vue').then(({ message }) => message.warning('仅支持上传PDF文件'));
+          return false;
+        }
+        return true;
+      },
     },
+    helpMessage: '仅支持单个PDF文件,编辑时需先删除旧文件再上传新文件',
   },
   {
     label: '排序',

+ 12 - 7
jeecgboot-vue3/src/views/careerguidancedocument/CareerGuidanceDocumentList.vue

@@ -125,16 +125,19 @@
   import {
     list,
     deleteOne,
-    getDownloadUrl,
-    getViewUrl,
     getCategoryTree,
     deleteCategory,
   } from './CareerGuidanceDocument.api';
   import CareerGuidanceDocumentModal from './components/CareerGuidanceDocumentModal.vue';
   import CareerGuidanceCategoryModal from './components/CareerGuidanceCategoryModal.vue';
+  import { getToken } from '/@/utils/auth';
+  import { useGlobSetting } from '/@/hooks/setting';
+  import { useMessage } from '/@/hooks/web/useMessage';
 
   const [registerModal, { openModal }] = useModal();
   const [registerCategoryModal, { openModal: openCategoryModal }] = useModal();
+  const { apiUrl } = useGlobSetting();
+  const { createMessage } = useMessage();
 
   const formRef = ref();
   const queryParam = reactive<any>({});
@@ -274,10 +277,14 @@
 
   // 文件管理
   function handleAddFile() {
+    if (!selectedCategoryId.value) {
+      createMessage.warning('请先在左侧目录树中选择一个目录');
+      return;
+    }
     openModal(true, {
       isUpdate: false,
       showFooter: true,
-      categoryId: selectedCategoryId.value || undefined,
+      categoryId: selectedCategoryId.value,
     });
   }
 
@@ -290,7 +297,7 @@
   }
 
   function handleView(record: Recordable) {
-    window.open(getViewUrl(record.id), '_blank');
+    window.open(`${apiUrl}/careerguidancedocument/careerGuidanceDocument/view?id=${record.id}&token=${getToken()}`, '_blank');
   }
 
   async function handleDelete(record) {
@@ -302,9 +309,7 @@
   }
 
   function handleDownload(record) {
-    if (record.fileUrl) {
-      window.open(getDownloadUrl(record.id), '_blank');
-    }
+    window.open(`${apiUrl}/careerguidancedocument/careerGuidanceDocument/download?id=${record.id}&token=${getToken()}`, '_blank');
   }
 </script>
 <style scoped>

+ 2 - 5
jeecgboot-vue3/src/views/careerguidancedocument/components/CareerGuidanceDocumentForm.vue

@@ -11,7 +11,7 @@
   import { saveOrUpdate } from '../CareerGuidanceDocument.api';
 
   const isUpdate = ref(true);
-  // categoryId 不放入 formSchema,单独维护
+  // categoryId 由外部设置,提交时手动合并(因 ifShow:false 隐藏字段不会注册到表单)
   const pendingCategoryId = ref<string>('');
 
   const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
@@ -38,15 +38,12 @@
   async function submitForm() {
     try {
       const values = await validate();
-      const isUpdateVal = unref(isUpdate);
-      // 手动合并 categoryId(不经过 formSchema)
       if (pendingCategoryId.value) {
         values.categoryId = pendingCategoryId.value;
       }
-      await saveOrUpdate(values, isUpdateVal);
+      await saveOrUpdate(values, unref(isUpdate));
       return true;
     } catch (error) {
-      console.warn('Form validate failed:', error);
       return false;
     }
   }

+ 30 - 147
jeecgboot-vue3/src/views/careerguidanceservice/CareerGuidanceService.data.ts

@@ -1,195 +1,78 @@
 import { BasicColumn } from '/@/components/Table';
-import { FormSchema } from '/@/components/Table';
-import { render } from '/@/utils/common/renderUtils';
-import { JVxeTypes, JVxeColumn } from '/@/components/jeecg/JVxeTable/types';
 
 export const columns: BasicColumn[] = [
+  {
+    title: '序号',
+    align: 'center',
+    dataIndex: 'index',
+    key: 'rowIndex',
+    width: 60,
+    customRender: ({ index }) => index + 1,
+  },
   {
     title: '姓名',
+    align: 'center',
     dataIndex: 'name',
     width: 120,
   },
+  {
+    title: '证件号',
+    align: 'center',
+    dataIndex: 'idCard',
+    width: 180,
+  },
   {
     title: '性别',
+    align: 'center',
     dataIndex: 'gender',
     width: 80,
   },
   {
     title: '年龄',
+    align: 'center',
     dataIndex: 'age',
     width: 80,
   },
   {
     title: '学历',
+    align: 'center',
     dataIndex: 'education',
     width: 100,
   },
+  {
+    title: '户口所在地',
+    align: 'center',
+    dataIndex: 'householdLocation',
+    width: 150,
+  },
   {
     title: '联系电话',
+    align: 'center',
     dataIndex: 'contactPhone',
     width: 130,
   },
   {
     title: '申请原因',
+    align: 'center',
     dataIndex: 'applicationReason',
     width: 200,
   },
   {
     title: '申请日期',
+    align: 'center',
     dataIndex: 'applicationDate',
-    width: 150,
+    width: 120,
   },
   {
     title: '是否已跟进',
+    align: 'center',
     dataIndex: 'isFollowedUp',
     width: 100,
-    customRender: ({ text }) => {
-      return render.renderDict(text, 'yn');
-    },
   },
   {
     title: '最后通知时间',
+    align: 'center',
     dataIndex: 'lastNotifyTime',
     width: 150,
   },
 ];
-
-export const searchFormSchema: FormSchema[] = [
-  {
-    label: '姓名',
-    field: 'name',
-    component: 'Input',
-    colProps: { span: 6 },
-  },
-  {
-    label: '是否已跟进',
-    field: 'isFollowedUp',
-    component: 'JDictSelectTag',
-    componentProps: {
-      dictCode: 'yn',
-    },
-    colProps: { span: 6 },
-  },
-  {
-    label: '户口所在地',
-    field: 'householdLocation',
-    component: 'Input',
-    colProps: { span: 6 },
-  },
-];
-
-export const formSchema: FormSchema[] = [
-  {
-    label: '姓名',
-    field: 'name',
-    component: 'Input',
-  },
-  {
-    label: '性别',
-    field: 'gender',
-    component: 'JDictSelectTag',
-    componentProps: {
-      dictCode: 'sex',
-    },
-  },
-  {
-    label: '年龄',
-    field: 'age',
-    component: 'InputNumber',
-    componentProps: {
-      style: 'width: 100%',
-    },
-  },
-  {
-    label: '学历',
-    field: 'education',
-    component: 'JDictSelectTag',
-    componentProps: {
-      dictCode: 'education',
-    },
-  },
-  {
-    label: '联系电话',
-    field: 'contactPhone',
-    component: 'Input',
-  },
-  {
-    label: '户口所在地',
-    field: 'householdLocation',
-    component: 'Input',
-  },
-  {
-    label: '申请原因',
-    field: 'applicationReason',
-    component: 'InputTextArea',
-  },
-  {
-    label: '申请日期',
-    field: 'applicationDate',
-    component: 'DatePicker',
-    componentProps: {
-      valueFormat: 'YYYY-MM-DD',
-    },
-  },
-  {
-    label: '是否已跟进',
-    field: 'isFollowedUp',
-    component: 'JDictSelectTag',
-    componentProps: {
-      dictCode: 'yn',
-    },
-  },
-  {
-    label: '最后通知时间',
-    field: 'lastNotifyTime',
-    component: 'DatePicker',
-    componentProps: {
-      showTime: true,
-      valueFormat: 'YYYY-MM-DD HH:mm:ss',
-    },
-  },
-];
-
-//子表单区域
-export const careerGuidanceRecordColumns: JVxeColumn[] = [
-  {
-    title: '记录类型',
-    key: 'recordType',
-    type: JVxeTypes.input,
-    width: "200px",
-    placeholder: '请输入${title}',
-    defaultValue: '',
-  },
-  {
-    title: '标题',
-    key: 'title',
-    type: JVxeTypes.input,
-    width: "200px",
-    placeholder: '请输入${title}',
-    defaultValue: '',
-  },
-  {
-    title: '内容',
-    key: 'content',
-    type: JVxeTypes.textarea,
-    width: "300px",
-    placeholder: '请输入${title}',
-    defaultValue: '',
-  },
-  {
-    title: '记录时间',
-    key: 'recordTime',
-    type: JVxeTypes.datetime,
-    width: "200px",
-    placeholder: '请选择${title}',
-    defaultValue: '',
-  },
-  {
-    title: '操作人',
-    key: 'operator',
-    type: JVxeTypes.input,
-    width: "200px",
-    placeholder: '请输入${title}',
-    defaultValue: '',
-  },
-];

+ 31 - 19
jeecgboot-vue3/src/views/careerguidanceservice/CareerGuidanceServiceList.vue

@@ -4,16 +4,6 @@
     <div class="jeecg-basic-table-form-container">
       <a-form ref="formRef" @keyup.enter.native="searchQuery" :model="queryParam" :label-col="labelCol" :wrapper-col="wrapperCol">
         <a-row :gutter="24">
-          <a-col :lg="6" :md="8" :sm="24">
-            <a-form-item label="姓名" name="name">
-              <a-input placeholder="请输入姓名" v-model:value="queryParam.name" allow-clear></a-input>
-            </a-form-item>
-          </a-col>
-          <a-col :lg="6" :md="8" :sm="24">
-            <a-form-item label="是否已跟进" name="isFollowedUp">
-              <a-select v-model:value="queryParam.isFollowedUp" :options="followUpOptions" placeholder="请选择是否已跟进" allow-clear />
-            </a-form-item>
-          </a-col>
           <a-col :lg="6" :md="8" :sm="24">
             <a-form-item label="户口所在地" name="householdLocation">
               <a-tree-select
@@ -25,6 +15,16 @@
               />
             </a-form-item>
           </a-col>
+          <a-col :lg="6" :md="8" :sm="24">
+            <a-form-item label="姓名" name="name">
+              <a-input placeholder="请输入姓名" v-model:value="queryParam.name" allow-clear></a-input>
+            </a-form-item>
+          </a-col>
+          <a-col :lg="6" :md="8" :sm="24">
+            <a-form-item label="是否已跟进" name="isFollowedUp">
+              <a-select v-model:value="queryParam.isFollowedUp" :options="followUpOptions" placeholder="请选择是否已跟进" allow-clear />
+            </a-form-item>
+          </a-col>
           <a-col :lg="6" :md="8" :sm="24">
             <a-form-item>
               <a-space>
@@ -49,14 +49,17 @@
       </template>
       <!--bodyCell插槽-->
       <template #bodyCell="{ column, text, record }">
-        <template v-if="column.dataIndex === 'gender'">
+        <template v-if="column.dataIndex === 'householdLocation'">
+          {{ (xzqhMap[String(text).substring(0, 6)] || xzqhMap[text] || text || '').replace('广东省湛江市','') }}
+        </template>
+        <template v-else-if="column.dataIndex === 'gender'">
           {{ getDictText('Gender', text) || text }}
         </template>
         <template v-else-if="column.dataIndex === 'education'">
           {{ getDictText('Education', text) || text }}
         </template>
         <template v-else-if="column.dataIndex === 'age'">
-          {{ text < 0 ? '-' : text }}
+          {{ (text == null || text <= 0) ? '-' : text }}
         </template>
         <template v-else-if="column.dataIndex === 'isFollowedUp'">
           {{ followUpMap[text] || text }}
@@ -105,7 +108,7 @@
 </template>
 
 <script lang="ts" name="careerGuidanceService" setup>
-  import { reactive, ref } from 'vue';
+  import { reactive, ref, computed } from 'vue';
   import { BasicTable, TableAction } from '/@/components/Table';
   import { useListPage } from '/@/hooks/system/useListPage';
   import { useDict } from '/@/hooks/dictionary/useDict';
@@ -123,14 +126,23 @@
   const { createMessage } = useMessage();
   const userStore = useUserStore();
 
-  const { getDictText } = useDict(['Gender', 'Education']);
+  const { dictMap, getDictText } = useDict(['Gender', 'Education', 'XZQH']);
+
+  const xzqhMap = computed(() => {
+    const map: Record<string, string> = {};
+    const items = dictMap['XZQH'] || [];
+    for (const item of items) {
+      if (item.code) map[item.code] = item.name || item.label || item.code;
+    }
+    return map;
+  });
 
-  // 是否已跟进选项(yn字典)
+  // 是否已跟进选项(DB存储"是"/"否"
   const followUpOptions = [
-    { label: '是', value: '1' },
-    { label: '否', value: '0' },
+    { label: '是', value: '' },
+    { label: '否', value: '' },
   ];
-  const followUpMap: Record<string, string> = { '1': '是', '0': '否' };
+  const followUpMap: Record<string, string> = { '是': '是', '否': '否', '1': '是', '0': '否' };
 
   // 户口所在地 XZQH 树(湛江区县)
   const searchTreeData = ref<any[]>([]);
@@ -225,7 +237,7 @@
           moduleType: 'career_guidance',
           subject: msgForm.subject,
           content: msgForm.content,
-          targets: [{ targetType: 'personal', targetId: currentMsgRecord.value.id }],
+          targets: [{ targetType: 'personal', targetId: currentMsgRecord.value.personalId }],
         },
       });
       createMessage.success('消息发送成功');

+ 90 - 0
jeecgboot-vue3/src/views/careerguidanceservice/components/CareerGuidanceServiceDetail.vue

@@ -0,0 +1,90 @@
+<template>
+  <div class="career-guidance-detail" v-if="props.record">
+    <div class="detail-header">
+      <div class="header-avatar">
+        <a-avatar :size="64" style="background-color: #1890ff">
+          {{ props.record.name ? props.record.name.substring(0, 1) : '?' }}
+        </a-avatar>
+      </div>
+      <div class="header-info">
+        <div class="header-name">
+          <span class="name">{{ props.record.name || '-' }}</span>
+          <span class="gender">{{ getDictText('Gender', props.record.gender) || props.record.gender || '' }}</span>
+          <span class="age" v-if="props.record.age != null">{{ props.record.age }}岁</span>
+        </div>
+        <div class="header-meta">
+          <span v-if="props.record.education">{{ getDictText('Education', props.record.education) || props.record.education }}</span>
+          <span v-if="props.record.contactPhone"> | {{ props.record.contactPhone }}</span>
+        </div>
+      </div>
+    </div>
+
+    <a-divider style="margin: 12px 0" />
+
+    <a-descriptions bordered :column="2" size="small">
+      <a-descriptions-item label="姓名">{{ props.record.name || '-' }}</a-descriptions-item>
+      <a-descriptions-item label="证件号">{{ props.record.idCard || '-' }}</a-descriptions-item>
+      <a-descriptions-item label="性别">{{ getDictText('Gender', props.record.gender) || props.record.gender || '-' }}</a-descriptions-item>
+      <a-descriptions-item label="年龄">{{ (props.record.age != null && props.record.age > 0) ? props.record.age + '岁' : '-' }}</a-descriptions-item>
+      <a-descriptions-item label="学历">{{ getDictText('Education', props.record.education) || props.record.education || '-' }}</a-descriptions-item>
+      <a-descriptions-item label="户口所在地">{{ xzqhName || '-' }}</a-descriptions-item>
+      <a-descriptions-item label="联系电话">{{ props.record.contactPhone || '-' }}</a-descriptions-item>
+      <a-descriptions-item label="申请原因">{{ props.record.applicationReason || '-' }}</a-descriptions-item>
+      <a-descriptions-item label="申请日期">{{ formatDate(props.record.applicationDate) }}</a-descriptions-item>
+      <a-descriptions-item label="是否已跟进">{{ props.record.isFollowedUp || '-' }}</a-descriptions-item>
+      <a-descriptions-item label="最后通知时间">{{ formatDate(props.record.lastNotifyTime) }}</a-descriptions-item>
+    </a-descriptions>
+  </div>
+  <div v-else style="text-align: center; padding: 40px; color: #999">
+    暂无数据
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { computed } from 'vue';
+  import dayjs from 'dayjs';
+  import { useDict } from '/@/hooks/dictionary/useDict';
+
+  const props = defineProps({
+    record: { type: Object, default: () => null },
+  });
+
+  const { getDictText, getDictOptions } = useDict(['Gender', 'Education', 'XZQH']);
+
+  const xzqhMap = computed(() => {
+    const map: Record<string, string> = {};
+    for (const item of getDictOptions('XZQH')) {
+      map[item.value] = item.label;
+    }
+    return map;
+  });
+
+  const xzqhName = computed(() => {
+    const code = props.record?.householdLocation;
+    if (!code) return '';
+    const s = String(code);
+    const name = xzqhMap.value[s.substring(0, 6)] || xzqhMap.value[s] || '';
+    return name.replace('广东省湛江市', '') || code;
+  });
+
+  function formatDate(date: any) {
+    if (!date) return '-';
+    return dayjs(date).format('YYYY-MM-DD');
+  }
+</script>
+
+<style lang="less" scoped>
+  .career-guidance-detail {
+    padding: 16px;
+    .detail-header {
+      display: flex; align-items: center; gap: 16px;
+      .header-info { flex: 1;
+        .header-name { margin-bottom: 6px;
+          .name { font-size: 20px; font-weight: 600; margin-right: 8px; }
+          .gender, .age { font-size: 14px; color: #666; margin-right: 6px; }
+        }
+        .header-meta { font-size: 13px; color: #999; }
+      }
+    }
+  }
+</style>

+ 68 - 32
jeecgboot-vue3/src/views/careerguidanceservice/components/CareerGuidanceServiceModal.vue

@@ -1,41 +1,77 @@
 <template>
-  <BasicModal v-bind="$attrs" @register="registerModal" :title="title" :width="800" @ok="handleSubmit">
-    <CareerGuidanceServiceForm ref="formRef" :formDisabled="disableSubmit" :formBpm="false" />
-  </BasicModal>
+  <j-modal
+    :fullscreen="true"
+    :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }"
+    :title="title"
+    :visible="visible"
+    :width="900"
+    cancelText="关闭"
+    maxHeight="700px"
+    @cancel="handleCancel"
+    @ok="handleOk"
+  >
+    <CareerGuidanceServiceDetail v-if="isDetailMode" :record="currentRecord" />
+    <CareerGuidanceServiceForm v-else ref="registerForm" @ok="submitCallback" :formDisabled="disableSubmit" :formBpm="false"></CareerGuidanceServiceForm>
+    <template #footer>
+      <a-button @click="handleCancel">{{ isDetailMode ? '关闭' : '取消' }}</a-button>
+      <a-button v-if="!isDetailMode" type="primary" @click="handleOk">确认</a-button>
+    </template>
+  </j-modal>
 </template>
 
 <script lang="ts" setup>
-  import { ref, computed, unref } from 'vue';
-  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { defineExpose, nextTick, ref } from 'vue';
   import CareerGuidanceServiceForm from './CareerGuidanceServiceForm.vue';
+  import CareerGuidanceServiceDetail from './CareerGuidanceServiceDetail.vue';
+  import JModal from '/@/components/Modal/src/JModal/JModal.vue';
 
+  const title = ref<string>('');
+  const visible = ref<boolean>(false);
+  const disableSubmit = ref<boolean>(false);
+  const isDetailMode = ref<boolean>(false);
+  const currentRecord = ref<any>({});
+  const registerForm = ref();
   const emit = defineEmits(['register', 'success']);
-  const isUpdate = ref(true);
-  const disableSubmit = ref(false);
-  const formRef = ref();
-  const title = computed(() => (!unref(isUpdate) ? '新增' : unref(disableSubmit) ? '详情' : '编辑'));
-
-  const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
-    setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter });
-    isUpdate.value = !!data?.isUpdate;
-    disableSubmit.value = !!data?.disableSubmit;
-
-    if (unref(isUpdate) || unref(disableSubmit)) {
-      formRef.value.edit(data.record);
-    } else {
-      formRef.value.add();
-    }
-  });
-
-  async function handleSubmit() {
-    if (unref(disableSubmit)) {
-      closeModal();
-      return;
-    }
-    let success = await formRef.value.submitForm();
-    if (success) {
-      closeModal();
-      emit('success');
-    }
+
+  function add() {
+    isDetailMode.value = false;
+    title.value = '新增职业指导服务';
+    visible.value = true;
+    nextTick(() => registerForm.value?.add());
+  }
+
+  function edit(record) {
+    isDetailMode.value = false;
+    title.value = '编辑职业指导服务';
+    visible.value = true;
+    currentRecord.value = record;
+    nextTick(() => registerForm.value?.edit(record));
+  }
+
+  function detail(record) {
+    isDetailMode.value = true;
+    title.value = '职业指导服务详情';
+    visible.value = true;
+    currentRecord.value = record;
+  }
+
+  function handleOk() {
+    if (isDetailMode.value) { handleCancel(); return; }
+    registerForm.value?.submitForm();
   }
+
+  function submitCallback() {
+    handleCancel();
+    emit('success');
+  }
+
+  function handleCancel() {
+    visible.value = false;
+  }
+
+  defineExpose({ add, edit, detail, disableSubmit });
 </script>
+
+<style lang="less">
+  .jee-hidden { display: none !important; }
+</style>

+ 8 - 2
jeecgboot-vue3/src/views/employmentassistance/EmploymentAssistanceList.vue

@@ -43,6 +43,7 @@
 
     <BasicTable @register="registerTable" :rowSelection="rowSelection">
       <template #tableTitle>
+        <a-button preIcon="ant-design:export-outlined" type="primary" @click="onExportXls">导出</a-button>
       </template>
       <template #bodyCell="{ column, text }">
         <template v-if="column.dataIndex === 'householdLocation'">
@@ -98,7 +99,7 @@
   import { useListPage } from '/@/hooks/system/useListPage';
   import { useDict } from '/@/hooks/dictionary/useDict';
   import { columns } from './EmploymentAssistance.data';
-  import { list } from './EmploymentAssistance.api';
+  import { getExportUrl, list } from './EmploymentAssistance.api';
   import EmploymentAssistanceModal from './components/EmploymentAssistanceModal.vue';
   import EmploymentAssistanceServiceFollowList from './components/EmploymentAssistanceServiceFollowList.vue';
   import { useMessage } from '/@/hooks/web/useMessage';
@@ -162,7 +163,7 @@
 
   loadZhanjiangDistricts();
 
-  const { tableContext } = useListPage({
+  const { tableContext, onExportXls } = useListPage({
     tableProps: {
       title: '就业援助管理',
       api: list,
@@ -175,6 +176,11 @@
         return params;
       },
     },
+    exportConfig: {
+      name: '就业援助管理',
+      url: getExportUrl,
+      params: queryParam,
+    },
   });
   const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
   const labelCol = reactive({ xs: 24, sm: 6, md: 8, xl: 6, xxl: 6 });

+ 8 - 2
jeecgboot-vue3/src/views/employmentregistration/EmploymentRegistrationList.vue

@@ -42,6 +42,7 @@
 
     <BasicTable @register="registerTable" :rowSelection="rowSelection">
       <template #tableTitle>
+        <a-button preIcon="ant-design:export-outlined" type="primary" @click="onExportXls">导出</a-button>
       </template>
       <template #bodyCell="{ column, text }">
         <template v-if="column.dataIndex === 'householdLocation'">
@@ -89,7 +90,7 @@
   import { useListPage } from '/@/hooks/system/useListPage';
   import { useDict } from '/@/hooks/dictionary/useDict';
   import { columns } from './EmploymentRegistration.data';
-  import { list } from './EmploymentRegistration.api';
+  import { getExportUrl, list } from './EmploymentRegistration.api';
   import EmploymentRegistrationModal from './components/EmploymentRegistrationModal.vue';
   import EmploymentRegistrationServiceFollowList from './components/EmploymentRegistrationServiceFollowList.vue';
   import { useMessage } from '/@/hooks/web/useMessage';
@@ -147,7 +148,7 @@
 
   loadZhanjiangDistricts();
 
-  const { tableContext } = useListPage({
+  const { tableContext, onExportXls } = useListPage({
     tableProps: {
       title: '就业登记管理',
       api: list,
@@ -160,6 +161,11 @@
         return params;
       },
     },
+    exportConfig: {
+      name: '就业登记管理',
+      url: getExportUrl,
+      params: queryParam,
+    },
   });
   const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
   const labelCol = reactive({ xs: 24, sm: 6, md: 8, xl: 6, xxl: 6 });

+ 0 - 0
jeecgboot-vue3/src/views/focuspersonnel/FocusPersonnelList.vue


Некоторые файлы не были показаны из-за большого количества измененных файлов