
package org.sky.scientific.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.sky.core.log.exception.ServiceException;
import org.sky.core.mp.base.BaseServiceImpl;
import org.sky.core.mp.support.Condition;
import org.sky.core.mp.support.Query;
import org.sky.core.secure.utils.SecureUtil;
import org.sky.core.tool.utils.*;
import org.sky.scientific.excel.XmTechnicianHoursExcel;
import org.sky.scientific.excel.XmTechnicianHoursListener;
import org.sky.scientific.excel.handle.CustomCellStyleHandler;
import org.sky.scientific.excel.handle.TechnicianWorkMergeStrategy;
import org.sky.scientific.excel.handle.TechnicianWorkStatisticsMergeStrategy;
import org.sky.scientific.mapper.*;
import org.sky.scientific.pojo.dto.TechnicianDTO;
import org.sky.scientific.pojo.entity.XmTechnicianEntity;
import org.sky.scientific.pojo.entity.XmTechnicianHours;
import org.sky.scientific.pojo.vo.DailyAttendance;
import org.sky.scientific.pojo.vo.DailyHours;
import org.sky.scientific.pojo.vo.TechnicianHoursMonthlyVO;
import org.sky.scientific.pojo.vo.XmTechnicianHoursVO;
import org.sky.scientific.service.IXmService;
import org.sky.scientific.service.IXmTechnicianHoursService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedInputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 研发人员研发工时记录表 服务实现类
 *
 * @author Kd
 * @since 2025-06-29
 */
@Slf4j
@Service
@AllArgsConstructor
public class XmTechnicianHoursServiceImpl extends BaseServiceImpl<XmTechnicianHoursMapper, XmTechnicianHours> implements IXmTechnicianHoursService {

	private final XmMapper xmMapper;
	private final AttendanceMapper attendanceMapper;
	private final TechnicianMapper technicianMapper;
	private final XmTechnicianMapper xmTechnicianMapper;
	private final IXmService xmService;

