This commit is contained in:
2025-11-21 10:53:04 +08:00
parent e24d6ef0b4
commit 24ca9148dd
61 changed files with 4918 additions and 582 deletions

View File

@@ -0,0 +1,15 @@
package com.delivery.framework.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/** 配送定位上报规则配置 */
@Data
@Component("locationReportProperties")
@ConfigurationProperties(prefix = "delivery.location")
public class LocationReportProperties {
private long minPersistIntervalMs;
private long stateTtlHours;
private long lockSeconds;
}

View File

@@ -32,6 +32,10 @@ public class ResourcesConfig implements WebMvcConfigurer
registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**")
.addResourceLocations("file:" + DeliveryConfig.getProfile() + "/");
/** D:\delivery 的静态映射 */
registry.addResourceHandler("/delivery/**")
.addResourceLocations("file:D:/delivery/");
/** swagger配置 */
registry.addResourceHandler("/swagger-ui/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")

View File

@@ -114,7 +114,14 @@ public class SecurityConfig
requests.antMatchers(
"/login",
"/register",
"/app/**",
"/delivery/**",
"/document/vehicle/**",
"/document/type/**",
"/document/location/**",
"/document/mtd/**",
"/document/info/**",
"/delivery/location/**",
"/document/attachment/**",
"/document/order/**",
// "/document/info/bill/groups",

View File

@@ -173,4 +173,28 @@ public class SysLoginService
{
userService.updateLoginInfo(userId, IpUtils.getIpAddr(), DateUtils.getNowDate());
}
public LoginUser loginWithoutCaptcha(String username, String password) {
loginPreCheck(username, password);
Authentication authentication;
try {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
AuthenticationContextHolder.setContext(token);
authentication = authenticationManager.authenticate(token);
}catch (Exception e){
throw new ServiceException("登录失败");
}finally {
AuthenticationContextHolder.clearContext();
}
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
recordLoginInfo(loginUser.getUserId());
return loginUser;
}
}

View File

@@ -1,15 +1,15 @@
package com.delivery.project.document.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.delivery.common.exception.ServiceException;
import com.delivery.common.utils.MinioUtil;
import com.delivery.common.utils.uuid.IdUtils;
import com.delivery.project.document.domain.dto.DeliveryAttachUploadDTO;
import com.delivery.project.document.domain.dto.DeliveryExecuteBindDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
@@ -32,6 +32,7 @@ import org.springframework.web.multipart.MultipartFile;
* @date 2025-10-15
*/
@RestController
@Slf4j
@RequestMapping("/document/attachment")
public class DeliveryAttachmentController extends BaseController
{
@@ -45,6 +46,7 @@ public class DeliveryAttachmentController extends BaseController
@Value("${minio.bucketName}")
private String bucketName;
/**
* 查询配送附件列表
*/
@@ -118,8 +120,8 @@ public class DeliveryAttachmentController extends BaseController
*/
// @PreAuthorize("@ss.hasPermi('document:attachment:add')")
// @Log(title = "配送附件-上传(仅MinIO)", businessType = BusinessType.OTHER)
@PostMapping(value = "/upload", consumes = {"multipart/form-data"})
public AjaxResult upload(@RequestPart("files") MultipartFile[] files,
@PostMapping(value = "/uploads", consumes = {"multipart/form-data"})
public AjaxResult uploads(@RequestPart("files") MultipartFile[] files,
@Validated @ModelAttribute DeliveryAttachUploadDTO dto) {
try {
// 用 scene + bizType 自动当作子目录(可空)
@@ -133,7 +135,7 @@ public class DeliveryAttachmentController extends BaseController
List<String> urls = minioUtil.uploadBatch(
files,
bucketName,
minioUtil.buildDateFolder("delivery", sub) // delivery/yyyy-MM-dd[/scene/bizType]
minioUtil.buildDateFolder("delivery", sub)
);
return AjaxResult.success("urls", urls);
} catch (ServiceException se) {
@@ -142,4 +144,36 @@ public class DeliveryAttachmentController extends BaseController
return AjaxResult.error("上传失败:" + e.getMessage());
}
}
/**
* 开始配送
* @param dto
* @return
*/
// @PreAuthorize("@ss.hasPermi('document:attachment:add')")
// @Log(title = "配送附件-执行绑定", businessType = BusinessType.INSERT)
@PostMapping("/executeBind")
public AjaxResult executeBind(@Validated @RequestBody DeliveryExecuteBindDTO dto) {
int saved = deliveryAttachmentService.executeBind(dto);
return AjaxResult.success()
.put("orderNo", dto.getOrderNo())
.put("scene", dto.getScene());
}
@Log(title = "配送附件上传", businessType = BusinessType.OTHER)
@PostMapping(value = "/upload", consumes = "multipart/form-data")
public AjaxResult upload(@RequestPart("files") MultipartFile[] files,
@Validated @ModelAttribute DeliveryAttachUploadDTO dto,
HttpServletRequest request) {
try {
List<String> urls = deliveryAttachmentService.upload(files, dto, request);
return AjaxResult.success("上传成功", urls);
} catch (Exception e) {
log.error("【附件上传失败】{}", e.getMessage(), e);
return AjaxResult.error("上传失败:" + e.getMessage());
}
}
}

View File

@@ -0,0 +1,118 @@
package com.delivery.project.document.controller;
import com.delivery.common.utils.poi.ExcelUtil;
import com.delivery.framework.aspectj.lang.annotation.Log;
import com.delivery.framework.aspectj.lang.enums.BusinessType;
import com.delivery.framework.web.controller.BaseController;
import com.delivery.framework.web.domain.AjaxResult;
import com.delivery.framework.web.page.TableDataInfo;
import com.delivery.project.document.domain.DeliveryLocationRecord;
import com.delivery.project.document.domain.dto.DeliverySimpleLocationDTO;
import com.delivery.project.document.domain.dto.LocationReportDTO;
import com.delivery.project.document.service.ILocationReportService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* 配送定位上报 Controller仅打印经纬度
*
* @author delivery
* @date 2025-10-20
*/
@Slf4j
@RestController
@RequestMapping("/delivery/location")
public class DeliveryLocationController extends BaseController {
@Autowired
private ILocationReportService deliveryLocationRecordService;
@PostMapping("/report")
public AjaxResult report(@Validated @RequestBody LocationReportDTO dto) {
String username = getUsername();
return AjaxResult.success(deliveryLocationRecordService.report(dto, username));
}
/**
* 查询配送定位记录(车牌+单号维度)列表
*/
@PreAuthorize("@ss.hasPermi('document:record:list')")
@GetMapping("/list")
public TableDataInfo list(DeliveryLocationRecord deliveryLocationRecord)
{
startPage();
List<DeliveryLocationRecord> list = deliveryLocationRecordService.selectDeliveryLocationRecordList(deliveryLocationRecord);
return getDataTable(list);
}
/**
* 导出配送定位记录(车牌+单号维度)列表
*/
@PreAuthorize("@ss.hasPermi('document:record:export')")
@Log(title = "配送定位记录(车牌+单号维度)", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, DeliveryLocationRecord deliveryLocationRecord)
{
List<DeliveryLocationRecord> list = deliveryLocationRecordService.selectDeliveryLocationRecordList(deliveryLocationRecord);
ExcelUtil<DeliveryLocationRecord> util = new ExcelUtil<DeliveryLocationRecord>(DeliveryLocationRecord.class);
util.exportExcel(response, list, "配送定位记录(车牌+单号维度)数据");
}
/**
* 获取配送定位记录(车牌+单号维度)详细信息
*/
@PreAuthorize("@ss.hasPermi('document:record:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(deliveryLocationRecordService.selectDeliveryLocationRecordById(id));
}
/**
* 新增配送定位记录(车牌+单号维度)
*/
@PreAuthorize("@ss.hasPermi('document:record:add')")
@Log(title = "配送定位记录(车牌+单号维度)", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody DeliveryLocationRecord deliveryLocationRecord)
{
return toAjax(deliveryLocationRecordService.insertDeliveryLocationRecord(deliveryLocationRecord));
}
/**
* 修改配送定位记录(车牌+单号维度)
*/
@PreAuthorize("@ss.hasPermi('document:record:edit')")
@Log(title = "配送定位记录(车牌+单号维度)", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody DeliveryLocationRecord deliveryLocationRecord)
{
return toAjax(deliveryLocationRecordService.updateDeliveryLocationRecord(deliveryLocationRecord));
}
/**
* 删除配送定位记录(车牌+单号维度)
*/
@PreAuthorize("@ss.hasPermi('document:record:remove')")
@Log(title = "配送定位记录(车牌+单号维度)", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(deliveryLocationRecordService.deleteDeliveryLocationRecordByIds(ids));
}
// @PreAuthorize("@ss.hasPermi('document:record:track')")
@GetMapping("/track")
public AjaxResult track(@RequestParam("orderNo") String orderNo,
@RequestParam("plateNo") String plateNo) {
List<DeliverySimpleLocationDTO> points =
deliveryLocationRecordService.selectTrackPoints(orderNo, plateNo);
return AjaxResult.success(points);
}
}

View File

@@ -3,7 +3,10 @@ package com.delivery.project.document.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import com.delivery.project.document.domain.RkInfo;
import com.delivery.project.document.domain.dto.DeliveryOrderCreateDTO;
import com.delivery.project.document.domain.dto.DeliveryOrderSaveDTO;
import com.delivery.project.document.domain.vo.DeliveryOrderGroupVO;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
@@ -88,8 +91,7 @@ public class DeliveryOrderController extends BaseController
@PreAuthorize("@ss.hasPermi('document:order:edit')")
@Log(title = "配送单据主", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody DeliveryOrder deliveryOrder)
{
public AjaxResult edit(@RequestBody DeliveryOrder deliveryOrder) {
return toAjax(deliveryOrderService.updateDeliveryOrder(deliveryOrder));
}
@@ -119,4 +121,41 @@ public class DeliveryOrderController extends BaseController
return toAjax(rows);
}
/** 新增配送单据:同一单号多行写入 */
// @PreAuthorize("@ss.hasPermi('document:order:add')")
// @Log(title = "配送单据", businessType = BusinessType.INSERT)
@PostMapping("/batch")
public AjaxResult createBatch(@RequestBody DeliveryOrderCreateDTO dto) {
String plateNo = deliveryOrderService.createOrder(dto);
return success("创建成功,单号:" + plateNo);
}
/** 列表:按单号分组(分页) */
// @PreAuthorize("@ss.hasPermi('document:order:list')")
@GetMapping("/group")
public TableDataInfo listGroup(DeliveryOrder query) {
startPage();
List<DeliveryOrderGroupVO> list = deliveryOrderService.listGroup(query);
return getDataTable(list);
}
/** 详情:按单号查询所有行 */
// @PreAuthorize("@ss.hasPermi('document:order:query')")
@GetMapping("/detail/{orderNo}")
public AjaxResult detail(@PathVariable String orderNo) {
return success(deliveryOrderService.listByOrderNo(orderNo));
}
/**
* 从智慧实物系统拉取待配送出库单据列表
*/
@GetMapping("/wisdom/rk/list")
public AjaxResult listRkFromWisdom() {
List<RkInfo> list = deliveryOrderService.listWisdomRkForDelivery();
return AjaxResult.success(list);
}
}

View File

@@ -0,0 +1,29 @@
package com.delivery.project.document.controller;
import com.delivery.framework.web.controller.BaseController;
import com.delivery.framework.web.domain.AjaxResult;
import com.delivery.project.document.domain.dto.LocationReportDTO;
import com.delivery.project.document.service.ILocationReportService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
///** 配送定位上报 Controller车牌+单号维度) */
//@RestController
//@RequestMapping("/document/location")
//public class LocationController extends BaseController {
//
// @Resource
// private ILocationReportService locationReportService;
//
// /** APP 上报定位(车牌号 + 配送单号 + 经纬度) */
//// @PreAuthorize("@ss.hasPermi('document:location:report')")
// @PostMapping("/report")
// public AjaxResult report(@Validated @RequestBody LocationReportDTO dto) {
//// String username = getUsername();
// System.out.println("1`1111111111111111111111111111");
// String username = "大爷的!!!";
// return AjaxResult.success(locationReportService.report(dto, username));
// }
//}

View File

@@ -0,0 +1,128 @@
package com.delivery.project.document.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import com.delivery.project.document.domain.dto.CalcTotalWvDTO;
import com.delivery.project.document.domain.vo.TotalWvVO;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import com.delivery.framework.aspectj.lang.annotation.Log;
import com.delivery.framework.aspectj.lang.enums.BusinessType;
import com.delivery.project.document.domain.Mtd;
import com.delivery.project.document.service.IMtdService;
import com.delivery.framework.web.controller.BaseController;
import com.delivery.framework.web.domain.AjaxResult;
import com.delivery.common.utils.poi.ExcelUtil;
import com.delivery.framework.web.page.TableDataInfo;
import org.springframework.web.multipart.MultipartFile;
/**
* 物料字典Controller
*
* @author delivery
* @date 2025-10-23
*/
@RestController
@RequestMapping("/document/mtd")
public class MtdController extends BaseController
{
@Autowired
private IMtdService mtdService;
/**
* 查询物料字典列表
*/
@PreAuthorize("@ss.hasPermi('document:mtd:list')")
@GetMapping("/list")
public TableDataInfo list(Mtd mtd)
{
startPage();
List<Mtd> list = mtdService.selectMtdList(mtd);
return getDataTable(list);
}
/**
* 导出物料字典列表
*/
@PreAuthorize("@ss.hasPermi('document:mtd:export')")
@Log(title = "物料字典", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, Mtd mtd)
{
List<Mtd> list = mtdService.selectMtdList(mtd);
ExcelUtil<Mtd> util = new ExcelUtil<Mtd>(Mtd.class);
util.exportExcel(response, list, "物料字典数据");
}
/**
* Excel 导入物料字典(同 wlNo 则更新,不存在则新增)
*/
@PreAuthorize("@ss.hasPermi('document:mtd:import')")
@Log(title = "物料字典", businessType = BusinessType.IMPORT)
@PostMapping("/importData")
public AjaxResult importData(@RequestParam("file") MultipartFile file) throws Exception
{
if (file == null || file.isEmpty()) {
return AjaxResult.error("导入文件不能为空");
}
ExcelUtil<Mtd> util = new ExcelUtil<>(Mtd.class);
List<Mtd> list = util.importExcel(file.getInputStream());
int handled = mtdService.importMtd(list, getUsername());
return success("导入成功,共处理 " + handled + "");
}
/**
* 获取物料字典详细信息
*/
@PreAuthorize("@ss.hasPermi('document:mtd:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(mtdService.selectMtdById(id));
}
/**
* 新增物料字典
*/
@PreAuthorize("@ss.hasPermi('document:mtd:add')")
@Log(title = "物料字典", businessType = BusinessType.INSERT)
@PostMapping()
public AjaxResult add(@RequestBody Mtd mtd)
{
return toAjax(mtdService.insertMtd(mtd));
}
/**
* 修改物料字典
*/
@PreAuthorize("@ss.hasPermi('document:mtd:edit')")
@Log(title = "物料字典", businessType = BusinessType.UPDATE)
@PutMapping()
public AjaxResult edit(@RequestBody Mtd mtd)
{
return toAjax(mtdService.updateMtd(mtd));
}
/**
* 删除物料字典
*/
@PreAuthorize("@ss.hasPermi('document:mtd:remove')")
@Log(title = "物料字典", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(mtdService.deleteMtdByIds(ids));
}
/**
* 计算货物的总体积和重量
*/
// @PreAuthorize("@ss.hasPermi('document:mtd:calc')")
@PostMapping("/calcTotalWv")
public AjaxResult calcTotalWv(@RequestBody CalcTotalWvDTO dto) {
TotalWvVO vo = mtdService.calcTotalWv(dto);
return AjaxResult.success(vo);
}
}

