
package org.sky.scientific.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillWrapper;
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.apache.poi.ss.util.CellRangeAddress;
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.TechnicianExcel;
import org.sky.scientific.excel.handle.MergeCellRangeWriteHandler;
import org.sky.scientific.excel.handle.XmcyryhzmExcel;
import org.sky.scientific.mapper.*;
import org.sky.scientific.pojo.dto.TechnicianDTO;
import org.sky.scientific.pojo.dto.XmSalaryDto;
import org.sky.scientific.pojo.dto.XmTechnicianDto;
import org.sky.scientific.pojo.entity.DepartmentEntity;
import org.sky.scientific.pojo.entity.TechnicianEntity;
import org.sky.scientific.pojo.entity.XmEntity;
import org.sky.scientific.pojo.entity.XmTechnicianEntity;
import org.sky.scientific.pojo.vo.*;
import org.sky.scientific.service.ITechnicianService;
import org.sky.system.cache.DictCache;
import org.sky.system.pojo.enums.DictEnum;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.InputStream;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 技术人员花名册 服务实现类
 *
 * @author sky
 * @since 2025-06-10
 */
@Slf4j
@Service
@AllArgsConstructor
public class TechnicianServiceImpl extends BaseServiceImpl<TechnicianMapper, TechnicianEntity> implements ITechnicianService {

	private final YsRyrgfyMapper ysRyrgfyMapper;
	private final XmTechnicianMapper xmTechnicianMapper;
	private final XmMapper xmMapper;
	private final XmTechnicianHoursMapper xmTechnicianHoursMapper;
	private final DepartmentMapper departmentMapper;

	@Override
	public boolean saveOrUpdateTechnician(TechnicianEntity technician) {
		if (StringUtil.isBlank(technician.getYearAndMonth())) {
			throw new ServiceException("参数yearAndMonth不能为空");
		}
		if (StringUtil.isBlank(technician.getNumber()) && StringUtil.isBlank(technician.getIdCard())) {
			throw new ServiceException("工号和身份证号码不能同时为空");
		}

		String unicode = baseMapper.getUnicodeByNumberAndIdentityCard(technician.getNumber(), technician.getIdCard());
		if (unicode == null) {
			unicode = UUID.randomUUID().toString();
		}
		technician.setUnicode(unicode);

		if (Func.isNull(technician.getId())) {
			//新增
			return this.save(technician);
		} else {
			return this.updateById(technician);
		}
//		LambdaQueryWrapper<TechnicianEntity> wrapper = Wrappers.<TechnicianEntity>lambdaQuery()
//			.eq(TechnicianEntity::getYearAndMonth, technician.getYearAndMonth())
//			.eq(TechnicianEntity::getUnicode, technician.getUnicode());
//		TechnicianEntity dbTechnician = baseMapper.selectOne(wrapper);
//		if (dbTechnician == null) {
//
//		}else {
//			return this.updateById(technician);
//		}
	}

	@Override
	public IPage<TechnicianVO> selectTechnicianPage(IPage<TechnicianVO> page, TechnicianDTO technician) {
		if (StringUtil.isBlank(technician.getYearAndMonth())) {
			throw new ServiceException("参数yearAndMonth不能为空");
		}
		List<TechnicianVO> voList = null;
		if (technician.getYearAndMonth().length() == 4) {
			voList = baseMapper.selectByYear(page, technician);
		} else if (technician.getYearAndMonth().length() == 6) {
			voList = baseMapper.selectTechnicianPage(page, technician);
		} else {
			throw new ServiceException("参数yearAndMonth只能是YYYY或YYYYmm格式");
		}
		return page.setRecords(voList);
	}

