配送单据接口修改

This commit is contained in:
2025-10-17 09:33:53 +08:00
parent ab9a48f968
commit e24d6ef0b4
5 changed files with 339 additions and 64 deletions

View File

@@ -0,0 +1,189 @@
package com.delivery.common.utils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.InputStream;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 本地图片保存工具
* - 根目录photo.root默认 /data/upload/images
* - 目录结构:{documentType}/yyyy-MM-dd/
* - 文件命名:
* 入库billNo_gysMc_yyyyMMddHHmmssSSS_index.ext
* 出库billNo_xmMs_yyyyMMddHHmmssSSS_index.ext
*/
@Component
public class LocalPhotoUtil {
/** 磁盘根目录Linux 默认) */
@Value("${photo.root:/data/upload/images}")
private String rootPath;
@Value("${photo.base-url:http://192.168.1.28/files}")
private String baseUrl;
private static final String ILLEGAL_CHARS = "\\/:*?\"<>|";
private static final long MAX_SIZE = 10 * 1024 * 1024; // 10MB
private static final Set<String> ALLOWED =
new HashSet<>(Arrays.asList("jpg","jpeg","png","gif","webp","bmp"));
/** yyyy-MM-dd目录 */
private String today() {
return new SimpleDateFormat("yyyy-MM-dd").format(new Date());
}
/** yyyyMMddHHmmssSSS文件戳 */
private String nowStamp() {
return new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
}
/**
* 生成基础名:
* - 入库(photoType=0)billNo_gysMc
* - 出库(photoType=1)billNo_xmMs
* - 若为空 → 一律替换成 "无"
*/
public String buildBaseName(String photoType, String billNo, String xmMs, String gysMc) {
String safeBillNo = sanitize(defaultString(billNo));
String part;
if ("0".equals(photoType)) {
part = sanitize(defaultString(gysMc, ""));
} else {
part = sanitize(defaultString(xmMs, ""));
}
return sanitize(safeBillNo + "_" + part);
}
/**
* 保存单个文件
* @param file 文件
* @param subDir 一级子目录documentType
* @param baseName 基础名
* @param index 序号(从1开始)
*/
public SaveResult saveOne(MultipartFile file, String subDir, String baseName, int index) throws Exception {
if (file == null || file.isEmpty()) {
throw new IllegalArgumentException("文件为空");
}
if (file.getSize() > MAX_SIZE) {
throw new IllegalArgumentException("图片不能超过10MB");
}
String ext = resolveExt(file);
String safeSub = sanitize(limitLen(defaultString(subDir, ""), 40));
String dirPart = (safeSub.isEmpty() ? "" : safeSub + File.separator) + today() + File.separator;
String stamp = nowStamp();
String safeBase = sanitize(limitLen(baseName, 80));
String fileName = safeBase + "_" + stamp + "_" + index + ext;
File dir = new File(ensureTrailingSlash(rootPath), dirPart);
Files.createDirectories(dir.toPath());
File dest = new File(dir, fileName);
File tmp = new File(dir, fileName + ".uploading");
try (InputStream in = file.getInputStream()) {
Files.copy(in, tmp.toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING);
}
Files.move(tmp.toPath(), dest.toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING);
SaveResult r = new SaveResult();
String webPath = (dirPart + fileName).replace("\\", "/");
r.setWebPath(webPath); // /{subDir}/yyyy-MM-dd/xxx.jpg
r.setFileName(fileName);
r.setAbsPath(dest.getAbsolutePath());
r.setUrl(joinUrl(baseUrl, webPath)); // http://ip/files/...
r.setSize(file.getSize());
return r;
}
/** 批量保存 */
public List<SaveResult> saveBatch(List<MultipartFile> files, String subDir, String baseName) throws Exception {
if (files == null || files.isEmpty()) return Collections.emptyList();
List<SaveResult> list = new ArrayList<>(files.size());
for (int i = 0; i < files.size(); i++) {
list.add(saveOne(files.get(i), subDir, baseName, i + 1));
}
return list;
}
/** 扩展名解析 + 白名单校验 */
private String resolveExt(MultipartFile file) {
String origin = file.getOriginalFilename();
if (origin != null) {
int dot = origin.lastIndexOf('.');
if (dot >= 0) {
String pure = origin.substring(dot + 1).toLowerCase(Locale.ROOT);
if (ALLOWED.contains(pure)) return "." + pure;
}
}
String ct = String.valueOf(file.getContentType()).toLowerCase(Locale.ROOT);
if (ct.contains("png")) return ".png";
if (ct.contains("jpeg") || ct.contains("jpg")) return ".jpg";
if (ct.contains("gif")) return ".gif";
if (ct.contains("webp")) return ".webp";
if (ct.contains("bmp")) return ".bmp";
return ".bin";
}
/** 清洗文件名 */
public String sanitize(String name) {
if (name == null) return "unnamed";
String t = name.trim().replaceAll("\\s+", " ");
StringBuilder sb = new StringBuilder(t.length());
for (int i = 0; i < t.length(); i++) {
char c = t.charAt(i);
sb.append(ILLEGAL_CHARS.indexOf(c) >= 0 ? '_' : c);
}
String out = sb.toString();
String upper = out.toUpperCase(Locale.ROOT);
if (upper.matches("CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9]")) {
out = "_" + out;
}
if (out.length() > 100) out = out.substring(0, 100);
return out;
}
private String defaultString(String s) { return s == null ? "" : s; }
private String defaultString(String s, String def) { return (s == null || s.isEmpty()) ? def : s; }
private String ensureTrailingSlash(String p) {
return (p.endsWith("/") || p.endsWith("\\")) ? p : (p + File.separator);
}
private String limitLen(String s, int max) {
if (s == null) return "";
return s.length() > max ? s.substring(0, max) : s;
}
private String joinUrl(String base, String path) {
String b = base.endsWith("/") ? base.substring(0, base.length()-1) : base;
String p = path.startsWith("/") ? path.substring(1) : path;
return b + "/" + p;
}
/** 返回值对象 */
public static class SaveResult {
private String webPath; // 相对Web路径(/subDir/yyyy-MM-dd/xxx.jpg)
private String fileName; // 文件名
private String absPath; // 服务器绝对路径
private String url; // 可直接访问的URL
private long size; // 字节
public String getWebPath() { return webPath; }
public void setWebPath(String webPath) { this.webPath = webPath; }
public String getFileName() { return fileName; }
public void setFileName(String fileName) { this.fileName = fileName; }
public String getAbsPath() { return absPath; }
public void setAbsPath(String absPath) { this.absPath = absPath; }
public String getUrl() { return url; }
public void setUrl(String url) { this.url = url; }
public long getSize() { return size; }
public void setSize(long size) { this.size = size; }
}
}

