package com.ruoyi.cms.service.impl;

import com.alibaba.fastjson2.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.cms.domain.*;
import com.ruoyi.cms.domain.vo.JobExcelVo;
import com.ruoyi.cms.util.notice.NoticeUtils;
import com.ruoyi.common.core.domain.entity.File;
import com.ruoyi.cms.domain.query.ESJobSearch;
import com.ruoyi.cms.domain.vo.CandidateVO;
import com.ruoyi.cms.mapper.*;
import com.ruoyi.cms.service.*;
import com.ruoyi.cms.util.*;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.TreeSelect;
import com.ruoyi.common.core.domain.entity.AppUser;
import com.ruoyi.common.core.domain.entity.Company;
import com.ruoyi.common.core.domain.entity.JobTitle;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.SiteSecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.dromara.easyes.core.biz.EsPageInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.URL;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 岗位Service业务层处理
 *
 * @author lishundong
 * @date 2024-09-03
 */
@Service
public class JobServiceImpl extends ServiceImpl<JobMapper,Job> implements IJobService
{
    @Autowired
    private JobMapper jobMapper;
    @Autowired
    private CompanyMapper companyMapper;
    @Autowired
    private JobApplyMapper jobApplyMapper;
    @Autowired
    private JobCollectionMapper jobCollectionMapper;
    @Autowired
    private AppReviewJobMapper appReviewJobMapper;
    @Autowired
    private IBussinessDictTypeService iBussinessDictTypeService;
    @Autowired
    private IBussinessDictDataService iBussinessDictDataService;
    @Autowired
    private IIndustryService industryService;
    @Autowired
    private IESJobSearchService iesJobSearchService;
    @Autowired
    private JobTitleMapper jobTitleMapper;
    Logger logger = LoggerFactory.getLogger(JobServiceImpl.class);
    // 高德API配置
    private static final String API_KEY = "32edd506a411de754d34a0f4ecb72345";
    private static final String AMAP_URL = "https://restapi.amap.com/v3/place/text";
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private JobContactMapper jobContactMapper;
    @Autowired
    private FileMapper fileMapper;
    @Autowired
    private IAppUserService appUserService;
    @Autowired
    private CompanyCollectionMapper companyCollectionMapper;
    @Autowired
    private NoticeMapper noticeMapper;

    /**
     * 更新工作地址的经纬度信息
     */
    public void updateJobLon() {
        System.out.println("开始执行工作地址经纬度更新...");

        // 1. 查询需要更新经纬度的工作地址
        List<Job> jobs = jobMapper.selectList(
                Wrappers.<Job>lambdaQuery()
                        .isNull(Job::getLongitude)  // 查询经度为null的记录
                        .or()
                        .eq(Job::getLongitude, BigDecimal.ZERO)  // 或者经度为0的记录
                                 // 限制每次处理10条
        );

        if (jobs.isEmpty()) {
            System.out.println("没有需要更新经纬度的工作地址");
            return;
        }

        // 2. 遍历处理每个工作地址
        for (Job job : jobs) {
            try {
                System.out.println("\n正在处理工作ID: " + job.getJobId() +
                        ", 地址: " + job.getJobLocation());

                // 调用高德API获取经纬度（添加适当延迟）
                TimeUnit.MILLISECONDS.sleep(500);
                Map<String, BigDecimal> coordinates = getCoordinates(job.getJobLocation(), "青岛市");

                if (coordinates != null) {
                    // 3. 更新工作记录的经纬度
                    job.setLongitude(coordinates.get("lng"));
                    job.setLatitude(coordinates.get("lat"));

                    // 使用MyBatis Plus的update方法
                    jobMapper.updateById(job);
                    System.out.println("成功更新工作ID: " + job.getJobId() +
                            " 的经纬度 -> 经度: " + job.getLongitude() +
                            ", 纬度: " + job.getLatitude());
                } else {
                    System.out.println("无法获取工作地址的经纬度: " + job.getJobLocation());
                    jobMapper.updateById(job);
                }
            } catch (Exception e) {
                System.err.println("处理工作ID " + job.getJobId() + " 时出错: " + e.getMessage());
            }
        }

        System.out.println("工作地址经纬度更新完成，共处理 " + jobs.size() + " 条记录");
    }

    /**
     * 调用高德地图API获取地址的经纬度并返回BigDecimal
     */
    private Map<String, BigDecimal> getCoordinates(String placeName, String city) {
        try {
            // 构造请求URL
            String encodedPlaceName = java.net.URLEncoder.encode(placeName, "UTF-8");
            String encodedCity = java.net.URLEncoder.encode(city, "UTF-8");

            String params = "key=" + API_KEY + "&keywords=" + encodedPlaceName +
                    "&types=190000&city=" + encodedCity + "&output=JSON";
            String requestUrl = AMAP_URL + "?" + params;

            // 发送HTTP请求
            URL url = new URL(requestUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");

            int responseCode = connection.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) {
                BufferedReader in = new BufferedReader(
                        new InputStreamReader(connection.getInputStream(), "UTF-8"));
                String inputLine;
                StringBuilder response = new StringBuilder();

                while ((inputLine = in.readLine()) != null) {
                    response.append(inputLine);
                }
                in.close();

                // 解析JSON响应
                com.alibaba.fastjson.JSONObject jsonResponse =
                        com.alibaba.fastjson.JSON.parseObject(response.toString());

                if ("1".equals(jsonResponse.getString("status"))) {
                    com.alibaba.fastjson.JSONArray pois = jsonResponse.getJSONArray("pois");
                    if (pois != null && !pois.isEmpty()) {
                        com.alibaba.fastjson.JSONObject firstPoi = pois.getJSONObject(0);
                        String location = firstPoi.getString("location");
                        String[] lngLat = location.split(",");

                        // 将字符串坐标转换为BigDecimal
                        Map<String, BigDecimal> coordinates = new HashMap<>();
                        coordinates.put("lng", new BigDecimal(lngLat[0]));
                        coordinates.put("lat", new BigDecimal(lngLat[1]));
                        return coordinates;
                    }
                }
            }
            return null;
        } catch (Exception e) {
            System.err.println("调用高德地图API出错: " + e.getMessage());
            return null;
        }
    }

    /**
     * 查询岗位
     *
     * @param jobId 岗位主键
     * @return 岗位
     */
    @Override
    public Job selectJobByJobId(Long jobId)
    {
        Job job = jobMapper.selectById(jobId);
        //查询公司信息
        if(Objects.nonNull(job.getCompanyId())){
            Company company = companyMapper.selectById(job.getCompanyId());
            job.setCompany(company);
        }
        //查询联系人
        JobContact contact=new JobContact();
        contact.setJobId(job.getJobId());
        List<JobContact> contacts=jobContactMapper.getSelectList(contact);
        if(contacts!=null){
            job.setJobContactList(contacts);
        }
        return job;
    }