	@SneakyThrows
	@Transactional(rollbackFor = Exception.class)
	@Override
	public synchronized void importXmTechnicianHours(MultipartFile file, String yearAndMonth) {
		XmTechnicianHoursListener listener = new XmTechnicianHoursListener(yearAndMonth);
 		EasyExcel.read(new BufferedInputStream(file.getInputStream()))
			.registerReadListener(listener)
			.sheet(0)
			.headRowNumber(3) // 重要：设置表头行数(0-based)
			.doRead();
		List<XmTechnicianHoursExcel> records = listener.getRecords();
		if (CollectionUtil.isEmpty(records)) {
			throw new ServiceException("未能从模板中读取数据，请检查模板格式是否正确");
		}

		//根据yearAndMonth获取考勤明细
		List<DailyAttendance> dailyAttendanceList = attendanceMapper.selectDailyAttendanceByUnicodeAndMonth(null, yearAndMonth);
		//每个人每天的考勤总时长
		Map<String, Double> attHoursByEveryDay = dailyAttendanceList.stream()
			.filter(item -> item.getWorkHours() != null && item.getWorkHours() > 0)
			.collect(Collectors.groupingBy(item -> item.getUnicode() + ":" + item.getWorkDate(), Collectors.summingDouble(DailyAttendance::getWorkHours)));

		//需要新增的，以便后续批量新增
		List<XmTechnicianHours> insertList = new ArrayList<>();
		//需要更新的，以便后续批量修改
		List<XmTechnicianHours> updateList = new ArrayList<>();

		int batchCount = 100;
		//月度工时列表
		List<XmTechnicianHours> dbList = baseMapper.selectByYearAndMonth(yearAndMonth, null, null);

		Map<String, Double> hoursMap = dbList.stream()
			.collect(Collectors.toMap(
				item -> item.getUnicode() + ":" + item.getXmId() + ":" + item.getWorkDate(),
				XmTechnicianHours::getWorkHours,
				(existing, replacement) -> existing
			));
		//将研发项目信息缓存起来,key=xmbh,value=xmId
		Map<String, Long> xmMap = new HashMap<>();
		//将研发人员信息缓存起来
		Map<String, String> technicianMap = new HashMap<>();

		for (XmTechnicianHoursExcel record : records) {

			//保存
			if (StringUtil.isBlank(record.getXmbh())) {
				throw new ServiceException("研发项目编号为空，请检查数据");
			}
			String xmKey = record.getXmbh();
			Long xmId = null;
			if (xmMap.containsKey(xmKey)) {
				xmId = xmMap.get(xmKey);
			} else {
				xmId = xmMapper.getXmIdByXmmcOrXmbh(null, null, record.getXmbh());
				if (xmId == null) {
					throw new ServiceException("序号为[" + record.getSerialNumber() + "]的研发项目[" + record.getXmbh() + "]不存在");
				}
				xmMap.put(xmKey, xmId);
			}

			String unicode = null;
			String techniciankey = record.getNumber() + ":" + record.getIdCard();
			if (technicianMap.containsKey(techniciankey)) {
				unicode = technicianMap.get(techniciankey);
			} else {
				unicode = technicianMapper.getUnicodeByNumberAndIdentityCard(record.getNumber(), record.getIdCard());
				if (unicode == null) {
					throw new ServiceException(TechnicianServiceImpl.getTechnicianNotExistTips(record.getNumber(), record.getIdCard()));
				}
				technicianMap.put(techniciankey, unicode);
			}

			String finalUnicode = unicode;
			Long finalXmId = xmId;

			for (DailyAttendance dailyAttendance : record.getDailyAttendances()) {
				Double workHours = Func.toDouble(dailyAttendance.getWorkHours(), 0.0);
				Double attHoursByUnicode = attHoursByEveryDay.getOrDefault(unicode + ":" + dailyAttendance.getWorkDate(), 0.0);
				if (attHoursByUnicode == 0 && workHours > 0) {
					throw new ServiceException("[" + record.getName() + "," + dailyAttendance.getWorkDate() + "]无考勤数据");
				}

				String key = unicode + ":" + finalXmId + ":" + dailyAttendance.getWorkDate();
				if (hoursMap.containsKey(key)) {
					//已有数据,
					//新工时=旧工时,则不处理
					if (Func.equals(hoursMap.get(key), workHours)) {
						continue;
					}
					//已有数据,新旧工时不相等则更新
					Optional<XmTechnicianHours> optional = dbList.stream().filter(item -> Objects.equals(item.getWorkDate(), dailyAttendance.getWorkDate()) && Objects.equals(item.getXmId(), finalXmId) && Objects.equals(item.getUnicode(), finalUnicode)).findFirst();
					if (optional.isPresent()) {
						XmTechnicianHours tmp = optional.get();
						tmp.setWorkHours(workHours);
						updateList.add(tmp);
					}
				} else {
					XmTechnicianHours entity = new XmTechnicianHours();
					entity.setXmId(xmId);
					entity.setUnicode(unicode);
					entity.setWorkDate(dailyAttendance.getWorkDate());
					entity.setWorkHours(workHours);
					entity.setTenantId(SecureUtil.getTenantId());
					entity.setCreateUser(SecureUtil.getUserId());
					entity.setCreateDept(Func.toLong(SecureUtil.getDeptId()));
					insertList.add(entity);
				}

				String attendanceByDay = unicode + ":" + dailyAttendance.getWorkDate();
				Map<String, Double> allHoursMap = Stream.concat(insertList.stream(), dbList.stream())
					.collect(Collectors.groupingBy(item -> item.getUnicode() + ":" + item.getWorkDate(), Collectors.summingDouble(XmTechnicianHours::getWorkHours)));
				Double attendanceHours = attHoursByEveryDay.getOrDefault(attendanceByDay, 0.0);
				Double hoursFilled = allHoursMap.getOrDefault(unicode + ":" + dailyAttendance.getWorkDate(), 0.0);
				if (attendanceHours < hoursFilled) {
					throw new ServiceException("[" + record.getName() + "在" + dailyAttendance.getWorkDate() + "的出勤时长为" + attendanceHours + "小时,累计填报工时" + hoursFilled + "小时，请核对数据");
				}
			}
		}
		for (int i = 0; i < insertList.size(); i += batchCount) {
			int end = Math.min(insertList.size(), i + batchCount);
			saveBatch(insertList.subList(i, end));
		}
		for (int i = 0; i < updateList.size(); i += batchCount) {
			int end = Math.min(updateList.size(), i + batchCount);
			baseMapper.batchUpdate(updateList.subList(i, end));
		}
	}

