From d65f8d0d5c74e61af8f890a9e1870df71c47fd1d Mon Sep 17 00:00:00 2001
From: chenshaohua <635616957@qq.com>
Date: Fri, 24 Jul 2026 17:28:06 +0800
Subject: [PATCH 1/8] =?UTF-8?q?=E5=B2=97=E4=BD=8D=E8=A1=A8=E6=B7=BB?=
=?UTF-8?q?=E5=8A=A0=E5=B2=97=E4=BD=8D=E7=89=B9=E5=BE=81=E5=AD=97=E6=AE=B5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/main/java/com/ruoyi/cms/domain/ESJobDocument.java | 3 +++
.../src/main/java/com/ruoyi/cms/domain/Job.java | 3 +++
.../src/main/resources/mapper/app/JobMapper.xml | 7 +++++--
3 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/ESJobDocument.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/ESJobDocument.java
index 62003a9..6e1c0bc 100644
--- a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/ESJobDocument.java
+++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/ESJobDocument.java
@@ -173,4 +173,7 @@ public class ESJobDocument
@ApiModelProperty("重点人群-1农民工、2团场职工、3失业人员、4零就业家庭、5零工人员、6退役军人")
private String keyPopulations;
+
+ @ApiModelProperty("岗位特征:(行政、后勤、管理、技术、销售等字典job_feature添加)")
+ private String jobFeature;
}
diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/Job.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/Job.java
index 61aa3ab..a8b2fd3 100644
--- a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/Job.java
+++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/Job.java
@@ -254,6 +254,9 @@ public class Job extends BaseEntity
@ApiModelProperty("状态 0上架,1下架")
private String jobStatus;
+ @ApiModelProperty("岗位特征:(行政、后勤、管理、技术、销售等字典job_feature添加)")
+ private String jobFeature;
+
@TableField(exist = false)
@ApiModelProperty("户外招聘会岗位审核状态")
private String fairReviewStatus;
diff --git a/ruoyi-bussiness/src/main/resources/mapper/app/JobMapper.xml b/ruoyi-bussiness/src/main/resources/mapper/app/JobMapper.xml
index a396ce3..f793cd7 100644
--- a/ruoyi-bussiness/src/main/resources/mapper/app/JobMapper.xml
+++ b/ruoyi-bussiness/src/main/resources/mapper/app/JobMapper.xml
@@ -45,6 +45,7 @@
+
@@ -94,6 +95,7 @@
+
@@ -155,7 +157,7 @@
job_location_area_code, posting_date, vacancies, latitude, longitude, "view", company_id,
is_hot, is_urgent, apply_num, description, is_publish, data_source, job_url, remark, del_flag,
create_by, create_time, row_id, job_category,job_type,job_address,job_status,is_key_populations,
- key_populations
+ key_populations,job_feature
) VALUES
(
@@ -164,7 +166,8 @@
#{job.vacancies}, #{job.latitude}, #{job.longitude}, #{job.view}, #{job.companyId},
#{job.isHot}, #{job.isUrgent}, #{job.applyNum}, #{job.description}, #{job.isPublish}, #{job.dataSource},
#{job.jobUrl}, #{job.remark}, #{job.delFlag}, #{job.createBy}, #{job.createTime},
- #{job.rowId}, #{job.jobCategory},#{job.jobType},#{job.jobAddress},#{job.jobStatus},#{job.isKeyPopulations},#{job.keyPopulations}
+ #{job.rowId}, #{job.jobCategory},#{job.jobType},#{job.jobAddress},#{job.jobStatus},#{job.isKeyPopulations},
+ #{job.keyPopulations},#{job.jobFeature}
)
From 49d138b6c44b3228356f5670205b507bc62fc4b7 Mon Sep 17 00:00:00 2001
From: chenshaohua <635616957@qq.com>
Date: Fri, 24 Jul 2026 18:03:39 +0800
Subject: [PATCH 2/8] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B5=8F=E8=A7=88?=
=?UTF-8?q?=E8=AE=B0=E5=BD=95=E7=9A=84=E8=AE=B0=E5=BD=95=E6=97=B6=E9=97=B4?=
=?UTF-8?q?=E5=92=8C=E6=AC=A1=E6=95=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../controller/app/HrUserBehaviorRecordController.java | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/app/HrUserBehaviorRecordController.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/app/HrUserBehaviorRecordController.java
index 1b6fd42..daefd3f 100644
--- a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/app/HrUserBehaviorRecordController.java
+++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/app/HrUserBehaviorRecordController.java
@@ -59,13 +59,8 @@ public class HrUserBehaviorRecordController extends BaseController {
record.setViewNum(1);
hrUserBehaviorRecordService.save(record);
} else {
- if (record.getViewDuration() > 0) {
- // 页面离开上报:累加时长
- exist.setViewDuration(exist.getViewDuration() + record.getViewDuration());
- } else {
- // 页面进入上报:次数+1
- exist.setViewNum(exist.getViewNum() + 1);
- }
+ exist.setViewNum(exist.getViewNum() + 1);
+ exist.setViewDuration(exist.getViewDuration() + record.getViewDuration());
hrUserBehaviorRecordService.updateById(exist);
}
return success();
From ee04c6d8e2fe059962c13d8971050f1c9c559377 Mon Sep 17 00:00:00 2001
From: chenshaohua <635616957@qq.com>
Date: Fri, 24 Jul 2026 18:46:32 +0800
Subject: [PATCH 3/8] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=90=8E=E7=AB=AF?=
=?UTF-8?q?=E4=BC=81=E4=B8=9A=E6=B5=8F=E8=A7=88=E6=B1=82=E8=81=8C=E8=80=85?=
=?UTF-8?q?=E7=AE=80=E5=8E=86=E6=8E=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../CmsHrUserBehaviorRecordController.java | 74 +++++++++++++++++++
1 file changed, 74 insertions(+)
create mode 100644 ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/cms/CmsHrUserBehaviorRecordController.java
diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/cms/CmsHrUserBehaviorRecordController.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/cms/CmsHrUserBehaviorRecordController.java
new file mode 100644
index 0000000..097ce26
--- /dev/null
+++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/cms/CmsHrUserBehaviorRecordController.java
@@ -0,0 +1,74 @@
+package com.ruoyi.cms.controller.cms;
+
+import com.ruoyi.cms.domain.msg.HrUserBehaviorRecord;
+import com.ruoyi.cms.service.msg.HrUserBehaviorRecordService;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.SiteSecurityUtils;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/cms/behavior")
+@Api(tags = "后台:行为记录")
+public class CmsHrUserBehaviorRecordController extends BaseController {
+
+ @Autowired
+ private HrUserBehaviorRecordService hrUserBehaviorRecordService;
+
+ /**
+ * 查询行为记录列表
+ */
+ @GetMapping("/list")
+ @PreAuthorize("@ss.hasPermi('cms:behavior:list')")
+ @ApiOperation("查询行为记录列表")
+ public TableDataInfo list(HrUserBehaviorRecord hrUserBehaviorRecord) {
+ if (!SiteSecurityUtils.isLogin()) {
+ throw new ServiceException("用户未登录");
+ }
+ Long userId = SiteSecurityUtils.getUserId();
+ boolean admin = SiteSecurityUtils.isAdmin(userId);
+ if (!admin) {
+ hrUserBehaviorRecord.setActorId(userId);
+ } else {
+ hrUserBehaviorRecord.setActorId(null);
+ }
+ startPage();
+ List list = hrUserBehaviorRecordService.selectBehaviorRecordList(hrUserBehaviorRecord);
+ return getDataTable(list);
+ }
+
+ /**
+ * 上报浏览行为/投递行为
+ */
+ @ApiOperation("上报浏览行为/投递行为")
+ @PreAuthorize("@ss.hasPermi('cms:behavior:report')")
+ @Log(title = "用户培训经历", businessType = BusinessType.INSERT)
+ @PostMapping("/report")
+ public AjaxResult reportBehavior(@RequestBody HrUserBehaviorRecord record) {
+ HrUserBehaviorRecord exist = hrUserBehaviorRecordService.getExistViewRecord(
+ record.getActorType(),
+ record.getActorId(),
+ record.getTargetType(),
+ record.getTargetId()
+ );
+ if (exist == null) {
+ record.setViewNum(1);
+ hrUserBehaviorRecordService.save(record);
+ } else {
+ exist.setViewNum(exist.getViewNum() + 1);
+ exist.setViewDuration(exist.getViewDuration() + record.getViewDuration());
+ hrUserBehaviorRecordService.updateById(exist);
+ }
+ return success();
+ }
+}
\ No newline at end of file
From 69ddabd0418320196de93a2b1870bb91d3deffeb Mon Sep 17 00:00:00 2001
From: francis-fh
Date: Fri, 24 Jul 2026 19:27:49 +0800
Subject: [PATCH 4/8] =?UTF-8?q?=E7=94=A8=E6=88=B7=E4=BF=A1=E6=81=AF?=
=?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=A2=9E=E5=8A=A0=E5=9F=B9=E8=AE=AD=E7=BB=8F?=
=?UTF-8?q?=E5=8E=86=E3=80=81=E6=95=99=E8=82=B2=E7=BB=8F=E5=8E=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../com/ruoyi/cms/service/impl/AppUserServiceImpl.java | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/impl/AppUserServiceImpl.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/impl/AppUserServiceImpl.java
index c074148..6b31961 100644
--- a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/impl/AppUserServiceImpl.java
+++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/impl/AppUserServiceImpl.java
@@ -598,6 +598,14 @@ public class AppUserServiceImpl extends ServiceImpl imple
List skillList=appSkillMapper.getList(parmsk);
appUser.setAppSkillsList(skillList);
+ //查询教育经历
+ List educationsList=appUserEducationMapper.selectEducationListByUserId(appUser.getUserId());
+ appUser.setEducationsList(educationsList);
+
+ //查询培训经历
+ List trainsList=appUserTrainMapper.selectTrainListByUserId(appUser.getUserId());
+ appUser.setTrainsList(trainsList);
+
//查询附件
File fileParm=new File();
fileParm.setBussinessid(appUser.getUserId());
From c8fc1c8f9e3141458fb7b7cb341f0071de20decd Mon Sep 17 00:00:00 2001
From: chenshaohua <635616957@qq.com>
Date: Fri, 24 Jul 2026 20:27:21 +0800
Subject: [PATCH 5/8] =?UTF-8?q?=E4=BF=AE=E6=94=B9pc=E7=AB=AF=E6=8E=A5?=
=?UTF-8?q?=E5=8F=A3=E6=B5=8F=E8=A7=88=E8=AE=B0=E5=BD=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../cms/controller/cms/CmsHrUserBehaviorRecordController.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/cms/CmsHrUserBehaviorRecordController.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/cms/CmsHrUserBehaviorRecordController.java
index 097ce26..abedcbc 100644
--- a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/cms/CmsHrUserBehaviorRecordController.java
+++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/cms/CmsHrUserBehaviorRecordController.java
@@ -51,8 +51,8 @@ public class CmsHrUserBehaviorRecordController extends BaseController {
* 上报浏览行为/投递行为
*/
@ApiOperation("上报浏览行为/投递行为")
- @PreAuthorize("@ss.hasPermi('cms:behavior:report')")
- @Log(title = "用户培训经历", businessType = BusinessType.INSERT)
+ //@PreAuthorize("@ss.hasPermi('cms:behavior:report')")
+ @Log(title = "浏览行为", businessType = BusinessType.INSERT)
@PostMapping("/report")
public AjaxResult reportBehavior(@RequestBody HrUserBehaviorRecord record) {
HrUserBehaviorRecord exist = hrUserBehaviorRecordService.getExistViewRecord(
From 5e9c776abdbcf555b2ceb6cee0414377e091d157 Mon Sep 17 00:00:00 2001
From: chenshaohua <635616957@qq.com>
Date: Sat, 25 Jul 2026 18:44:20 +0800
Subject: [PATCH 6/8] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=8E=A8=E8=8D=90?=
=?UTF-8?q?=E5=B2=97=E4=BD=8D=E5=AE=8C=E6=88=90=E4=B8=80=E4=B8=8B=E5=87=A0?=
=?UTF-8?q?=E7=82=B9=EF=BC=9A=201.=E6=A0=B9=E6=8D=AE=E5=B7=A5=E4=BD=9C?=
=?UTF-8?q?=E7=BB=8F=E9=AA=8C=E6=8E=A8=E8=8D=90=E7=9B=B8=E5=85=B3=E7=9A=84?=
=?UTF-8?q?=E5=B2=97=E4=BD=8D4.0=E5=88=86=202.=E6=A0=B9=E6=8D=AE=E6=8A=95?=
=?UTF-8?q?=E9=80=92=E8=AE=B0=E5=BD=95=E6=8E=A8=E8=8D=90=E7=9B=B8=E5=85=B3?=
=?UTF-8?q?=E7=9A=84=E5=B2=97=E4=BD=8D3.0=E5=88=86=203.=E6=A0=B9=E6=8D=AE?=
=?UTF-8?q?=E6=94=B6=E8=97=8F=E8=AE=B0=E5=BD=95=E6=8E=A8=E8=8D=90=E7=9B=B8?=
=?UTF-8?q?=E5=85=B3=E5=B2=97=E4=BD=8D2.0=E5=88=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../ruoyi/cms/domain/query/ESJobSearch.java | 11 +-
.../com/ruoyi/cms/mapper/JobApplyMapper.java | 2 +
.../ruoyi/cms/mapper/JobCollectionMapper.java | 3 +
.../cms/service/impl/ESJobSearchImpl.java | 78 ++++++--
.../cms/util/remommend/JobRecommendUtil.java | 177 ++++++++++++++++++
.../resources/mapper/app/JobApplyMapper.xml | 6 +
.../mapper/app/JobCollectionMapper.xml | 7 +
7 files changed, 270 insertions(+), 14 deletions(-)
create mode 100644 ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/remommend/JobRecommendUtil.java
diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/query/ESJobSearch.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/query/ESJobSearch.java
index e8c162c..8c671c8 100644
--- a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/query/ESJobSearch.java
+++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/query/ESJobSearch.java
@@ -1,7 +1,7 @@
package com.ruoyi.cms.domain.query;
-import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.cms.domain.Job;
+import com.ruoyi.common.core.domain.entity.UserWorkExperiences;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@@ -63,4 +63,13 @@ public class ESJobSearch extends Job
//结束时间
private Date endDate;
+
+ /**岗位收藏集合*/
+ private List collectNames;
+
+ /**岗位申请集合*/
+ private List applyNames;
+
+ /**工作经历*/
+ private List userWorkExperiences;
}
diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/mapper/JobApplyMapper.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/mapper/JobApplyMapper.java
index 6927d6d..25182ea 100644
--- a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/mapper/JobApplyMapper.java
+++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/mapper/JobApplyMapper.java
@@ -42,4 +42,6 @@ public interface JobApplyMapper extends BaseMapper
int updateJobZphApply(JobApply jobApply);
JobApply selectByJobIdAndUserId(@Param("jobId") Long jobId, @Param("userId") Long userId);
+
+ List queryApplyNames(@Param("userId") Long userId);
}
diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/mapper/JobCollectionMapper.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/mapper/JobCollectionMapper.java
index b8184aa..0672ae6 100644
--- a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/mapper/JobCollectionMapper.java
+++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/mapper/JobCollectionMapper.java
@@ -5,6 +5,7 @@ import java.util.List;
import com.ruoyi.cms.domain.Job;
import com.ruoyi.cms.domain.JobCollection;
+import org.apache.ibatis.annotations.Param;
/**
* 用户岗位收藏Mapper接口
@@ -24,4 +25,6 @@ public interface JobCollectionMapper extends BaseMapper
List collectionJob(Long userId);
public List selectJobCollectionListJob(JobCollection jobCollection);
+
+ List queryCollectNames(@Param("userId") Long userId);
}
diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/impl/ESJobSearchImpl.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/impl/ESJobSearchImpl.java
index 77b992b..037ca8c 100644
--- a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/impl/ESJobSearchImpl.java
+++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/service/impl/ESJobSearchImpl.java
@@ -2,21 +2,20 @@ package com.ruoyi.cms.service.impl;
import com.alibaba.fastjson.JSON;
import com.ruoyi.cms.domain.vo.CompanyVo;
-import com.ruoyi.cms.mapper.AppUserBlockCompanyMapper;
-import com.ruoyi.cms.mapper.CompanyMapper;
+import com.ruoyi.cms.mapper.*;
import com.ruoyi.cms.service.ICompanyService;
+import com.ruoyi.cms.util.remommend.JobRecommendUtil;
import com.ruoyi.common.core.domain.entity.AppUser;
import com.ruoyi.cms.domain.ESJobDocument;
import com.ruoyi.cms.domain.Job;
import com.ruoyi.cms.domain.query.ESJobSearch;
import com.ruoyi.cms.mapper.es.EsJobDocumentMapper;
-import com.ruoyi.cms.mapper.JobMapper;
-import com.ruoyi.cms.mapper.JobTitleMapper;
import com.ruoyi.cms.service.IESJobSearchService;
import com.ruoyi.common.core.domain.entity.JobTitle;
import com.ruoyi.cms.util.ListUtil;
import com.ruoyi.cms.util.StringUtil;
import com.ruoyi.common.core.domain.entity.Company;
+import com.ruoyi.common.core.domain.entity.UserWorkExperiences;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.utils.DateUtils;
@@ -62,6 +61,12 @@ public class ESJobSearchImpl implements IESJobSearchService
private RedisCache redisCache;
@Autowired
private AppUserBlockCompanyMapper appUserBlockCompanyMapper;
+ @Autowired
+ private JobApplyMapper jobApplyMapper;
+ @Autowired
+ private JobCollectionMapper jobCollectionMapper;
+ @Autowired
+ private UserWorkExperiencesMapper userWorkExperiencesMapper;
// 锁的key(唯一标识ES索引初始化)
private static final String ES_INIT_LOCK_KEY = "es:job_document:init:lock";
@@ -245,15 +250,12 @@ public class ESJobSearchImpl implements IESJobSearchService
//查询
if(SiteSecurityUtils.isLogin()){
AppUser appUser = appUserService.selectAppUserByUserId(SiteSecurityUtils.getUserId());
- List jobs=appUserBlockCompanyMapper.selectBlockCompanyIdsByUserIdJobs(appUser.getUserId());
- if (jobs != null) {
- jobIds.addAll(jobs.stream().distinct().collect(Collectors.toList()));
- }
if (!StringUtil.isEmptyOrNull(esJobSearch.getCode())) {
newSearch.setCode(esJobSearch.getCode());
}
+ //求职者
if (!isCompanyUser) {
- setJobSeekerParams(appUser, esJobSearch, newSearch);
+ setJobSeekerParams(appUser, esJobSearch, newSearch,jobIds);
}
}
@@ -327,7 +329,7 @@ public class ESJobSearchImpl implements IESJobSearchService
* @param esJobSearch
* @param newSearch
*/
- private void setJobSeekerParams(AppUser appUser, ESJobSearch esJobSearch, ESJobSearch newSearch) {
+ private void setJobSeekerParams(AppUser appUser, ESJobSearch esJobSearch, ESJobSearch newSearch,List jobIds) {
if(!ListUtil.isEmptyOrNull(appUser.getJobTitle())){
List jobTitle = appUser.getJobTitle();
newSearch.setJobTitle(String.join(",", jobTitle));
@@ -348,9 +350,10 @@ public class ESJobSearchImpl implements IESJobSearchService
if(!StringUtil.isEmptyOrNull(esJobSearch.getArea())){
newSearch.setArea(esJobSearch.getArea());
}
- if(!StringUtil.isEmptyOrNull(appUser.getWorkExperience())){
- newSearch.setExperience(appUser.getWorkExperience());
- }
+ //影响后面按照经验打分,先注释
+// if(!StringUtil.isEmptyOrNull(appUser.getWorkExperience())){
+// newSearch.setExperience(appUser.getWorkExperience());
+// }
if(!StringUtil.isEmptyOrNull(esJobSearch.getExperience())){
newSearch.setExperience(esJobSearch.getExperience());
}
@@ -379,6 +382,22 @@ public class ESJobSearchImpl implements IESJobSearchService
if(!StringUtil.isEmptyOrNull(esJobSearch.getCompanyNature())){
newSearch.setCompanyNature(esJobSearch.getCompanyNature());
}
+ //屏蔽企业集合
+ List jobs=appUserBlockCompanyMapper.selectBlockCompanyIdsByUserIdJobs(appUser.getUserId());
+ if (jobs != null) {
+ jobIds.addAll(jobs.stream().distinct().collect(Collectors.toList()));
+ }
+ //查询投递集合
+ List applyNames=jobApplyMapper.queryApplyNames(appUser.getUserId());
+ newSearch.setApplyNames(applyNames);
+ //查询收藏集合
+ List collectNames=jobCollectionMapper.queryCollectNames(appUser.getUserId());
+ newSearch.setCollectNames(collectNames);
+ //工作经历集合
+ UserWorkExperiences expQuery = new UserWorkExperiences();
+ expQuery.setUserId(appUser.getUserId());
+ List userWorkExperiences=userWorkExperiencesMapper.getWorkExperiencesList(expQuery);
+ newSearch.setUserWorkExperiences(userWorkExperiences);
}
/**
@@ -644,6 +663,13 @@ public class ESJobSearchImpl implements IESJobSearchService
if(!StringUtil.isEmptyOrNull(esJobSearch.getCompanyName())){
wrapper.and(x->x.like(ESJobDocument::getCompanyName,esJobSearch.getCompanyName()));
}
+ // 1.简历工作经历(最高权重)
+ JobRecommendUtil.appendWorkExpMatch(wrapper, esJobSearch.getUserWorkExperiences());
+ //投递记录
+ JobRecommendUtil.appendJobTitleMatch(wrapper, esJobSearch.getApplyNames(), JobRecommendUtil.APPLY_TITLE_BOOST);
+ //收藏记录
+ JobRecommendUtil.appendJobTitleMatch(wrapper, esJobSearch.getCollectNames(), JobRecommendUtil.COLLECT_TITLE_BOOST);
+
//按时间段来查询数据
Date startDate = esJobSearch.getStartDate();
Date endDate = esJobSearch.getEndDate();
@@ -1030,6 +1056,17 @@ public class ESJobSearchImpl implements IESJobSearchService
if(!StringUtil.isEmptyOrNull(appUser.getSalaryMin())){
newSearch.setMinSalary(Long.valueOf(appUser.getSalaryMin()));
}
+ //查询投递集合
+ List applyNames=jobApplyMapper.queryApplyNames(appUser.getUserId());
+ newSearch.setApplyNames(applyNames);
+ //查询收藏集合
+ List collectNames=jobCollectionMapper.queryCollectNames(appUser.getUserId());
+ newSearch.setCollectNames(collectNames);
+ //工作经历集合
+ UserWorkExperiences expQuery = new UserWorkExperiences();
+ expQuery.setUserId(appUser.getUserId());
+ List userWorkExperiences=userWorkExperiencesMapper.getWorkExperiencesList(expQuery);
+ newSearch.setUserWorkExperiences(userWorkExperiences);
}
// 请求参数始终优先于用户资料偏好
@@ -1263,4 +1300,19 @@ public class ESJobSearchImpl implements IESJobSearchService
return -1;
}
}
+
+ /**
+ * 岗位名称推荐
+ * @param wrapper
+ * @param nameList
+ * @param boost
+ */
+ /*private void appendJobTitleMatch(LambdaEsQueryWrapper wrapper, List nameList, Float boost) {
+ if (CollUtil.isEmpty(nameList)) {
+ return;
+ }
+ for (String title : nameList) {
+ wrapper.should(w -> w.match(ESJobDocument::getJobTitle, title, boost));
+ }
+ }*/
}
diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/remommend/JobRecommendUtil.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/remommend/JobRecommendUtil.java
new file mode 100644
index 0000000..60916a7
--- /dev/null
+++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/util/remommend/JobRecommendUtil.java
@@ -0,0 +1,177 @@
+package com.ruoyi.cms.util.remommend;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import com.ruoyi.cms.domain.ESJobDocument;
+import com.ruoyi.common.core.domain.entity.UserWorkExperiences;
+import org.dromara.easyes.core.conditions.select.LambdaEsQueryWrapper;
+
+import java.time.LocalDate;
+import java.time.Period;
+import java.util.*;
+
+/**
+ * 岗位推荐查询条件工具类
+ * 权重规则:
+ * 简历岗位名称(4/6/7) > 投递名称(3) > 收藏名称(2)
+ * 经验区间字典:
+ * 0=经验不限,1=实习生,2=应届毕业生,3=1年以下,4=1-3年,5=3-5年,6=5-10年,7=10年以上
+ */
+public class JobRecommendUtil {
+
+ // =====================权重常量,集中调参=====================
+ /** 工作经历岗位名称:任职不足1年 */
+ public static final Float POS_BOOST_SHORT = 4F;
+ /** 工作经历岗位名称:1~3年 */
+ public static final Float POS_BOOST_MID = 6F;
+ /** 工作经历岗位名称:>=3年 */
+ public static final Float POS_BOOST_LONG = 7F;
+
+ /** 投递岗位名称权重 */
+ public static final Float APPLY_TITLE_BOOST = 3F;
+ /** 收藏岗位名称权重 */
+ public static final Float COLLECT_TITLE_BOOST = 2F;
+
+ /** 经验区间匹配权重 */
+ public static final Float EXP_MATCH_SELF = 3F;
+ public static final Float EXP_MATCH_LOW1 = 2F;
+ public static final Float EXP_MATCH_LOWER = 1F;
+
+ // =====================基础计算方法=====================
+
+ /**
+ * 计算单条工作经历时长(月)
+ * @param startDateStr 开始年月 yyyy-MM
+ * @param endDateStr 结束年月 yyyy-MM;null代表至今在职
+ */
+ public static int calcWorkMonth(String startDateStr, String endDateStr) {
+ if (StrUtil.isBlank(startDateStr)) {
+ return 0;
+ }
+ LocalDate start = LocalDate.parse(startDateStr + "-01");
+ LocalDate end;
+ if (StrUtil.isBlank(endDateStr)) {
+ end = LocalDate.now();
+ } else {
+ end = LocalDate.parse(endDateStr + "-01");
+ }
+ Period period = Period.between(start, end);
+ return period.getYears() * 12 + period.getMonths();
+ }
+
+ /**
+ * 计算全部工作经历总月数(简单累加,不处理时间重叠)
+ */
+ public static int calcTotalWorkMonth(List expList) {
+ if (CollUtil.isEmpty(expList)) {
+ return 0;
+ }
+ return expList.stream()
+ .mapToInt(exp -> calcWorkMonth(exp.getStartDate(), exp.getEndDate()))
+ .sum();
+ }
+
+ /**
+ * 总工作月数 → 转换经验编码
+ */
+ public static Integer convertMonthToExpCode(int totalMonth) {
+ if (totalMonth <= 0) {
+ // 无工作经历:应届生
+ return 2;
+ }
+ double year = totalMonth / 12.0;
+ if (year < 1) {
+ return 3;
+ } else if (year <= 3) {
+ return 4;
+ } else if (year <= 5) {
+ return 5;
+ } else if (year <= 10) {
+ return 6;
+ } else {
+ return 7;
+ }
+ }
+
+ /**
+ * 追加简历工作经历匹配条件
+ * 1.岗位名称匹配
+ * 2.经验等级匹配
+ */
+ public static void appendWorkExpMatch(LambdaEsQueryWrapper wrapper, List expList) {
+ if (CollUtil.isEmpty(expList)) {
+ return;
+ }
+
+ Map posBoostMap = new HashMap<>();
+ for (UserWorkExperiences exp : expList) {
+ String position = exp.getPosition();
+ if (StrUtil.isBlank(position)) {
+ continue;
+ }
+ int month = calcWorkMonth(exp.getStartDate(), exp.getEndDate());
+ Float boost;
+ if (month >= 36) {
+ boost = POS_BOOST_LONG;
+ } else if (month >= 12) {
+ boost = POS_BOOST_MID;
+ } else {
+ boost = POS_BOOST_SHORT;
+ }
+ posBoostMap.compute(position, (k, old) -> old == null ? boost : Math.max(old, boost));
+ }
+
+ // 名称拼接,带上对应权重
+ for (Map.Entry entry : posBoostMap.entrySet()) {
+ String title = entry.getKey();
+ Float boost = entry.getValue();
+ wrapper.should(w -> w.match("jobTitle", title, boost));
+ }
+
+ // 经验区间梯度加权
+ int totalMonth = calcTotalWorkMonth(expList);
+ Integer userExpCode = convertMonthToExpCode(totalMonth);
+ appendExpCodeMatch(wrapper, userExpCode);
+ }
+
+ /**
+ * 根据用户经验编码,追加经验等级should条件
+ */
+ private static void appendExpCodeMatch(LambdaEsQueryWrapper wrapper, Integer userExpCode) {
+ // 应届生 / 实习生
+ if (userExpCode <= 2) {
+ wrapper.should(w -> w.match("experience_int", 0, EXP_MATCH_SELF));
+ wrapper.should(w -> w.match("experience_int", 1, EXP_MATCH_SELF));
+ wrapper.should(w -> w.match("experience_int", 2, EXP_MATCH_SELF));
+ return;
+ }
+ int selfCode = userExpCode;
+ int lower1 = selfCode - 1;
+ List lowerAll = new ArrayList<>();
+ for (int i = 0; i < lower1; i++) {
+ lowerAll.add(i);
+ }
+ // 完全匹配自身经验区间,最高权重
+ wrapper.should(w -> w.match("experience_int", selfCode, EXP_MATCH_SELF));
+ // 低一档经验,次权重
+ wrapper.should(w -> w.match("experience_int", lower1, EXP_MATCH_LOW1));
+ // 低两档及经验不限,最低权重
+ for (Integer code : lowerAll) {
+ wrapper.should(w -> w.match("experience_int", code, EXP_MATCH_LOWER));
+ }
+ }
+
+ /**
+ * 批量追加岗位名称match(投递/收藏通用)
+ * @param nameList 岗位名称集合
+ * @param boost 权重
+ */
+ public static void appendJobTitleMatch(LambdaEsQueryWrapper wrapper, List nameList, Float boost) {
+ if (CollUtil.isEmpty(nameList)) {
+ return;
+ }
+ for (String title : nameList) {
+ wrapper.should(w -> w.match(ESJobDocument::getJobTitle, title, boost));
+ }
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-bussiness/src/main/resources/mapper/app/JobApplyMapper.xml b/ruoyi-bussiness/src/main/resources/mapper/app/JobApplyMapper.xml
index b12efb0..5290572 100644
--- a/ruoyi-bussiness/src/main/resources/mapper/app/JobApplyMapper.xml
+++ b/ruoyi-bussiness/src/main/resources/mapper/app/JobApplyMapper.xml
@@ -200,4 +200,10 @@
+
+
\ No newline at end of file
diff --git a/ruoyi-bussiness/src/main/resources/mapper/app/JobCollectionMapper.xml b/ruoyi-bussiness/src/main/resources/mapper/app/JobCollectionMapper.xml
index da06995..5a12285 100644
--- a/ruoyi-bussiness/src/main/resources/mapper/app/JobCollectionMapper.xml
+++ b/ruoyi-bussiness/src/main/resources/mapper/app/JobCollectionMapper.xml
@@ -44,4 +44,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
and a.user_id = #{userId}
+
+
\ No newline at end of file
From b4f7b63e6d7b68d3b53b346c4d66dc5ccb928593 Mon Sep 17 00:00:00 2001
From: lapuda <577732344@qq.com>
Date: Sat, 25 Jul 2026 22:17:10 +0800
Subject: [PATCH 7/8] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=8B=9B=E8=81=98?=
=?UTF-8?q?=E4=BC=9A=E8=AF=A6=E6=83=85=E5=9B=BE=E7=89=87=E4=B8=8E=E5=B1=95?=
=?UTF-8?q?=E4=BD=8D=E6=95=B0=E6=8D=AE=E5=A5=91=E7=BA=A6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../BUG-001-public-title-year-mismatch.md | 63 ++++++++++++++
...G-002-public-filter-field-accessibility.md | 38 +++++++++
...BUG-003-online-detail-host-unit-missing.md | 46 ++++++++++
...BUG-004-enterprise-filter-accessibility.md | 48 +++++++++++
...enterprise-outdoor-filter-accessibility.md | 48 +++++++++++
...-enterprise-outdoor-image-mixed-content.md | 61 ++++++++++++++
...terprise-outdoor-signup-no-confirmation.md | 64 ++++++++++++++
...rprise-outdoor-job-dialog-accessibility.md | 50 +++++++++++
.../BUG-009-outdoor-booth-count-mismatch.md | 55 ++++++++++++
...rise-crosscity-statistics-accessibility.md | 43 ++++++++++
...e-crosscity-region-dialog-accessibility.md | 49 +++++++++++
.../BUG-012-admin-filter-accessibility.md | 50 +++++++++++
.../bugs/BUG-013-admin-avatar-404.md | 53 ++++++++++++
...-public-admin-jobfair-type-inconsistent.md | 53 ++++++++++++
.../BUG-015-admin-job-raw-dictionary-codes.md | 57 +++++++++++++
...-016-admin-outdoor-filter-accessibility.md | 51 +++++++++++
...in-outdoor-company-filter-accessibility.md | 51 +++++++++++
...-admin-outdoor-attendance-accessibility.md | 52 ++++++++++++
...admin-outdoor-unsubscribe-accessibility.md | 50 +++++++++++
...UG-020-admin-outdoor-jobs-accessibility.md | 45 ++++++++++
...021-admin-outdoor-attendee-autocomplete.md | 45 ++++++++++
...-022-admin-outdoor-device-accessibility.md | 48 +++++++++++
...UG-023-admin-venue-filter-accessibility.md | 51 +++++++++++
...24-admin-venue-add-dialog-accessibility.md | 46 ++++++++++
...025-admin-statistics-date-accessibility.md | 45 ++++++++++
...-admin-institution-filter-accessibility.md | 51 +++++++++++
...n-crosscity-region-dialog-accessibility.md | 55 ++++++++++++
...region-selector-not-keyboard-accessible.md | 49 +++++++++++
...-crosscity-region-selection-no-job-loop.md | 44 ++++++++++
.../job-fair/2026-07-25/bugs/FIX-STATUS.md | 84 +++++++++++++++++++
.../app/AppOutdoorFairController.java | 33 ++++----
.../controller/cms/OutdoorFairController.java | 47 ++++++++++-
.../cms/domain/rc/PublicJobFairDetail.java | 26 ++++++
.../cms/mapper/OutdoorFairBoothMapper.java | 16 ++++
.../mapper/rc/PublicJobFairMapper.xml | 16 +++-
.../ruoyi/common/utils/RequestUrlUtils.java | 46 ++++++++++
36 files changed, 1710 insertions(+), 19 deletions(-)
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-001-public-title-year-mismatch.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-002-public-filter-field-accessibility.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-003-online-detail-host-unit-missing.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-004-enterprise-filter-accessibility.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-005-enterprise-outdoor-filter-accessibility.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-006-enterprise-outdoor-image-mixed-content.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-007-enterprise-outdoor-signup-no-confirmation.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-008-enterprise-outdoor-job-dialog-accessibility.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-009-outdoor-booth-count-mismatch.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-010-enterprise-crosscity-statistics-accessibility.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-011-enterprise-crosscity-region-dialog-accessibility.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-012-admin-filter-accessibility.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-013-admin-avatar-404.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-014-public-admin-jobfair-type-inconsistent.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-015-admin-job-raw-dictionary-codes.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-016-admin-outdoor-filter-accessibility.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-017-admin-outdoor-company-filter-accessibility.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-018-admin-outdoor-attendance-accessibility.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-019-admin-outdoor-unsubscribe-accessibility.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-020-admin-outdoor-jobs-accessibility.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-021-admin-outdoor-attendee-autocomplete.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-022-admin-outdoor-device-accessibility.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-023-admin-venue-filter-accessibility.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-024-admin-venue-add-dialog-accessibility.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-025-admin-statistics-date-accessibility.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-026-admin-institution-filter-accessibility.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-027-admin-crosscity-region-dialog-accessibility.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-028-crosscity-region-selector-not-keyboard-accessible.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/BUG-029-crosscity-region-selection-no-job-loop.md
create mode 100644 docs/test-reports/job-fair/2026-07-25/bugs/FIX-STATUS.md
diff --git a/docs/test-reports/job-fair/2026-07-25/bugs/BUG-001-public-title-year-mismatch.md b/docs/test-reports/job-fair/2026-07-25/bugs/BUG-001-public-title-year-mismatch.md
new file mode 100644
index 0000000..9d0e806
--- /dev/null
+++ b/docs/test-reports/job-fair/2026-07-25/bugs/BUG-001-public-title-year-mismatch.md
@@ -0,0 +1,63 @@
+# BUG-001:招聘会标题年份与举办日期不一致
+
+- 严重级别:P2(数据展示不一致,可能误导求职者)
+- 状态:历史数据待确认;代码处理结果见 [FIX-STATUS.md](./FIX-STATUS.md)
+- 环境:test
+- 角色:PC 求职者(未登录)
+- 入口:`/shihezi/job-portal/job-fair`
+- 发现时间:2026-07-25(页面响应时间为 2026-07-24 17:06:09 GMT)
+
+## 前置条件
+
+使用测试环境公开求职者入口,当前日期为 2026-07-25,进入招聘会列表,保持默认“线上招聘会”和 2026 年 7 月视图。
+
+## 复现步骤
+
+1. 打开 `https://test.xjshzly.longbiosphere.com/shihezi`。
+2. 点击顶部“招聘会”。
+3. 在“本月招聘会”区域查看 07 月 31 日的第一条招聘会。
+4. 对照页面标题和页面展示的举办日期。
+
+## 预期结果
+
+招聘会标题中的年份、列表日期和详情中的举办日期应指向同一场次/同一年度;如果标题是业务名称,应至少不能与举办日期产生明确矛盾。
+
+## 实际结果
+
+页面展示标题为“2028年‘百日千万招聘专项行动’暨第五场人才夜市——退役军人(军属)专场招聘会”,但该条目接口字段 `jobFairStartTime` 和 `jobFairEndTime` 均为 `2026-07-31`。页面因此把“2028 年”标题放在 2026 年 7 月的招聘会安排中。
+
+## 浏览器证据
+
+- 页面截图:[JF-PO-001-public-list-baseline.png](../screenshots/JF-PO-001-public-list-baseline.png)
+- `GET /api/shihezi/app/jobfair/public/jobfair/currentMonth`:HTTP 200
+- 响应中同一对象的关键字段:
+
+```json
+{
+ "jobFairId": "1032",
+ "jobFairTitle": "2028年“百日千万招聘专项行动”暨第五场人才夜市——退役军人(军属)专场招聘会",
+ "jobFairStartTime": "2026-07-31 20:00:00",
+ "jobFairEndTime": "2026-07-31 23:00:00"
+}
+```
+
+- 页面“本月招聘会”同时显示“07月31日”和上述标题。
+- 控制台无 JavaScript 异常;该问题是业务数据/展示一致性问题,不是请求失败。
+
+### 企业端户外列表的同类复现
+
+在企业端户外招聘会列表中还观察到同类数据矛盾:`id=8` 的标题为“2028年石河子市文旅商贸户外招聘会”,但列表返回 `holdTime=2026-10-24 10:00:00`、`endTime=2026-10-24 17:00:00`。该记录通过 `GET /api/shihezi/cms/outdoor-fair/list?current=1&pageSize=20` 返回,页面在“开始时间/结束时间”列显示为 2026 年。
+
+该观察扩大了问题影响范围:同类标题年度错误同时出现在公开线上招聘会和企业端户外招聘会数据中。
+
+## 影响范围
+
+公开求职者可能误以为活动发生在 2028 年,造成报名/参会时间判断错误;管理端、企业端若复用同一条数据,也可能出现同样的不一致。
+
+## 初步定位
+
+从浏览器响应看,标题和时间在服务端返回时已经不一致,优先检查测试环境招聘会 `1032` 的基础数据或管理端编辑保存逻辑;前端目前直接展示服务端标题,没有做年份一致性校验。
+
+## 建议
+
+核对该招聘会真实年度并修正数据;同时在管理端新增/编辑时增加标题年度与举办时间年度的校验或提示,避免再次产生矛盾。跨管理端、企业端和公开端复验同一 `jobFairId` 的标题与时间。
diff --git a/docs/test-reports/job-fair/2026-07-25/bugs/BUG-002-public-filter-field-accessibility.md b/docs/test-reports/job-fair/2026-07-25/bugs/BUG-002-public-filter-field-accessibility.md
new file mode 100644
index 0000000..ad0abe8
--- /dev/null
+++ b/docs/test-reports/job-fair/2026-07-25/bugs/BUG-002-public-filter-field-accessibility.md
@@ -0,0 +1,38 @@
+# BUG-002:招聘会筛选表单字段缺少 id/name
+
+- 严重级别:P3(可访问性与自动填充兼容性问题)
+- 状态:代码已修复,待测试环境浏览器复验
+- 环境:test
+- 角色:PC 求职者(未登录)
+- 入口:`/shihezi/job-portal/job-fair`
+- 发现时间:2026-07-25
+
+## 复现步骤
+
+1. 打开测试环境 PC 求职者入口并进入“招聘会”。
+2. 保持默认“线上招聘会”页签。
+3. 查看招聘会筛选区域的搜索输入框和年月下拉字段。
+4. 在 Chrome DevTools 控制台消息中查看页面加载后的 Issue。
+
+## 预期结果
+
+表单控件应具备稳定的 `id` 或 `name`,并与可见标签/辅助文本建立关联,便于键盘用户、读屏软件和浏览器自动填充识别。
+
+## 实际结果
+
+Chrome DevTools 报告:`A form field element should have an id or name attribute (count: 1)`。招聘会页面的筛选区包含搜索框和年月选择控件,其中至少一个原生表单字段没有 `id` 或 `name`。
+
+## 浏览器证据
+
+- 页面截图:[JF-PO-001-public-list-baseline.png](../screenshots/JF-PO-001-public-list-baseline.png)
+- 页面 accessibility snapshot 显示筛选区域包含“搜索招聘会名称”搜索框以及年月 combobox。
+- 控制台消息:`msgid=9 [issue] A form field element should have an id or name attribute (count: 1)`。
+- 相关接口均返回 HTTP 200;问题不影响本次数据请求,但影响表单语义和辅助技术。
+
+## 影响范围
+
+使用读屏、自动填充、浏览器表单识别或自动化测试的用户可能无法稳定定位该字段;也会降低招聘会页面的无障碍质量。
+
+## 建议
+
+为对应原生控件补充唯一 `id/name`,并使用 `label` 或 `aria-labelledby` 与可见字段名称关联;修复后重新运行 DevTools accessibility/issue 检查。
diff --git a/docs/test-reports/job-fair/2026-07-25/bugs/BUG-003-online-detail-host-unit-missing.md b/docs/test-reports/job-fair/2026-07-25/bugs/BUG-003-online-detail-host-unit-missing.md
new file mode 100644
index 0000000..1db5b05
--- /dev/null
+++ b/docs/test-reports/job-fair/2026-07-25/bugs/BUG-003-online-detail-host-unit-missing.md
@@ -0,0 +1,46 @@
+# BUG-003:线上招聘会详情缺少列表已有的主办单位
+
+- 严重级别:P2(同一业务对象页面信息不一致)
+- 状态:后端契约已修复,待测试环境浏览器复验
+- 环境:test
+- 角色:PC 求职者(未登录)
+- 入口:`/shihezi/job-portal/job-fair`
+- 招聘会:`jobFairId=1050`
+- 发现时间:2026-07-25
+
+## 复现步骤
+
+1. 打开测试环境 PC 求职者端的“招聘会”页面。
+2. 在“线上招聘会”列表找到“2026年师市高校毕业生等青年就业服务专项行动直播带岗”。
+3. 观察列表卡片的主办/组织单位字段。
+4. 点击该场招聘会进入详情页。
+5. 对照详情页“主办单位”字段。
+
+## 预期结果
+
+同一 `jobFairId` 的列表和详情应展示一致的主办单位;详情接口应返回该字段,或前端使用列表上下文中的同值回填。
+
+## 实际结果
+
+列表展示“石河子人力资源产业园创新创业基地”,详情页展示“主办单位:暂未公布”。
+
+## 浏览器证据
+
+- 列表/详情截图:[JF-PO-004-online-detail.png](../screenshots/JF-PO-004-online-detail.png)
+- 详情截图中“主办单位:暂未公布”。
+- `GET /api/shihezi/app/jobfair/public/jobfair/detail?jobFairId=1050`:HTTP 200,响应包含标题、地址、简介、起止时间、类型、经纬度、`isSignUp`,但没有 `jobFairHostUnit` 或 `jobFairOrganizeUnit`。
+- 列表接口 `GET /api/shihezi/app/jobfair/public/jobfair/page?pageNum=1&pageSize=9` 同一场次返回 `jobFairHostUnit` 与 `jobFairOrganizeUnit`。
+- `GET /api/shihezi/app/jobfair/public/jobfair/enterprises-with-jobs-by-job-fair-id?jobFairId=1050`:HTTP 200,岗位展示正常。
+- 控制台无 JavaScript 异常,仅有 CSS preload 警告。
+
+## 影响范围
+
+求职者从列表进入详情时会丢失重要的主办单位信息,降低可信度;管理端或分享深链接直接打开详情时同样可能出现信息缺失。
+
+## 初步定位
+
+前端详情组件优先读取 `jobFairHostUnit` / `jobFairOrganizeUnit`,但详情接口响应未提供这两个字段;列表接口字段完整。优先检查公共招聘会详情 DTO/查询 SQL 的字段映射和公开详情接口序列化。
+
+## 建议
+
+统一列表与详情返回模型;补齐详情接口主办单位字段,并用同一 `jobFairId` 验证列表、详情和管理端数据一致性。
diff --git a/docs/test-reports/job-fair/2026-07-25/bugs/BUG-004-enterprise-filter-accessibility.md b/docs/test-reports/job-fair/2026-07-25/bugs/BUG-004-enterprise-filter-accessibility.md
new file mode 100644
index 0000000..bd002e3
--- /dev/null
+++ b/docs/test-reports/job-fair/2026-07-25/bugs/BUG-004-enterprise-filter-accessibility.md
@@ -0,0 +1,48 @@
+# BUG-004:企业端线上招聘会筛选表单缺少字段语义关联
+
+- 严重级别:P3(可访问性与自动填充兼容性问题)
+- 状态:代码已修复,待测试环境浏览器复验
+- 环境:test
+- 角色:PC 企业端(已登录测试企业角色)
+- 入口:`/shihezi/jobfair/public-job-fair`
+- 发现时间:2026-07-25
+
+## 复现步骤
+
+1. 使用测试企业角色进入企业管理端。
+2. 打开“招聘会管理 → 线上招聘会”。
+3. 保持默认查询条件,观察“时间范围”和“招聘会标题”筛选控件。
+4. 打开 Chrome DevTools 的 Issues/Console 信息,查看页面加载后的表单问题。
+
+## 预期结果
+
+筛选区的原生表单字段应具备稳定且唯一的 `id` 或 `name`,并与可见标签建立语义关联;读屏软件、键盘用户、浏览器自动填充和自动化测试应能明确识别“时间范围”和“招聘会标题”。
+
+## 实际结果
+
+Chrome DevTools 报告:
+
+- `No label associated with a form field (count: 1)`
+- `A form field element should have an id or name attribute (count: 2)`
+
+页面可正常加载列表和查询接口,但至少两个筛选字段没有稳定的字段标识,且至少一个字段没有与标签关联。
+
+## 浏览器证据
+
+- 页面截图:[JF-EN-004-enterprise-filter-form.png](../screenshots/JF-EN-004-enterprise-filter-form.png)
+- accessibility snapshot 显示筛选字段为“时间范围 :”文本框、“请选择”文本框和“招聘会标题 :”文本框,但未显示对应的稳定 `id/name` 语义信息。
+- 控制台 Issue:`msgid=26 [issue] No label associated with a form field (count: 1)`。
+- 控制台 Issue:`msgid=27 [issue] A form field element should have an id or name attribute (count: 2)`。
+- `GET /api/shihezi/cms/publicJobFair/list?pageNum=1&pageSize=20`:HTTP 200,列表返回 56 条数据;问题发生在页面表单 DOM 语义层,不是接口加载失败。
+
+## 影响范围
+
+使用读屏、浏览器自动填充、键盘辅助技术或表单自动化的企业用户可能无法稳定定位或理解筛选字段;也会使企业端招聘会页面无法通过基础无障碍检查。
+
+## 初步定位
+
+企业端线上招聘会列表页的筛选表单渲染了时间范围 DatePicker/Input 和招聘会标题 Input,但页面运行时没有为全部原生字段输出可识别的 `id/name`,且至少一个字段没有对应 `