View File

@@ -3,9 +3,7 @@ package com.delivery.project.document.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import com.delivery.project.document.domain.dto.RkInfoQueryDTO;
import com.github.pagehelper.PageHelper;
import io.swagger.annotations.ApiOperation;
import com.delivery.project.document.service.IVehicleTypeService;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
@@ -42,9 +40,13 @@ public class RkInfoController extends BaseController
* 查询库存单据明细列表
*/
@GetMapping("/list")
public TableDataInfo list(RkInfo rkInfo) {
public TableDataInfo list(RkInfo rkInfo)
{
List<RkInfo> list = rkInfoService.selectRkInfoList(rkInfo);
// 封装分页返回
return getDataTable(list);
}
/**
* 导出库存单据明细列表
@@ -62,7 +64,7 @@ public class RkInfoController extends BaseController
/**
* 获取库存单据明细详细信息
*/
@PreAuthorize("@ss.hasPermi('document:info:query')")
// @PreAuthorize("@ss.hasPermi('document:info:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
@@ -114,4 +116,15 @@ public class RkInfoController extends BaseController
return getDataTable(rows);
}
@GetMapping("/page")
public TableDataInfo page(RkInfo rkInfo)
{
// 开启分页
startPage();
List<RkInfo> list = rkInfoService.selectRkInfoList(rkInfo);
// 封装分页返回
return getDataTable(list);
}
}

View File

@@ -0,0 +1,104 @@
package com.delivery.project.document.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.delivery.framework.aspectj.lang.annotation.Log;
import com.delivery.framework.aspectj.lang.enums.BusinessType;
import com.delivery.project.document.domain.Vehicle;
import com.delivery.project.document.service.IVehicleService;
import com.delivery.framework.web.controller.BaseController;
import com.delivery.framework.web.domain.AjaxResult;
import com.delivery.common.utils.poi.ExcelUtil;
import com.delivery.framework.web.page.TableDataInfo;
/**
* 车辆档案Controller
*
* @author delivery
* @date 2025-10-23
*/
@RestController
@RequestMapping("/document/vehicle")
public class VehicleController extends BaseController
{
@Autowired
private IVehicleService vehicleService;
/**
* 查询车辆档案列表
*/
// @PreAuthorize("@ss.hasPermi('document:vehicle:list')")
@GetMapping("/list")
public TableDataInfo list(Vehicle vehicle)
{
startPage();
List<Vehicle> list = vehicleService.selectVehicleList(vehicle);
return getDataTable(list);
}
/**
* 导出车辆档案列表
*/
@PreAuthorize("@ss.hasPermi('document:vehicle:export')")
@Log(title = "车辆档案", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, Vehicle vehicle)
{
List<Vehicle> list = vehicleService.selectVehicleList(vehicle);
ExcelUtil<Vehicle> util = new ExcelUtil<Vehicle>(Vehicle.class);
util.exportExcel(response, list, "车辆档案数据");
}
/**
* 获取车辆档案详细信息
*/
@PreAuthorize("@ss.hasPermi('document:vehicle:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(vehicleService.selectVehicleById(id));
}
/**
* 新增车辆档案
*/
@PreAuthorize("@ss.hasPermi('document:vehicle:add')")
@Log(title = "车辆档案", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody Vehicle vehicle)
{
return toAjax(vehicleService.insertVehicle(vehicle));
}
/**
* 修改车辆档案
*/
@PreAuthorize("@ss.hasPermi('document:vehicle:edit')")
@Log(title = "车辆档案", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody Vehicle vehicle)
{
return toAjax(vehicleService.updateVehicle(vehicle));
}
/**
* 删除车辆档案
*/
@PreAuthorize("@ss.hasPermi('document:vehicle:remove')")
@Log(title = "车辆档案", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(vehicleService.deleteVehicleByIds(ids));
}
}

View File

@@ -0,0 +1,127 @@
package com.delivery.project.document.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import com.delivery.project.document.domain.dto.CalcSuggestFeeDTO;
import com.delivery.project.document.domain.vo.CalcSuggestFeeVO;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.delivery.framework.aspectj.lang.annotation.Log;
import com.delivery.framework.aspectj.lang.enums.BusinessType;
import com.delivery.project.document.domain.VehicleType;
import com.delivery.project.document.service.IVehicleTypeService;
import com.delivery.framework.web.controller.BaseController;
import com.delivery.framework.web.domain.AjaxResult;
import com.delivery.common.utils.poi.ExcelUtil;
import com.delivery.framework.web.page.TableDataInfo;
/**
* 车型定义Controller
*
* @author delivery
* @date 2025-10-23
*/
@RestController
@RequestMapping("/document/type")
public class VehicleTypeController extends BaseController
{
@Autowired
private IVehicleTypeService vehicleTypeService;
/**
* 查询车型定义列表
*/
// @PreAuthorize("@ss.hasPermi('document:type:list')")
@GetMapping("/list")
public TableDataInfo list(VehicleType vehicleType)
{
startPage();
List<VehicleType> list = vehicleTypeService.selectVehicleTypeList(vehicleType);
return getDataTable(list);
}
/**
* 导出车型定义列表
*/
@PreAuthorize("@ss.hasPermi('document:type:export')")
@Log(title = "车型定义", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, VehicleType vehicleType)
{
List<VehicleType> list = vehicleTypeService.selectVehicleTypeList(vehicleType);
ExcelUtil<VehicleType> util = new ExcelUtil<VehicleType>(VehicleType.class);
util.exportExcel(response, list, "车型定义数据");
}
/**
* 获取车型定义详细信息
*/
@PreAuthorize("@ss.hasPermi('document:type:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(vehicleTypeService.selectVehicleTypeById(id));
}
/**
* 新增车型定义
*/
@PreAuthorize("@ss.hasPermi('document:type:add')")
@Log(title = "车型定义", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody VehicleType vehicleType)
{
return toAjax(vehicleTypeService.insertVehicleType(vehicleType));
}
/**
* 修改车型定义
*/
@PreAuthorize("@ss.hasPermi('document:type:edit')")
@Log(title = "车型定义", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody VehicleType vehicleType)
{
return toAjax(vehicleTypeService.updateVehicleType(vehicleType));
}
/**
* 删除车型定义
*/
@PreAuthorize("@ss.hasPermi('document:type:remove')")
@Log(title = "车型定义", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(vehicleTypeService.deleteVehicleTypeByIds(ids));
}
/** 计算建议费用(支持前端指定车型重算) */
@PostMapping("/fee")
public AjaxResult calcSuggestFee(@RequestBody CalcSuggestFeeDTO dto) {
CalcSuggestFeeVO vo = vehicleTypeService.calcSuggestFee(dto);
return success(vo);
}
/**
* 查看全部车型
* @param vehicleType
* @return
*/
@GetMapping("/page")
public TableDataInfo page(VehicleType vehicleType)
{
List<VehicleType> list = vehicleTypeService.selectVehicleTypeList(vehicleType);
return getDataTable(list);
}
}

View File

@@ -0,0 +1,49 @@
package com.delivery.project.document.controller.app;
import com.delivery.framework.security.LoginBody;
import com.delivery.framework.security.LoginUser;
import com.delivery.framework.security.service.SysLoginService;
import com.delivery.framework.security.service.TokenService;
import com.delivery.framework.web.domain.AjaxResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/app")
public class LoginController {
@Autowired
private SysLoginService loginService;
@Autowired
private TokenService tokenService;
/**
* 登录方法
*
* @param loginBody 登录信息
* @return 结果
*/
@PostMapping("/login")
public AjaxResult login(@RequestBody LoginBody loginBody) {
String username = loginBody.getUsername();
String password = loginBody.getPassword();
LoginUser loginUser = loginService.loginWithoutCaptcha(username, password);
String token = tokenService.createToken(loginUser);
Map<String, Object> data = new HashMap<>();
data.put("token", token);
data.put("user", loginUser.getUser());
return AjaxResult.success(data);
}
}

View File

@@ -20,7 +20,7 @@ public class DeliveryAttachment extends BaseEntity
/** 关联的配送单ID */
@Excel(name = "关联的配送单ID")
private Long orderId;
private String orderNo;
/** 场景ORIGIN起点、DEST终点 */
@@ -57,14 +57,14 @@ public class DeliveryAttachment extends BaseEntity
return id;
}
public void setOrderId(Long orderId)
public void setOrderNo(String orderNo)
{
this.orderId = orderId;
this.orderNo = orderNo;
}
public Long getOrderId()
public String getOrderNo()
{
return orderId;
return orderNo;
}
public void setScene(String scene)
@@ -131,7 +131,7 @@ public class DeliveryAttachment extends BaseEntity
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("orderId", getOrderId())
.append("orderNo", getOrderNo())
.append("scene", getScene())
.append("bizType", getBizType())
.append("url", getUrl())

View File

@@ -0,0 +1,39 @@
package com.delivery.project.document.domain;
import com.delivery.framework.web.domain.BaseEntity;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/** 配送定位落库记录实体(车牌+单号维度) */
@Data
public class DeliveryLocationRecord extends BaseEntity {
/** 主键ID */
private Long id;
/** 车牌号(上报维度之一) */
private String plateNo;
/** 配送单号(上报维度之一,必填) */
private String orderNo;
/** 经度保留6位小数 */
private BigDecimal lng;
/** 纬度保留6位小数 */
private BigDecimal lat;
/** 服务端接收并确认落库时间 */
private Date reportedAt;
/** 来源app等 */
private String source;
/** 触发原因3_same/15s_tick等 */
private String remark;
/** 是否删除0正常1删除 */
private String isDelete;
}

View File