	@Override
	public IPage<XmTechnicianHoursVO> selectPage(XmTechnicianHoursVO dto, IPage<XmTechnicianHoursVO> page) {
		if (StringUtil.isBlank(dto.getYearAndMonth())) {
			throw new ServiceException("参数yearAndMonth不能为空");
		}
		int year = Integer.parseInt(dto.getYearAndMonth().substring(0, 4));
		int month = Integer.parseInt(dto.getYearAndMonth().substring(4));
		LocalDate startDate = LocalDate.of(year, month, 1);
		int daysInMonth = startDate.lengthOfMonth();

		List<XmTechnicianHoursVO> list = baseMapper.selectHoursPage(page, dto);
		for (XmTechnicianHoursVO temp : list) {

			List<DailyAttendance> dailyHoursList = baseMapper.selectByXmIdAndUnicode(temp.getXmId(), temp.getUnicode(), dto.getYearAndMonth());
			for (int i = 0; i < daysInMonth; i++) {
				String everyDay = startDate.plusDays(i).format(DateTimeFormatter.ofPattern(DateUtil.PATTERN_YYYYMMDD));
				//过滤某天的工时数据
				Optional<DailyAttendance> any = dailyHoursList.stream().filter(daily -> Func.equals(daily.getWorkDate(), everyDay)).findAny();
				DailyAttendance daily = null;
				if (any.isEmpty()) {
					daily = new DailyAttendance();
					daily.setWorkDate(everyDay);
					daily.setWorkHours(0.0);
					dailyHoursList.add(i, daily);
				}
			}
			temp.setDailyAttendances(dailyHoursList);
		}
		return page.setRecords(list);
	}

