package com.prolog.cs.biz.inbound.service.impl.rqc;

import com.prolog.cs.biz.inbound.constant.InboundBizConstant;
import com.prolog.cs.biz.inbound.dao.rqc.RqcReLinesMapper;
import com.prolog.cs.biz.inbound.dao.task.TaskQcRecheckDetailMapper;
import com.prolog.cs.biz.inbound.dto.PageParam;
import com.prolog.cs.biz.inbound.dto.rqc.RqcReLinesDTO;
import com.prolog.cs.biz.inbound.model.rqc.RqcReLines;
import com.prolog.cs.biz.inbound.model.task.TaskQcRecheckDetail;
import com.prolog.cs.biz.inbound.service.impl.BaseServiceImpl;
import com.prolog.cs.biz.inbound.service.impl.processor.Helper.TaskHelper;
import com.prolog.cs.biz.inbound.service.impl.processor.enums.ConclusionEnum;
import com.prolog.cs.biz.inbound.service.impl.processor.enums.RcvTypeStrategyEnum;
import com.prolog.cs.biz.inbound.service.impl.processor.enums.RqcStrategyEnum;
import com.prolog.cs.biz.inbound.service.impl.processor.factory.RcvSubjectFactory;
import com.prolog.cs.biz.inbound.service.impl.processor.factory.RcvSubjectFactoryS1Impl;
import com.prolog.cs.biz.inbound.service.impl.processor.handler.TaskHandler;
import com.prolog.cs.biz.inbound.service.rqc.RqcReLinesService;
import com.prolog.cs.biz.inbound.utils.ObjectUtils;
import com.prolog.cs.biz.inbound.utils.UserUtils;
import com.prolog.cs.biz.inbound.vo.RqcDQueryVO;
import com.prolog.framework.authority.core.dto.UserDTO;
import com.prolog.framework.beans.BeanUtils;
import com.prolog.framework.core.exception.BizException;
import com.prolog.framework.core.pojo.Page;
import com.prolog.framework.core.restriction.*;
import com.prolog.framework.dao.util.DaoUtils;
import com.prolog.framework.dao.util.PageUtils;
import com.prolog.framework.utils.AssertUtils;
import com.prolog.framework.utils.MapUtils;
import com.prolog.framework.utils.StringUtils;
import org.apache.commons.collections4.map.HashedMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.Validator;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class RqcReLinesServiceImpl extends BaseServiceImpl<RqcReLines> implements RqcReLinesService {

	@Resource
	private RqcReLinesMapper mapper;

	@Resource
	private UserUtils userUtils;

	@Resource
	@Qualifier("rqcReTaskHandler")
	private TaskHandler handler;

	@Resource
	private TaskHelper taskHelper;

	@Resource
	private Validator validator;

	@Resource
	private TaskQcRecheckDetailMapper taskQcRecheckDetailMapper;

	private final static String batchAddMsg = "复检行明细批量保存操作";
	private static Logger logger = LoggerFactory.getLogger(RqcLinesServiceImpl.class);

	@Override
	public Page<RqcReLinesDTO> findList(RqcReLinesDTO rqcReLinesDTO, PageParam pageParam) {
		Criteria criteria = buildPageParam(pageParam, true, null, RqcReLines.class);
		Restriction r = buildCondition(rqcReLinesDTO);
		criteria.setRestriction(r);
		this.startPage(pageParam);
		List<RqcReLines> list = this.mapper.findByCriteria(criteria);
		Page<RqcReLines> page = PageUtils.getPage(list);
		Page<RqcReLinesDTO> page2 = this.buildPage(page, (p) -> {
			RqcReLinesDTO target = new RqcReLinesDTO();
			BeanUtils.copyProperties(p, target);
			return target;
		});
		return page2;
	}

	/**
	 * 复检逻辑2.0: 1.先判断任务被执行的次数 次数在默认复检次数内 第0次: 新增复检行操作 结论判断: 若是合格/不合格
	 * 直接结束掉任务,并推送任务到下一阶段(并标识复检已完成) 若是待处理, 更新任务池任务执行次数 第1次: 更新复检行操作
	 * (次数,以及覆盖上一次的结论和原因) 结论判断: 若是合格/不合格 直接结束任务,并推送任务到下一阶段(并标识复检已完成) 待处理,更新任务池的次数
	 * 次数不在默认范围 更新任务状态为不可执行
	 */
	@Transactional
	@Override
	public Object batchAdd(List<RqcReLinesDTO> rqcReLinesDTOList, String masterId, UserDTO userDTO, Date now) {
		if (ObjectUtils.isNull(userDTO))
			userDTO = userUtils.getUserDTO();
		if (ObjectUtils.isNull(now))
			now = new Date();
		int row = 0;
		boolean editFlag = Boolean.FALSE;
		// 需要新增的复检行
		List<RqcReLines> list = new ArrayList<>();
		// 更新完成状态的任务id
		List<String> taskIds = new ArrayList<>();
		List<Map<String, Object>> taskIdMapList2 = new ArrayList<>();
		// 需要往下推送结论的复检数据
		List<RqcReLinesDTO> list2 = new ArrayList<>();
		// 需要更新的复检行-对应任务id
		List<String> taskId2 = new ArrayList<>();
		// 需要更新的复检行
		List<RqcReLinesDTO> list3 = new ArrayList<>();
		// 需要更新次数的任务id
		List<Map<String, Object>> taskIdMapList = new ArrayList<>();
		// 需要更新上一节点的任务id
		List<String> taskId3 = new ArrayList<>();
		
		RqcReLinesDTO rqcReLinesDTO2 = rqcReLinesDTOList.get(0);
		String warehouseId = rqcReLinesDTO2.getWarehouseId();
		String mtlOwnerId = rqcReLinesDTO2.getMtlOwnerId();
		String inboundRerqcPendToerp = taskHelper.getInboundRerqcPendToerp(warehouseId, mtlOwnerId);
		
		for (RqcReLinesDTO rqcReLinesDTO : rqcReLinesDTOList) {
			row++;
			// 数据准确性计算
			BigDecimal pieceQty = rqcReLinesDTO.getPieceQty();
			BigDecimal conversionRate = rqcReLinesDTO.getConversionRate();
			if (conversionRate == null) {
				conversionRate = BigDecimal.ONE;
				rqcReLinesDTO.setConversionRate(conversionRate);
			}
			if (pieceQty == null) {
				pieceQty = BigDecimal.ZERO;
				rqcReLinesDTO.setPieceQty(pieceQty);
			}
			BigDecimal caseQty = pieceQty.divide(conversionRate, 0, RoundingMode.DOWN); // 整件
			BigDecimal multiply = caseQty.multiply(conversionRate);
			BigDecimal oddPieceQty = pieceQty.subtract(multiply); // 零散
			BigDecimal qty = pieceQty.divide(conversionRate, 4, RoundingMode.DOWN); // 包装总量
			// 保证数据正确性再次进行计算
			rqcReLinesDTO.setOddCaseQty(caseQty);
			rqcReLinesDTO.setOddPieceQty(oddPieceQty);

			String taskReRqcId = rqcReLinesDTO.getTaskReRqcId(); // 来源任务id
			// 头表id
//			String rqcReHeaderId = rqcReLinesDTO.getRqcReHeaderId();
//			if (ObjectUtils.isNull(rqcReHeaderId))
			rqcReLinesDTO.setRqcReHeaderId(masterId);

			// 并发判断 乐观锁
			if (!canCommit(rqcReLinesDTO, row))
				throw new BizException(
						String.format("第[%d]行任务保存复检行失败!taskReRqcId为[%s]的复检任务已处理,请刷新重试", row, taskReRqcId));

			// 结论的默认处理
			String stockStatus = rqcReLinesDTO.getStockStatus();
			Integer times = rqcReLinesDTO.getTimes();
			if (times == null) {
				times = 0;
			}

			// 更新复检的任务次数(不管待不待处理,几次复检,都更新任务次数和执行者)
			Map<String, Object> map = new HashMap<>();
			map.put("taskId", taskReRqcId);
			map.put("times", times + 1);
			map.put("executor", userDTO.getUsername());
			taskIdMapList.add(map);

			if (times == 0) {
				// 第一次复检
				String qcReResult = rqcReLinesDTO.getQcReResult();
				if (StringUtils.isBlank(qcReResult)) {
					qcReResult = "0";
				}
//				if (StringUtils.isBlank(stockStatus))
				stockStatus = qcReResult;
//				if (StringUtils.isBlank(stockStatus))
//					stockStatus = "0"; // 默认合格
				rqcReLinesDTO.setStockStatus(stockStatus);

				this.buildDefaultField(rqcReLinesDTO, editFlag, userDTO);
				this.vaildateModel(rqcReLinesDTO, editFlag, userDTO, row, validator, batchAddMsg);
				RqcReLines lines = new RqcReLines();
				BeanUtils.copyProperties(rqcReLinesDTO, lines);
				this.buildField(lines, editFlag, userDTO, now);
				lines.setQcReDate(now);
				lines.setQcReOpcode(userDTO.getUsername());
				lines.setRefType(1);
				list.add(lines);
				if (stockStatus.equals(InboundBizConstant.QUALIFIED)
						|| stockStatus.equals(InboundBizConstant.UN_QUALIFIED)) {
					// 合格/不合格
					taskIds.add(taskReRqcId);
					Map<String, Object> map2 = new HashMap<>();
					map2.put("taskId", taskReRqcId);
					map2.put("times", times + 1);
					map2.put("executor", userDTO.getUsername());
					taskIdMapList2.add(map2);
					list2.add(rqcReLinesDTO);
				} else if (stockStatus.equals(InboundBizConstant.PENDING)) {
					if ("1".equals(inboundRerqcPendToerp)) {
						this.doRerqcPendToerp(rqcReLinesDTO);
					}
					// 待处理
					taskId3.add(taskReRqcId); // 记录多次复检上一节点的来源
				}
				continue;
			}
			if (0 < times && times < InboundBizConstant.DEFAULT_RECHECK_TIMES - 1) {
				// 大于0 小于最大次数 不是最终复检
				if (times == 1) {
					String qcReResult2 = rqcReLinesDTO.getQcReResult2();
					if (StringUtils.isBlank(qcReResult2)) {
						qcReResult2 = "0";
					}
//					if (StringUtils.isBlank(stockStatus))
					stockStatus = qcReResult2;
					rqcReLinesDTO.setQcReDate2(now);
					rqcReLinesDTO.setQcReOpcode2(userDTO.getUsername());
				}
				if (times >= 2) {
					String qcReResult3 = rqcReLinesDTO.getQcReResult3();
//					if (StringUtils.isBlank(stockStatus))
					if (StringUtils.isBlank(qcReResult3)) {
						qcReResult3 = "0";
					}
					stockStatus = qcReResult3;
					rqcReLinesDTO.setQcReDate3(now);
					rqcReLinesDTO.setQcReOpcode3(userDTO.getUsername());
				}
				rqcReLinesDTO.setStockStatus(stockStatus);

				// 更新复检行
				rqcReLinesDTO.setTimes(times + 1);
				list3.add(rqcReLinesDTO);
				taskId2.add(taskReRqcId);

				if (stockStatus.equals(InboundBizConstant.QUALIFIED)
						|| stockStatus.equals(InboundBizConstant.UN_QUALIFIED)) {
					// 合格/不合格
					taskIds.add(taskReRqcId);
					Map<String, Object> map2 = new HashMap<>();
					map2.put("taskId", taskReRqcId);
					map2.put("times", times + 1);
					map2.put("executor", userDTO.getUsername());
					taskIdMapList2.add(map2);
					list2.add(rqcReLinesDTO);
				} else if (stockStatus.equals(InboundBizConstant.PENDING)) {
					// 待处理
					if ("1".equals(inboundRerqcPendToerp)) {
						this.doRerqcPendToerp(rqcReLinesDTO);
					}
					taskId3.add(taskReRqcId); // 记录多次复检上一节点的来源
				}
				continue;
			}
			if (times == InboundBizConstant.DEFAULT_RECHECK_TIMES - 1) {
				// 等于最大次数,最终复检
				if (times == 1) {
					String qcReResult2 = rqcReLinesDTO.getQcReResult2();
					if (StringUtils.isBlank(qcReResult2)) {
						qcReResult2 = "0";
					}
//					if (StringUtils.isBlank(stockStatus))
					stockStatus = qcReResult2;
					rqcReLinesDTO.setQcReDate2(now);
					rqcReLinesDTO.setQcReOpcode2(userDTO.getUsername());
				}
				if (times >= 2) {
					String qcReResult3 = rqcReLinesDTO.getQcReResult3();
					if (StringUtils.isBlank(qcReResult3)) {
						qcReResult3 = "0";
					}
//					if (StringUtils.isBlank(stockStatus))
					stockStatus = qcReResult3;
					rqcReLinesDTO.setQcReDate3(now);
					rqcReLinesDTO.setQcReOpcode3(userDTO.getUsername());
				}
				rqcReLinesDTO.setStockStatus(stockStatus);

				// 更新复检行
				rqcReLinesDTO.setTimes(InboundBizConstant.DEFAULT_RECHECK_TIMES);
				list3.add(rqcReLinesDTO);
				taskId2.add(taskReRqcId);

				// 更新质检状态及,提交结论并推送任务到质检(质检任务标识中的有已复检的标识)
				if (stockStatus.equals(InboundBizConstant.PENDING)) {
					if ("1".equals(inboundRerqcPendToerp)) {
						this.doRerqcPendToerp(rqcReLinesDTO);
					}
					rqcReLinesDTO.setStockStatus(InboundBizConstant.UN_QUALIFIED);
				}
				taskIds.add(taskReRqcId);
				Map<String, Object> map2 = new HashMap<>();
				map2.put("taskId", taskReRqcId);
				map2.put("times", times + 1);
				map2.put("executor", userDTO.getUsername());
				taskIdMapList2.add(map2);
				list2.add(rqcReLinesDTO);
				continue;

			}
			if (times > InboundBizConstant.DEFAULT_RECHECK_TIMES - 1) {
				// 大于最大次数
				taskIds.add(taskReRqcId);
				Map<String, Object> map2 = new HashMap<>();
				map2.put("taskId", taskReRqcId);
				map2.put("times", InboundBizConstant.DEFAULT_RECHECK_TIMES);
				map2.put("executor", userDTO.getUsername());
				taskIdMapList2.add(map2);
				continue;
			}
		}

		// 保存质检行
		excuteInsert(list);

		// 更新质检行
		excuteUpdate(list3, userDTO, now);

		// 更新任务池任务执行次数
		this.updateTaskPoolTimes(taskIdMapList, userDTO, now);

		// 更新任务池状态
		this.updateTaskPoolStatus(taskIds, userDTO, now);
//		this.updateTaskPoolStatus2(taskIdMapList2, userDTO, now);

		// 更新任务上一节点来源及id
		this.updateTaskPoolPreNode(taskId3, userDTO, now);

		// 往下推送任务
		String rcvHeaderId = rqcReLinesDTOList.get(0).getRcvHeaderId();
		excutePushTask(list, list2, list3, rcvHeaderId);

		return true;
	}

//	private void updateTaskPoolStatus2(List<Map<String, Object>> taskIdMapList2, UserDTO userDTO, Date now) {
//		if (ObjectUtils.isNull(taskIdMapList2))
//			return;
//		// 更新质检池任务状态 为已执行
//		Criteria criteria = new Criteria(TaskQcRecheckDetail.class);
//		for (Map<String, Object> map : taskIdMapList2) {
//			
//		}
//		criteria.setRestriction(Restrictions.in("taskRecheckId", taskIds.toArray()));
//		taskQcRecheckDetailMapper.updateMapByCriteria(
//				MapUtils.put("status", "0").put("times", InboundBizConstant.DEFAULT_RECHECK_TIMES).getMap(), criteria); // 置为已完成
//
//	}

	private void doRerqcPendToerp(RqcReLinesDTO rqcReLinesDTO) {
		// TODO 发送erp二次复检
		
	}

	private void updateTaskPoolPreNode(List<String> taskId3, UserDTO userDTO, Date now) {
		if (ObjectUtils.isNull(taskId3)) {
			return;
		}
		for (String taskId : taskId3) {
			this.taskQcRecheckDetailMapper.updatePreNodeLineId(taskId,userDTO.getUsername());
		}
	}

	private void excutePushTask(List<RqcReLines> list, List<RqcReLinesDTO> list2, List<RqcReLinesDTO> list3,
			String rcvHeaderId) {
		if (ObjectUtils.isNull(list2))
			return;

		try {
			excuteBuildRelatedId(list, list2, list3);
		} catch (Exception e) {
			e.printStackTrace();
		}
		// 1. 根据质复检的结论分组到复检组,上架组 TODO
		Map<String, List<RqcReLinesDTO>> collect = Optional.ofNullable(list2).orElse(Collections.emptyList()).stream()
				.distinct().collect(Collectors.groupingBy(RqcReLinesDTO::getStockStatus));
		AssertUtils.notNull(collect, "复检行数据根据结论分组为空!");
		// 2. 是否需要判断质检次数,复检次数
		int times = InboundBizConstant.DEFAULT_RECHECK_TIMES; // 第二次入复检/质检
//		String rcvHeaderId = rqcReLinesDTOList.get(0).getRcvHeaderId();
		// 3. 获取策略
		RcvTypeStrategyEnum rcvTypeStrategyEnum = taskHelper.getRcvTypeStrategyEnum(rcvHeaderId);
		RqcStrategyEnum rqcStrategyEnum = taskHelper.getRqcStrategyEnum(rcvHeaderId);

		for (List<RqcReLinesDTO> rqcReLinesList : collect.values()) {
			String conclusion = rqcReLinesList.get(0).getStockStatus();

			// 4. 调用策略工厂来组织执行器执行任务
			RcvSubjectFactory factory = new RcvSubjectFactoryS1Impl();
			Map<String, Object> map = new HashedMap<>();
			map.put("data", rqcReLinesList);
			map.put("times", times);
			// map 放入 mtlOwnerId、warehouseId
			map.put("mtlOwnerId", rqcReLinesList.get(0).getMtlOwnerId());
			map.put("warehouseId", rqcReLinesList.get(0).getWarehouseId());
			ConclusionEnum conclusionEnum = taskHelper.getConclusionEnum(conclusion);
			factory.excuteTask(map, handler, rcvTypeStrategyEnum, rqcStrategyEnum, conclusionEnum,false);
		}

	}

	private void excuteBuildRelatedId(List<RqcReLines> list, List<RqcReLinesDTO> list2, List<RqcReLinesDTO> list3) {
		Map<String, List<RqcReLinesDTO>> collect2 = list2.stream()
				.collect(Collectors.groupingBy(RqcReLinesDTO::getTaskReRqcId));
		if (ObjectUtils.isNotNull(collect2)) {
			// 新增的id
			if (ObjectUtils.isNotNull(list)) {
				for (RqcReLines line : list) {
					String taskId = line.getTaskReRqcId();
					List<RqcReLinesDTO> list4 = collect2.get(taskId);
					if (ObjectUtils.isNotNull(list4)) {
						list4.get(0).setRqcReLineId(line.getRqcReLineId());
					}
				}
			}
			// 修改的id
			if (ObjectUtils.isNotNull(list3)) {
				Set<String> collect = list3.stream().map(RqcReLinesDTO::getTaskReRqcId).collect(Collectors.toSet());
				String[] array = collect.toArray(new String[collect.size()]);
				Criteria crt = Criteria.forClass(RqcReLines.class);
				crt.setRestriction(Restrictions.in("taskReRqcId", array));
				String[] includes = { "rqcReLineId", "taskReRqcId" };
				FieldSelector include = FieldSelector.newInstance().include(includes);
				List<RqcReLines> findFieldsByCriteria = this.mapper.findFieldsByCriteria(include, crt);
				if (findFieldsByCriteria != null) {
					for (RqcReLines lines : findFieldsByCriteria) {
						String taskId = lines.getTaskReRqcId();
						List<RqcReLinesDTO> list5 = collect2.get(taskId);
						if (ObjectUtils.isNotNull(list5)) {
							list5.get(0).setRqcReLineId(lines.getRqcReLineId());
						}
					}
				}
			}
		}

	}

	private void updateTaskPoolTimes(List<Map<String, Object>> taskIdMapList, UserDTO userDTO, Date now) {
		if (ObjectUtils.isNull(taskIdMapList))
			return;
		for (Map<String, Object> map : taskIdMapList) {
			String taskId = (String) map.get("taskId");
			String executor = (String) map.get("executor");
			Integer times = (Integer) map.get("times");
			taskQcRecheckDetailMapper.updateMapById(taskId,
					MapUtils.put("times", times).put("executor", executor).getMap(), TaskQcRecheckDetail.class);
		}
	}

	private void excuteUpdate(List<RqcReLinesDTO> list3, UserDTO userDTO, Date now) {
		if (ObjectUtils.isNull(list3))
			return;

		Criteria crt = Criteria.forClass(RqcReLines.class);
		Map<String, Object> map = new HashMap<>();
		for (RqcReLinesDTO rqcReLinesDTO : list3) {
			map.clear();
			Integer times = rqcReLinesDTO.getTimes();
			map.put("times", times);
			String qcReReason = rqcReLinesDTO.getQcReReason();
			if (!StringUtils.isBlank(qcReReason))
				map.put("qcReReason", qcReReason);
			Date qcReDate = rqcReLinesDTO.getQcReDate();
			if (ObjectUtils.isNull(qcReDate))
				qcReDate = now;
			map.put("qcReDate", qcReDate);
			String qcReOpcode = rqcReLinesDTO.getQcReOpcode();
			if (StringUtils.isBlank(qcReOpcode))
				qcReOpcode = userDTO.getUsername();
			map.put("qcReOpcode", qcReOpcode);

			String qcReResult = rqcReLinesDTO.getQcReResult();
			if (!StringUtils.isBlank(qcReResult))
				map.put("qcReResult", qcReResult);
			String stockStatus = rqcReLinesDTO.getStockStatus();
			if (!StringUtils.isBlank(stockStatus))
				map.put("stockStatus", stockStatus);
			String qcReGrade2 = rqcReLinesDTO.getQcReGrade();
			if (!StringUtils.isBlank(qcReGrade2))
				map.put("qcReGrade2", qcReGrade2);
			String taskReRqcId = rqcReLinesDTO.getTaskReRqcId();

			// 复检人2
			Date qcReDate2 = rqcReLinesDTO.getQcReDate2();
			if (qcReDate2 == null)
				qcReDate2 = now;
			map.put("qcReDate2", qcReDate2);
			String qcReOpcode2 = rqcReLinesDTO.getQcReOpcode2();
			if (!StringUtils.isBlank(qcReOpcode2))
				map.put("qcReOpcode2", qcReOpcode2);
			String qcReResult2 = rqcReLinesDTO.getQcReResult2();
			if (!StringUtils.isBlank(qcReResult2))
				map.put("qcReResult2", qcReResult2);
			String qcReSuggestion2 = rqcReLinesDTO.getQcReSuggestion2();
			if (!StringUtils.isBlank(qcReSuggestion2))
				map.put("qcReSuggestion2", qcReSuggestion2);
			// 复检人3
			Date qcReDate3 = rqcReLinesDTO.getQcReDate3();
			if (qcReDate3 == null)
				qcReDate3 = now;
			map.put("qcReDate3", qcReDate3);
			String qcReOpcode3 = rqcReLinesDTO.getQcReOpcode3();
			if (!StringUtils.isBlank(qcReOpcode3))
				map.put("qcReOpcode3", qcReOpcode3);
			String qcReResult3 = rqcReLinesDTO.getQcReResult3();
			if (!StringUtils.isBlank(qcReResult3))
				map.put("qcReResult3", qcReResult3);
			String qcReSuggestion3 = rqcReLinesDTO.getQcReSuggestion3();
			if (!StringUtils.isBlank(qcReSuggestion3))
				map.put("qcReSuggestion3", qcReSuggestion3);

			String modifier = userDTO.getId();
			String modifierName = userDTO.getUsername();
			Date gmtModified = now;
			map.put("modifier", modifier);
			map.put("modifierName", modifierName);
			map.put("gmtModified", gmtModified);

			crt.setRestriction(Restrictions.eq("taskReRqcId", taskReRqcId));

			this.mapper.updateMapByCriteria(map, crt);
		}

	}

	private void excuteInsert(List<RqcReLines> list) {
		if (ObjectUtils.isNull(list))
			return;
		if (list.size() > 500) {
			try {
				DaoUtils.saveBatch(this.mapper, list, 500);
			} catch (Exception e) {
				logger.error("复检行保存失败,错误详情:", e);
				throw new BizException("批量保存复检行失败!");
			}
		} else {
			this.mapper.saveBatch(list);
		}

	}

	private void updateTaskPoolStatus(List<String> taskIds, UserDTO userDTO, Date now) {
		if (ObjectUtils.isNull(taskIds))
			return;
		// 更新质检池任务状态 为已执行
		Criteria criteria = new Criteria(TaskQcRecheckDetail.class);
		criteria.setRestriction(Restrictions.in("taskRecheckId", taskIds.toArray()));
		taskQcRecheckDetailMapper.updateMapByCriteria(MapUtils.put("status", "0").getMap(), criteria); // 置为已完成

	}

	private boolean canCommit(RqcReLinesDTO rqcReLinesDTO, int row) {
		// 任务是否已被处理了
		String taskReRqcId = rqcReLinesDTO.getTaskReRqcId();
		AssertUtils.isNotBlank(taskReRqcId, String.format("第[%d]行,任务id为空!", row));

		TaskQcRecheckDetail taskQcRecheckDetail = this.taskQcRecheckDetailMapper.findById(taskReRqcId,
				TaskQcRecheckDetail.class);
		AssertUtils.notNull(taskQcRecheckDetail, String.format("第[%d]行,复检任务id:[%s]查询的任务为空!", row, taskReRqcId));
		// 一个Task对应一个行
		Integer dataVersion = taskQcRecheckDetail.getDataVersion();
		String status = taskQcRecheckDetail.getStatus();
		Integer times = taskQcRecheckDetail.getTimes();
		if (!"1".equals(status))
			throw new BizException(String.format("第[%d]行,复检任务id:[%s]查询的任务已经执行完毕,请刷新重试!", row, taskReRqcId));
		if (null != times && times >= InboundBizConstant.DEFAULT_RECHECK_TIMES)
			throw new BizException(String.format("第[%d]行,复检任务id:[%s]查询的任务次数超出,请刷新重试!", row, taskReRqcId));
		rqcReLinesDTO.setTimes(times);
		Criteria criteria = new Criteria(TaskQcRecheckDetail.class);
		Restriction eq = Restrictions.eq("taskRecheckId", taskReRqcId);
		Restriction eq1 = Restrictions.eq("dataVersion", dataVersion);
		Restriction eq2 = Restrictions.eq("status", status);
		Restriction and = Restrictions.and(eq, eq1, eq2);
		criteria.setRestriction(and);
		long l = this.taskQcRecheckDetailMapper
				.updateMapByCriteria(MapUtils.put("dataVersion", dataVersion + 1).getMap(), criteria);
		if (l <= 0)
			return false;
		String refLineId = taskQcRecheckDetail.getRefLineId();
		rqcReLinesDTO.setRefLineId(refLineId);
		return true;
	}
//	private boolean canCommit(RqcReLinesDTO rqcReLinesDTO) {
//		// 任务是否已被处理了
//		String taskReRqcId = rqcReLinesDTO.getTaskReRqcId();
//		// 一个Task对应一个行
//		Criteria criteria = new Criteria(RqcReLines.class);
//		criteria.setRestriction(Restrictions.eq("taskReRqcId", taskReRqcId));
//		long l = this.mapper.findCountByCriteria(criteria);
//		if (l > 0)
//			return false;
//		return true;
//	}

	private void buildField(RqcReLines lines, boolean editFlag, UserDTO userDTO, Date now) {
		// 构建系统级字段
		this.setBaseSysField(lines, editFlag, userDTO, now);
		this.vaildateModelRepeated(lines, editFlag, userDTO);
	}

	private void buildDefaultField(RqcReLinesDTO rqcReLinesDTO, boolean editFlag, UserDTO userDTO) {
		// 构建默认字段
		String qcReStatus = rqcReLinesDTO.getQcReStatus();
		if (StringUtils.isBlank(qcReStatus)) {
			rqcReLinesDTO.setQcReStatus("1");// 默认复检状态1 确认
		}
	}

	private Restriction buildCondition(RqcReLinesDTO rqcReLinesDTO) {
		// 构建查询条件
		if (ObjectUtils.isNull(rqcReLinesDTO))
			return null;

		String rcvHeaderId = rqcReLinesDTO.getRcvHeaderId();
		String asnHeaderId = rqcReLinesDTO.getAsnHeaderId();
		String itemId = rqcReLinesDTO.getItemId();
		String stockStatus = rqcReLinesDTO.getStockStatus();
		String rqcReHeaderId = rqcReLinesDTO.getRqcReHeaderId();

		Restriction r0 = Restrictions.eq("enterpriseId", userUtils.getEnterpriseId());
		Restriction r1 = StringUtils.isBlank(rcvHeaderId) ? null : Restrictions.eq("rcvHeaderId", rcvHeaderId);
		Restriction r2 = StringUtils.isBlank(asnHeaderId) ? null : Restrictions.eq("asnHeaderId", asnHeaderId);
		Restriction r3 = StringUtils.isBlank(itemId) ? null : Restrictions.eq("itemId", itemId);
		Restriction r4 = StringUtils.isBlank(rqcReHeaderId) ? null : Restrictions.eq("rqcReHeaderId", rqcReHeaderId);
		Restriction r5 = StringUtils.isBlank(stockStatus) ? null : Restrictions.eq("stockStatus", stockStatus);

		return Restrictions.and(r0, r1, r2, r3, r4, r5);
	}

	@Override
	public Object add(RqcReLinesDTO rqcReLinesDTO) {
		// TODO vaildate
		UserDTO userDTO = userUtils.getUserDTO();
		RqcReLines rqcReLines = new RqcReLines();
		BeanUtils.copyProperties(rqcReLinesDTO, rqcReLines);
		this.vaildateModel(rqcReLines, false, userDTO);
		this.buildField(rqcReLines, false, userDTO);
		this.mapper.save(rqcReLines);
		return "";
	}

	@Override
	public Object edit(String pkId, RqcReLinesDTO rqcReLinesDTO) {
		// TODO
		UserDTO userDTO = userUtils.getUserDTO();
		RqcReLines rqcReLines = new RqcReLines();
		BeanUtils.copyProperties(rqcReLinesDTO, rqcReLines);
		this.vaildateModel(rqcReLines, true, userDTO);
		this.buildField(rqcReLines, true, userDTO);
		this.mapper.updateWithValidate(rqcReLines,
				p -> ObjectUtils.isNotNull(p) && ObjectUtils.isNotNull(p.getValue()));
		return "";
	}

	@Override
	@Transactional
	public Object delIds(List<String> pkIdList) {
		// TODO
		pkIdList.stream().filter(p -> canEdit(p)).forEach(p -> this.mapper.deleteById(p, RqcReLines.class));
		return "";
	}

	@Override
	public Object deletePkId(String pkId) {
		// TODO
		if (canEdit(pkId))
			throw new BizException("can not del");
		this.mapper.deleteById(pkId, RqcReLines.class);
		return "";
	}

	@Override
	@Transactional
	public Object batchAdd(List<RqcReLinesDTO> rqcReLinesDTOList) {
		// TODO
		UserDTO userDTO = userUtils.getUserDTO();
		for (RqcReLinesDTO rqcReLinesDTO : rqcReLinesDTOList) {
			RqcReLines rqcReLines = new RqcReLines();
			BeanUtils.copyProperties(rqcReLinesDTO, rqcReLines);
			this.vaildateModel(rqcReLines, false, userDTO);
			this.buildField(rqcReLines, false, userDTO);
			this.mapper.save(rqcReLines);
		}
		return "";
	}

	@Override
	@Transactional
	public Object batchEdit(List<RqcReLinesDTO> rqcReLinesDTOList) {
		// TODO
		UserDTO userDTO = userUtils.getUserDTO();
		for (RqcReLinesDTO rqcReLinesDTO : rqcReLinesDTOList) {
			RqcReLines rqcReLines = new RqcReLines();
			BeanUtils.copyProperties(rqcReLinesDTO, rqcReLines);
			this.vaildateModel(rqcReLines, true, userDTO);
			this.buildField(rqcReLines, true, userDTO);
			this.mapper.updateWithValidate(rqcReLines,
					p -> ObjectUtils.isNotNull(p) && ObjectUtils.isNotNull(p.getValue()));
		}
		return "";
	}

	@Override
	public List<RqcReLinesDTO> findAll(RqcReLinesDTO rqcReLinesDTO) {
		// TODO
		Criteria criteria = Criteria.forClass(RqcReLines.class);
		RqcReLinesDTO condition = rqcReLinesDTO;
		Restriction r = buildCondition(condition);
		criteria.setRestriction(r);
		Order order = Order.newInstance().desc("gmtModified");
		criteria.setOrder(order);
		List<RqcReLines> list = this.mapper.findByCriteria(criteria);
		List<RqcReLinesDTO> collect = Optional.ofNullable(list).orElse(Collections.emptyList()).stream().map(p -> {
			RqcReLinesDTO target = new RqcReLinesDTO();
			BeanUtils.copyProperties(p, target);
			return target;
		}).collect(Collectors.toList());
		return collect;
	}

	private RqcReLines findById(String id) {
		return this.mapper.findById(id, RqcReLines.class);
	}

	private void vaildateModel(RqcReLines rqcReLines, Boolean editFlag, UserDTO userDTO) {
		if (!editFlag) {
			// TODO add
		} else {
			// TODO
		}
	}

	private void buildField(RqcReLines lines, Boolean editFlag, UserDTO userDTO) {
		if (!editFlag) {
			// TODO add
		} else {
			// TODO edit
		}
		vaildateModelRepeated(lines, editFlag, userDTO);
	}

	private void vaildateModelRepeated(RqcReLines rqcReLines, Boolean editFlag, UserDTO userDTO) {
		if (!editFlag) {
			// TODO add
		} else {
			// TODO
		}
	}

	private boolean canEdit(String pkId) {
		boolean flag = true;
		RqcReLines rqcReLines = this.findById(pkId);
		AssertUtils.notNull(rqcReLines, "model empty!");
		// TODO
		if (true)
			flag = false;
		return flag;
	}

	@Override
	public Page<RqcReLinesDTO> findList(RqcDQueryVO queryVO) {
		queryVO.setEnterpriseId(userUtils.getEnterpriseId());
		this.startPage(queryVO);
		List<RqcReLinesDTO> list = this.mapper.findList(queryVO);
		return PageUtils.getPage(list);
	}

	@Override
	public RqcReLinesDTO findByPkId(String pkId) {
		AssertUtils.isNotBlank(pkId, "查询的主键id不可为空!");
		RqcReLinesDTO dto = this.mapper.findByPkId(pkId);
		AssertUtils.notNull(dto, "查询对象为空!");
		return dto;
	}

	/**
	 * 复检逻辑: 1.先判断任务被执行的次数 次数在默认复检次数内 第0次: 新增复检行操作 结论判断: 若是合格/不合格
	 * 直接结束掉任务,并推送任务到下一阶段(并标识复检已完成) 若是待处理, 更新任务池任务执行次数 第1次: 更新复检行操作
	 * (次数,以及覆盖上一次的结论和原因) 结论判断: 若是合格/不合格 直接结束任务,并推送任务到下一阶段(并标识复检已完成) 待处理,更新任务池的次数
	 * 次数不在默认范围 更新任务状态为不可执行
	 */
//	@Transactional
//	@Override
//	public Object batchAdd(List<RqcReLinesDTO> rqcReLinesDTOList, String masterId, UserDTO userDTO, Date now) {
//		// 前端需要把times给带过来
//		// 批量添加
//		// 批量添加
//		if (ObjectUtils.isNull(userDTO))
//			userDTO = userUtils.getUserDTO();
//		if (ObjectUtils.isNull(now))
//			now = new Date();
//		int row = 0;
//		boolean editFlag = Boolean.FALSE;
//		List<RqcReLines> list = new ArrayList<>();
//
//		// 更新完成状态的任务id
//		List<String> taskIds = new ArrayList<>();
//		// 需要往下推送结论的复检数据
//		List<RqcReLinesDTO> list2 = new ArrayList<>();
//		// 需要更新的复检行-对应任务id
//		List<String> taskId2 = new ArrayList<>();
//		// 需要更新的复检行
//		List<RqcReLinesDTO> list3 = new ArrayList<>();
//		// 需要更新次数的任务id
//		List<Map<String, Object>> taskIdMapList = new ArrayList<>();
//
//		for (RqcReLinesDTO rqcReLinesDTO : rqcReLinesDTOList) {
//			row++;
//
//			// 数据准确性计算
//			BigDecimal pieceQty = rqcReLinesDTO.getPieceQty();
//			BigDecimal conversionRate = rqcReLinesDTO.getConversionRate();
//			if (conversionRate == null) {
//				conversionRate = BigDecimal.ONE;
//				rqcReLinesDTO.setConversionRate(conversionRate);
//			}
//			if (pieceQty == null) {
//				pieceQty = BigDecimal.ZERO;
//				rqcReLinesDTO.setPieceQty(pieceQty);
//			}
//			BigDecimal caseQty = pieceQty.divide(conversionRate, 0, RoundingMode.DOWN); // 整件
//			BigDecimal multiply = caseQty.multiply(conversionRate);
//			BigDecimal oddPieceQty = pieceQty.subtract(multiply); // 零散
//			BigDecimal qty = pieceQty.divide(conversionRate, 4, RoundingMode.DOWN); // 包装总量
//			// 保证数据正确性再次进行计算
//			rqcReLinesDTO.setOddCaseQty(caseQty);
//			rqcReLinesDTO.setOddPieceQty(oddPieceQty);
//
//			String taskReRqcId = rqcReLinesDTO.getTaskReRqcId(); // 来源任务id
//			// 头表id
//			String rqcReHeaderId = rqcReLinesDTO.getRqcReHeaderId();
//			if (ObjectUtils.isNull(rqcReHeaderId))
//				rqcReLinesDTO.setRqcReHeaderId(masterId);
//
//			// 并发判断 乐观锁
//			if (!canCommit(rqcReLinesDTO, row))
//				throw new BizException(
//						String.format("第[%d]行任务保存复检行失败!taskReRqcId为[%s]的复检任务已处理,请刷新重试", row, taskReRqcId));
//
//			// 结论的默认处理
//			String stockStatus = rqcReLinesDTO.getStockStatus();
//			String qcReResult = rqcReLinesDTO.getQcReResult();
//			if (StringUtils.isBlank(stockStatus))
//				stockStatus = qcReResult;
//			if (StringUtils.isBlank(stockStatus))
//				stockStatus = "0"; // 默认合格
//			rqcReLinesDTO.setStockStatus(stockStatus);
//
//			Integer times = rqcReLinesDTO.getTimes();
//			if (times != null && times == InboundBizConstant.DEFAULT_RECHECK_TIMES - 1) {
//				// 最后一次质检
//				// 更新任务的状态为已完成
//				taskIds.add(taskReRqcId);
//				// 需要提交结论并推送任务到质检(质检任务标识中的有已复检的标识)
//				list2.add(rqcReLinesDTO);
//				// 需要更新的复检明细行
//				list3.add(rqcReLinesDTO);
//				rqcReLinesDTO.setTimes(InboundBizConstant.DEFAULT_RECHECK_TIMES);
//				taskId2.add(taskReRqcId);
//				continue;
//			}
//			if (times == null || times == 0) {
//				// 第一次复检
//				// 插入数据到复检明细行
//				this.buildDefaultField(rqcReLinesDTO, editFlag, userDTO);
//				this.vaildateModel(rqcReLinesDTO, editFlag, userDTO, row, validator, batchAddMsg);
//				RqcReLines lines = new RqcReLines();
//				BeanUtils.copyProperties(rqcReLinesDTO, lines);
//				this.buildField(lines, editFlag, userDTO, now);
//				// TODO 这里是否需要记录质检的次数及其操作信息
////				this.mapper.save(lines);
//				list.add(lines);
//
//				if (stockStatus.equals("0") || stockStatus.equals("1")) {
//					taskIds.add(taskReRqcId);
//					list2.add(rqcReLinesDTO);
//				}
//				Map<String, Object> map = new HashMap<>();
//				if (stockStatus.equals("2")) { // 待处理
//					// 更新次数为1
//					map.put("taskId", taskReRqcId);
//					map.put("times", 1);
//					taskIdMapList.add(map);
//				}
//				continue;
//			}
//			// 不是最终复检
//			// 更新复检行数据
//			list3.add(rqcReLinesDTO);
//			rqcReLinesDTO.setTimes(times + 1);
//			taskId2.add(taskReRqcId);
//			// 更新复检的任务次数
//			Map<String, Object> map2 = new HashMap<>();
//			map2.put("taskId", taskReRqcId);
//			map2.put("times", times + 1);
//			taskIdMapList.add(map2);
//		}
//
//		// 保存质检行
//		excuteInsert(list);
//
//		// 更新质检行
//		excuteUpdate(list3, userDTO, now);
//
//		// 更新任务池任务执行次数
//		this.updateTaskPoolTimes(taskIdMapList, userDTO, now);
//
//		// 更新任务池状态
//		this.updateTaskPoolStatus(taskIds, userDTO, now);
//
//		// 往下推送任务
//		String rcvHeaderId = rqcReLinesDTOList.get(0).getRcvHeaderId();
//
//		excutePushTask(list, list2, list3, rcvHeaderId);
//
//		return true;
//	}
}