@@ -12,17 +12,19 @@ import org.apache.commons.lang3.builder.ToStringStyle;
/**
* 配送单据主对象 delivery_order
*
* @author delivery
* @date 2025-10-15
*/
public class DeliveryOrder extends BaseEntity
{
public class DeliveryOrder extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 主键ID */
private Long id;
/** 配送单据号 */
@Excel(name = "配送单据号")
private String orderNo;
/** 项目描述 */
@Excel(name = "项目描述")
private String xmMs;
@@ -86,7 +88,15 @@ public class DeliveryOrder extends BaseEntity
/** 配送车牌 */
@Excel(name = "配送车牌")
private String vehiclePlate;
private String plateNo;
/** 司机姓名 */
@Excel(name = "司机姓名")
private String driverName;
/** 司机联系方式 */
@Excel(name = "司机联系方式")
private String driverPhone;
/** 发货人姓名 */
@Excel(name = "发货人姓名")
@@ -112,6 +122,21 @@ public class DeliveryOrder extends BaseEntity
@Excel(name = "配送吨位")
private BigDecimal deliveryTon;
/** 货物尺寸(㎡),占地面积 */
@Excel(name = "货物尺寸(㎡)")
private BigDecimal goodsSize;
/** 配送状态0待接单1已接单2配送中3已签收 */
@Excel(name = "配送状态", readConverterExp = "0=待接单,1=已接单,2=配送中,3=已签收")
private String orderStatus;
/** 车辆类型ID */
private Long vehicleTypeId;
/** 车辆类型名称 */
@Excel(name = "车辆类型名称")
private String vehicleTypeName;
/** 是否删除0正常 1已删除 */
@Excel(name = "是否删除", readConverterExp = "0=正常,1=已删除")
private String isDelete;
@@ -119,281 +144,175 @@ public class DeliveryOrder extends BaseEntity
/** 连表查询用:附件列表 */
private List<DeliveryAttachment> attachments;
// ===================== 费用与里程 =====================
/** 建议费用(按车型单价*里程的推荐值) */
@Excel(name = "建议费用")
private BigDecimal suggestFee;
/** 实际费用(最终确认费用) */
@Excel(name = "实际费用")
private BigDecimal actualFee;
/** 高速费用 */
@Excel(name = "高速费用")
private BigDecimal tollFee;
/** 总公里数(行驶里程) */
@Excel(name = "总公里数")
private BigDecimal totalKm;
// ===================== Getter & Setter =====================
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getOrderNo() { return orderNo; }
public void setOrderNo(String orderNo) { this.orderNo = orderNo; }
public String getXmMs() { return xmMs; }
public void setXmMs(String xmMs) { this.xmMs = xmMs; }
public String getXmNo() { return xmNo; }
public void setXmNo(String xmNo) { this.xmNo = xmNo; }
public String getWlNo() { return wlNo; }
public void setWlNo(String wlNo) { this.wlNo = wlNo; }
public String getWlMs() { return wlMs; }
public void setWlMs(String wlMs) { this.wlMs = wlMs; }
public BigDecimal getRealQty() { return realQty; }
public void setRealQty(BigDecimal realQty) { this.realQty = realQty; }
public String getDw() { return dw; }
public void setDw(String dw) { this.dw = dw; }
public String getSapNo() { return sapNo; }
public void setSapNo(String sapNo) { this.sapNo = sapNo; }
public String getGysMc() { return gysMc; }
public void setGysMc(String gysMc) { this.gysMc = gysMc; }
public String getOriginName() { return originName; }
public void setOriginName(String originName) { this.originName = originName; }
public BigDecimal getOriginLng() { return originLng; }
public void setOriginLng(BigDecimal originLng) { this.originLng = originLng; }
public BigDecimal getOriginLat() { return originLat; }
public void setOriginLat(BigDecimal originLat) { this.originLat = originLat; }
public String getDestName() { return destName; }
public void setDestName(String destName) { this.destName = destName; }
public BigDecimal getDestLng() { return destLng; }
public void setDestLng(BigDecimal destLng) { this.destLng = destLng; }
public BigDecimal getDestLat() { return destLat; }
public void setDestLat(BigDecimal destLat) { this.destLat = destLat; }
public Date getDeliveryDate() { return deliveryDate; }
public void setDeliveryDate(Date deliveryDate) { this.deliveryDate = deliveryDate; }
public String getplateNo() { return plateNo; }
public void setplateNo(String plateNo) { this.plateNo = plateNo; }
public String getDriverName() { return driverName; }
public void setDriverName(String driverName) { this.driverName = driverName; }
public String getDriverPhone() { return driverPhone; }
public void setDriverPhone(String driverPhone) { this.driverPhone = driverPhone; }
public String getShipperName() { return shipperName; }
public void setShipperName(String shipperName) { this.shipperName = shipperName; }
public String getShipperPhone() { return shipperPhone; }
public void setShipperPhone(String shipperPhone) { this.shipperPhone = shipperPhone; }
public String getReceiverName() { return receiverName; }
public void setReceiverName(String receiverName) { this.receiverName = receiverName; }
public String getReceiverPhone() { return receiverPhone; }
public void setReceiverPhone(String receiverPhone) { this.receiverPhone = receiverPhone; }
public String getReceiverOrgName() { return receiverOrgName; }
public void setReceiverOrgName(String receiverOrgName) { this.receiverOrgName = receiverOrgName; }
public BigDecimal getDeliveryTon() { return deliveryTon; }
public void setDeliveryTon(BigDecimal deliveryTon) { this.deliveryTon = deliveryTon; }
public BigDecimal getGoodsSize() { return goodsSize; }
public void setGoodsSize(BigDecimal goodsSize) { this.goodsSize = goodsSize; }
public String getOrderStatus() { return orderStatus; }
public void setOrderStatus(String orderStatus) { this.orderStatus = orderStatus; }
public Long getVehicleTypeId() { return vehicleTypeId; }
public void setVehicleTypeId(Long vehicleTypeId) { this.vehicleTypeId = vehicleTypeId; }
public String getVehicleTypeName() { return vehicleTypeName; }
public void setVehicleTypeName(String vehicleTypeName) { this.vehicleTypeName = vehicleTypeName; }
public String getIsDelete() { return isDelete; }
public void setIsDelete(String isDelete) { this.isDelete = isDelete; }
public List<DeliveryAttachment> getAttachments() { return attachments; }
public void setAttachments(List<DeliveryAttachment> attachments) { this.attachments = attachments; }
public void setId(Long id)
{
this.id = id;
}
public BigDecimal getSuggestFee() { return suggestFee; }
public void setSuggestFee(BigDecimal suggestFee) { this.suggestFee = suggestFee; }
public Long getId()
{
return id;
}
public BigDecimal getActualFee() { return actualFee; }
public void setActualFee(BigDecimal actualFee) { this.actualFee = actualFee; }
public void setXmMs(String xmMs)
{
this.xmMs = xmMs;
}
public BigDecimal getTollFee() { return tollFee; }
public void setTollFee(BigDecimal tollFee) { this.tollFee = tollFee; }
public String getXmMs()
{
return xmMs;
}
public void setXmNo(String xmNo)
{
this.xmNo = xmNo;
}
public String getXmNo()
{
return xmNo;
}
public void setWlNo(String wlNo)
{
this.wlNo = wlNo;
}
public String getWlNo()
{
return wlNo;
}
public void setWlMs(String wlMs)
{
this.wlMs = wlMs;
}
public String getWlMs()
{
return wlMs;
}
public void setRealQty(BigDecimal realQty)
{
this.realQty = realQty;
}
public BigDecimal getRealQty()
{
return realQty;
}
public void setDw(String dw)
{
this.dw = dw;
}
public String getDw()
{
return dw;
}
public void setSapNo(String sapNo)
{
this.sapNo = sapNo;
}
public String getSapNo()
{
return sapNo;
}
public void setGysMc(String gysMc)
{
this.gysMc = gysMc;
}
public String getGysMc()
{
return gysMc;
}
public void setOriginName(String originName)
{
this.originName = originName;
}
public String getOriginName()
{
return originName;
}
public void setOriginLng(BigDecimal originLng)
{
this.originLng = originLng;
}
public BigDecimal getOriginLng()
{
return originLng;
}
public void setOriginLat(BigDecimal originLat)
{
this.originLat = originLat;
}
public BigDecimal getOriginLat()
{
return originLat;
}
public void setDestName(String destName)
{
this.destName = destName;
}
public String getDestName()
{
return destName;
}
public void setDestLng(BigDecimal destLng)
{
this.destLng = destLng;
}
public BigDecimal getDestLng()
{
return destLng;
}
public void setDestLat(BigDecimal destLat)
{
this.destLat = destLat;
}
public BigDecimal getDestLat()
{
return destLat;
}
public void setDeliveryDate(Date deliveryDate)
{
this.deliveryDate = deliveryDate;
}
public Date getDeliveryDate()
{
return deliveryDate;
}
public void setVehiclePlate(String vehiclePlate)
{
this.vehiclePlate = vehiclePlate;
}
public String getVehiclePlate()
{
return vehiclePlate;
}
public void setShipperName(String shipperName)
{
this.shipperName = shipperName;
}
public String getShipperName()
{
return shipperName;
}
public void setShipperPhone(String shipperPhone)
{
this.shipperPhone = shipperPhone;
}
public String getShipperPhone()
{
return shipperPhone;
}
public void setReceiverName(String receiverName)
{
this.receiverName = receiverName;
}
public String getReceiverName()
{
return receiverName;
}
public void setReceiverPhone(String receiverPhone)
{
this.receiverPhone = receiverPhone;
}
public String getReceiverPhone()
{
return receiverPhone;
}
public void setReceiverOrgName(String receiverOrgName)
{
this.receiverOrgName = receiverOrgName;
}
public String getReceiverOrgName()
{
return receiverOrgName;
}
public void setDeliveryTon(BigDecimal deliveryTon)
{
this.deliveryTon = deliveryTon;
}
public BigDecimal getDeliveryTon()
{
return deliveryTon;
}
public void setIsDelete(String isDelete)
{
this.isDelete = isDelete;
}
public String getIsDelete()
{
return isDelete;
}
public BigDecimal getTotalKm() { return totalKm; }
public void setTotalKm(BigDecimal totalKm) { this.totalKm = totalKm; }
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("xmMs", getXmMs())
.append("xmNo", getXmNo())
.append("wlNo", getWlNo())
.append("wlMs", getWlMs())
.append("realQty", getRealQty())
.append("dw", getDw())
.append("sapNo", getSapNo())
.append("gysMc", getGysMc())
.append("remark", getRemark())
.append("originName", getOriginName())
.append("originLng", getOriginLng())
.append("originLat", getOriginLat())
.append("destName", getDestName())
.append("destLng", getDestLng())
.append("destLat", getDestLat())
.append("deliveryDate", getDeliveryDate())
.append("vehiclePlate", getVehiclePlate())
.append("shipperName", getShipperName())
.append("shipperPhone", getShipperPhone())
.append("receiverName", getReceiverName())
.append("receiverPhone", getReceiverPhone())
.append("receiverOrgName", getReceiverOrgName())
.append("deliveryTon", getDeliveryTon())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("isDelete", getIsDelete())
.toString();
.append("id", getId())
.append("orderNo", getOrderNo())
.append("xmMs", getXmMs())
.append("xmNo", getXmNo())
.append("wlNo", getWlNo())
.append("wlMs", getWlMs())
.append("realQty", getRealQty())
.append("dw", getDw())
.append("sapNo", getSapNo())
.append("gysMc", getGysMc())
.append("originName", getOriginName())
.append("originLng", getOriginLng())
.append("originLat", getOriginLat())
.append("destName", getDestName())
.append("destLng", getDestLng())
.append("destLat", getDestLat())
.append("deliveryDate", getDeliveryDate())
.append("plateNo", getplateNo())
.append("driverName", getDriverName())
.append("driverPhone", getDriverPhone())
.append("shipperName", getShipperName())
.append("shipperPhone", getShipperPhone())
.append("receiverName", getReceiverName())
.append("receiverPhone", getReceiverPhone())
.append("receiverOrgName", getReceiverOrgName())
.append("deliveryTon", getDeliveryTon())
.append("goodsSize", getGoodsSize())
.append("orderStatus", getOrderStatus())
.append("suggestFee", getSuggestFee())
.append("actualFee", getActualFee())
.append("tollFee", getTollFee())
.append("totalKm", getTotalKm())
.append("vehicleTypeId", getVehicleTypeId())
.append("vehicleTypeName", getVehicleTypeName())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("isDelete", getIsDelete())
.toString();
}
}

View File

@@ -0,0 +1,25 @@
package com.delivery.project.document.domain;
import lombok.Data;
import java.math.BigDecimal;
/** 按单号部分字段更新动态UPDATE覆盖该单号下所有行 */
@Data
public class DeliveryOrderPatch {
private String plateNo;
// 地理位置
private BigDecimal originLng;
private BigDecimal originLat;
private BigDecimal destLng;
private BigDecimal destLat;
// 状态 & 费用
private String orderStatus; // "2" 配送中 / "3" 已签收
private BigDecimal actualFee;
private BigDecimal totalKm;
private BigDecimal tollFee;
// 元数据
private String updateBy;
}

View File

@@ -0,0 +1,132 @@
package com.delivery.project.document.domain;
import java.math.BigDecimal;
import com.delivery.framework.aspectj.lang.annotation.Excel;
import com.delivery.framework.web.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
/**
* 物料字典对象 mtd
*
* @author delivery
* @date 2025-10-23
*/
public class Mtd extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 主键ID */
private Long id;
/** 物料编号 */
@Excel(name = "物料编号")
private String wlNo;
/** 物料描述 */
@Excel(name = "物料描述")
private String wlMs;
/** 计量单位 */
@Excel(name = "计量单位")
private String dw;
/** 单件重量(kg) */
@Excel(name = "单件重量(kg)")
private BigDecimal weightKg;
/** 单件体积(立方米) */
@Excel(name = "单件体积(立方米)")
private BigDecimal volumeM3;
/** 是否删除(0正常 1删除) */
@Excel(name = "是否删除(0正常 1删除)")
private String isDelete;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setWlNo(String wlNo)
{
this.wlNo = wlNo;
}
public String getWlNo()
{
return wlNo;
}
public void setWlMs(String wlMs)
{
this.wlMs = wlMs;
}
public String getWlMs()
{
return wlMs;
}
public void setDw(String dw)
{
this.dw = dw;
}
public String getDw()
{
return dw;
}
public void setWeightKg(BigDecimal weightKg)
{
this.weightKg = weightKg;
}
public BigDecimal getWeightKg()
{
return weightKg;
}
public void setVolumeM3(BigDecimal volumeM3)
{
this.volumeM3 = volumeM3;
}
public BigDecimal getVolumeM3()
{
return volumeM3;
}
public void setIsDelete(String isDelete)
{
this.isDelete = isDelete;
}
public String getIsDelete()
{
return isDelete;
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("wlNo", getWlNo())
.append("wlMs", getWlMs())
.append("dw", getDw())
.append("weightKg", getWeightKg())
.append("volumeM3", getVolumeM3())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("isDelete", getIsDelete())
.toString();
}
}

View File