	@Override
	public boolean batchSubmit(XmTechnicianHoursVO dto) {
		if (StringUtil.isBlank(dto.getYearAndMonth())) {
			throw new ServiceException("参数yearAndMonth不能为空");
		}
		if (dto.getXmId() == null) {
			throw new ServiceException("参数xmId不能为空");
		}
		if (dto.getUnicode() == null) {
			throw new ServiceException("参数unicode不能为空");
		}

		xmService.judgeXmIsOver(dto.getXmId(), true);

		//获取考勤数据，具体人员在某个月的考勤明细
		List<DailyAttendance> dailyAttendanceList = attendanceMapper.selectDailyAttendanceByUnicodeAndMonth(dto.getUnicode(), dto.getYearAndMonth());
		if (CollectionUtil.isEmpty(dailyAttendanceList)) {
			throw new ServiceException("未设置考勤数据");
		}

		//获取工时数据， 具体人员在某月的工时数据
		List<XmTechnicianHours> dbList = baseMapper.selectByYearAndMonth(dto.getYearAndMonth(), null, dto.getUnicode());

		//需要新增的，以便后续批量新增
		List<XmTechnicianHours> insertList = new ArrayList<>();
		//需要更新的，以便后续批量修改
		List<XmTechnicianHours> updateList = new ArrayList<>();

		for (DailyAttendance dailyAttendance : dto.getDailyAttendances()) {
			//查询一天的考勤时长
			double attendanceHours = dailyAttendanceList.stream().filter(e -> Objects.equals(e.getWorkDate(), dailyAttendance.getWorkDate())).mapToDouble(DailyAttendance::getWorkHours).sum();
			if (attendanceHours <= 0) {
				throw new ServiceException(dailyAttendance.getWorkDate() + "当天无考勤数据");
			}
			if(dailyAttendance.getWorkHours() == null){
				dailyAttendance.setWorkHours(0.0);
			}
			//查询一天所有项目的工时总和
			double hoursTotal = dbList.stream().filter(e -> Objects.equals(e.getWorkDate(), dailyAttendance.getWorkDate())).mapToDouble(XmTechnicianHours::getWorkHours).sum();

			List<XmTechnicianHours> tempList = dbList.stream().filter(e -> Objects.equals(e.getWorkDate(), dailyAttendance.getWorkDate()) && Objects.equals(e.getXmId(), dto.getXmId())).toList();
			Long primaryKey = CollectionUtil.isEmpty(tempList) ? null : tempList.get(0).getId();

			XmTechnicianHours entity = new XmTechnicianHours();
			if (primaryKey == null) {

				//当 考勤时长 - 项目工时总和 - 新的工时 < 0，则提示“考勤时长不足”
				//当天已填总工时+新增的工时 > 考勤时长
//				if (attendanceHours -   - dailyAttendance.getWorkHours() < 0) {
				if (hoursTotal + dailyAttendance.getWorkHours() > attendanceHours) {
					throw new ServiceException(dailyAttendance.getWorkDate() + "的考勤时长为" + attendanceHours + "小时，可填写的工时不能超过" + (attendanceHours - hoursTotal) + "小时，请核对数据");
				}

				entity.setXmId(dto.getXmId());
				entity.setUnicode(dto.getUnicode());
				entity.setWorkDate(dailyAttendance.getWorkDate());
				entity.setWorkHours(dailyAttendance.getWorkHours());
				entity.setTenantId(SecureUtil.getTenantId());
				entity.setCreateUser(SecureUtil.getUserId());
				entity.setCreateDept(Func.toLong(SecureUtil.getDeptId()));
				insertList.add(entity);
			} else {
 				//当天已填总工时-修改前的工时+修改后的工时 > 考勤时长
 				if (hoursTotal - tempList.get(0).getWorkHours() + dailyAttendance.getWorkHours() > attendanceHours) {
					throw new ServiceException(dailyAttendance.getWorkDate() + "的考勤时长为" + attendanceHours + "小时，可填写的工时不能超过" + (attendanceHours + tempList.get(0).getWorkHours() - hoursTotal) + "小时，请核对数据");
				}
				entity.setId(primaryKey);
				entity.setWorkHours(dailyAttendance.getWorkHours());
				updateList.add(entity);
			}
		}

		if (!insertList.isEmpty()) {
			this.saveBatch(insertList);
			insertList.clear();
		}
		if (!updateList.isEmpty()) {
			baseMapper.batchUpdate(updateList);
			updateList.clear();
		}
		return true;
	}

	@Override
	public boolean clearHours(JSONObject dto) {
		JSONArray xmAndUnicodeList = dto.getJSONArray("xmAndUnicodeList");
		if (xmAndUnicodeList == null || xmAndUnicodeList.isEmpty()) {
			throw new ServiceException("参数xmAndUnicodeList不能为空");
		}
		for (int i = 0; i < xmAndUnicodeList.size(); i++) {
			JSONObject object = xmAndUnicodeList.getJSONObject(i);
			Long xmId = object.getLong("xmId");
			xmService.judgeXmIsOver(xmId, true);
			String unicode = object.getString("unicode");
			String yearAndMonth = dto.getString("yearAndMonth");
			baseMapper.clearByXmUnicodeMonth(yearAndMonth, xmId, unicode);
		}
		return true;
	}

	@Override
	public boolean deleteHours(JSONObject dto) {
 		JSONArray xmAndUnicodeList = dto.getJSONArray("xmAndUnicodeList");
		if (xmAndUnicodeList == null || xmAndUnicodeList.isEmpty()) {
			throw new ServiceException("参数xmAndUnicodeList不能为空");
		}
		for (int i = 0; i < xmAndUnicodeList.size(); i++) {
			JSONObject object = xmAndUnicodeList.getJSONObject(i);
			Long xmId = object.getLong("xmId");
			xmService.judgeXmIsOver(xmId, true);
			String unicode = object.getString("unicode");
			String yearAndMonth = dto.getString("yearAndMonth");
			baseMapper.deleteByXmUnicodeMonth(yearAndMonth, xmId, unicode);
		}
		return true;
	}

