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<TmsCustomerInfo>()
|
.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<TmsVehicle>()
|
.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<TmsDriver>()
|
.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<TmsServiceProvider>()
|
.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<TmsConsignor>()
|
.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<String> errors = new ArrayList<>();
|
List<String> 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<TransportRouteVi> 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<TransportRouteVi> routes = transportRouteViMapper.selectList(wrapper);
|
|
Map<String, Object> 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<TmsCustomerInfo>()
|
.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<SysDictData> 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<TmsConsignor>()
|
.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<TmsConsignor>()
|
.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<TmsVehicle>()
|
.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<String, Object> 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<String, List<DictDataVO>> dictDataMap = new HashMap<>();
|
|
if (StringUtils.isNotEmpty(dictTypes)) {
|
String[] types = dictTypes.split(",");
|
for (String type : types) {
|
String trimmedType = type.trim();
|
// 调用Mapper按字典类型查询字典数据
|
List<SysDictData> dictDataList = sysDictDataMapper.selectDictDataByType(trimmedType);
|
|
// 转换为VO列表
|
List<DictDataVO> 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);
|
}
|
|
|
|
|
}
|