package com.xiaopeng.maintenance.service.impl; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.xiaopeng.api.dto.pushMessage.AliPushMessageDTO; import com.xiaopeng.api.feignClient.account.XpOperationUserClient; import com.xiaopeng.api.feignClient.account.XpOperationUserNotificationClient; import com.xiaopeng.api.feignClient.maintenance.XpCheckLocationClient; import com.xiaopeng.api.feignClient.pushMessage.XpAliPushMessageClient; import com.xiaopeng.api.feignClient.pushMessage.XpPushMessageClient; import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetClient; import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetOperationsClient; import com.xiaopeng.api.model.account.XpOperationsUser; import com.xiaopeng.api.model.account.XpOperationsUserNotification; import com.xiaopeng.api.model.maintenance.*; import com.xiaopeng.api.model.vehicleConvoy.XpFleet; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.enums.NotificationTypeEnum; import com.xiaopeng.common.core.utils.poi.ExcelUtil; import com.xiaopeng.common.core.web.page.PageInfo; import com.xiaopeng.maintenance.dto.ExportSchedulingDetailDto; import com.xiaopeng.maintenance.dto.SchedulingDetailDto; import com.xiaopeng.maintenance.dto.ShiftAttendanceDTO; import com.xiaopeng.maintenance.mapper.XpCheckLocationMapper; import com.xiaopeng.maintenance.mapper.XpClockInMapper; import com.xiaopeng.maintenance.mapper.XpSchedulingDetailMapper; import com.xiaopeng.maintenance.mapper.XpShiftMapper; import com.xiaopeng.maintenance.service.IXpSchedulingDetailService; import com.xiaopeng.maintenance.service.IXpSchedulingService; import com.xiaopeng.maintenance.service.IXpShiftService; import com.xiaopeng.maintenance.vo.*; import org.apache.poi.hssf.usermodel.*; import org.apache.poi.hssf.util.HSSFColor; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddress; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.annotation.Resource; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import java.net.URLEncoder; import java.time.DayOfWeek; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.format.TextStyle; import java.time.temporal.TemporalAdjusters; import java.util.*; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** *

* 排班明细 服务实现类 *

