package org.dromara.project.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdcardUtil;
import cn.hutool.core.util.PhoneUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.dromara.common.core.constant.DateConstant;
import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.project.constant.BusConstructionUserConstant;
import org.dromara.project.domain.*;
import org.dromara.project.domain.enums.BusAttendanceClockStatusEnum;
import org.dromara.project.domain.enums.BusAttendanceCommuterEnum;
import org.dromara.project.domain.enums.BusConstructionUserAttendanceStatusEnum;
import org.dromara.project.domain.enums.BusConstructionUserFileStatusEnum;
import org.dromara.project.domain.exportvo.BusConstructionUserExportVo;
import org.dromara.project.domain.dto.constructionuser.*;
import org.dromara.project.domain.vo.constructionuser.BusConstructionUserAttendanceByDay;
import org.dromara.project.domain.vo.constructionuser.BusConstructionUserAttendanceMonthVo;
import org.dromara.project.domain.vo.constructionuser.BusConstructionUserAttendanceTotalVo;
import org.dromara.project.domain.vo.constructionuser.BusConstructionUserGisVo;
import org.dromara.project.domain.vo.constructionuser.BusConstructionUserVo;
import org.dromara.project.domain.vo.contractor.BusContractorVo;
import org.dromara.project.mapper.BusConstructionUserMapper;
import org.dromara.project.service.*;
import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.service.ISysDictTypeService;
import org.dromara.system.service.ISysOssService;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.YearMonth;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 施工人员Service业务层处理
 *
 * @author lcj
 * @date 2025-03-07
 */