View File

@@ -39,7 +39,7 @@ public class DeliveryOrderController extends BaseController
/** /**
* 查询配送单据主列表 * 查询配送单据主列表
*/ */
@PreAuthorize("@ss.hasPermi('document:order:list')") // @PreAuthorize("@ss.hasPermi('document:order:list')")
@GetMapping("/list") @GetMapping("/list")
public TableDataInfo list(DeliveryOrder deliveryOrder) public TableDataInfo list(DeliveryOrder deliveryOrder)
{ {
@@ -64,7 +64,7 @@ public class DeliveryOrderController extends BaseController
/** /**
* 获取配送单据主详细信息 * 获取配送单据主详细信息
*/ */
@PreAuthorize("@ss.hasPermi('document:order:query')") // @PreAuthorize("@ss.hasPermi('document:order:query')")
@GetMapping(value = "/{id}") @GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id) public AjaxResult getInfo(@PathVariable("id") Long id)
{ {
@@ -105,6 +105,11 @@ public class DeliveryOrderController extends BaseController
} }
/**
* 保存配送单
* @param dto
* @return
*/
// @PreAuthorize("@ss.hasPermi('document:order:add')") // @PreAuthorize("@ss.hasPermi('document:order:add')")
// @Log(title = "配送单据主-保存(含附件)", businessType = BusinessType.INSERT) // @Log(title = "配送单据主-保存(含附件)", businessType = BusinessType.INSERT)
@PostMapping("/save") @PostMapping("/save")

View File

@@ -2,6 +2,7 @@ package com.delivery.project.document.domain;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Date; import java.util.Date;
import java.util.List;
import com.delivery.framework.web.domain.BaseEntity; import com.delivery.framework.web.domain.BaseEntity;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
@@ -115,6 +116,12 @@ public class DeliveryOrder extends BaseEntity
@Excel(name = "是否删除", readConverterExp = "0=正常,1=已删除") @Excel(name = "是否删除", readConverterExp = "0=正常,1=已删除")
private String isDelete; private String isDelete;
/** 连表查询用:附件列表 */
private List<DeliveryAttachment> attachments;
public List<DeliveryAttachment> getAttachments() { return attachments; }
public void setAttachments(List<DeliveryAttachment> attachments) { this.attachments = attachments; }
public void setId(Long id) public void setId(Long id)
{ {
this.id = id; this.id = id;

View File

@@ -113,4 +113,8 @@ minio:
accessKey: admin accessKey: admin
secretKey: admin123 secretKey: admin123
bucketName: delivery bucketName: delivery
public-read: true public-read: true
upload:
base-dir: /data/upload/images
base-url: http://192.168.1.28/files

View File

@@ -3,75 +3,145 @@
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.delivery.project.document.mapper.DeliveryOrderMapper"> <mapper namespace="com.delivery.project.document.mapper.DeliveryOrderMapper">
<resultMap type="DeliveryOrder" id="DeliveryOrderResult"> <resultMap type="com.delivery.project.document.domain.DeliveryOrder" id="DeliveryOrderResult">
<result property="id" column="id" /> <!-- 用 <id> 保证连表折叠 -->
<result property="xmMs" column="xm_ms" /> <id property="id" column="id"/>
<result property="xmNo" column="xm_no" /> <result property="xmMs" column="xm_ms"/>
<result property="wlNo" column="wl_no" /> <result property="xmNo" column="xm_no"/>
<result property="wlMs" column="wl_ms" /> <result property="wlNo" column="wl_no"/>
<result property="realQty" column="real_qty" /> <result property="wlMs" column="wl_ms"/>
<result property="dw" column="dw" /> <result property="realQty" column="real_qty"/>
<result property="sapNo" column="sap_no" /> <result property="dw" column="dw"/>
<result property="gysMc" column="gys_mc" /> <result property="sapNo" column="sap_no"/>
<result property="remark" column="remark" /> <result property="gysMc" column="gys_mc"/>
<result property="originName" column="origin_name" /> <result property="remark" column="remark"/>
<result property="originLng" column="origin_lng" />
<result property="originLat" column="origin_lat" /> <result property="originName" column="origin_name"/>
<result property="destName" column="dest_name" /> <result property="originLng" column="origin_lng"/>
<result property="destLng" column="dest_lng" /> <result property="originLat" column="origin_lat"/>
<result property="destLat" column="dest_lat" />
<result property="deliveryDate" column="delivery_date" /> <result property="destName" column="dest_name"/>
<result property="vehiclePlate" column="vehicle_plate" /> <result property="destLng" column="dest_lng"/>
<result property="shipperName" column="shipper_name" /> <result property="destLat" column="dest_lat"/>
<result property="shipperPhone" column="shipper_phone" />
<result property="receiverName" column="receiver_name" /> <result property="deliveryDate" column="delivery_date"/>
<result property="receiverPhone" column="receiver_phone" /> <result property="vehiclePlate" column="vehicle_plate"/>
<result property="receiverOrgName" column="receiver_org_name" />
<result property="deliveryTon" column="delivery_ton" /> <result property="shipperName" column="shipper_name"/>
<result property="createBy" column="create_by" /> <result property="shipperPhone" column="shipper_phone"/>
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" /> <result property="receiverName" column="receiver_name"/>
<result property="updateTime" column="update_time" /> <result property="receiverPhone" column="receiver_phone"/>
<result property="isDelete" column="is_delete" /> <result property="receiverOrgName" column="receiver_org_name"/>
<result property="deliveryTon" column="delivery_ton"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="isDelete" column="is_delete"/>
</resultMap> </resultMap>
<!-- 附件行(按你给的 DeliveryAttachment 字段) -->
<resultMap id="DeliveryAttachmentInlineResult"
type="com.delivery.project.document.domain.DeliveryAttachment">
<id property="id" column="att_id"/>
<result property="orderId" column="att_order_id"/>
<result property="scene" column="att_scene"/>
<result property="bizType" column="att_biz_type"/>
<result property="url" column="att_url"/>
<result property="status" column="att_status"/>
<result property="sortNo" column="att_sort_no"/>
<result property="remark" column="att_remark"/>
<result property="createBy" column="att_create_by"/>
<result property="createTime" column="att_create_time"/>
<result property="updateBy" column="att_update_by"/>
<result property="updateTime" column="att_update_time"/>
<result property="isDelete" column="att_is_delete"/>
</resultMap>
<!-- 主实体 + 附件集合 -->
<resultMap id="DeliveryOrderWithAttachResult"
type="com.delivery.project.document.domain.DeliveryOrder"
extends="DeliveryOrderResult">
<collection property="attachments"
resultMap="DeliveryAttachmentInlineResult"/>
</resultMap>
<sql id="selectDeliveryOrderVoWithAttach">
select
do.id, do.xm_ms, do.xm_no, do.wl_no, do.wl_ms, do.real_qty, do.dw, do.sap_no, do.gys_mc,
do.remark, do.origin_name, do.origin_lng, do.origin_lat,
do.dest_name, do.dest_lng, do.dest_lat,
do.delivery_date, do.vehicle_plate,
do.shipper_name, do.shipper_phone,
do.receiver_name, do.receiver_phone, do.receiver_org_name,
do.delivery_ton, do.create_by, do.create_time, do.update_by, do.update_time, do.is_delete,
-- 附件列att_ 前缀)
da.id as att_id,
da.order_id as att_order_id,
da.scene as att_scene,
da.biz_type as att_biz_type,
da.url as att_url,
da.status as att_status,
da.sort_no as att_sort_no,
da.remark as att_remark,
da.create_by as att_create_by,
da.create_time as att_create_time,
da.update_by as att_update_by,
da.update_time as att_update_time,
da.is_delete as att_is_delete
from delivery_order do
left join delivery_attachment da
on da.order_id = do.id
and da.is_delete = '0'
</sql>
<sql id="selectDeliveryOrderVo"> <sql id="selectDeliveryOrderVo">
select id, xm_ms, xm_no, wl_no, wl_ms, real_qty, dw, sap_no, gys_mc, remark, origin_name, origin_lng, origin_lat, dest_name, dest_lng, dest_lat, delivery_date, vehicle_plate, shipper_name, shipper_phone, receiver_name, receiver_phone, receiver_org_name, delivery_ton, create_by, create_time, update_by, update_time, is_delete from delivery_order select id, xm_ms, xm_no, wl_no, wl_ms, real_qty, dw, sap_no, gys_mc, remark, origin_name, origin_lng, origin_lat, dest_name, dest_lng, dest_lat, delivery_date, vehicle_plate, shipper_name, shipper_phone, receiver_name, receiver_phone, receiver_org_name, delivery_ton, create_by, create_time, update_by, update_time, is_delete from delivery_order
</sql> </sql>
<select id="selectDeliveryOrderList" parameterType="DeliveryOrder" resultMap="DeliveryOrderResult"> <select id="selectDeliveryOrderList"
<include refid="selectDeliveryOrderVo"/> parameterType="com.delivery.project.document.domain.DeliveryOrder"
<where> resultMap="DeliveryOrderWithAttachResult">
<if test="xmMs != null and xmMs != ''"> and xm_ms = #{xmMs}</if> <include refid="selectDeliveryOrderVoWithAttach"/>
<if test="xmNo != null and xmNo != ''"> and xm_no = #{xmNo}</if> <where>
<if test="wlNo != null and wlNo != ''"> and wl_no = #{wlNo}</if> <if test="xmMs != null and xmMs != ''"> and do.xm_ms = #{xmMs}</if>
<if test="wlMs != null and wlMs != ''"> and wl_ms = #{wlMs}</if> <if test="xmNo != null and xmNo != ''"> and do.xm_no = #{xmNo}</if>
<if test="realQty != null "> and real_qty = #{realQty}</if> <if test="wlNo != null and wlNo != ''"> and do.wl_no = #{wlNo}</if>
<if test="dw != null and dw != ''"> and dw = #{dw}</if> <if test="wlMs != null and wlMs != ''"> and do.wl_ms = #{wlMs}</if>
<if test="sapNo != null and sapNo != ''"> and sap_no = #{sapNo}</if> <if test="realQty != null "> and do.real_qty = #{realQty}</if>
<if test="gysMc != null and gysMc != ''"> and gys_mc = #{gysMc}</if> <if test="dw != null and dw != ''"> and do.dw = #{dw}</if>
<if test="originName != null and originName != ''"> and origin_name like concat('%', #{originName}, '%')</if> <if test="sapNo != null and sapNo != ''"> and do.sap_no = #{sapNo}</if>
<if test="originLng != null "> and origin_lng = #{originLng}</if> <if test="gysMc != null and gysMc != ''"> and do.gys_mc = #{gysMc}</if>
<if test="originLat != null "> and origin_lat = #{originLat}</if> <if test="originName != null and originName != ''"> and do.origin_name like concat('%', #{originName}, '%')</if>
<if test="destName != null and destName != ''"> and dest_name like concat('%', #{destName}, '%')</if> <if test="originLng != null "> and do.origin_lng = #{originLng}</if>
<if test="destLng != null "> and dest_lng = #{destLng}</if> <if test="originLat != null "> and do.origin_lat = #{originLat}</if>
<if test="destLat != null "> and dest_lat = #{destLat}</if> <if test="destName != null and destName != ''"> and do.dest_name like concat('%', #{destName}, '%')</if>
<if test="deliveryDate != null "> and delivery_date = #{deliveryDate}</if> <if test="destLng != null "> and do.dest_lng = #{destLng}</if>
<if test="vehiclePlate != null and vehiclePlate != ''"> and vehicle_plate = #{vehiclePlate}</if> <if test="destLat != null "> and do.dest_lat = #{destLat}</if>
<if test="shipperName != null and shipperName != ''"> and shipper_name like concat('%', #{shipperName}, '%')</if> <if test="deliveryDate != null "> and do.delivery_date = #{deliveryDate}</if>
<if test="shipperPhone != null and shipperPhone != ''"> and shipper_phone = #{shipperPhone}</if> <if test="vehiclePlate != null and vehiclePlate != ''"> and do.vehicle_plate = #{vehiclePlate}</if>
<if test="receiverName != null and receiverName != ''"> and receiver_name like concat('%', #{receiverName}, '%')</if> <if test="shipperName != null and shipperName != ''"> and do.shipper_name like concat('%', #{shipperName}, '%')</if>
<if test="receiverPhone != null and receiverPhone != ''"> and receiver_phone = #{receiverPhone}</if> <if test="shipperPhone != null and shipperPhone != ''"> and do.shipper_phone = #{shipperPhone}</if>
<if test="receiverOrgName != null and receiverOrgName != ''"> and receiver_org_name like concat('%', #{receiverOrgName}, '%')</if> <if test="receiverName != null and receiverName != ''"> and do.receiver_name like concat('%', #{receiverName}, '%')</if>
<if test="deliveryTon != null "> and delivery_ton = #{deliveryTon}</if> <if test="receiverPhone != null and receiverPhone != ''"> and do.receiver_phone = #{receiverPhone}</if>
<if test="isDelete != null and isDelete != ''"> and is_delete = #{isDelete}</if> <if test="receiverOrgName != null and receiverOrgName != ''"> and do.receiver_org_name like concat('%', #{receiverOrgName}, '%')</if>
<if test="deliveryTon != null "> and do.delivery_ton = #{deliveryTon}</if>
<if test="isDelete != null and isDelete != ''"> and do.is_delete = #{isDelete}</if>
</where> </where>
</select> </select>
<select id="selectDeliveryOrderById" parameterType="Long" resultMap="DeliveryOrderResult"> <select id="selectDeliveryOrderById"
<include refid="selectDeliveryOrderVo"/> parameterType="long"
where id = #{id} resultMap="DeliveryOrderWithAttachResult">
<include refid="selectDeliveryOrderVoWithAttach"/>
where do.id = #{id}
limit 1
</select> </select>
<insert id="insertDeliveryOrder" parameterType="DeliveryOrder" useGeneratedKeys="true" keyProperty="id"> <insert id="insertDeliveryOrder" parameterType="DeliveryOrder" useGeneratedKeys="true" keyProperty="id">