/**
 * BladeX Commercial License Agreement
 * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
 * <p>
 * Use of this software is governed by the Commercial License Agreement
 * obtained after purchasing a license from BladeX.
 * <p>
 * 1. This software is for development use only under a valid license
 * from BladeX.
 * <p>
 * 2. Redistribution of this software's source code to any third party
 * without a commercial license is strictly prohibited.
 * <p>
 * 3. Licensees may copyright their own code but cannot use segments
 * from this software for such purposes. Copyright of this software
 * remains with BladeX.
 * <p>
 * Using this software signifies agreement to this License, and the software
 * must not be used for illegal purposes.
 * <p>
 * Author: Chill Zhuang (bladejava@qq.com)
 */
package org.springblade.transport.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.AllArgsConstructor;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.tool.jackson.JsonUtil;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.system.cache.UserCache;
import org.springblade.system.pojo.entity.Dept;
import org.springblade.transport.excel.TransportPlanExcel;
import org.springblade.transport.excel.TransportPlanImportExcel;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import org.springblade.transport.mapper.TransportPlanMapper;
import org.springblade.transport.pojo.dto.TransportPlanDispatchRequest;
import org.springblade.transport.pojo.entity.TransportPlan;
import org.springblade.transport.pojo.entity.Waybill;
import org.springblade.transport.pojo.vo.BusinessRemoveResultVO;
import org.springblade.transport.pojo.vo.TransportPlanVO;
import org.springblade.transport.service.ITransportPlanService;
import org.springblade.transport.service.IWaybillService;
import org.springblade.transport.support.TransportBusinessSupport;
import org.springblade.transport.wrapper.TransportPlanWrapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Objects;

/**
 * 运输计划 服务实现类
 *
 * @author Chill
 */
@Service
@AllArgsConstructor
public class TransportPlanServiceImpl extends BaseServiceImpl<TransportPlanMapper, TransportPlan> implements ITransportPlanService {

	private final IWaybillService waybillService;

	@Override
	public IPage<TransportPlanVO> selectTransportPlanPage(IPage<TransportPlan> page, TransportPlanVO transportPlan) {
		IPage<TransportPlan> entityPage = page(page, buildQuery(transportPlan));
		return TransportPlanWrapper.build().pageVO(entityPage);
	}

