/**
 * BladeX Commercial License Agreement
 * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
 */
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 jakarta.annotation.Resource;
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.LoadingManageExcel;
import org.springblade.transport.mapper.LoadingManageMapper;
import org.springblade.transport.mapper.WaybillMapper;
import org.springblade.transport.pojo.entity.LoadingManage;
import org.springblade.transport.pojo.entity.Waybill;
import org.springblade.transport.pojo.vo.BusinessRemoveResultVO;
import org.springblade.transport.pojo.vo.LoadingManageVO;
import org.springblade.transport.service.ILoadingManageService;
import org.springblade.transport.support.TransportBusinessSupport;
import org.springblade.transport.wrapper.LoadingManageWrapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 配载管理 服务实现类
 *
 * @author Chill
 */
@Service
public class LoadingManageServiceImpl extends BaseServiceImpl<LoadingManageMapper, LoadingManage> implements ILoadingManageService {

	private static final String STATUS_DRAFT = "draft";
	private static final String STATUS_PENDING = "pending";
	private static final String STATUS_RUNNING = "running";
	private static final String STATUS_COMPLETED = "completed";
	private static final String STATUS_CANCELLED = "cancelled";
	private static final String CARRIER_SELF = "自运";
	private static final String CARRIER_COMPANY = "承运商";
	private static final String CARRIER_PLATFORM = "网签平台";

	@Resource
	private WaybillMapper waybillMapper;

	@Override
	public IPage<LoadingManageVO> selectLoadingManagePage(IPage<LoadingManage> page, LoadingManageVO loadingManage) {
		IPage<LoadingManage> entityPage = page(page, buildQuery(loadingManage));
		IPage<LoadingManageVO> voPage = LoadingManageWrapper.build().pageVO(entityPage);
		voPage.getRecords().forEach(this::fillReadonly);
		return voPage;
	}

