package cn.iocoder.yudao.module.tickets.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.common.*;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.base.entity.RepairRecords;
import cn.iocoder.yudao.module.base.entity.RepairWorker;
import cn.iocoder.yudao.module.base.service.RepairRecordsService;
import cn.iocoder.yudao.module.base.service.RepairWorkerService;
import cn.iocoder.yudao.module.base.vo.RepairRecordsPageReqVO;
import cn.iocoder.yudao.module.base.vo.RepairRecordsRespVO;
import cn.iocoder.yudao.module.booking.entity.DlRepairBooking;
import cn.iocoder.yudao.module.booking.service.DlRepairBookingService;
import cn.iocoder.yudao.module.company.entity.Company;
import cn.iocoder.yudao.module.company.service.CompanyService;
import cn.iocoder.yudao.module.conf.entity.BaseType;
import cn.iocoder.yudao.module.conf.service.BaseTypeService;
import cn.iocoder.yudao.module.custom.entity.*;
import cn.iocoder.yudao.module.custom.service.*;
import cn.iocoder.yudao.module.custom.vo.CarMainReqVO;
import cn.iocoder.yudao.module.custom.vo.CarMainRespVO;
import cn.iocoder.yudao.module.custom.vo.CustomerMainRespVO;
import cn.iocoder.yudao.module.order.entity.RepairOrderInfo;
import cn.iocoder.yudao.module.order.service.RepairOrderInfoService;
import cn.iocoder.yudao.module.order.vo.RepairOrderInfoSaveReqVO;
import cn.iocoder.yudao.module.other.entity.DlRepairOther;
import cn.iocoder.yudao.module.other.service.DlRepairOtherService;
import cn.iocoder.yudao.module.project.entity.RepairProject;
import cn.iocoder.yudao.module.project.entity.RepairWares;
import cn.iocoder.yudao.module.project.service.RepairProjectService;
import cn.iocoder.yudao.module.project.service.RepairWaresService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO;
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
import cn.iocoder.yudao.module.system.api.permission.RoleApi;
import cn.iocoder.yudao.module.system.api.permission.dto.RoleReqDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import cn.iocoder.yudao.module.system.api.user.dto.UserDTO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import cn.iocoder.yudao.module.tickets.dto.JobTypeProfitDTO;
import cn.iocoder.yudao.module.tickets.entity.DlRepairTickets;
import cn.iocoder.yudao.module.tickets.entity.DlRepairTitem;
import cn.iocoder.yudao.module.tickets.entity.DlTicketWares;
import cn.iocoder.yudao.module.tickets.entity.DlTwItem;
import cn.iocoder.yudao.module.tickets.mapper.DlRepairTicketsMapper;
import cn.iocoder.yudao.module.tickets.mapper.DlRepairTitemMapper;
import cn.iocoder.yudao.module.tickets.service.DlRepairTicketsService;
import cn.iocoder.yudao.module.tickets.service.DlRepairTitemService;
import cn.iocoder.yudao.module.tickets.service.DlTicketWaresService;
import cn.iocoder.yudao.module.tickets.service.DlTwItemService;
import cn.iocoder.yudao.module.tickets.tools.WordUtil;
import cn.iocoder.yudao.module.tickets.utils.TicketsOperateUtil;
import cn.iocoder.yudao.module.tickets.vo.*;
import cn.iocoder.yudao.util.CreateQRCodeUtil;
import cn.iocoder.yudao.util.SendSmsUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.config.ConfigureBuilder;
import com.deepoove.poi.data.PictureRenderData;
import com.deepoove.poi.data.PictureType;
import com.deepoove.poi.plugin.table.HackLoopTableRenderPolicy;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.common.BaseConstants.ORDER_TENANT_NAME;
import static cn.iocoder.yudao.common.RepairCons.*;
import static cn.iocoder.yudao.common.RepairDictConstants.BASE_BUSINESS_CONFIG;
import static cn.iocoder.yudao.common.RepairDictConstants.REPAIR_WORK_TYPE;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;

/**
 * 针对表【dl_repair_tickets(维修工单表)】的数据库操作Service实现
 * @author 小李
 * @date 18:19 2024/9/13
 **/