@@ -145,6 +145,9 @@ public class RkInfo extends BaseEntity {
@Excel(name = "库位码")
private String pcode;
@Excel(name = "出库类型名称")
private String ckTypeName;
/** 库位16进制编码 */
@Excel(name = "库位16进制编码")
private String pcodeId;
@@ -204,6 +207,12 @@ public class RkInfo extends BaseEntity {
@Excel(name = "供应计划ID")
private Long gysJhId;
// ===================== MTD 侧重量/体积 =====================
/** 单件重量(kg) */
private BigDecimal weightKg;
/** 单件体积(m³) */
private BigDecimal volumeM3;
// ===================== Getter / Setter =====================
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
@@ -310,6 +319,9 @@ public class RkInfo extends BaseEntity {
public String getCkLihuoY() { return ckLihuoY; }
public void setCkLihuoY(String ckLihuoY) { this.ckLihuoY = ckLihuoY; }
public String getCkTypeName() { return ckTypeName; }
public void setCkTypeName(String ckTypeName) { this.ckTypeName = ckTypeName; }
public String getTeamCode() { return teamCode; }
public void setTeamCode(String teamCode) { this.teamCode = teamCode; }
@@ -334,6 +346,11 @@ public class RkInfo extends BaseEntity {
public Long getGysJhId() { return gysJhId; }
public void setGysJhId(Long gysJhId) { this.gysJhId = gysJhId; }
public BigDecimal getWeightKg() { return weightKg; }
public void setWeightKg(BigDecimal weightKg) { this.weightKg = weightKg; }
public BigDecimal getVolumeM3() { return volumeM3; }
public void setVolumeM3(BigDecimal volumeM3) { this.volumeM3 = volumeM3; }
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
@@ -374,6 +391,7 @@ public class RkInfo extends BaseEntity {
.append("ckLihuoY", getCkLihuoY())
.append("teamCode", getTeamCode())
.append("ckType", getCkType())
.append("ckTypeName", getCkTypeName())
.append("ckRemark", getCkRemark())
.append("rkTime", getRkTime())
.append("lyTime", getLyTime())
@@ -385,6 +403,8 @@ public class RkInfo extends BaseEntity {
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("weightKg", getWeightKg())
.append("volumeM3", getVolumeM3())
.toString();
}
}

View File

@@ -0,0 +1,258 @@
package com.delivery.project.document.domain;
import java.math.BigDecimal;
import java.util.Date;
import com.delivery.framework.web.domain.BaseEntity;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.delivery.framework.aspectj.lang.annotation.Excel;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
/**
* 车辆档案对象 vehicle
*
* @author delivery
* @date 2025-10-23
*/
public class Vehicle extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 主键ID */
private Long id;
/** 车牌号(唯一) */
@Excel(name = "车牌号", readConverterExp = "唯=一")
private String plateNo;
/** VIN/车架号 */
@Excel(name = "VIN/车架号")
private String vin;
/** 车型IDvehicle_type.id */
@Excel(name = "车型ID", readConverterExp = "v=ehicle_type.id")
private Long vehicleTypeId;
/** 申报承重(吨) */
@Excel(name = "申报承重(吨)")
private BigDecimal declaredWeightTon;
/** 申报载方(立方米) */
@Excel(name = "申报载方(立方米)")
private BigDecimal declaredVolumeM3;
/** 司机姓名 */
@Excel(name = "司机姓名")
private String driverName;
/** 司机手机号 */
@Excel(name = "司机手机号")
private String driverPhone;
/** 司机身份证号(可选) */
@Excel(name = "司机身份证号(可选)")
private String driverIdNo;
/** 驾驶证号(可选) */
@Excel(name = "驾驶证号(可选)")
private String driverLicenseNo;
/** 准驾车型(可选) */
@Excel(name = "准驾车型(可选)")
private String driverLicenseLevel;
/** 年检到期日 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "年检到期日", width = 30, dateFormat = "yyyy-MM-dd")
private Date inspectionExpireDate;
/** 保险到期日 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "保险到期日", width = 30, dateFormat = "yyyy-MM-dd")
private Date insuranceExpireDate;
/** 状态(0启用 1停用 2维修) */
@Excel(name = "状态(0启用 1停用 2维修)")
private String status;
/** 是否删除(0正常 1删除) */
@Excel(name = "是否删除(0正常 1删除)")
private String isDelete;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setPlateNo(String plateNo)
{
this.plateNo = plateNo;
}
public String getPlateNo()
{
return plateNo;
}
public void setVin(String vin)
{
this.vin = vin;
}
public String getVin()
{
return vin;
}
public void setVehicleTypeId(Long vehicleTypeId)
{
this.vehicleTypeId = vehicleTypeId;
}
public Long getVehicleTypeId()
{
return vehicleTypeId;
}
public void setDeclaredWeightTon(BigDecimal declaredWeightTon)
{
this.declaredWeightTon = declaredWeightTon;
}
public BigDecimal getDeclaredWeightTon()
{
return declaredWeightTon;
}
public void setDeclaredVolumeM3(BigDecimal declaredVolumeM3)
{
this.declaredVolumeM3 = declaredVolumeM3;
}
public BigDecimal getDeclaredVolumeM3()
{
return declaredVolumeM3;
}
public void setDriverName(String driverName)
{
this.driverName = driverName;
}
public String getDriverName()
{
return driverName;
}
public void setDriverPhone(String driverPhone)
{
this.driverPhone = driverPhone;
}
public String getDriverPhone()
{
return driverPhone;
}
public void setDriverIdNo(String driverIdNo)
{
this.driverIdNo = driverIdNo;
}
public String getDriverIdNo()
{
return driverIdNo;
}
public void setDriverLicenseNo(String driverLicenseNo)
{
this.driverLicenseNo = driverLicenseNo;
}
public String getDriverLicenseNo()
{
return driverLicenseNo;
}
public void setDriverLicenseLevel(String driverLicenseLevel)
{
this.driverLicenseLevel = driverLicenseLevel;
}
public String getDriverLicenseLevel()
{
return driverLicenseLevel;
}
public void setInspectionExpireDate(Date inspectionExpireDate)
{
this.inspectionExpireDate = inspectionExpireDate;
}
public Date getInspectionExpireDate()
{
return inspectionExpireDate;
}
public void setInsuranceExpireDate(Date insuranceExpireDate)
{
this.insuranceExpireDate = insuranceExpireDate;
}
public Date getInsuranceExpireDate()
{
return insuranceExpireDate;
}
public void setStatus(String status)
{
this.status = status;
}
public String getStatus()
{
return status;
}
public void setIsDelete(String isDelete)
{
this.isDelete = isDelete;
}
public String getIsDelete()
{
return isDelete;
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("plateNo", getPlateNo())
.append("vin", getVin())
.append("vehicleTypeId", getVehicleTypeId())
.append("declaredWeightTon", getDeclaredWeightTon())
.append("declaredVolumeM3", getDeclaredVolumeM3())
.append("driverName", getDriverName())
.append("driverPhone", getDriverPhone())
.append("driverIdNo", getDriverIdNo())
.append("driverLicenseNo", getDriverLicenseNo())
.append("driverLicenseLevel", getDriverLicenseLevel())
.append("inspectionExpireDate", getInspectionExpireDate())
.append("insuranceExpireDate", getInsuranceExpireDate())
.append("status", getStatus())
.append("remark", getRemark())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("isDelete", getIsDelete())
.toString();
}
}

View File

@@ -0,0 +1,178 @@
package com.delivery.project.document.domain;
import java.math.BigDecimal;
import com.delivery.framework.aspectj.lang.annotation.Excel;
import com.delivery.framework.web.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
/**
* 车型定义对象 vehicle_type
*
* @author delivery
* @date 2025-10-23
*/
public class VehicleType extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 主键ID */
private Long id;
/** 车型编码(唯一) */
@Excel(name = "车型编码", readConverterExp = "唯=一")
private String typeCode;
/** 车型名称(如:微面/小面/3米8/5米2/6米8/7米6/9米6/13米/17米5 */
@Excel(name = "车型名称", readConverterExp = "如=:微面/小面/3米8/5米2/6米8/7米6/9米6/13米/17米5")
private String typeName;
/** 承重下限(吨, 含) */
@Excel(name = "承重下限(吨, 含)")
private BigDecimal weightMinTon;
/** 承重上限(吨, 含) */
@Excel(name = "承重上限(吨, 含)")
private BigDecimal weightMaxTon;
/** 载方下限(立方米, 含) */
@Excel(name = "载方下限(立方米, 含)")
private BigDecimal volumeMinM3;
/** 载方上限(立方米, 含) */
@Excel(name = "载方上限(立方米, 含)")
private BigDecimal volumeMaxM3;
/** 每公里单价(元/km) */
@Excel(name = "每公里单价(元/km)")
private BigDecimal unitPricePerKm;
/** 状态(0启用 1停用) */
@Excel(name = "状态(0启用 1停用)")
private String status;
/** 是否删除(0正常 1删除) */
@Excel(name = "是否删除(0正常 1删除)")
private String isDelete;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setTypeCode(String typeCode)
{
this.typeCode = typeCode;
}
public String getTypeCode()
{
return typeCode;
}
public void setTypeName(String typeName)
{
this.typeName = typeName;
}
public String getTypeName()
{
return typeName;
}
public void setWeightMinTon(BigDecimal weightMinTon)
{
this.weightMinTon = weightMinTon;
}
public BigDecimal getWeightMinTon()
{
return weightMinTon;
}
public void setWeightMaxTon(BigDecimal weightMaxTon)
{
this.weightMaxTon = weightMaxTon;
}
public BigDecimal getWeightMaxTon()
{
return weightMaxTon;
}
public void setVolumeMinM3(BigDecimal volumeMinM3)
{
this.volumeMinM3 = volumeMinM3;
}
public BigDecimal getVolumeMinM3()
{
return volumeMinM3;
}
public void setVolumeMaxM3(BigDecimal volumeMaxM3)
{
this.volumeMaxM3 = volumeMaxM3;
}
public BigDecimal getVolumeMaxM3()
{
return volumeMaxM3;
}
public void setUnitPricePerKm(BigDecimal unitPricePerKm)
{
this.unitPricePerKm = unitPricePerKm;
}
public BigDecimal getUnitPricePerKm()
{
return unitPricePerKm;
}
public void setStatus(String status)
{
this.status = status;
}
public String getStatus()
{
return status;
}
public void setIsDelete(String isDelete)
{
this.isDelete = isDelete;
}
public String getIsDelete()
{
return isDelete;
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("typeCode", getTypeCode())
.append("typeName", getTypeName())
.append("weightMinTon", getWeightMinTon())
.append("weightMaxTon", getWeightMaxTon())
.append("volumeMinM3", getVolumeMinM3())
.append("volumeMaxM3", getVolumeMaxM3())
.append("unitPricePerKm", getUnitPricePerKm())
.append("status", getStatus())
.append("remark", getRemark())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("isDelete", getIsDelete())
.toString();
}
}

View File

@@ -0,0 +1,24 @@
// src/main/java/com/delivery/project/document/domain/dto/CalcSuggestFeeDTO.java
package com.delivery.project.document.domain.dto;
import lombok.Data;
import java.math.BigDecimal;
/**
* 计算建议费用 - 请求体 DTO
* 前端传入:货物重量、体积、行程公里数;若前端手动选择车型则传 vehicleTypeId。
*/
@Data
public class CalcSuggestFeeDTO {
/** 货物重量(单位:吨) */
private BigDecimal weightTon;
/** 货物体积(单位:立方米) */
private BigDecimal volumeM3;
/** 行程距离(单位:公里) */
private BigDecimal distanceKm;
/** 指定车型ID可选传入则按该车型计算建议费用 */
private Long vehicleTypeId;
}

View File

@@ -0,0 +1,28 @@
// CalcTotalWvDTO.java
package com.delivery.project.document.domain.dto;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
/**
* 计算总重量/总体积 请求DTO
* - 支持仅传 wlNos每个物料默认1件重复出现按数量累加
* - 也支持 itemswlNo + qty若两者同时传以 items 为准
*/
@Data
public class CalcTotalWvDTO {
/** 物料号列表(可重复;每次出现视作数量+1 */
private List<String> wlNos;
/** 物料+数量(更精确的传法) */
private List<Item> items;
@Data
public static class Item {
/** 物料号 */
private String wlNo;
/** 数量可空默认1<=0 会按1处理 */
private BigDecimal qty;
}
}

View File

@@ -0,0 +1,19 @@
package com.delivery.project.document.domain.dto;
import lombok.Data;
/** 已上传图片条目只传URL */
@Data
public class DeliveryAttachItemDTO {
/** 场景ORIGIN / DEST必填 */
private String scene;
/** 类型SIGN_DRIVER / SIGN_COURIER / SIGN_RECEIVER / PHOTO_SITE / PHOTO_BILL必填 */
private String bizType;
/** 图片URL必填 */
private String url;
/** 排序(可选) */
private Long sortNo;
/** 备注(可选) */
private String remark;
}

View File

@@ -0,0 +1,37 @@
package com.delivery.project.document.domain.dto;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.List;
/** 执行配送单据绑定URL + 回写经纬度/司机/状态) */
@Data
public class DeliveryExecuteBindDTO {
/** 配送单据号delivery_order.order_no */
@NotNull
private String orderNo;
/** 当前执行场景ORIGIN / DEST */
@NotNull
private String scene;
/** 司机信息ORIGIN 场景带上DEST 可补齐) */
private String driverName;
private String driverPhone;
private String plateNo;//配送车牌
/** APP 当前定位 */
private Double lng;
private Double lat;
/** 费用(仅在 DEST 时会回写,可选) */
private BigDecimal actualFee; // 实际费用
private BigDecimal tollFee; // 高速费用
/** 本次执行的附件条目URL 列表) */
@NotNull
private List<DeliveryAttachItemDTO> attachments;
}

View File

@@ -0,0 +1,105 @@
package com.delivery.project.document.domain.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/**
* 配送单据创建入参对象(头 + 行)
* 说明:用于前端创建配送单时提交数据,后端写入 delivery_order 表
*
* @author delivery
* @date 2025-10-28
*/
@Data
public class DeliveryOrderCreateDTO {
/**
* 配送单据号,可传可不传;
* 不传时由后端自动生成(格式如 DO202510280001
*/
private String orderNo;
// ==================== 头部信息(整单通用) ====================
/** 起始地点名称 */
private String originName;
/** 起始地点经度 */
private BigDecimal originLng;
/** 起始地点纬度 */
private BigDecimal originLat;
/** 目的地点名称 */
private String destName;
/** 目的地点经度 */
private BigDecimal destLng;
/** 目的地点纬度 */
private BigDecimal destLat;
/** 配送日期yyyy-MM-dd 格式) */
@JsonFormat(pattern = "yyyy-MM-dd")
private Date deliveryDate;
/** 配送车辆车牌号 */
private String plateNo;
/** 发货人姓名 */
private String shipperName;
/** 发货人联系电话 */
private String shipperPhone;
/** 收货人姓名 */
private String receiverName;
/** 收货人联系电话 */
private String receiverPhone;
/** 收货单位名称 */
private String receiverOrgName;
/** 配送吨位(单位:吨) */
private BigDecimal deliveryTon;
/** 货物体积(单位:立方米) */
private BigDecimal goodsSize;
/** 单据状态(默认 0待发货1起运2送达 */
private String orderStatus;
/** 车辆类型ID外键对应 vehicle_type 表主键) */
private Long vehicleTypeId;
/** 车辆类型名称小型面包车、9.6米货车等) */
private String vehicleTypeName;
/** 系统建议运费(根据车型和里程计算) */
private BigDecimal suggestFee;
/** 实际运费(人工调整或司机确认后) */
private BigDecimal actualFee;
/** 过路费(收费站、高速费等额外费用) */
private BigDecimal tollFee;
/** 配送总里程(单位:公里) */
private BigDecimal totalKm;
/** 备注说明(可填写其他补充信息) */
private String remark;
// ==================== 行明细(多物料行) ====================
/**
* 物料明细项列表(同一单号下多条记录)
* 每条记录包含项目、物料、数量、单位等信息
*/
private List<DeliveryOrderLineDTO> items;
}

View File

@@ -0,0 +1,17 @@
package com.delivery.project.document.domain.dto;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class DeliveryOrderLineDTO {
private String xmMs; // 项目描述
private String xmNo; // 项目号
private String wlNo; // 物料号
private String wlMs; // 物料描述
private BigDecimal realQty; // 实际入库数量
private String dw; // 计量单位
private String sapNo; // SAP订单编号
private String gysMc; // 供应商名称
}

View File

@@ -6,31 +6,114 @@ import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/**
* 配送单据(创建入参)
* 说明:前端提交新建配送单据时使用
*/
@Data
public class DeliveryOrderSaveDTO {
// ===================== 项目信息 =====================
/** 项目描述(来源项目/业务项目名称) */
private String xmMs;
/** 项目号(来源项目编号) */
private String xmNo;
// ===================== 物料信息 =====================
/** 物料号 */
private String wlNo;
/** 物料描述 */
private String wlMs;
/** 实际入库数量(与配送关联的数量) */
private BigDecimal realQty;
/** 计量单位(如:个、米、吨等) */
private String dw;
/** SAP 订单编号(便于对账/追踪) */
private String sapNo;
/** 供应商名称 */
private String gysMc;
// ===================== 起止地点 =====================
/** 起始地点名称 */
private String originName;
/** 起始地点经度WGS84一般为经度小数度 */
private BigDecimal originLng;
/** 起始地点纬度WGS84一般为纬度小数度 */
private BigDecimal originLat;
/** 目的地点名称 */
private String destName;
/** 目的地点经度WGS84 */
private BigDecimal destLng;
/** 目的地点纬度WGS84 */
private BigDecimal destLat;
// ===================== 配送信息 =====================
/** 配送日期(计划/实际发车日期) */
private Date deliveryDate;
private String vehiclePlate;
/** 配送车牌 */
private String plateNo;
/** 发货人姓名 */
private String shipperName;
/** 发货人联系方式 */
private String shipperPhone;
/** 接收人姓名 */
private String receiverName;
/** 接收人联系方式 */
private String receiverPhone;
/** 接收单位名称 */
private String receiverOrgName;
/** 配送吨位(货重,单位:吨) */
private BigDecimal deliveryTon;
/** 附件列表前端从上传接口获取的URL传入 */
/** 货物尺寸(㎡),即占地面积 */
private BigDecimal goodsSize;
// ===================== 车型(新增) =====================
/** 车辆类型ID关联 vehicle_type 表主键) */
private Long vehicleTypeId;
/** 车辆类型名称(冗余存储,便于展示与历史追溯) */
private String vehicleTypeName;
// ===================== 费用与里程(新增) =====================
/** 建议费用(按车型单价与里程计算的推荐值) */
private BigDecimal suggestFee;
/** 实际费用(最终确认费用) */
private BigDecimal actualFee;
/** 高速费用(本次运输产生的过路/高速费) */
private BigDecimal tollFee;
/** 总公里数(本次运输的行驶里程,单位:公里) */
private BigDecimal totalKm;
// ===================== 附件 =====================
/** 附件列表(前端从上传接口获取的 URL 传入) */
private List<DeliveryAttachUploadDTO> attachments;
}

View File

@@ -0,0 +1,13 @@
package com.delivery.project.document.domain.dto;
import lombok.Data;
@Data
public class DeliverySimpleLocationDTO {
/** 经度 */
private Double lng;
/** 纬度 */
private Double lat;
}

View File

@@ -0,0 +1,31 @@
package com.delivery.project.document.domain.dto;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
/** APP 上报定位请求体 */
@Data
public class LocationReportDTO {
/** 车牌号(维度之一) */
@NotBlank(message = "车牌号不能为空")
private String plateNo;
/** 配送单号(维度之一,必填) */
@NotBlank(message = "配送单号不能为空")
private String orderNo;
/** 经度保留6位小数 */
@NotNull(message = "经度不能为空")
private BigDecimal lng;
/** 纬度保留6位小数 */
@NotNull(message = "纬度不能为空")
private BigDecimal lat;
/** 客户端时间戳(可选) */
private Long clientTs;
}

View File

@@ -0,0 +1,57 @@
// src/main/java/com/delivery/project/document/domain/vo/CalcSuggestFeeVO.java
package com.delivery.project.document.domain.vo;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
/**
* 计算建议费用 - 响应体 VO
* 返回推荐/选定的车型、单价、建议费用,以及候选车型列表。
*/
@Data
public class CalcSuggestFeeVO {
/** 选定/推荐车型ID */
private Long vehicleTypeId;
/** 选定/推荐车型名称 */
private String vehicleTypeName;
/** 每公里单价(单位:元/公里) */
private BigDecimal unitPricePerKm;
/** 建议费用单位四舍五入保留2位小数 */
private BigDecimal suggestFee;
/** 候选车型列表(按价格/容量排序) */
private List<VehicleTypeOptionVO> candidates;
/**
* 候选车型项 VO
* 表示一条可选车型的基本信息。
*/
@Data
public static class VehicleTypeOptionVO {
/** 车型ID */
private Long id;
/** 车型名称 */
private String name;
/** 每公里单价(单位:元/公里) */
private BigDecimal unitPricePerKm;
/** 承重下限(单位:吨,含) */
private BigDecimal weightMinTon;
/** 承重上限(单位:吨,含) */
private BigDecimal weightMaxTon;
/** 载方下限(单位:立方米,含) */
private BigDecimal volumeMinM3;
/** 载方上限(单位:立方米,含) */
private BigDecimal volumeMaxM3;
}
}

View File

@@ -0,0 +1,57 @@
package com.delivery.project.document.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
* 配送单据汇总视图对象
*
* 用途:
* - 用于列表页或统计页面展示配送单的核心概要信息;
* - 不含附件、物料明细,只反映每张单据的基础汇总信息。
*
* @author delivery
* @date 2025-10-29
*/
@Data
public class DeliveryOrderGroupVO {
/** 配送单号 */
private String orderNo;
/** 配送日期格式yyyy-MM-dd */
@JsonFormat(pattern = "yyyy-MM-dd")
private Date deliveryDate;
/** 起始地点名称(发货点) */
private String originName;
/** 目的地点名称(收货点) */
private String destName;
/** 车辆车牌号 */
private String plateNo;
/** 配送单状态 */
private String orderStatus;
/** 发货人姓名 */
private String shipperName;
/** 收货人姓名 */
private String receiverName;
/** 物料行数(明细数量,用于显示一单包含多少种货物) */
private Integer itemCount;
/** 数量合计(汇总所有明细的总数量,可为空) */
private BigDecimal totalQty;
}

View File

@@ -0,0 +1,16 @@
// TotalWvVO.java
package com.delivery.project.document.domain.vo;
import lombok.Data;
import java.math.BigDecimal;
/**
* 仅返回总重量与总体积
*/
@Data
public class TotalWvVO {
/** 总重量kg */
private BigDecimal totalWeightKg;
/** 总体积 */
private BigDecimal totalVolumeM3;
}

View File

@@ -58,4 +58,10 @@ public interface DeliveryAttachmentMapper
* @return 结果
*/
public int deleteDeliveryAttachmentByIds(Long[] ids);
/**
* 批量插入
* @param list
*/
void batchInsert(List<DeliveryAttachment> list);
}

View File

@@ -0,0 +1,74 @@
package com.delivery.project.document.mapper;
import com.delivery.project.document.domain.DeliveryLocationRecord;
import com.delivery.project.document.domain.dto.DeliverySimpleLocationDTO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface DeliveryLocationRecordMapper {
/** 插入一条定位记录 */
int insert(DeliveryLocationRecord record);
/** 批量插入定位记录 */
int insertBatch(List<DeliveryLocationRecord> batch);
/**
* 查询配送定位记录(车牌+单号维度)
*
* @param id 配送定位记录(车牌+单号维度)主键
* @return 配送定位记录(车牌+单号维度)
*/
public DeliveryLocationRecord selectDeliveryLocationRecordById(Long id);
/**
* 查询配送定位记录(车牌+单号维度)列表
*
* @param deliveryLocationRecord 配送定位记录(车牌+单号维度)
* @return 配送定位记录(车牌+单号维度)集合
*/
public List<DeliveryLocationRecord> selectDeliveryLocationRecordList(DeliveryLocationRecord deliveryLocationRecord);
/**
* 新增配送定位记录(车牌+单号维度)
*
* @param deliveryLocationRecord 配送定位记录(车牌+单号维度)
* @return 结果
*/
public int insertDeliveryLocationRecord(DeliveryLocationRecord deliveryLocationRecord);
/**
* 修改配送定位记录(车牌+单号维度)
*
* @param deliveryLocationRecord 配送定位记录(车牌+单号维度)
* @return 结果
*/
public int updateDeliveryLocationRecord(DeliveryLocationRecord deliveryLocationRecord);
/**
* 删除配送定位记录(车牌+单号维度)
*
* @param id 配送定位记录(车牌+单号维度)主键
* @return 结果
*/
public int deleteDeliveryLocationRecordById(Long id);
/**
* 批量删除配送定位记录(车牌+单号维度)
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteDeliveryLocationRecordByIds(Long[] ids);
/**
* 查询车辆轨迹点
*
* @param plateNo 订单编号
* @param plateNo 车牌号
* @return 轨迹点
*/
List<DeliverySimpleLocationDTO> selectTrackPoints(String orderNo, String plateNo);
}

View File

@@ -2,6 +2,8 @@ package com.delivery.project.document.mapper;
import java.util.List;
import com.delivery.project.document.domain.DeliveryOrder;
import com.delivery.project.document.domain.vo.DeliveryOrderGroupVO;
import org.apache.ibatis.annotations.Param;
/**
* 配送单据主Mapper接口
@@ -58,4 +60,36 @@ public interface DeliveryOrderMapper
* @return 结果
*/
public int deleteDeliveryOrderByIds(Long[] ids);
/**
* 批量插入
*
* @param rows
* @return
*/
int batchInsert(@Param("list") List<DeliveryOrder> rows);
/**
* 分组查询
*
* @param query
* @return
*/
List<DeliveryOrderGroupVO> selectGroupList(DeliveryOrder query);
/**
* 根据单号查询
*
* @param plateNo
* @return
*/
List<DeliveryOrder> selectByOrderNo(String orderNo);
/**
* 根据单号查询
*
* @param orderNo
* @return
*/
DeliveryOrder selectDeliveryOrderByOrderNo(String orderNo);
}