	@Override
	public TransportPlanVO detail(Long id) {
		return TransportPlanWrapper.build().entityVO(loadEditable(id, false));
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean submit(TransportPlan transportPlan) {
		boolean created = Func.isEmpty(transportPlan.getId());
		if (!created) {
			TransportPlan oldRecord = loadEditable(transportPlan.getId(), true);
			transportPlan.setPlanNo(oldRecord.getPlanNo());
			transportPlan.setDeptId(oldRecord.getDeptId());
			transportPlan.setDeptName(oldRecord.getDeptName());
		}
		prepare(transportPlan);
		if (created && Func.isEmpty(transportPlan.getPlanNo())) {
			transportPlan.setPlanNo(nextCode());
		}
		validate(transportPlan);
		return saveOrUpdate(transportPlan);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public BusinessRemoveResultVO removeTransportPlan(String ids) {
		List<Long> idList = Func.toLongList(ids);
		if (Func.isEmpty(idList)) {
			throw new ServiceException("请选择需要删除的数据");
		}
		BusinessRemoveResultVO result = new BusinessRemoveResultVO();
		List<Long> deleteIdList = new ArrayList<>();
		for (TransportPlan transportPlan : listByIds(idList)) {
			TransportBusinessSupport.assertCurrentDept(transportPlan.getDeptId(), "运输计划");
			if (shouldSkipDelete(transportPlan)) {
				result.setSkippedCount(result.getSkippedCount() + 1);
				result.getSkippedCodes().add(transportPlan.getPlanNo());
				continue;
			}
			deleteIdList.add(transportPlan.getId());
		}
		if (Func.isNotEmpty(deleteIdList)) {
			deleteLogic(deleteIdList);
		}
		result.setSuccessCount(deleteIdList.size());
		return result;
	}

	@Override
	public List<TransportPlanExcel> exportTransportPlan(TransportPlanVO transportPlan, String ids) {
		LambdaQueryWrapper<TransportPlan> queryWrapper = buildQuery(transportPlan);
		if (Func.isNotEmpty(ids)) {
			queryWrapper.in(TransportPlan::getId, Func.toLongList(ids));
		}
		return list(queryWrapper).stream().map(record -> {
			TransportPlanExcel excel = new TransportPlanExcel();
			BeanUtil.copyProperties(record, excel);
			excel.setCreateUserName(UserCache.getUserRealName(record.getCreateUser()));
			excel.setUpdateUserName(UserCache.getUserRealName(record.getUpdateUser()));
			return excel;
		}).toList();
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public List<TransportPlanImportExcel> importTransportPlan(List<TransportPlanImportExcel> data, Long projectId, String projectName, Long contractId, String contractName, String customerName) {
		if (Func.isEmpty(data)) {
			throw new ServiceException("导入数据不能为空");
		}
		if (Func.isEmpty(projectId)) {
			throw new ServiceException("项目不能为空");
		}
		TransportBusinessSupport.validateRequired(projectName, "项目不能为空");
		if (Func.isEmpty(contractId)) {
			throw new ServiceException("客户合同不能为空");
		}
		TransportBusinessSupport.validateRequired(contractName, "客户合同不能为空");
		List<TransportPlanImportExcel> errorList = new ArrayList<>();
		for (int index = 0; index < data.size(); index++) {
			TransportPlanImportExcel excel = data.get(index);
			try {
				LocalDate planStartDate = parseImportDate(excel.getPlanStartDate(), "计划开始日期");
				LocalDate planEndDate = parseImportDate(excel.getPlanEndDate(), "计划结束日期");
				validateImportExcel(excel, planStartDate, planEndDate);
				TransportPlan transportPlan = new TransportPlan();
				transportPlan.setProjectId(projectId);
				transportPlan.setProjectName(projectName);
				transportPlan.setContractId(contractId);
				transportPlan.setContractName(contractName);
				transportPlan.setCustomerName(customerName);
				transportPlan.setPlanName(excel.getPlanName());
				transportPlan.setTransportType(excel.getTransportType());
				transportPlan.setPlanStartDate(planStartDate);
				transportPlan.setPlanEndDate(planEndDate);
				transportPlan.setDepartureAddress(excel.getDepartureAddress());
				transportPlan.setDepartureContact(excel.getDepartureContact());
				transportPlan.setDeparturePhone(excel.getDeparturePhone());
				transportPlan.setArrivalAddress(excel.getArrivalAddress());
				transportPlan.setArrivalContact(excel.getArrivalContact());
				transportPlan.setArrivalPhone(excel.getArrivalPhone());
				transportPlan.setRemark(excel.getRemark());
				transportPlan.setGoodsJson(JsonUtil.toJson(List.of(importGoods(excel))));
				transportPlan.setDataSource("批量导入");
				transportPlan.setBusinessStatus("waiting_dispatch");
				submit(transportPlan);
			} catch (Exception exception) {
				excel.setErrorMessage("第" + (index + 2) + "行：" + exception.getMessage());
				errorList.add(excel);
			}
		}
		return errorList;
	}

	private LinkedHashMap<String, Object> importGoods(TransportPlanImportExcel excel) {
		LinkedHashMap<String, Object> goods = new LinkedHashMap<>();
		goods.put("cargoName", excel.getCargoName());
		goods.put("cargoType", excel.getCargoType());
		goods.put("quantity", excel.getQuantity());
		goods.put("quantityUnit", excel.getQuantityUnit());
		goods.put("packageType", excel.getPackageType());
		goods.put("brand", excel.getBrand());
		goods.put("specification", excel.getSpecification());
		goods.put("model", excel.getModel());
		goods.put("materialCode", excel.getMaterialCode());
		goods.put("deviceCode", excel.getDeviceCode());
		return goods;
	}

	private void validateImportExcel(TransportPlanImportExcel excel, LocalDate planStartDate, LocalDate planEndDate) {
		TransportBusinessSupport.validateRequired(excel.getPlanName(), "计划名称不能为空");
		TransportBusinessSupport.validateRequired(excel.getTransportType(), "运输方式不能为空");
		TransportBusinessSupport.validateRequired(excel.getCargoType(), "货物类型不能为空");
		TransportBusinessSupport.validateRequired(excel.getDepartureAddress(), "发货地址不能为空");
		TransportBusinessSupport.validateRequired(excel.getArrivalAddress(), "收货地址不能为空");
		validateImportLength(excel.getPlanName(), 50, "计划名称");
		validateImportLength(excel.getDepartureAddress(), 255, "发货地址");
		validateImportLength(excel.getArrivalAddress(), 255, "收货地址");
		validateImportLength(excel.getDepartureContact(), 50, "发货联系人");
		validateImportLength(excel.getArrivalContact(), 50, "收货联系人");
		validateImportLength(excel.getDeparturePhone(), 50, "发货联系方式");
		validateImportLength(excel.getArrivalPhone(), 50, "收货联系方式");
		validateImportLength(excel.getRemark(), 200, "备注");
		TransportBusinessSupport.validatePhone(excel.getDeparturePhone(), "发货联系方式格式不正确");
		TransportBusinessSupport.validatePhone(excel.getArrivalPhone(), "收货联系方式格式不正确");
		TransportBusinessSupport.validateNonNegative(excel.getQuantity(), "数量");
		TransportBusinessSupport.validateDateRange(planStartDate, planEndDate, "计划结束日期不能早于计划开始日期");
	}

	private LocalDate parseImportDate(String value, String fieldName) {
		TransportBusinessSupport.validateRequired(value, fieldName + "不能为空");
		try {
			return LocalDate.parse(value.trim(), DateTimeFormatter.ISO_LOCAL_DATE);
		} catch (Exception exception) {
			throw new ServiceException(fieldName + "格式必须为 YYYY-MM-DD");
		}
	}

	private void validateImportLength(String value, int maxLength, String fieldName) {
		if (Func.isNotEmpty(value) && value.length() > maxLength) {
			throw new ServiceException(fieldName + "不能超过" + maxLength + "个字符");
		}
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public TransportPlanVO copy(Long id) {
		TransportPlan source = loadEditable(id, true);
		TransportPlan target = new TransportPlan();
		target.setPlanName(source.getPlanName());
		target.setProjectId(source.getProjectId());
		target.setProjectName(source.getProjectName());
		target.setContractId(source.getContractId());
		target.setContractName(source.getContractName());
		target.setCustomerName(source.getCustomerName());
		target.setTransportType(source.getTransportType());
		target.setPlanStartDate(source.getPlanStartDate());
		target.setPlanEndDate(source.getPlanEndDate());
		target.setDepartureAddressId(source.getDepartureAddressId());
		target.setDepartureName(source.getDepartureName());
		target.setDepartureAddress(source.getDepartureAddress());
		target.setDepartureContact(source.getDepartureContact());
		target.setDeparturePhone(source.getDeparturePhone());
		target.setArrivalAddressId(source.getArrivalAddressId());
		target.setArrivalName(source.getArrivalName());
		target.setArrivalAddress(source.getArrivalAddress());
		target.setArrivalContact(source.getArrivalContact());
		target.setArrivalPhone(source.getArrivalPhone());
		target.setGoodsJson(source.getGoodsJson());
		target.setAttachmentsJson(source.getAttachmentsJson());
		target.setDataSource(source.getDataSource());
		target.setBusinessStatus(source.getBusinessStatus());
		target.setRemark(source.getRemark());
		target.setPlanName(source.getPlanName() + " - 副本");
		target.setBusinessStatus("draft");
		target.setPlanNo(nextCode());
		prepare(target);
		validate(target);
		save(target);
		return detail(target.getId());
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public int dispatch(TransportPlanDispatchRequest request) {
		if (request == null || Func.isEmpty(request.getId())) {
			throw new ServiceException("运输计划主键不能为空");
		}
		if (Func.isEmpty(request.getWaybills())) {
			throw new ServiceException("请至少添加一条调度明细");
		}
		TransportPlan plan = loadEditable(request.getId(), true);
		if ("completed".equals(plan.getBusinessStatus()) || "cancelled".equals(plan.getBusinessStatus())) {
			throw new ServiceException("当前运输计划状态不允许调度");
		}
		if ("draft".equals(request.getMode())) {
			return 0;
		}
		for (Waybill waybill : request.getWaybills()) {
			if (waybill == null) {
				throw new ServiceException("调度明细不能为空");
			}
			waybill.setId(null);
			waybill.setPlanId(plan.getId());
			waybill.setPlanName(plan.getPlanName());
			waybill.setProjectId(plan.getProjectId());
			waybill.setProjectName(plan.getProjectName());
			waybill.setContractId(plan.getContractId());
			waybill.setContractName(plan.getContractName());
			waybill.setCustomerName(plan.getCustomerName());
			if (Func.isEmpty(waybill.getDepartureAddress())) {
				waybill.setDepartureAddressId(plan.getDepartureAddressId());
				waybill.setDepartureName(plan.getDepartureName());
				waybill.setDepartureAddress(plan.getDepartureAddress());
				waybill.setDepartureContact(plan.getDepartureContact());
				waybill.setDeparturePhone(plan.getDeparturePhone());
			}
			if (Func.isEmpty(waybill.getArrivalAddress())) {
				waybill.setArrivalAddressId(plan.getArrivalAddressId());
				waybill.setArrivalName(plan.getArrivalName());
				waybill.setArrivalAddress(plan.getArrivalAddress());
				waybill.setArrivalContact(plan.getArrivalContact());
				waybill.setArrivalPhone(plan.getArrivalPhone());
			}
			waybill.setAttachmentsJson(plan.getAttachmentsJson());
			waybill.setDataSource("计划调度");
			waybill.setBusinessStatus("pending");
			waybillService.submit(waybill);
		}
		plan.setBusinessStatus("dispatching");
		updateById(plan);
		return request.getWaybills().size();
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean cancel(Long id) {
		TransportPlan transportPlan = loadEditable(id, true);
		if ("completed".equals(transportPlan.getBusinessStatus()) || "cancelled".equals(transportPlan.getBusinessStatus())) {
			throw new ServiceException("当前状态不允许取消");
		}
		transportPlan.setBusinessStatus("cancelled");
		return updateById(transportPlan);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean complete(Long id) {
		TransportPlan transportPlan = loadEditable(id, true);
		if ("completed".equals(transportPlan.getBusinessStatus()) || "cancelled".equals(transportPlan.getBusinessStatus())) {
			throw new ServiceException("当前状态不允许完成");
		}
		transportPlan.setBusinessStatus("completed");
		return updateById(transportPlan);
	}

	private LambdaQueryWrapper<TransportPlan> buildQuery(TransportPlanVO transportPlan) {
		TransportBusinessSupport.validateAllDept(transportPlan.getAllDept(), "运输计划");
		LambdaQueryWrapper<TransportPlan> queryWrapper = Wrappers.<TransportPlan>lambdaQuery().eq(TransportPlan::getIsDeleted, 0);
		if (!Objects.equals(transportPlan.getAllDept(), 1)) {
			queryWrapper.eq(TransportPlan::getDeptId, TransportBusinessSupport.currentDeptId("运输计划"));
		} else if (Func.isNotEmpty(transportPlan.getDeptId())) {
			queryWrapper.eq(TransportPlan::getDeptId, transportPlan.getDeptId());
		}
		if (Func.isNotEmpty(transportPlan.getPlanNo())) {
			queryWrapper.eq(TransportPlan::getPlanNo, transportPlan.getPlanNo());
		}
		if (Func.isNotEmpty(transportPlan.getPlanName())) {
			queryWrapper.like(TransportPlan::getPlanName, transportPlan.getPlanName());
		}
		if (Func.isNotEmpty(transportPlan.getProjectName())) {
			queryWrapper.like(TransportPlan::getProjectName, transportPlan.getProjectName());
		}
		if (Func.isNotEmpty(transportPlan.getContractName())) {
			queryWrapper.like(TransportPlan::getContractName, transportPlan.getContractName());
		}
		if (Func.isNotEmpty(transportPlan.getCustomerName())) {
			queryWrapper.like(TransportPlan::getCustomerName, transportPlan.getCustomerName());
		}
		if (Func.isNotEmpty(transportPlan.getTransportType())) {
			queryWrapper.eq(TransportPlan::getTransportType, transportPlan.getTransportType());
		}
		if (Func.isNotEmpty(transportPlan.getDepartureName())) {
			queryWrapper.like(TransportPlan::getDepartureName, transportPlan.getDepartureName());
		}
		if (Func.isNotEmpty(transportPlan.getDepartureAddress())) {
			queryWrapper.like(TransportPlan::getDepartureAddress, transportPlan.getDepartureAddress());
		}
		if (Func.isNotEmpty(transportPlan.getDepartureContact())) {
			queryWrapper.like(TransportPlan::getDepartureContact, transportPlan.getDepartureContact());
		}
		if (Func.isNotEmpty(transportPlan.getDeparturePhone())) {
			queryWrapper.like(TransportPlan::getDeparturePhone, transportPlan.getDeparturePhone());
		}
		if (Func.isNotEmpty(transportPlan.getArrivalName())) {
			queryWrapper.like(TransportPlan::getArrivalName, transportPlan.getArrivalName());
		}
		if (Func.isNotEmpty(transportPlan.getArrivalAddress())) {
			queryWrapper.like(TransportPlan::getArrivalAddress, transportPlan.getArrivalAddress());
		}
		if (Func.isNotEmpty(transportPlan.getArrivalContact())) {
			queryWrapper.like(TransportPlan::getArrivalContact, transportPlan.getArrivalContact());
		}
		if (Func.isNotEmpty(transportPlan.getArrivalPhone())) {
			queryWrapper.like(TransportPlan::getArrivalPhone, transportPlan.getArrivalPhone());
		}
		if (Func.isNotEmpty(transportPlan.getDataSource())) {
			queryWrapper.eq(TransportPlan::getDataSource, transportPlan.getDataSource());
		}
		if (Func.isNotEmpty(transportPlan.getBusinessStatus())) {
			queryWrapper.eq(TransportPlan::getBusinessStatus, transportPlan.getBusinessStatus());
		}
		if (Func.isNotEmpty(transportPlan.getRemark())) {
			queryWrapper.like(TransportPlan::getRemark, transportPlan.getRemark());
		}
		queryWrapper.orderByDesc(TransportPlan::getCreateTime);
		return queryWrapper;
	}

	private void prepare(TransportPlan transportPlan) {
		transportPlan.setPlanNo(TransportBusinessSupport.trimToNull(transportPlan.getPlanNo()));
		transportPlan.setPlanName(TransportBusinessSupport.trimToNull(transportPlan.getPlanName()));
		transportPlan.setProjectName(TransportBusinessSupport.trimToNull(transportPlan.getProjectName()));
		transportPlan.setContractName(TransportBusinessSupport.trimToNull(transportPlan.getContractName()));
		transportPlan.setCustomerName(TransportBusinessSupport.trimToNull(transportPlan.getCustomerName()));
		transportPlan.setTransportType(TransportBusinessSupport.trimToNull(transportPlan.getTransportType()));
		transportPlan.setDepartureName(TransportBusinessSupport.trimToNull(transportPlan.getDepartureName()));
		transportPlan.setDepartureAddress(TransportBusinessSupport.trimToNull(transportPlan.getDepartureAddress()));
		transportPlan.setDepartureContact(TransportBusinessSupport.trimToNull(transportPlan.getDepartureContact()));
		transportPlan.setDeparturePhone(TransportBusinessSupport.trimToNull(transportPlan.getDeparturePhone()));
		transportPlan.setArrivalName(TransportBusinessSupport.trimToNull(transportPlan.getArrivalName()));
		transportPlan.setArrivalAddress(TransportBusinessSupport.trimToNull(transportPlan.getArrivalAddress()));
		transportPlan.setArrivalContact(TransportBusinessSupport.trimToNull(transportPlan.getArrivalContact()));
		transportPlan.setArrivalPhone(TransportBusinessSupport.trimToNull(transportPlan.getArrivalPhone()));
		transportPlan.setGoodsJson(TransportBusinessSupport.trimToNull(transportPlan.getGoodsJson()));
		transportPlan.setAttachmentsJson(TransportBusinessSupport.trimToNull(transportPlan.getAttachmentsJson()));
		transportPlan.setDataSource(TransportBusinessSupport.trimToNull(transportPlan.getDataSource()));
		transportPlan.setBusinessStatus(TransportBusinessSupport.trimToNull(transportPlan.getBusinessStatus()));
		transportPlan.setDeptName(TransportBusinessSupport.trimToNull(transportPlan.getDeptName()));
		transportPlan.setRemark(TransportBusinessSupport.trimToNull(transportPlan.getRemark()));
		if (Func.isEmpty(transportPlan.getDeptId())) {
			Dept dept = TransportBusinessSupport.currentDept("运输计划");
			transportPlan.setDeptId(dept.getId());
			transportPlan.setDeptName(dept.getDeptName());
		}
		if (transportPlan.getStatus() == null) { transportPlan.setStatus(1); }
		if (Func.isEmpty(transportPlan.getBusinessStatus())) { transportPlan.setBusinessStatus("waiting_dispatch"); }
	}

	private void validate(TransportPlan transportPlan) {
		TransportBusinessSupport.validateRequired(transportPlan.getPlanName(), "计划名称不能为空");
		TransportBusinessSupport.validateRequired(transportPlan.getProjectName(), "项目不能为空");
		TransportBusinessSupport.validateRequired(transportPlan.getContractName(), "客户合同不能为空");
		TransportBusinessSupport.validateRequired(transportPlan.getTransportType(), "运输类型不能为空");
		TransportBusinessSupport.validateRequired(transportPlan.getDepartureAddress(), "发货地址不能为空");
		TransportBusinessSupport.validateRequired(transportPlan.getArrivalAddress(), "收货地址不能为空");
		TransportBusinessSupport.validateRequired(transportPlan.getGoodsJson(), "货物信息不能为空");
		TransportBusinessSupport.validateLength(transportPlan.getPlanNo(), 255, "计划单号不能超过255字");
		TransportBusinessSupport.validateLength(transportPlan.getPlanName(), 255, "计划名称不能超过255字");
		TransportBusinessSupport.validateLength(transportPlan.getProjectName(), 255, "项目不能超过255字");
		TransportBusinessSupport.validateLength(transportPlan.getContractName(), 255, "客户合同不能超过255字");
		TransportBusinessSupport.validateLength(transportPlan.getCustomerName(), 255, "客户名称不能超过255字");
		TransportBusinessSupport.validateLength(transportPlan.getTransportType(), 255, "运输类型不能超过255字");
		TransportBusinessSupport.validateLength(transportPlan.getDepartureName(), 255, "发货地不能超过255字");
		TransportBusinessSupport.validateLength(transportPlan.getDepartureAddress(), 255, "发货地址不能超过255字");
		TransportBusinessSupport.validateLength(transportPlan.getDepartureContact(), 255, "发货联系人不能超过255字");
		TransportBusinessSupport.validateLength(transportPlan.getDeparturePhone(), 255, "发货联系方式不能超过255字");
		TransportBusinessSupport.validateLength(transportPlan.getArrivalName(), 255, "收货地不能超过255字");
		TransportBusinessSupport.validateLength(transportPlan.getArrivalAddress(), 255, "收货地址不能超过255字");
		TransportBusinessSupport.validateLength(transportPlan.getArrivalContact(), 255, "收货联系人不能超过255字");
		TransportBusinessSupport.validateLength(transportPlan.getArrivalPhone(), 255, "收货联系方式不能超过255字");
		TransportBusinessSupport.validateLength(transportPlan.getGoodsJson(), 8000, "货物信息不能超过8000字");
		TransportBusinessSupport.validateLength(transportPlan.getAttachmentsJson(), 8000, "附件不能超过8000字");
		TransportBusinessSupport.validateLength(transportPlan.getDataSource(), 255, "数据来源不能超过255字");
		TransportBusinessSupport.validateLength(transportPlan.getBusinessStatus(), 255, "业务状态不能超过255字");
		TransportBusinessSupport.validateLength(transportPlan.getDeptName(), 255, "所属组织不能超过255字");
		TransportBusinessSupport.validateLength(transportPlan.getRemark(), 500, "备注不能超过500字");
		TransportBusinessSupport.validateDateRange(transportPlan.getPlanStartDate(), transportPlan.getPlanEndDate(), "计划开始日期不能晚于计划结束日期");
	}

	private TransportPlan loadEditable(Long id, boolean checkDept) {
		if (Func.isEmpty(id)) {
			throw new ServiceException("主键不能为空");
		}
		TransportPlan transportPlan = getById(id);
		if (Func.isEmpty(transportPlan) || Objects.equals(transportPlan.getIsDeleted(), 1)) {
			throw new ServiceException("运输计划不存在");
		}
		if (checkDept) {
			TransportBusinessSupport.assertCurrentDept(transportPlan.getDeptId(), "运输计划");
		}
		return transportPlan;
	}

	private boolean shouldSkipDelete(TransportPlan transportPlan) {
		return false;
	}

	private synchronized String nextCode() {
		String prefix = "JH" + LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
		List<TransportPlan> latestList = list(Wrappers.<TransportPlan>lambdaQuery()
			.select(TransportPlan::getPlanNo)
			.likeRight(TransportPlan::getPlanNo, prefix)
			.orderByDesc(TransportPlan::getPlanNo)
			.last("LIMIT 1"));
		int next = 1;
		if (Func.isNotEmpty(latestList) && Func.isNotEmpty(latestList.get(0).getPlanNo())) {
			String serial = latestList.get(0).getPlanNo().substring(prefix.length());
			if (serial.chars().allMatch(Character::isDigit)) {
				next = Integer.parseInt(serial) + 1;
			}
		}
		return prefix + String.format("%03d", next);
	}

}