	@Override
	public void export(OutputStream os, List<XmTechnicianHoursVO> list, String yearAndMonth) {
		try {
			AtomicInteger index = new AtomicInteger(1);
			for (XmTechnicianHoursVO vo : list) {
				vo.setXh(index.getAndIncrement() + "");
			}
			Integer year = Integer.valueOf(yearAndMonth.substring(0, 4));
			Integer month = Integer.valueOf(yearAndMonth.substring(4));

			// 构建数据
			List<List<Object>> data = buildData(list, year, month);

			// 导出Excel（包含合并单元格策略）
			EasyExcel.write(os)
				.registerWriteHandler(new TechnicianWorkMergeStrategy(year, month))
				.registerWriteHandler(new CustomCellStyleHandler())
				.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
				.autoCloseStream(true)
				.sheet("每月研发工时打卡")
				.doWrite(data);

		} catch (Exception e) {
			throw new ServiceException("导出失败：" + e.getLocalizedMessage());
		}
	}

	/**
	 * 构建数据行
	 */
	private List<List<Object>> buildData(List<XmTechnicianHoursVO> list, Integer year, Integer month) {
		List<List<Object>> data = new ArrayList<>();

		LocalDate startDate = LocalDate.of(year, month, 1);
		int daysInMonth = startDate.lengthOfMonth();
		DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
		DateTimeFormatter weekFormatter = DateTimeFormatter.ofPattern("E", Locale.CHINA);
		DateTimeFormatter dayFormatter = DateTimeFormatter.ofPattern("d");

		// 第一行：标题行
		List<Object> titleRow = new ArrayList<>();
		titleRow.add("每月研发工时打卡" + year + "年" + month + "月");
		for (int i = 1; i < daysInMonth + 7; i++) { // 7 = 7个固定列
			titleRow.add("");
		}
		data.add(titleRow);

		// 第二行：纵向表头标题
		List<Object> verticalHeaderRow = new ArrayList<>();
		verticalHeaderRow.add("序号");
		verticalHeaderRow.add("研发项目编号");
		verticalHeaderRow.add("研发项目名称");
		verticalHeaderRow.add("工号");
		verticalHeaderRow.add("姓名");
		verticalHeaderRow.add("身份证号码");
		verticalHeaderRow.add("科研人员类别");
		//第二行：周几行
		for (int i = 1; i <= daysInMonth; i++) {
			LocalDate date = LocalDate.of(year, month, i);
			verticalHeaderRow.add(" "+date.format(weekFormatter).substring(1)+" ");
		}
		data.add(verticalHeaderRow);

		// 第三行：日期数字行
		List<Object> dayRow = new ArrayList<>();
		dayRow.add("");
		dayRow.add("");
		dayRow.add("");
		dayRow.add("");
		dayRow.add("");
		dayRow.add("");
		dayRow.add("");
		for (int day = 1; day <= daysInMonth; day++) {
			LocalDate date = LocalDate.of(year, month, day);
			dayRow.add(date.format(dayFormatter));
		}
		dayRow.add("");
		dayRow.add("");
		data.add(dayRow);

		// 第4行开始：数据行
		for (XmTechnicianHoursVO vo : list) {
			List<Object> dataRow = new ArrayList<>();

			// 序号
			dataRow.add(vo.getXh());
			// 研发项目编号
			dataRow.add(vo.getXmbh());
			//研发项目名称
			dataRow.add(vo.getXmmc());
			// 工号
			dataRow.add(vo.getNumber());
			//姓名
			dataRow.add(vo.getName());
			// 身份证号码
			dataRow.add(vo.getIdCard());
			// 科研人员类别
			dataRow.add(vo.getPersonnelType());

			// 每日考勤数据
			Map<String, Double> dailyMap = new HashMap<>();

			for (DailyAttendance daily : vo.getDailyAttendances()) {
				dailyMap.put(daily.getWorkDate(), daily.getWorkHours());
			}

			for (int day = 1; day <= daysInMonth; day++) {
				LocalDate currentDate = LocalDate.of(year, month, day);
				String dateKey = currentDate.format(dateFormatter);
				dataRow.add(dailyMap.getOrDefault(dateKey, 0.0));
			}
			data.add(dataRow);
		}

		return data;
	}