View File

@@ -0,0 +1,73 @@
package com.delivery.project.document.mapper;
import java.util.List;
import com.delivery.project.document.domain.Mtd;
import org.apache.ibatis.annotations.Param;
/**
* 物料字典Mapper接口
*
* @author delivery
* @date 2025-10-23
*/
public interface MtdMapper
{
/**
* 查询物料字典
*
* @param id 物料字典主键
* @return 物料字典
*/
public Mtd selectMtdById(Long id);
/**
* 查询物料字典列表
*
* @param mtd 物料字典
* @return 物料字典集合
*/
public List<Mtd> selectMtdList(Mtd mtd);
/**
* 新增物料字典
*
* @param mtd 物料字典
* @return 结果
*/
public int insertMtd(Mtd mtd);
/**
* 修改物料字典
*
* @param mtd 物料字典
* @return 结果
*/
public int updateMtd(Mtd mtd);
/**
* 删除物料字典
*
* @param id 物料字典主键
* @return 结果
*/
public int deleteMtdById(Long id);
/**
* 批量删除物料字典
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteMtdByIds(Long[] ids);
/**
* 批量 UPSERT同 wlNo 更新,不存在则新增)
* 依赖 wl_no 唯一索引
*/
int batchUpsertMtd(List<Mtd> list);
/**
* 根据多个物料编号查询物料信息
*/
List<Mtd> selectByWlNos(@Param("wlNos") List<String> wlNos);
}

View File

@@ -66,4 +66,6 @@ public interface RkInfoMapper
* 不新增 resultMap / VO直接用 RkInfoResult 映射需要的字段
*/
List<RkInfo> selectGroupedByBill(@Param("q") RkInfo rkInfo);
}

View File

@@ -0,0 +1,61 @@
package com.delivery.project.document.mapper;
import java.util.List;
import com.delivery.project.document.domain.Vehicle;
/**
* 车辆档案Mapper接口
*
* @author delivery
* @date 2025-10-23
*/
public interface VehicleMapper
{
/**
* 查询车辆档案
*
* @param id 车辆档案主键
* @return 车辆档案
*/
public Vehicle selectVehicleById(Long id);
/**
* 查询车辆档案列表
*
* @param vehicle 车辆档案
* @return 车辆档案集合
*/
public List<Vehicle> selectVehicleList(Vehicle vehicle);
/**
* 新增车辆档案
*
* @param vehicle 车辆档案
* @return 结果
*/
public int insertVehicle(Vehicle vehicle);
/**
* 修改车辆档案
*
* @param vehicle 车辆档案
* @return 结果
*/
public int updateVehicle(Vehicle vehicle);
/**
* 删除车辆档案
*
* @param id 车辆档案主键
* @return 结果
*/
public int deleteVehicleById(Long id);
/**
* 批量删除车辆档案
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteVehicleByIds(Long[] ids);
}

View File

@@ -0,0 +1,80 @@
package com.delivery.project.document.mapper;
import java.math.BigDecimal;
import java.util.List;
import com.delivery.project.document.domain.VehicleType;
/**
* 车型定义Mapper接口
*
* @author delivery
* @date 2025-10-23
*/
public interface VehicleTypeMapper
{
/**
* 查询车型定义
*
* @param id 车型定义主键
* @return 车型定义
*/
public VehicleType selectVehicleTypeById(Long id);
/**
* 查询车型定义列表
*
* @param vehicleType 车型定义
* @return 车型定义集合
*/
public List<VehicleType> selectVehicleTypeList(VehicleType vehicleType);
/**
* 新增车型定义
*
* @param vehicleType 车型定义
* @return 结果
*/
public int insertVehicleType(VehicleType vehicleType);
/**
* 修改车型定义
*
* @param vehicleType 车型定义
* @return 结果
*/
public int updateVehicleType(VehicleType vehicleType);
/**
* 删除车型定义
*
* @param id 车型定义主键
* @return 结果
*/
public int deleteVehicleTypeById(Long id);
/**
* 批量删除车型定义
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteVehicleTypeByIds(Long[] ids);
/**
* 批量查询车型定义
*
* @param w 宽
* @param v 高
* @return 批量结果
*/
List<VehicleType> selectMatchTypes(BigDecimal w, Comparable<BigDecimal> v);
/**
* 批量查询车型定义
*
* @param w 宽
* @param v 高
* @return 批量结果
*/
List<VehicleType> selectFallbackTypes(BigDecimal w, BigDecimal v);
}

View File

@@ -2,8 +2,12 @@ package com.delivery.project.document.service;
import java.util.List;
import com.delivery.project.document.domain.DeliveryAttachment;
import com.delivery.project.document.domain.dto.DeliveryAttachUploadDTO;
import com.delivery.project.document.domain.dto.DeliveryExecuteBindDTO;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
/**
* 配送附件Service接口
*
@@ -60,4 +64,9 @@ public interface IDeliveryAttachmentService
*/
public int deleteDeliveryAttachmentById(Long id);
int executeBind(DeliveryExecuteBindDTO dto);
List<String> upload(MultipartFile[] files, DeliveryAttachUploadDTO dto, HttpServletRequest request);
}

View File

@@ -2,7 +2,11 @@ package com.delivery.project.document.service;
import java.util.List;
import com.delivery.project.document.domain.DeliveryOrder;
import com.delivery.project.document.domain.RkInfo;
import com.delivery.project.document.domain.dto.DeliveryOrderCreateDTO;
import com.delivery.project.document.domain.dto.DeliveryOrderSaveDTO;
import com.delivery.project.document.domain.vo.DeliveryOrderGroupVO;
import org.springframework.beans.factory.annotation.Value;
/**
* 配送单据主Service接口
@@ -68,4 +72,36 @@ public interface IDeliveryOrderService
* @return
*/
int saveOrderWithAttachments(DeliveryOrderSaveDTO dto, String username);
/**
* 新建配送单据:同一单号多行写入
* @param dto
* @return
*/
String createOrder(DeliveryOrderCreateDTO dto);
/**
* 列表:按单号分组(分页)
* @param query
* @return
*/
List<DeliveryOrderGroupVO> listGroup(DeliveryOrder query);
/**
* 详情:按单号查询所有行
* @param plateNo
* @return
*/
/** 详情:按单号查行 */
List<DeliveryOrder> listByOrderNo(String orderNo);
/**
* 从智慧实物系统查询待配送出库单据
*
* @return rk_info 列表
*/
List<RkInfo> listWisdomRkForDelivery();
}

View File

@@ -0,0 +1,75 @@
package com.delivery.project.document.service;
import com.delivery.project.document.domain.DeliveryLocationRecord;
import com.delivery.project.document.domain.dto.DeliverySimpleLocationDTO;
import com.delivery.project.document.domain.dto.LocationReportDTO;
import java.util.List;
import java.util.Map;
/** 定位上报服务接口(实现层由 Impl 提供) */
public interface ILocationReportService {
/**
* APP 上报定位
* @param dto 上报体
* @param username 当前操作人(由 Controller 使用 BaseController.getUsername() 传入)
* @return 处理结果
*/
Map<String, Object> report(LocationReportDTO dto, String username);
/**
* 查询配送定位记录(车牌+单号维度)
*
* @param id 配送定位记录(车牌+单号维度)主键
* @return 配送定位记录(车牌+单号维度)
*/
public DeliveryLocationRecord selectDeliveryLocationRecordById(Long id);
/**
* 查询配送定位记录(车牌+单号维度)列表
*
* @param deliveryLocationRecord 配送定位记录(车牌+单号维度)
* @return 配送定位记录(车牌+单号维度)集合
*/
public List<DeliveryLocationRecord> selectDeliveryLocationRecordList(DeliveryLocationRecord deliveryLocationRecord);
/**
* 新增配送定位记录(车牌+单号维度)
*
* @param deliveryLocationRecord 配送定位记录(车牌+单号维度)
* @return 结果
*/
public int insertDeliveryLocationRecord(DeliveryLocationRecord deliveryLocationRecord);
/**
* 修改配送定位记录(车牌+单号维度)
*
* @param deliveryLocationRecord 配送定位记录(车牌+单号维度)
* @return 结果
*/
public int updateDeliveryLocationRecord(DeliveryLocationRecord deliveryLocationRecord);
/**
* 批量删除配送定位记录(车牌+单号维度)
*
* @param ids 需要删除的配送定位记录(车牌+单号维度)主键集合
* @return 结果
*/
public int deleteDeliveryLocationRecordByIds(Long[] ids);
/**
* 删除配送定位记录(车牌+单号维度)信息
*
* @param id 配送定位记录(车牌+单号维度)主键
* @return 结果
*/
public int deleteDeliveryLocationRecordById(Long id);
/**
* 查询轨迹点
* @param plateNo 订单号
* @param plateNo 车牌号
* @return 轨迹点
*/
List<DeliverySimpleLocationDTO> selectTrackPoints(String orderNo, String plateNo);
}