@Service
public class DlRepairTicketsServiceImpl extends ServiceImpl<DlRepairTicketsMapper, DlRepairTickets>
        implements DlRepairTicketsService {

    @Resource
    private DlRepairTitemService titemService;
    @Resource
    private DlRepairTitemMapper repairTitemMapper;
    @Resource
    private DlRepairTicketsMapper repairTicketsMapper;

    @Resource
    private DeptApi deptApi;

    @Resource
    private RepairProjectService projectService;

    @Resource
    private RepairWaresService waresService;

    @Resource
    private DlRepairOtherService otherService;

    @Resource
    private DictDataApi dictDataApi;

    @Resource
    private CustomerMainService customerMainService;

    @Resource
    @Lazy
    private RepairOrderInfoService repairOrderInfoService;

    @Resource
    @Lazy
    private DlRepairBookingService bookingService;

    @Resource
    @Lazy
    private CustomerMainService customerService;

    @Resource
    @Lazy
    private CarMainService carMainService;

    @Resource
    @Lazy
    private CustomerCarService customerCarService;

    @Resource
    @Lazy
    private AdminUserApi adminUserApi;

    @Resource
    @Lazy
    private PermissionApi permissionApi;

    @Resource
    @Lazy
    private RoleApi roleApi;

    @Resource
    @Lazy
    private RepairWorkerService repairWorkerService;

    @Resource
    private RepairRecordsService repairRecordsService;

    @Resource
    @Lazy
    private DlTicketWaresService ticketWaresService;

    @Resource
    @Lazy
    private DlTwItemService twItemService;

    @Resource
    @Lazy
    private CarBrandService carBrandService;

    @Resource
    @Lazy
    private CarModelService carModelService;

    @Resource
    @Lazy
    private BaseTypeService baseTypeService;

    @Resource
    @Lazy
    private CompanyService companyService;
    @Resource
    private AdminUserService userService;
    @Resource
    private TicketsOperateUtil operateUtil;

    /**
     * 维修工单表 新增
     * @param ticketsRespVO 新增对象
     * @author 小李
     * @date 11:33 2024/9/20
     **/
    @Override
    @DSTransactional
    public DlRepairTicketsRespVO createTickets(DlRepairTicketsRespVO ticketsRespVO) {
        // 验证
        if (ObjectUtil.isEmpty(ticketsRespVO.getCarId())) {
            throw exception0(500, "车辆信息为空");
        }
        if (ObjectUtil.isEmpty(ticketsRespVO.getAdviserId())) {
            throw exception0(500, "服务顾问为空");
        }
//        ticketsRespVO.getItemList().forEach(item -> {
//            if (ObjectUtil.isEmpty(item.getRepairIds()) || ObjectUtil.isEmpty(item.getSaleId())) {
//                String message = "";
//                switch (item.getItemType()) {
//                    case "01":
//                        message += "项目：";
//                        break;
//                    case "02":
//                        message += "配件：";
//                        break;
//                    case "03":
//                        message += "其他：";
//                }
//                message += (item.getItemName() + (ObjectUtil.isEmpty(item.getRepairIds()) ? "施工人员" : "销售人员") + "为空");
//                throw exception0(500, message);
//            }
//        });


        // 门店信息
        Long deptId = SecurityFrameworkUtils.getLoginUserDeptId();
        DeptRespDTO dept = deptApi.getDept(deptId);
        if (ObjectUtil.isNotEmpty(dept)) {
            ticketsRespVO.setCorpId(dept.getCorpId());
        }

        /* 更新车辆的字段最近保养日期、最近保养公里数等等 start */
        if (null != ticketsRespVO.getMaintenanceDate() || null != ticketsRespVO.getMaintenanceMileage()) {
            CarMain carMain = carMainService.getById(ticketsRespVO.getCarId());
            if (null != carMain.getCarRegisterDate()) {
                carMain.setCarYear(operateUtil.computeCarYear(Date.from(carMain.getCarRegisterDate().atZone(ZoneId.systemDefault()).toInstant())));
            }
            //保养日期
            if (null != ticketsRespVO.getMaintenanceDate()) {
                carMain.setMaintenanceDate(ticketsRespVO.getMaintenanceDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
            }
            //保养里程
            if (null != ticketsRespVO.getMaintenanceMileage()) {
                carMain.setMaintenanceMileage(ticketsRespVO.getMaintenanceMileage());
            }
            //下次年检日期
            if (null != ticketsRespVO.getNextInspectionDate()) {
                carMain.setNextInspectionDate(ticketsRespVO.getNextInspectionDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
            }
            //保险到期日期
            if (null != ticketsRespVO.getInsuranceExpiryDate()) {
                carMain.setInsuranceExpiryDate(ticketsRespVO.getInsuranceExpiryDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
            }
            //保险公司名称
            if (null != ticketsRespVO.getInsuranceName()) {
                carMain.setInsuranceName(ticketsRespVO.getInsuranceName());
            }
            //承保险种,多个英文逗号隔开
            if (null != ticketsRespVO.getInsuranceType()) {
                carMain.setInsuranceType(ticketsRespVO.getInsuranceType());
            }
            if (null != ticketsRespVO.getJiaoqiang()) {
                carMain.setJiaoqiang(ticketsRespVO.getJiaoqiang());
            }
            if (null != ticketsRespVO.getShangye()) {
                carMain.setShangye(ticketsRespVO.getShangye());
            }
            carMainService.updateById(carMain);
        }
        /* 更新车辆的最近保养日期、最近保养公里数 end */

        /* 更新客户来源 */
        if (StringUtils.isNotEmpty(ticketsRespVO.getCusFrom())) {
            CustomerMain customerMain = customerMainService.getById(ticketsRespVO.getUserId());
            customerMain.setDataFrom(ticketsRespVO.getCusFrom());
            customerMainService.updateById(customerMain);
        }

        // 计算参考成本、参考毛利、领料状态、工单进行状态
        // TODO 参考成本 暂时为0
        ticketsRespVO.setCost(new BigDecimal("0"));
        // TODO 参考毛利 暂时为总金额
        ticketsRespVO.setProfit(ticketsRespVO.getTotalPrice());
        // 领料状态
        // 如果有配件就是未领料 没有配件就是无配件
        if (CollectionUtil.isEmpty(ticketsRespVO.getItemList())) {
            throw exception(RepairErrorCodeConstants.ITEM_IS_EMPTY);
        }
        List<DlRepairTitem> itemList = ticketsRespVO.getItemList();
        List<DlRepairTitem> collect = itemList.stream().filter(item -> item.getItemType().equals("02")).collect(Collectors.toList());
        ticketsRespVO.setPartStatus(CollectionUtil.isEmpty(collect) ? "01" : "02");
        // 工单进行状态 默认是等待接单
        ticketsRespVO.setTicketsWorkStatus(TicketsWorkStatusEnum.WAITING_RECEIVING.getCode());
        // 工单状态 默认是待派工
        ticketsRespVO.setTicketsStatus(TicketsStatusEnum.NO_WORK.getCode());

        // 新增主表
        baseMapper.insert(ticketsRespVO);

        //获取当前登录用户
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        // 新增子表
        itemList.forEach(item -> {
            item.setTicketId(ticketsRespVO.getId());
            if (null == item.getSaleId()) {
                //默认销售人员是当前用户
                item.setSaleId(loginUser.getId());
                item.setSaleName(loginUser.getInfo().get("nickname"));
            }
            if ("01".equals(item.getItemType())) {
                //维修项目,初始状态，待派工
                item.setItemStatus(TicketsItemStatusEnum.WAITING_WORK.getCode());
            } else if ("02".equals(item.getItemType())) {
                //维修配件，初始状态，待领料
                item.setItemStatus(TicketsItemStatusEnum.WAITING_RECEIVE.getCode());
            }
        });
        titemService.saveBatch(itemList);

        // 创建订单
        RepairOrderInfoSaveReqVO repairOrderInfo = new RepairOrderInfoSaveReqVO();
        // 订单号采用时间戳+工单ID和hash值取余后取3位
        repairOrderInfo.setOrderNo(System.currentTimeMillis() + Math.abs(ticketsRespVO.getId().hashCode() % 1000) + "");
        repairOrderInfo.setGoodsId(ticketsRespVO.getId());
        repairOrderInfo.setTenantName(ORDER_TENANT_NAME);
        DictDataRespDTO repairType = dictDataApi.getDictData("repair_type", ticketsRespVO.getRepairType());
        if (ObjectUtil.isNotEmpty(repairType)) {
            repairOrderInfo.setGoodsTitle(repairType.getLabel());
        } else {
            repairOrderInfo.setGoodsTitle("维修");
        }
        repairOrderInfo.setGoodsType("2");
        CustomerMainRespVO customerById = customerMainService.getCustomerById(ticketsRespVO.getUserId());
        if (ObjectUtil.isNotEmpty(customerById)) {
            repairOrderInfo.setUserId(customerById.getUserId());
        } else {
            repairOrderInfo.setUserId(0L);
        }
        repairOrderInfo.setCusId(customerById.getId());
        repairOrderInfo.setCusName(customerById.getCusName());
        repairOrderInfo.setCusPhone(customerById.getPhoneNumber());
        repairOrderInfo.setGoodsPrice(ticketsRespVO.getTotalPrice());
        repairOrderInfo.setOrderTime(ticketsRespVO.getCreateTime());
        repairOrderInfo.setOrderStatus("0");
        repairOrderInfo.setPayMoney(ticketsRespVO.getTotalPrice());
        if (ObjectUtil.isNotEmpty(deptId)) {
            repairOrderInfo.setDeptId(Integer.parseInt(deptId + ""));
        }

        // 如果选择了预约还要更新预约记录
        if (ObjectUtil.isNotEmpty(ticketsRespVO.getBookingId())) {
            DlRepairBooking dlRepairBooking = new DlRepairBooking();
            dlRepairBooking.setId(ticketsRespVO.getBookingId());
            dlRepairBooking.setTicketsId(ticketsRespVO.getId());
            dlRepairBooking.setBookingStatus("02");
            bookingService.updateById(dlRepairBooking);
        }
        repairOrderInfoService.saveOrderInfo(repairOrderInfo);
        //最后记录操作日志--创建工单
        repairRecordsService.saveRepairRecord(ticketsRespVO.getId(), null, RecordTypeEnum.CJGD.getCode(), "创建工单", ticketsRespVO.getImage());

        // 通知客户
        // 构建消息
        String message = String.format("您的爱车%s已开始%s", ticketsRespVO.getCarNo(), repairOrderInfo.getGoodsTitle());
        repairWorkerService.sentMessageToCus(SystemEnum.REPAIR.getCode(), ticketsRespVO.getId(), message, true);
        return ticketsRespVO;
    }

    /**
     * 维修工单表 分页
     * @param repairTicketsReqVO 查询对象
     * @author 小李
     * @date 20:51 2024/9/20
     **/
    @Override
    public IPage<DlRepairTickets> getTicketsPage(DlRepairTicketsReqVO repairTicketsReqVO, Page<DlRepairTickets> page) {
        IPage<DlRepairTickets> ticketsPage = baseMapper.getTicketsPage(repairTicketsReqVO, page);
        ticketsPage.getRecords().forEach(item -> {
            if (ObjectUtil.isNotEmpty(item.getSettlementStr())) {
                // 将json字符串转换成对象
                item.setSettlement(JSON.parseObject(item.getSettlementStr(), TicketsSettlementVO.class));
            }
        });
        return ticketsPage;
    }

    /**
     * 获得一个工单的详细信息
     * @param id    工单ID
     * @param ifApp 是否是APP发起的查询
     * @author 小李
     * @date 16:05 2024/9/21
     **/
    @Override
    public DlRepairTicketsRespVO getTicketsById(String id, Boolean ifApp) {
        // 查工单主表
        DlRepairTickets dlRepairTickets = baseMapper.selectById(id);
        //翻译维修类型
        List<DictDataRespDTO> repairTypeList = dictDataApi.getDictDataList(DICT_REPAIR_TYPE);
        Map<String, String> repairTypeMap = repairTypeList.stream().collect(Collectors.toMap(DictDataRespDTO::getValue, DictDataRespDTO::getLabel));
        DlRepairTicketsRespVO result = BeanUtil.toBean(dlRepairTickets, DlRepairTicketsRespVO.class);
        result.setRepairTypeText(repairTypeMap.getOrDefault(dlRepairTickets.getRepairType(), ""));
        //查是否存在总检完成的记录，取出他的时间
        LambdaQueryWrapper<RepairRecords> queryWrapper = new LambdaQueryWrapper<RepairRecords>()
                .eq(RepairRecords::getTicketId, id)
                .eq(RepairRecords::getType, RecordTypeEnum.ZJ.getCode())
                .orderByDesc(BaseDO::getCreateTime);
        List<RepairRecords> repairRecords = repairRecordsService.list(queryWrapper);
        if (!repairRecords.isEmpty()) {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
            String formattedString = repairRecords.get(0).getCreateTime().format(formatter);
            result.setRealOverDate(formattedString);
        }

        AdminUserDO user = userService.getUser(Long.valueOf(dlRepairTickets.getAdviserId()));
        result.setLinkPhone(user.getMobile());
        //查车辆
        if (ObjectUtil.isNotEmpty(dlRepairTickets.getCarId())) {
            CarMainRespVO carInfo = carMainService.queryCarById(dlRepairTickets.getCarId());
            //计算车龄
            if (null != carInfo.getCarRegisterDate()) {
                Double carYear = operateUtil.computeCarYear(Date.from(carInfo.getCarRegisterDate().atZone(ZoneId.systemDefault()).toInstant()));
                //更新回去
                CarMain carMain = new CarMain();
                carMain.setId(dlRepairTickets.getCarId());
                carMain.setCarYear(carYear);
                if (null == carInfo.getCarYear() || !carInfo.getCarYear().equals(carYear)) {
                    carMainService.updateById(carMain);
                }
                carInfo.setCarYear(carYear);
            }
            result.setCarInfo(carInfo);
        }

        //查用户信息
        CustomerMain customerInfo = customerService.getById(dlRepairTickets.getUserId());
        if (StringUtils.isNotEmpty(customerInfo.getDataFrom())) {
            //翻译客户来源
            List<DictDataRespDTO> dataFromList = dictDataApi.getDictDataList(DICT_CUS_DATA_FROM);
            Map<String, String> dataFromMap = dataFromList.stream().collect(Collectors.toMap(DictDataRespDTO::getValue, DictDataRespDTO::getLabel));
            customerInfo.setDataFromText(dataFromMap.get(customerInfo.getDataFrom()));
        }
        result.setCustomerInfo(customerInfo);
        // 查工单子表
        List<DlRepairTitem> itemList = titemService.list(new LambdaQueryWrapper<DlRepairTitem>().eq(DlRepairTitem::getTicketId, id));
        List<DlRepairTitemReqVO> items = itemList.stream().map(item -> BeanUtil.toBean(item, DlRepairTitemReqVO.class)).collect(Collectors.toList());


        //处理项目 取项目
        List<DlRepairTitemReqVO> projects = items.stream().filter(item -> item.getItemType().equals("01")).collect(Collectors.toList());
        //pc原逻辑
        if (CollectionUtil.isNotEmpty(projects)) {
            Set<String> ids = projects.stream().map(DlRepairTitemReqVO::getProjectId).collect(Collectors.toSet());
            List<RepairProject> repairProjects = projectService.listByIds(ids);
            items.forEach(item -> repairProjects.stream().filter(i -> i.getId().equals(item.getProjectId())).findFirst().ifPresent(item::setProject));
        }
        result.setProjects(projects);
        if (ifApp) {
            //app新增逻辑
            if (CollectionUtil.isNotEmpty(projects)) {
                List<AppWaresGroupVO> projectList = new ArrayList<>();
                //查所有服务分类（项目分类）
                List<BaseType> baseTypeList = baseTypeService.list();
                Map<String, String> baseTypeMap = baseTypeList.stream().collect(Collectors.toMap(BaseType::getId, BaseType::getName));
                //先过滤出itenTypeId为null或空为单独一个组
                List<DlRepairTitemReqVO> nullList = projects.stream().filter(item -> StringUtils.isEmpty(item.getItemTypeId())).collect(Collectors.toList());
                if (CollectionUtil.isNotEmpty(nullList)) {
                    AppWaresGroupVO waresGroupVO = new AppWaresGroupVO();
                    waresGroupVO.setWares(nullList);
                    waresGroupVO.setGroupName("未知分组");
                    waresGroupVO.setNums(nullList.size());
                    waresGroupVO.setShowAll(false);
                    waresGroupVO.setServicer(nullList.get(0).getSaleName());
                    waresGroupVO.setWorker(nullList.get(0).getRepairNames());
                    projectList.add(waresGroupVO);
                }
                //过滤出有分类的进行分组
                Map<String, List<DlRepairTitemReqVO>> groupListMap = projects.stream().filter(item -> StringUtils.isNotEmpty(item.getItemTypeId())).collect(Collectors.groupingBy(DlRepairTitem::getItemTypeId));
                for (String key : groupListMap.keySet()) {
                    AppWaresGroupVO waresGroupVO = new AppWaresGroupVO();
                    waresGroupVO.setWares(groupListMap.get(key));
                    waresGroupVO.setGroupName(baseTypeMap.get(key));
                    waresGroupVO.setGroupId(key);
                    waresGroupVO.setNums(groupListMap.get(key).size());
                    waresGroupVO.setShowAll(false);
                    waresGroupVO.setServicer(groupListMap.get(key).get(0).getSaleName());
                    waresGroupVO.setWorker(groupListMap.get(key).get(0).getRepairNames());
                    projectList.add(waresGroupVO);
                }
                result.setProjectList(projectList);
            }
        }

        //处理配件
        if (ifApp) {
            //app的逻辑
            List<AppWaresGroupVO> waresGroupList = new ArrayList<>();
            //查这个工单的配件
            List<DlRepairTitemReqVO> waresList = repairTitemMapper.selectByTicketId(id);
            if (CollectionUtil.isNotEmpty(waresList)) {
                //先过滤出itenTypeId为null或空，或者itemTypeName为null或空的，为单独一个组
                List<DlRepairTitemReqVO> nullList = waresList.stream().filter(item -> StringUtils.isEmpty(item.getItemTypeId()) || StringUtils.isEmpty(item.getItemTypeName())).collect(Collectors.toList());
                if (CollectionUtil.isNotEmpty(nullList)) {
                    AppWaresGroupVO waresGroupVO = new AppWaresGroupVO();
                    waresGroupVO.setWares(nullList);
                    waresGroupVO.setGroupName("未知分组");
                    waresGroupVO.setNums(nullList.size());
                    waresGroupVO.setTotalAmount(nullList.stream().peek(item -> {
                        if (null == item.getItemMoney()) {
                            item.setItemMoney(new BigDecimal(0));
                        }
                    }).map(DlRepairTitem::getItemMoney).reduce(BigDecimal.ZERO, BigDecimal::add));
                    waresGroupList.add(waresGroupVO);
                }
                //过滤出有分类的进行分组
                Map<String, List<DlRepairTitemReqVO>> groupListMap = waresList.stream().filter(item -> StringUtils.isNotEmpty(item.getItemTypeId()) && StringUtils.isNotEmpty(item.getItemTypeName())).collect(Collectors.groupingBy(DlRepairTitem::getItemTypeId));
                for (String key : groupListMap.keySet()) {
                    AppWaresGroupVO waresGroupVO = new AppWaresGroupVO();
                    waresGroupVO.setWares(groupListMap.get(key));
                    waresGroupVO.setGroupName(groupListMap.get(key).get(0).getItemTypeName());
                    waresGroupVO.setGroupId(key);
                    waresGroupVO.setNums(groupListMap.get(key).size());
                    waresGroupVO.setTotalAmount(groupListMap.get(key).stream().peek(item -> {
                        if (null == item.getItemMoney()) {
                            item.setItemMoney(new BigDecimal(0));
                        }
                    }).map(DlRepairTitem::getItemMoney).reduce(BigDecimal.ZERO, BigDecimal::add));
                    waresGroupList.add(waresGroupVO);
                }
            }
            result.setWaresGroupList(waresGroupList);
        } else {
            // 取配件----原有逻辑-电脑端
            List<DlRepairTitemReqVO> wares = items.stream().filter(item -> item.getItemType().equals("02")).collect(Collectors.toList());
            if (CollectionUtil.isNotEmpty(wares)) {
                // 单位字典
                List<DictDataRespDTO> recordTypeList = dictDataApi.getDictDataList(DICT_REPAIR_UNIT);
                Map<String, String> unitMap = recordTypeList.stream().collect(Collectors.toMap(DictDataRespDTO::getValue, DictDataRespDTO::getLabel));
                Set<String> ids = wares.stream().map(DlRepairTitemReqVO::getPartId).collect(Collectors.toSet());
                List<RepairWares> repairWares = waresService.listByIds(ids);
                items.forEach(item -> {
                    repairWares.stream().filter(i -> i.getId().equals(item.getPartId())).findFirst().ifPresent(item::setWare);
                    item.setUnitText(unitMap.get(item.getItemUnit()));
                });
            }
            result.setWares(wares);
        }
        // 取附加
        List<DlRepairTitemReqVO> others = items.stream().filter(item -> item.getItemType().equals("03")).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(others)) {
            Set<String> ids = others.stream().map(DlRepairTitemReqVO::getOtherId).collect(Collectors.toSet());
            List<DlRepairOther> dlRepairOthers = otherService.listByIds(ids);
            items.forEach(item -> dlRepairOthers.stream().filter(i -> i.getId().equals(item.getOtherId())).findFirst().ifPresent(item::setOther));
        }
        result.setItems(items);
        // 查询工单操作记录
        RepairRecordsPageReqVO entity = new RepairRecordsPageReqVO();
        entity.setTicketId(id);
        List<RepairRecordsRespVO> records = repairRecordsService.queryList(entity);
        // 操作记录类型
        List<DictDataRespDTO> recordTypeList = dictDataApi.getDictDataList(DICT_REPAIR_RECORDS_TYPE);
        Map<String, String> typeMap = recordTypeList.stream().collect(Collectors.toMap(DictDataRespDTO::getValue, DictDataRespDTO::getLabel));
        result.setRecords(records.stream().peek(item -> item.setType(typeMap.get(item.getType()))).collect(Collectors.toList()));


        if (!ifApp) {
            //查询维修工人表
            List<RepairWorker> workers = repairWorkerService.listByTicketId(id);
            //转成map
            Map<Long, RepairWorker> workerMap = workers.stream().collect(Collectors.toMap(RepairWorker::getUserId, item -> item));


            //配件总利润
            BigDecimal waresProfit = BigDecimal.ZERO;
            List<JobTypeProfitDTO> resultList = new ArrayList<>();

            if (CollectionUtil.isNotEmpty(items)) {
                for (DlRepairTitemReqVO item : items) {
                    BigDecimal itemProfit = item.getItemProfit() != null ? item.getItemProfit() : BigDecimal.ZERO;
                    waresProfit = waresProfit.add(itemProfit);
                    BigDecimal itemMoney = item.getItemMoney() != null ? item.getItemMoney() : BigDecimal.ZERO;
                    // 成本 = 售价 - 利润（也可以直接从 item.getItemCost() 取，如果有字段）
                    BigDecimal itemCost = itemMoney.subtract(itemProfit);

                    if (StringUtils.isNotEmpty(item.getRepairIds())) {
                        if (StringUtils.isNotEmpty(item.getRepairIds())) {
                            String[] repairIdArr = item.getRepairIds().split(",");
                            for (String repairIdStr : repairIdArr) {
                                if (StringUtils.isNotEmpty(repairIdStr)) {
                                    RepairWorker repairWorker = workerMap.get(Long.valueOf(repairIdStr));
                                    if (repairWorker != null) {
                                        String jobType = repairWorker.getWorkType();
                                        if (jobType == null) {
                                        }

                                        JobTypeProfitDTO dto = resultList.stream()
                                                .filter(r -> r.getJobTypeCode().equals(jobType))
                                                .findFirst()
                                                .orElseGet(() -> {
                                                    JobTypeProfitDTO newDto = new JobTypeProfitDTO();
                                                    newDto.setJobTypeCode(jobType);
                                                    newDto.setJobTypeName(dictDataApi.getDictDataLabel(REPAIR_WORK_TYPE, jobType));
                                                    newDto.setProfit(BigDecimal.ZERO);
                                                    newDto.setMoney(BigDecimal.ZERO);
                                                    newDto.setWorkMoney(BigDecimal.ZERO);
                                                    newDto.setCost(BigDecimal.ZERO); // 新增成本字段
                                                    resultList.add(newDto);
                                                    return newDto;
                                                });

                                        if ("02".equals(item.getItemType())) { // 配件
                                            // 多人平摊利润/金额/成本
                                            int numWorkers = repairIdArr.length;
                                            BigDecimal profitPerWorker = itemProfit.divide(BigDecimal.valueOf(numWorkers), 4, RoundingMode.HALF_UP);
                                            BigDecimal moneyPerWorker = itemMoney.divide(BigDecimal.valueOf(numWorkers), 4, RoundingMode.HALF_UP);
                                            BigDecimal costPerWorker = itemCost.divide(BigDecimal.valueOf(numWorkers), 4, RoundingMode.HALF_UP);

                                            dto.setProfit(dto.getProfit().add(profitPerWorker));
                                            dto.setMoney(dto.getMoney().add(moneyPerWorker));
                                            dto.setCost(dto.getCost().add(costPerWorker));
                                        } else if ("01".equals(item.getItemType())) { // 工时
                                            dto.setWorkMoney(dto.getWorkMoney().add(itemMoney));
                                        }
                                    }
                                }
                            }
                        }

                    }

                }
            }

            // ====== 计算分组毛利率 ======
            for (JobTypeProfitDTO dto : resultList) {
                BigDecimal partsProfit = dto.getProfit();   // 配件利润
                BigDecimal partsMoney = dto.getMoney();     // 配件售价
                BigDecimal workMoney = dto.getWorkMoney();  // 工时售价

                // 不含工时
                BigDecimal profitRateWithoutWork = BigDecimal.ZERO;
                if (partsMoney.compareTo(BigDecimal.ZERO) > 0) {
                    profitRateWithoutWork = partsProfit.divide(partsMoney, 4, RoundingMode.HALF_UP);
                }

                // 含工时
                BigDecimal profitRateWithWork = BigDecimal.ZERO;
                BigDecimal totalMoney = partsMoney.add(workMoney);
                if (totalMoney.compareTo(BigDecimal.ZERO) > 0) {
                    profitRateWithWork = partsProfit.divide(totalMoney, 4, RoundingMode.HALF_UP);
                }

                dto.setProfitRateWithoutWork(profitRateWithoutWork);
                dto.setProfitRateWithWork(profitRateWithWork);
            }


            // 最终返回 list
            result.setGroupByJobType(resultList);


            //计算含工时项目毛利率
            //配件总利润除以工单总价
            BigDecimal profitRate = new BigDecimal(0);
            if (ObjectUtil.isNotEmpty(dlRepairTickets.getTotalPrice())) {
                if (dlRepairTickets.getTotalPrice().compareTo(BigDecimal.ZERO) != 0) {
                    profitRate = waresProfit.divide(dlRepairTickets.getTotalPrice(), 2, RoundingMode.HALF_UP);
                }
                result.setProfitRate(profitRate);

                // 工单成本 售价 - 利润
                result.setCost(dlRepairTickets.getTotalPrice().subtract(waresProfit));
            }
            //计算不含工时项目毛利率
            if (ObjectUtil.isNotEmpty(dlRepairTickets.getPartPrice())) {
                BigDecimal profitRateNo = new BigDecimal(0);
                if (dlRepairTickets.getPartPrice().compareTo(BigDecimal.ZERO) != 0) {
                    profitRateNo = waresProfit.divide(dlRepairTickets.getPartPrice(), 2, RoundingMode.HALF_UP);
                }
                result.setProfitRateNo(profitRateNo);
            }
        }


        return result;
    }

    /**
     * 维修工单表 作废
     * 传对象是因为需要有作废备注
     * @param repairTicketsReqVO 工单对象
     * @author 小李
     * @date 19:46 2024/9/22
     **/
    @Override
    @DSTransactional
    public void setTicketsVoid(DlRepairTicketsReqVO repairTicketsReqVO) {
        // 作废维修工单
        DlRepairTickets tickets = new DlRepairTickets();
        tickets.setId(repairTicketsReqVO.getId());
        if (ObjectUtil.isNotEmpty(repairTicketsReqVO.getRemark())) {
            tickets.setRemark(repairTicketsReqVO.getRemark());
        }
        tickets.setTicketsStatus("03");
        baseMapper.updateById(tickets);
        // 删除订单
        repairOrderInfoService.remove(new LambdaQueryWrapper<RepairOrderInfo>().eq(RepairOrderInfo::getGoodsId, repairTicketsReqVO.getId()));
        // 记录日志
        repairRecordsService.saveRepairRecord(repairTicketsReqVO.getId(), null, RecordTypeEnum.ZFGG.getCode(), repairTicketsReqVO.getRemark(), null);
    }

    /**
     * 维修工单表 结算
     * @param repairTicketsRespVO 工单
     * @author 小李
     * @date 8:50 2024/9/23
     **/
    @Override
    @DSTransactional
    public void setTicketsPaid(DlRepairTicketsRespVO repairTicketsRespVO) {
        // 更新订单状态
        RepairOrderInfo one = repairOrderInfoService.getOne(new LambdaQueryWrapper<RepairOrderInfo>().eq(RepairOrderInfo::getGoodsId, repairTicketsRespVO.getId()));
        if (ObjectUtil.isEmpty(one)) {
            throw exception0(500, "未找到该笔订单");
        }
        RepairOrderInfo repairOrderInfo = new RepairOrderInfo();
        repairOrderInfo.setId(one.getId());
        repairOrderInfo.setPayType(repairTicketsRespVO.getPayType());

        // 更新工单
        // 在这里把工单的状态更新为已完成---需要判断，如果是线上支付，需要在支付回调更新----有点问题
//        if (repairTicketsRespVO.getPayType().equals("01")) {
//            // 线上支付需要在回调中去更新状态工单状态和订单状态
//
//        } else {
        // 线下支付和签单、挂账等其他支付方式才是直接结束工单和订单
        DlRepairTickets repairTickets = this.getById(repairTicketsRespVO.getId());
        //设置工单支付状态
        repairTicketsRespVO.setPayStatus("03");
        if ("1".equals(repairTickets.getIsHandover())) {
            //已交车，已结算。那么工单状态改为 已完成
            repairTicketsRespVO.setTicketsStatus(TicketsStatusEnum.OVER.getCode());
            //工单结束
            repairTicketsRespVO.setIsFinish("1");
        }

        // 根据工单id 查询工单记录中的审批记录
        RepairRecords record = repairRecordsService.getOne(Wrappers.<RepairRecords>lambdaQuery()
                .eq(RepairRecords::getTicketId, repairTicketsRespVO.getId())
                .eq(RepairRecords::getType, RecordTypeEnum.JSSP)
                .orderByDesc(RepairRecords::getCreateTime)
                .last("limit 1"));

        // 提取冗余字段
        String otherData = record.getOtherData();
        // 将json 转为对象
        TicketsSettlementVO approvalVO = JSON.parseObject(otherData, TicketsSettlementVO.class);

        // 设置订单金额信息
        repairOrderInfo.setPayMoney(approvalVO.getActualMoney());
        repairOrderInfo.setGoodsPrice(approvalVO.getMoney());

        //判断优惠类型
        if (approvalVO.getDiscountType().equals("1")) {
            repairOrderInfo.setReduceMoney(BigDecimal.valueOf(approvalVO.getDiscount()));
        } else if (approvalVO.getDiscountType().equals("2")) {
            // 折扣计算
            repairOrderInfo.setReduceMoney(repairOrderInfo.getGoodsPrice().multiply(BigDecimal.valueOf(approvalVO.getDiscount() / 10)));
        }

        // 设置订单支付时间和支付结果
        repairOrderInfo.setPayTime(LocalDateTime.now());
        repairOrderInfo.setOrderStatus("1");
//        }
        // 更新工单
        baseMapper.updateById(repairTicketsRespVO);
        //更新订单
        repairOrderInfoService.updateById(repairOrderInfo);
        // 记录日志
        repairRecordsService.saveRepairRecord(one.getGoodsId(), null, RecordTypeEnum.SK.getCode(), "线下支付结算", null);

        // 更新工单主表时间
        refreshUpdateTime(repairTicketsRespVO.getId());
    }

    /**
     * @return void
     * @Author 许
     * @Description 结算申请
     * @Date 10:13 2025/8/20
     * @Param [repairTicketsRespVO]
     **/
    public void setTicketsSettlement(TicketsSettlementVO vo) {
        /*添加结算数据*/
        // 将vo转为json
        String json = JSON.toJSONString(vo);

        //添加维修记录
        repairRecordsService.saveRepairRecord(vo.getTicketId(), null, RecordTypeEnum.JSSQ.getCode(), "结算申请", null, json);

        /*获取字典 判断是否自动审核通过该结算申请*/
        DictDataRespDTO repairSettlementReview = dictDataApi.getDictData(BASE_BUSINESS_CONFIG, "repair_settlement_review");
        boolean ifReview = false;
        if (ObjectUtil.isNotEmpty(repairSettlementReview)) {
            ifReview = repairSettlementReview.getRemark().equals("1");
        }

        // 如果是自动审核直接修改支付状态
        if (ifReview) {
            //添加维修记录
            repairRecordsService.saveRepairRecord(vo.getTicketId(), null, RecordTypeEnum.JSSP.getCode(), "自动结算审批", null, json);

            // 修改工单支付状态为待收款
            update(Wrappers.<DlRepairTickets>lambdaUpdate()
                    .eq(DlRepairTickets::getId, vo.getTicketId())
                    .set(DlRepairTickets::getPayStatus, "02"));
            // 修改工单支付状态为待收款
            update(Wrappers.<DlRepairTickets>lambdaUpdate()
                    .eq(DlRepairTickets::getId, vo.getTicketId())
                    .set(DlRepairTickets::getPayStatus, "02"));
        }
    }

    /**
     * @return void
     * @Author 许
     * @Description 结算审批
     * @Date 13:41 2025/8/20
     * @Param [vo]
     */
    @Override
    public void settlementReview(TicketsSettlementVO vo) {
        /*添加结算审核记录*/
        // 将vo转为json
        String json = JSON.toJSONString(vo);

        //添加维修记录
        repairRecordsService.saveRepairRecord(vo.getTicketId(), null, RecordTypeEnum.JSSP.getCode(), "结算审批", null, json);

        // 修改工单支付状态为待收款
        update(Wrappers.<DlRepairTickets>lambdaUpdate()
                .eq(DlRepairTickets::getId, vo.getTicketId())
                .set(DlRepairTickets::getPayStatus, "02"));
    }

    /**
     * @return cn.iocoder.yudao.module.tickets.vo.TicketsSettlementVO
     * @Author 许
     * @Description 查询审批金额
     * @Date 14:20 2025/8/20
     * @Param [repairTicketsReqVO]
     */
    @Override
    public TicketsSettlementVO getSettlement(DlRepairTicketsReqVO repairTicketsReqVO) {
        // 根据工单id 查询工单记录中的审批记录
        RepairRecords record = repairRecordsService.getOne(Wrappers.<RepairRecords>lambdaQuery()
                .eq(RepairRecords::getTicketId, repairTicketsReqVO.getId())
                .eq(RepairRecords::getType, RecordTypeEnum.JSSP)
                .orderByDesc(RepairRecords::getCreateTime)
                .last("limit 1"));

        // 提取冗余字段
        if (record == null)
            return null;
        String otherData = record.getOtherData();
        // 将json 转为对象
        TicketsSettlementVO approvalVO = JSON.parseObject(otherData, TicketsSettlementVO.class);
        return approvalVO;
    }

    /**
     * @return java.util.List<java.util.Map>
     * @Author 许
     * @Description 统计详情-细分工单类型
     * @Date 14:15 2025/9/3
     * @Param [startDate, endDate]
     */
    @Override
    public  Map<String, Object> getBossNumStatistics(String startDate, String endDate) {
        Map<String, Object> resultMap = new HashMap<>();
        List<Map<String, Object>> statsList = new ArrayList<>();

        // 构建查询条件
        QueryWrapper<DlRepairTickets> queryWrapper = new QueryWrapper<>();

        if (startDate != null && endDate != null) {
            // 如果有时间范围，添加时间条件
            queryWrapper.between("create_time",
                    startDate + " 00:00:00",
                    endDate + " 23:59:59");
        }

        // 根据条件查询工单
        List<DlRepairTickets> ticketsInRange = this.list(queryWrapper);

        // 维修中（按 repairType 分组）
        Map<String, Long> workingStats = ticketsInRange.stream()
                .filter(item -> TicketsStatusEnum.WORKING.getCode().equals(item.getTicketsStatus()))
                .collect(Collectors.groupingBy(DlRepairTickets::getRepairType, Collectors.counting()));
        statsList.add(createStatsNode("workingNum", "维修中", workingStats));

        // 在厂（未交车且不是已完成状态 03）
        Map<String, Long> inCompanyStats = ticketsInRange.stream()
                .filter(item -> "0".equals(item.getIsHandover()))
                .filter(item -> !"03".equals(item.getTicketsStatus()))
                .collect(Collectors.groupingBy(DlRepairTickets::getRepairType, Collectors.counting()));
        statsList.add(createStatsNode("inCompanyNum", "在厂", inCompanyStats));

        // 未结算
        List<String> noPayCodeList = Arrays.asList("04", "05", "07", "01");
        Map<String, Long> noPayStats = ticketsInRange.stream()
                .filter(item -> noPayCodeList.contains(item.getTicketsStatus()))
                .collect(Collectors.groupingBy(DlRepairTickets::getRepairType, Collectors.counting()));
        statsList.add(createStatsNode("noPayNum", "未结算", noPayStats));

        // 进厂数（所有在范围内创建的）
        Map<String, Long> newOrderStats = ticketsInRange.stream()
                .collect(Collectors.groupingBy(DlRepairTickets::getRepairType, Collectors.counting()));
        statsList.add(createStatsNode("newOrderNum", "进厂数", newOrderStats));

        // 已完成（通过 mapper 查询并按 repairType 分组）
        Map<String, Long> overStats = getOverStatsByRepairType(startDate, endDate);
        statsList.add(createStatsNode("overNum", "已完成", overStats));

        // 已交车（通过 mapper 查询并按 repairType 分组）
        Map<String, Long> giveCusStats = getGiveCusStatsByRepairType(startDate, endDate);
        statsList.add(createStatsNode("giveCusNum", "已交车", giveCusStats));

        // 添加到结果map
        resultMap.put("stats", statsList);

        // 统计金额信息
        Map<String, Object> amountStats = calculateAmountStats(startDate, endDate);
        resultMap.putAll(amountStats);

        return resultMap;
    }

    // 获取已完成工单按维修类型分组的统计（优化版本）
    private Map<String, Long> getOverStatsByRepairType(String startDate, String endDate) {
        // 直接使用 mapper 查询已完成工单并按 repairType 分组
        QueryWrapper<DlRepairTickets> queryWrapper = new QueryWrapper<>();
        queryWrapper.ne("drt.tickets_status", "03"); // 已完成状态

        if (startDate != null && endDate != null) {
            queryWrapper.between("drt.create_time",
                    startDate + " 00:00:00",
                    endDate + " 23:59:59");
        }
        queryWrapper.eq("drr.type", RecordTypeEnum.ZJ.getCode());

//        // 使用 group by 直接在数据库层面分组统计
//        List<Map<String, Object>> result = repairTicketsMapper.selectMaps(
//                queryWrapper.select("repair_type", "count(*) as count")
//                        .groupBy("repair_type")
//                queryWrapper.getSqlSelect()
//        );

        // 使用 group by 直接在数据库层面分组统计
        List<Map<String, Object>> result = repairTicketsMapper.selectTicketIdByParamsNew(
                queryWrapper.select("repair_type", "count(*) as count")
                        .groupBy("repair_type")
        );

        return convertGroupResult(result);
    }

    // 获取已交车工单按维修类型分组的统计（优化版本）
    private Map<String, Long> getGiveCusStatsByRepairType(String startDate, String endDate) {
        // 直接使用 mapper 查询已交车工单并按 repairType 分组
        QueryWrapper<DlRepairTickets> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("is_handover", "1"); // 已交车

        if (startDate != null && endDate != null) {
            queryWrapper.between("create_time",
                    startDate + " 00:00:00",
                    endDate + " 23:59:59");
        }

        // 使用 group by 直接在数据库层面分组统计
        List<Map<String, Object>> result = repairTicketsMapper.selectMaps(
                queryWrapper.select("repair_type", "count(*) as count")
                        .groupBy("repair_type")
        );

        return convertGroupResult(result);
    }

    // 转换分组查询结果
    private Map<String, Long> convertGroupResult(List<Map<String, Object>> result) {
        Map<String, Long> stats = new HashMap<>();
        if (result != null) {
            for (Map<String, Object> map : result) {
                String repairType = (String) map.get("repair_type");
                Long count = (Long) map.get("count");
                if (repairType != null && count != null) {
                    stats.put(repairType, count);
                }
            }
        }
        return stats;
    }

    // 创建统计节点的辅助方法
    private Map<String, Object> createStatsNode(String code, String name, Map<String, Long> stats) {
        Map<String, Object> node = new HashMap<>();
        node.put("code", code);
        node.put("name", name);

        List<Map<String, Object>> children = stats.entrySet().stream()
                .map(entry -> {
                    Map<String, Object> child = new HashMap<>();
                    child.put("repairType", entry.getKey());
                    child.put("count", entry.getValue());
                    return child;
                })
                .collect(Collectors.toList());

        node.put("children", children);

        // 计算该统计项的总数
        long total = stats.values().stream().mapToLong(Long::longValue).sum();
        node.put("total", total);

        return node;
    }

    // 计算金额统计
    private Map<String, Object> calculateAmountStats(String startDate, String endDate) {
        Map<String, Object> amountMap = new HashMap<>();

        // 应收款：竣工已结算的单子的总金额
        QueryWrapper<DlRepairTickets> receivableWrapper = new QueryWrapper<>();
//        receivableWrapper.eq("tickets_status", "03");

        if (startDate != null && endDate != null) {
            receivableWrapper.between("drt.create_time",
                    startDate + " 00:00:00",
                    endDate + " 23:59:59");
        }

        List<Map<String, Object>> receivables = repairTicketsMapper.selectSumAmount(receivableWrapper);
        BigDecimal receivableAmount = BigDecimal.ZERO;
        Integer receivableCount = 0;
        BigDecimal receivedAmount = BigDecimal.ZERO;
        Integer receivedCount = 0;
        for (Map<String, Object> receivable : receivables) {
            // 转换为TicketsSettlementVO
            Object otherData = receivable.get("otherData");
            TicketsSettlementVO settlement = JSONObject.parseObject(
                    otherData.toString(), TicketsSettlementVO.class
            );

            //计算实付金额的总和
            BigDecimal actualMoney = settlement.getActualMoney();
            receivableAmount = receivableAmount.add(actualMoney);

            receivableCount++;

            //判断是否是已经确认收款的
            Object payType = receivable.get("payType");
            if (payType != null && "03".equals(payType.toString())) {
                // 计算已收款金额
                BigDecimal isConfirmedReceive = settlement.getActualMoney();
                receivedAmount = receivedAmount.add(isConfirmedReceive);

                receivedCount++;
            }
        }
        amountMap.put("receivableAmount", receivableAmount != null ? receivableAmount : BigDecimal.ZERO);
        amountMap.put("receivableCount", receivableCount);

        // 已收款：应收款里面出纳确认收款的金额
        amountMap.put("receivedAmount", receivedAmount != null ? receivedAmount : BigDecimal.ZERO);
        amountMap.put("receivedCount", receivedCount);

        // 待收款：应收 - 已收
        BigDecimal receivable = (BigDecimal) amountMap.get("receivableAmount");
        BigDecimal received = (BigDecimal) amountMap.get("receivedAmount");
        BigDecimal pendingAmount = receivable.subtract(received);
        amountMap.put("pendingAmount", pendingAmount);
        amountMap.put("pendingCount", receivableCount - receivedCount);

        return amountMap;
    }


    /**
     * 客户信息和车辆信息 新增、修改
     * @param customerAndCarVO 用户信息和车辆信息
     * @author 小李
     * @date 9:25 2024/10/8
     **/
    @Override
    @DSTransactional
    public void updateCustomerAndCar(CustomerAndCarVO customerAndCarVO) {
        // 新增用户信息,如果不存在
        if (ObjectUtil.isNotEmpty(customerAndCarVO.getUserInfo().getPhoneNumber()) && ObjectUtil.isNotEmpty(customerAndCarVO.getUserInfo().getCusName())) {
            AdminUserRespDTO userByMobile = adminUserApi.getUserByMobile(customerAndCarVO.getUserInfo().getPhoneNumber());
            if (ObjectUtil.isEmpty(userByMobile)) {
                UserDTO userDTO = new UserDTO();
                userDTO.setMobile(customerAndCarVO.getUserInfo().getPhoneNumber());
                userDTO.setNickname(customerAndCarVO.getUserInfo().getCusName());
                userDTO.setUsername(customerAndCarVO.getUserInfo().getPhoneNumber());
                userDTO.setPassword("123456");
                adminUserApi.createUser(userDTO);
                // 如果新增了数据，id没有回填
                if (ObjectUtil.isEmpty(userDTO.getId())) {
                    AdminUserRespDTO byMobile = adminUserApi.getUserByMobile(userDTO.getMobile());
                    userDTO.setId(byMobile.getId());
                }
                customerAndCarVO.getUserInfo().setUserId(userDTO.getId());
            } else {
                customerAndCarVO.getUserInfo().setUserId(userByMobile.getId());
            }
        }
        // 客户信息
        if (ObjectUtil.isNotEmpty(customerAndCarVO.getUserInfo())) {
            if (ObjectUtil.isEmpty(customerAndCarVO.getUserInfo().getId())) {
                // 新增之前判断一下电话号码是否重复
                List<CustomerMain> list = customerService.list(new LambdaQueryWrapper<CustomerMain>().eq(CustomerMain::getPhoneNumber, customerAndCarVO.getUserInfo().getPhoneNumber()));
                if (CollectionUtil.isNotEmpty(list)) {
                    throw exception0(500, "电话号码重复");
                }
                customerService.save(customerAndCarVO.getUserInfo());
                // 如果新增了数据，id没有回填
                if (ObjectUtil.isEmpty(customerAndCarVO.getUserInfo().getId())) {
                    CustomerMain byPhoneNumber = customerService.getOne(new LambdaQueryWrapper<CustomerMain>().eq(CustomerMain::getPhoneNumber, customerAndCarVO.getUserInfo().getPhoneNumber()));
                    customerAndCarVO.getUserInfo().setId(byPhoneNumber.getId());
                }
            } else {
                customerService.updateById(customerAndCarVO.getUserInfo());
            }
        }
        // 新增车辆信息
        if (ObjectUtil.isNotEmpty(customerAndCarVO.getCarInfo())) {
            if (ObjectUtil.isEmpty(customerAndCarVO.getCarInfo().getId())) {
                customerAndCarVO.getCarInfo().setUserId(customerAndCarVO.getUserInfo().getUserId());
                // 新增之前判断一下车牌重复没
                List<CarMain> list = carMainService.list(new LambdaQueryWrapper<CarMain>().eq(CarMain::getLicenseNumber, customerAndCarVO.getCarInfo().getLicenseNumber()));
                if (CollectionUtil.isNotEmpty(list)) {
                    throw exception0(500, "车牌重复");
                }
                carMainService.createCarMain(customerAndCarVO.getCarInfo());
                // 如果新增了数据，id没有回填
                if (ObjectUtil.isEmpty(customerAndCarVO.getCarInfo().getId())) {
                    CarMain byLicenseNumber = carMainService.getOne(new LambdaQueryWrapper<CarMain>().eq(CarMain::getLicenseNumber, customerAndCarVO.getCarInfo().getLicenseNumber()));
                    customerAndCarVO.getCarInfo().setId(byLicenseNumber.getId());
                }
            } else {
                carMainService.updateCarMain(customerAndCarVO.getCarInfo());
            }
        }
        // 新增客户车辆信息关联
        String userId = customerAndCarVO.getUserInfo().getId();
        String carId = customerAndCarVO.getCarInfo().getId();
        if (ObjectUtil.isNotEmpty(userId) && ObjectUtil.isNotEmpty(carId)) {
            // 先查询有没有记录
            CustomerCar one = customerCarService.getOne(new LambdaQueryWrapper<CustomerCar>().and(item -> {
                item.eq(CustomerCar::getCusId, userId)
                        .eq(CustomerCar::getCarId, carId);
            }));
            // 没有就新增
            if (ObjectUtil.isEmpty(one)) {
                CustomerCar customerCar = new CustomerCar();
                customerCar.setCusId(userId);
                customerCar.setCarId(carId);
                customerCarService.saveOrUpdate(customerCar);
            }
        }
    }

    /**
     * 打印工单
     * @param response HttpServletResponse
     * @param id       String
     * @author PQZ
     * @date 14:00 2024/10/12
     **/
    @Override
    @DSTransactional
    public void print(HttpServletResponse response, String id) {
        // 开始之前先计算一次工单
        boolean flag = computeTicket(id);
        if (!flag) {
            throw exception0(500, "系统异常");
        }
        // 预备工作
        // 先把这个工单和工单的所有子表信息取出来备用
        DlRepairTickets tickets = baseMapper.selectById(id);
        List<DlRepairTitem> titems = titemService.list(new LambdaQueryWrapper<DlRepairTitem>().in(DlRepairTitem::getTicketId, id));
        // 时间和日期格式化
        DateTimeFormatter time = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        DateTimeFormatter date = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        // 构建数据对象
        Map<String, Object> params = new HashMap<>();
        String fileName = "结算单.docx";
        String tmpPath = fileName;
        // 设置列表配置，如果有多个列表时需加.bind("list1", policy) 新列表配置即可
        ConfigureBuilder configureBuilder = Configure.builder();
        /*
            数据填入 以下带序号都是在模板的角度在分
         */

        /*
            1、工单基础信息（模板前四行）
         */
        // 工号（这里用的工单号）
        params.put("jobNumber", tickets.getTicketNo());

        // 客户信息（名称、送修人、送修人电话，用的工单中存的用户信息）
        params.put("customerName", tickets.getUserName());
        params.put("repairer", tickets.getUserName());
        params.put("repairerTelephone", tickets.getUserMobile());

        // 车辆信息（车牌、车架号、开单时间、已行里程、旧件处理，这些是工单中有的）
        params.put("plate", tickets.getCarNo());
        params.put("frameNumber", tickets.getCarVin());
        params.put("billingTime", time.format(tickets.getCreateTime()));
        // 已行里程
        params.put("mileage", tickets.getMileageTraveled());
        // 旧件处理
        // 获取字典数据
        List<DictDataRespDTO> dataList = dictDataApi.getDictDataList(DICT_REPAIR_PART_DISPOSAL);
        // 找到对应的处理方式
        DictDataRespDTO dictDataRespDTO = dataList.stream().filter(item -> item.getValue().equals(tickets.getPartDisposal())).findFirst().orElse(null);
        params.put("oldhandle", Objects.requireNonNull(dictDataRespDTO).getLabel());

        // 车辆其他信息（发动机号码、三包单位、保险名称、车型）
        CarMain carMain = carMainService.getById(tickets.getCarId());
        params.put("engineNumber", carMain.getEngineNumber());
        // 三包单位、保险名称
        params.put("tripleUnit", tickets.getThreePackUnits());
        params.put("insuranceName", tickets.getInsuranceName());
        // 先查车辆对应的品牌
        String brandAndModel = "";
        if (ObjectUtil.isNotEmpty(carMain.getCarBrand())) {
            CarBrand brand = carBrandService.getById(carMain.getCarBrand());
            if (ObjectUtil.isNotEmpty(brand)) {
                brandAndModel += brand.getBrandName();
                // 在查车辆对应的型号
                CarModel carModel = carModelService.getById(carMain.getCarModel());
                brandAndModel += " " + (carModel == null ? "" : carModel.getModelName());
            }
        }
        params.put("carType", brandAndModel);
        /*
            2、维修项目
         */
        // 取出维修工单子表的维修项目
        List<DlRepairTitem> projectList = titems.stream().filter(item -> item.getItemType().equals("01")).collect(Collectors.toList());
        // 构建导出对象
        List<Map<String, Object>> projects = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(projectList)) {
            projectList.forEach(item -> {
                Map<String, Object> project = new HashMap<>();
                project.put("repRort", projects.size() + 1);
                project.put("repItem", item.getItemName());
                project.put("repPrice", item.getItemPrice());
                project.put("repCount", item.getItemCount());
                BigDecimal itemDiscount = item.getItemDiscount();
                if (ObjectUtil.isEmpty(item.getItemDiscount())) {
                    itemDiscount = new BigDecimal("1.00");
                }
                project.put("repDiscount", itemDiscount.equals(new BigDecimal("1.00")) ? "1.00" : item.getItemDiscount());
                // 工时费 项目打折后的费用
                // 取不到先给0
                project.put("labourAmount", item.getItemMoney());
                project.put("major", item.getRepairNames());
                project.put("repRemark", item.getRemark());
                if (ObjectUtil.isNotEmpty(item.getItemTypeId())) {
                    BaseType baseType = baseTypeService.getById(item.getItemTypeId());
                    project.put("repNature", baseType == null ? "" : baseType.getName());
                }
                projects.add(project);
            });
        }
        configureBuilder.bind("projects", new HackLoopTableRenderPolicy());
        params.put("projects", projects);

        /*
            3、材料清单
         */
        // 取出维修工单子表的配件
        List<DlRepairTitem> waresList = titems.stream().filter(item -> item.getItemType().equals("02")).collect(Collectors.toList());
        List<Map<String, Object>> wares = new ArrayList<>();
        // 取出对应需要的其他表的数据备用
        List<DictDataRespDTO> unitDataList = dictDataApi.getDictDataList(DICT_REPAIR_UNIT);
        if (CollectionUtil.isNotEmpty(waresList)) {
            waresList.forEach(item -> {
                Map<String, Object> ware = new HashMap<>();
                ware.put("matRort", wares.size() + 1);
                // 配件号
                // 先取配件
                RepairWares wareById = waresService.getOne(new LambdaQueryWrapper<RepairWares>().eq(RepairWares::getId, item.getPartId()));
                String matNum = "";
                if (ObjectUtil.isNotEmpty(wareById)) {
                    matNum = wareById.getCode();
                }
                ware.put("matNum", matNum);
                ware.put("matName", item.getItemName());
                // 单位
                // 先取单位字典
                String matUnit = "";
                if (CollectionUtil.isNotEmpty(unitDataList) && ObjectUtil.isNotEmpty(item.getItemUnit())) {
                    DictDataRespDTO unit = unitDataList.stream().filter(i -> i.getValue().equals(item.getItemUnit())).findFirst().orElse(null);
                    if (unit != null) {
                        matUnit = unit.getLabel();
                    }
                }
                ware.put("matUnit", matUnit);
                ware.put("matPrice", item.getItemPrice());
                ware.put("matCount", item.getItemCount());
                BigDecimal itemDiscount = item.getItemDiscount();
                if (ObjectUtil.isEmpty(item.getItemDiscount())) {
                    itemDiscount = new BigDecimal("1.00");
                }
                ware.put("matDiscount", itemDiscount.equals(new BigDecimal("1.00")) ? "无折扣" : item.getItemDiscount());
                ware.put("matAmount", item.getItemMoney());
                ware.put("matRemark", item.getRemark());
                if (ObjectUtil.isNotEmpty(item.getItemTypeId())) {
                    BaseType baseType = baseTypeService.getById(item.getItemTypeId());
                    ware.put("matNature", baseType == null ? "" : baseType.getName());
                }
                wares.add(ware);
            });
        }
        configureBuilder.bind("wares", new HackLoopTableRenderPolicy());
        params.put("wares", wares);
        // 小计
        params.put("matTotalAmount", tickets.getPartPrice());

        /*
            4、其他信息
         */
        // 目前能取到：材料费、材料费优惠、总费用、应收费、大写、工单备注、工时费、工时费优惠、其他费、材料工时优惠
        // 计算配件没打折的总金额
        BigDecimal matCost = waresList.stream()
                .map(item -> item.getItemPrice().multiply(BigDecimal.valueOf(item.getItemCount())))
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        params.put("matCost", matCost);
        // 没打折的金额减去打折后的总金额得到优惠了多少钱
        BigDecimal matFavorable = matCost.subtract(ObjectUtil.isNotEmpty(tickets.getPartPrice()) ? tickets.getPartPrice() : BigDecimal.ZERO);
        params.put("matFavorable", matFavorable);
        // 计算没打折的工单总价
        BigDecimal matTotalCost = titems.stream().map(item -> item.getItemPrice().multiply(BigDecimal.valueOf(item.getItemCount()))).reduce(BigDecimal.ZERO, BigDecimal::add);
        // 加上特殊费用
        // 管理费
        BigDecimal managerMoney = ObjectUtil.isNotEmpty(tickets.getManagerMoney()) ? tickets.getManagerMoney() : new BigDecimal("0");
        // 救援费
        BigDecimal rescueMoney = ObjectUtil.isNotEmpty(tickets.getRescueMoney()) ? tickets.getRescueMoney() : new BigDecimal("0");
        // 三包费
        BigDecimal threePackMoney = ObjectUtil.isNotEmpty(tickets.getThreePackMoney()) ? tickets.getThreePackMoney() : new BigDecimal("0");
        // 定损费
        BigDecimal confirmFaultMoney = ObjectUtil.isNotEmpty(tickets.getConfirmFaultMoney()) ? tickets.getConfirmFaultMoney() : new BigDecimal("0");
        matTotalCost = matTotalCost.add(managerMoney).add(rescueMoney).add(threePackMoney).add(confirmFaultMoney);
        params.put("matTotalCost", matTotalCost);
        // 应收费目前是工单的打折后的总价
        params.put("allCost", tickets.getTotalPrice());
        // 应收费转大写
        String bigAllCost = MoneyUtils.toChinese(tickets.getTotalPrice());
        params.put("bigAllCost", bigAllCost);
        // 这个备注现在是工单备注
        params.put("paperRemark", tickets.getRemark());
        // 项目没打折的总价
        BigDecimal labourCost = projectList.stream().map(item -> item.getItemPrice().multiply(BigDecimal.valueOf(item.getItemCount()))).reduce(BigDecimal.ZERO, BigDecimal::add);
        params.put("labourCost", labourCost);
        // 项目没打折的总价减打折后的总价就是优惠
        BigDecimal labourFavorable = labourCost.subtract(ObjectUtil.isNotEmpty(tickets.getProjectPrice()) ? tickets.getProjectPrice() : BigDecimal.ZERO);
        params.put("labourFavorable", labourFavorable);
        // 其他费用 维修工单的其他子项的费用
        params.put("otherCost", ObjectUtil.isNotEmpty(tickets.getOtherPrice()) ? tickets.getOtherPrice() : 0.00);
        // 材料工时优惠 材料优惠+工时优惠
        params.put("matlabourFavorable", "0.00");

        // 管理费、救援费、三包费、定损费、结算备注（这个是因为和工单备注用的同一个）、todo 客户签字
        params.put("manageCost", ObjectUtil.isNotEmpty(tickets.getManagerMoney()) ? tickets.getManagerMoney() : "0.00");
        params.put("rescueCost", ObjectUtil.isNotEmpty(tickets.getRescueMoney()) ? tickets.getRescueMoney() : "0.00");
        params.put("tripleCost", ObjectUtil.isNotEmpty(tickets.getThreePackMoney()) ? tickets.getThreePackMoney() : "0.00");
        params.put("lossCost", ObjectUtil.isNotEmpty(tickets.getConfirmFaultMoney()) ? tickets.getConfirmFaultMoney() : "0.00");
        params.put("costRemark", tickets.getBillingRemark());
        /*
            5、最后结尾
         */
        // 目前能取到：服务顾问、联系电话、结算日期、付款情况
        params.put("serviceConsultant", tickets.getAdviserName());
        AdminUserRespDTO adviser = adminUserApi.getUser(Long.valueOf(tickets.getAdviserId()));
        if (ObjectUtil.isNotEmpty(adviser) && ObjectUtil.isNotEmpty(adviser.getMobile())) {
            params.put("serviceTelephone", adviser.getMobile());
        }
        // 结算日期
        params.put("settleDate", date.format(LocalDateTime.now()));
        // 取订单
        RepairOrderInfo orderInfo = repairOrderInfoService.getOne(new LambdaQueryWrapper<RepairOrderInfo>().eq(RepairOrderInfo::getGoodsId, tickets.getId()));
        if (ObjectUtil.isNotEmpty(orderInfo)) {
            // 付款情况
            // 先取支付字典
            List<DictDataRespDTO> payTypes = dictDataApi.getDictDataList(DICT_REPAIR_PAY_TYPE);
            String costCondition = "未支付";
            if (ObjectUtil.isNotEmpty(payTypes) && ObjectUtil.isNotEmpty(orderInfo.getPayType())) {
                DictDataRespDTO payType = payTypes.stream().filter(item -> item.getValue().equals(orderInfo.getPayType())).findFirst().orElse(null);
                if (payType != null) {
                    costCondition = payType.getLabel();
                }
            }
            params.put("costCondition", costCondition);
        }

        // 单位、开户行、地址、账号
        // 取当前租户的公司们
        Long tenantId = tickets.getTenantId();
        List<Company> companies = companyService.list(new LambdaQueryWrapper<Company>().in(Company::getTenantId, tenantId));
        // 得到属于当前业务的公司
        Company company = companies.stream().filter(item -> {
            String[] service = item.getServiceCodes().split(",");
            for (String s : service) {
                if (s.equals("weixiu")) {
                    return true;
                }
            }
            return false;
        }).findFirst().orElse(null);
        String corporation = "";
        String bank = "";
        String bankAddress = "";
        String account = "";
        if (company != null) {
            corporation = company.getCorpName();
            bankAddress = company.getAddress();
            bank = company.getBankAccount();
            account = company.getAccount();
        }
        // 取不到的先给未知
        params.put("corporation", corporation);
        params.put("bank", bank);
        params.put("bankAddress", bankAddress);
        params.put("account", account);

        // 调支付接口
        String payUrl = "";
        try {
            Map<String, Object> stringObjectMap = repairOrderInfoService.payTransactionsCode(orderInfo.getId());
            payUrl = (String) stringObjectMap.get("code_url");
            // 这个地方可能报错，报错就不生成二维码了，保证工单是能出来的
            // 生成收款二维码
            int width = 100, height = 100;
            byte[] texts = CreateQRCodeUtil.GenerateQRCode(payUrl, width, height);
            if (ObjectUtil.isNotEmpty(texts)) {
                params.put("qrCode", new PictureRenderData(width, height, PictureType.PNG, texts));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        Configure config = configureBuilder.build();
        try {
            InputStream inputStream = XWPFTemplate.class.getResourceAsStream("/templates/modified_ticketTemplate.docx");
            XWPFTemplate template = XWPFTemplate.compile(inputStream, config);
            template.render(params);
            FileOutputStream fos = new FileOutputStream(tmpPath);
            template.write(fos);
            fos.flush();
            template.close();
            WordUtil.down(response, tmpPath, fileName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 翻译字典
     * @param dlRepairTicketsIPage TODO
     * @return com.baomidou.mybatisplus.core.metadata.IPage<cn.iocoder.yudao.module.tickets.entity.DlRepairTickets>
     * @author vinjor-M
     * @date 16:25 2025/1/9
     **/
    private IPage<DlRepairTickets> dealDictText(IPage<DlRepairTickets> dlRepairTicketsIPage) {
        //维修类型
        List<DictDataRespDTO> repairTypeList = dictDataApi.getDictDataList(DICT_REPAIR_TYPE);
        Map<String, String> repairTypeMap = repairTypeList.stream().collect(Collectors.toMap(DictDataRespDTO::getValue, DictDataRespDTO::getLabel));
        //支付方式
        List<DictDataRespDTO> payTypeList = dictDataApi.getDictDataList(DICT_REPAIR_PAY_TYPE);

        dlRepairTicketsIPage.getRecords().forEach(item -> {
            if (StringUtils.isNotEmpty(item.getRepairType())) {
                item.setRepairTypeText(repairTypeMap.get(item.getRepairType()));
            }
            if (StringUtils.isNotEmpty(item.getPayType())) {
                payTypeList.forEach(payType -> {
                    if (item.getPayType().contains(payType.getValue())) {
                        if (StringUtils.isEmpty(item.getPayTypeText())) {
                            item.setPayTypeText(payType.getLabel());
                        } else {
                            item.setPayTypeText(item.getPayTypeText() + "," + payType.getLabel());
                        }
                    }
                });
            }
        });
        return dlRepairTicketsIPage;
    }

    /**
     * 分类查询工单分页
     * @param repairTicketsReqVO 查询对象
     * @author 小李
     * @date 16:26 2024/10/12
     **/
    @Override
    public IPage<DlRepairTickets> getPageType(DlRepairTicketsReqVO repairTicketsReqVO, Page<DlRepairTickets> page) {
        String userRoleCode = getUserRole();
        if (null != repairTicketsReqVO.getIdList() && repairTicketsReqVO.getIdList().size() > 0) {
            //根据id集和查询
            DlRepairTicketsReqVO queryObj = new DlRepairTicketsReqVO();
            queryObj.setIdList(repairTicketsReqVO.getIdList());
            return dealDictText(baseMapper.getPageTypeAll(queryObj, page));
        }
        if (userRoleCode.equals(RepairRoleEnum.ADMIN.getCode())) {
            //维修管理员看所有数据
        } else if (userRoleCode.equals(RepairRoleEnum.WAREHOUSE.getCode())) {
            //仓管看到所有数据
        } else if (userRoleCode.equals(RepairRoleEnum.INSPECTION.getCode())) {
            //总检
            if (RepairCons.TICKETS_WAITING.equals(repairTicketsReqVO.getSelectType())) {
                //查待处理的，查需要自己处理的，否则就是查所有
                repairTicketsReqVO.setUserIds(Collections.singletonList(SecurityFrameworkUtils.getLoginUserId()));
            }
        } else if (userRoleCode.equals(RepairRoleEnum.ADVISOR.getCode())) {
            //服务顾问，暂时看所有的工单
//            repairTicketsReqVO.setAdviserId(String.valueOf(SecurityFrameworkUtils.getLoginUserId()));
        } else if (userRoleCode.equals(RepairRoleEnum.REPAIR_STAFF.getCode())) {
            //维修工，进一步判断是否是班组长
            //维修工，只查维修中、
            boolean ifLeader = repairWorkerService.getIfLeader();
            repairTicketsReqVO.setRoleCode(RepairRoleEnum.REPAIR_STAFF.getCode());
            if (ifLeader) {
                //班组长，看本班组所有数据
                RepairWorker one = repairWorkerService.getOne(new LambdaQueryWrapper<RepairWorker>().eq(RepairWorker::getUserId, SecurityFrameworkUtils.getLoginUserId()));
                // 查自己班组的员工信息
                List<RepairWorker> list = repairWorkerService.list(new LambdaQueryWrapper<RepairWorker>().eq(RepairWorker::getWorkType, one.getWorkType()));
                // 所有的员工信息
                String userIdsStr = "";
                List<Long> userIds = new ArrayList<>();
                for (RepairWorker worker : list) {
                    userIds.add(worker.getUserId());
                    if ("".equals(userIdsStr)) {
                        userIdsStr = String.valueOf(worker.getUserId());
                    } else {
                        userIdsStr = userIdsStr + "," + worker.getUserId();
                    }
                }
                repairTicketsReqVO.setUserIds(userIds);
                repairTicketsReqVO.setUserIdsStr(userIdsStr);
            } else {
                //普通维修工
                repairTicketsReqVO.setUserIds(Collections.singletonList(SecurityFrameworkUtils.getLoginUserId()));
            }
        } else {
            return null;
        }
        if (RepairCons.TICKETS_WAITING.equals(repairTicketsReqVO.getSelectType())) {
            //查待处理
            return dealDictText(baseMapper.getPageType(repairTicketsReqVO, page));
        } else {
            //查所有,
            if (("jinchang".equals(repairTicketsReqVO.getTicketsStatus()) ||
                    "yijungong".equals(repairTicketsReqVO.getTicketsStatus()) ||
                    "yijiaoche".equals(repairTicketsReqVO.getTicketsStatus())) && StringUtils.isNotEmpty(repairTicketsReqVO.getStartDate())) {
                //如果是查询进场、已竣工、已交车三个状态，同时选了时间范围的，需要单独处理
                String startDate = repairTicketsReqVO.getStartDate() + " 00:00:00";
                String endDate = repairTicketsReqVO.getEndDate() + " 23:59:59";
                List<String> idList = new ArrayList<>();
                if ("yijungong".equals(repairTicketsReqVO.getTicketsStatus())) {
                    //已竣工
                    idList = repairTicketsMapper.selectTicketIdByParams(null, RecordTypeEnum.ZJ.getCode(), startDate, endDate);
                } else if ("yijiaoche".equals(repairTicketsReqVO.getTicketsStatus())) {
                    //已交车
                    idList = repairTicketsMapper.selectTicketIdByParams(null, RecordTypeEnum.JC.getCode(), startDate, endDate);
                } else {
                    //进厂
                    repairTicketsReqVO.setStartDate(startDate);
                    repairTicketsReqVO.setEndDate(endDate);
                }
                if (null != idList && !idList.isEmpty()) {
                    repairTicketsReqVO.setIdList(idList);
                    //时间查询条件置空
                    repairTicketsReqVO.setStartDate(null);
                    repairTicketsReqVO.setEndDate(null);
                }
            } else {
                //否则查询时间不生效，按维修状态查
                List<String> statusList = new ArrayList<>();
                if ("weixiuzhong".equals(repairTicketsReqVO.getTicketsStatus())) {
                    //维修中
                    statusList.add(TicketsStatusEnum.WORKING.getCode());
                } else if ("weijiesuan".equals(repairTicketsReqVO.getTicketsStatus())) {
                    //未结算
                    statusList = Arrays.asList("04", "05", "07", "01");
                } else if ("zaichang".equals(repairTicketsReqVO.getTicketsStatus())) {
                    //在厂，就是没交车的,且不能是已作废和已完成的
                    repairTicketsReqVO.setIsHandover("0");
                    statusList = Arrays.asList("04", "05", "07", "01", "06", "02");
                } else if ("jinchang".equals(repairTicketsReqVO.getTicketsStatus())) {
                    //进厂
                    statusList.add(TicketsStatusEnum.NO_WORK.getCode());
                } else if ("yijungong".equals(repairTicketsReqVO.getTicketsStatus())) {
                    //已竣工
                    statusList = Arrays.asList("07", "01", "06", "02", "08");
                } else if ("yijiaoche".equals(repairTicketsReqVO.getTicketsStatus())) {
                    //已交车
                    repairTicketsReqVO.setIsHandover("1");
                }
                if (!statusList.isEmpty()) {
                    repairTicketsReqVO.setStatusList(statusList);
                }
            }
            return dealDictText(baseMapper.getPageTypeAll(repairTicketsReqVO, page));
        }
    }

    /**
     * 判断登录用户的角色，针对维修工单中的四个角色
     * @author 小李
     * @date 9:28 2024/10/14
     **/
    @Override
    public String getUserRole() {
        // 查看当前登录用户是什么角色
        // 当前登录用户的角色信息
        List<Long> byUserId = permissionApi.getRoleIdsByUserId(SecurityFrameworkUtils.getLoginUserId());
        // 所有的角色信息
        List<RoleReqDTO> roleList = roleApi.getRoleList();
        // 过滤出登录用户有的角色信息
        List<RoleReqDTO> collect = roleList.stream().filter(item -> byUserId.contains(item.getId())).collect(Collectors.toList());
        // 取出角色名称
        List<String> codes = collect.stream().map(RoleReqDTO::getCode).collect(Collectors.toList());
        // 维修业务管理员--可以看所有数据，但不能做操作
        if (codes.contains(RepairRoleEnum.ADMIN.getCode())) {
            return RepairRoleEnum.ADMIN.getCode();
        }
        // 总检
        if (codes.contains(RepairRoleEnum.INSPECTION.getCode())) {
            return RepairRoleEnum.INSPECTION.getCode();
        }
        // 服务顾问
        if (codes.contains(RepairRoleEnum.ADVISOR.getCode())) {
            return RepairRoleEnum.ADVISOR.getCode();
        }
        // 维修工（或班组长）
        if (codes.contains(RepairRoleEnum.REPAIR_STAFF.getCode())) {
            return RepairRoleEnum.REPAIR_STAFF.getCode();
        }
        // 维修仓库管理员
        if (codes.contains(RepairRoleEnum.WAREHOUSE.getCode())) {
            return RepairRoleEnum.WAREHOUSE.getCode();
        }
        // 维修财务
        if (codes.contains(RepairRoleEnum.WXCW.getCode())) {
            return RepairRoleEnum.WXCW.getCode();
        }
        // 啥也不是
        return null;
    }

    /**
     * 接单
     * @param id 工单ID
     * @author 小李
     * @date 11:48 2024/10/14
     **/
    @Override
    @DSTransactional
    public void updateTake(String id, String image) {
        // 改变时看看是否已经被接了
        int update = baseMapper.update(new LambdaUpdateWrapper<DlRepairTickets>()
                .set(DlRepairTickets::getTicketsWorkStatus, TicketsWorkStatusEnum.RECEIVED.getCode())
                .and(item -> {
                    item.eq(DlRepairTickets::getId, id)
                            .eq(DlRepairTickets::getTicketsWorkStatus, TicketsWorkStatusEnum.WAITING_RECEIVING.getCode());
                }));
        if (update != 1) {
            throw exception0(500, "工单已被其他人接单了");
        }
        //最后记录操作日志--接单
        repairRecordsService.saveRepairRecord(id, null, RecordTypeEnum.jd.getCode(), "接单", image);

        // 通知客户
        // 构建消息
        DlRepairTickets tickets = baseMapper.selectById(id);
        RepairWorker worker = repairWorkerService.getOne(new LambdaQueryWrapper<RepairWorker>().eq(RepairWorker::getUserId, SecurityFrameworkUtils.getLoginUserId()).last("limit 1"));
        List<DictDataRespDTO> dataList = dictDataApi.getDictDataList("repair_work_type");
        DictDataRespDTO dictDataRespDTO = dataList.stream().filter(item -> item.getValue().equals(worker.getWorkType())).findFirst().orElse(null);
        String message = String.format("您的爱车%s已由%s %s接单了", tickets.getCarNo(), (dictDataRespDTO != null ? dictDataRespDTO.getLabel() : "维修工"), worker.getUserName());
        repairWorkerService.sentMessageToCus(SystemEnum.REPAIR.getCode(), id, message, false);

        // 更新工单主表时间
        refreshUpdateTime(id);
    }

    /**
     * 指派员工，通知施工
     * @param reqVO 请求对象
     * @author 小李
     * @date 15:34 2024/10/14
     **/
    @Override
    public void updateRepair(DlRepairTicketsReqVO reqVO) {
        // 指派的时候看下是不是已经有人接单了----vinjor-m 服务顾问指派，不需要判断是否已有人接单
        int update = baseMapper.update(new LambdaUpdateWrapper<DlRepairTickets>()
                .set(DlRepairTickets::getNowRepairId, reqVO.getNowRepairId())
                .set(DlRepairTickets::getNowRepairName, reqVO.getNowRepairName())
                //工单状态设置为---施工中
                .set(DlRepairTickets::getTicketsStatus, TicketsStatusEnum.WORKING.getCode())
                //维修状态设置为待接单
                .set(DlRepairTickets::getTicketsWorkStatus, TicketsWorkStatusEnum.WAITING_RECEIVING.getCode())
                .and(item -> item.eq(DlRepairTickets::getId, reqVO.getId()))
        );
        if (update != 1) {
            throw exception0(500, "工单已开始");
        }
        repairWorkerService.sentMessage(SystemEnum.REPAIR.getCode(), reqVO.getNowRepairId(), "您有新的工单要处理");

        // 获取当前操作人的身份----小李新加的逻辑
        String userRole = getUserRole();
        if (userRole.equals(RepairRoleEnum.REPAIR_STAFF.getCode())) {
            //指派施工，施工项目中不包含选中人员处理
            setTicketItem(reqVO.getId(), reqVO.getNowRepairId(), reqVO.getNowRepairName());
        }
        //最后记录操作日志--指派施工--pull下来的逻辑
        String code = RecordTypeEnum.ZPSG.getCode();
        String remark = "指派施工";

        // 总检
        if (userRole.equals(RepairRoleEnum.INSPECTION.getCode())) {
            repairRecordsService.saveRepairRecord(reqVO.getId(), null, RecordTypeEnum.NFPG.getCode(), reqVO.getRemark(), reqVO.getImage());
        } else {
            // 其他
            //最后记录操作日志--指派施工
            repairRecordsService.saveRepairRecord(reqVO.getId(), null, code, remark, null);
        }

        // 更新工单主表时间
        refreshUpdateTime(reqVO.getId());
    }

    /**
     * 指派施工，施工项目中不包含选中人员处理
     * @param ticketId      工单id
     * @param nowRepairId   当前处理人id
     * @param nowRepairName 当前处理人名称
     * @author PQZ
     * @date 16:30 2024/11/13
     **/
    private void setTicketItem(String ticketId, Long nowRepairId, String nowRepairName) {
        //查询所有维修工
        List<RepairWorker> list = repairWorkerService.list();
        //查询当前工单下所有项目
        LambdaQueryWrapper<DlRepairTitem> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(DlRepairTitem::getTicketId, ticketId);
        List<DlRepairTitem> itemList = titemService.list(lambdaQueryWrapper);
        //过滤出不包括当前指派人的值
        List<DlRepairTitem> filterList = itemList.stream().filter(item -> !item.getRepairNames().contains(nowRepairName)).collect(Collectors.toList());
        List<DlRepairTitem> saveList = new ArrayList<>();
        //当前处理人工作组
        Map<Long, RepairWorker> workerMap = list.stream().collect(Collectors.toMap(RepairWorker::getUserId, worker -> worker));
        String workType = workerMap.get(nowRepairId).getWorkType();
        filterList.forEach(item -> {
            //深拷贝
            DlRepairTitem saveItem = BeanUtils.toBean(item, DlRepairTitem.class);
            if (StringUtils.isNotEmpty(saveItem.getRepairIds())) {
                //取出用户id
                List<Long> repairIds = Arrays.stream(saveItem.getRepairIds().split(",")).map(Long::parseLong).collect(Collectors.toList());
                repairIds.forEach(idItem -> {
                    // 获取对应的 worker
                    RepairWorker worker = workerMap.get(idItem);
                    // 检查 worker 是否存在并比较 workType
                    if (worker != null && workType.equals(worker.getWorkType())) {
                        saveList.add(saveItem);
                        return;
                    }
                });
            }
        });
        saveList.forEach(item -> {
            String newRepairId = item.getRepairIds() + "," + nowRepairId;
            String newRepairName = item.getRepairNames() + "," + nowRepairName;
            item.setRepairIds(newRepairId);
            item.setRepairNames(newRepairName);
        });
        titemService.updateBatchById(saveList);

    }

    /**
     * 更新工单状态(针对开始施工、施工中记录、施工完成)
     * @param respVO 请求对象
     * @author 小李
     * @date 15:46 2024/10/18
     **/
    @Override
    @DSTransactional
    public void updateStatus(DlRepairTicketsRespVO respVO) {
        // 如果是开始施工，但项目还在施工中，就不能施工
        if (RecordTypeEnum.KSSG.getCode().equals(respVO.getRecordType())) {
            DlRepairTitem titem = titemService.getById(respVO.getItem().getId());
            if (TicketsItemStatusEnum.WORKING.getCode().equals(titem.getItemStatus())) {
                throw exception0(500, "选择的项目在施工中，不能重新开始施工");
            }
        }

        if (StringUtils.isNotEmpty(respVO.getTicketsWorkStatus())) {
            // 更新工单主表
            LambdaUpdateWrapper<DlRepairTickets> wrapper = new LambdaUpdateWrapper<>();
            wrapper.eq(DlRepairTickets::getId, respVO.getId());
            wrapper.set(DlRepairTickets::getTicketsWorkStatus, respVO.getTicketsWorkStatus());
            // 这里注掉，不能让员工更新工单的状态为完成，后面还有总检、服务顾问
//            if (ObjectUtil.isNotEmpty(respVO.getIsFinish())) {
//                wrapper.set(DlRepairTickets::getIsFinish, respVO.getIsFinish());
//            }
            baseMapper.update(wrapper);
        }
        if (null != respVO.getItem() && StringUtils.isNotEmpty(respVO.getItem().getItemStatus())) {
            // 更新工单子表
            titemService.update(new LambdaUpdateWrapper<DlRepairTitem>()
                    .set(DlRepairTitem::getItemStatus, respVO.getItem().getItemStatus())
                    .eq(DlRepairTitem::getId, respVO.getItem().getId())
            );
        }
        // 记录日志
        repairRecordsService.saveRepairRecord(respVO.getId(), respVO.getItem().getId(), respVO.getRecordType(), respVO.getRemark(), respVO.getImage());

        // 通知服务顾问和维修总检
        DlRepairTickets tickets = baseMapper.selectById(respVO.getId());
        DlRepairTitem titem = titemService.getById(respVO.getItem().getId());
         /*
            消息组成：谁，对那个工单，那个项目，干了什么
            谁：工单的当前施工人
            对那个工单：工单号
            那个项目：操作的项目
            干了什么：提交的请求
            干了什么有三种情况：开始施工、施工中的记录上传、施工完成
            施工完成又分四种情况：阶段完成、完成并移交下一班组、完成并移交总检、完成工单，其中部分完成同开始施工、施工中的记录上传一致，只上传，不改变
          */
        // 消息生成
        String message = createMessage(tickets.getNowRepairName(), tickets.getCarNo(), null == titem ? "" : titem.getItemName(), respVO.getRecordType(), respVO.getFinishType(), respVO.getNextName());
        // 通知服务顾问
        // 如果员工完成了订单
        if (RecordTypeEnum.SGWCZJ.getCode().equals(respVO.getRecordType()) && "03".equals(respVO.getFinishType())) {
            repairWorkerService.sentMessage(SystemEnum.REPAIR.getCode(), Long.valueOf(tickets.getAdviserId()), "您有新的工单可以出厂检验");
            // 需要把工单的当前操作人移交服务顾问
            baseMapper.update(new LambdaUpdateWrapper<DlRepairTickets>()
                    .setSql("now_repair_id = adviser_id")
                    .setSql("now_repair_name = adviser_name")
                    .eq(DlRepairTickets::getId, respVO.getId()));

            // 因为目前的配件申请表在申请的时候就把配件加到工单中了，但后续的领取并没有同步到工单，所以如果这里是员工直接完成移交给服务顾问，就需要同步一下
            boolean flag = syncTicketWaresToTicket(respVO.getId());
            if (!flag) {
                throw exception0(500, "系统异常");
            }
        } else {
            repairWorkerService.sentMessage(SystemEnum.REPAIR.getCode(), Long.valueOf(tickets.getAdviserId()), message);
        }
        // 通知总检
        // 总检查角色包含总检的员工
        // 取这个角色的角色信息
        RoleReqDTO roleInfo = roleApi.getRoleInfo(RepairRoleEnum.INSPECTION.getCode());
        // 通过角色信息查有这个角色的人
        List<Long> ids = permissionApi.getUserIdByRoleId(roleInfo.getId());
        if (CollectionUtil.isEmpty(ids)) return;
        // 如果是移交给总检就需要通知总检并让其总检
        if (RecordTypeEnum.SGWCZJ.getCode().equals(respVO.getRecordType()) && "02".equals(respVO.getFinishType())) {
            // 工单移交给总检(理论上讲只有一个总检，就算有多个这里面也只给一个)
            AdminUserRespDTO user = adminUserApi.getUser(ids.get(0));
            baseMapper.update(new LambdaUpdateWrapper<DlRepairTickets>()
                    .set(DlRepairTickets::getNowRepairId, user.getId())
                    .set(DlRepairTickets::getNowRepairName, user.getNickname())
                    .set(DlRepairTickets::getTicketsWorkStatus, "05")
                    .eq(DlRepairTickets::getId, respVO.getId()));
            repairWorkerService.sentMessage(SystemEnum.REPAIR.getCode(), user.getId(), "您有新的工单需要总检");
        } else {
            ids.forEach(id -> {
                repairWorkerService.sentMessage(SystemEnum.REPAIR.getCode(), id, message);
            });
        }

        //通知客户（开始施工、施工完成）
        if (respVO.getRecordType().equals(RecordTypeEnum.KSSG.getCode()) || respVO.getRecordType().equals(RecordTypeEnum.SGWCZJ.getCode())) {
            // 构建消息
            RepairWorker worker = repairWorkerService.getOne(new LambdaQueryWrapper<RepairWorker>().eq(RepairWorker::getUserId, SecurityFrameworkUtils.getLoginUserId()).last("limit 1"));
            List<DictDataRespDTO> dataList = dictDataApi.getDictDataList("repair_work_type");
            DictDataRespDTO dictDataRespDTO = dataList.stream().filter(item -> item.getValue().equals(worker.getWorkType())).findFirst().orElse(null);
            String newMessage = String.format(
                    "您的爱车%s已由%s %s%s",
                    tickets.getCarNo(),
                    (dictDataRespDTO != null ? dictDataRespDTO.getLabel() : "维修工"),
                    worker.getUserName(),
                    (respVO.getRecordType().equals(RecordTypeEnum.KSSG.getCode()) ? "开始施工" : "施工完成")
            );
            repairWorkerService.sentMessageToCus(SystemEnum.REPAIR.getCode(), tickets.getId(), newMessage, false);
        }

        // 更新工单主表时间
        refreshUpdateTime(respVO.getId());
    }

    private String createMessage(String who, String no, String projectName, String type, String finishType, String nextName) {
        String result = who;
        switch (type) {
            // 开始施工
            case "kssg":
                return result + "开始施工车牌为：" + no + "工单中" + projectName + "项目";
            // 施工记录
            case "sgz":
                return result + "上传了车牌为：" + no + "工单中" + projectName + "项目的维修记录";
            // 施工完成
            case "sgwczj":
                if (ObjectUtil.isNotEmpty(finishType)) {
                    result = result + "完成了车牌为：" + no + "工单中" + projectName + "项目";
                    switch (finishType) {
                        // 完成并移交下一班组
                        case "01":
                            return result + "并移交工单给了" + nextName;
                        // 完成并移交总检
                        case "02":
                            return result + "并移交给了总检";
                        // 完成工单
                        case "03":
                            return who + "完成了工单";
                        default:
                            return "异常消息";
                    }
                } else {
                    return result + "部分完成了车牌为：" + no + "工单中" + projectName + "项目";
                }
            default:
                return "异常消息";
        }
    }

    /**
     * 配件客户是否可见
     * @param id   工单ID
     * @param show 是否可见 1可见， 0不可见
     * @author 小李
     * @date 21:52 2024/10/18
     **/
    @Override
    public void updateShow(String id, String show) {
        DlRepairTickets dlRepairTickets = new DlRepairTickets();
        dlRepairTickets.setId(id);
        dlRepairTickets.setPartShow(show);
        baseMapper.updateById(dlRepairTickets);

        refreshUpdateTime(id);
    }

    /**
     * 查待总检或待出厂检验的工单
     * 维修服务顾问和总检用的
     * @param repairTicketsReqVO 请求对象
     * @author 小李
     * @date 11:40 2024/10/23
     **/
    @Override
    public IPage<DlRepairTickets> getPageByRole(DlRepairTicketsReqVO repairTicketsReqVO, Page<DlRepairTickets> page) {
        String userRoleCode = getUserRole();
        // 当前操作人的id
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        //设置查询类型---待总检或待出厂检验的工单
        repairTicketsReqVO.setSelectType("special");
        // 维修服务顾问
        if (userRoleCode.equals(RepairRoleEnum.ADVISOR.getCode())) {
            // 查所有的服务顾问
            List<UserDTO> userDTOS = roleApi.selectUserListByRoleCode(TenantContextHolder.getRequiredTenantId(), RepairRoleEnum.ADVISOR.getCode());
            List<Long> ids = userDTOS.stream().map(UserDTO::getId).collect(Collectors.toList());
            repairTicketsReqVO.setNowRepairIds(ids);
//            repairTicketsReqVO.setNowRepairId(loginUserId);
            // 因为完成状态需要在交车之后，所以注掉这个
//            repairTicketsReqVO.setIsFinish("1");
        } else if (userRoleCode.equals(RepairRoleEnum.INSPECTION.getCode())) {
            // 总检
            repairTicketsReqVO.setNowRepairId(loginUserId);
            // 因为完成状态需要在交车之后，所以注掉这个
//            repairTicketsReqVO.setIsFinish("0");
        } else {
            return null;
        }
        // 还是用分类查询，不过需要重新构造查询条件
        return baseMapper.getPageType(repairTicketsReqVO, page);
    }

    /**
     * 维修总检完成总检
     * @param respVO 请求对象
     * @author 小李
     * @date 16:48 2024/10/23
     **/
    @Override
    @DSTransactional
    public void inspection(DlRepairTicketsRespVO respVO) {
        // 新增日志
        repairRecordsService.saveRepairRecord(respVO.getId(), null, RecordTypeEnum.ZJ.getCode(), respVO.getRemark(), respVO.getImage());
        // 更新工单
        baseMapper.update(new LambdaUpdateWrapper<DlRepairTickets>()
                        // 因为完成状态需要在交车之后，所以注掉这个
//                .set(DlRepairTickets::getIsFinish, "1")
                        .setSql("now_repair_id = adviser_id")
                        .setSql("now_repair_name = adviser_name")
                        .eq(DlRepairTickets::getId, respVO.getId())
        );

        boolean flag = syncTicketWaresToTicket(respVO.getId());
        if (!flag) {
            throw exception0(500, "系统异常");
        }

        // 通知服务顾问
        DlRepairTickets tickets = baseMapper.selectById(respVO.getId());
        repairWorkerService.sentMessage(SystemEnum.REPAIR.getCode(), Long.valueOf(tickets.getAdviserId()), "您有新的工单可以出厂检验");

        // 通知客户
        // 构建消息
        DictDataRespDTO repairType = dictDataApi.getDictData("repair_type", tickets.getRepairType());
        String message = String.format("您的爱车%s已%s完成", tickets.getCarNo(), (repairType != null ? repairType.getLabel() : "维修保养"));
        repairWorkerService.sentMessageToCus(SystemEnum.REPAIR.getCode(), tickets.getId(), message, true);

        // 更新工单主表状态
        refreshUpdateTime(respVO.getId());
    }

    /**
     * 用于同步一个工单的所有配件申请表的数据到维修工单，主要针对已领取数量小于申请数量的情况
     * @param id 维修工单ID
     * @author 小李
     * @date 9:52 2024/10/26
     **/
    @Override
    public boolean syncTicketWaresToTicket(String id) {
        // 先查满足条件的申请表---配件申请表现在只有待审批和已审批，并且其子表的数据也可能同时有两种情况
//        ArrayList<String> status = new ArrayList<>();
//        status.add("01"); // 待审核
//        status.add("05"); // 已驳回
        // 查工单所有已通过的配件申请单----这里直接查全部
        List<DlTicketWares> list = ticketWaresService.list(new LambdaQueryWrapper<DlTicketWares>().and(item -> {
            item.eq(DlTicketWares::getTicketId, id);
//                    .notIn(DlTicketWares::getStatus, status);
        }));
        if (CollectionUtil.isNotEmpty(list)) {
            // 查配件申请表的所有已领取数量小于申请数量的子表信息----这里需要加上一个条件，得是审核通过的才行
            List<String> ids = list.stream().map(DlTicketWares::getId).collect(Collectors.toList());
            List<DlTwItem> twItems = twItemService.list(new LambdaQueryWrapper<DlTwItem>().and(item -> {
                item.in(DlTwItem::getTwId, ids)
                        .eq(DlTwItem::getWaresStatus, "1")
                        .apply("wares_already_count < wares_count");
            }));
            if (CollectionUtil.isNotEmpty(twItems)) {
                // 更新工单子表（主要是把已领料的字段同步给工单配件的数量字段）
                // 查询工单子表（三个查询条件，属于那个工单，属于配件类型，属于需要改变的（即申请表的已领取数量小于申请数量的））
                Set<String> waresId = twItems.stream().map(DlTwItem::getWaresId).collect(Collectors.toSet());
                List<DlRepairTitem> items = titemService.list(new LambdaQueryWrapper<DlRepairTitem>().and(item -> {
                    item.eq(DlRepairTitem::getTicketId, id)
                            .eq(DlRepairTitem::getItemType, "02")
                            .in(DlRepairTitem::getPartId, waresId);
                }));
                // 同一个配件可能申请过多次，属于是一个工单配件可能是对应多个申请表配件
                List<DlRepairTitem> AllItems = items.stream().map(item -> {
                    DlRepairTitem titem = new DlRepairTitem();
                    titem.setId(item.getId());
                    List<DlTwItem> wares = twItems.stream().filter(i -> i.getWaresId().equals(item.getPartId())).collect(Collectors.toList());
                    int sum = wares.stream().mapToInt(DlTwItem::getWaresAlreadyCount).sum();
                    titem.setItemCount(sum);
                    BigDecimal discount = item.getItemDiscount() == null ? BigDecimal.ONE : item.getItemDiscount();
                    titem.setItemMoney(item.getItemPrice().multiply(BigDecimal.valueOf(sum)).multiply(discount));
                    return titem;
                }).collect(Collectors.toList());

                // 可能出现配件申请单审核通过了没有任何领取或全部退了的情况，所以如果itemCount等于0，直接把这个数据删掉
                // 过滤出需要删除的数据
                List<DlRepairTitem> delItems = AllItems.stream().filter(item -> item.getItemCount() == 0).collect(Collectors.toList());
                if (CollectionUtil.isNotEmpty(delItems)) {
                    // 删除需要删的
                    titemService.removeBatchByIds(delItems);
                    Set<String> delIds = delItems.stream().map(DlRepairTitem::getId).collect(Collectors.toSet());
                    // 得到需要改的
                    List<DlRepairTitem> updateItems = AllItems.stream().filter(item -> delIds.contains(item.getId())).collect(Collectors.toList());
                    if (CollectionUtil.isNotEmpty(updateItems)) {
                        titemService.updateBatchById(updateItems);
                    }
                } else {
                    // 没有需要删的就是全部都需要改
                    titemService.updateBatchById(AllItems);
                }

                // 重新计算维修工单
                return computeTicket(id);
            }
        }
        return true;
    }

    /**
     * 服务顾问上传出厂检验日志
     * @param respVO 请求对象
     * @author 小李
     * @date 17:47 2024/10/23
     **/
    @Override
    public void confirm(DlRepairTicketsRespVO respVO) {
        // 更新工单状态
        baseMapper.update(new LambdaUpdateWrapper<DlRepairTickets>()
                .set(DlRepairTickets::getTicketsWorkStatus, TicketsWorkStatusEnum.END.getCode())
                .set(DlRepairTickets::getTicketsStatus, TicketsStatusEnum.WAITING_NOTICE.getCode())
                .eq(DlRepairTickets::getId, respVO.getId())
        );

        // 记录日志
        repairRecordsService.saveRepairRecord(respVO.getId(), null, RecordTypeEnum.JSGD.getCode(), respVO.getRemark(), respVO.getImage());

        // 更新工单时间
        refreshUpdateTime(respVO.getId());
    }

    /**
     * 从总检的角度差维修中、已完成的工单数量
     * @return java.util.Map<java.lang.String, java.lang.Integer>
     * @author vinjor-M
     * @date 11:30 2024/10/24
     **/
    @Override
    public Map<String, Object> getBossNum(String selectType, String startDate, String endDate) {
        Map<String, Object> rtnMap = new HashMap<>();
        //维修中---当前这一时刻
        long workingNum = 0;
        List<String> workingIdList = new ArrayList<>();
        //在厂数--当前这一时刻
        long inCompanyNum = 0;
        List<String> inCompanyIdList = new ArrayList<>();
        //进场数
        long newOrderNum = 0;
        List<String> newOrderIdList = new ArrayList<>();
        //已完成
        long overNum = 0;
        List<String> overIdList = new ArrayList<>();
        //已交车
        long giveCusNum = 0;
        List<String> giveCusIdList = new ArrayList<>();
        //未结算
        long noPayNum = 0;
        List<String> noPayIdList = new ArrayList<>();
        List<DlRepairTickets> repairTickets = this.list();
        if (!repairTickets.isEmpty()) {
            //维修中
            workingIdList = repairTickets.stream().filter(item -> TicketsStatusEnum.WORKING.getCode().equals(item.getTicketsStatus())).map(DlRepairTickets::getId).collect(Collectors.toList());
            workingNum = workingIdList.size();
            //只要没交车都算在厂
            inCompanyIdList = repairTickets.stream().filter(item -> "0".equals(item.getIsHandover())).filter(item -> !"03".equals(item.getTicketsStatus())).map(DlRepairTickets::getId).collect(Collectors.toList());
            inCompanyNum = inCompanyIdList.size();
            //只要没结算，都是未结算
            List<String> noPayCodeList = Arrays.asList("04", "05", "07", "01");
            noPayIdList = repairTickets.stream().filter(item -> noPayCodeList.contains(item.getTicketsStatus())).map(DlRepairTickets::getId).collect(Collectors.toList());
            noPayNum = noPayIdList.size();
            LocalDateTime currentTime = LocalDateTime.now();
            if ("today".equals(selectType)) {
                String nowDayStr = DateUtil.formatDate(new Date());
                //查当日进厂数、已完成、已交车
                newOrderIdList = repairTickets.stream().filter(item -> item.getCreateTime().toLocalDate().equals(currentTime.toLocalDate())).map(DlRepairTickets::getId).collect(Collectors.toList());
                newOrderNum = newOrderIdList.size();
                //查当日已完成的（总检完成的）
                overIdList = repairTicketsMapper.selectTicketIdByParams(nowDayStr, RecordTypeEnum.ZJ.getCode(), null, null);
                overNum = overIdList.size();
                //查当日已交车的
                giveCusIdList = repairTicketsMapper.selectTicketIdByParams(nowDayStr, RecordTypeEnum.JC.getCode(), null, null);
                giveCusNum = giveCusIdList.size();
            } else if ("all".equals(selectType)) {
                //查累计进厂数、已完成、已交车
                newOrderIdList = repairTickets.stream().map(DlRepairTickets::getId).collect(Collectors.toList());
                newOrderNum = repairTickets.size();
                //查累计已完成的（总检完成的）
                List<String> overCodeList = Arrays.asList("07", "01", "06", "02", "08");
                overIdList = repairTickets.stream().filter(item -> overCodeList.contains(item.getTicketsStatus())).map(DlRepairTickets::getId).collect(Collectors.toList());
                overNum = overIdList.size();
                //查累计已交车的
                giveCusIdList = repairTickets.stream().filter(item -> "1".equals(item.getIsHandover())).map(DlRepairTickets::getId).collect(Collectors.toList());
                giveCusNum = giveCusIdList.size();
            } else {
                //查某个时间范围内进厂数、已完成、已交车
                LocalDateTime startTime = LocalDateTime.parse(startDate + " 00:00:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                LocalDateTime endTime = LocalDateTime.parse(endDate + " 23:59:59", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                newOrderIdList = repairTickets.stream().filter(item -> item.getCreateTime().isAfter(startTime) && item.getCreateTime().isBefore(endTime)).map(DlRepairTickets::getId).collect(Collectors.toList());
                newOrderNum = newOrderIdList.size();
                //查某区间范围内已完成的（总检完成的）
                overIdList = repairTicketsMapper.selectTicketIdByParams(null, RecordTypeEnum.ZJ.getCode(), startDate + " 00:00:00", endDate + " 23:59:59");
                overNum = overIdList.size();
                //查某区间范围内已交车的
                giveCusIdList = repairTicketsMapper.selectTicketIdByParams(null, RecordTypeEnum.JC.getCode(), startDate + " 00:00:00", endDate + " 23:59:59");
                giveCusNum = giveCusIdList.size();
            }
        }
        rtnMap.put("workingNum", workingNum);
        rtnMap.put("workingIdList", workingIdList);
        rtnMap.put("inCompanyNum", inCompanyNum);
        rtnMap.put("inCompanyIdList", inCompanyIdList);
        rtnMap.put("newOrderNum", newOrderNum);
        rtnMap.put("newOrderIdList", newOrderIdList);
        rtnMap.put("overNum", overNum);
        rtnMap.put("overIdList", overIdList);
        rtnMap.put("giveCusNum", giveCusNum);
        rtnMap.put("giveCusIdList", giveCusIdList);
        rtnMap.put("noPayNum", noPayNum);
        rtnMap.put("noPayIdList", noPayIdList);
        return rtnMap;
    }

    /**
     * @Author 许
     * @Description 数据统计
     * @Date 10:51 2025/9/1
     * @Param [startDate, endDate]
     * @return java.util.Map<java.lang.String, java.lang.Object>
     **/
    @Override
    public Map<String, Object> getBossNum(String startDate, String endDate) {
        Map<String, Object> rtnMap = new HashMap<>();
        long workingNum = 0;
        long inCompanyNum = 0;
        long newOrderNum = 0;
        long overNum = 0;
        long giveCusNum = 0;
        long noPayNum = 0;

        List<String> workingIdList = new ArrayList<>();
        List<String> inCompanyIdList = new ArrayList<>();
        List<String> newOrderIdList = new ArrayList<>();
        List<String> overIdList = new ArrayList<>();
        List<String> giveCusIdList = new ArrayList<>();
        List<String> noPayIdList = new ArrayList<>();

        // 查询所有工单
        List<DlRepairTickets> repairTickets = this.list();
        if (!repairTickets.isEmpty()) {
            List<DlRepairTickets> ticketsInRange = repairTickets;

            // 只有当startDate和endDate都不为null时才进行时间过滤
            if (startDate != null && endDate != null) {
                // 时间范围
                LocalDateTime startTime = LocalDateTime.parse(startDate + " 00:00:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                LocalDateTime endTime = LocalDateTime.parse(endDate + " 23:59:59", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

                // 在时间范围内的工单
                ticketsInRange = repairTickets.stream()
                        .filter(item -> {
                            LocalDateTime createTime = item.getCreateTime();
                            return (createTime.isEqual(startTime) || createTime.isAfter(startTime)) &&
                                    (createTime.isEqual(endTime) || createTime.isBefore(endTime));
                        })
                        .collect(Collectors.toList());
            }

            // 维修中
            workingIdList = ticketsInRange.stream()
                    .filter(item -> TicketsStatusEnum.WORKING.getCode().equals(item.getTicketsStatus()))
                    .map(DlRepairTickets::getId).collect(Collectors.toList());
            workingNum = workingIdList.size();

            // 在厂（未交车且不是已完成状态 03）
            inCompanyIdList = ticketsInRange.stream()
                    .filter(item -> "0".equals(item.getIsHandover()))
                    .filter(item -> !"03".equals(item.getTicketsStatus()))
                    .map(DlRepairTickets::getId).collect(Collectors.toList());
            inCompanyNum = inCompanyIdList.size();

            // 未结算
            List<String> noPayCodeList = Arrays.asList("04", "05", "07", "01");
            noPayIdList = ticketsInRange.stream()
                    .filter(item -> noPayCodeList.contains(item.getTicketsStatus()))
                    .map(DlRepairTickets::getId).collect(Collectors.toList());
            noPayNum = noPayIdList.size();

            // 进厂数（所有在范围内创建的）
            newOrderIdList = ticketsInRange.stream().map(DlRepairTickets::getId).collect(Collectors.toList());
            newOrderNum = newOrderIdList.size();

            // 已完成（走 mapper 查记录）
            overIdList = repairTicketsMapper.selectTicketIdByParams(null, RecordTypeEnum.ZJ.getCode(),
                    startDate != null ? startDate + " 00:00:00" : null,
                    endDate != null ? endDate + " 23:59:59" : null);
            overNum = overIdList.size();

            // 已交车（走 mapper 查记录）
            giveCusIdList = repairTicketsMapper.selectTicketIdByParams(null, RecordTypeEnum.JC.getCode(),
                    startDate != null ? startDate + " 00:00:00" : null,
                    endDate != null ? endDate + " 23:59:59" : null);
            giveCusNum = giveCusIdList.size();
        }

        rtnMap.put("workingNum", workingNum);
        rtnMap.put("workingIdList", workingIdList);
        rtnMap.put("inCompanyNum", inCompanyNum);
        rtnMap.put("inCompanyIdList", inCompanyIdList);
        rtnMap.put("newOrderNum", newOrderNum);
        rtnMap.put("newOrderIdList", newOrderIdList);
        rtnMap.put("overNum", overNum);
        rtnMap.put("overIdList", overIdList);
        rtnMap.put("giveCusNum", giveCusNum);
        rtnMap.put("giveCusIdList", giveCusIdList);
        rtnMap.put("noPayNum", noPayNum);
        rtnMap.put("noPayIdList", noPayIdList);
        return rtnMap;
    }


    /**
     * 服务顾问通知客户取车
     * @param noticeCusVO 请求对象
     * @author 小李
     * @date 22:40 2024/10/23
     **/
    @Override
    @DSTransactional
    public void noticeCus(NoticeCusVO noticeCusVO) {
        // 获取工单信息
        DlRepairTickets tickets = baseMapper.selectById(noticeCusVO.getId());
        // 更新工单状态为待结算（待取车）
        baseMapper.update(new LambdaUpdateWrapper<DlRepairTickets>()
                .set(DlRepairTickets::getTicketsStatus, TicketsStatusEnum.NO_PAY.getCode())
                .eq(DlRepairTickets::getId, noticeCusVO.getId())
        );
        // 这里如果是拨打电话通知的，就不走下面的逻辑了，判断条件是看name是不是就是客户的名字
        if (noticeCusVO.getName().equals(tickets.getUserName())) {
            repairRecordsService.saveRepairRecord(noticeCusVO.getId(), null, RecordTypeEnum.TZQC.getCode(), tickets.getAdviserName() + "电话通知客户取车", null);
            return;
        }
        // 获取客户信息
        CustomerMain cus = customerService.getById(tickets.getUserId());
        // 维修项目
        List<DictDataRespDTO> repairType = dictDataApi.getDictDataList(DICT_REPAIR_TYPE);
        // 默认是维修
        String type = "维修";
        DictDataRespDTO dictDataRespDTO = repairType.stream()
                .filter(item -> item.getValue().equals(tickets.getRepairType()))
                .findFirst()
                .orElse(null);
        if (dictDataRespDTO != null) {
            type = dictDataRespDTO.getLabel();
        }
        // 构建消息
        String message = String.format(
                "尊敬的客户您好：您的爱车%s已%s完毕，请您前来取车。若有问题可以联系%s，联系电话：%s"
                , tickets.getCarNo(), type, noticeCusVO.getName(), noticeCusVO.getMobile()
        );
        // todo 小程序通知客户
        if (null != cus && StringUtils.isNotEmpty(cus.getPhoneNumber())) {
            // 短信通知客户
            SendSmsUtil.sendMsgCommon(new String[]{message}, cus.getPhoneNumber(), "1400852709", "蓝安汽车小程序", "2143603");
        }
        // 记录日志
        String recordStr = noticeCusVO.getName()
                + "通知客户取车:" + message;
        repairRecordsService.saveRepairRecord(noticeCusVO.getId(), null, RecordTypeEnum.TZQC.getCode(), recordStr, null);

        // 更新工单主表时间
        refreshUpdateTime(noticeCusVO.getId());
    }

    /**
     * 判断是否工单子表的任何一个类目没有价格或价格为0
     * @param id 工单ID
     * @author 小李
     * @date 15:00 2024/11/16
     **/
    @Override
    public Boolean hasPrice(String id) {
        List<DlRepairTitem> titems = titemService.list(new LambdaQueryWrapper<DlRepairTitem>().in(DlRepairTitem::getTicketId, id));
        for (DlRepairTitem item : titems) {
            // 如果有为空的，直接报错
            if (ObjectUtil.isEmpty(item.getItemPrice())) {
                throw exception0(500, "工单中有项目或配件没有设置价格，请服务顾问填写项目价格，仓库填写配件价格");
            }
            // 如果有为0的，那就提示，让操作人选择
            if (item.getItemPrice().equals(new BigDecimal("0.00"))) {
                return false;
            }
        }
        return true;
    }

    /**
     * 重新计算工单的一些数值，子表也要重新计算
     * @param ticketId 工单ID
     * @author 小李
     * @date 15:47 2024/10/24
     **/
    @Override
    @DSTransactional
    public boolean computeTicket(String ticketId) {
        Long time1 = System.currentTimeMillis();
        /*1.查主表 */
        DlRepairTickets oldTicket = baseMapper.selectById(ticketId);
        // 管理费
        BigDecimal managerMoney = ObjectUtil.isNotEmpty(oldTicket.getManagerMoney()) ? oldTicket.getManagerMoney() : new BigDecimal("0");
        // 救援费
        BigDecimal rescueMoney = ObjectUtil.isNotEmpty(oldTicket.getRescueMoney()) ? oldTicket.getRescueMoney() : new BigDecimal("0");
        // 三包费
        BigDecimal threePackMoney = ObjectUtil.isNotEmpty(oldTicket.getThreePackMoney()) ? oldTicket.getThreePackMoney() : new BigDecimal("0");
        // 定损费
        BigDecimal confirmFaultMoney = ObjectUtil.isNotEmpty(oldTicket.getConfirmFaultMoney()) ? oldTicket.getConfirmFaultMoney() : new BigDecimal("0");
        /*2.查子表 */
        List<DlRepairTitem> list = titemService.list(new LambdaQueryWrapper<DlRepairTitem>().eq(DlRepairTitem::getTicketId, ticketId));
        if (CollectionUtil.isEmpty(list)) {
            throw exception0(500, "没有项目、配件或其他");
        }
        /*3.遍历数据--遍历这一次，处理所有需要处理的*/
        //商品总数
        Integer goodsCount = 0;
        //工单总价
        BigDecimal totalAmount = BigDecimal.ZERO.add(managerMoney).add(rescueMoney).add(threePackMoney).add(confirmFaultMoney);
        //项目总价
        BigDecimal proAmount = BigDecimal.ZERO;
        //配件总价
        BigDecimal waresAmount = BigDecimal.ZERO;
        //其他总价
        BigDecimal otherAmount = BigDecimal.ZERO;
        for (DlRepairTitem item : list) {
            //空字段设置默认值
            if (null == item.getItemDiscount()) {
                item.setItemDiscount(new BigDecimal(1));
            }
            if (null == item.getItemPrice()) {
                item.setItemPrice(BigDecimal.ZERO);
            }
            if (null == item.getItemCount()) {
                item.setItemCount(1);
            }
            //总价
            item.setItemMoney(item.getItemPrice().multiply(BigDecimal.valueOf(item.getItemCount())).multiply(item.getItemDiscount()));
            //商品数量
            goodsCount += item.getItemCount();
            //工单总价
            totalAmount = totalAmount.add(item.getItemMoney());
            if ("01".equals(item.getItemType())) {
                //项目
                proAmount = proAmount.add(item.getItemMoney());
            } else if ("02".equals(item.getItemType())) {
                //配件
                waresAmount = waresAmount.add(item.getItemMoney());
            } else if ("03".equals(item.getItemType())) {
                //其他
                otherAmount = otherAmount.add(item.getItemMoney());
            }
        }
        /*4.设置指定字段值*/
        oldTicket.setCount(goodsCount);
        oldTicket.setTotalPrice(totalAmount);
        oldTicket.setProjectPrice(proAmount);
        oldTicket.setPartPrice(waresAmount);
        oldTicket.setOtherPrice(otherAmount);
        /*5.更新数据*/
        //暂不更新，只要计算不出问题就行
//        titemService.updateBatchById(list);
        baseMapper.updateById(oldTicket);
        // 因为订单是在工单创建的时候就新增了，后面加的配件之类的金额没有同步到订单，这里同步一下
        RepairOrderInfo orderInfo = repairOrderInfoService.getOne(new LambdaQueryWrapper<RepairOrderInfo>().eq(RepairOrderInfo::getGoodsId, ticketId));
        // 更新商品原价和实付金额
        orderInfo.setGoodsPrice(totalAmount);
        orderInfo.setPayMoney(totalAmount);
        repairOrderInfoService.updateById(orderInfo);
        Long time2 = System.currentTimeMillis();
        System.out.println("时间差：" + (time2 - time1));

        // 更新主表时间
        refreshUpdateTime(ticketId);
        return true;
    }

    /**
     * 新增工单子项
     * @param respVO 主要有两个参数，主表ID,itemList(这个需要前端处理成子表的对象能接收的数据)
     * @author 小李
     * @date 19:59 2024/10/24
     **/
    @Override
    @DSTransactional
    public void addItems(DlRepairTicketsRespVO respVO) {
        // 先判断是否施工人员和销售人员是否都设置了
        respVO.getItemList().forEach(item -> {
            if (ObjectUtil.isEmpty(item.getRepairIds())) {
                throw exception0(500, "施工人员尚未设置");
            }
            if (ObjectUtil.isEmpty(item.getSaleId())) {
                throw exception0(500, "销售人员尚未设置");
            }
        });
        // 新增子表
        List<DlRepairTitem> itemList = respVO.getItemList();
        itemList.forEach(item -> item.setTicketId(respVO.getId()));
        titemService.saveBatch(itemList);

        // 更新主表
        boolean flag = computeTicket(respVO.getId());
        if (!flag) {
            throw exception0(500, "系统错误");
        }
    }

    /**
     * 超时自动总检
     * @author 小李
     * @date 16:35 2024/10/26
     **/
    @Override
    @DSTransactional
    public void autoInspection() {
        // 查所有需要总检但还没总检的工单
        List<DlRepairTickets> tickets = baseMapper.selectList(new LambdaQueryWrapper<DlRepairTickets>().eq(DlRepairTickets::getTicketsWorkStatus, "05"));
        // 如果没有，直接退出
        if (CollectionUtil.isEmpty(tickets)) {
            return;
        }
        // 获取超时自动总检配置的时间
        List<DictDataRespDTO> dataList = dictDataApi.getDictDataList(BaseConstants.AUTO_INSPECTION);
        // 理论上是只有一个的，但防止被删了或者别的什么原因，直接报错
        DictDataRespDTO dictDataRespDTO = dataList.stream().findFirst().orElse(null);
        if (ObjectUtil.isEmpty(dictDataRespDTO)) {
            log.error("超时自动总检时间没有配置");
            return;
        }
        // 计算超时的工单
        // 超时时间
        int time = Integer.parseInt(dictDataRespDTO.getValue());

        List<DlRepairTickets> list = tickets.stream().filter(item -> {
            // 当前时间---放到里面是因为如果数据量多，动态获取当前时间更能满足需求
            LocalDateTime now = LocalDateTime.now();
            return ChronoUnit.MINUTES.between(item.getUpdateTime(), now) > time;
        }).collect(Collectors.toList());

        // 同步工单的配件申请表到工单,如果都没超时，退出
        if (CollectionUtil.isEmpty(list)) {
            return;
        }
        // 先取总检们
        // 取这个角色的角色信息
        RoleReqDTO roleInfo = roleApi.getRoleInfo(RepairRoleEnum.INSPECTION.getCode());
        // 通过角色信息查有这个角色的人
        List<Long> ids = permissionApi.getUserIdByRoleId(roleInfo.getId());
        // 如果没有总检，直接报错退出
        if (CollectionUtil.isEmpty(ids)) {
            log.error("系统中没有总检");
            return;
        }
        // 在过滤一次，主要是过滤掉没法同步申请表到工单的数据
        List<DlRepairTickets> result = list.stream().filter(item -> {
            boolean flag = syncTicketWaresToTicket(item.getId());
            // 如果出现更新不到的情况
            if (!flag) {
                // 通知总检，让其手动操作
                ids.forEach(id -> {
                    repairWorkerService.sentMessage(SystemEnum.REPAIR.getCode(), id, "待总检车牌为：" + item.getCarNo() + "工单已经超时，但无法自动总检，请手动处理");
                });
            }
            return flag;
        }).collect(Collectors.toList());
        // 如果没有记录同步成功，报错并退出
        if (CollectionUtil.isEmpty(result)) {
            log.error("自动总检失败，原因可能是无法同步申请表到工单");
        }
        // 移交服务顾问
        List<DlRepairTickets> updateTickets = result.stream().map(item -> {
            DlRepairTickets ticket = new DlRepairTickets();
            ticket.setId(item.getId());
            ticket.setNowRepairId(Long.valueOf(item.getAdviserId()));
            ticket.setNowRepairName(item.getAdviserName());
            return ticket;
        }).collect(Collectors.toList());
        baseMapper.updateById(updateTickets);
        // 通知总检和服务顾问
        result.forEach(item -> {
            repairWorkerService.sentMessage(SystemEnum.REPAIR.getCode(), Long.valueOf(item.getAdviserId()), "您有新的工单可以出厂检验");
            ids.forEach(id -> {
                repairWorkerService.sentMessage(SystemEnum.REPAIR.getCode(), id, "车牌为：" + item.getCarNo() + "工单已由系统自动总检并移交服务顾问");
            });
            // 记录日志
            repairRecordsService.saveRepairRecord(item.getId(), null, RecordTypeEnum.ZJ.getCode(), "该工单由系统自动总检完成", null);
        });
    }

    /**
     * 修改工单主表，只是主表
     * @param respVO 请求对象
     * @author 小李
     * @date 10:25 2024/10/30
     **/
    @Override
    @DSTransactional
    public void updateTicket(DlRepairTicketsRespVO respVO) {
        // 更新主表
        baseMapper.updateById(respVO);

        // 重新计算
        boolean flag = computeTicket(respVO.getId());
        if (!flag) {
            throw exception0(500, "系统异常");
        }
    }

    /**
     * 删除工单
     * @param id 工单ID
     * @author 小李
     * @date 20:05 2024/11/1
     **/
    @Override
    @DSTransactional
    public void removeTicketById(String id) {
        // 删除订单
        repairOrderInfoService.remove(new LambdaQueryWrapper<RepairOrderInfo>().eq(RepairOrderInfo::getGoodsId, id));
        // 删除工单
        baseMapper.deleteById(id);
        // 删除工单子表
        titemService.remove(new LambdaQueryWrapper<DlRepairTitem>().eq(DlRepairTitem::getTicketId, id));
        // 记录操作日志
        repairRecordsService.saveRepairRecord(id, null, RecordTypeEnum.SCGG.getCode(), null, null);
    }

    /**
     * 服务顾问交车
     * @author vinjor-M
     * @date 16:51 2024/11/13
     **/
    @Override
    public void overOrder(DlRepairTicketsRespVO respVO) {
        DlRepairTickets repairTickets = this.getById(respVO.getId());
        if (TicketsStatusEnum.NO_PAY.getCode().equals(repairTickets.getTicketsStatus())) {
            //没结账呢还
            repairTickets.setIsHandover("1");
        } else {
            //结完账了
            repairTickets.setIsHandover("1");
            repairTickets.setTicketsStatus(TicketsStatusEnum.OVER.getCode());
            repairTickets.setTicketsWorkStatus(TicketsWorkStatusEnum.END.getCode());
            repairTickets.setIsFinish("1");
        }
        // 更新工单状态
        this.updateById(repairTickets);
        // 记录日志
        repairRecordsService.saveRepairRecord(respVO.getId(), null, RecordTypeEnum.JC.getCode(), respVO.getRemark(), respVO.getImage());
        refreshUpdateTime(respVO.getId());
    }

    /**
     * 根据工单ID查客户和车辆信息
     * @param id id
     * @author 小李
     * @date 19:07 2024/11/18
     **/
    @Override
    public CustomerAndCarVO getCusAndCarById(String id) {
        // 查工单
        DlRepairTickets tickets = baseMapper.selectById(id);
        // 查用户
        CustomerMain customerMain = customerService.getById(tickets.getUserId());
        // 查车辆
        CarMain carMain = carMainService.getById(tickets.getCarId());
        // 映射
        CustomerAndCarVO customerAndCarVO = new CustomerAndCarVO();
        customerAndCarVO.setUserInfo(customerMain);
        customerAndCarVO.setCarInfo(BeanUtil.toBean(carMain, CarMainReqVO.class));
        return customerAndCarVO;
    }

    /**
     * 用于刷新工单的更新时间
     * @param id 工单ID
     * @author 小李
     * @date 10:31 2024/11/27
     **/
    @Override
    public void refreshUpdateTime(String id) {
        if (ObjectUtil.isNotEmpty(id)) {
            baseMapper.update(new LambdaUpdateWrapper<DlRepairTickets>()
                    .set(DlRepairTickets::getUpdateTime, LocalDateTime.now())
                    .eq(DlRepairTickets::getId, id)
            );
        }
    }

    /**
     * @return void
     * @Author 许
     * @Description //TODO 接车
     * @Date 14:07 2025/8/13
     * @Param [id, image, remark]
     */
    @Override
    @DSTransactional
    public void pickCar(String id, String image, String remark) {
        /* 1.修改工单表接车状态为已接车*/
        baseMapper.update(Wrappers.<DlRepairTickets>lambdaUpdate()
                .eq(DlRepairTickets::getId, id)
                .set(DlRepairTickets::getIsPickCar, 1));

        /* 2.步骤表里添加接车*/
        repairRecordsService.saveRepairRecord(id, null, RecordTypeEnum.PICKCAR.getCode(), "接车", image);
    }

    /**
     * @return java.util.Map<java.lang.String, java.lang.Object>
     * @Author 许
     * @Description 工单统计
     * @Date 15:54 2025/8/19
     * @Param [repairTicketsReqVO]
     */
    @Override
    public Map<String, Object> getStatistics(DlRepairTicketsReqVO repairTicketsReqVO) {
        return baseMapper.getStatistics(repairTicketsReqVO);
    }
}