    @Override
    public Job selectHttpJobByJobId(Long jobId, HttpServletRequest request)
    {
        Job job = jobMapper.selectById(jobId);
        //查询公司信息
        if(Objects.nonNull(job.getCompanyId())){
            Company company = companyMapper.selectById(job.getCompanyId());
            job.setCompany(company);
        }
        //查询联系人
        JobContact contact = new JobContact();
        contact.setJobId(jobId);
        List<JobContact> contacts = jobContactMapper.getSelectList(contact);
        job.setJobContactList(contacts == null ? Collections.emptyList() : contacts);
        //查询附件
        String baseFilePath = StringUtil.getFilePath(request);
        //查询附件
        File queryFile = new File();
        queryFile.setBussinessid(jobId);
        List<File> filesList = Optional.ofNullable(fileMapper.selectFileList(queryFile))
                .orElseGet(Collections::emptyList);
        //添加路径
        List<File> processedFiles = filesList.stream()
                .filter(Objects::nonNull)
                .filter(file -> file.getFileUrl() != null && !file.getFileUrl().trim().isEmpty())
                .peek(file -> file.setFileUrl(String.join("", baseFilePath, file.getFileUrl())))
                .collect(Collectors.toList());

        job.setFilesList(processedFiles);

        return job;
    }

    /**
     * 查询岗位列表
     *
     * @param job 岗位
     * @return 岗位
     */
    @Override
    public List<Job> selectJobList(Job job)
    {
        List<Job> jobs = jobMapper.selectJobList(job);
        return jobs;
    }