	@SneakyThrows
	@Override
	public void exportTechnician(HttpServletResponse response, TechnicianDTO technician) {
		Query query = new Query().setCurrent(1).setSize(Integer.MAX_VALUE);
		List<TechnicianVO> technicianList = this.selectTechnicianPage(Condition.getPage(query), technician).getRecords();
		if (CollectionUtil.isEmpty(technicianList)) {
			throw new ServiceException("花名册无数据!");
		}
		AtomicInteger i = new AtomicInteger(1);
		technicianList.forEach(temp -> {
			temp.setGenderName(DictCache.getValue(DictEnum.SEX, temp.getGender()));
			temp.setXh(i.getAndIncrement());
		});

		InputStream templateFileName = this.getClass().getClassLoader().getResourceAsStream("export-template/技术人员花名册.xls");
		response.setContentType("application/vnd.ms-excel");
		response.setCharacterEncoding("utf-8");
		response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("技术人员花名册.xls", Charsets.UTF_8));
		ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
			.withTemplate((templateFileName))
			.excelType(ExcelTypeEnum.XLS)
			.build();

		WriteSheet writeSheet = EasyExcel.writerSheet().build();
		//设置list集合数据
		excelWriter.fill(new FillWrapper("list", technicianList), writeSheet);
		excelWriter.finish();
		if (templateFileName != null) {
			templateFileName.close();
		}
	}

	@Transactional
	@Override
	public void importTechnician(List<TechnicianExcel> data, String yearAndMonth) {

		List<TechnicianEntity> insertList = new ArrayList<>();

		for (TechnicianExcel excel : data) {
			TechnicianEntity technician = Objects.requireNonNull(BeanUtil.copyProperties(excel, TechnicianDTO.class));

			technician.setNumber(Func.isBlank(technician.getNumber()) ? null : technician.getNumber().trim());
			technician.setIdCard(Func.isBlank(technician.getIdCard()) ? null : technician.getIdCard().trim());
			//转换 性别字典
			technician.setGender(Integer.valueOf(DictCache.getKey(DictEnum.SEX, excel.getGenderName())));
			technician.setYearAndMonth(yearAndMonth);

			if (Func.isNotBlank(excel.getDepartment())) {
				List<String> tmpList = Arrays.asList(excel.getDepartment().split("/"));
				String department = null;
				if (tmpList.size() == 1 || tmpList.size() == 2) {
					department = tmpList.get(tmpList.size() - 1);
				} else {
					throw new ServiceException("所属二级单位/部门最多只能有两级，用'/'分隔");
				}
				LambdaQueryWrapper<DepartmentEntity> eq = Wrappers.<DepartmentEntity>lambdaQuery()
					.eq(DepartmentEntity::getName, department)
					.eq(DepartmentEntity::getYearAndMonth, StringUtil.sub(yearAndMonth, 0, 4));
				DepartmentEntity dbDepartment = departmentMapper.selectOne(eq);
				if (Func.isNull(dbDepartment)) {
					throw new ServiceException("序号为[" + excel.getXh() + "]的部门/科室/项目部[" + excel.getDepartment() + "]不存在");
				}
				technician.setDepartmentId(dbDepartment.getId());
			}
			String unicode = baseMapper.getUnicodeByNumberAndIdentityCard(technician.getNumber(), technician.getIdCard());
			if (unicode == null) {
				unicode = UUID.randomUUID().toString();
			}
			technician.setUnicode(unicode);

			insertList.add(technician);
			if (insertList.size() % 20 == 0) {
				this.saveBatch(insertList);
				insertList.clear();
			}
		}
		if (Func.isNotEmpty(insertList)) {
			this.saveBatch(insertList);
		}
	}

	@Transactional
	@Override
	public boolean fetchLastMonthData(TechnicianEntity technician) {
		//查询上个月的数据
		//得到上个月的年月
		String lastDate = DateUtil.format(DateUtil.minusMonths(DateUtil.parse(technician.getYearAndMonth(), DateUtil.PATTERN_YYYYMM), 1), DateUtil.PATTERN_YYYYMM);
		List<TechnicianEntity> lastMonthList = this.baseMapper.selectList(Wrappers.<TechnicianEntity>lambdaQuery().eq(TechnicianEntity::getYearAndMonth, lastDate));
		if (CollectionUtil.isEmpty(lastMonthList)) {
			throw new ServiceException("上个月人员名单为空");
		}

		List<TechnicianEntity> currentMonthList = this.baseMapper.selectList(Wrappers.<TechnicianEntity>lambdaQuery().eq(TechnicianEntity::getYearAndMonth, technician.getYearAndMonth()));
		if (Func.isEmpty(currentMonthList)) {
			//本月无数据
			lastMonthList.forEach(temp -> {
				temp.setId(null);
				temp.setYearAndMonth(technician.getYearAndMonth());
			});
			return this.saveBatch(lastMonthList);
		} else {
			//与本月数据对比，去重后保存至数据库
			List<String> unicodeList_curr = currentMonthList.stream().map(TechnicianEntity::getUnicode).toList();

			List<TechnicianEntity> insertList = new ArrayList<>();
			for (TechnicianEntity temp : lastMonthList) {
				temp.setYearAndMonth(technician.getYearAndMonth());
				temp.setId(null);

				if (!unicodeList_curr.contains(temp.getUnicode())) {
					insertList.add(temp);
				}
				if (insertList.size() % 20 == 0) {
					this.saveBatch(insertList);
					insertList.clear();
				}
			}
			if (Func.isNotEmpty(insertList)) {
				this.saveBatch(insertList);
			}
		}
		return true;
	}

	@Override
	public boolean batchDelete(List<TechnicianEntity> list) {
		return baseMapper.batchDelete(list.stream().map(TechnicianEntity::getId).toList()) > 0;
	}

	@Transactional
	@Override
	public void zhijieyinyongyusuanbiaomingdan(String yearAndMonth) {

//		直接引用预算表名单 改成引入数据，逻辑：
//		1、先选择月份，
//		2、先根据项目开发周期查项目列表，（选择月份在项目开发周期内）
//		3、根据项目引入上个月的参研人员名单（基础信息），若无数据，则走第4步
//		4、再引入预算表的人员人工费用。（只有基础信息时，附属字段为空）
//		5、回第3步。

		//先根据项目开发周期查项目列表，（选择月份在项目开发周期内）
		List<XmEntity> xmList = xmMapper.listByYearAndMonth(yearAndMonth);
		Date lastDate = DateUtil.minusMonths(DateUtil.parse(yearAndMonth, DateUtil.PATTERN_YYYYMM), 1);
		for (XmEntity xm : xmList) {
			//根据项目引入上个月的参研人员名单
			LambdaQueryWrapper<XmTechnicianEntity> wrapper = Wrappers.<XmTechnicianEntity>lambdaQuery()
				.eq(XmTechnicianEntity::getXmId, xm.getId())
				.eq(XmTechnicianEntity::getYearAndMonth, DateUtil.format(lastDate, DateUtil.PATTERN_YYYYMM));
			List<XmTechnicianEntity> lastMonthTechnicianList = xmTechnicianMapper.selectList(wrapper);
			//若无数据，则引入预算表的人员人工费用
			List<String> unicodeList = null;
			if (CollectionUtil.isEmpty(lastMonthTechnicianList)) {
				Query query = new Query();
				query.setCurrent(1);
				query.setSize(Integer.MAX_VALUE);

				YsRyrgfyVO vo = new YsRyrgfyVO();
				vo.setXmId(xm.getId());

				List<YsRyrgfyVO> ysRyrgfyList = ysRyrgfyMapper.selectYsRyrgfyPage(Condition.getPage(query), vo);
				unicodeList = ysRyrgfyList.stream().map(YsRyrgfyVO::getUnicode).toList();
			} else {
				unicodeList = lastMonthTechnicianList.stream().map(XmTechnicianEntity::getUnicode).toList();
			}
			insertXmTechnician(unicodeList, xm.getId(), yearAndMonth);
		}
	}

	private void insertXmTechnician(List<String> unicodeList, Long xmId, String yearAndMonth) {
		for (String unicode : unicodeList) {
			XmTechnicianEntity monthlyEntity = new XmTechnicianEntity();
			monthlyEntity.setXmId(xmId);
			monthlyEntity.setUnicode(unicode);
			monthlyEntity.setYearAndMonth(yearAndMonth);
			monthlyEntity.setCreateUser(SecureUtil.getUserId());
			monthlyEntity.setUpdateUser(SecureUtil.getUserId());

			LambdaQueryWrapper<XmTechnicianEntity> queryWrapper = Wrappers.<XmTechnicianEntity>lambdaQuery().
				eq(XmTechnicianEntity::getYearAndMonth, yearAndMonth).
				eq(XmTechnicianEntity::getUnicode, unicode).
				eq(XmTechnicianEntity::getXmId, xmId);
			if (xmTechnicianMapper.selectCount(queryWrapper) == 0) {
				xmTechnicianMapper.insert(monthlyEntity);
			}
		}
	}

	@Override
	public IPage<XmTechnicianEntity> meiyueshijicanyuanrenyuanmingdan(IPage<XmTechnicianEntity> page, XmTechnicianDto entity) {
		return xmTechnicianMapper.meiyueshijicanyuanrenyuanmingdan(page, entity);
	}

	@SneakyThrows
	@Override
	public void exportXmTechnicianMonthly(HttpServletResponse response, XmTechnicianDto dto) {
		Query query = new Query().setCurrent(1).setSize(Integer.MAX_VALUE);
		List<XmTechnicianEntity> list = this.meiyueshijicanyuanrenyuanmingdan(Condition.getPage(query), dto).getRecords();

		AtomicInteger i = new AtomicInteger(1);
		list.forEach(temp -> {
			temp.setXh(i.getAndIncrement() + "");
		});

		InputStream templateFileName = this.getClass().getClassLoader().getResourceAsStream("export-template/每月实际参研人员名单.xls");
		response.setContentType("application/vnd.ms-excel");
		response.setCharacterEncoding("utf-8");
		response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("每月实际参研人员名单.xls", Charsets.UTF_8));
		ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
			.withTemplate((templateFileName))
			.excelType(ExcelTypeEnum.XLS)
			.build();

		WriteSheet writeSheet = EasyExcel.writerSheet().build();
		//设置list集合数据
		excelWriter.fill(list, writeSheet);
		Map<String, Object> map = new HashMap<>();
		map.put("year", dto.getYearAndMonth().substring(0, 4));
		map.put("month", dto.getYearAndMonth().substring(4));
		excelWriter.fill(map, writeSheet);
		excelWriter.finish();
		if (templateFileName != null) {
			templateFileName.close();
		}
	}

	@Override
	public boolean submitXmTechnicianMonthly(XmTechnicianEntity entity) {
		int affectNum = 0;
		entity.setId(null);
		entity.setYearAndMonth(entity.getYearAndMonth());
		entity.setCreateUser(SecureUtil.getUserId());
		entity.setUpdateUser(SecureUtil.getUserId());
		entity.setTenantId(SecureUtil.getTenantId());
		LambdaQueryWrapper<XmTechnicianEntity> wrapper = Wrappers.<XmTechnicianEntity>lambdaQuery().
			eq(XmTechnicianEntity::getYearAndMonth, entity.getYearAndMonth()).
			eq(XmTechnicianEntity::getUnicode, entity.getUnicode()).
			eq(XmTechnicianEntity::getXmId, entity.getXmId());
		if (xmTechnicianMapper.selectCount(wrapper) == 0) {
			affectNum = xmTechnicianMapper.insert(entity);
		} else {
			throw new ServiceException("科技人员已是项目在[" + entity.getYearAndMonth() + "]的参研人员，请勿重复新增");
		}
		return affectNum == 1;
	}

	@Override
	public boolean physicalDeleteXmTechnicianMonthlyByIds(List<Long> ids) {
		if (CollectionUtil.isEmpty(ids)) {
			throw new ServiceException("参数不能为空");
		}
		return xmTechnicianMapper.physicalDeleteXmTechnicianMonthlyByIds(ids) == ids.size();
	}

	@Override
	public IPage<TechnicianEntity> selectTechnicianPageByDateInterval(IPage<TechnicianEntity> page, HashMap<String, String> param) {
		if (!param.containsKey("startDate") || !param.containsKey("endDate")) {
			throw new ServiceException("startDate或者endDate为空");
		}
		String startDate = param.get("startDate");
		String endDate = param.get("endDate");
		if (startDate.contains("-")) {
			startDate = startDate.replace("-", "");
		}
		if (endDate.contains("-")) {
			endDate = endDate.replace("-", "");
		}

		if (startDate.length() == 8) {
			startDate = startDate.substring(0, 6);
		}
		if (endDate.length() == 8) {
			endDate = endDate.substring(0, 6);
		}
		param.put("startDate", startDate);
		param.put("endDate", endDate);
		List<TechnicianEntity> list = baseMapper.selectByDateInterval(page, param);
		return page.setRecords(list);
	}

	@Override
	public IPage<XmTechnicianSummary> xiangmucanyanrenyuanhuizongbiao(IPage<XmTechnicianSummary> page, XmTechnicianEntity entity) {
		if (StringUtil.isAllBlank(entity.getYearAndMonth())) {
			throw new ServiceException("参数yearAndMonth不能为空");
		}
		List<XmTechnicianSummary> summary = null;
		if (entity.getYearAndMonth().length() == 6) {
			summary = xmTechnicianMapper.xmTechnicianSummaryByMonthly(page, entity);
		} else {
			summary = xmTechnicianMapper.xmTechnicianSummaryByYear(page, entity);
		}
		return page.setRecords(summary);
	}

	@Override
	public IPage<XmTechnicianVO> canyanrenyuanhuamingce(IPage<XmTechnicianVO> page, XmTechnicianDto entity) {
		List<XmTechnicianVO> list = xmTechnicianMapper.canyanrenyuanhuamingce(page, entity);
		for (XmTechnicianVO vo : list) {
			// 参研项目列表
			vo.setXmList(xmTechnicianMapper.selectXmListByUnicodeAndYear(vo.getUnicode(), entity.getYearAndMonth()));
			// 参研时长列表
			List<DailyAttendance> attendances = xmTechnicianHoursMapper.selectHoursListByUnicodeAndYear(vo.getUnicode(), entity.getYearAndMonth());


			vo.setHoursList(attendances);
		}
		return page.setRecords(list);
	}