View File

@@ -0,0 +1,80 @@
package com.delivery.project.document.service;
import java.util.List;
import com.delivery.project.document.domain.Mtd;
import com.delivery.project.document.domain.dto.CalcTotalWvDTO;
import com.delivery.project.document.domain.vo.TotalWvVO;
/**
* 物料字典Service接口
*
* @author delivery
* @date 2025-10-23
*/
public interface IMtdService
{
/**
* 查询物料字典
*
* @param id 物料字典主键
* @return 物料字典
*/
public Mtd selectMtdById(Long id);
/**
* 查询物料字典列表
*
* @param mtd 物料字典
* @return 物料字典集合
*/
public List<Mtd> selectMtdList(Mtd mtd);
/**
* 新增物料字典
*
* @param mtd 物料字典
* @return 结果
*/
public int insertMtd(Mtd mtd);
/**
* 修改物料字典
*
* @param mtd 物料字典
* @return 结果
*/
public int updateMtd(Mtd mtd);
/**
* 批量删除物料字典
*
* @param ids 需要删除的物料字典主键集合
* @return 结果
*/
public int deleteMtdByIds(Long[] ids);
/**
* 删除物料字典信息
*
* @param id 物料字典主键
* @return 结果
*/
public int deleteMtdById(Long id);
/**
* 导入物料字典数据
*
* @param list 物料字典数据列表
* @param username 更新者
* @return 结果
*/
int importMtd(List<Mtd> list, String username);
/**
* 计算总重量和体积
*
* @param dto 计算参数
* @return 结果
*/
TotalWvVO calcTotalWv(CalcTotalWvDTO dto);
}

View File

@@ -0,0 +1,61 @@
package com.delivery.project.document.service;
import java.util.List;
import com.delivery.project.document.domain.Vehicle;
/**
* 车辆档案Service接口
*
* @author delivery
* @date 2025-10-23
*/
public interface IVehicleService
{
/**
* 查询车辆档案
*
* @param id 车辆档案主键
* @return 车辆档案
*/
public Vehicle selectVehicleById(Long id);
/**
* 查询车辆档案列表
*
* @param vehicle 车辆档案
* @return 车辆档案集合
*/
public List<Vehicle> selectVehicleList(Vehicle vehicle);
/**
* 新增车辆档案
*
* @param vehicle 车辆档案
* @return 结果
*/
public int insertVehicle(Vehicle vehicle);
/**
* 修改车辆档案
*
* @param vehicle 车辆档案
* @return 结果
*/
public int updateVehicle(Vehicle vehicle);
/**
* 批量删除车辆档案
*
* @param ids 需要删除的车辆档案主键集合
* @return 结果
*/
public int deleteVehicleByIds(Long[] ids);
/**
* 删除车辆档案信息
*
* @param id 车辆档案主键
* @return 结果
*/
public int deleteVehicleById(Long id);
}

View File

@@ -0,0 +1,70 @@
package com.delivery.project.document.service;
import java.util.List;
import com.delivery.project.document.domain.VehicleType;
import com.delivery.project.document.domain.dto.CalcSuggestFeeDTO;
import com.delivery.project.document.domain.vo.CalcSuggestFeeVO;
/**
* 车型定义Service接口
*
* @author delivery
* @date 2025-10-23
*/
public interface IVehicleTypeService
{
/**
* 查询车型定义
*
* @param id 车型定义主键
* @return 车型定义
*/
public VehicleType selectVehicleTypeById(Long id);
/**
* 查询车型定义列表
*
* @param vehicleType 车型定义
* @return 车型定义集合
*/
public List<VehicleType> selectVehicleTypeList(VehicleType vehicleType);
/**
* 新增车型定义
*
* @param vehicleType 车型定义
* @return 结果
*/
public int insertVehicleType(VehicleType vehicleType);
/**
* 修改车型定义
*
* @param vehicleType 车型定义
* @return 结果
*/
public int updateVehicleType(VehicleType vehicleType);
/**
* 批量删除车型定义
*
* @param ids 需要删除的车型定义主键集合
* @return 结果
*/
public int deleteVehicleTypeByIds(Long[] ids);
/**
* 删除车型定义信息
*
* @param id 车型定义主键
* @return 结果
*/
public int deleteVehicleTypeById(Long id);
/**
* 计算建议运费
* @param dto
* @return
*/
CalcSuggestFeeVO calcSuggestFee(CalcSuggestFeeDTO dto);
}

View File

@@ -0,0 +1,161 @@
package com.delivery.project.document.service;
import com.delivery.framework.config.LocationReportProperties;
import com.delivery.project.document.domain.DeliveryLocationRecord;
import com.delivery.project.document.mapper.DeliveryLocationRecordMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.*;
/**
* 定位数据批量落库任务
* ---------------------------------------------
* 功能说明:
* 每隔固定时间默认15秒执行一次将 Redis 中缓存的定位数据批量写入数据库。
*
* 核心逻辑:
* 1. 每辆车(车牌 + 订单号)对应一个独立的 Redis ZSET
* 2. ZSET 内部保存该车在 15 秒窗口内的唯一经纬度坐标;
* 3. 定时任务遍历所有活跃 ZSET
* - 取出所有在窗口时间内的坐标;
* - 对比上次已落库坐标(跨窗口去重);
* - 批量插入数据库;
* - 清理已处理坐标;
* - 更新“最后一次落库坐标”;
*
* 优点:
* ✅ ZSET 天然去重(同一经纬度不会重复插入);
* ✅ 批量落库,减轻数据库压力;
* ✅ 支持数千辆车同时上报;
* ✅ 可水平扩展。
*/
@Slf4j
@Component
@EnableScheduling
@RequiredArgsConstructor
public class LocationBatchFlusher {
/** Redis 客户端 */
private final StringRedisTemplate redis;
/** 业务配置项(包含定时间隔、缓存时间等) */
private final LocationReportProperties props;
/** MyBatis Mapper用于批量插入数据库 */
private final DeliveryLocationRecordMapper mapper;
/** 活跃车辆集合Set 结构保存所有存在ZSET的key */
private static final String KEY_INDEX = "delivery:loc:index";
/** 状态存储Hash 结构,保存每个车的最后一次写库坐标) */
private static final String KEY_STATE_PREFIX = "delivery:loc:state:";
/**
* 定时任务每隔固定时间如15秒执行一次
* fixedDelay 表示“上一次任务执行完毕后延迟多少毫秒再执行下一次”
* 取值来自配置类 LocationReportProperties.minPersistIntervalMs
*/
@Scheduled(fixedDelayString = "#{@locationReportProperties.minPersistIntervalMs}")
public void flushWindow() {
// 获取所有活跃ZSET的key每辆车对应一个
Set<String> keys = redis.opsForSet().members(KEY_INDEX);
if (keys == null || keys.isEmpty()) return;
long now = System.currentTimeMillis(); // 当前时间戳
List<DeliveryLocationRecord> batch = new ArrayList<>(); // 批量写入列表
// 遍历每一辆车的ZSET
for (String zsetKey : keys) {
String plate = parsePlate(zsetKey); // 提取车牌号
String order = parseOrder(zsetKey); // 提取订单号
String stateKey = KEY_STATE_PREFIX + plate + ":" + order; // 每辆车的状态key
// ✅ 从ZSET中获取当前时间之前的所有坐标
Set<ZSetOperations.TypedTuple<String>> tuples =
redis.opsForZSet().rangeByScoreWithScores(zsetKey, 0, now);
if (tuples == null || tuples.isEmpty()) continue;
// 读取“上次已落库的最后一条坐标”,用于跨窗口去重
String lastWritten = (String) redis.opsForHash().get(stateKey, "lastWritten");
// 临时变量记录这次批量中最后插入的member
String lastInsertedMember = lastWritten;
// 遍历ZSET中的坐标数据
for (ZSetOperations.TypedTuple<String> t : tuples) {
String member = t.getValue(); // 坐标字符串,如 "120.125600,30.276800"
Double score = t.getScore(); // ZSET中存储的时间戳首次上报时间
if (member == null || score == null) continue;
// ❗跨窗口去重逻辑:如果当前坐标与上次写库坐标完全一致,则跳过
if (member.equals(lastWritten)) continue;
// 拆分经纬度
String[] arr = member.split(",");
if (arr.length != 2) continue;
// 构造数据库实体对象
DeliveryLocationRecord r = new DeliveryLocationRecord();
r.setPlateNo(plate);
r.setOrderNo(order);
r.setLng(new BigDecimal(arr[0]));
r.setLat(new BigDecimal(arr[1]));
r.setReportedAt(new Date(score.longValue())); // 上报时间取ZSET中的分数
r.setSource("app");
r.setRemark("tick_" + props.getMinPersistIntervalMs());
r.setCreateBy("");
r.setIsDelete("0");
batch.add(r);
// 记录这次批量最后插入的坐标用于更新lastWritten
lastInsertedMember = member;
}
// 清理:删除本窗口内已处理的坐标数据
redis.opsForZSet().removeRangeByScore(zsetKey, 0, now);
// 记录最新写库坐标,用于下一轮去重
if (lastInsertedMember != null) {
redis.opsForHash().put(stateKey, "lastWritten", lastInsertedMember);
redis.expire(stateKey, props.getStateTtlHours(), java.util.concurrent.TimeUnit.HOURS);
}
}
// 若本轮有新数据,则批量插入数据库
if (!batch.isEmpty()) {
int n = mapper.insertBatch(batch);
log.info("[location-flush] 本轮批量落库完成:插入 {} 条定位记录,共处理 {} 个ZSET", n, keys.size());
}
}
/**
* 从ZSET的key中解析出车牌号
* 例如delivery:loc:buf:浙A88888:D202511030001 → 返回“浙A88888”
*/
private String parsePlate(String zsetKey) {
String prefix = "delivery:loc:buf:";
if (!zsetKey.startsWith(prefix)) return "";
String tail = zsetKey.substring(prefix.length());
int i = tail.indexOf(':');
return i < 0 ? tail : tail.substring(0, i);
}
/**
* 从ZSET的key中解析出订单号
* 例如delivery:loc:buf:浙A88888:D202511030001 → 返回“D202511030001”
*/
private String parseOrder(String zsetKey) {
int p = zsetKey.lastIndexOf(':');
return p < 0 ? "" : zsetKey.substring(p + 1);
}
}

View File