	@Override
	public LoadingManageVO detail(Long id) {
		LoadingManageVO loadingManageVO = LoadingManageWrapper.build().entityVO(loadExists(id));
		fillReadonly(loadingManageVO);
		return loadingManageVO;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean saveDraft(LoadingManage loadingManage) {
		prepare(loadingManage);
		validate(loadingManage, true);
		if (Func.isNotEmpty(loadingManage.getId())) {
			LoadingManage oldRecord = loadEditable(loadingManage.getId(), true);
			loadingManage.setLoadingNo(oldRecord.getLoadingNo());
			loadingManage.setDeptId(oldRecord.getDeptId());
			loadingManage.setDeptName(oldRecord.getDeptName());
		}
		prepareCreateOrUpdate(loadingManage);
		loadingManage.setBusinessStatus(STATUS_DRAFT);
		if (Func.isEmpty(loadingManage.getLoadingNo())) {
			loadingManage.setLoadingNo(nextCode());
		}
		return saveOrUpdate(loadingManage);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean submit(LoadingManage loadingManage) {
		prepare(loadingManage);
		validate(loadingManage, false);
		List<Long> oldWaybillIds = new ArrayList<>();
		if (Func.isNotEmpty(loadingManage.getId())) {
			LoadingManage oldRecord = loadEditable(loadingManage.getId(), true);
			oldWaybillIds = waybillIds(oldRecord.getWaybillIdsJson());
			loadingManage.setLoadingNo(oldRecord.getLoadingNo());
			loadingManage.setDeptId(oldRecord.getDeptId());
			loadingManage.setDeptName(oldRecord.getDeptName());
		}
		prepareCreateOrUpdate(loadingManage);
		loadingManage.setBusinessStatus(STATUS_PENDING);
		if (Func.isEmpty(loadingManage.getLoadingNo())) {
			loadingManage.setLoadingNo(nextCode());
		}
		validateWaybillsAvailable(loadingManage, waybillIds(loadingManage.getWaybillIdsJson()));
		boolean result = saveOrUpdate(loadingManage);
		syncAssociatedWaybills(loadingManage, STATUS_PENDING, oldWaybillIds);
		return result;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public BusinessRemoveResultVO removeLoadingManage(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 (LoadingManage loadingManage : listByIds(idList)) {
			TransportBusinessSupport.assertCurrentDept(loadingManage.getDeptId(), "配载管理");
			if (!Objects.equals(loadingManage.getBusinessStatus(), STATUS_DRAFT)) {
				result.setSkippedCount(result.getSkippedCount() + 1);
				result.getSkippedCodes().add(loadingManage.getLoadingNo());
				continue;
			}
			deleteIdList.add(loadingManage.getId());
		}
		if (Func.isNotEmpty(deleteIdList)) {
			deleteLogic(deleteIdList);
		}
		result.setSuccessCount(deleteIdList.size());
		return result;
	}

	@Override
	public List<LoadingManageExcel> exportLoadingManage(LoadingManageVO loadingManage, String ids) {
		LambdaQueryWrapper<LoadingManage> queryWrapper = buildQuery(loadingManage);
		if (Func.isNotEmpty(ids)) {
			queryWrapper.in(LoadingManage::getId, Func.toLongList(ids));
		}
		return list(queryWrapper).stream().map(record -> {
			LoadingManageExcel excel = new LoadingManageExcel();
			BeanUtil.copyProperties(record, excel);
			excel.setCreateUserName(UserCache.getUserRealName(record.getCreateUser()));
			excel.setUpdateUserName(UserCache.getUserRealName(record.getUpdateUser()));
			excel.setMileage(formatMileage(record.getMileage()));
			return excel;
		}).toList();
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public LoadingManageVO copy(Long id) {
		LoadingManage source = loadEditable(id, true);
		LoadingManage target = new LoadingManage();
		target.setLoadingSubNos(source.getLoadingSubNos());
		target.setWaybillIdsJson(source.getWaybillIdsJson());
		target.setProjectId(source.getProjectId());
		target.setProjectName(source.getProjectName());
		target.setCustomerName(source.getCustomerName());
		target.setTransportType(source.getTransportType());
		target.setCargoType(source.getCargoType());
		target.setCargoName(source.getCargoName());
		target.setVehicleNo(source.getVehicleNo());
		target.setTrailerVehicleNo(source.getTrailerVehicleNo());
		target.setDriverName(source.getDriverName());
		target.setDriverPhone(source.getDriverPhone());
		target.setEscortName(source.getEscortName());
		target.setEscortPhone(source.getEscortPhone());
		target.setCarrierType(source.getCarrierType());
		target.setCarrierName(source.getCarrierName());
		target.setDepartureAddress(source.getDepartureAddress());
		target.setTransitAddress(source.getTransitAddress());
		target.setArrivalAddress(source.getArrivalAddress());
		target.setOriginalNo(source.getOriginalNo());
		target.setDataSource(source.getDataSource());
		target.setStartDate(source.getStartDate());
		target.setEndDate(source.getEndDate());
		target.setPlanName(source.getPlanName());
		target.setBatchNo(source.getBatchNo());
		target.setCurrentProcessNode(source.getCurrentProcessNode());
		target.setMileage(source.getMileage());
		target.setEstimatedStartDate(source.getEstimatedStartDate());
		target.setEstimatedEndDate(source.getEstimatedEndDate());
		target.setTaskRemark(source.getTaskRemark());
		target.setGoodsJson(source.getGoodsJson());
		target.setRouteJson(source.getRouteJson());
		target.setTaskInfoJson(source.getTaskInfoJson());
		target.setDeptId(source.getDeptId());
		target.setDeptName(source.getDeptName());
		target.setBusinessStatus(STATUS_DRAFT);
		target.setLoadingNo(nextCode());
		prepare(target);
		validate(target, true);
		save(target);
		return detail(target.getId());
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean reassign(LoadingManage loadingManage) {
		LoadingManage oldRecord = loadEditable(loadingManage.getId(), true);
		if (!Objects.equals(oldRecord.getBusinessStatus(), STATUS_PENDING)) {
			throw new ServiceException("仅待执行状态允许重新派单");
		}
		prepare(loadingManage);
		validate(loadingManage, false);
		List<Long> oldWaybillIds = waybillIds(oldRecord.getWaybillIdsJson());
		loadingManage.setLoadingNo(oldRecord.getLoadingNo());
		loadingManage.setDeptId(oldRecord.getDeptId());
		loadingManage.setDeptName(oldRecord.getDeptName());
		loadingManage.setBusinessStatus(STATUS_PENDING);
		validateWaybillsAvailable(loadingManage, waybillIds(loadingManage.getWaybillIdsJson()));
		boolean result = updateById(loadingManage);
		syncAssociatedWaybills(loadingManage, STATUS_PENDING, oldWaybillIds);
		return result;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean cancel(Long id) {
		LoadingManage loadingManage = loadEditable(id, true);
		if (!Objects.equals(loadingManage.getBusinessStatus(), STATUS_PENDING)) {
			throw new ServiceException("仅待执行状态允许取消");
		}
		loadingManage.setBusinessStatus(STATUS_CANCELLED);
		boolean result = updateById(loadingManage);
		releaseAssociatedWaybills(waybillIds(loadingManage.getWaybillIdsJson()), STATUS_PENDING);
		return result;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean complete(Long id) {
		LoadingManage loadingManage = loadEditable(id, true);
		if (!Objects.equals(loadingManage.getBusinessStatus(), STATUS_RUNNING)) {
			throw new ServiceException("仅进行中状态允许完成");
		}
		loadingManage.setBusinessStatus(STATUS_COMPLETED);
		boolean result = updateById(loadingManage);
		syncAssociatedWaybills(loadingManage, STATUS_COMPLETED, new ArrayList<>());
		return result;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public BusinessRemoveResultVO batchComplete(String ids) {
		List<Long> idList = Func.toLongList(ids);
		if (Func.isEmpty(idList)) {
			throw new ServiceException("请选择需要完成的数据");
		}
		BusinessRemoveResultVO result = new BusinessRemoveResultVO();
		for (LoadingManage loadingManage : listByIds(idList)) {
			try {
				complete(loadingManage.getId());
				result.setSuccessCount(result.getSuccessCount() + 1);
			} catch (Exception exception) {
				result.setSkippedCount(result.getSkippedCount() + 1);
				result.getSkippedCodes().add(loadingManage.getLoadingNo());
			}
		}
		return result;
	}

	private LambdaQueryWrapper<LoadingManage> buildQuery(LoadingManageVO loadingManage) {
		TransportBusinessSupport.validateAllDept(loadingManage.getAllDept(), "配载管理");
		LambdaQueryWrapper<LoadingManage> queryWrapper = Wrappers.<LoadingManage>lambdaQuery().eq(LoadingManage::getIsDeleted, 0);
		if (!Objects.equals(loadingManage.getAllDept(), 1)) {
			queryWrapper.eq(LoadingManage::getDeptId, TransportBusinessSupport.currentDeptId("配载管理"));
		} else if (Func.isNotEmpty(loadingManage.getDeptId())) {
			queryWrapper.eq(LoadingManage::getDeptId, loadingManage.getDeptId());
		}
		if (Func.isNotEmpty(loadingManage.getLoadingNo())) {
			queryWrapper.eq(LoadingManage::getLoadingNo, loadingManage.getLoadingNo());
		}
		if (Func.isNotEmpty(loadingManage.getWaybillNo())) {
			queryWrapper.like(LoadingManage::getLoadingSubNos, loadingManage.getWaybillNo());
		}
		if (Func.isNotEmpty(loadingManage.getProjectName())) {
			queryWrapper.like(LoadingManage::getProjectName, loadingManage.getProjectName());
		}
		if (Func.isNotEmpty(loadingManage.getCustomerName())) {
			queryWrapper.like(LoadingManage::getCustomerName, loadingManage.getCustomerName());
		}
		if (Func.isNotEmpty(loadingManage.getTransportType())) {
			queryWrapper.eq(LoadingManage::getTransportType, loadingManage.getTransportType());
		}
		if (Func.isNotEmpty(loadingManage.getCargoType())) {
			queryWrapper.like(LoadingManage::getCargoType, loadingManage.getCargoType());
		}
		if (Func.isNotEmpty(loadingManage.getDepartureAddress())) {
			queryWrapper.like(LoadingManage::getDepartureAddress, loadingManage.getDepartureAddress());
		}
		if (Func.isNotEmpty(loadingManage.getArrivalAddress())) {
			queryWrapper.like(LoadingManage::getArrivalAddress, loadingManage.getArrivalAddress());
		}
		if (Func.isNotEmpty(loadingManage.getCargoName())) {
			queryWrapper.like(LoadingManage::getCargoName, loadingManage.getCargoName());
		}
		if (Func.isNotEmpty(loadingManage.getDriverName())) {
			queryWrapper.like(LoadingManage::getDriverName, loadingManage.getDriverName());
		}
		if (Func.isNotEmpty(loadingManage.getVehicleNo())) {
			queryWrapper.like(LoadingManage::getVehicleNo, loadingManage.getVehicleNo());
		}
		if (Func.isNotEmpty(loadingManage.getOriginalNo())) {
			queryWrapper.like(LoadingManage::getOriginalNo, loadingManage.getOriginalNo());
		}
		if (Func.isNotEmpty(loadingManage.getCarrierName())) {
			queryWrapper.like(LoadingManage::getCarrierName, loadingManage.getCarrierName());
		}
		if (Func.isNotEmpty(loadingManage.getDataSource())) {
			queryWrapper.eq(LoadingManage::getDataSource, loadingManage.getDataSource());
		}
		if (Func.isNotEmpty(loadingManage.getStartDateStart())) {
			queryWrapper.ge(LoadingManage::getStartDate, loadingManage.getStartDateStart());
		}
		if (Func.isNotEmpty(loadingManage.getStartDateEnd())) {
			queryWrapper.le(LoadingManage::getStartDate, loadingManage.getStartDateEnd());
		}
		if (Func.isNotEmpty(loadingManage.getEndDateStart())) {
			queryWrapper.ge(LoadingManage::getEndDate, loadingManage.getEndDateStart());
		}
		if (Func.isNotEmpty(loadingManage.getEndDateEnd())) {
			queryWrapper.le(LoadingManage::getEndDate, loadingManage.getEndDateEnd());
		}
		if (Func.isNotEmpty(loadingManage.getBusinessStatus())) {
			queryWrapper.eq(LoadingManage::getBusinessStatus, loadingManage.getBusinessStatus());
		}
		if (Func.isNotEmpty(loadingManage.getPlanName())) {
			queryWrapper.like(LoadingManage::getPlanName, loadingManage.getPlanName());
		}
		if (Func.isNotEmpty(loadingManage.getBatchNo())) {
			queryWrapper.like(LoadingManage::getBatchNo, loadingManage.getBatchNo());
		}
		if (Func.isNotEmpty(loadingManage.getCurrentProcessNode())) {
			queryWrapper.like(LoadingManage::getCurrentProcessNode, loadingManage.getCurrentProcessNode());
		}
		queryWrapper.orderByDesc(LoadingManage::getCreateTime);
		return queryWrapper;
	}

	private void prepare(LoadingManage loadingManage) {
		loadingManage.setLoadingNo(TransportBusinessSupport.trimToNull(loadingManage.getLoadingNo()));
		loadingManage.setLoadingSubNos(TransportBusinessSupport.trimToNull(loadingManage.getLoadingSubNos()));
		loadingManage.setWaybillIdsJson(TransportBusinessSupport.trimToNull(loadingManage.getWaybillIdsJson()));
		loadingManage.setProjectName(TransportBusinessSupport.trimToNull(loadingManage.getProjectName()));
		loadingManage.setCustomerName(TransportBusinessSupport.trimToNull(loadingManage.getCustomerName()));
		loadingManage.setTransportType(TransportBusinessSupport.trimToNull(loadingManage.getTransportType()));
		loadingManage.setCargoType(TransportBusinessSupport.trimToNull(loadingManage.getCargoType()));
		loadingManage.setCargoName(TransportBusinessSupport.trimToNull(loadingManage.getCargoName()));
		loadingManage.setVehicleNo(TransportBusinessSupport.trimToNull(loadingManage.getVehicleNo()));
		loadingManage.setTrailerVehicleNo(TransportBusinessSupport.trimToNull(loadingManage.getTrailerVehicleNo()));
		loadingManage.setDriverName(TransportBusinessSupport.trimToNull(loadingManage.getDriverName()));
		loadingManage.setDriverPhone(TransportBusinessSupport.trimToNull(loadingManage.getDriverPhone()));
		loadingManage.setEscortName(TransportBusinessSupport.trimToNull(loadingManage.getEscortName()));
		loadingManage.setEscortPhone(TransportBusinessSupport.trimToNull(loadingManage.getEscortPhone()));
		loadingManage.setCarrierType(TransportBusinessSupport.trimToNull(loadingManage.getCarrierType()));
		loadingManage.setCarrierName(TransportBusinessSupport.trimToNull(loadingManage.getCarrierName()));
		loadingManage.setDepartureAddress(TransportBusinessSupport.trimToNull(loadingManage.getDepartureAddress()));
		loadingManage.setTransitAddress(TransportBusinessSupport.trimToNull(loadingManage.getTransitAddress()));
		loadingManage.setArrivalAddress(TransportBusinessSupport.trimToNull(loadingManage.getArrivalAddress()));
		loadingManage.setOriginalNo(TransportBusinessSupport.trimToNull(loadingManage.getOriginalNo()));
		loadingManage.setDataSource(TransportBusinessSupport.trimToNull(loadingManage.getDataSource()));
		loadingManage.setPlanName(TransportBusinessSupport.trimToNull(loadingManage.getPlanName()));
		loadingManage.setBatchNo(TransportBusinessSupport.trimToNull(loadingManage.getBatchNo()));
		loadingManage.setCurrentProcessNode(TransportBusinessSupport.trimToNull(loadingManage.getCurrentProcessNode()));
		loadingManage.setTaskRemark(TransportBusinessSupport.trimToNull(loadingManage.getTaskRemark()));
		loadingManage.setGoodsJson(TransportBusinessSupport.trimToNull(loadingManage.getGoodsJson()));
		loadingManage.setRouteJson(TransportBusinessSupport.trimToNull(loadingManage.getRouteJson()));
		loadingManage.setTaskInfoJson(TransportBusinessSupport.trimToNull(loadingManage.getTaskInfoJson()));
		loadingManage.setDeptName(TransportBusinessSupport.trimToNull(loadingManage.getDeptName()));
		if (Func.isEmpty(loadingManage.getDeptId())) {
			Dept dept = TransportBusinessSupport.currentDept("配载管理");
			loadingManage.setDeptId(dept.getId());
			loadingManage.setDeptName(dept.getDeptName());
		}
		if (Func.isEmpty(loadingManage.getBusinessStatus())) {
			loadingManage.setBusinessStatus(STATUS_DRAFT);
		}
	}

	private void validate(LoadingManage loadingManage, boolean draftMode) {
		TransportBusinessSupport.validateRequired(loadingManage.getLoadingSubNos(), "配载子单号不能为空");
		TransportBusinessSupport.validateRequired(loadingManage.getTransportType(), "运输类型不能为空");
		if (draftMode) {
			validateCommon(loadingManage);
			return;
		}
		TransportBusinessSupport.validateRequired(loadingManage.getCarrierType(), "承运类型不能为空");
		TransportBusinessSupport.validateRequired(loadingManage.getVehicleNo(), "车/船/航班/班列不能为空");
		TransportBusinessSupport.validateRequired(loadingManage.getDepartureAddress(), "发货地不能为空");
		TransportBusinessSupport.validateRequired(loadingManage.getArrivalAddress(), "到货地不能为空");
		if (Objects.equals(loadingManage.getCarrierType(), CARRIER_SELF)) {
			TransportBusinessSupport.validateRequired(loadingManage.getDriverName(), "司机不能为空");
			TransportBusinessSupport.validateRequired(loadingManage.getDriverPhone(), "联系电话不能为空");
		} else if (Objects.equals(loadingManage.getCarrierType(), CARRIER_COMPANY) || Objects.equals(loadingManage.getCarrierType(), CARRIER_PLATFORM)) {
			TransportBusinessSupport.validateRequired(loadingManage.getCarrierName(), "承运商不能为空");
		}
		TransportBusinessSupport.validateLength(loadingManage.getLoadingNo(), 100, "配载单号不能超过100字");
		TransportBusinessSupport.validateLength(loadingManage.getLoadingSubNos(), 200, "配载子单号不能超过200字");
		TransportBusinessSupport.validateLength(loadingManage.getProjectName(), 100, "项目名称不能超过100字");
		TransportBusinessSupport.validateLength(loadingManage.getCustomerName(), 100, "客户名称不能超过100字");
		TransportBusinessSupport.validateLength(loadingManage.getTransportType(), 100, "运输类型不能超过100字");
		TransportBusinessSupport.validateLength(loadingManage.getCargoType(), 100, "货物类型不能超过100字");
		TransportBusinessSupport.validateLength(loadingManage.getCargoName(), 100, "货物名称不能超过100字");
		TransportBusinessSupport.validateLength(loadingManage.getVehicleNo(), 100, "车/船/航班/班列不能超过100字");
		TransportBusinessSupport.validateLength(loadingManage.getDriverName(), 100, "司机不能超过100字");
		TransportBusinessSupport.validateLength(loadingManage.getDriverPhone(), 50, "联系电话不能超过50字");
		TransportBusinessSupport.validateLength(loadingManage.getTrailerVehicleNo(), 100, "挂车车牌号不能超过100字");
		TransportBusinessSupport.validateLength(loadingManage.getEscortName(), 100, "押运人不能超过100字");
		TransportBusinessSupport.validateLength(loadingManage.getEscortPhone(), 50, "押运人手机号不能超过50字");
		TransportBusinessSupport.validateLength(loadingManage.getCarrierType(), 50, "承运类型不能超过50字");
		TransportBusinessSupport.validateLength(loadingManage.getCarrierName(), 100, "承运商不能超过100字");
		TransportBusinessSupport.validateLength(loadingManage.getDepartureAddress(), 100, "发货地不能超过100字");
		TransportBusinessSupport.validateLength(loadingManage.getTransitAddress(), 200, "途经地不能超过200字");
		TransportBusinessSupport.validateLength(loadingManage.getArrivalAddress(), 100, "到货地不能超过100字");
		TransportBusinessSupport.validateLength(loadingManage.getOriginalNo(), 100, "原始单号不能超过100字");
		TransportBusinessSupport.validateLength(loadingManage.getDataSource(), 100, "数据来源不能超过100字");
		TransportBusinessSupport.validateLength(loadingManage.getPlanName(), 100, "计划名称不能超过100字");
		TransportBusinessSupport.validateLength(loadingManage.getBatchNo(), 100, "运单批次号不能超过100字");
		TransportBusinessSupport.validateLength(loadingManage.getCurrentProcessNode(), 100, "过程节点不能超过100字");
		TransportBusinessSupport.validateLength(loadingManage.getWaybillIdsJson(), 4000, "运单ID集合不能超过4000字");
		TransportBusinessSupport.validateLength(loadingManage.getGoodsJson(), 8000, "货物信息不能超过8000字");
		TransportBusinessSupport.validateLength(loadingManage.getRouteJson(), 8000, "路线信息不能超过8000字");
		TransportBusinessSupport.validateLength(loadingManage.getTaskInfoJson(), 8000, "任务信息不能超过8000字");
		TransportBusinessSupport.validateLength(loadingManage.getTaskRemark(), 500, "任务备注不能超过500字");
		TransportBusinessSupport.validatePhone(loadingManage.getDriverPhone(), "联系电话格式不正确");
		TransportBusinessSupport.validatePhone(loadingManage.getEscortPhone(), "押运人手机号格式不正确");
		TransportBusinessSupport.validateNonNegative(loadingManage.getMileage(), "里程");
		TransportBusinessSupport.validateDateRange(loadingManage.getStartDate(), loadingManage.getEndDate(), "开始日期不能晚于结束日期");
		if (loadingManage.getEstimatedStartDate() != null && loadingManage.getEstimatedEndDate() != null
			&& loadingManage.getEstimatedEndDate().isBefore(loadingManage.getEstimatedStartDate())) {
			throw new ServiceException("预计完成日期不能早于预计发货日期");
		}
	}

	private void validateCommon(LoadingManage loadingManage) {
		TransportBusinessSupport.validateLength(loadingManage.getLoadingNo(), 100, "配载单号不能超过100字");
		TransportBusinessSupport.validateLength(loadingManage.getLoadingSubNos(), 200, "配载子单号不能超过200字");
		TransportBusinessSupport.validateLength(loadingManage.getProjectName(), 100, "项目名称不能超过100字");
		TransportBusinessSupport.validateLength(loadingManage.getCustomerName(), 100, "客户名称不能超过100字");
		TransportBusinessSupport.validateLength(loadingManage.getTransportType(), 100, "运输类型不能超过100字");
		TransportBusinessSupport.validateLength(loadingManage.getWaybillIdsJson(), 4000, "运单ID集合不能超过4000字");
		TransportBusinessSupport.validateLength(loadingManage.getGoodsJson(), 8000, "货物信息不能超过8000字");
		TransportBusinessSupport.validateLength(loadingManage.getRouteJson(), 8000, "路线信息不能超过8000字");
		TransportBusinessSupport.validateLength(loadingManage.getTaskInfoJson(), 8000, "任务信息不能超过8000字");
		TransportBusinessSupport.validateLength(loadingManage.getTaskRemark(), 500, "任务备注不能超过500字");
	}

	private void validateWaybillsAvailable(LoadingManage loadingManage, List<Long> waybillIds) {
		if (Func.isEmpty(waybillIds)) {
			throw new ServiceException("请选择待配载运单");
		}
		List<Waybill> waybills = waybillMapper.selectList(Wrappers.<Waybill>lambdaQuery()
			.eq(Waybill::getIsDeleted, 0)
			.in(Waybill::getId, waybillIds));
		if (waybills.size() != waybillIds.size()) {
			throw new ServiceException("待配载运单不存在或已删除");
		}
		for (Waybill waybill : waybills) {
			TransportBusinessSupport.assertCurrentDept(waybill.getDeptId(), "运单管理");
			if (Func.isNotEmpty(waybill.getLoadingNo()) && !Objects.equals(waybill.getLoadingNo(), loadingManage.getLoadingNo())) {
				throw new ServiceException("运单" + waybill.getWaybillNo() + "已关联其他配载单");
			}
			if ((Objects.equals(waybill.getBusinessStatus(), STATUS_COMPLETED) || Objects.equals(waybill.getBusinessStatus(), STATUS_CANCELLED))
				&& !Objects.equals(waybill.getLoadingNo(), loadingManage.getLoadingNo())) {
				throw new ServiceException("运单" + waybill.getWaybillNo() + "当前状态不允许配载");
			}
		}
	}

	private void syncAssociatedWaybills(LoadingManage loadingManage, String status, List<Long> oldWaybillIds) {
		List<Long> newWaybillIds = waybillIds(loadingManage.getWaybillIdsJson());
		if (Func.isNotEmpty(oldWaybillIds)) {
			List<Long> removedIds = new ArrayList<>(oldWaybillIds);
			removedIds.removeAll(newWaybillIds);
			releaseAssociatedWaybills(removedIds, STATUS_PENDING);
		}
		if (Func.isEmpty(newWaybillIds)) {
			return;
		}
		waybillMapper.update(null, Wrappers.<Waybill>lambdaUpdate()
			.in(Waybill::getId, newWaybillIds)
			.set(Waybill::getLoadingNo, loadingManage.getLoadingNo())
			.set(Waybill::getBusinessStatus, status)
			.set(Waybill::getCarrierType, loadingManage.getCarrierType())
			.set(Waybill::getCarrierName, loadingManage.getCarrierName())
			.set(Waybill::getDriverName, loadingManage.getDriverName())
			.set(Waybill::getDriverPhone, loadingManage.getDriverPhone())
			.set(Waybill::getVehicleNo, loadingManage.getVehicleNo())
			.set(Waybill::getTrailerVehicleNo, loadingManage.getTrailerVehicleNo())
			.set(Waybill::getEscortName, loadingManage.getEscortName())
			.set(Waybill::getEscortPhone, loadingManage.getEscortPhone())
			.set(Waybill::getMileage, loadingManage.getMileage())
			.set(Waybill::getEstimatedStartTime, toDateTime(loadingManage.getEstimatedStartDate()))
			.set(Waybill::getEstimatedEndTime, toDateTime(loadingManage.getEstimatedEndDate()))
			.set(Waybill::getTaskRemark, loadingManage.getTaskRemark())
			.set(Waybill::getTaskInfoJson, loadingManage.getTaskInfoJson())
			.set(Waybill::getCurrentProcessNode, loadingManage.getCurrentProcessNode()));
	}

	private void releaseAssociatedWaybills(List<Long> waybillIds, String status) {
		if (Func.isEmpty(waybillIds)) {
			return;
		}
		waybillMapper.update(null, Wrappers.<Waybill>lambdaUpdate()
			.in(Waybill::getId, waybillIds)
			.set(Waybill::getLoadingNo, null)
			.set(Waybill::getBusinessStatus, status));
	}

	private List<Long> waybillIds(String value) {
		if (Func.isEmpty(value)) {
			return new ArrayList<>();
		}
		try {
			Object parsed = JsonUtil.parse(value, List.class);
			if (parsed instanceof List<?> list) {
				return list.stream()
					.filter(Objects::nonNull)
					.map(item -> Long.valueOf(String.valueOf(item)))
					.toList();
			}
		} catch (Exception ignored) {
			// 兼容历史逗号分隔存储。
		}
		return Func.toLongList(value);
	}

	private LocalDateTime toDateTime(LocalDate date) {
		return date == null ? null : date.atStartOfDay();
	}

	private LoadingManage loadExists(Long id) {
		if (Func.isEmpty(id)) {
			throw new ServiceException("主键不能为空");
		}
		LoadingManage loadingManage = getById(id);
		if (Func.isEmpty(loadingManage) || Objects.equals(loadingManage.getIsDeleted(), 1)) {
			throw new ServiceException("配载管理不存在");
		}
		return loadingManage;
	}

	private LoadingManage loadEditable(Long id, boolean checkDept) {
		LoadingManage loadingManage = loadExists(id);
		if (checkDept) {
			TransportBusinessSupport.assertCurrentDept(loadingManage.getDeptId(), "配载管理");
		}
		if (Objects.equals(loadingManage.getBusinessStatus(), STATUS_COMPLETED) || Objects.equals(loadingManage.getBusinessStatus(), STATUS_CANCELLED)) {
			throw new ServiceException("当前状态不允许修改");
		}
		return loadingManage;
	}

	private void prepareCreateOrUpdate(LoadingManage loadingManage) {
		if (Func.isEmpty(loadingManage.getId())) {
			loadingManage.setLoadingNo(TransportBusinessSupport.trimToNull(loadingManage.getLoadingNo()));
		}
	}

	private void fillReadonly(LoadingManageVO loadingManageVO) {
		// 保持与现有业务模块一致，包装类已完成主字段映射，这里预留兼容入口。
	}

	private String formatMileage(BigDecimal mileage) {
		if (mileage == null) {
			return null;
		}
		BigDecimal value = mileage.setScale(2, RoundingMode.HALF_UP).stripTrailingZeros();
		return value.toPlainString();
	}

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

}