//	public static void main(String[] args) {
//		List<Integer> list = new ArrayList<>();
//		list.add(1);
//		list.add(2);
//		list.add(0, 5);
//		System.out.println(list);
//	}

	@Override
	public IPage<TechnicianVO> niandugaokejirenyuanhuizongbiao(IPage<TechnicianVO> page, TechnicianDTO dto) {
		//TODO 目前因测试数据不多，需求是 年度天数大于183的才显示，这里先用18代替，后期要改回来
		dto.setTenantId(SecureUtil.getTenantId());
		List<TechnicianVO> list = baseMapper.niandugaokejirenyuanhuizongbiao(page, dto);
		for (TechnicianVO vo : list) {
			vo.setGenderName(DictCache.getValue(DictEnum.SEX, vo.getGender()));
		}
		return page.setRecords(list);
	}

	@Override
	public IPage<XmTechnicianSalaryVO> meiyueyanfarengongfeiyongfeifenpeibiao(IPage<XmTechnicianSalaryVO> page, XmTechnicianDto dto) {
		if (StringUtil.isBlank(dto.getYearAndMonth())) {
			throw new ServiceException("参数yearAndMonth不能为空");
		}
		dto.setTenantId(SecureUtil.getTenantId());
		List<XmTechnicianSalaryVO> list = null;
		if (dto.getYearAndMonth().length() == 6) {
			list = xmTechnicianMapper.meiyueyanfarengongfeiyongfeifenpeibiao(page, dto);
		} else if (dto.getYearAndMonth().length() == 4) {
			list = xmTechnicianMapper.meiyueyanfarengongfeiyongfeifenpeibiaoYear(page, dto);
		} else {
			throw new ServiceException("参数yearAndMonth格式错误，yyyyMM或yyyy");
		}
		return page.setRecords(list);
	}

	@Override
	public IPage<XmSalaryVO> meiyueyanfaxiangmurengongfenpeihuizongbiao(IPage<XmSalaryVO> page, XmSalaryDto dto) {
		if (StringUtil.isBlank(dto.getYearAndMonth())) {
			throw new ServiceException("参数yearAndMonth不能为空");
		}
		dto.setTenantId(SecureUtil.getTenantId());
		List<XmSalaryVO> list = null;
		if (dto.getYearAndMonth().length() == 6) {
			list = xmTechnicianMapper.meiyueyanfaxiangmurengongfenpeihuizongbiao(page, dto);
		} else if (dto.getYearAndMonth().length() == 4) {
			list = xmTechnicianMapper.meiyueyanfaxiangmurengongfenpeihuizongbiaoByYear(page, dto);
		} else {
			throw new ServiceException("参数yearAndMonth格式错误，yyyyMM或yyyy");
		}
		return page.setRecords(list);
	}

	@SneakyThrows
	@Override
	public void exportCyryhmc(HttpServletResponse response, XmTechnicianDto dto) {
		Query query = new Query().setCurrent(1).setSize(Integer.MAX_VALUE);
		List<XmTechnicianVO> list = this.canyanrenyuanhuamingce(Condition.getPage(query), dto).getRecords();

		List<Map<String, Object>> resultList = new ArrayList<>(list.size());

		AtomicInteger i = new AtomicInteger(1);
		list.forEach(temp -> {
			temp.setXh(i.getAndIncrement() + "");
			temp.setXmmcJoin(temp.getXmList().stream().map(XmEntity::getXmmc).collect(Collectors.joining(";")));
			temp.setXmCount(temp.getXmList().size());

			List<BigDecimal> hours1 = new ArrayList<>(temp.getHoursList().stream().map(x -> BigDecimal.valueOf(x.getWorkHours())).toList());
			Map<String, Object> map =  BeanUtil.beanToMap(temp);
			for (int k = 0; k<12; k++) {
				String month = k < 10 ? "0" + (k+1) : (k+1) + "";
				if (temp.getHoursList().stream().noneMatch(a -> Func.equals(a.getWorkDate(), dto.getYearAndMonth() + month))) {
 					hours1.add(k, new BigDecimal(0));
				}
				map.put("month" + (k+1), hours1.get(k));
			}
			map.put("hoursByMonth",  hours1.stream().reduce(BigDecimal.ZERO, BigDecimal::add));
			resultList.add(map);
		});

		InputStream templateFileName = this.getClass().getClassLoader().getResourceAsStream("export-template/参研人员花名册.xls");
		response.setContentType("application/vnd.ms-excel");
		response.setCharacterEncoding("utf-8");
		response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("参研人员花名册.xls", Charsets.UTF_8));
		ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
			.withTemplate((templateFileName))
			.excelType(ExcelTypeEnum.XLS)
			.build();

		WriteSheet writeSheet = EasyExcel.writerSheet().build();
		//设置list集合数据
		excelWriter.fill(resultList, writeSheet);
		Map<String, Object> map = new HashMap<>();
		map.put("year", dto.getYearAndMonth().substring(0, 4));
		map.put("month", dto.getYearAndMonth().substring(4));
		excelWriter.fill(map, writeSheet);
		excelWriter.finish();
		if (templateFileName != null) {
			templateFileName.close();
		}
	}

	@SneakyThrows
	@Override
	public void exportXmcyryhzb(HttpServletResponse response, XmTechnicianEntity dto) {

		InputStream templateFileName = this.getClass().getClassLoader().getResourceAsStream("export-template/项目参研人员汇总表.xls");
		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<XmTechnicianSummary> list = this.xiangmucanyanrenyuanhuizongbiao(Condition.getPage(query), dto).getRecords();

		List<XmcyryhzmExcel> dataList = new ArrayList<>();
		AtomicInteger i = new AtomicInteger(1);

		list.forEach(temp -> {

			List<XmTechnicianEntity> technicianList = temp.getTechnicianList();
			technicianList.forEach(technician -> {
				XmcyryhzmExcel data = new XmcyryhzmExcel();
				data.setXh(i.getAndIncrement()+"");
				data.setXmbh(temp.getXmbh());
				data.setXmmc(temp.getXmmc());
				data.setName(technician.getName());
				data.setNumber(technician.getNumber());
				data.setIdCard(technician.getIdCard());
				data.setDepartment(technician.getDepartment());
				data.setPost(technician.getPost());
				data.setPostLocation(technician.getPostLocation());
				data.setNature(technician.getNature());
				data.setSituation(technician.getSituation());
				data.setPersonnelType(technician.getPersonnelType());
				dataList.add(data);
 			});
		});

		List<CellRangeAddress> rangeCellList = createCellRange(dataList, new int[]{1,2}, 2, XmcyryhzmExcel::getXmbh);

		ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
			.withTemplate((templateFileName))
			.registerWriteHandler(new MergeCellRangeWriteHandler(rangeCellList))
			.excelType(ExcelTypeEnum.XLS)
			.build();

		WriteSheet writeSheet = EasyExcel.writerSheet().build();
		//设置list集合数据
		excelWriter.fill(dataList, writeSheet);
		Map<String, Object> map = new HashMap<>();
		map.put("year", dto.getYearAndMonth().substring(0, 4));
		map.put("month", dto.getYearAndMonth().substring(4));
		excelWriter.fill(map, writeSheet);
		excelWriter.finish();
		if (templateFileName != null) {
			templateFileName.close();
		}
	}

	/**
	 * 生成合并区域
	 *
	 * @param detailList       数据列表
	 * @param mergeColumnIndex 要合并的列索引
	 * @param startRowIndex    起始行（含表头时，表头行数）
	 * @param groupFunction    分组函数，如 e -> e.get某字段()
	 * @return 合并区域集合
	 */
	public static <T> List<CellRangeAddress> createCellRange(List<T> detailList, int[] mergeColumnIndex, int startRowIndex, Function<T, String> groupFunction) {
		if (detailList == null || detailList.isEmpty()) {
			return Collections.emptyList();
		}
		// 计算每个key下的数量
		Map<String, Long> groupMap = new LinkedHashMap<>();
		for (T item : detailList) {
			String key = groupFunction.apply(item);
			groupMap.put(key, groupMap.getOrDefault(key, 0L) + 1);
		}

		List<CellRangeAddress> rangeCellList = new ArrayList<>();
		// 当前行数
		int lineCount = startRowIndex;

		for (Map.Entry<String, Long> entry : groupMap.entrySet()) {
			int count = entry.getValue().intValue();
			if (count > 1) {
				int endRowIndex = lineCount + count - 1;
				for (int columnIndex : mergeColumnIndex) {
					rangeCellList.add(new CellRangeAddress(lineCount, endRowIndex, columnIndex, columnIndex));
				}
			}
			lineCount += count;
		}

		return rangeCellList;
	}

	@SneakyThrows
	@Override
	public void exportNdgkjryhzb(HttpServletResponse response, TechnicianDTO dto) {
		Query query = new Query().setCurrent(1).setSize(Integer.MAX_VALUE);
		List<TechnicianVO> list = this.niandugaokejirenyuanhuizongbiao(Condition.getPage(query), dto).getRecords();

		AtomicInteger i = new AtomicInteger(1);
		list.forEach(temp -> {
			temp.setXh(i.getAndIncrement());
		});

		InputStream templateFileName = this.getClass().getClassLoader().getResourceAsStream("export-template/年度高新科技人员汇总表.xls");
		response.setContentType("application/vnd.ms-excel");
		response.setCharacterEncoding("utf-8");
		response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("年度高新科技人员汇总表.xls", Charsets.UTF_8));
		ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
			.withTemplate((templateFileName))
			.excelType(ExcelTypeEnum.XLS)
			.build();

		WriteSheet writeSheet = EasyExcel.writerSheet().build();
		//设置list集合数据
		excelWriter.fill(list, writeSheet);
		Map<String, Object> map = new HashMap<>();
		map.put("year", dto.getYearAndMonth().substring(0, 4));
		excelWriter.fill(map, writeSheet);
		excelWriter.finish();
		if (templateFileName != null) {
			templateFileName.close();
		}
	}

}