@@ -1,19 +1,33 @@
package com.delivery.project.document.service.impl;
import java.util.List;
import java.util.Locale;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.text.SimpleDateFormat;
import java.util.*;
import com.delivery.common.exception.ServiceException;
import com.delivery.common.utils.DateUtils;
import com.delivery.common.utils.MinioUtil;
import com.delivery.common.utils.StringUtils;
import com.delivery.project.document.domain.DeliveryOrder;
import com.delivery.project.document.domain.dto.DeliveryAttachItemDTO;
import com.delivery.project.document.domain.dto.DeliveryAttachUploadDTO;
import com.delivery.project.document.domain.dto.DeliveryExecuteBindDTO;
import com.delivery.project.document.mapper.DeliveryOrderMapper;
import com.delivery.project.document.service.IDeliveryOrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.delivery.project.document.mapper.DeliveryAttachmentMapper;
import com.delivery.project.document.domain.DeliveryAttachment;
import com.delivery.project.document.service.IDeliveryAttachmentService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import javax.servlet.http.HttpServletRequest;
/**
* 配送附件Service业务层处理
@@ -22,11 +36,18 @@ import org.springframework.web.multipart.MultipartFile;
* @date 2025-10-15
*/
@Service
@Slf4j
public class DeliveryAttachmentServiceImpl implements IDeliveryAttachmentService
{
@Autowired
private DeliveryAttachmentMapper deliveryAttachmentMapper;
@Autowired
private DeliveryOrderMapper deliveryOrderMapper;
@Autowired
private IDeliveryOrderService deliveryOrderService;
/**
* 查询配送附件
*
@@ -101,5 +122,133 @@ public class DeliveryAttachmentServiceImpl implements IDeliveryAttachmentService
return deliveryAttachmentMapper.deleteDeliveryAttachmentById(id);
}
@Override
@Transactional(rollbackFor = Exception.class)
public int executeBind(DeliveryExecuteBindDTO dto) {
// 1) 校验订单存在
DeliveryOrder exist = deliveryOrderMapper.selectDeliveryOrderByOrderNo(dto.getOrderNo());
if (exist == null) {
throw new ServiceException("配送单不存在:" + dto.getOrderNo());
}
if (dto.getAttachments() == null || dto.getAttachments().isEmpty()) {
throw new ServiceException("附件列表不能为空");
}
// 2) 批量插入附件(只传 URL
List<DeliveryAttachment> list = new ArrayList<>();
// String username = getUsername();
String username = "大爷的!!!";
for (DeliveryAttachItemDTO it : dto.getAttachments()) {
if (it == null) continue;
if (it.getScene() == null || it.getBizType() == null || it.getUrl() == null) continue;
DeliveryAttachment a = new DeliveryAttachment();
a.setOrderNo(dto.getOrderNo());
a.setScene(it.getScene());
a.setBizType(it.getBizType());
a.setUrl(it.getUrl());
a.setSortNo(it.getSortNo());
a.setRemark(it.getRemark());
a.setStatus("1"); // 已绑定
a.setIsDelete("0");
a.setCreateBy(username);
a.setCreateTime(DateUtils.getNowDate());
list.add(a);
}
if (list.isEmpty()) {
throw new ServiceException("有效附件条目为空");
}
deliveryAttachmentMapper.batchInsert(list);
// 3) 回写配送单(仅填需要更新的字段),交给已有 updateDeliveryOrder(按 order_no 更新)
DeliveryOrder patch = new DeliveryOrder();
patch.setOrderNo(dto.getOrderNo());
String scene = dto.getScene() == null ? "" : dto.getScene().toUpperCase(Locale.ROOT);
if ("ORIGIN".equals(scene)) {
// 起点:司机信息 + 起点经纬度 + 状态=1
if (dto.getDriverName() != null) patch.setDriverName(dto.getDriverName());
if (dto.getDriverPhone() != null) patch.setDriverPhone(dto.getDriverPhone());
if (dto.getPlateNo() != null) patch.setplateNo(dto.getPlateNo());
if (dto.getLng() != null) patch.setOriginLng(BigDecimal.valueOf(dto.getLng()));
if (dto.getLat() != null) patch.setOriginLat(BigDecimal.valueOf(dto.getLat()));
patch.setOrderStatus("2");
} else if ("DEST".equals(scene)) {
// 终点:终点经纬度 + 状态=2
if (dto.getLng() != null) patch.setDestLng(BigDecimal.valueOf(dto.getLng()));
if (dto.getLat() != null) patch.setDestLat(BigDecimal.valueOf(dto.getLat()));
if (dto.getActualFee() != null) {
if (dto.getActualFee().compareTo(BigDecimal.ZERO) < 0) {
throw new ServiceException("实际费用不能为负数");
}
patch.setActualFee(dto.getActualFee());
}
if (dto.getTollFee() != null) {
if (dto.getTollFee().compareTo(BigDecimal.ZERO) < 0) {
throw new ServiceException("高速费用不能为负数");
}
patch.setTollFee(dto.getTollFee());
}
patch.setOrderStatus("3");
}
deliveryOrderService.updateDeliveryOrder(patch);
return list.size();
}
// 保存目录 D:\delivery
private static final String BASE_PATH = "D:/delivery";
@Override
public List<String> upload(MultipartFile[] files, DeliveryAttachUploadDTO dto, HttpServletRequest request) {
if (files == null || files.length == 0) {
throw new ServiceException("上传文件不能为空");
}
if (dto == null || StringUtils.isBlank(dto.getScene()) || StringUtils.isBlank(dto.getBizType())) {
throw new ServiceException("scene 和 bizType 不能为空");
}
String scene = dto.getScene().trim().toLowerCase(Locale.ROOT);
String bizType = dto.getBizType().trim().toLowerCase(Locale.ROOT);
String dateFolder = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
Path targetDir = Paths.get(BASE_PATH, dateFolder, scene, bizType);
try {
Files.createDirectories(targetDir);
List<String> urls = new ArrayList<>();
for (MultipartFile file : files) {
if (file.isEmpty()) continue;
String original = file.getOriginalFilename();
String ext = "";
if (original != null && original.contains(".")) {
ext = original.substring(original.lastIndexOf("."));
}
String newName = UUID.randomUUID().toString().replace("-", "") + ext;
Path targetFile = targetDir.resolve(newName);
Files.copy(file.getInputStream(), targetFile, StandardCopyOption.REPLACE_EXISTING);
// 自动识别访问前缀http://ip:port
String baseUrl = ServletUriComponentsBuilder.fromRequestUri(request)
.replacePath(null)
.build()
.toUriString();
// 返回完整URL
String url = baseUrl + "/delivery/" + dateFolder + "/" + scene + "/" + bizType + "/" + newName;
urls.add(url);
}
log.info("【附件上传成功】{}", urls);
return urls;
} catch (Exception e) {
log.error("文件保存失败", e);
throw new ServiceException("文件保存失败:" + e.getMessage());
}
}
}

View File

@@ -1,22 +1,36 @@
package com.delivery.project.document.service.impl;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.delivery.common.exception.ServiceException;
import com.delivery.common.utils.DateUtils;
import com.delivery.common.utils.StringUtils;
import com.delivery.common.utils.http.HttpUtils;
import com.delivery.framework.web.domain.AjaxResult;
import com.delivery.project.document.domain.DeliveryAttachment;
import com.delivery.project.document.domain.RkInfo;
import com.delivery.project.document.domain.dto.DeliveryAttachUploadDTO;
import com.delivery.project.document.domain.dto.DeliveryOrderCreateDTO;
import com.delivery.project.document.domain.dto.DeliveryOrderLineDTO;
import com.delivery.project.document.domain.dto.DeliveryOrderSaveDTO;
import com.delivery.project.document.domain.vo.DeliveryOrderGroupVO;
import com.delivery.project.document.mapper.DeliveryAttachmentMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.delivery.project.document.mapper.DeliveryOrderMapper;
import com.delivery.project.document.domain.DeliveryOrder;
import com.delivery.project.document.service.IDeliveryOrderService;
import org.springframework.transaction.annotation.Transactional;
import static com.delivery.common.utils.SecurityUtils.getUsername;
/**
* 配送单据主Service业务层处理
*
@@ -33,6 +47,9 @@ public class DeliveryOrderServiceImpl implements IDeliveryOrderService
@Autowired
private DeliveryAttachmentMapper deliveryAttachmentMapper;
@Value("${wisdom.base-url}")
private String wisdomBaseUrl;
/**
* 查询配送单据主
*
@@ -77,8 +94,10 @@ public class DeliveryOrderServiceImpl implements IDeliveryOrderService
* @return 结果
*/
@Override
public int updateDeliveryOrder(DeliveryOrder deliveryOrder)
{
public int updateDeliveryOrder(DeliveryOrder deliveryOrder) {
if (deliveryOrder.getOrderNo() == null) {
throw new ServiceException("orderNo 不能为空");
}
deliveryOrder.setUpdateTime(DateUtils.getNowDate());
return deliveryOrderMapper.updateDeliveryOrder(deliveryOrder);
}
@@ -118,7 +137,7 @@ public class DeliveryOrderServiceImpl implements IDeliveryOrderService
order.setCreateTime(DateUtils.getNowDate());
deliveryOrderMapper.insertDeliveryOrder(order);
Long orderId = order.getId();
String orderNo = order.getOrderNo();
int affected = 1;
// 2. 保存附件
@@ -131,7 +150,7 @@ public class DeliveryOrderServiceImpl implements IDeliveryOrderService
}
DeliveryAttachment att = new DeliveryAttachment();
att.setOrderId(orderId);
att.setOrderNo(orderNo);
att.setScene(item.getScene());
att.setBizType(item.getBizType());
att.setUrl(item.getUrl());
@@ -150,4 +169,140 @@ public class DeliveryOrderServiceImpl implements IDeliveryOrderService
return affected;
}
@Override
@Transactional(rollbackFor = Exception.class)
public String createOrder(DeliveryOrderCreateDTO dto) {
// ========== 1. 生成配送单号 ==========
// 如果前端未指定单号则按规则生成一个唯一编号示例DO20251027104532123
String orderNo = (dto.getOrderNo() == null || dto.getOrderNo().trim().isEmpty())
? "DO" + DateUtils.dateTimeNow("yyyyMMddHHmmssSSS")
: dto.getOrderNo().trim();
// 当前时间,用于所有行的 create_time
Date now = DateUtils.getNowDate();
// 定义要插入的记录集合
List<DeliveryOrder> rows = new ArrayList<>();
// ========== 2. 遍历每一条物料明细 ==========
for (DeliveryOrderLineDTO it : dto.getItems()) {
// 创建实体对象(每一行对应一条数据库记录)
DeliveryOrder row = new DeliveryOrder();
// ========== 2.1 公共头部字段(相同单号下字段一致) ==========
row.setOrderNo(orderNo); // 同单号绑定
row.setOriginName(dto.getOriginName()); // 起点名称
row.setDestName(dto.getDestName()); // 终点名称
row.setDeliveryDate(dto.getDeliveryDate()); // 配送日期
row.setShipperName(dto.getShipperName()); // 发货人姓名
row.setShipperPhone(dto.getShipperPhone()); // 发货人电话
row.setReceiverName(dto.getReceiverName()); // 收货人姓名
row.setReceiverPhone(dto.getReceiverPhone()); // 收货人电话
row.setReceiverOrgName(dto.getReceiverOrgName()); // 收货单位名称
row.setDeliveryTon(dto.getDeliveryTon()); // 配送吨位
row.setGoodsSize(dto.getGoodsSize()); // 货物尺寸
row.setOrderStatus(dto.getOrderStatus() == null ? "1" : dto.getOrderStatus()); // 默认状态:已接单
row.setVehicleTypeId(dto.getVehicleTypeId()); // 车辆类型ID
row.setVehicleTypeName(dto.getVehicleTypeName()); // 车辆类型名称
row.setSuggestFee(dto.getSuggestFee()); // 建议费用
row.setActualFee(dto.getActualFee()); // 实际费用
row.setTollFee(dto.getTollFee()); // 高速费
row.setTotalKm(dto.getTotalKm()); // 总公里数
row.setRemark(dto.getRemark()); // 备注
// ========== 2.2 明细字段(每行不同) ==========
row.setXmMs(it.getXmMs()); // 项目描述
row.setXmNo(it.getXmNo()); // 项目号
row.setWlNo(it.getWlNo()); // 物料号
row.setWlMs(it.getWlMs()); // 物料描述
row.setRealQty(it.getRealQty()); // 实际入库数量
row.setDw(it.getDw()); // 计量单位
row.setSapNo(it.getSapNo()); // SAP订单号
row.setGysMc(it.getGysMc()); // 供应商名称
// ========== 2.3 通用字段 ==========
row.setIsDelete("0"); // 正常状态
row.setCreateTime(now); // 创建时间
row.setCreateBy(getUsername());
// row.setCreateBy("大爷的!");
// 添加到批量集合中
rows.add(row);
}
// ========== 3. 批量落库 ==========
if (!rows.isEmpty()) {
// 使用 Mapper.xml 的 <foreach> 一次插入多条记录
deliveryOrderMapper.batchInsert(rows);
}
// ========== 4. 返回单号 ==========
return orderNo;
}
@Override
public List<DeliveryOrderGroupVO> listGroup(DeliveryOrder query) {
return deliveryOrderMapper.selectGroupList(query);
}
@Override
public List<DeliveryOrder> listByOrderNo(String orderNo) {
return deliveryOrderMapper.selectByOrderNo(orderNo);
}
/**
* 从【智慧实物管理系统】查询“待配送的出库单据”
* 对应智慧实物接口GET /wisdom/stock/delivery
*
* @return List<RkInfo>
*/
@Override
public List<RkInfo> listWisdomRkForDelivery() {
// 1. 拼接请求地址
String url = wisdomBaseUrl + "/wisdom/stock/delivery";
// 2. 发送 GET 请求
String respJson = HttpUtils.sendGet(url);
if (StringUtils.isEmpty(respJson)) {
throw new ServiceException("调用智慧实物接口失败:返回结果为空");
}
// 3. 解析 JSON统一返回结构{ code, msg, data }
JSONObject json = JSON.parseObject(respJson);
if (json == null) {
throw new ServiceException("调用智慧实物接口失败:解析返回结果为空");
}
Integer code = json.getInteger("code");
if (code == null || code != 200) {
String msg = json.getString("msg");
throw new ServiceException("调用智慧实物接口失败code=" + code + "msg=" + msg);
}
Object dataObj = json.get("data");
if (dataObj == null) {
// 没有数据就返回空集合,不算错误
return new ArrayList<>();
}
// data 可能本身就是数组,也可能是单个对象
JSONArray dataArr;
if (dataObj instanceof JSONArray) {
dataArr = (JSONArray) dataObj;
} else {
dataArr = JSON.parseArray(JSON.toJSONString(dataObj));
}
if (dataArr == null || dataArr.isEmpty()) {
return new ArrayList<>();
}
// 4. 转成 List<RkInfo>
return JSON.parseArray(dataArr.toJSONString(), RkInfo.class);
}
}

View File

@@ -0,0 +1,185 @@
package com.delivery.project.document.service.impl;
import com.delivery.common.exception.ServiceException;
import com.delivery.common.utils.DateUtils;
import com.delivery.framework.config.LocationReportProperties;
import com.delivery.project.document.domain.DeliveryLocationRecord;
import com.delivery.project.document.domain.dto.DeliverySimpleLocationDTO;
import com.delivery.project.document.domain.dto.LocationReportDTO;
import com.delivery.project.document.mapper.DeliveryLocationRecordMapper;
import com.delivery.project.document.service.ILocationReportService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* 定位上报:仅写 ZSET 缓冲member=lng,latscore=ts按 15s 周期由定时任务批量入库
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class LocationReportServiceImpl implements ILocationReportService {
@Autowired
private DeliveryLocationRecordMapper deliveryLocationRecordMapper;
private final StringRedisTemplate redis;
private final LocationReportProperties props;
private static final String KEY_BUF_PREFIX = "delivery:loc:buf:"; // delivery:loc:buf:{plate}:{order}
private static final String KEY_INDEX = "delivery:loc:index"; // 活跃 ZSET 索引Set
/**
* 上报车辆位置信息。
*
* @param dto 包含车辆位置相关信息的数据传输对象,必须包含车牌号、订单号、经纬度等字段
* @param username 当前操作用户标识(可用于日志或权限校验,当前未使用)
* @return 返回一个Map结构的结果包含是否成功缓冲、原因、车牌号、订单号及时间戳等信息
*/
@Override
public Map<String, Object> report(LocationReportDTO dto, String username) {
final String plate = dto.getPlateNo().trim();
final String orderNo = dto.getOrderNo().trim();
final String zsetKey = KEY_BUF_PREFIX + plate + ":" + orderNo;
final String lockKey = "delivery:loc:lock:" + plate + ":" + orderNo;
// 使用 Redis 分布式锁防止并发写入同一辆车的位置数据
Boolean locked = redis.opsForValue().setIfAbsent(lockKey, "1", props.getLockSeconds(), TimeUnit.SECONDS);
if (locked == null || !locked) {
return resp(false, "concurrent_skip", plate, orderNo, 0);
}
try {
BigDecimal lng = dto.getLng().setScale(6, RoundingMode.HALF_UP);
BigDecimal lat = dto.getLat().setScale(6, RoundingMode.HALF_UP);
long now = System.currentTimeMillis();
String member = lng.toPlainString() + "," + lat.toPlainString();
// ✅ 去重缓存:只在不存在时写入(等价 ZADD NX
redis.opsForZSet().addIfAbsent(zsetKey, member, now);
// 设置过期时间并记录该key为活跃key
redis.expire(zsetKey, props.getStateTtlHours(), TimeUnit.HOURS);
redis.opsForSet().add(KEY_INDEX, zsetKey);
return resp(true, "buffered", plate, orderNo, now);
} catch (Exception e) {
throw new ServiceException("定位上报处理失败:" + e.getMessage());
} finally {
// 释放分布式锁
redis.delete(lockKey);
}
}
/**
* 构造响应结果。
*
* @param ok 是否处理成功
* @param reason 处理结果的原因描述
* @param plate 车牌号码
* @param orderNo 订单编号
* @param ts 时间戳
* @return 封装后的响应Map对象
*/
private Map<String, Object> resp(boolean ok, String reason, String plate, String orderNo, long ts) {
Map<String, Object> m = new HashMap<>();
m.put("buffered", ok);
m.put("reason", reason);
m.put("plateNo", plate);
m.put("orderNo", orderNo);
m.put("ts", ts);
return m;
}
/**
* 查询配送定位记录(车牌+单号维度)
*
* @param id 配送定位记录(车牌+单号维度)主键
* @return 配送定位记录(车牌+单号维度)
*/
@Override
public DeliveryLocationRecord selectDeliveryLocationRecordById(Long id)
{
return deliveryLocationRecordMapper.selectDeliveryLocationRecordById(id);
}
/**
* 查询配送定位记录(车牌+单号维度)列表
*
* @param deliveryLocationRecord 配送定位记录(车牌+单号维度)
* @return 配送定位记录(车牌+单号维度)
*/
@Override
public List<DeliveryLocationRecord> selectDeliveryLocationRecordList(DeliveryLocationRecord deliveryLocationRecord)
{
return deliveryLocationRecordMapper.selectDeliveryLocationRecordList(deliveryLocationRecord);
}
/**
* 新增配送定位记录(车牌+单号维度)
*
* @param deliveryLocationRecord 配送定位记录(车牌+单号维度)
* @return 结果
*/
@Override
public int insertDeliveryLocationRecord(DeliveryLocationRecord deliveryLocationRecord)
{
deliveryLocationRecord.setCreateTime(DateUtils.getNowDate());
return deliveryLocationRecordMapper.insertDeliveryLocationRecord(deliveryLocationRecord);
}
/**
* 修改配送定位记录(车牌+单号维度)
*
* @param deliveryLocationRecord 配送定位记录(车牌+单号维度)
* @return 结果
*/
@Override
public int updateDeliveryLocationRecord(DeliveryLocationRecord deliveryLocationRecord)
{
deliveryLocationRecord.setUpdateTime(DateUtils.getNowDate());
return deliveryLocationRecordMapper.updateDeliveryLocationRecord(deliveryLocationRecord);
}
/**
* 批量删除配送定位记录(车牌+单号维度)
*
* @param ids 需要删除的配送定位记录(车牌+单号维度)主键
* @return 结果
*/
@Override
public int deleteDeliveryLocationRecordByIds(Long[] ids)
{
return deliveryLocationRecordMapper.deleteDeliveryLocationRecordByIds(ids);
}
/**
* 删除配送定位记录(车牌+单号维度)信息
*
* @param id 配送定位记录(车牌+单号维度)主键
* @return 结果
*/
@Override
public int deleteDeliveryLocationRecordById(Long id)
{
return deliveryLocationRecordMapper.deleteDeliveryLocationRecordById(id);
}
@Override
public List<DeliverySimpleLocationDTO> selectTrackPoints(String orderNo, String plateNo) {
return deliveryLocationRecordMapper.selectTrackPoints(orderNo, plateNo);
}
}