@Service
public class BusConstructionUserServiceImpl extends ServiceImpl<BusConstructionUserMapper, BusConstructionUser>
    implements IBusConstructionUserService {

    @Resource
    private IBusProjectService projectService;

    @Lazy
    @Resource
    private IBusContractorService contractorService;

    @Lazy
    @Resource
    private IBusProjectTeamMemberService projectTeamMemberService;

    @Resource
    private ISysOssService ossService;

    @Lazy
    @Resource
    private IBusConstructionBlacklistService constructionBlacklistService;

    @Lazy
    @Resource
    private IBusWorkWageService workWageService;

    @Lazy
    @Resource
    private IBusConstructionUserFileService constructionUserFileService;

    @Resource
    private ISysDictTypeService dictTypeService;

    @Lazy
    @Resource
    private IBusAttendanceService attendanceService;

    /**
     * 查询施工人员
     *
     * @param id 主键
     * @return 施工人员
     */
    @Override
    public BusConstructionUserVo queryById(Long id) {
        BusConstructionUser constructionUser = this.getById(id);
        if (constructionUser == null) {
            throw new ServiceException("施工人员信息不存在", HttpStatus.NOT_FOUND);
        }
        return this.getVo(constructionUser);
    }

    /**
     * 分页查询施工人员列表
     *
     * @param req       查询条件
     * @param pageQuery 分页参数
     * @return 施工人员分页列表
     */
    @Override
    public TableDataInfo<BusConstructionUserVo> queryPageList(BusConstructionUserQueryReq req, PageQuery pageQuery) {
        // 查询数据库
        Page<BusConstructionUser> result = this.page(pageQuery.build(), this.buildQueryWrapper(req));
        return TableDataInfo.build(getVoPage(result));
    }

    /**
     * 分页查询施工人员考勤列表
     *
     * @param req       查询条件
     * @param pageQuery 分页参数
     * @return 施工人员考勤分页列表
     */
    @Override
    public TableDataInfo<BusConstructionUserAttendanceTotalVo> queryPageAttendanceList(BusConstructionUserAttendanceQueryReq req,
                                                                                       PageQuery pageQuery) {
        return TableDataInfo.build(getAttendanceTotalVoPage(req, pageQuery));
    }

    /**
     * 查询施工人员月考勤列表
     *
     * @param req 查询条件
     * @return 施工人员考勤月列表
     */
    @Override
    public List<BusConstructionUserAttendanceMonthVo> queryAttendanceMonthList(BusConstructionUserAttendanceMonthReq req) {
        Long id = req.getUserId();
        String clockMonth = req.getClockMonth();
        if (this.getById(id) == null) {
            throw new ServiceException("施工人员信息不存在", HttpStatus.NOT_FOUND);
        }
        // 解析月份
        YearMonth yearMonth;
        if (StringUtils.isNotBlank(clockMonth)) {
            // 校验月份格式
            if (!DateConstant.YEAR_MONTH_PATTERN.matcher(clockMonth).matches()) {
                throw new ServiceException("月份格式不正确", HttpStatus.BAD_REQUEST);
            }
            yearMonth = YearMonth.parse(clockMonth);
        } else {
            // 如果月份为空，则默认查询当前月份
            yearMonth = YearMonth.now();
        }
        // 计算当月第一天 / 最后一天
        Date start = DateUtils.toDate(yearMonth.atDay(1));
        Date end = DateUtils.toDate(yearMonth.atEndOfMonth());
        // 查询当月考勤记录
        List<BusAttendance> dateList = attendanceService.lambdaQuery()
            .eq(BusAttendance::getUserId, id)
            .between(BusAttendance::getClockDate, start, end)
            .list();
        Map<Date, List<BusAttendance>> dateListMap = dateList.stream().collect(Collectors.groupingBy(BusAttendance::getClockDate));
        // 获取所有打卡图片id
        List<Long> picIds = dateList.stream().map(BusAttendance::getFacePic).filter(Objects::nonNull).map(Long::valueOf).toList();
        Map<Long, List<SysOssVo>> ossIdUrlMap = ossService.listByIds(picIds)
            .stream().collect(Collectors.groupingBy(SysOssVo::getOssId));
        // 遍历每天，计算考勤状态
        List<BusConstructionUserAttendanceMonthVo> respList = new ArrayList<>();
        dateListMap.forEach((date, attendanceList) -> {
            BusConstructionUserAttendanceMonthVo resp = new BusConstructionUserAttendanceMonthVo();
            resp.setId(id);
            resp.setClockDate(date);
            // 获取考勤记录
            BusConstructionUserAttendanceByDay day = BusConstructionUserAttendanceByDay.build(attendanceList);
            // 填充打卡图片url
            if (day != null) {
                if (day.getUpClockPicId() != null) {
                    day.setUpClockPic(ossIdUrlMap.get(day.getUpClockPicId()).get(0).getUrl());
                }
                if (day.getDownClockPicId() != null) {
                    day.setDownClockPic(ossIdUrlMap.get(day.getDownClockPicId()).get(0).getUrl());
                }
            }
            String clockInStatus = null;
            String clockOutStatus = null;
            String clockAllDayStatus = null;
            String status;
            for (BusAttendance attendance : attendanceList) {
                // 获取上下班状态
                if (BusAttendanceCommuterEnum.CLOCKIN.getValue().equals(attendance.getCommuter())) {
                    clockInStatus = attendance.getClockStatus();
                } else if (BusAttendanceCommuterEnum.CLOCKOUT.getValue().equals(attendance.getCommuter())) {
                    clockOutStatus = attendance.getClockStatus();
                } else {
                    clockAllDayStatus = attendance.getClockStatus();
                }
            }
            // 统计当天考勤状态
            if (BusAttendanceClockStatusEnum.LEAVE.getValue().equals(clockAllDayStatus)) {
                status = BusConstructionUserAttendanceStatusEnum.LEAVE.getValue();
            } else if (BusAttendanceClockStatusEnum.UNCLOCK.getValue().equals(clockInStatus)
                && BusAttendanceClockStatusEnum.UNCLOCK.getValue().equals(clockOutStatus)) {
                status = BusConstructionUserAttendanceStatusEnum.UNCLOCK.getValue();
            } else if (BusAttendanceClockStatusEnum.UNCLOCK.getValue().equals(clockInStatus)
                || BusAttendanceClockStatusEnum.UNCLOCK.getValue().equals(clockOutStatus)) {
                status = BusConstructionUserAttendanceStatusEnum.ONECLOCK.getValue();
            } else {
                status = BusConstructionUserAttendanceStatusEnum.NORMAL.getValue();
            }
            resp.setStatus(status);
            resp.setClockList(day);
            respList.add(resp);
        });
        // 按打卡日期正序排列
        respList.sort(Comparator.comparing(BusConstructionUserAttendanceMonthVo::getClockDate));
        return respList;
    }

    /**
     * 查询符合条件的施工人员列表
     *
     * @param req 查询条件
     * @return 施工人员列表
     */
    @Override
    public List<BusConstructionUserExportVo> queryList(BusConstructionUserQueryReq req) {
        LambdaQueryWrapper<BusConstructionUser> lqw = this.buildQueryWrapper(req);
        List<BusConstructionUserVo> constructionUserVoList = this.list(lqw).stream().map(this::getVo).toList();
        // 关联项目信息
        Set<Long> projectIdList = constructionUserVoList.stream().map(BusConstructionUserVo::getProjectId)
            .collect(Collectors.toSet());
        Map<Long, List<BusProject>> projectIdProjectMap = projectService.listByIds(projectIdList).stream()
            .collect(Collectors.groupingBy(BusProject::getId));
        List<BusConstructionUserExportVo> constructionUserExportVoList = new ArrayList<>();
        if (CollUtil.isNotEmpty(constructionUserVoList)) {
            constructionUserExportVoList = constructionUserVoList.stream().map(constructionUserVo -> {
                BusConstructionUserExportVo constructionUserExportVo = new BusConstructionUserExportVo();
                BeanUtils.copyProperties(constructionUserVo, constructionUserExportVo);
                // 关联分包公司信息
                constructionUserExportVo.setContractorName(constructionUserVo.getContractorVo().getName());
                // 关联项目信息
                Long projectId = constructionUserVo.getProjectId();
                String projectName = null;
                if (projectIdProjectMap.containsKey(projectId)) {
                    projectName = projectService.getVo(projectIdProjectMap.get(projectId).get(0)).getProjectName();
                }
                constructionUserExportVo.setProjectName(projectName);
                return constructionUserExportVo;
            }).toList();
        }
        return constructionUserExportVoList;
    }

    /**
     * 获取施工人员大屏数据
     *
     * @param req 查询条件
     * @return 施工人员大屏数据
     */
    @Override
    public BusConstructionUserGisVo getGisData(BusConstructionUserGisReq req) {
        // 参数校验
        Long projectId = req.getProjectId();
        BusProject project = projectService.getById(projectId);
        if (project == null) {
            throw new ServiceException("项目信息不存在", HttpStatus.NOT_FOUND);
        }
        // 获取大屏数据
        BusConstructionUserGisVo resp = new BusConstructionUserGisVo();
        // 获取施工人员总数
        Long count = this.lambdaQuery()
            .eq(BusConstructionUser::getProjectId, projectId).count();
        resp.setPeopleCount(count);
        // 获取考勤数据
        List<Long> attendancePeopleList = attendanceService.listAttendancePeopleByProjectId(projectId);
        resp.setAttendanceCount((long) attendancePeopleList.size());
        // 计算考勤率
        resp.setAttendanceRate(count == 0 ? "0" : String.format("%.2f", attendancePeopleList.size() * 100.0 / count));
        return resp;
    }

    /**
     * 新增施工人员
     *
     * @param req 施工人员
     * @return 是否新增成功
     */
    @Override
    public Long insertByBo(BusConstructionUserCreateReq req) {
        // 将实体类和 DTO 进行转换
        BusConstructionUser constructionUser = new BusConstructionUser();
        BeanUtils.copyProperties(req, constructionUser);
        // 数据校验
        validEntityBeforeSave(constructionUser, true);
        Long userId = LoginHelper.getUserId();
        projectService.validAuth(req.getProjectId(), userId);
        // 操作数据库
        boolean save = this.save(constructionUser);
        if (!save) {
            throw new ServiceException("新增施工人员失败，数据库异常", HttpStatus.ERROR);
        }
        return constructionUser.getId();
    }

    /**
     * 修改施工人员
     *
     * @param req 施工人员
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(BusConstructionUserUpdateReq req) {
        // 将实体类和 DTO 进行转换
        BusConstructionUser constructionUser = new BusConstructionUser();
        BeanUtils.copyProperties(req, constructionUser);
        // 数据校验
        validEntityBeforeSave(constructionUser, false);
        // 判断是否存在
        BusConstructionUser oldConstructionUser = this.getById(constructionUser.getId());
        if (oldConstructionUser == null) {
            throw new ServiceException("修改施工人员失败，数据不存在", HttpStatus.NOT_FOUND);
        }
        // 判断当前操作用户是否有权限
        Long userId = LoginHelper.getUserId();
        projectService.validAuth(oldConstructionUser.getProjectId(), userId);
        // 操作数据库
        return this.updateById(constructionUser);
    }

    /**
     * 修改施工人员工资
     *
     * @param req 修改施工人员工资对象
     * @return 是否修改成功
     */
    @Override
    public Boolean updateSalary(BusConstructionUserUpdateSalaryReq req) {
        Long id = req.getId();
        Long salary = req.getSalary();
        // 判断对应施工人员是否存在
        BusConstructionUser oldConstructionUser = this.getById(id);
        if (oldConstructionUser == null) {
            throw new ServiceException("施工人员信息不存在", HttpStatus.NOT_FOUND);
        }
        // 判断当前操作用户是否有权限
        Long userId = LoginHelper.getUserId();
        projectService.validAuth(oldConstructionUser.getProjectId(), userId);
        BusConstructionUser constructionUser = new BusConstructionUser();
        constructionUser.setId(id);
        // 修改薪水
        constructionUser.setSalary(Objects.requireNonNullElse(salary, 0L));
        // 操作数据库
        return this.updateById(constructionUser);
    }

    /**
     * 修改施工人员打卡状态
     *
     * @param req 修改施工人员打卡状态对象
     * @return 是否修改成功
     */
    @Override
    public Boolean updateClock(BusConstructionUserUpdateClockReq req) {
        Long id = req.getId();
        String clock = req.getClock();
        // 判断对应施工人员是否存在
        BusConstructionUser oldConstructionUser = this.getById(id);
        if (oldConstructionUser == null) {
            throw new ServiceException("施工人员信息不存在", HttpStatus.NOT_FOUND);
        }
        // 判断当前操作用户是否有权限
        Long userId = LoginHelper.getUserId();
        projectService.validAuth(oldConstructionUser.getProjectId(), userId);
        if (clock.equals(oldConstructionUser.getClock())) {
            return true;
        }
        BusConstructionUser constructionUser = new BusConstructionUser();
        constructionUser.setId(id);
        constructionUser.setClock(clock);
        return this.updateById(constructionUser);
    }

    /**
     * 修改施工人员项目(人员迁移)
     *
     * @param req 修改施工人员项目对象
     * @return 是否修改成功
     */
    @Override
    public Boolean changeUserProject(BusConstructionUserChangeProjectReq req) {
        Long id = req.getId();
        // 判断对应施工人员是否存在
        BusConstructionUser constructionUser = this.getById(id);
        if (constructionUser == null) {
            throw new ServiceException("施工人员信息不存在", HttpStatus.NOT_FOUND);
        }
        // 判断当前操作用户是否有权限
        Long userId = LoginHelper.getUserId();
        projectService.validAuth(List.of(constructionUser.getProjectId(), req.getProjectId()), userId);
        // 判断用户是否还存在于班组
        Long count = projectTeamMemberService.lambdaQuery().eq(BusProjectTeamMember::getMemberId, id).count();
        if (count > 0) {
            throw new ServiceException("施工人员还未退场，不能修改", HttpStatus.BAD_REQUEST);
        }
        // 数据校验
        if (req.getProjectId().equals(constructionUser.getProjectId())) {
            throw new ServiceException("已在当前项目下，请勿重复修改", HttpStatus.BAD_REQUEST);
        }
        if (req.getContractorId().equals(constructionUser.getContractorId())) {
            throw new ServiceException("已在当前分包公司下，请勿重复修改", HttpStatus.BAD_REQUEST);
        }
        BusConstructionUser validUser = new BusConstructionUser();
        BeanUtils.copyProperties(req, validUser);
        this.validEntityBeforeSave(validUser, false);
        // 操作数据库，更新数据
        LambdaUpdateWrapper<BusConstructionUser> lambdaUpdate = Wrappers.lambdaUpdate(BusConstructionUser.class)
            .eq(BusConstructionUser::getId, id)
            .set(BusConstructionUser::getProjectId, req.getProjectId())
            .set(BusConstructionUser::getContractorId, req.getContractorId());
        return this.update(lambdaUpdate);
    }

    /**
     * 批量修改施工人员状态
     *
     * @param req 批量修改施工人员状态对象
     * @return 是否修改成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean batchUpdateStatus(BusConstructionUserBatchUpdateStatusReq req) {
        List<Long> idList = req.getIdList();
        String status = req.getStatus();
        // 查询对应数据是否存在
        if (CollUtil.isEmpty(idList)) {
            throw new ServiceException("施工人员 id 不能为空", HttpStatus.BAD_REQUEST);
        }
        List<BusConstructionUser> constructionUserList = this.listByIds(idList);
        // 判断对应数据是否都存在
        if (constructionUserList.size() != idList.size()) {
            throw new ServiceException("修改施工人员状态失败，数据缺失", HttpStatus.BAD_REQUEST);
        }
        // 判断当前操作用户是否有权限
        Long userId = LoginHelper.getUserId();
        List<Long> projectIdList = constructionUserList.stream()
            .map(BusConstructionUser::getProjectId)
            .distinct().toList();
        projectService.validAuth(projectIdList, userId);
        // 批量修改
        List<BusConstructionUser> list = constructionUserList.stream()
            .filter(user -> !status.equals(user.getStatus()))
            .map(user -> {
                BusConstructionUser constructionUser = new BusConstructionUser();
                constructionUser.setId(user.getId());
                constructionUser.setStatus(status);
                return constructionUser;
            })
            .toList();
        if (CollUtil.isNotEmpty(list)) {
            boolean result = this.updateBatchById(list);
            if (!result) {
                throw new ServiceException("修改施工人员状态失败，数据库异常", HttpStatus.ERROR);
            }
        }
        return true;
    }

    /**
     * 根据项目id批量修改施工人员打卡状态
     *
     * @param req 批量修改施工人员打卡状态对象
     * @return 是否修改成功
     */
    @Override
    public Boolean batchUpdateClockByProjectId(BusConstructionUserBatchUpdateClockReq req) {
        Long projectId = req.getProjectId();
        String clock = req.getClock();
        // 校验打卡状态是否为空
        if (StringUtils.isEmpty(clock)) {
            throw new ServiceException("打卡状态不能为空", HttpStatus.BAD_REQUEST);
        }
        // 校验项目id和对应项目是否存在
        if (projectId == null) {
            throw new ServiceException("项目 id 不能为空", HttpStatus.BAD_REQUEST);
        }
        if (projectService.getById(projectId) == null) {
            throw new ServiceException("对应项目不存在", HttpStatus.NOT_FOUND);
        }
        // 权限校验
        Long userId = LoginHelper.getUserId();
        projectService.validAuth(projectId, userId);
        // 操作数据库，批量修改
        LambdaUpdateWrapper<BusConstructionUser> lambdaUpdate = Wrappers.lambdaUpdate(BusConstructionUser.class)
            .eq(BusConstructionUser::getProjectId, projectId)
            .set(BusConstructionUser::getClock, clock);
        boolean update = this.update(lambdaUpdate);
        if (!update) {
            throw new ServiceException("修改施工人员打卡状态失败，数据库异常", HttpStatus.ERROR);
        }
        return true;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(BusConstructionUser entity, Boolean create) {
        // TODO 做一些数据校验,如唯一约束
        // 校验项目id和对应项目是否存在
        Long projectId = entity.getProjectId();
        // 校验分包公司id和对应项目是否存在
        Long contractorId = entity.getContractorId();
        String typeOfWork = entity.getTypeOfWork();
        String wageMeasureUnit = entity.getWageMeasureUnit();
        String phone = entity.getPhone();
        String sfzNumber = entity.getSfzNumber();
        if (projectId == null) {
            throw new ServiceException("项目 id 不能为空", HttpStatus.BAD_REQUEST);
        }
        if (create) {
            if (contractorId == null) {
                throw new ServiceException("分包公司 id 不能为空", HttpStatus.BAD_REQUEST);
            }
            if (StringUtils.isEmpty(typeOfWork)) {
                throw new ServiceException("工种不能为空", HttpStatus.BAD_REQUEST);
            }
            if (StringUtils.isEmpty(wageMeasureUnit)) {
                throw new ServiceException("工资计量单位不能为空", HttpStatus.BAD_REQUEST);
            }
        }
        if (projectService.getById(projectId) == null) {
            throw new ServiceException("对应项目不存在", HttpStatus.NOT_FOUND);
        }
        if (contractorId != null) {
            BusContractor contractor = contractorService.getById(contractorId);
            if (contractor == null) {
                throw new ServiceException("分包单位信息不存在", HttpStatus.NOT_FOUND);
            }
            Long contractorProjectId = contractor.getProjectId();
            if (!projectId.equals(contractorProjectId)) {
                throw new ServiceException("分包单位不属于当前项目，请重新选择", HttpStatus.BAD_REQUEST);
            }
        }
        if (StringUtils.isNotEmpty(wageMeasureUnit) && StringUtils.isNotEmpty(typeOfWork)) {
            LambdaQueryWrapper<BusWorkWage> lqw = Wrappers.lambdaQuery(BusWorkWage.class)
                .eq(BusWorkWage::getProjectId, projectId)
                .eq(BusWorkWage::getWorkType, typeOfWork)
                .eq(BusWorkWage::getWageMeasureUnit, wageMeasureUnit);
            long count = workWageService.count(lqw);
            if (count <= 0) {
                throw new ServiceException("当前工种没有定义工资标准，请前往工种薪水设置进行设置后再选择", HttpStatus.BAD_REQUEST);
            }
        }
        if (StringUtils.isNotEmpty(phone) && !PhoneUtil.isPhone(phone)) {
            throw new ServiceException("手机号码格式不正确", HttpStatus.BAD_REQUEST);
        }
        if (StringUtils.isNotEmpty(sfzNumber) && !IdcardUtil.isValidCard(sfzNumber)) {
            throw new ServiceException("身份证号码格式不正确", HttpStatus.BAD_REQUEST);
        }
    }

    /**
     * 校验并批量删除施工人员信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        Long userId = LoginHelper.getUserId();
        List<BusConstructionUser> constructionUserList = this.listByIds(ids);
        if (isValid) {
            // 做一些业务上的校验,判断是否需要校验
            // 获取项目id列表
            List<Long> projectIdList = constructionUserList.stream().map(BusConstructionUser::getProjectId).toList();
            // 判断是否有权限操作对应项目下的内容
            projectService.validAuth(projectIdList, userId);
            // 判断待删除的人员是否存在于班组
            LambdaQueryWrapper<BusProjectTeamMember> projectTeamMemberLqw = Wrappers.lambdaQuery(BusProjectTeamMember.class)
                .in(BusProjectTeamMember::getMemberId, ids);
            if (projectTeamMemberService.count(projectTeamMemberLqw) > 0) {
                throw new ServiceException("删除施工人员信息失败，施工人员存在于班组中", HttpStatus.BAD_REQUEST);
            }
        }
        // 判断对应数据是否都存在
        if (constructionUserList.size() != ids.size()) {
            throw new ServiceException("删除施工人员信息失败，数据缺失", HttpStatus.BAD_REQUEST);
        }
        // 批量删除施工人员下的文件信息
        LambdaQueryWrapper<BusConstructionUserFile> constructionUserFileLqw = Wrappers.lambdaQuery(BusConstructionUserFile.class)
            .in(BusConstructionUserFile::getUserId, ids);
        boolean removeFile = constructionUserFileService.remove(constructionUserFileLqw);
        if (!removeFile) {
            throw new ServiceException("删除施工人员信息失败，施工人员文件信息删除失败", HttpStatus.ERROR);
        }
        // 批量删除施工人员信息
        return this.removeBatchByIds(ids);
    }

    /**
     * 获取施工人员视图对象
     *
     * @param constructionUser 施工人员对象
     * @return 施工人员视图对象
     */
    @Override
    public BusConstructionUserVo getVo(BusConstructionUser constructionUser) {
        // 对象转封装类
        BusConstructionUserVo constructionUserVo = new BusConstructionUserVo();
        if (constructionUser == null) {
            return constructionUserVo;
        }
        BeanUtils.copyProperties(constructionUser, constructionUserVo);
        // 关联查询分包公司信息信息
        Long contractorId = constructionUser.getContractorId();
        if (contractorId != null) {
            constructionUserVo.setContractorVo(contractorService.queryById(contractorId));
        }
        // 关联查询用户头像url
        String facePic = constructionUser.getFacePic();
        if (StringUtils.isNotBlank(facePic)) {
            constructionUserVo.setFacePicUrl(ossService.getById(Long.parseLong(facePic)).getUrl());
        }
        // 关联查询薪水标准
        Long projectId = constructionUser.getProjectId();
        String typeOfWork = constructionUser.getTypeOfWork();
        String wageMeasureUnit = constructionUser.getWageMeasureUnit();
        if (projectId != null && StringUtils.isNotEmpty(typeOfWork) && StringUtils.isNotEmpty(wageMeasureUnit)) {
            LambdaQueryWrapper<BusWorkWage> lqw = Wrappers.lambdaQuery(BusWorkWage.class)
                .eq(BusWorkWage::getProjectId, projectId)
                .eq(BusWorkWage::getWorkType, typeOfWork)
                .eq(BusWorkWage::getWageMeasureUnit, wageMeasureUnit);
            BusWorkWage workWage = workWageService.getOne(lqw);
            if (workWage != null) {
                constructionUserVo.setStandardSalary(workWage.getWage());
            }
        }
        return constructionUserVo;
    }

    /**
     * 获取施工人员查询条件封装
     *
     * @param req 查询条件
     * @return 查询条件封装
     */
    @Override
    public LambdaQueryWrapper<BusConstructionUser> buildQueryWrapper(BusConstructionUserQueryReq req) {
        LambdaQueryWrapper<BusConstructionUser> lqw = Wrappers.lambdaQuery();
        if (req == null) {
            return lqw;
        }
        // 从对象中取值
        Long id = req.getId();
        String userName = req.getUserName();
        Long projectId = req.getProjectId();
        Long contractorId = req.getContractorId();
        Long teamId = req.getTeamId();
        Long notTeamId = req.getNotTeamId();
        String teamName = req.getTeamName();
        String status = req.getStatus();
        String isPinch = req.getIsPinch();
        String phone = req.getPhone();
        String sex = req.getSex();
        String nation = req.getNation();
        String sfzNumber = req.getSfzNumber();
        String nativePlace = req.getNativePlace();
        String yhkNumber = req.getYhkNumber();
        String yhkOpeningBank = req.getYhkOpeningBank();
        String yhkCardholder = req.getYhkCardholder();
        String typeOfWork = req.getTypeOfWork();
        String wageMeasureUnit = req.getWageMeasureUnit();
        String clock = req.getClock();
        Long salary = req.getSalary();
        String remark = req.getRemark();
        // 模糊查询
        lqw.like(StringUtils.isNotBlank(userName), BusConstructionUser::getUserName, userName);
        lqw.like(StringUtils.isNotBlank(remark), BusConstructionUser::getRemark, remark);
        lqw.like(StringUtils.isNotBlank(phone), BusConstructionUser::getPhone, phone);
        lqw.like(StringUtils.isNotBlank(nation), BusConstructionUser::getNation, nation);
        lqw.like(StringUtils.isNotBlank(sfzNumber), BusConstructionUser::getSfzNumber, sfzNumber);
        lqw.like(StringUtils.isNotBlank(nativePlace), BusConstructionUser::getNativePlace, nativePlace);
        lqw.like(StringUtils.isNotBlank(yhkNumber), BusConstructionUser::getYhkNumber, yhkNumber);
        lqw.like(StringUtils.isNotBlank(yhkOpeningBank), BusConstructionUser::getYhkOpeningBank, yhkOpeningBank);
        lqw.like(StringUtils.isNotBlank(yhkCardholder), BusConstructionUser::getYhkCardholder, yhkCardholder);
        lqw.like(StringUtils.isNotBlank(teamName), BusConstructionUser::getTeamName, teamName);
        // 精确查询
        lqw.eq(StringUtils.isNotBlank(status), BusConstructionUser::getStatus, status);
        lqw.eq(ObjectUtils.isNotEmpty(id), BusConstructionUser::getId, id);
        lqw.eq(ObjectUtils.isNotEmpty(projectId), BusConstructionUser::getProjectId, projectId);
        lqw.eq(ObjectUtils.isNotEmpty(contractorId), BusConstructionUser::getContractorId, contractorId);
        lqw.eq(ObjectUtils.isNotEmpty(teamId), BusConstructionUser::getTeamId, teamId);
        lqw.eq(StringUtils.isNotBlank(isPinch), BusConstructionUser::getIsPinch, isPinch);
        lqw.eq(StringUtils.isNotBlank(sex), BusConstructionUser::getSex, sex);
        lqw.eq(ObjectUtils.isNotEmpty(typeOfWork), BusConstructionUser::getTypeOfWork, typeOfWork);
        lqw.eq(ObjectUtils.isNotEmpty(clock), BusConstructionUser::getClock, clock);
        lqw.eq(ObjectUtils.isNotEmpty(salary), BusConstructionUser::getSalary, salary);
        lqw.eq(StringUtils.isNotBlank(wageMeasureUnit), BusConstructionUser::getWageMeasureUnit, wageMeasureUnit);
        // 精准查询，不等于
        if (ObjectUtils.isNotEmpty(notTeamId)) {
            lqw.and(wrapper -> wrapper
                .ne(BusConstructionUser::getTeamId, notTeamId)
                .or().isNull(BusConstructionUser::getTeamId));
        }
        // 查询当前项目下的黑名单人员
        if (projectId != null) {
            List<Long> blackUserIdList = constructionBlacklistService.queryIdListByProjectId(projectId);
            // 查询结果移除黑名单人员
            if (CollUtil.isNotEmpty(blackUserIdList)) {
                lqw.notIn(BusConstructionUser::getId, blackUserIdList);
            }
        }
        return lqw;
    }

    /**
     * 获取施工人员分页对象视图
     *
     * @param constructionUserPage 施工人员分页对象
     * @return 施工人员分页对象视图
     */
    @Override
    public Page<BusConstructionUserVo> getVoPage(Page<BusConstructionUser> constructionUserPage) {
        List<BusConstructionUser> constructionUserList = constructionUserPage.getRecords();
        Page<BusConstructionUserVo> constructionUserVoPage = new Page<>(
            constructionUserPage.getCurrent(),
            constructionUserPage.getSize(),
            constructionUserPage.getTotal());
        if (CollUtil.isEmpty(constructionUserList)) {
            return constructionUserVoPage;
        }
        // 关联查询分包公司信息
        Set<Long> contractorIdSet = constructionUserList.stream().map(BusConstructionUser::getContractorId)
            .collect(Collectors.toSet());
        Map<Long, List<BusContractor>> contractorIdContractorMap = contractorService.listByIds(contractorIdSet).stream()
            .collect(Collectors.groupingBy(BusContractor::getId));
        // 关联查询工资标准
        LambdaQueryWrapper<BusWorkWage> workWageLqw = Wrappers.lambdaQuery(BusWorkWage.class)
            .in(BusWorkWage::getProjectId, constructionUserList.stream().map(BusConstructionUser::getProjectId).toList())
            .in(BusWorkWage::getWorkType, constructionUserList.stream().map(BusConstructionUser::getTypeOfWork).toList())
            .in(BusWorkWage::getWageMeasureUnit, constructionUserList.stream().map(BusConstructionUser::getWageMeasureUnit).toList());
        Map<String, Long> workWageMap = workWageService.list(workWageLqw).stream().collect(Collectors.toMap(
            workWage -> workWage.getWorkType() + "_" + workWage.getWageMeasureUnit(),
            BusWorkWage::getWage,
            (wage1, wage2) -> wage1
        ));
        int fileTypeSize = dictTypeService.selectDictDataByType(BusConstructionUserConstant.USER_FILE_TYPE).size();
        // 填充信息
        List<BusConstructionUserVo> constructionUserVoList = constructionUserList.stream().map(constructionUser -> {
            BusConstructionUserVo constructionUserVo = new BusConstructionUserVo();
            BeanUtils.copyProperties(constructionUser, constructionUserVo);
            // 关联分包公司信息
            Long contractorId = constructionUser.getContractorId();
            BusContractorVo contractor = null;
            if (contractorIdContractorMap.containsKey(contractorId)) {
                contractor = contractorService.getVo(contractorIdContractorMap.get(contractorId).get(0));
            }
            constructionUserVo.setContractorVo(contractor);
            // 关联工资标准
            // 构造相应的 key
            String key = constructionUser.getTypeOfWork() + "_" + constructionUser.getWageMeasureUnit();
            // 如果映射中存在对应的数据，则设置工资
            if (workWageMap.containsKey(key)) {
                constructionUserVo.setStandardSalary(workWageMap.get(key));
            }
            // 关联施工人员文件上传状态
            LambdaQueryWrapper<BusConstructionUserFile> constructionUserFileLqw = Wrappers.lambdaQuery(BusConstructionUserFile.class)
                .eq(BusConstructionUserFile::getUserId, constructionUser.getId());
            long count = constructionUserFileService.count(constructionUserFileLqw);
            if (count <= 0) {
                constructionUserVo.setFileUploadStatus(BusConstructionUserFileStatusEnum.NOUPLOAD.getValue());
            } else if (count < fileTypeSize) {
                constructionUserVo.setFileUploadStatus(BusConstructionUserFileStatusEnum.PARTUPLOAD.getValue());
            } else if (count == fileTypeSize) {
                constructionUserVo.setFileUploadStatus(BusConstructionUserFileStatusEnum.UPLOAD.getValue());
            }
            return constructionUserVo;
        }).toList();
        constructionUserVoPage.setRecords(constructionUserVoList);
        return constructionUserVoPage;
    }

    /**
     * 获取施工人员考勤分页对象视图
     *
     * @param req 施工人员考勤分页查询条件
     * @return 施工人员考勤分页对象视图
     */
    @Override
    public Page<BusConstructionUserAttendanceTotalVo> getAttendanceTotalVoPage(BusConstructionUserAttendanceQueryReq req,
                                                                               PageQuery pageQuery) {
        LambdaQueryWrapper<BusConstructionUser> lqw = Wrappers.lambdaQuery();
        // 从对象中取值
        String userName = req.getUserName();
        Long projectId = req.getProjectId();
        Long teamId = req.getTeamId();
        String typeOfWork = req.getTypeOfWork();
        String clockDate = req.getClockDate();
        // 联表查询
        LambdaQueryWrapper<BusAttendance> attendanceLqw = Wrappers.lambdaQuery(BusAttendance.class)
            .eq(BusAttendance::getProjectId, projectId);
        if (ObjectUtils.isNotEmpty(clockDate)) {
            String clockMonth = clockDate.substring(0, 7);
            // 校验月份格式
            if (!DateConstant.YEAR_MONTH_PATTERN.matcher(clockMonth).matches()) {
                throw new ServiceException("月份格式不正确", HttpStatus.BAD_REQUEST);
            }
            // 解析月份
            YearMonth yearMonth = YearMonth.parse(clockMonth);
            // 判断是否大于当前月份
            if (yearMonth.isAfter(YearMonth.now())) {
                throw new ServiceException("不能查看大于当前月份的记录", HttpStatus.BAD_REQUEST);
            }
            // 计算当月第一天 / 最后一天
            Date start = DateUtils.toDate(yearMonth.atDay(1));
            Date end = DateUtils.toDate(yearMonth.atEndOfMonth());
            attendanceLqw.between(BusAttendance::getClockDate, start, end);
            List<Long> userIdList = attendanceService.list(attendanceLqw)
                .stream().map(BusAttendance::getUserId).toList();
            if (CollUtil.isNotEmpty(userIdList)) {
                lqw.in(BusConstructionUser::getId, userIdList);
            }
        }
        // 模糊查询
        lqw.like(StringUtils.isNotBlank(userName), BusConstructionUser::getUserName, userName);
        // 精确查询
        lqw.eq(ObjectUtils.isNotEmpty(projectId), BusConstructionUser::getProjectId, projectId);
        lqw.eq(ObjectUtils.isNotEmpty(teamId), BusConstructionUser::getTeamId, teamId);
        lqw.eq(StringUtils.isNotBlank(typeOfWork), BusConstructionUser::getTypeOfWork, typeOfWork);
        // 查询当前项目下的黑名单人员
        List<Long> blackUserIdList = constructionBlacklistService.queryIdListByProjectId(projectId);
        // 查询结果移除黑名单人员
        if (CollUtil.isNotEmpty(blackUserIdList)) {
            lqw.notIn(BusConstructionUser::getId, blackUserIdList);
        }
        // 分页查询获取数据
        Page<BusConstructionUser> constructionUserPage = this.page(pageQuery.build(), lqw);
        List<BusConstructionUser> constructionUserList = constructionUserPage.getRecords();
        Page<BusConstructionUserAttendanceTotalVo> constructionUserAttendanceTotalPage = new Page<>(
            constructionUserPage.getCurrent(),
            constructionUserPage.getSize(),
            constructionUserPage.getTotal());
        if (CollUtil.isEmpty(constructionUserList)) {
            return constructionUserAttendanceTotalPage;
        }
        // 获取施工人员id列表
        List<Long> userIdList = constructionUserList.stream().map(BusConstructionUser::getId).toList();
        // 关联查询施工人员考勤列表
        attendanceLqw.in(BusAttendance::getUserId, userIdList);
        Map<Long, List<BusAttendance>> userIdBusAttendanceListMap = attendanceService.list(attendanceLqw)
            .stream().collect(Collectors.groupingBy(BusAttendance::getUserId));
        // 填充信息
        List<BusConstructionUserAttendanceTotalVo> userAttendanceTotalList = constructionUserList.stream().map(constructionUser -> {
            BusConstructionUserAttendanceTotalVo constructionUserAttendanceTotalResp = new BusConstructionUserAttendanceTotalVo();
            Long id = constructionUser.getId();
            BeanUtils.copyProperties(constructionUser, constructionUserAttendanceTotalResp);
            // 关联施工人员考勤信息
            int attendanceDays = 0;
            int lateDays = 0;
            int leaveEarlyDays = 0;
            int unClockDays = 0;
            int leaveDays = 0;
            if (userIdBusAttendanceListMap.containsKey(id)) {
                List<BusAttendance> attendanceList = userIdBusAttendanceListMap.get(id);
                if (CollUtil.isNotEmpty(attendanceList)) {
                    // 1. 按打卡日期分组
                    Map<Date, List<BusAttendance>> dailyMap = attendanceList.stream()
                        .collect(Collectors.groupingBy(BusAttendance::getClockDate));
                    // 2. 对每一天的记录计算状态
                    for (List<BusAttendance> dailyList : dailyMap.values()) {
                        String clockInStatus = null;
                        String clockOutStatus = null;
                        String clockAllDayStatus = null;
                        // 获取上下班状态
                        for (BusAttendance attendance : dailyList) {
                            if (BusAttendanceCommuterEnum.CLOCKIN.getValue().equals(attendance.getCommuter())) {
                                clockInStatus = attendance.getClockStatus();
                            } else if (BusAttendanceCommuterEnum.CLOCKOUT.getValue().equals(attendance.getCommuter())) {
                                clockOutStatus = attendance.getClockStatus();
                            } else {
                                clockAllDayStatus = attendance.getClockStatus();
                            }
                        }
                        // 统计考勤状态
                        if (BusAttendanceClockStatusEnum.LATE.getValue().equals(clockInStatus)) {
                            lateDays++;
                        }
                        if (BusAttendanceClockStatusEnum.LEAVEEARLY.getValue().equals(clockOutStatus)) {
                            leaveEarlyDays++;
                        }
                        if (BusAttendanceClockStatusEnum.LEAVE.getValue().equals(clockAllDayStatus)) {
                            leaveDays++;
                        } else {
                            if (clockInStatus == null || BusAttendanceClockStatusEnum.UNCLOCK.getValue().equals(clockInStatus)) {
                                unClockDays++;
                            }
                            if (clockOutStatus == null || BusAttendanceClockStatusEnum.UNCLOCK.getValue().equals(clockOutStatus)) {
                                unClockDays++;
                            }
                        }
                    }
                    attendanceDays = dailyMap.size() - leaveDays;
                }
            }
            constructionUserAttendanceTotalResp.setAttendanceDays(attendanceDays);
            constructionUserAttendanceTotalResp.setLateDays(lateDays);
            constructionUserAttendanceTotalResp.setLeaveEarlyDays(leaveEarlyDays);
            constructionUserAttendanceTotalResp.setUnClockDays(unClockDays);
            constructionUserAttendanceTotalResp.setLeaveDays(leaveDays);
            return constructionUserAttendanceTotalResp;
        }).toList();
        constructionUserAttendanceTotalPage.setRecords(userAttendanceTotalList);
        return constructionUserAttendanceTotalPage;
    }

}
