diff --git a/pc-cattle-transportation/src/api/shipping.js b/pc-cattle-transportation/src/api/shipping.js index 8230717..4a82338 100644 --- a/pc-cattle-transportation/src/api/shipping.js +++ b/pc-cattle-transportation/src/api/shipping.js @@ -265,6 +265,15 @@ export function orderGetDetail(id) { }); } +// 批量更新订单的delivery_id字段 +export function updateOrderDeliveryId(data) { + return request({ + url: '/order/updateDeliveryId', + method: 'POST', + data, + }); +} + // 批量导入订单 export function orderBatchImport(data) { return request({ diff --git a/pc-cattle-transportation/src/views/entry/details.vue b/pc-cattle-transportation/src/views/entry/details.vue index 8bdd7be..f5cbac9 100644 --- a/pc-cattle-transportation/src/views/entry/details.vue +++ b/pc-cattle-transportation/src/views/entry/details.vue @@ -41,6 +41,7 @@ {{ data.baseInfo.endLocation || '-' }} {{ data.baseInfo.estimatedDeliveryTime || '-' }} {{ data.baseInfo.createTime || '' }} + {{ data.baseInfo.freight ? data.baseInfo.freight + ' 元' : '-' }} {{ totalRegisteredDevices }} 个 diff --git a/pc-cattle-transportation/src/views/shipping/createDeliveryDialog.vue b/pc-cattle-transportation/src/views/shipping/createDeliveryDialog.vue index 356fb6a..82820ac 100644 --- a/pc-cattle-transportation/src/views/shipping/createDeliveryDialog.vue +++ b/pc-cattle-transportation/src/views/shipping/createDeliveryDialog.vue @@ -14,10 +14,11 @@ > - + - - - - - - - - - - - - - - - - - @@ -224,6 +198,19 @@ /> + + + + + + + @@ -924,9 +911,9 @@ import { ref, reactive, computed, watch } from 'vue'; import { ElMessage } from 'element-plus'; import { ElImageViewer } from 'element-plus'; import { Plus, UploadFilled, Delete, ZoomIn } from '@element-plus/icons-vue'; -import { createDelivery, updateDeviceDeliveryId, orderPageQuery, getDeliveryDetail } from '@/api/shipping.js'; +import { createDelivery, updateDeviceDeliveryId, orderPageQuery, getDeliveryDetail, updateOrderDeliveryId } from '@/api/shipping.js'; import * as shippingApi from '@/api/shipping.js'; -import { memberListByType, driverList as fetchDriverList, vehicleList as fetchVehicleList } from '@/api/userManage.js'; +import { driverList as fetchDriverList, vehicleList as fetchVehicleList } from '@/api/userManage.js'; import { iotDeviceQueryList } from '@/api/hardware.js'; import { BaiduMap, BmMapType, BmMarker } from 'vue-baidu-map-3x'; import { useUserStore } from '../../store/user'; @@ -944,8 +931,6 @@ const isSubmitSuccess = ref(false); // 标记是否刚刚提交成功 const serverList = ref([]); const eartagList = ref([]); const collarList = ref([]); -const supplierList = ref([]); -const buyerList = ref([]); const driverOptions = ref([]); const vehicleOptions = ref([]); const orderList = ref([]); @@ -954,9 +939,7 @@ const showEndLocationMap = ref(false); const formData = reactive({ editId: null, // 编辑时的运单ID - orderId: null, - shipper: null, - buyer: null, + orderIds: [], // 关联订单ID数组(多选) plateNumber: null, driverId: null, // 司机ID(后端用) driverName: null, @@ -976,6 +959,7 @@ const formData = reactive({ endLat: '', endLon: '', cattleCount: 1, + freight: null, // 司机运费 quarantineCertNo: '', remark: '', // 装车相关字段 @@ -1039,9 +1023,7 @@ const validateArrivalTime = (rule, value, callback) => { }; const rules = { - orderId: [{ required: false, message: '请选择订单', trigger: 'change' }], - shipper: [{ required: true, message: '请选择发货方', trigger: 'change' }], - buyer: [{ required: true, message: '请选择采购方', trigger: 'change' }], + orderIds: [{ required: false, message: '请选择订单', trigger: 'change' }], plateNumber: [{ required: true, message: '请选择车牌号', trigger: 'change' }], driverId: [{ required: true, message: '请选择司机', trigger: 'change' }], driverPhone: [{ required: true, validator: validatePhone, trigger: 'blur' }], @@ -1086,15 +1068,13 @@ const buildSubmitData = () => { const data = { // 基本信息 - shipperId: formData.shipper, - buyerId: formData.buyer, plateNumber: formData.plateNumber, driverId: formData.driverId, // 传递司机ID给后端 driverName: driverNameStr, driverPhone: formData.driverPhone, - // 关联信息 - orderId: formData.orderId, + // 关联信息(将订单ID数组转换为逗号分隔的字符串) + orderId: formData.orderIds && formData.orderIds.length > 0 ? formData.orderIds.join(',') : null, // 设备ID serverId: formData.serverId, @@ -1120,6 +1100,7 @@ const buildSubmitData = () => { // 其他信息 cattleCount: formData.cattleCount, + freight: formData.freight, quarantineCertNo: formData.quarantineCertNo, remark: formData.remark, @@ -1157,7 +1138,7 @@ const buildSubmitData = () => { if (data[key] === undefined) { // 数值类型字段保留null,其他字段转换为空字符串 if (key === 'cattleCount' || - key === 'orderId' || key === 'shipperId' || key === 'buyerId' || + key === 'orderIds' || key === 'driverId' || key === 'serverId') { data[key] = null; } else { @@ -1182,7 +1163,6 @@ const open = async (editData = null) => { // 并行加载所有下拉列表数据 await Promise.all([ - loadSupplierAndBuyerList(), loadDeviceOptions(), loadDriverList(), loadVehicleList(), @@ -1257,12 +1237,21 @@ const fillFormWithEditData = (editData) => { const delivery = editData.delivery || editData; // 兼容两种数据结构 - // 基本信息 - formData.orderId = delivery.orderId || null; - - // 发货方和采购方:优先使用根级的 supplierId 和 buyerId - formData.shipper = editData.supplierId || (delivery.supplierId ? parseInt(delivery.supplierId) : null); - formData.buyer = editData.buyerId || delivery.buyerId || null; + // 基本信息(将逗号分隔的订单ID字符串转换为数组) + if (delivery.orderId) { + if (typeof delivery.orderId === 'string') { + // 如果是字符串,按逗号分割 + formData.orderIds = delivery.orderId.split(',').map(id => parseInt(id.trim())).filter(id => !isNaN(id)); + } else if (Array.isArray(delivery.orderId)) { + // 如果已经是数组,直接使用 + formData.orderIds = delivery.orderId; + } else { + // 如果是单个数字,转换为数组 + formData.orderIds = [delivery.orderId]; + } + } else { + formData.orderIds = []; + } // 车牌号 formData.plateNumber = delivery.licensePlate || ''; @@ -1326,6 +1315,9 @@ const fillFormWithEditData = (editData) => { // 牛只数量(从ratedQuantity字段映射) formData.cattleCount = delivery.ratedQuantity || 1; + // 司机运费 + formData.freight = delivery.freight || null; + // 照片 formData.quarantineTickeyUrl = delivery.quarantineTickeyUrl || ''; formData.poundListImg = delivery.poundListImg || ''; @@ -1352,25 +1344,6 @@ const fillFormWithEditData = (editData) => { ElMessage.success('已加载运单数据'); }; -// 加载供应商和采购方列表 -const loadSupplierAndBuyerList = async () => { - try { - // 加载供应商列表 (type=2) - const supplierRes = await memberListByType({ type: 2, pageNum: 1, pageSize: 9999 }); - if (supplierRes.code === 200) { - supplierList.value = supplierRes.data?.rows || supplierRes.data || []; - } - - // 加载采购方列表 (type=4) - const buyerRes = await memberListByType({ type: 4, pageNum: 1, pageSize: 9999 }); - if (buyerRes.code === 200) { - buyerList.value = buyerRes.data?.rows || buyerRes.data || []; - } - } catch (error) { - console.error('加载供应商/采购方列表失败', error); - } -}; - // 加载设备选项 const loadDeviceOptions = async () => { try { @@ -1464,121 +1437,11 @@ const loadOrderList = async () => { } }; -// 订单选择变化时自动填充发货方和采购方 -const handleOrderChange = async (orderId) => { - if (orderId) { - try { - // 从订单列表中查找选中的订单 - const order = orderList.value.find(item => item.id === orderId); - if (order) { - // 处理 buyerId:支持数字、字符串(逗号分隔)和数组格式,取第一个值 - let buyerId = null; - if (order.buyerId) { - if (typeof order.buyerId === 'number') { - buyerId = order.buyerId; - } else if (Array.isArray(order.buyerId) && order.buyerId.length > 0) { - buyerId = parseInt(order.buyerId[0]); - } else if (typeof order.buyerId === 'string' && order.buyerId.trim() !== '') { - const ids = order.buyerId.split(',').map(id => id.trim()).filter(id => id !== ''); - buyerId = ids.length > 0 ? parseInt(ids[0]) : null; - } - } - - // 处理 sellerId:支持数字、字符串(逗号分隔)和数组格式,取第一个值 - let sellerId = null; - if (order.sellerId) { - if (typeof order.sellerId === 'number') { - sellerId = order.sellerId; - } else if (Array.isArray(order.sellerId) && order.sellerId.length > 0) { - sellerId = parseInt(order.sellerId[0]); - } else if (typeof order.sellerId === 'string' && order.sellerId.trim() !== '') { - const ids = order.sellerId.split(',').map(id => id.trim()).filter(id => id !== ''); - sellerId = ids.length > 0 ? parseInt(ids[0]) : null; - } - } - - // 确保buyerId在buyerList中,如果不在则查询并添加 - if (buyerId && !buyerList.value.find(item => item.id === buyerId)) { - try { - // 查询所有类型的用户,找到对应的buyerId - const allBuyerRes = await memberListByType({ pageNum: 1, pageSize: 9999 }); - if (allBuyerRes.code === 200) { - const allBuyers = allBuyerRes.data?.rows || allBuyerRes.data || []; - const buyerInfo = allBuyers.find(item => item.id === buyerId); - if (buyerInfo) { - // 如果找到,添加到buyerList中 - buyerList.value.push(buyerInfo); - } else { - // 如果找不到,使用订单中的buyerName创建一个临时对象 - if (order.buyerName) { - buyerList.value.push({ - id: buyerId, - username: order.buyerName, - mobile: '' - }); - } - } - } - } catch (error) { - console.error('查询采购方信息失败', error); - // 如果查询失败,使用订单中的buyerName创建一个临时对象 - if (order.buyerName) { - buyerList.value.push({ - id: buyerId, - username: order.buyerName, - mobile: '' - }); - } - } - } - - // 确保sellerId在supplierList中,如果不在则查询并添加 - if (sellerId && !supplierList.value.find(item => item.id === sellerId)) { - try { - // 查询所有类型的用户,找到对应的sellerId - const allSupplierRes = await memberListByType({ pageNum: 1, pageSize: 9999 }); - if (allSupplierRes.code === 200) { - const allSuppliers = allSupplierRes.data?.rows || allSupplierRes.data || []; - const supplierInfo = allSuppliers.find(item => item.id === sellerId); - if (supplierInfo) { - // 如果找到,添加到supplierList中 - supplierList.value.push(supplierInfo); - } else { - // 如果找不到,使用订单中的sellerName创建一个临时对象 - if (order.sellerName) { - supplierList.value.push({ - id: sellerId, - username: order.sellerName, - mobile: '' - }); - } - } - } - } catch (error) { - console.error('查询发货方信息失败', error); - // 如果查询失败,使用订单中的sellerName创建一个临时对象 - if (order.sellerName) { - supplierList.value.push({ - id: sellerId, - username: order.sellerName, - mobile: '' - }); - } - } - } - - // 自动填充发货方(卖方)和采购方(买方) - formData.shipper = sellerId || null; - formData.buyer = buyerId || null; - - - ElMessage.success('已自动填充发货方和采购方信息'); - } - } catch (error) { - console.error('加载订单详情失败', error); - } - } else { - // 清除订单时不清除发货方和采购方,让用户手动选择 +// 订单选择变化时的处理(多选模式) +const handleOrderChange = async (orderIds) => { + // 多选模式下,orderIds 是数组 + if (orderIds && orderIds.length > 0) { + console.log('已选择订单:', orderIds); } }; @@ -1752,6 +1615,24 @@ const handleSubmit = () => { if (deliveryId) { // 更新设备的delivery_id await updateSelectedDevicesDeliveryId(deliveryId); + + // 更新关联订单的delivery_id + if (formData.orderIds && formData.orderIds.length > 0) { + try { + const updateOrderRes = await updateOrderDeliveryId({ + deliveryId: deliveryId, + orderIds: formData.orderIds + }); + if (updateOrderRes.code === 200) { + console.log('[ORDER-UPDATE] 成功更新订单delivery_id,订单数量:', formData.orderIds.length); + } else { + console.warn('[ORDER-UPDATE] 更新订单delivery_id失败:', updateOrderRes.msg); + } + } catch (orderError) { + console.error('[ORDER-UPDATE] 更新订单delivery_id异常:', orderError); + // 不阻止流程,只记录错误 + } + } } // 提交成功后立即清除对应模式的缓存 @@ -2037,9 +1918,7 @@ const handleClose = () => { // 编辑模式:保存到 editFormData[editId] deliveryFormStore.saveEditFormData(formData.editId, { editId: formData.editId, - orderId: formData.orderId, - shipper: formData.shipper, - buyer: formData.buyer, + orderIds: formData.orderIds, plateNumber: formData.plateNumber, driverId: formData.driverId, driverPhone: formData.driverPhone, @@ -2058,6 +1937,7 @@ const handleClose = () => { endLat: formData.endLat, endLon: formData.endLon, cattleCount: formData.cattleCount, + freight: formData.freight, quarantineCertNo: formData.quarantineCertNo, remark: formData.remark, emptyWeight: formData.emptyWeight, @@ -2082,9 +1962,7 @@ const handleClose = () => { } else { // 新增模式:保存到 newFormData deliveryFormStore.saveNewFormData({ - orderId: formData.orderId, - shipper: formData.shipper, - buyer: formData.buyer, + orderIds: formData.orderIds, plateNumber: formData.plateNumber, driverId: formData.driverId, driverPhone: formData.driverPhone, @@ -2103,6 +1981,7 @@ const handleClose = () => { endLat: formData.endLat, endLon: formData.endLon, cattleCount: formData.cattleCount, + freight: formData.freight, quarantineCertNo: formData.quarantineCertNo, remark: formData.remark, emptyWeight: formData.emptyWeight, @@ -2133,7 +2012,9 @@ const handleClose = () => { formRef.value?.resetFields(); // 清空所有表单字段 Object.keys(formData).forEach(key => { - if (key === 'orderId' || key === 'editId') { + if (key === 'orderIds') { + formData[key] = []; + } else if (key === 'editId') { formData[key] = null; } else if (typeof formData[key] === 'number') { formData[key] = key === 'cattleCount' ? 1 : null; @@ -2177,9 +2058,7 @@ watch( // 编辑模式:保存到 editFormData[editId] deliveryFormStore.saveEditFormData(formData.editId, { editId: formData.editId, - orderId: formData.orderId, - shipper: formData.shipper, - buyer: formData.buyer, + orderIds: formData.orderIds, plateNumber: formData.plateNumber, driverId: formData.driverId, driverPhone: formData.driverPhone, @@ -2222,15 +2101,11 @@ watch( } else { // 新增模式:保存到 newFormData console.log('[CACHE] 保存新增模式数据:', { - shipper: formData.shipper, - buyer: formData.buyer, plateNumber: formData.plateNumber, driverId: formData.driverId, }); deliveryFormStore.saveNewFormData({ - orderId: formData.orderId, - shipper: formData.shipper, - buyer: formData.buyer, + orderIds: formData.orderIds, plateNumber: formData.plateNumber, driverId: formData.driverId, driverPhone: formData.driverPhone, diff --git a/pc-cattle-transportation/src/views/shipping/loadingOrder.vue b/pc-cattle-transportation/src/views/shipping/loadingOrder.vue index 7782b15..19f99dd 100644 --- a/pc-cattle-transportation/src/views/shipping/loadingOrder.vue +++ b/pc-cattle-transportation/src/views/shipping/loadingOrder.vue @@ -55,6 +55,26 @@ {{ scope.row.settlementTypeDesc || '--' }} + + + + + + + + + + + + - + @@ -92,18 +158,30 @@ import { ref, reactive, onMounted } from 'vue'; import { ElMessage, ElMessageBox } from 'element-plus'; import { ArrowDown } from '@element-plus/icons-vue'; +import { useRouter } from 'vue-router'; import * as XLSX from 'xlsx'; import baseSearch from '@/components/common/searchCustom/index.vue'; import Pagination from '@/components/Pagination/index.vue'; import { orderPageQuery, orderDelete, orderBatchImport } from '@/api/shipping.js'; import { memberListByType, userAdd } from '@/api/userManage.js'; +import { getYingyanTrack, waybillDetail } from '@/api/abroad.js'; +import { BMPGL } from '@/utils/loadBmap.js'; +import { nextTick } from 'vue'; import OrderDialog from './orderDialog.vue'; import CreateDeliveryDialog from './createDeliveryDialog.vue'; +const router = useRouter(); const baseSearchRef = ref(); const OrderDialogRef = ref(); const CreateDeliveryDialogRef = ref(); const fileInputRef = ref(); + +// 轨迹相关 +const trackDialogVisible = ref(false); +const trackLoading = ref(false); +const trackPath = ref([]); +const deliveryStatus = ref(null); +const trackMapInstance = ref(null); const formItemList = reactive([ { label: '买方', @@ -119,6 +197,34 @@ const formItemList = reactive([ span: 6, placeholder: '请输入卖方', }, + { + label: '运单号', + type: 'input', + param: 'deliveryNumber', + span: 6, + placeholder: '请输入运单号', + }, + { + label: '起始地', + type: 'input', + param: 'startLocation', + span: 6, + placeholder: '请输入起始地', + }, + { + label: '目的地', + type: 'input', + param: 'endLocation', + span: 6, + placeholder: '请输入目的地', + }, + { + label: '单价', + type: 'input', + param: 'firmPrice', + span: 6, + placeholder: '请输入单价', + }, { label: '结算方式', type: 'select', @@ -254,6 +360,169 @@ const showCreateDeliveryDialog = () => { } }; +// 查看运送清单详情 +const viewDeliveryDetail = (deliveryId) => { + if (!deliveryId) { + ElMessage.warning('运单ID不存在'); + return; + } + router.push({ + path: '/entry/details', + query: { + id: deliveryId + } + }); +}; + +// 查看轨迹 +const viewTrack = async (deliveryId, status) => { + if (!deliveryId) { + ElMessage.warning('运单ID不存在,无法查看轨迹'); + return; + } + + // 判断状态:status >= 2 可查看轨迹 + if (status == null || status < 2) { + ElMessage.warning('该运单尚未开始运输,暂无轨迹数据'); + return; + } + + trackDialogVisible.value = true; + trackLoading.value = true; + trackPath.value = []; + deliveryStatus.value = status; + + try { + const res = await getYingyanTrack({ deliveryId: deliveryId }); + if (res.code === 200 && res.data) { + const rawPoints = Array.isArray(res.data.trackPoints) ? res.data.trackPoints : []; + trackPath.value = rawPoints + .map(item => { + const lng = parseFloat(item.longitude ?? item.lng ?? 0); + const lat = parseFloat(item.latitude ?? item.lat ?? 0); + if (Number.isNaN(lng) || Number.isNaN(lat) || lng === 0 || lat === 0) { + return null; + } + return { + lng, + lat, + locTime: item.locTime + }; + }) + .filter(Boolean); + + if (trackPath.value.length === 0) { + ElMessage.warning('暂无有效轨迹点'); + trackLoading.value = false; + return; + } + + // 初始化地图 + await nextTick(); + await initTrackMap(); + } else { + ElMessage.warning(res.msg || '暂无轨迹数据'); + trackLoading.value = false; + } + } catch (error) { + console.error('加载轨迹数据失败:', error); + ElMessage.error('加载轨迹数据失败'); + trackLoading.value = false; + } +}; + +// 初始化轨迹地图 +const initTrackMap = async () => { + if (trackPath.value.length === 0) { + trackLoading.value = false; + return; + } + + try { + const BMapGL = await BMPGL('fLz8UwJSM3ayYl6dtsWYp7TQ8993R6kC'); + + // 创建地图实例 + trackMapInstance.value = new BMapGL.Map('trackMap'); + + // 计算地图中心点和缩放级别 + const bounds = calculateBounds(trackPath.value); + const centerPoint = new BMapGL.Point(bounds.center.lng, bounds.center.lat); + trackMapInstance.value.centerAndZoom(centerPoint, bounds.zoom); + trackMapInstance.value.enableScrollWheelZoom(true); + + // 绘制轨迹线 + const polyline = new BMapGL.Polyline( + trackPath.value.map(p => new BMapGL.Point(p.lng, p.lat)), + { + strokeColor: '#3388ff', + strokeWeight: 4, + strokeOpacity: 0.8 + } + ); + trackMapInstance.value.addOverlay(polyline); + + // 添加起点标记 + if (trackPath.value.length > 0) { + const startPoint = new BMapGL.Point(trackPath.value[0].lng, trackPath.value[0].lat); + const startMarker = new BMapGL.Marker(startPoint); + trackMapInstance.value.addOverlay(startMarker); + } + + // 添加终点标记 + if (trackPath.value.length > 1) { + const endPoint = new BMapGL.Point( + trackPath.value[trackPath.value.length - 1].lng, + trackPath.value[trackPath.value.length - 1].lat + ); + const endMarker = new BMapGL.Marker(endPoint); + trackMapInstance.value.addOverlay(endMarker); + } + + trackLoading.value = false; + } catch (error) { + console.error('地图初始化失败:', error); + ElMessage.error('地图加载失败'); + trackLoading.value = false; + } +}; + +// 计算轨迹边界 +const calculateBounds = (points) => { + if (points.length === 0) { + return { center: { lng: 116.404, lat: 39.915 }, zoom: 15 }; + } + + let minLng = points[0].lng; + let maxLng = points[0].lng; + let minLat = points[0].lat; + let maxLat = points[0].lat; + + points.forEach(point => { + minLng = Math.min(minLng, point.lng); + maxLng = Math.max(maxLng, point.lng); + minLat = Math.min(minLat, point.lat); + maxLat = Math.max(maxLat, point.lat); + }); + + const center = { + lng: (minLng + maxLng) / 2, + lat: (minLat + maxLat) / 2 + }; + + // 计算缩放级别(简单估算) + const lngDiff = maxLng - minLng; + const latDiff = maxLat - minLat; + const maxDiff = Math.max(lngDiff, latDiff); + let zoom = 15; + if (maxDiff > 0.1) zoom = 10; + else if (maxDiff > 0.05) zoom = 11; + else if (maxDiff > 0.02) zoom = 12; + else if (maxDiff > 0.01) zoom = 13; + else if (maxDiff > 0.005) zoom = 14; + + return { center, zoom }; +}; + // 删除订单(逻辑删除) const del = (id) => { ElMessageBox.confirm('请确认是否删除订单?删除后将不可恢复', '提示', { diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/OrderController.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/OrderController.java index d69518f..8f8794a 100644 --- a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/OrderController.java +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/controller/OrderController.java @@ -155,5 +155,49 @@ public class OrderController { return AjaxResult.error("批量导入订单失败:" + e.getMessage()); } } + + /** + * 批量更新订单的delivery_id字段 + */ + @SaCheckPermission("order:edit") + @PostMapping("/updateDeliveryId") + public AjaxResult updateDeliveryId(@RequestBody Map params) { + try { + logger.info("批量更新订单delivery_id,参数:{}", params); + + Integer deliveryId = null; + if (params.get("deliveryId") != null) { + Object deliveryIdObj = params.get("deliveryId"); + if (deliveryIdObj instanceof Integer) { + deliveryId = (Integer) deliveryIdObj; + } else if (deliveryIdObj instanceof String) { + try { + deliveryId = Integer.parseInt((String) deliveryIdObj); + } catch (NumberFormatException e) { + logger.error("deliveryId格式不正确:{}", deliveryIdObj); + return AjaxResult.error("deliveryId格式不正确"); + } + } + } + + @SuppressWarnings("unchecked") + List orderIds = (List) params.get("orderIds"); + + if (deliveryId == null) { + logger.error("更新失败:运送清单ID不能为空"); + return AjaxResult.error("运送清单ID不能为空"); + } + + if (orderIds == null || orderIds.isEmpty()) { + logger.warn("订单ID列表为空,跳过更新"); + return AjaxResult.success("订单ID列表为空,无需更新"); + } + + return orderService.updateOrderDeliveryId(deliveryId, orderIds); + } catch (Exception e) { + logger.error("批量更新订单delivery_id失败:{}", e.getMessage(), e); + return AjaxResult.error("批量更新订单delivery_id失败:" + e.getMessage()); + } + } } diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliveryCreateDto.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliveryCreateDto.java index 84cad2f..438ab1f 100644 --- a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliveryCreateDto.java +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliveryCreateDto.java @@ -14,8 +14,8 @@ import java.util.List; */ @Data public class DeliveryCreateDto { - /** 关联订单ID */ - private Integer orderId; + /** 关联订单ID(多个订单ID用逗号分隔的字符串,如:"1,2,3") */ + private String orderId; /** * 发货方ID @@ -107,6 +107,11 @@ public class DeliveryCreateDto { @Min(value = 1, message = "牛只数量至少为1") private Integer cattleCount; + /** + * 司机运费(元) + */ + private java.math.BigDecimal freight; + /** * 检疫证号 */ diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliveryEditDto.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliveryEditDto.java index f143161..ade673d 100644 --- a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliveryEditDto.java +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/dto/DeliveryEditDto.java @@ -69,6 +69,9 @@ public class DeliveryEditDto { /** 落地过磅重量 */ private String landingEntruckWeight; + /** 司机运费(元) */ + private java.math.BigDecimal freight; + /** 检疫票照片 */ private String quarantineTickeyUrl; /** 传纸质磅单(双章) */ diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/Delivery.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/Delivery.java index 21cd73e..ee4830c 100644 --- a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/Delivery.java +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/Delivery.java @@ -34,10 +34,10 @@ public class Delivery implements Serializable { private Integer id; /** - * 订单ID(关联order表) + * 订单ID(关联order表,多个订单ID用逗号分隔,如:1,2,3) */ @TableField("order_id") - private Integer orderId; + private String orderId; /** * 运单号 @@ -279,6 +279,12 @@ public class Delivery implements Serializable { @TableField("landingEntruck_weight") private String landingEntruckWeight; + /** + * 司机运费(元) + */ + @TableField("freight") + private java.math.BigDecimal freight; + /** * 检疫票 */ diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/Order.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/Order.java index 8ff6ba0..0747335 100644 --- a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/Order.java +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/Order.java @@ -53,6 +53,12 @@ public class Order implements Serializable { @TableField("firm_price") private java.math.BigDecimal firmPrice; + /** + * 运送清单ID(关联delivery表) + */ + @TableField("delivery_id") + private Integer deliveryId; + /** * 逻辑删除标记(0-正常,1-已删除) */ @@ -109,5 +115,35 @@ public class Order implements Serializable { */ @TableField(exist = false) private String settlementTypeDesc; + + /** + * 运单号(关联delivery表,不存储在数据库中,用于显示) + */ + @TableField(exist = false) + private String deliveryNumber; + + /** + * 起始地(关联delivery表,不存储在数据库中,用于显示) + */ + @TableField(exist = false) + private String startLocation; + + /** + * 目的地(关联delivery表,不存储在数据库中,用于显示) + */ + @TableField(exist = false) + private String endLocation; + + /** + * 单价(关联delivery表,不存储在数据库中,用于显示) + */ + @TableField(exist = false) + private java.math.BigDecimal firmPriceFromDelivery; + + /** + * 运送清单状态(关联delivery表,不存储在数据库中,用于显示) + */ + @TableField(exist = false) + private Integer deliveryStatus; } diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IOrderService.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IOrderService.java index 92294fd..b99605d 100644 --- a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IOrderService.java +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/IOrderService.java @@ -63,5 +63,14 @@ public interface IOrderService extends IService { * @return AjaxResult */ AjaxResult batchImportOrders(List> orders); + + /** + * 批量更新订单的delivery_id字段 + * + * @param deliveryId 运送清单ID + * @param orderIds 订单ID列表 + * @return AjaxResult + */ + AjaxResult updateOrderDeliveryId(Integer deliveryId, List orderIds); } diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/DeliveryServiceImpl.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/DeliveryServiceImpl.java index 59bb1ba..44c0e50 100644 --- a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/DeliveryServiceImpl.java +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/DeliveryServiceImpl.java @@ -797,6 +797,8 @@ public class DeliveryServiceImpl extends ServiceImpl i delivery.setEmptyWeight(StringUtils.isNotEmpty(dto.getEmptyWeight()) ? dto.getEmptyWeight() : null); delivery.setEntruckWeight(StringUtils.isNotEmpty(dto.getEntruckWeight()) ? dto.getEntruckWeight() : null); delivery.setLandingEntruckWeight(StringUtils.isNotEmpty(dto.getLandingEntruckWeight()) ? dto.getLandingEntruckWeight() : null); + // 司机运费 + delivery.setFreight(dto.getFreight()); // 照片 delivery.setQuarantineTickeyUrl(dto.getQuarantineTickeyUrl()); delivery.setPoundListImg(dto.getPoundListImg()); @@ -1055,6 +1057,10 @@ public class DeliveryServiceImpl extends ServiceImpl i if (dto.getLandingEntruckWeight() != null) { delivery.setLandingEntruckWeight(StringUtils.isNotEmpty(dto.getLandingEntruckWeight()) ? dto.getLandingEntruckWeight() : null); } + // 更新司机运费 + if (dto.getFreight() != null) { + delivery.setFreight(dto.getFreight()); + } // 更新照片字段:将空字符串转换为null,避免前端显示问题 // 注意:前端总是传递字段(即使是空字符串),只有在传递了有效URL时才更新 diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/OrderServiceImpl.java b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/OrderServiceImpl.java index bf51acb..aaeeb6b 100644 --- a/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/OrderServiceImpl.java +++ b/tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/OrderServiceImpl.java @@ -46,6 +46,9 @@ public class OrderServiceImpl extends ServiceImpl implements @Autowired private SysUserMapper sysUserMapper; + @Autowired + private com.aiotagro.cattletrade.business.mapper.DeliveryMapper deliveryMapper; + /** * 分页查询订单列表 */ @@ -58,9 +61,13 @@ public class OrderServiceImpl extends ServiceImpl implements String sellerName = params.get("sellerName") != null ? (String) params.get("sellerName") : null; String startTime = params.get("startTime") != null ? (String) params.get("startTime") : null; String endTime = params.get("endTime") != null ? (String) params.get("endTime") : null; + String deliveryNumber = params.get("deliveryNumber") != null ? (String) params.get("deliveryNumber") : null; + String startLocation = params.get("startLocation") != null ? (String) params.get("startLocation") : null; + String endLocation = params.get("endLocation") != null ? (String) params.get("endLocation") : null; + String firmPrice = params.get("firmPrice") != null ? (String) params.get("firmPrice") : null; - logger.info("分页查询订单列表,页码:{},每页数量:{},买方:{},卖方:{},结算方式:{}", - pageNum, pageSize, buyerName, sellerName, settlementType); + logger.info("分页查询订单列表,页码:{},每页数量:{},买方:{},卖方:{},结算方式:{},运单号:{},起始地:{},目的地:{},单价:{}", + pageNum, pageSize, buyerName, sellerName, settlementType, deliveryNumber, startLocation, endLocation, firmPrice); // 构建查询条件 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); @@ -86,9 +93,13 @@ public class OrderServiceImpl extends ServiceImpl implements queryWrapper.orderByDesc(Order::getCreateTime); - // 判断是否需要先过滤再分页(如果提供了买方或卖方名称搜索) + // 判断是否需要先过滤再分页(如果提供了买方、卖方、运单号、起始地、目的地、单价搜索) boolean needFilter = (buyerName != null && !buyerName.trim().isEmpty()) || - (sellerName != null && !sellerName.trim().isEmpty()); + (sellerName != null && !sellerName.trim().isEmpty()) || + (deliveryNumber != null && !deliveryNumber.trim().isEmpty()) || + (startLocation != null && !startLocation.trim().isEmpty()) || + (endLocation != null && !endLocation.trim().isEmpty()) || + (firmPrice != null && !firmPrice.trim().isEmpty()); List filteredList; long total; @@ -112,6 +123,32 @@ public class OrderServiceImpl extends ServiceImpl implements .filter(order -> order.getSellerName() != null && order.getSellerName().contains(sellerName.trim())) .collect(java.util.stream.Collectors.toList()); } + if (deliveryNumber != null && !deliveryNumber.trim().isEmpty()) { + filteredList = filteredList.stream() + .filter(order -> order.getDeliveryNumber() != null && order.getDeliveryNumber().contains(deliveryNumber.trim())) + .collect(java.util.stream.Collectors.toList()); + } + if (startLocation != null && !startLocation.trim().isEmpty()) { + filteredList = filteredList.stream() + .filter(order -> order.getStartLocation() != null && order.getStartLocation().contains(startLocation.trim())) + .collect(java.util.stream.Collectors.toList()); + } + if (endLocation != null && !endLocation.trim().isEmpty()) { + filteredList = filteredList.stream() + .filter(order -> order.getEndLocation() != null && order.getEndLocation().contains(endLocation.trim())) + .collect(java.util.stream.Collectors.toList()); + } + if (firmPrice != null && !firmPrice.trim().isEmpty()) { + filteredList = filteredList.stream() + .filter(order -> { + java.math.BigDecimal price = order.getFirmPriceFromDelivery() != null ? order.getFirmPriceFromDelivery() : order.getFirmPrice(); + if (price == null) { + return false; + } + return price.toString().contains(firmPrice.trim()); + }) + .collect(java.util.stream.Collectors.toList()); + } // 获取总数 total = filteredList.size(); @@ -143,6 +180,45 @@ public class OrderServiceImpl extends ServiceImpl implements logger.info("查询到{}条订单记录,分页后{}条", total, filteredList.size()); } + // 优化排序:按运送清单分组排序 + // 排序规则:1. 有deliveryId的订单按deliveryId分组,相同deliveryId的订单放在一起 + // 2. 按deliveryId排序(null值放在最后) + // 3. 同一deliveryId内的订单按创建时间排序 + filteredList.sort((o1, o2) -> { + Integer deliveryId1 = o1.getDeliveryId(); + Integer deliveryId2 = o2.getDeliveryId(); + + // 如果两个订单都没有deliveryId,按创建时间倒序 + if (deliveryId1 == null && deliveryId2 == null) { + if (o1.getCreateTime() != null && o2.getCreateTime() != null) { + return o2.getCreateTime().compareTo(o1.getCreateTime()); + } + return 0; + } + + // 如果只有一个订单有deliveryId,有deliveryId的排在前面 + if (deliveryId1 == null) { + return 1; // o1排在后面 + } + if (deliveryId2 == null) { + return -1; // o2排在后面 + } + + // 两个订单都有deliveryId,先按deliveryId排序 + int deliveryIdCompare = deliveryId1.compareTo(deliveryId2); + if (deliveryIdCompare != 0) { + return deliveryIdCompare; + } + + // deliveryId相同,按创建时间倒序 + if (o1.getCreateTime() != null && o2.getCreateTime() != null) { + return o2.getCreateTime().compareTo(o1.getCreateTime()); + } + return 0; + }); + + logger.info("订单列表排序完成,共{}条记录", filteredList.size()); + // 构建分页结果 return new PageResultResponse<>(total, filteredList); } @@ -336,6 +412,7 @@ public class OrderServiceImpl extends ServiceImpl implements java.util.Set buyerIds = new java.util.HashSet<>(); java.util.Set sellerIds = new java.util.HashSet<>(); java.util.Set creatorIds = new java.util.HashSet<>(); + java.util.Set deliveryIds = new java.util.HashSet<>(); for (Order order : orders) { // 收集买方ID @@ -368,6 +445,10 @@ public class OrderServiceImpl extends ServiceImpl implements if (order.getCreatedBy() != null) { creatorIds.add(order.getCreatedBy()); } + // 收集运送清单ID + if (order.getDeliveryId() != null) { + deliveryIds.add(order.getDeliveryId()); + } } // 批量查询买方信息 @@ -427,6 +508,21 @@ public class OrderServiceImpl extends ServiceImpl implements } } + // 批量查询运送清单信息 + java.util.Map deliveryMap = new java.util.HashMap<>(); + if (!deliveryIds.isEmpty()) { + try { + List deliveryList = deliveryMapper.selectBatchIds(new ArrayList<>(deliveryIds)); + for (com.aiotagro.cattletrade.business.entity.Delivery delivery : deliveryList) { + if (delivery != null && delivery.getId() != null) { + deliveryMap.put(delivery.getId(), delivery); + } + } + } catch (Exception e) { + logger.warn("批量查询运送清单信息失败:{}", e.getMessage()); + } + } + // 批量填充订单信息 for (Order order : orders) { // 填充买方名称 @@ -473,6 +569,24 @@ public class OrderServiceImpl extends ServiceImpl implements } } + // 填充运送清单信息 + if (order.getDeliveryId() != null) { + com.aiotagro.cattletrade.business.entity.Delivery delivery = deliveryMap.get(order.getDeliveryId()); + if (delivery != null) { + order.setDeliveryNumber(delivery.getDeliveryNumber()); + order.setStartLocation(delivery.getStartLocation()); + order.setEndLocation(delivery.getEndLocation()); + order.setDeliveryStatus(delivery.getStatus()); + // 如果订单本身没有firmPrice,使用运送清单的firmPrice + if (order.getFirmPrice() == null && delivery.getFirmPrice() != null) { + order.setFirmPriceFromDelivery(java.math.BigDecimal.valueOf(delivery.getFirmPrice())); + } else if (delivery.getFirmPrice() != null) { + // 如果订单有firmPrice,也填充运送清单的firmPrice用于显示 + order.setFirmPriceFromDelivery(java.math.BigDecimal.valueOf(delivery.getFirmPrice())); + } + } + } + // 填充结算方式描述 if (order.getSettlementType() != null) { switch (order.getSettlementType()) { @@ -672,5 +786,87 @@ public class OrderServiceImpl extends ServiceImpl implements return AjaxResult.success("批量导入完成:成功" + successCount + "条,失败" + failCount + "条", result); } } + + /** + * 批量更新订单的delivery_id字段 + * + * @param deliveryId 运送清单ID + * @param orderIds 订单ID列表 + * @return AjaxResult + */ + @Override + @Transactional + public AjaxResult updateOrderDeliveryId(Integer deliveryId, List orderIds) { + try { + logger.info("批量更新订单delivery_id,运送清单ID:{},订单ID列表:{}", deliveryId, orderIds); + + if (orderIds == null || orderIds.isEmpty()) { + logger.warn("订单ID列表为空,跳过更新"); + return AjaxResult.success("订单ID列表为空,无需更新"); + } + + if (deliveryId == null) { + logger.error("运送清单ID不能为空"); + return AjaxResult.error("运送清单ID不能为空"); + } + + // 批量更新订单的delivery_id + int successCount = 0; + int failCount = 0; + List failMessages = new ArrayList<>(); + + for (Integer orderId : orderIds) { + try { + Order order = orderMapper.selectById(orderId); + if (order == null) { + failCount++; + failMessages.add("订单ID " + orderId + " 不存在"); + logger.warn("订单不存在,ID:{}", orderId); + continue; + } + + order.setDeliveryId(deliveryId); + order.setUpdateTime(new Date()); + try { + order.setUpdatedBy(SecurityUtil.getCurrentUserId()); + } catch (Exception e) { + logger.warn("获取当前用户失败,使用订单原有创建人:{}", e.getMessage()); + if (order.getCreatedBy() != null) { + order.setUpdatedBy(order.getCreatedBy()); + } + } + + int result = orderMapper.updateById(order); + if (result > 0) { + successCount++; + logger.info("更新订单delivery_id成功,订单ID:{},运送清单ID:{}", orderId, deliveryId); + } else { + failCount++; + failMessages.add("订单ID " + orderId + " 更新失败"); + logger.warn("更新订单delivery_id失败,订单ID:{}", orderId); + } + } catch (Exception e) { + failCount++; + failMessages.add("订单ID " + orderId + " 更新异常:" + e.getMessage()); + logger.error("更新订单delivery_id异常,订单ID:{},错误:{}", orderId, e.getMessage(), e); + } + } + + logger.info("批量更新订单delivery_id完成,成功:{}条,失败:{}条", successCount, failCount); + + if (failCount == 0) { + return AjaxResult.success("批量更新成功,共更新" + successCount + "条订单"); + } else { + Map result = new java.util.HashMap<>(); + result.put("successCount", successCount); + result.put("failCount", failCount); + result.put("failMessages", failMessages); + return AjaxResult.success("批量更新完成:成功" + successCount + "条,失败" + failCount + "条", result); + } + } catch (Exception e) { + logger.error("批量更新订单delivery_id异常:{}", e.getMessage(), e); + return AjaxResult.error("批量更新订单delivery_id失败:" + e.getMessage()); + } + } } diff --git a/tradeCattle/aiotagro-cattle-trade/src/main/resources/db/migration/alter_delivery_order_id_to_varchar.sql b/tradeCattle/aiotagro-cattle-trade/src/main/resources/db/migration/alter_delivery_order_id_to_varchar.sql new file mode 100644 index 0000000..83de53c --- /dev/null +++ b/tradeCattle/aiotagro-cattle-trade/src/main/resources/db/migration/alter_delivery_order_id_to_varchar.sql @@ -0,0 +1,14 @@ +USE cattletrade; + +-- 修改 delivery 表的 order_id 字段类型为 VARCHAR,用于存储多个订单ID(逗号分隔) +-- 将原来的 INTEGER 类型改为 VARCHAR(500),支持存储多个订单ID,如 "1,2,3" + +-- 注意:如果表中已有数据,需要先将现有的 order_id 值转换为字符串格式 +-- 例如:如果 order_id = 123,转换后仍为 "123" + +ALTER TABLE `delivery` + MODIFY COLUMN `order_id` VARCHAR(500) NULL DEFAULT NULL COMMENT '订单ID(多个订单ID用逗号分隔,如:1,2,3)'; + +-- 如果表中已有数据,将现有的整数 order_id 转换为字符串格式 +-- UPDATE delivery SET order_id = CAST(order_id AS CHAR) WHERE order_id IS NOT NULL; +