package com.ruoyi.api.third.controller; import java.util.*; import java.util.stream.Collectors; import cn.hutool.json.JSONUtil; import com.ruoyi.cwgl.domain.DispatchOrder; import com.ruoyi.tms.mapper.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.entity.SysDictData; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.api.third.util.ApiAuthUtil; import com.ruoyi.system.mapper.SysDictDataMapper; import com.ruoyi.tms.domain.*; import com.ruoyi.tms.domain.vo.*; import com.ruoyi.tms.service.ITmsDispatchOrderService; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; /** * AI调度专用第三方接口 (无需权限, 使用appId/secret认证) * * @author ruoyi * @date 2026-03-06 */ @RestController @RequestMapping("/api/third/aiDispatch") public class TmsAiDispatchController extends BaseController { @Resource private TransportRouteViMapper transportRouteViMapper; @Resource private TmsCustomerInfoMapper tmsCustomerInfoMapper; @Resource private TmsServiceProviderMapper tmsServiceProviderMapper; @Resource private TmsDriverMapper tmsDriverMapper; @Resource private TmsVehicleMapper tmsVehicleMapper; @Resource private TmsConsignorMapper tmsConsignorMapper; @Autowired private ITmsDispatchOrderService tmsDispatchOrderService; @Autowired private SysDictDataMapper sysDictDataMapper; @Autowired private ApiAuthUtil apiAuthUtil; /** * 验证客户是否存在,且返回客户信息接口 * @param customerName * @return */ @GetMapping("/getCustomerData") public AjaxResult getCustomerData( @RequestParam(value = "customerName", required = true) String customerName){ logger.info("getCustomerData:{}",customerName); TmsCustomerInfo tmsCustomerInfo = tmsCustomerInfoMapper.selectOne(new LambdaQueryWrapper() .like(TmsCustomerInfo::getCustomerFullName, customerName) .last("limit 1") ); if (tmsCustomerInfo == null){ return error("客户不存在"); }else{ return AjaxResult.success(tmsCustomerInfo); } } /** * 验证车辆是否存在,且返回车辆信息接口 * @param licensePlate * @param licenseHk * @param licenseMo * @return */ @GetMapping("/getVehicleData") public AjaxResult getVehicleData( @RequestParam(value = "licensePlate", required = false) String licensePlate, @RequestParam(value = "licenseHk", required = false) String licenseHk, @RequestParam(value = "licenseMo", required = false) String licenseMo, @RequestParam(value = "serviceProviderId", required = false) Integer serviceProviderId ){ logger.info("getVehicleData:{},{},{}",licensePlate,licenseHk,licenseMo); TmsVehicle tmsCustomerInfo = tmsVehicleMapper.selectOne(new LambdaQueryWrapper() .like(StringUtils.isNotEmpty(licensePlate),TmsVehicle::getLicensePlate, licensePlate) .like(StringUtils.isNotEmpty(licenseHk),TmsVehicle::getLicenseHk, licenseHk) .like(StringUtils.isNotEmpty(licenseMo),TmsVehicle::getLicenseMo, licenseMo) .like(serviceProviderId != null,TmsVehicle::getServiceProviderId, serviceProviderId) .last("limit 1") ); if (tmsCustomerInfo == null){ return error("车辆不存在"); }else{ return AjaxResult.success(tmsCustomerInfo); } } /** * 获取司机接口 (公开接口, 无需认证) * 支持按名称筛选 * * @param driverName 司机名称(可选,模糊查询) * @return 司机列表 */ @GetMapping("/getDriverData") public AjaxResult getDriverData( @RequestParam(value = "driverName", required = true) String driverName, @RequestParam(value = "vehicleProviderId", required = false) Integer vehicleProviderId ) { logger.info("getVehicleData:{}",driverName); TmsDriver tmsDriver = tmsDriverMapper.selectOne(new LambdaQueryWrapper() .like(TmsDriver::getDriverName, driverName) .eq(vehicleProviderId !=null,TmsDriver::getVehicleProviderId, vehicleProviderId) .last("limit 1") ); if (tmsDriver == null){ return error("司机信息不存在"); }else{ return AjaxResult.success(tmsDriver); } } @GetMapping("/getVehicleProviderData") public AjaxResult getVehicleProviderData( @RequestParam(value = "serviceName", required = true) String serviceName) { logger.info("getVehicleProviderData:{}",serviceName); TmsServiceProvider provider = tmsServiceProviderMapper.selectOne(new LambdaQueryWrapper() .like(TmsServiceProvider::getServiceName, serviceName) .last("limit 1") ); if (provider == null){ return error("车辆服务商信息不存在"); }else{ return AjaxResult.success(provider); } } @GetMapping("/getConsignorData") public AjaxResult getConsignorData( @RequestParam(value = "consignorName", required = true) String consignorName, @RequestParam(value = "customerName", required = true) String customerName) { logger.info("getConsignorData:{}",consignorName); TmsConsignor tmsConsignor = tmsConsignorMapper.selectOne(new LambdaQueryWrapper() .like(TmsConsignor::getConsignorName, consignorName) .eq(TmsConsignor::getCustomerName, customerName) .last("limit 1") ); if (tmsConsignor == null){ return error("装卸货点不存在"); }else{ return AjaxResult.success(tmsConsignor); } } /** * 路线验证接口 (需要appId/secret认证) * * @param vo 路线验证请求 * @param request HTTP请求 * @return 验证结果 */ @PostMapping("/validateRoute") public AjaxResult validateRoute(@RequestBody RouteValidationVO vo, HttpServletRequest request) { logger.info("validateRoute:{}", JSONUtil.toJsonStr(vo)); AiValidationResultVO result = new AiValidationResultVO(); List errors = new ArrayList<>(); List warnings = new ArrayList<>(); // 验证必填字段 if (StringUtils.isEmpty(vo.getOriginCity())) { errors.add("起始城市不能为空"); } if (StringUtils.isEmpty(vo.getDestCity())) { errors.add("目的地城市不能为空"); } if (StringUtils.isEmpty(vo.getVehicleType())) { errors.add("车辆类型不能为空"); } // 如果有错误,直接返回 if (!errors.isEmpty()) { result.setIsValid(false); result.setErrors(errors); result.setWarnings(warnings); return success(result); } // 查询匹配的路线 LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); if (StringUtils.isNotEmpty(vo.getOriginCity())) { wrapper.like(TransportRouteVi::getTransportRoute, vo.getOriginCity()); } if (StringUtils.isNotEmpty(vo.getDestCity())) { wrapper.like(TransportRouteVi::getTransportRoute, vo.getDestCity()); } wrapper.eq(TransportRouteVi::getVehicleType, vo.getVehicleType()); wrapper.eq(TransportRouteVi::getCustomerFullName, vo.getCustomerName()); List routes = transportRouteViMapper.selectList(wrapper); Map data = new HashMap<>(); data.put("matchedRoutes", routes); data.put("routeCount", routes.size()); if (routes.isEmpty()) { warnings.add("未找到匹配的运输路线"); } result.setIsValid(routes.size() > 0); result.setErrors(errors); result.setWarnings(warnings); result.setData(data); return success(result); } /** * 创建调度单接口 (AI优化版, 需要appId/secret认证) * * @param order AI调度单请求对象 * @param request HTTP请求 * @return 创建结果 */ @PostMapping("/createOrder") public AjaxResult createOrder(@RequestBody TmsDispatchOrder order, HttpServletRequest request) { // appId/secret 认证 if (!apiAuthUtil.validateAppAuth(request)) { return apiAuthUtil.getUnauthorizedResult(); } try { // ========== 1. 客户验证 ========== if (StringUtils.isEmpty(order.getCustomerName())) { return error("客户名称不能为空"); } TmsCustomerInfo customer = tmsCustomerInfoMapper.selectOne(new LambdaQueryWrapper() .like(TmsCustomerInfo::getCustomerFullName, order.getCustomerName()) .last("limit 1") ); if (customer == null) { return error("客户不存在,请确认客户名称或先在系统中维护客户信息"); } order.setCustomerId(customer.getId()); // ========== 2. 车型验证 ========== if (StringUtils.isEmpty(order.getRequiredVehicleTypes())) { return error("车型不能为空"); } List vehicleTypes = sysDictDataMapper.selectDictDataByType("vehicle_type"); boolean vehicleTypeValid = vehicleTypes.stream() .anyMatch(dict -> dict.getDictValue().equals(order.getRequiredVehicleTypes())); if (!vehicleTypeValid) { return error("车型不存在,请确认车型或先在字典中维护车型数据"); } // ========== 3. 装货点验证(如果提供了装货点名称)========== if (StringUtils.isNotEmpty(order.getShipperName())) { TmsConsignor shipper = tmsConsignorMapper.selectOne(new LambdaQueryWrapper() .like(TmsConsignor::getConsignorName, order.getShipperName()) .eq(TmsConsignor::getCustomerName, order.getCustomerName()) .last("limit 1") ); if (shipper == null) { return error("装货点不存在,请确认装货点名称或先在系统中维护装货点信息"); } order.setShipperId(shipper.getId()); } // ========== 4. 卸货点验证(如果提供了卸货点名称)========== if (StringUtils.isNotEmpty(order.getReceiverName())) { TmsConsignor receiver = tmsConsignorMapper.selectOne(new LambdaQueryWrapper() .like(TmsConsignor::getConsignorName, order.getReceiverName()) .eq(TmsConsignor::getCustomerName, order.getCustomerName()) .last("limit 1") ); if (receiver == null) { return error("卸货点不存在,请确认卸货点名称或先在系统中维护卸货点信息"); } order.setReceiverId(receiver.getId()); } // ========== 5. 路线验证(AI必须传quoteDetailId)========== if (order.getQuoteDetailId() == null) { return error("报价明细ID不能为空,请先调用validateRoute接口获取可用路线并选择一条"); } // 根据quoteDetailId查询路线信息 TransportRouteVi route = transportRouteViMapper.selectById(order.getQuoteDetailId()); if (route == null) { return error("报价明细不存在,请确认报价明细ID是否正确"); } // 验证路线是否匹配客户 if (!route.getCustomerFullName().equals(order.getCustomerName())) { return error("报价明细与客户不匹配,请确认选择的路线是否属于该客户"); } // 验证路线是否匹配车型 Integer vehicleTypeInt = null; try { vehicleTypeInt = Integer.parseInt(order.getRequiredVehicleTypes()); } catch (NumberFormatException e) { return error("车型格式错误"); } if (!route.getVehicleType().equals(vehicleTypeInt)) { return error("报价明细与车型不匹配,请确认选择的路线车型是否正确"); } // 使用路线信息填充订单 order.setTransportLine(route.getTransportRoute()); order.setStartRegionCode(route.getStartRegionCode()); order.setEndRegionCode(route.getEndRegionCode()); order.setProjectId(route.getProjectId()); order.setProjectName(route.getProjectName()); order.setContractId(route.getContractId()); order.setContractName(route.getContractName()); // ========== 6. 订单类型判断 & 必填校验 ========== Integer orderType = order.getOrderType(); if (orderType == null) { return error("订单类型不能为空"); } // 基础订单(0):装货地+卸货地必须齐全 if (orderType == 0) { if (StringUtils.isEmpty(order.getShipperRegionLabel()) || StringUtils.isEmpty(order.getReceiverRegionLabel())) { return error("基础订单的装货地和卸货地不能为空"); } } // 预配订单(1):卸货地可为空,但装货地必须有 else if (orderType == 1) { if (StringUtils.isEmpty(order.getShipperRegionLabel())) { return error("预配订单的装货地不能为空"); } } // ========== 7. 派车信息验证 ========== // 7.1 司机验证(必填) if (order.getMainDriverId() == null) { return error("司机信息不能为空"); } TmsDriver driver = tmsDriverMapper.selectById(order.getMainDriverId()); if (driver == null) { return error("司机不存在,请确认司机信息"); } order.setMainDriverName(driver.getDriverName()); // 7.2 车辆验证(必填) if (StringUtils.isEmpty(order.getLicensePlate())) { return error("车牌号不能为空"); } TmsVehicle vehicle = tmsVehicleMapper.selectOne(new LambdaQueryWrapper() .eq(TmsVehicle::getLicensePlate, order.getLicensePlate()) .last("limit 1") ); if (vehicle == null) { return error("车辆不存在,请确认车牌号"); } order.setVehicleId(vehicle.getId()); order.setActualVehicleType(vehicle.getVehicleType()); // 7.3 运营模式验证 Integer operationMode = order.getOperationMode(); if (operationMode == null) { return error("运营模式不能为空(1=自营车队,0=外协)"); } // 7.4 外协模式:必须提供车辆服务商 if (operationMode == 0) { if (order.getVehicleProviderId() == null) { return error("外协模式下,车辆服务商不能为空"); } TmsServiceProvider provider = tmsServiceProviderMapper.selectById(order.getVehicleProviderId()); if (provider == null) { return error("车辆服务商不存在,请确认服务商信息"); } order.setVehicleProviderName(provider.getServiceName()); } // 设置初始状态为待分配 (0) order.setStatus(0); // 保存订单 int result = tmsDispatchOrderService.insertTmsDispatchOrder2(order); if (result > 0) { // 返回新创建的订单信息 Map data = new HashMap<>(); data.put("orderNo", order.getDispatchNo()); data.put("customerId", order.getCustomerId()); data.put("customerName", order.getCustomerName()); data.put("originCity", order.getShipperRegionLabel()); data.put("destCity", order.getReceiverRegionLabel()); data.put("transportLine", order.getTransportLine()); data.put("vehicleType", order.getRequiredVehicleTypes()); data.put("mainDriverName", order.getMainDriverName()); data.put("licensePlate", order.getLicensePlate()); data.put("operationMode", order.getOperationMode()); data.put("vehicleProviderName", order.getVehicleProviderName()); data.put("status", order.getStatus()); return success(data); } else { return error("创建调度单失败"); } } catch (Exception e) { logger.error("创建调度单异常: ", e); return error("创建调度单异常: " + e.getMessage()); } } /** * 获取字典数据接口 (公开接口, 无需认证) * 支持多个字典类型,用逗号分隔 * * @param dictTypes 字典类型,逗号分隔 * @return 字典数据 */ @GetMapping("/getDictData") public AjaxResult getDictData(@RequestParam(value = "dictTypes", required = false) String dictTypes) { Map> dictDataMap = new HashMap<>(); if (StringUtils.isNotEmpty(dictTypes)) { String[] types = dictTypes.split(","); for (String type : types) { String trimmedType = type.trim(); // 调用Mapper按字典类型查询字典数据 List dictDataList = sysDictDataMapper.selectDictDataByType(trimmedType); // 转换为VO列表 List voList = new ArrayList<>(); if (dictDataList != null && !dictDataList.isEmpty()) { voList = dictDataList.stream().map(dictData -> { DictDataVO vo = new DictDataVO( dictData.getDictLabel(), dictData.getDictValue(), dictData.getDictType() ); return vo; }).collect(Collectors.toList()); } dictDataMap.put(trimmedType, voList); } } return success(dictDataMap); } }