View File

@@ -0,0 +1,172 @@
package com.delivery.project.document.service.impl;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import com.delivery.common.exception.ServiceException;
import com.delivery.common.utils.DateUtils;
import com.delivery.project.document.domain.dto.CalcTotalWvDTO;
import com.delivery.project.document.domain.vo.TotalWvVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.delivery.project.document.mapper.MtdMapper;
import com.delivery.project.document.domain.Mtd;
import com.delivery.project.document.service.IMtdService;
import static com.delivery.common.utils.StringUtils.nvl;
/**
* 物料字典Service业务层处理
*
* @author delivery
* @date 2025-10-23
*/
@Service
public class MtdServiceImpl implements IMtdService
{
@Autowired
private MtdMapper mtdMapper;
/**
* 查询物料字典
*
* @param id 物料字典主键
* @return 物料字典
*/
@Override
public Mtd selectMtdById(Long id)
{
return mtdMapper.selectMtdById(id);
}
/**
* 查询物料字典列表
*
* @param mtd 物料字典
* @return 物料字典
*/
@Override
public List<Mtd> selectMtdList(Mtd mtd)
{
return mtdMapper.selectMtdList(mtd);
}
/**
* 新增物料字典
*
* @param mtd 物料字典
* @return 结果
*/
@Override
public int insertMtd(Mtd mtd)
{
mtd.setCreateTime(DateUtils.getNowDate());
return mtdMapper.insertMtd(mtd);
}
/**
* 修改物料字典
*
* @param mtd 物料字典
* @return 结果
*/
@Override
public int updateMtd(Mtd mtd)
{
mtd.setUpdateTime(DateUtils.getNowDate());
return mtdMapper.updateMtd(mtd);
}
/**
* 批量删除物料字典
*
* @param ids 需要删除的物料字典主键
* @return 结果
*/
@Override
public int deleteMtdByIds(Long[] ids)
{
return mtdMapper.deleteMtdByIds(ids);
}
/**
* 删除物料字典信息
*
* @param id 物料字典主键
* @return 结果
*/
@Override
public int deleteMtdById(Long id)
{
return mtdMapper.deleteMtdById(id);
}
// MtdServiceImpl.java 追加实现
@Override
public int importMtd(List<Mtd> list, String operator)
{
if (list == null || list.isEmpty()) {
throw new ServiceException("导入数据为空");
}
// 清洗与校验
List<Mtd> clean = new ArrayList<>(list.size());
for (Mtd m : list) {
if (m == null) continue;
if (m.getWlNo() == null || m.getWlNo().trim().isEmpty()) {
// wlNo 必填
continue;
}
m.setWlNo(m.getWlNo().trim());
if (m.getIsDelete() == null) m.setIsDelete("0");
// 写入操作人与时间
m.setUpdateBy(operator);
m.setUpdateTime(DateUtils.getNowDate());
// 如果是新增场景create_by/create_time 也一起填上ON DUPLICATE 不会覆盖 create_*
if (m.getCreateBy() == null) m.setCreateBy(operator);
if (m.getCreateTime() == null) m.setCreateTime(DateUtils.getNowDate());
clean.add(m);
}
if (clean.isEmpty()) {
throw new ServiceException("有效导入数据为空(缺少 wlNo");
}
// 批量 UPSERT依赖 mtd.wl_no 唯一索引)
return mtdMapper.batchUpsertMtd(clean);
}
@Override
public TotalWvVO calcTotalWv(CalcTotalWvDTO dto) {
if (dto == null || dto.getWlNos() == null || dto.getWlNos().isEmpty()) {
TotalWvVO empty = new TotalWvVO();
empty.setTotalWeightKg(BigDecimal.ZERO);
empty.setTotalVolumeM3(BigDecimal.ZERO);
return empty;
}
// 查询数据库中所有匹配的物料信息
List<Mtd> list = mtdMapper.selectByWlNos(dto.getWlNos());
BigDecimal totalWeight = BigDecimal.ZERO;
BigDecimal totalVolume = BigDecimal.ZERO;
for (Mtd m : list) {
BigDecimal w = m.getWeightKg() == null ? BigDecimal.ZERO : m.getWeightKg();
BigDecimal v = m.getVolumeM3() == null ? BigDecimal.ZERO : m.getVolumeM3();
totalWeight = totalWeight.add(w);
totalVolume = totalVolume.add(v);
}
TotalWvVO vo = new TotalWvVO();
vo.setTotalWeightKg(totalWeight);
vo.setTotalVolumeM3(totalVolume);
return vo;
}
}

View File

@@ -0,0 +1,96 @@
package com.delivery.project.document.service.impl;
import java.util.List;
import com.delivery.common.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.delivery.project.document.mapper.VehicleMapper;
import com.delivery.project.document.domain.Vehicle;
import com.delivery.project.document.service.IVehicleService;
/**
* 车辆档案Service业务层处理
*
* @author delivery
* @date 2025-10-23
*/
@Service
public class VehicleServiceImpl implements IVehicleService
{
@Autowired
private VehicleMapper vehicleMapper;
/**
* 查询车辆档案
*
* @param id 车辆档案主键
* @return 车辆档案
*/
@Override
public Vehicle selectVehicleById(Long id)
{
return vehicleMapper.selectVehicleById(id);
}
/**
* 查询车辆档案列表
*
* @param vehicle 车辆档案
* @return 车辆档案
*/
@Override
public List<Vehicle> selectVehicleList(Vehicle vehicle)
{
return vehicleMapper.selectVehicleList(vehicle);
}
/**
* 新增车辆档案
*
* @param vehicle 车辆档案
* @return 结果
*/
@Override
public int insertVehicle(Vehicle vehicle)
{
vehicle.setCreateTime(DateUtils.getNowDate());
return vehicleMapper.insertVehicle(vehicle);
}
/**
* 修改车辆档案
*
* @param vehicle 车辆档案
* @return 结果
*/
@Override
public int updateVehicle(Vehicle vehicle)
{
vehicle.setUpdateTime(DateUtils.getNowDate());
return vehicleMapper.updateVehicle(vehicle);
}
/**
* 批量删除车辆档案
*
* @param ids 需要删除的车辆档案主键
* @return 结果
*/
@Override
public int deleteVehicleByIds(Long[] ids)
{
return vehicleMapper.deleteVehicleByIds(ids);
}
/**
* 删除车辆档案信息
*
* @param id 车辆档案主键
* @return 结果
*/
@Override
public int deleteVehicleById(Long id)
{
return vehicleMapper.deleteVehicleById(id);
}
}

View File

@@ -0,0 +1,177 @@
package com.delivery.project.document.service.impl;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import com.delivery.common.utils.DateUtils;
import com.delivery.project.document.domain.dto.CalcSuggestFeeDTO;
import com.delivery.project.document.domain.vo.CalcSuggestFeeVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.delivery.project.document.mapper.VehicleTypeMapper;
import com.delivery.project.document.domain.VehicleType;
import com.delivery.project.document.service.IVehicleTypeService;
/**
* 车型定义Service业务层处理
*
* @author delivery
* @date 2025-10-23
*/
@Service
public class VehicleTypeServiceImpl implements IVehicleTypeService
{
@Autowired
private VehicleTypeMapper vehicleTypeMapper;
/**
* 查询车型定义
*
* @param id 车型定义主键
* @return 车型定义
*/
@Override
public VehicleType selectVehicleTypeById(Long id)
{
return vehicleTypeMapper.selectVehicleTypeById(id);
}
/**
* 查询车型定义列表
*
* @param vehicleType 车型定义
* @return 车型定义
*/
@Override
public List<VehicleType> selectVehicleTypeList(VehicleType vehicleType)
{
return vehicleTypeMapper.selectVehicleTypeList(vehicleType);
}
/**
* 新增车型定义
*
* @param vehicleType 车型定义
* @return 结果
*/
@Override
public int insertVehicleType(VehicleType vehicleType)
{
vehicleType.setCreateTime(DateUtils.getNowDate());
return vehicleTypeMapper.insertVehicleType(vehicleType);
}
/**
* 修改车型定义
*
* @param vehicleType 车型定义
* @return 结果
*/
@Override
public int updateVehicleType(VehicleType vehicleType)
{
vehicleType.setUpdateTime(DateUtils.getNowDate());
return vehicleTypeMapper.updateVehicleType(vehicleType);
}
/**
* 批量删除车型定义
*
* @param ids 需要删除的车型定义主键
* @return 结果
*/
@Override
public int deleteVehicleTypeByIds(Long[] ids)
{
return vehicleTypeMapper.deleteVehicleTypeByIds(ids);
}
/**
* 删除车型定义信息
*
* @param id 车型定义主键
* @return 结果
*/
@Override
public int deleteVehicleTypeById(Long id)
{
return vehicleTypeMapper.deleteVehicleTypeById(id);
}
@Override
public CalcSuggestFeeVO calcSuggestFee(CalcSuggestFeeDTO dto)
{
// ========== 0) 入参兜底 ==========
BigDecimal w = nvl(dto.getWeightTon()); // 货物重量(吨)
BigDecimal v = nvl(dto.getVolumeM3()); // 货物体积(立方米)
BigDecimal km = nvl(dto.getDistanceKm()); // 行程公里数(公里)
// ========== 1) 候选车型:严格匹配,若无则兜底匹配 ==========
List<VehicleType> candidates = vehicleTypeMapper.selectMatchTypes(w, v);
if (candidates == null || candidates.isEmpty()) {
candidates = vehicleTypeMapper.selectFallbackTypes(w, v);
}
if (candidates == null || candidates.isEmpty()) {
throw new RuntimeException("未找到适配车型,请检查车型配置或输入参数。");
}
// 稳定排序:单价升序 -> 承重上限升序 -> 载方上限升序 -> id 升序(数据库已排序,这里再兜一层)
candidates.sort(Comparator
.comparing(VehicleType::getUnitPricePerKm, Comparator.nullsLast(BigDecimal::compareTo))
.thenComparing(VehicleType::getWeightMaxTon, Comparator.nullsLast(BigDecimal::compareTo))
.thenComparing(VehicleType::getVolumeMaxM3, Comparator.nullsLast(BigDecimal::compareTo))
.thenComparing(VehicleType::getId, Comparator.nullsLast(Long::compareTo)));
// ========== 2) 选定车型:前端指定 or 系统推荐 ==========
VehicleType chosen;
if (dto.getVehicleTypeId() != null) {
// 前端点名车型:优先在候选中找,找不到则直接按主键查
chosen = candidates.stream()
.filter(t -> dto.getVehicleTypeId().equals(t.getId()))
.findFirst()
.orElseGet(() -> vehicleTypeMapper.selectVehicleTypeById(dto.getVehicleTypeId()));
if (chosen == null) {
throw new RuntimeException("指定车型不存在或已被禁用id=" + dto.getVehicleTypeId());
}
} else {
chosen = candidates.get(0);
}
// ========== 3) 计算建议费用:单价 × 公里数保留2位 ==========
BigDecimal price = chosen.getUnitPricePerKm();
if (price == null) {
throw new RuntimeException("车型【" + chosen.getTypeName() + "】未配置每公里单价unit_price_per_km 为空)");
}
BigDecimal fee = price.multiply(km).setScale(2, RoundingMode.HALF_UP);
// ========== 4) 组装返回 ==========
CalcSuggestFeeVO vo = new CalcSuggestFeeVO();
vo.setVehicleTypeId(chosen.getId());
vo.setVehicleTypeName(chosen.getTypeName());
vo.setUnitPricePerKm(price);
vo.setSuggestFee(fee);
// 候选项
List<CalcSuggestFeeVO.VehicleTypeOptionVO> list = new ArrayList<>(candidates.size());
for (VehicleType t : candidates) {
CalcSuggestFeeVO.VehicleTypeOptionVO o = new CalcSuggestFeeVO.VehicleTypeOptionVO();
o.setId(t.getId());
o.setName(t.getTypeName());
o.setUnitPricePerKm(t.getUnitPricePerKm());
o.setWeightMinTon(t.getWeightMinTon());
o.setWeightMaxTon(t.getWeightMaxTon());
o.setVolumeMinM3(t.getVolumeMinM3());
o.setVolumeMaxM3(t.getVolumeMaxM3());
list.add(o);
}
vo.setCandidates(list);
return vo;
}
private static BigDecimal nvl(BigDecimal x) {
return x == null ? BigDecimal.ZERO : x;
}
}