* * @author mitao * @since 2026-02-03 */ @Service public class XpSchedulingDetailServiceImpl extends ServiceImpl implements IXpSchedulingDetailService { @Resource private XpFleetClient xpFleetClient; @Resource private XpCheckLocationMapper xpCheckLocationMapper; @Resource private XpShiftMapper xpShiftMapper; @Resource private XpClockInMapper xpClockInMapper; @Resource private IXpSchedulingService xpSchedulingService; @Resource private IXpShiftService xpShiftService; @Resource private XpFleetOperationsClient xpFleetOperationsClient; @Resource private XpOperationUserClient xpOperationUserClient; @Resource private XpAliPushMessageClient xpAliPushMessageClient; @Resource private XpOperationUserNotificationClient xpOperationUserNotificationClient; @Override public ShiftAttendanceVO shiftAttendance(ShiftAttendanceDTO dto) { // 查询用户所属车队 XpFleet xpFleet = xpFleetClient.getFleetByOperationsUserId(dto.getOperationsUserId()).getData(); if (Objects.isNull(xpFleet)) { return null; } // 查询打卡地 XpCheckLocation xpCheckLocation = xpCheckLocationMapper.selectById(xpFleet.getLocationId()); if (Objects.isNull(xpCheckLocation)) { return null; } // 查询排班明细 XpSchedulingDetail schedulingDetail = this.getOne(Wrappers.lambdaQuery(XpSchedulingDetail.class) .likeRight(XpSchedulingDetail::getPlanTime, dto.getSchedulingDate()) .eq(XpSchedulingDetail::getFleetId, xpFleet.getId()) .last("LIMIT 1")); if (Objects.isNull(schedulingDetail)) { return null; } // 查询班次 XpShift xpShift = xpShiftMapper.selectById(schedulingDetail.getShiftId()); if (Objects.isNull(xpShift)) { return null; } ShiftAttendanceVO shiftAttendanceVO = new ShiftAttendanceVO(); shiftAttendanceVO.setXpShift(xpShift); shiftAttendanceVO.setXpCheckLocation(xpCheckLocation); // 查询当前用户打卡信息 XpClockIn xpClockIn = xpClockInMapper.selectOne(Wrappers.lambdaQuery(XpClockIn.class) .eq(XpClockIn::getAppUserId, dto.getOperationsUserId()) .likeRight(XpClockIn::getClockInTime, dto.getSchedulingDate()) .last("LIMIT 1")); if (Objects.isNull(xpClockIn)) { shiftAttendanceVO.setClockIn(false); shiftAttendanceVO.setClockOut(false); } else { shiftAttendanceVO.setXpClockIn(xpClockIn); shiftAttendanceVO.setClockIn(true); shiftAttendanceVO.setClockOut(xpClockIn.getClockOutTime() != null); } return shiftAttendanceVO; } /** * 排班明细分页列表 * * @param query * @return */ @Override public PageInfo pageList(SchedulingDetailVo query) { PageInfo pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize()); List schedulingDetailDtos = this.baseMapper.pageList(pageInfo, query.getMonth()); LocalDate parse1 = LocalDate.parse(query.getMonth() + "-01", DateTimeFormatter.ofPattern("yyyy-MM-dd")); //找出月份的最大天 int dayOfMonth = parse1.with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth(); List xpShifts = xpShiftService.list(new QueryWrapper().eq("is_deleted", 0).orderByAsc("id")); for (SchedulingDetailDto schedulingDetailDto : schedulingDetailDtos) { String days = schedulingDetailDto.getDays(); if (StringUtils.isNotBlank(days)) { String[] split = days.split(","); List list = Arrays.asList(split); for (int i = 1; i <= dayOfMonth; i++) { Map map1 = new HashMap<>(); for (String s : split) { String[] split1 = s.split("_"); if (i == Integer.parseInt(split1[0])) { map1.put("shiftId", split1[1]); map1.put("shiftName", split1[2]); map1.put("shiftColor", split1[3]); break; } } setEnglishDay(i, schedulingDetailDto, map1); } List> maps = new ArrayList<>(); for (XpShift xpShift : xpShifts) { String shiftName = xpShift.getShiftName(); long allDay = list.stream().filter(s -> s.contains(shiftName)).count(); Map map = new HashMap<>(); map.put("shiftId", xpShift.getId().toString()); map.put("shiftName", shiftName); map.put("shiftColor", xpShift.getShiftColor()); map.put("count", allDay + ""); maps.add(map); } schedulingDetailDto.setSubtotal(maps); } } pageInfo.setRecords(schedulingDetailDtos); return pageInfo; } /** * 给每天的字段赋值 * * @param day * @param dto * @param map */ private void setEnglishDay(int day, SchedulingDetailDto dto, Map map) { switch (day) { case 1: dto.setOne(map); break; case 2: dto.setTwo(map); break; case 3: dto.setThree(map); break; case 4: dto.setFour(map); break; case 5: dto.setFive(map); break; case 6: dto.setSix(map); break; case 7: dto.setSeven(map); break; case 8: dto.setEight(map); break; case 9: dto.setNine(map); break; case 10: dto.setTen(map); break; case 11: dto.setEleven(map); break; case 12: dto.setTwelve(map); break; case 13: dto.setThirteen(map); break; case 14: dto.setFourteen(map); break; case 15: dto.setFifteen(map); break; case 16: dto.setSixteen(map); break; case 17: dto.setSeventeen(map); break; case 18: dto.setEighteen(map); break; case 19: dto.setNineteen(map); break; case 20: dto.setTwenty(map); break; case 21: dto.setTwentyOne(map); break; case 22: dto.setTwentyTwo(map); break; case 23: dto.setTwentyThree(map); break; case 24: dto.setTwentyFour(map); break; case 25: dto.setTwentyFive(map); break; case 26: dto.setTwentySix(map); break; case 27: dto.setTwentySeven(map); break; case 28: dto.setTwentyEight(map); break; case 29: dto.setTwentyNine(map); break; case 30: dto.setThirty(map); break; case 31: dto.setThirtyOne(map); break; } } /** * 给每天的字段赋值 * * @param day * @param dto * @param shiftName */ private void setEnglishDay(int day, ExportSchedulingDetailDto dto, String shiftName) { switch (day) { case 1: dto.setOne(shiftName); break; case 2: dto.setTwo(shiftName); break; case 3: dto.setThree(shiftName); break; case 4: dto.setFour(shiftName); break; case 5: dto.setFive(shiftName); break; case 6: dto.setSix(shiftName); break; case 7: dto.setSeven(shiftName); break; case 8: dto.setEight(shiftName); break; case 9: dto.setNine(shiftName); break; case 10: dto.setTen(shiftName); break; case 11: dto.setEleven(shiftName); break; case 12: dto.setTwelve(shiftName); break; case 13: dto.setThirteen(shiftName); break; case 14: dto.setFourteen(shiftName); break; case 15: dto.setFifteen(shiftName); break; case 16: dto.setSixteen(shiftName); break; case 17: dto.setSeventeen(shiftName); break; case 18: dto.setEighteen(shiftName); break; case 19: dto.setNineteen(shiftName); break; case 20: dto.setTwenty(shiftName); break; case 21: dto.setTwentyOne(shiftName); break; case 22: dto.setTwentyTwo(shiftName); break; case 23: dto.setTwentyThree(shiftName); break; case 24: dto.setTwentyFour(shiftName); break; case 25: dto.setTwentyFive(shiftName); break; case 26: dto.setTwentySix(shiftName); break; case 27: dto.setTwentySeven(shiftName); break; case 28: dto.setTwentyEight(shiftName); break; case 29: dto.setTwentyNine(shiftName); break; case 30: dto.setThirty(shiftName); break; case 31: dto.setThirtyOne(shiftName); break; } } /** * 保存排班明细 * * @param vo * @return */ @Override public R saveSchedulingDetail(SaveSchedulingVo vo, Long userId) { String month = vo.getMonth(); List list = this.list(new QueryWrapper().eq("is_deleted", 0).last(" and DATE_FORMAT(plan_time,'%Y-%m') = '" + month + "'")); //保存排班数据 XpScheduling xpScheduling = xpSchedulingService.getOne(new QueryWrapper().eq("is_deleted", 0).last(" and DATE_FORMAT(scheduling_date,'%Y-%m') = '" + month + "'")); if (null == xpScheduling) { xpScheduling = new XpScheduling(); xpScheduling.setDeleted(false); xpScheduling.setCreateTime(LocalDateTime.now()); xpScheduling.setUpdateTime(LocalDateTime.now()); xpScheduling.setCreateBy(userId.toString()); xpScheduling.setUpdateBy(userId.toString()); } xpScheduling.setSchedulingDate(LocalDate.parse(month + "-01", DateTimeFormatter.ofPattern("yyyy-MM-dd"))); xpScheduling.setCommitUserId(userId); xpSchedulingService.saveOrUpdate(xpScheduling); List xpShifts = xpShiftService.list(new QueryWrapper().eq("is_deleted", 0)); //构建排班明细数据 List datas = vo.getDatas(); List schedulingDetails = new ArrayList<>(); //存放推送消息 List pushMessage = new ArrayList<>(); //存放通知数据 List notifications = new ArrayList<>(); for (SaveSchedulingDetailVo data : datas) { Long fleetId = data.getFleetId(); List days = data.getDays(); for (SaveSchedulingDetailDay day : days) { Long shiftId = day.getShiftId(); if(null == shiftId){ continue; } LocalDate parse = LocalDate.parse(day.getDayTime(), DateTimeFormatter.ofPattern("yyyy-MM-dd")); //查询判断数据是否存在 XpSchedulingDetail schedulingDetail = list.stream().filter(s -> s.getFleetId().equals(fleetId) && s.getPlanTime().equals(parse) ).findAny().orElse(null); //构建存储对象 if (null == schedulingDetail) { schedulingDetail = new XpSchedulingDetail(); schedulingDetail.setCreateBy(userId.toString()); schedulingDetail.setCreateTime(LocalDateTime.now()); schedulingDetail.setUpdateBy(userId.toString()); schedulingDetail.setUpdateTime(LocalDateTime.now()); schedulingDetail.setDeleted(false); } else { //构建通知消息 Long old_shiftId = schedulingDetail.getShiftId(); if (!old_shiftId.equals(shiftId)) { //获取车队的所有人员 XpShift xpShift1 = xpShifts.stream().filter(s -> s.getId().equals(old_shiftId)).findAny().orElse(null); XpShift xpShift2 = xpShifts.stream().filter(s -> s.getId().equals(shiftId)).findAny().orElse(null); List operationsIds = xpFleetOperationsClient.getOperationsIdsByFleetId(fleetId).getData(); if (null != operationsIds && !operationsIds.isEmpty()) { List data1 = xpOperationUserClient.listByIds(operationsIds).getData(); for (XpOperationsUser xpOperationsUser : data1) { String nickName = xpOperationsUser.getNickName(); String message = nickName + "请注意," + month + "你的排班由" + xpShift1.getShiftName() + "变更为了" + xpShift2.getShiftName() + "。详情请联系平台管理员。"; //构建消息推送对象 AliPushMessageDTO aliPushMessageDTO = buildPushMessage(message, xpOperationsUser); pushMessage.add(aliPushMessageDTO); //构建消息存储 XpOperationsUserNotification operationsUserNotification = buildXpOperationsUserNotification(message, aliPushMessageDTO.getBody(), xpOperationsUser); notifications.add(operationsUserNotification); } } } } schedulingDetail.setSchedulingId(xpScheduling.getId()); schedulingDetail.setFleetId(fleetId); schedulingDetail.setShiftId(shiftId); XpShift xpShift = xpShifts.stream().filter(s -> s.getId().equals(shiftId)).findAny().orElse(null); if (null != xpShift) { schedulingDetail.setIsAllDay(xpShift.getShiftName().contains("全天")); } schedulingDetail.setPlanTime(parse); schedulingDetails.add(schedulingDetail); } } //批量保存排班明细数据 if (!schedulingDetails.isEmpty()) { this.saveOrUpdateBatch(schedulingDetails); } //异步处理消息通知数据 pushMessage(pushMessage, notifications); return R.ok(); } /** * 构建推送消息对象 * * @param message * @param xpOperationsUser * @return */ private AliPushMessageDTO buildPushMessage(String message, XpOperationsUser xpOperationsUser) { JSONObject body = new JSONObject(); body.put("content", message); body.put("strategy", 4); body.put("type", 0); body.put("id", 0); AliPushMessageDTO aliPushMessageDTO = new AliPushMessageDTO(); aliPushMessageDTO.setDeviceType(xpOperationsUser.getDeviceType()); aliPushMessageDTO.setAccount(xpOperationsUser.getPhoneNumber()); aliPushMessageDTO.setTitle("排班变更提示"); aliPushMessageDTO.setBody(body.toJSONString()); aliPushMessageDTO.setPushType("NOTICE"); return aliPushMessageDTO; } /** * 构建消息存储 * * @param body * @param xpOperationsUser * @return */ private XpOperationsUserNotification buildXpOperationsUserNotification(String message, String body, XpOperationsUser xpOperationsUser) { XpOperationsUserNotification operationsUserNotification = new XpOperationsUserNotification(); operationsUserNotification.setNotificationType(NotificationTypeEnum.SCHEDULE_CHANGED.getCode()); operationsUserNotification.setNotificationContent(message); operationsUserNotification.setOperationsUserId(xpOperationsUser.getId()); operationsUserNotification.setReadStatus(0); operationsUserNotification.setNotificationExt(body); return operationsUserNotification; } /** * 异步处理消息推送 * * @param pushMessage * @param notifications */ private void pushMessage(List pushMessage, List notifications) { if (pushMessage.isEmpty()) { return; } ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); executor.execute(() -> { xpOperationUserNotificationClient.addNotificationBatch(notifications); for (AliPushMessageDTO aliPushMessageDTO : pushMessage) { xpAliPushMessageClient.pushMessage(aliPushMessageDTO); } }); } /** * 导出排班数据 * * @param month * @param response */ @Override public void exportExcel(String month, HttpServletResponse response) { PageInfo pageInfo = new PageInfo<>(0, 10000); List schedulingDetailDtos = this.baseMapper.pageList(pageInfo, month); //获取月份的最大天数 int m = Integer.parseInt(month.split("-")[1]); LocalDate parse1 = LocalDate.parse(month + "-01", DateTimeFormatter.ofPattern("yyyy-MM-dd")); int dayOfMonth = parse1.with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth(); HSSFWorkbook hssfWorkbook = new HSSFWorkbook(); HSSFCellStyle cellStyle = hssfWorkbook.createCellStyle(); cellStyle.setAlignment(HorizontalAlignment.CENTER); cellStyle.setVerticalAlignment(VerticalAlignment.CENTER); HSSFSheet hssfSheet = hssfWorkbook.createSheet(); hssfSheet.setColumnWidth(0, 6 * 256); hssfSheet.setDefaultRowHeightInPoints(20f); HSSFCellStyle cellStyle1 = hssfWorkbook.createCellStyle(); cellStyle1.setFillForegroundColor(IndexedColors.GREY_40_PERCENT.index); cellStyle1.setAlignment(HorizontalAlignment.CENTER); cellStyle1.setVerticalAlignment(VerticalAlignment.CENTER); //添加第一行标题 HSSFRow hssfRow = hssfSheet.createRow(0); List xpShifts = xpShiftService.list(new QueryWrapper().eq("is_deleted", 0)); //设置单元格数量 int totalCell = dayOfMonth + 1 + xpShifts.size(); //设置表头样式,合并单元格 CellRangeAddress region = new CellRangeAddress(0, 1, 0, 0); hssfSheet.addMergedRegion(region); region = new CellRangeAddress(0, 1, dayOfMonth + 1, totalCell - 1); hssfSheet.addMergedRegion(region); //设置单元格数据 for (int i = 0; i < totalCell; i++) { HSSFCell cell = hssfRow.createCell(i); cell.setCellStyle(cellStyle1); if(0 == i){ cell.setCellValue("车队"); }else if(i <= dayOfMonth){ cell.setCellValue(m + "/" + i); }else{ cell.setCellValue("排班小计"); } } //添加第二行标题 HSSFRow hssfRow1 = hssfSheet.createRow(1); //设置单元格数量 //设置单元格数据 for (int i = 0; i < totalCell; i++) { HSSFCell cell = hssfRow1.createCell(i); cell.setCellStyle(cellStyle1); if(i > 0 && i <= dayOfMonth){ LocalDate parse = LocalDate.parse(month + "-" + String.format("%02d", i), DateTimeFormatter.ofPattern("yyyy-MM-dd")); String week = ""; switch (parse.getDayOfWeek()) { case MONDAY: week = "一"; break; case TUESDAY: week = "二"; break; case WEDNESDAY: week = "三"; break; case THURSDAY: week = "四"; break; case FRIDAY: week = "五"; break; case SATURDAY: week = "六"; break; case SUNDAY: week = "日"; break; } cell.setCellValue(week); } } //构建数据部分的内容 int rowNum = 2; for (SchedulingDetailDto schedulingDetailDto : schedulingDetailDtos) { HSSFRow row = hssfSheet.createRow(rowNum); HSSFCell cell = row.createCell(0); cell.setCellStyle(cellStyle); cell.setCellValue(schedulingDetailDto.getFleetName()); String days = schedulingDetailDto.getDays(); if (StringUtils.isNotBlank(days)) { String[] split = days.split(","); List list = Arrays.asList(split); for (int i = 1; i <= dayOfMonth; i++) { String shiftName = "休"; for (String s : split) { String[] split1 = s.split("_"); if (i == Integer.parseInt(split1[0])) { shiftName = split1[2]; break; } } HSSFCell cell1 = row.createCell(i); cell1.setCellValue(shiftName); } for (int i = 0; i < xpShifts.size(); i++) { String shiftName = xpShifts.get(i).getShiftName(); long allDay = list.stream().filter(s -> s.contains(shiftName)).count(); HSSFCell cell1 = row.createCell(dayOfMonth + 1 + i); cell1.setCellStyle(cellStyle); cell1.setCellValue(shiftName + " " + allDay); } } rowNum++; } try { response.setContentType("application/vnd.ms-excel"); response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("排班计划管理.xls", "UTF-8")); ServletOutputStream outputStream = response.getOutputStream(); hssfWorkbook.write(outputStream); outputStream.flush(); outputStream.close(); }catch (Exception e){ e.printStackTrace(); } } }