	@Override
	public List<JSONObject> selectListForPrint(XmTechnicianHoursVO dto) {
		return baseMapper.selectListForPrint(dto);
	}

	@Override
	public IPage<TechnicianHoursMonthlyVO> meiyueyanfagongshifenpeibiao(IPage<TechnicianHoursMonthlyVO> page, TechnicianDTO dto) {
		if (StringUtil.isBlank(dto.getYearAndMonth())) {
			throw new ServiceException("参数yearAndMonth不能为空");
		}
		List<TechnicianHoursMonthlyVO> list = baseMapper.meiyueyanfagongshifenpeibiao(page, dto);

		int year = Integer.parseInt(dto.getYearAndMonth().substring(0, 4));
		int month = Integer.parseInt(dto.getYearAndMonth().substring(4));
		LocalDate startDate = LocalDate.of(year, month, 1);
		int daysInMonth = startDate.lengthOfMonth();

		for (TechnicianHoursMonthlyVO vo : list) {
			List<DailyHours> dailyHours = baseMapper.selectDailyHoursByUnicodeAndMonth(dto.getYearAndMonth(), vo.getUnicode());
			//总出勤小时
			double attendanceTotal = 0;
			//总研发工时
			double workTotal = 0;
			//总非研发工时
			double unworkTotal = 0;
			//考勤数据
			List<DailyAttendance> attendanceList = attendanceMapper.selectDailyAttendanceByUnicodeAndMonth(vo.getUnicode(), dto.getYearAndMonth());
			for (int i = 0; i < daysInMonth; i++) {
				String everyDay = startDate.plusDays(i).format(DateTimeFormatter.ofPattern(DateUtil.PATTERN_YYYYMMDD));
				//过滤某天的考勤数据
				Optional<DailyAttendance> any = attendanceList.stream().filter(attendance -> Func.equals(attendance.getWorkDate(), everyDay)).findAny();

				Optional<DailyHours> optional = dailyHours.stream().filter(daily -> Func.equals(daily.getWorkDate(), everyDay)).findAny();
				DailyHours daily = null;
				if (optional.isPresent()) {
					daily = optional.get();
				} else {
					daily = new DailyHours();
					daily.setWorkDate(everyDay);
					daily.setWorkHours(0.0);
					daily.setUnworkHours(0.0);
					dailyHours.add(i, daily);
				}
				//设置考勤数据
				if (any.isPresent()) {
					daily.setAttendanceHours(any.get().getWorkHours());
				} else {
					daily.setAttendanceHours(0.0);
				}
				//设置一天的非研发工时=当天考勤-研发工时
				daily.setUnworkHours(Math.max(daily.getAttendanceHours() - daily.getWorkHours(), 0));
			}
			attendanceTotal = dailyHours.stream().mapToDouble(DailyHours::getAttendanceHours).sum();
			workTotal = dailyHours.stream().mapToDouble(DailyHours::getWorkHours).sum();
			unworkTotal = dailyHours.stream().mapToDouble(DailyHours::getUnworkHours).sum();

			vo.setWorkHoursTotal(workTotal);
//			总非研发工时 = 总出勤 - 总研发
			vo.setUnworkHoursTotal(unworkTotal);
			//研发工时分配率 = 总研发工时/总出勤小时
			if (workTotal == 0 || attendanceTotal == 0) {
				vo.setWorkHoursRate(0.0);
			} else {
				vo.setWorkHoursRate(workTotal / attendanceTotal);
			}

			//非研发工时分配率 = 总非研发工时/总出勤小时
			if (vo.getUnworkHoursTotal() == 0 || attendanceTotal == 0) {
				vo.setUnworkHoursRate(0.0);
			} else {
				vo.setUnworkHoursRate(vo.getUnworkHoursTotal() / attendanceTotal);
			}
			vo.setDailyHoursList(dailyHours);
		}
		return page.setRecords(list);
	}