    @Override
    public List<Job> selectHttpJobList(Job job,HttpServletRequest request)
    {
        List<Job> jobs = jobMapper.selectJobList(job);
        if (CollectionUtils.isNotEmpty(jobs)) {
            String baseFilePath = StringUtil.getFilePath(request);
            baseFilePath = baseFilePath == null ? "" : baseFilePath;

            List<Long> jobIds = jobs.stream().filter(Objects::nonNull)
                    .map(Job::getJobId).filter(Objects::nonNull)
                    .collect(Collectors.toList());

            //查询所有附件
            List<File> allFiles = CollectionUtils.isNotEmpty(jobIds)
                    ? Optional.ofNullable(fileMapper.selectFileListByBussinessIds(jobIds))
                    .orElseGet(Collections::emptyList)
                    : Collections.emptyList();
            //查询所有联系人
            List<JobContact> allJobContacts = CollectionUtils.isNotEmpty(jobIds)
                    ? Optional.ofNullable(jobContactMapper.selectByJobIds(jobIds)).orElse(Collections.emptyList())
                    : Collections.emptyList();
            //岗位联系人
            Map<Long, List<JobContact>> jobContactGroupMap = allJobContacts.stream()
                    .filter(Objects::nonNull)
                    .filter(contact -> Objects.nonNull(contact.getJobId()))
                    .collect(Collectors.groupingBy(
                            JobContact::getJobId,
                            Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)
                    ));

            //附件分组
            String finalBaseFilePath = baseFilePath;
            Map<Long, List<File>> fileGroupMap = allFiles.stream()
                    .filter(Objects::nonNull)
                    .filter(file -> Objects.nonNull(file.getBussinessid()))
                    .filter(file -> StringUtils.isNotBlank(file.getFileUrl()))
                    .peek(file -> {
                        String fileUrl = file.getFileUrl().trim();
                        String fullFileUrl = StringUtils.join(finalBaseFilePath, fileUrl);
                        file.setFileUrl(fullFileUrl);
                    })
                    .collect(Collectors.groupingBy(
                            File::getBussinessid,
                            Collectors.collectingAndThen(Collectors.toList(),Collections::unmodifiableList)
                    ));

            jobs.forEach(jobItem -> {
                if (Objects.nonNull(jobItem)) {
                    Long jobItemId = jobItem.getJobId();
                    List<File> jobFiles = fileGroupMap.getOrDefault(jobItemId, Collections.emptyList());
                    List<JobContact> jobContent = jobContactGroupMap.getOrDefault(jobItemId, Collections.emptyList());
                    jobItem.setFilesList(jobFiles);
                    jobItem.setJobContactList(jobContent);
                }
            });
        }
        return jobs;
    }

    @Override
    public int view(Long jobId) {
        Job job = jobMapper.selectById(jobId);
        if(Objects.isNull(job)){
            throw new ServiceException("岗位不存在");
        }
        Long view=job.getView()==null?0L:job.getView();
        job.setView(view+1);
        jobMapper.updateById(job);
        return 1;
    }

    /**
     * 新增岗位
     *
     * @param job 岗位
     * @return 结果
     */
    @Transactional
    @Override
    public int insertJob(Job job)
    {
        if(job.getJobLocationAreaCode()!=null){
            job.setRegionCode(StringUtil.getAreaToRegionCode(String.valueOf(job.getJobLocationAreaCode())));
        }
        int insert = jobMapper.insert(job);
        //todo 线程池管理
//        Thread thread = new Thread(() -> {
//            VectorJob jobVector = jobMapper.selectVectorJob(job.getJobId());
//            jobVector.setJobUrl("http://39.98.44.136/app#/packageA/pages/post/post?jobId="+job.getJobId());
//            String jsonBody = JSONUtil.toJsonStr(jobVector);
//            HttpUtil.post("http://39.98.44.136:6004/insert_vector", jsonBody);
//        });
//        thread.start();
        if(insert>0){
            //添加联系人
            List<JobContact> jobContactList = job.getJobContactList() != null ? job.getJobContactList() : Collections.emptyList();
            jobContactList.forEach(x->{
                JobContact jobContact=new JobContact();
                BeanUtils.copyProperties(x,jobContact);
                jobContact.setJobId(job.getJobId());
                jobContactMapper.insert(jobContact);
            });
            //添加附件
            List<File> filesList = job.getFilesList() != null ? job.getFilesList() : Collections.emptyList();
            List<Long> longs = filesList.stream().filter(Objects::nonNull).map(File::getBussinessid).collect(Collectors.toList());
            if(!longs.isEmpty()){
                fileMapper.updateBussinessids(longs,job.getJobId());
            }
        }
        return insert;
    }


    /**
     * 修改岗位
     *
     * @param job 岗位
     * @return 结果
     */
    @Override
    @Transactional
    public int updateJob(Job job)
    {
        if(job.getJobLocationAreaCode()!=null){
            job.setRegionCode(StringUtil.getAreaToRegionCode(String.valueOf(job.getJobLocationAreaCode())));
        }
        LocalDateTime now = LocalDateTime.now();
        String formattedDate = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        //修改岗位状态
        if(job.getIsPublish()!=null) {
            if(job.getIsPublish()==1){
                job.setPostingDate(formattedDate);
                //修改es缓存
                iesJobSearchService.updateJob(job.getJobId());
                //添加岗位上新
                Job parmJob=null;
                if(job.getCompanyId()==null){
                    //传递job消息不完整
                    parmJob=jobMapper.getJobInfo(job.getJobId());
                }
                List<AppUser> users=companyCollectionMapper.selectAppuserList(parmJob.getCompanyId());
                if(users!=null&&users.size()>0){
                    List<Notice> notices= NoticeUtils.createGwsxNotice(users,parmJob);
                    noticeMapper.batchInsert(notices);
                }
            }else {
                job.setPostingDate(null);
                iesJobSearchService.deleteJob(job.getJobId());
            }
        }
        int i=jobMapper.updateById(job);
        //修改岗位联系人列表
        if(i>0){
            // 处理联系人
            handleJobContact(job);
            //添加附件
            handleJobFile(job);
        }
        return i;
    }

    /**
     * 处理联系人
     * @param job
     */
    private void handleJobContact(Job job) {
        List<JobContact> jobContactList = Optional.ofNullable(job.getJobContactList()).orElse(Collections.emptyList());
        if (CollectionUtils.isEmpty(jobContactList)) {
            return;
        }
        List<JobContact> insertList = new ArrayList<>();
        List<JobContact> updateList = new ArrayList<>();

        for (JobContact originContact : jobContactList) {
            if (originContact == null) {
                continue;
            }

            JobContact targetContact = new JobContact();
            targetContact.setJobId(job.getJobId());
            targetContact.setContactPerson(originContact.getContactPerson());
            targetContact.setContactPersonPhone(originContact.getContactPersonPhone());
            targetContact.setPosition(originContact.getPosition());

            if (originContact.getId() == null) {
                insertList.add(targetContact);
            } else {
                targetContact.setId(originContact.getId());
                updateList.add(targetContact);
            }
        }

        if (!CollectionUtils.isEmpty(insertList)) {
            jobContactMapper.batchInsert(insertList);
        }

        if (!CollectionUtils.isEmpty(updateList)) {
            updateList.forEach(contact -> {
                LambdaUpdateWrapper<JobContact> updateWrapper = Wrappers.<JobContact>lambdaUpdate()
                        .eq(JobContact::getId, contact.getId());
                jobContactMapper.update(contact, updateWrapper);
            });
        }
    }

    /**
     * 处理附件
     * @param job
     */
    private void handleJobFile(Job job) {
        List<File> filesList = Optional.ofNullable(job.getFilesList()).orElse(Collections.emptyList());
        List<Long> fileIds = filesList.stream()
                .filter(Objects::nonNull)
                .map(File::getBussinessid)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        if (!CollectionUtils.isEmpty(fileIds)) {
            fileMapper.updateBussinessids(fileIds, job.getJobId());
        }
    }

    /**
     * 批量删除岗位
     *
     * @param jobIds 需要删除的岗位主键
     * @return 结果
     */
    @Override
    public int deleteJobByJobIds(Long[] jobIds)
    {
        int i=jobMapper.deleteBatchIds(Arrays.asList(jobIds));
        if(i>0){
            jobContactMapper.update(null,Wrappers.<JobContact>lambdaUpdate()
                    .in(JobContact::getJobId, Arrays.asList(jobIds))
                    .set(JobContact::getDelFlag, Constants.Del_FLAG_DELETE));
        }
        return i;
    }

    @Override
    public EsPageInfo<ESJobDocument> appList(ESJobSearch jobQuery) {
        EsPageInfo<ESJobDocument> jobs = iesJobSearchService.appList(jobQuery);
        List<ESJobDocument> esJobDocuments = userCollection(jobs.getList());
        jobs.setList(esJobDocuments);
        return jobs;
    }

    @Override
    public List<ESJobDocument> recommend(ESJobSearch esJobSearch) {
        RedisCache redisCache = SpringUtils.getBean(RedisCache.class);
        JSONArray cacheObject = redisCache.getCacheObject(esJobSearch.getSessionId());
        List<Long> jobList = new ArrayList<>();
        if(Objects.isNull(cacheObject)){
            ArrayList<Long> longs = new ArrayList<>();
            jobList =longs;
        }else {
            jobList = cacheObject.toList(Long.class);
        }
        //从es中查询
        List<ESJobDocument> jobListResult = iesJobSearchService.selectTextListExceptJobId(esJobSearch,jobList);
        //存入当前session中查看的岗位 避免重复 todo 定时删除 key上保存用户信息
        jobList.addAll(jobListResult.stream().map(ESJobDocument::getJobId).collect(Collectors.toList()));
        redisCache.setCacheObject(esJobSearch.getSessionId(),jobList);
        List<ESJobDocument> esJobDocuments = userCollection(jobListResult);
        return esJobDocuments;
    }

    @Override
    public EsPageInfo<ESJobDocument> countyJobList(ESJobSearch job) {
        EsPageInfo<ESJobDocument> esJobDocumentEsPageInfo = iesJobSearchService.countyJobList(job);
        List<ESJobDocument> esJobDocuments = userCollection(esJobDocumentEsPageInfo.getList());
        esJobDocumentEsPageInfo.setList(esJobDocuments);
        return esJobDocumentEsPageInfo;
    }

    @Override
    public EsPageInfo<ESJobDocument> subway(ESJobSearch jobQuery) {
        EsPageInfo<ESJobDocument> esJobDocumentEsPageInfo = iesJobSearchService.subway(jobQuery);
        List<ESJobDocument> esJobDocuments = userCollection(esJobDocumentEsPageInfo.getList());
        esJobDocumentEsPageInfo.setList(esJobDocuments);
        return esJobDocumentEsPageInfo;
    }

    @Override
    public EsPageInfo<ESJobDocument> commercialArea(ESJobSearch jobQuery) {
        EsPageInfo<ESJobDocument> esJobDocumentEsPageInfo = iesJobSearchService.commercialArea(jobQuery);
        List<ESJobDocument> esJobDocuments = userCollection(esJobDocumentEsPageInfo.getList());
        esJobDocumentEsPageInfo.setList(esJobDocuments);
        return esJobDocumentEsPageInfo;
    }

    @Override
    public Job selectJobByJobIdApp(Long jobId) {
            Job job = jobMapper.selectById(jobId);
            //查询公司信息
            if(Objects.nonNull(job.getCompanyId())){
                Company company = companyMapper.selectById(job.getCompanyId());
                job.setCompany(company);
            }
            if(SiteSecurityUtils.isLogin()){
                //查询申请信息
                Long applyCount = jobApplyMapper.selectCount(Wrappers.<JobApply>lambdaQuery().eq(JobApply::getJobId, jobId).eq(JobApply::getUserId, SiteSecurityUtils.getUserId()));
                job.setIsApply(applyCount>0?1:0);
                //查询收藏信息
                Long collectionCount = jobCollectionMapper.selectCount(Wrappers.<JobCollection>lambdaQuery().eq(JobCollection::getJobId, jobId).eq(JobCollection::getUserId, SiteSecurityUtils.getUserId()));
                job.setIsCollection(collectionCount>0?1:0);
                //todo asyn
                //保存浏览记录
                List<AppReviewJob> appReviewJobs = appReviewJobMapper.selectList(Wrappers.<AppReviewJob>lambdaQuery().eq(AppReviewJob::getUserId, SiteSecurityUtils.getUserId()).eq(AppReviewJob::getJobId, jobId));
                //之前相同岗位的记录删除 保存最新的浏览记录
                if(!appReviewJobs.isEmpty()){
                    appReviewJobMapper.deleteBatchIds(appReviewJobs.stream().map(AppReviewJob::getId).collect(Collectors.toList()));
                }
                AppReviewJob appReviewJob = new AppReviewJob();
                appReviewJob.setUserId(SiteSecurityUtils.getUserId());
                LocalDateTime now = LocalDateTime.now();
                String formattedDate = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                appReviewJob.setReviewDate(formattedDate);
                appReviewJob.setJobId(jobId);
                appReviewJobMapper.insert(appReviewJob);
            }
            this.view(jobId);
            return job;

    }

    @Override
    public Job selectHttpJobByJobIdApp(Long jobId,HttpServletRequest request) {
        Job job = jobMapper.selectById(jobId);
        //查询公司信息
        if(Objects.nonNull(job.getCompanyId())){
            Company company = companyMapper.selectById(job.getCompanyId());
            job.setCompany(company);
        }
        if(SiteSecurityUtils.isLogin()){
            //查询申请信息
            Long applyCount = jobApplyMapper.selectCount(Wrappers.<JobApply>lambdaQuery().eq(JobApply::getJobId, jobId).eq(JobApply::getUserId, SiteSecurityUtils.getUserId()));
            job.setIsApply(applyCount>0?1:0);
            //查询收藏信息
            Long collectionCount = jobCollectionMapper.selectCount(Wrappers.<JobCollection>lambdaQuery().eq(JobCollection::getJobId, jobId).eq(JobCollection::getUserId, SiteSecurityUtils.getUserId()));
            job.setIsCollection(collectionCount>0?1:0);
            //todo asyn
            //保存浏览记录
            List<AppReviewJob> appReviewJobs = appReviewJobMapper.selectList(Wrappers.<AppReviewJob>lambdaQuery().eq(AppReviewJob::getUserId, SiteSecurityUtils.getUserId()).eq(AppReviewJob::getJobId, jobId));
            //之前相同岗位的记录删除 保存最新的浏览记录
            if(!appReviewJobs.isEmpty()){
                appReviewJobMapper.deleteBatchIds(appReviewJobs.stream().map(AppReviewJob::getId).collect(Collectors.toList()));
            }
            AppReviewJob appReviewJob = new AppReviewJob();
            appReviewJob.setUserId(SiteSecurityUtils.getUserId());
            LocalDateTime now = LocalDateTime.now();
            String formattedDate = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            appReviewJob.setReviewDate(formattedDate);
            appReviewJob.setJobId(jobId);
            appReviewJobMapper.insert(appReviewJob);
        }
        this.view(jobId);
        //查询联系人
        JobContact contact = new JobContact();
        contact.setJobId(jobId);
        List<JobContact> contacts = jobContactMapper.getSelectList(contact);
        job.setJobContactList(contacts == null ? Collections.emptyList() : contacts);
        //查询附件
        String baseFilePath = StringUtil.getFilePath(request);
        File queryFile = new File();
        queryFile.setBussinessid(jobId);
        List<File> filesList = Optional.ofNullable(fileMapper.selectFileList(queryFile))
                .orElseGet(Collections::emptyList);
        //添加路径
        List<File> processedFiles = filesList.stream()
                .filter(Objects::nonNull)
                .filter(file -> file.getFileUrl() != null && !file.getFileUrl().trim().isEmpty())
                .peek(file -> file.setFileUrl(String.join("", baseFilePath, file.getFileUrl())))
                .collect(Collectors.toList());

        job.setFilesList(processedFiles);
        //查询投递人
        List<AppUser> appUsers=jobMapper.selectApplyJobUserList(jobId);
        job.setApplyUsers(appUsers == null ? Collections.emptyList() : appUsers);
        return job;

    }

    @Override
    public List<CandidateVO> candidates(Long jobId) {
        List<CandidateVO> jobList = jobApplyMapper.candidates(jobId);
        List<JobTitle> jobTitles = jobTitleMapper.selectList(new QueryWrapper<>());
        jobList.forEach(x->x.setJobTitle(wrapperJobTitleList(jobTitles,x.getJobTitleId())));
        return jobList;
    }

    private List<String> wrapperJobTitleList(List<JobTitle> jobTitles, String jobTitleId) {
        List<String> result = new ArrayList<>();
        if(StringUtil.isEmptyOrNull(jobTitleId)){return result;}
        // 将 jobTitleId 按逗号分隔，得到 jobId 列表
        List<String> jobIdList = Arrays.asList(jobTitleId.split(","));
        // 遍历 jobTitles，比对 jobId
        for (JobTitle jobTitle : jobTitles) {
            if (jobIdList.contains(jobTitle.getJobId().toString())) {
                result.add(jobTitle.getJobName());
            }
        }
        return result;
    }

    @Override
    public void updateLon() {
        updateJobLon();
    }

    @Override
    public List<ESJobDocument> littleVideo(ESJobSearch esJobSearch) {

        return iesJobSearchService.littleVideo(esJobSearch);

    }

    @Override
    public List<ESJobDocument> littleVideoRandom(String uuid, Integer count,String jobTitle) {
        ObjectMapper objectMapper = new ObjectMapper();
        List<Long> cacheList = new ArrayList<>();

        try {
            // 从Redis获取字符串格式的缓存
            String cacheJson = redisCache.getCacheObject("random_video:" + uuid);
            if (cacheJson != null && !cacheJson.isEmpty()) {
                // 将JSON字符串转换为List<Long>
                cacheList = objectMapper.readValue(cacheJson, new TypeReference<List<Long>>() {});
            }
        } catch (Exception e) {
            // 处理转换异常，重置缓存
            log.error("Failed to parse cache JSON for key: random_video:" + uuid, e);
            redisCache.deleteObject("random_video:" + uuid);
        }

        // 从服务获取随机视频
        List<ESJobDocument> esJobDocuments = iesJobSearchService.littleVideoRandom(cacheList, count,jobTitle);
        if(esJobDocuments.isEmpty()){
            esJobDocuments = iesJobSearchService.littleVideoRandom(new ArrayList<>(), count,jobTitle);
            redisCache.deleteObject("random_video:" + uuid);
        }


        // 提取新视频ID并添加到缓存列表
        List<Long> newJobIds = esJobDocuments.stream()
                .map(ESJobDocument::getJobId)
                .collect(Collectors.toList());
        cacheList.addAll(newJobIds);

        try {
            // 将更新后的列表转换为JSON字符串
            String updatedJson = objectMapper.writeValueAsString(cacheList);
            // 保存到Redis
            redisCache.setCacheObject("random_video:" + uuid, updatedJson);
        } catch (Exception e) {
            log.error("Failed to serialize cache list for key: random_video:" + uuid, e);
        }

        return esJobDocuments;
    }

    @Override
    public void importRow(String path) {
        try {
            logger.info("开始导入Excel数据...");
            // 读取Excel文件中的数据
            List<RowWork> rowWorks = ExcelToObject.readExcelToObjects(path, RowWork.class);
            logger.info("从Excel中读取到 {} 条数据", rowWorks.size());

            // 按ORG分类存储数据
            Map<String, List<RowWork>> orgDataMap = new HashMap<>();
            orgDataMap.put("智联招聘", new ArrayList<>());
            orgDataMap.put("58同城", new ArrayList<>());
            orgDataMap.put("前程无忧", new ArrayList<>());
            orgDataMap.put("猎聘网", new ArrayList<>());

            // 遍历数据，按ORG分类，每类最多取20000条
            for (RowWork rowWork : rowWorks) {
                String org = rowWork.getORG(); // 假设RowWork有getORG()方法
                if (orgDataMap.containsKey(org)) {
                    List<RowWork> orgList = orgDataMap.get(org);
                    if (orgList.size() < 20000) {
                        orgList.add(rowWork);
                    }
                }
            }

            // 统计各类数据量
            int zlCount = orgDataMap.get("智联招聘").size();
            int tcCount = orgDataMap.get("58同城").size();
            int qcCount = orgDataMap.get("前程无忧").size();
            int lpCount = orgDataMap.get("前程无忧").size();
            logger.info("智联招聘数据量: {}", zlCount);
            logger.info("58同城数据量: {}", tcCount);
            logger.info("前程无忧数据量: {}", qcCount);
            logger.info("猎聘网数据量: {}", lpCount);

            // 合并所有数据（智联招聘 + 58同城 + 前程无忧）
            List<RowWork> allData = new ArrayList<>();
            allData.addAll(orgDataMap.get("智联招聘"));
            allData.addAll(orgDataMap.get("58同城"));
            allData.addAll(orgDataMap.get("前程无忧"));
            allData.addAll(orgDataMap.get("猎聘网"));

            // 批量插入数据
            int batchSize = 50;
            int totalInserted = 0;
            List<RowWork> batchList = new ArrayList<>();

            for (RowWork rowWork : allData) {
//                Thread.sleep(500); // 控制处理速度（可选）
                batchList.add(rowWork);
                if (batchList.size() >= batchSize) {
                    try {
                        jobMapper.insertBatchRowWork(batchList);
                        totalInserted += batchList.size();
                        logger.info("成功插入 {} 条数据，当前总共插入 {} 条数据", batchList.size(), totalInserted);
                        batchList.clear();
                    } catch (Exception e) {
                        logger.error("批量插入失败: " + e.getMessage(), e);
                    }
                }
            }

            // 插入剩余数据
            if (!batchList.isEmpty()) {
                try {
                    jobMapper.insertBatchRowWork(batchList);
                    totalInserted += batchList.size();
                    logger.info("成功插入剩余的 {} 条数据，当前总共插入 {} 条数据", batchList.size(), totalInserted);
                } catch (Exception e) {
                    logger.error("批量插入剩余数据失败: " + e.getMessage(), e);
                }
            }

            logger.info("数据导入完成，总共插入 {} 条数据", totalInserted);
        } catch (Exception e) {
            logger.error("导入数据过程中发生异常: " + e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }
    @Override
    public void importData() {
        try {
            logger.info("开始导入数据...");

            // 获取字典数据
            List<BussinessDictData> dictData = iBussinessDictTypeService.selectDictDataByType("education");
            List<BussinessDictData> expData = iBussinessDictTypeService.selectDictDataByType("experience");
            List<BussinessDictData> areaData = iBussinessDictTypeService.selectDictDataByType("area");
            logger.info("成功获取字典数据: education={}, experience={}, area={}", dictData.size(), expData.size(), areaData.size());

            // 获取需要插入的行数据
            List<RowWork> rowWorks = this.baseMapper.selectAllInsertRowWork();
            logger.info("从数据库中获取到 {} 条待插入的行数据", rowWorks.size());

            // 获取公司数据并转换
            List<Company> companyRowList = this.baseMapper.selectRowCompany();
            List<Company> companyList = company2New(companyRowList);
            logger.info("成功转换 {} 条公司数据", companyList.size());

            // 批量插入公司数据
            int batchSizeCompany = 50; // 每批次插入50条数据
            List<Company> companyBatch = new ArrayList<>();
            int totalCompanyInserted = 0;

            for (Company company : companyList) {
                companyBatch.add(company);
                if (companyBatch.size() == batchSizeCompany) {
                    companyMapper.batchInsert(companyBatch); // 批量插入
                    totalCompanyInserted += companyBatch.size();
                    logger.info("成功插入 {} 条公司数据，当前总共插入 {} 条", companyBatch.size(), totalCompanyInserted);
                    companyBatch.clear(); // 清空批次
                }
            }

            // 插入剩余的记录
            if (!companyBatch.isEmpty()) {
                companyMapper.batchInsert(companyBatch);
                totalCompanyInserted += companyBatch.size();
                logger.info("成功插入剩余的 {} 条公司数据，当前总共插入 {} 条", companyBatch.size(), totalCompanyInserted);
            }

            // 获取所有公司数据
            List<Company> allCompany = companyMapper.selectCompanyList(new Company());
            logger.info("从数据库中获取到 {} 条公司数据", allCompany.size());

            // 批量插入职位数据
            List<Job> jobBatch = new ArrayList<>();
            int batchSize = 50;
            int totalJobInserted = 0;
            String string = UUID.randomUUID().toString();
            logger.info("生成唯一标识符: {}", string);
            LocalDateTime now = LocalDateTime.now();
            String formattedDate = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            for (RowWork rowWork : rowWorks) {
                try {
                    Job job = new Job();
                    if(StringUtil.isEmptyOrNull(rowWork.getAca112())){
                        continue;
                    }
                    job.setJobTitle(rowWork.getAca112());
                    job.setMinSalary(Long.valueOf(rowWork.getSalaryLow()));
                    job.setMaxSalary(Long.valueOf(rowWork.getSalaryHight()));
                    if(Objects.isNull(rowWork.getAac011())){
                        job.setEducation("-1");
                    }else {
                        job.setEducation(iBussinessDictDataService.findCode(dictData, rowWork.getAac011()));
                    }
                    if(Objects.isNull(rowWork.getExperience())){
                        job.setExperience("0");
                    }else {
                        job.setExperience(iBussinessDictDataService.findCode(expData, rowWork.getExperience()));
                    }
                    job.setCompanyName(rowWork.getAAB004());
                    job.setJobLocation(rowWork.getAAE006());
                    job.setPostingDate(formattedDate);
                    job.setJobCategory(rowWork.getJobCategory());
                    if (rowWork.getAcb240() == null) {
                        job.setVacancies(-1L);
                    } else {
                        job.setVacancies(Long.valueOf(rowWork.getAcb240().replace("人", "")));
                    }
                    if (rowWork.getLatitude() != null && !rowWork.getLatitude().isEmpty()) {
                        job.setLatitude(new BigDecimal(rowWork.getLatitude()));
                        job.setLongitude(new BigDecimal(rowWork.getLongitude()));
                    }
                    job.setView(0L);
                    job.setCompanyId(getCompanyId(allCompany, rowWork.getAAB004()));
                    job.setIsHot(0);
                    job.setApplyNum(0);
                    if(Objects.isNull(rowWork.getCounty())){
                        job.setJobLocationAreaCode(null);
                    }else {
                        job.setJobLocationAreaCode(Objects.requireNonNull(iBussinessDictDataService.findCode(areaData, rowWork.getCounty())));
                    }
                    job.setDescription(rowWork.getAcb22a());
                    job.setIsPublish(1);
                    job.setDataSource(rowWork.getORG());
                    job.setJobUrl(rowWork.getACE760());
                    job.setRemark(string);
                    job.setDelFlag("0");
                    job.setCreateBy(string);
                    job.setCreateTime(formattedDate);
                    job.setRowId(Long.valueOf(rowWork.getId()));

                    jobBatch.add(job);

                    // 当批次达到50条时，执行批量插入
                    if (jobBatch.size() == batchSize) {
                        this.baseMapper.batchInsert(jobBatch);
                        totalJobInserted += jobBatch.size();
                        logger.info("成功插入 {} 条职位数据，当前总共插入 {} 条", jobBatch.size(), totalJobInserted);
                        jobBatch.clear(); // 清空批次
                    }
                } catch (Exception e) {
                    logger.error("处理职位数据时发生异常: {}", e.getMessage(), e);
                }
            }

            // 插入剩余的记录
            if (!jobBatch.isEmpty()) {
                this.baseMapper.batchInsert(jobBatch);
                totalJobInserted += jobBatch.size();
                logger.info("成功插入剩余的 {} 条职位数据，当前总共插入 {} 条", jobBatch.size(), totalJobInserted);
            }

            logger.info("数据导入完成，总共插入 {} 条公司数据和 {} 条职位数据", totalCompanyInserted, totalJobInserted);
        } catch (Exception e) {
            logger.error("导入数据过程中发生异常: {}", e.getMessage(), e);
            throw new RuntimeException(e);
        }
        iesJobSearchService.resetTextCache();
    }

    private Long getCompanyId(List<Company> allCompany, String aab004) {
        for (Company company:allCompany){
            if(company.getName().equals(aab004)){
                return company.getCompanyId();
            }
        }
        return null;
    }

    private List<Company> company2New(List<Company> companyRowList) {
        List<BussinessDictData> scaleData = iBussinessDictTypeService.selectDictDataByType("scale");
        List<BussinessDictData> natureData = iBussinessDictTypeService.selectDictDataByType("company_nature");
        List<TreeSelect> treeSelects = industryService.appIndustry();
        List<Company> companyList = new ArrayList<>();
        LocalDateTime now = LocalDateTime.now();
        String formattedDate = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        for(Company company:companyRowList){
            Company companyNew = new Company();
            companyNew.setName(company.getName());
            companyNew.setLocation(company.getLocation());
            if(Objects.isNull(company.getScale())){
                companyNew.setScale("0");
            }else {
                companyNew.setScale(iBussinessDictDataService.findCode(scaleData,company.getScale()));
            }
            companyNew.setDelFlag("0");
            companyNew.setCreateBy("system");
            companyNew.setCreateTime(formattedDate);
            companyNew.setRemark("add");
            companyNew.setDescription(company.getDescription());
            companyNew.setIndustry(findIndustry(treeSelects,company));
            if(Objects.isNull(company.getNature())){
                companyNew.setNature("8");
            }else {
                companyNew.setNature(iBussinessDictDataService.findCode(natureData,company.getNature()));
            }
            companyList.add(companyNew);
        }
        return companyList;
    }


    private String findIndustry(List<TreeSelect> treeSelects, Company company) {
        if(company.getRemark()==null){return "";}
        if(Objects.equals(company.getRemark(), "")){return "";}
        for (TreeSelect treeSelect:treeSelects){
            if(treeSelect.getLabel().equals(company.getRemark())){
                if(company.getIndustry()==null){return String.valueOf(treeSelect.getId());}
                if(Objects.equals(company.getIndustry(), "")){return String.valueOf(treeSelect.getId());}
                for(TreeSelect treeSelectSub:treeSelect.getChildren()){
                    if(treeSelectSub.getLabel().equals(company.getIndustry())){
                        return String.valueOf(treeSelectSub.getId());
                    }
                }
            }
        }
        return "";
    }

    private List<ESJobDocument> userCollection(List<ESJobDocument> jobs){
        if(jobs.isEmpty()){return new ArrayList<>();}
        if(SiteSecurityUtils.isLogin()){
            //收藏
            Set<Long> collectionIds = jobCollectionMapper.selectList(Wrappers.<JobCollection>lambdaQuery()
                            .eq(JobCollection::getUserId, SiteSecurityUtils.getUserId())
                            .in(JobCollection::getJobId, jobs.stream().map(ESJobDocument::getJobId).collect(Collectors.toList())))
                    .stream().map(JobCollection::getJobId).collect(Collectors.toSet());
            for (ESJobDocument j : jobs) {
                if (collectionIds.contains(j.getJobId())) {
                    j.setIsCollection(1);
                }
            }
        }
        return jobs;
    }

    private List<ESJobDocument> sysUserCollection(List<ESJobDocument> jobs){
        if(jobs.isEmpty()){return new ArrayList<>();}
        if(SecurityUtils.isLogin()){
            //收藏
            Set<Long> collectionIds = jobCollectionMapper.selectList(Wrappers.<JobCollection>lambdaQuery()
                            .eq(JobCollection::getUserId, SecurityUtils.getUserId())
                            .in(JobCollection::getJobId, jobs.stream().map(ESJobDocument::getJobId).collect(Collectors.toList())))
                    .stream().map(JobCollection::getJobId).collect(Collectors.toSet());
            for (ESJobDocument j : jobs) {
                if (collectionIds.contains(j.getJobId())) {
                    j.setIsCollection(1);
                }
            }
        }
        return jobs;
    }

    @Override
    public void publishJob(Job job) {
        job.setIsPublish(0);
//        Long userId = SiteSecurityUtils.getUserId();
//        Company company = companyMapper.selectOne(Wrappers.<Company>lambdaQuery().eq(Company::getUserId, userId).eq(Company::getStatus, 1));
//        if(Objects.isNull(company)){
//            throw new ServiceException("请先完成招聘企业登记");
//        }
//        job.setCompanyId(company.getCompanyId());
        //保存region
        if(job.getJobLocationAreaCode()!=null){
            job.setRegionCode(StringUtil.getAreaToRegionCode(String.valueOf(job.getJobLocationAreaCode())));
        }
        jobMapper.insert(job);

        //修改联系人
        List<JobContact> jobContactList = job.getJobContactList() != null ? job.getJobContactList() : Collections.emptyList();
        jobContactList.stream().filter(Objects::nonNull).forEach(it -> it.setJobId(job.getJobId()));
        if(!jobContactList.isEmpty()){
            jobContactMapper.batchInsert(jobContactList);
        }
        //添加附件
        List<File> filesList = job.getFilesList() != null ? job.getFilesList() : Collections.emptyList();
        List<Long> longs = filesList.stream().filter(Objects::nonNull).map(File::getBussinessid).collect(Collectors.toList());
        if(!longs.isEmpty()){
            fileMapper.updateBussinessids(longs,job.getJobId());
        }

        iesJobSearchService.updateJob(job.getJobId());
    }

    @Override
    public List<AppUser> selectApplyJobUserList(Long jobId) {
        return jobMapper.selectApplyJobUserList(jobId);
    }

    @Override
    public List<ESJobDocument> sysRecommend(ESJobSearch esJobSearch) {
        Long userId = SecurityUtils.isLogin() ? SecurityUtils.getUserId() : null;
        List<Long> viewedJobIds = new ArrayList<>();
        String jobCacheKey = "";
        AppUser appUser = null;

        if (userId != null) {
            jobCacheKey = CacheConstants.SYS_JOB_IDS + userId;
            try {
                JSONArray cacheObject = redisCache.getCacheObject(jobCacheKey);
                if (Objects.isNull(cacheObject)) {
                    cacheObject = new JSONArray();
                }
                viewedJobIds = cacheObject.stream()
                        .map(o -> Long.parseLong(o.toString()))
                        .distinct().limit(100).collect(Collectors.toList());

                String idCard = RoleUtils.getCurrentUseridCard();
                if (StringUtils.isNotEmpty(idCard)) {
                    appUser = appUserService.selectAppuserByIdcard(idCard);
                }
            } catch (Exception e) {
                log.error("获取用户已查看岗位缓存失败", e);
                viewedJobIds = new ArrayList<>();
            }
        }

        List<ESJobDocument> jobListResult = new ArrayList<>();
        try {
            jobListResult = iesJobSearchService.selectSysTextListExceptJobId(esJobSearch, viewedJobIds, appUser);
            //降级策略：如果过滤后无数据，忽略已查看记录重新查询
            if (CollectionUtils.isEmpty(jobListResult) && !viewedJobIds.isEmpty()) {
                log.warn("用户{}已查看岗位过多，忽略过滤条件重新查询");
                jobListResult = iesJobSearchService.selectSysTextListExceptJobId(esJobSearch, new ArrayList<>(), appUser);
            }
        } catch (Exception e) {
            log.error("ES推荐岗位查询失败", e);
            return new ArrayList<>();
        }

        if (userId != null && !CollectionUtils.isEmpty(jobListResult)) {
            try {
                List<Long> newJobIds = jobListResult.stream().map(ESJobDocument::getJobId).distinct().collect(Collectors.toList());
                List<Long> updatedJobIds = Stream.concat(viewedJobIds.stream(), newJobIds.stream()).distinct().limit(100).collect(Collectors.toList());
                redisCache.setCacheObject(jobCacheKey, updatedJobIds, 24, TimeUnit.HOURS);
            } catch (Exception e) {
                log.error("更新用户已查看岗位缓存失败", e);
            }
        }

        List<ESJobDocument> esJobDocuments = sysUserCollection(jobListResult);
        return esJobDocuments;
    }

    public List<Job> selectAllJob(){
        int batchSize = 1000; // 每批次处理的数据量
        int offset = 0;
        Map<String,Object> params=new HashMap<>();
        params.put("batchSize", batchSize);
        params.put("offset", offset*batchSize);
        return jobMapper.selectAllJob(params);
    }

    @Override
    public Integer getTotals(Job job) {
        return jobMapper.getTotals(job);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void uploadFileJob(List<JobExcelVo> list) {
        //查询所有企业
        Map<String, Long> companyNameToIdMap = companyNameToIdMap(list);
        //岗位字典转换
        List<Job> jobList = buildJobList(list, companyNameToIdMap);
        //岗位去重
        List<Job> dedupedJobList = dedupJobList(jobList);
        //批量保存岗
        jobMapper.updateFileBatchInsert(dedupedJobList);
        //构建联系人列表（关联去重后的岗位）
        List<JobContact> contactList = buildJobContactList(dedupedJobList, list, companyNameToIdMap);
        //联系人去重（按：企业ID+联系人手机号 去重；企业ID为0/null时，仅按手机号去重）
        List<JobContact> dedupedContactList = dedupContactList(contactList, dedupedJobList);
        //批量保存联系人
        if (CollectionUtils.isNotEmpty(dedupedContactList)) {
            jobContactMapper.batchInsert(dedupedContactList);
        }
    }

    /**
     * 企业名称查询关联（核心业务逻辑）
     */
    private Map<String, Long> companyNameToIdMap(List<JobExcelVo> allExcelVoList) {
        Set<String> companyNameSet = allExcelVoList.stream()
                .map(JobExcelVo::getCompanyName)
                .filter(StringUtils::hasText)
                .collect(Collectors.toSet());

        if (companyNameSet.isEmpty()) {
            return Collections.emptyMap();
        }

        List<Company> companies = companyMapper.selectByNames(new ArrayList<>(companyNameSet));
        if (CollectionUtils.isEmpty(companies)) {
            return Collections.emptyMap();
        }
        return companies.stream()
                .collect(Collectors.toMap(
                        Company::getName,
                        Company::getCompanyId,
                        (v1, v2) -> v1
                ));
    }

    /**
     * 字典转换 + Job对象构建（业务数据转换）
     */
    private List<Job> buildJobList(List<JobExcelVo> allExcelVoList, Map<String, Long> companyNameToIdMap) {
        return allExcelVoList.stream().map(it -> {
            Job job = new Job();
            BeanUtils.copyProperties(it, job);
            //字典转换
            String education = DictUtils.getDictValue("education", it.getEducation());
            if (education == null || education.trim().isEmpty()) {
                education = StringUtil.getEduCodeByDesc(it.getEducation());
            }
            String experience = DictUtils.getDictValue("experience", it.getExperience());
            String jobType = DictUtils.getDictValue("job_type", it.getJobType());
            String type = DictUtils.getDictValue("position_type", it.getType());
            String areaCode = DictUtils.getDictValue("area", it.getJobLocation());
            // 字段赋值
            job.setEducation(education);
            job.setExperience(experience);
            job.setJobType(jobType);
            job.setType(type);
            job.setJobLocation(StringUtils.isBlank(it.getJobAddress()) ? it.getJobLocation() : it.getJobAddress());
            job.setJobLocationAreaCode(StringUtils.isBlank(areaCode) ? "0" : areaCode);
            job.setIsPublish(1);
            job.setDataSource("1");
            job.setPostingDate(DateUtils.dateTimeNow(DateUtils.YYYY_MM_DD_HH_MM_SS));
            job.setView(0L);

            String companyName = it.getCompanyName();
            job.setCompanyName(companyName);
            job.setCompanyId(companyNameToIdMap.getOrDefault(companyName, null));

            return job;
        }).collect(Collectors.toList());
    }

    /**
     * 岗位去重（业务规则：企业ID+岗位名称+工作地点唯一）
     */
    private List<Job> dedupJobList(List<Job> jobList) {
        Map<String, Job> dedupMap = new LinkedHashMap<>();
        for (Job job : jobList) {
            String dedupKey = String.format(
                    "%s_%s_%s",
                    Optional.ofNullable(job.getCompanyId()).orElse(null),
                    job.getJobTitle().trim(),
                    job.getJobLocation().trim(),
                    job.getMinSalary(),
                    job.getMaxSalary()
            );
            dedupMap.putIfAbsent(dedupKey, job);
        }
        return new ArrayList<>(dedupMap.values());
    }

    /**
     * 构建联系人列表（业务关联）
     */
    private List<JobContact> buildJobContactList(List<Job> dedupedJobList, List<JobExcelVo> allExcelVoList, Map<String, Long> companyNameToIdMap) {
        List<JobContact> contactList = new ArrayList<>();

        Map<String, Job> jobMatchMap = dedupedJobList.stream()
                .collect(Collectors.toMap(
                        job -> String.format(
                                "%s_%s",
                                job.getJobTitle().trim(),
                                job.getJobLocation().trim()
                        ),
                        job -> job
                ));

        for (JobExcelVo vo : allExcelVoList) {
            String jobTitle = vo.getJobTitle().trim();
            String jobLocation = StringUtils.isBlank(vo.getJobAddress()) ? vo.getJobLocation().trim() : vo.getJobAddress().trim();
            String matchKey = String.format("%s_%s", jobTitle, jobLocation);

            Job matchedJob = jobMatchMap.get(matchKey);
            if (matchedJob == null) {
                continue;
            }

            List<String> contactNames = StringUtil.splitCellMultiLineData(vo.getContactPerson());
            List<String> contactPhones = StringUtil.splitContactPhones(vo.getContactPersonPhone());

            if (contactNames.isEmpty() || contactPhones.isEmpty()) {
                System.out.printf(
                        "警告：岗位【%s】的联系人姓名或手机号为空！姓名：%s，手机号：%s，跳过该组数据%n",
                        matchedJob.getJobTitle(), vo.getContactPerson(), vo.getContactPersonPhone()
                );
                continue;
            }

            int nameSize = contactNames.size();
            int phoneSize = contactPhones.size();
            int phoneIndex = 0;

            for (int nameIdx = 0; nameIdx < nameSize; nameIdx++) {
                String currentName = contactNames.get(nameIdx).trim();
                if (StringUtils.isBlank(currentName)) {
                    continue;
                }

                int phonesForCurrentName;
                if (nameSize == 1) {
                    phonesForCurrentName = phoneSize - phoneIndex;
                } else {
                    phonesForCurrentName = 1;
                }

                // 避免索引越界，修正实际可匹配的手机号数量
                int actualPhoneCount = Math.min(phonesForCurrentName, phoneSize - phoneIndex);
                if (actualPhoneCount <= 0) {
                    break;
                }

                // 为当前姓名分配对应的手机号（1个或多个）
                for (int p = 0; p < actualPhoneCount; p++) {
                    String currentPhone = contactPhones.get(phoneIndex).trim();
                    if (StringUtils.isBlank(currentPhone)) {
                        phoneIndex++;
                        continue;
                    }

                    // 封装联系人数据
                    JobContact contact = new JobContact();
                    contact.setJobId(matchedJob.getJobId());
                    contact.setContactPerson(currentName);
                    contact.setContactPersonPhone(currentPhone);
                    contactList.add(contact);

                    phoneIndex++;
                }
            }

            if (phoneIndex < phoneSize) {
                System.out.printf(
                        "提示：岗位【%s】有 %d 个手机号未分配对应姓名，已忽略%n",
                        matchedJob.getJobTitle(), phoneSize - phoneIndex
                );
            }
        }

        return contactList;
    }

    /**
     * 联系人去重（兼容企业ID为0的情况）
     */
    private List<JobContact> dedupContactList(List<JobContact> contactList, List<Job> dedupedJobList) {
        if (CollectionUtils.isEmpty(contactList)) {
            return new ArrayList<>();
        }

        Set<Long> validJobIds = dedupedJobList.stream().map(Job::getJobId).filter(Objects::nonNull).collect(Collectors.toSet());

        Map<String, JobContact> dedupMap = new LinkedHashMap<>();
        for (JobContact contact : contactList) {
            if (StringUtils.isBlank(contact.getContactPersonPhone())) {
                System.out.printf("跳过联系人：姓名=%s（手机号为空）%n", contact.getContactPerson());
                continue;
            }

            Long jobId = contact.getJobId();
            if (jobId == null || !validJobIds.contains(jobId)) {
                System.out.printf("跳过联系人：姓名=%s（岗位ID无效或不存在）%n", contact.getContactPerson());
                continue;
            }

            String dedupKey = String.format("%s_%s",jobId,contact.getContactPersonPhone().trim());

            dedupMap.putIfAbsent(dedupKey, contact);
        }

        return new ArrayList<>(dedupMap.values());
    }

    @Transactional
    @Override
    public int jobDown(Long jobId) {
        Job job=new Job();
        job.setJobId(jobId);
        job.setJobStatus("1");
        job.setDownTime(DateUtils.dateTimeNow(DateUtils.YYYY_MM_DD_HH_MM_SS));
        int i=jobMapper.updateStatus(job);
        //刷新单条数据
        iesJobSearchService.updateJob(jobId);
        return i;
    }

    @Transactional
    @Override
    public int jobUp(Long jobId) {
        Job job=new Job();
        job.setJobId(jobId);
        job.setJobStatus("0");
        job.setDownTime(DateUtils.getMonthThree(DateUtils.YYYY_MM_DD_HH_MM_SS));
        //刷新单条数据
        int i=jobMapper.updateStatus(job);
        iesJobSearchService.updateJob(jobId);
        return i;
    }

    @Override
    public void updateJobDown() {
        //查询下架岗位
        List<Job> list=jobMapper.getJobDownList();
        List<Long> ids=list.stream().map(Job::getJobId).collect(Collectors.toList());
        //更新岗位状态
        jobMapper.updateJobDown(ids);
        //刷新es
        ids.forEach(it->{
            iesJobSearchService.updateJob(it);
        });
    }
}