	@Override
	public void exportMeiyueyanfagongshifenpeibiao(HttpServletResponse response, TechnicianDTO dto) {
		try {
			response.setContentType("application/vnd.ms-excel");
			response.setCharacterEncoding("utf-8");
			response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("每月研发工时分配表.xls", Charsets.UTF_8));

			Query query = new Query().setCurrent(1).setSize(Integer.MAX_VALUE);

			List<TechnicianHoursMonthlyVO> list = this.meiyueyanfagongshifenpeibiao(Condition.getPage(query), dto).getRecords();
			AtomicInteger index = new AtomicInteger(1);
			for (TechnicianHoursMonthlyVO vo : list) {
				vo.setXh(index.getAndIncrement() + "");
			}
			Integer year = Integer.valueOf(dto.getYearAndMonth().substring(0, 4));
			Integer month = Integer.valueOf(dto.getYearAndMonth().substring(4));

			// 构建数据
			List<List<Object>> data = TechnicianWorkStatisticsMergeStrategy.buildData(list, year, month);

			// 导出Excel（包含合并单元格策略）
			EasyExcel.write(response.getOutputStream())
				.registerWriteHandler(new TechnicianWorkStatisticsMergeStrategy(list, year, month))
				.registerWriteHandler(new CustomCellStyleHandler())
				.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
				.autoCloseStream(true)
				.sheet("每月研发工时分配表")
				.doWrite(data);

		} catch (Exception e) {
			throw new ServiceException("导出失败：" + e.getLocalizedMessage());
		}
	}

	@Override
	@Transactional
	public boolean fetch(String yearAndMonth) {
		List<XmTechnicianEntity> list = xmTechnicianMapper.selectList(Wrappers.<XmTechnicianEntity>lambdaQuery().eq(XmTechnicianEntity::getYearAndMonth, yearAndMonth));
		if (Func.isEmpty(list)) {
			throw new ServiceException("本月没有参研人员");
		}
		//过滤掉已结题项目
		List<Long> jtXmIdList = new ArrayList<>();
		for (XmTechnicianEntity entity : list) {
			if (xmService.judgeXmIsOver(entity.getXmId(), false)) {
				jtXmIdList.add(entity.getXmId());
			}
		}
		list = list.stream().filter(xmTechnician -> !jtXmIdList.contains(xmTechnician.getXmId())).toList();

		List<XmTechnicianHours> insertList = new ArrayList<>();
		for (XmTechnicianEntity xmTechnician : list) {
			XmTechnicianHours hours = new XmTechnicianHours();
			hours.setXmId(xmTechnician.getXmId());
			hours.setUnicode(xmTechnician.getUnicode());
			hours.setWorkDate(yearAndMonth + "01");
			hours.setWorkHours(0.0);
			hours.setTenantId(SecureUtil.getTenantId());
			insertList.add(hours);
		}

		LambdaQueryWrapper<XmTechnicianHours> queryWrapper = Wrappers.<XmTechnicianHours>lambdaQuery().apply("left(work_date, 6) = {0}", yearAndMonth);
		List<XmTechnicianHours> hoursList = baseMapper.selectList(queryWrapper);
		Map<Long, List<String>> collect = hoursList.stream().collect(Collectors.groupingBy(XmTechnicianHours::getXmId, Collectors.mapping(XmTechnicianHours::getUnicode, Collectors.toList())));
		if (collect.isEmpty()) {
			return this.saveBatch(insertList);
		} else {
 			List<XmTechnicianHours> deduplicationList = insertList.stream().filter(item -> !(collect.containsKey(item.getXmId()) && collect.get(item.getXmId()).contains(item.getUnicode()))).toList();
			if (Func.isEmpty(deduplicationList)) {
				throw new ServiceException("本月参研人员已全部引用");
			}
			return this.saveBatch(deduplicationList);
		}
	}
}
