添加功能下载验收单
This commit is contained in:
@@ -284,6 +284,15 @@ export function downloadDeliveryPackage(id) {
|
||||
});
|
||||
}
|
||||
|
||||
// 下载验收单(生成HTML格式的牛只发车验收单)
|
||||
export function downloadAcceptanceForm(id) {
|
||||
return request({
|
||||
url: `/delivery/downloadAcceptanceForm?id=${id}`,
|
||||
method: 'GET',
|
||||
responseType: 'blob', // 用于下载HTML文件
|
||||
});
|
||||
}
|
||||
|
||||
// 获取运送清单详情(用于编辑)
|
||||
export function getDeliveryDetail(id) {
|
||||
return request({
|
||||
|
||||
@@ -52,15 +52,17 @@ axios.interceptors.response.use(
|
||||
// 单独判断导出问题-start
|
||||
if(toString.call(responseData) === '[object Blob]' && (responseData.type !== 'application/json')) {
|
||||
download(response);
|
||||
return ElMessage({
|
||||
type: 'success',
|
||||
message: '导出成功!',
|
||||
});
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: '导出成功!',
|
||||
});
|
||||
return Promise.resolve(response.data); // 返回 Promise,避免返回对象
|
||||
}else if(toString.call(responseData) === '[object Blob]' && (responseData.type == 'application/json')){
|
||||
setTimeout(() => {
|
||||
window.location.href = '/login';
|
||||
}, 1000);
|
||||
return ElMessage.error(`登录过期,请重新登录!`);
|
||||
ElMessage.error(`登录过期,请重新登录!`);
|
||||
return Promise.reject('登录过期,请重新登录!'); // 返回 Promise.reject,避免返回对象
|
||||
}
|
||||
// 单独判断导出问题-end
|
||||
|
||||
@@ -71,7 +73,8 @@ axios.interceptors.response.use(
|
||||
setTimeout(() => {
|
||||
window.location.href = '/login';
|
||||
}, 1000);
|
||||
return ElMessage.error(`登录过期,请重新登录!`);
|
||||
ElMessage.error(`登录过期,请重新登录!`);
|
||||
return Promise.reject('登录过期,请重新登录!'); // 返回 Promise.reject,避免返回对象
|
||||
}
|
||||
// console.log(responseData);
|
||||
// if (responseData && (responseData.code === 650)) {
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
<el-button type="warning" link @click="editDelivery(scope.row)" v-hasPermi="['entry:edit']">编辑</el-button>
|
||||
<el-button type="success" link @click="updateStatus(scope.row)" v-hasPermi="['entry:status']">修改状态</el-button>
|
||||
<el-button type="info" link @click="downloadPackage(scope.row)" :loading="downLoading[scope.row.id]" v-hasPermi="['entry:download']">打包文件</el-button>
|
||||
<el-button type="primary" link @click="downloadAcceptanceFormHandler(scope.row)" :loading="downLoading[scope.row.id]" v-hasPermi="['entry:export']">下载验收单</el-button>
|
||||
<el-button type="danger" link @click="deleteDelivery(scope.row)" v-hasPermi="['entry:delete']">删除</el-button>
|
||||
</div>
|
||||
</template>
|
||||
@@ -125,7 +126,7 @@ import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import baseSearch from '@/components/common/searchCustom/index.vue';
|
||||
import createDeliveryDialog from '@/views/shipping/createDeliveryDialog.vue';
|
||||
import { inspectionList, downloadZip, pageDeviceList } from '@/api/abroad.js';
|
||||
import { updateDeliveryStatus, deleteDeliveryLogic, downloadDeliveryPackage, getDeliveryDetail } from '@/api/shipping.js';
|
||||
import { updateDeliveryStatus, deleteDeliveryLogic, downloadDeliveryPackage, getDeliveryDetail, downloadAcceptanceForm } from '@/api/shipping.js';
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
@@ -679,10 +680,52 @@ const downloadPackage = async (row) => {
|
||||
ElMessage.info('正在打包文件,请稍候...');
|
||||
|
||||
// 调用后端API打包文件(包含图片、视频和信息)
|
||||
// 注意:由于响应拦截器设置了 responseType: 'blob',返回的是 Blob 对象
|
||||
const res = await downloadDeliveryPackage(row.id);
|
||||
|
||||
// 创建下载链接
|
||||
const blob = new Blob([res], { type: 'application/zip' });
|
||||
// 确保 res 是 Blob 对象,如果不是则转换为 Blob
|
||||
let blob;
|
||||
if (res instanceof Blob) {
|
||||
blob = res;
|
||||
} else {
|
||||
// 如果不是 Blob,尝试转换为 Blob
|
||||
blob = new Blob([res], { type: 'application/zip' });
|
||||
}
|
||||
|
||||
// 检查响应是否是有效的ZIP文件(通过检查ZIP文件魔数)
|
||||
// ZIP 文件的魔数是:50 4B 03 04 (PK\x03\x04)
|
||||
const arrayBuffer = await blob.arrayBuffer();
|
||||
const uint8Array = new Uint8Array(arrayBuffer);
|
||||
|
||||
// 检查是否是ZIP文件(前4个字节是 50 4B 03 04)
|
||||
const isZipFile = uint8Array.length >= 4 &&
|
||||
uint8Array[0] === 0x50 &&
|
||||
uint8Array[1] === 0x4B &&
|
||||
uint8Array[2] === 0x03 &&
|
||||
uint8Array[3] === 0x04;
|
||||
|
||||
if (!isZipFile) {
|
||||
// 响应是错误信息(JSON或文本),尝试读取并显示错误
|
||||
try {
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
const errorText = decoder.decode(uint8Array);
|
||||
let errorMsg = '打包文件失败';
|
||||
try {
|
||||
// 尝试解析为 JSON
|
||||
const errorObj = JSON.parse(errorText);
|
||||
errorMsg = errorObj.msg || errorObj.message || errorText;
|
||||
} catch (e) {
|
||||
// 不是 JSON,直接使用文本
|
||||
errorMsg = errorText || '打包文件失败,请重试';
|
||||
}
|
||||
ElMessage.error(errorMsg);
|
||||
} catch (e) {
|
||||
ElMessage.error('打包文件失败,请重试');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建下载链接(使用验证后的 Blob)
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
@@ -695,7 +738,69 @@ const downloadPackage = async (row) => {
|
||||
ElMessage.success('文件打包成功');
|
||||
} catch (error) {
|
||||
console.error('打包文件失败:', error);
|
||||
ElMessage.error('打包文件失败,请重试');
|
||||
ElMessage.error(error.message || '打包文件失败,请重试');
|
||||
} finally {
|
||||
downLoading[row.id] = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 下载验收单
|
||||
const downloadAcceptanceFormHandler = async (row) => {
|
||||
try {
|
||||
downLoading[row.id] = true;
|
||||
ElMessage.info('正在生成验收单,请稍候...');
|
||||
|
||||
// 调用后端API生成验收单
|
||||
const res = await downloadAcceptanceForm(row.id);
|
||||
|
||||
// 确保 res 是 Blob 对象
|
||||
let blob;
|
||||
if (res instanceof Blob) {
|
||||
blob = res;
|
||||
} else {
|
||||
blob = new Blob([res], { type: 'text/html;charset=UTF-8' });
|
||||
}
|
||||
|
||||
// 检查是否是HTML文件(通过检查内容)
|
||||
const arrayBuffer = await blob.arrayBuffer();
|
||||
const uint8Array = new Uint8Array(arrayBuffer);
|
||||
|
||||
// 尝试解码为文本,检查是否是HTML
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
const htmlText = decoder.decode(uint8Array);
|
||||
|
||||
// 检查是否是错误响应(JSON格式)
|
||||
if (htmlText.trim().startsWith('{') || htmlText.trim().startsWith('[')) {
|
||||
try {
|
||||
const errorObj = JSON.parse(htmlText);
|
||||
ElMessage.error(errorObj.msg || errorObj.message || '生成验收单失败,请重试');
|
||||
} catch (e) {
|
||||
ElMessage.error('生成验收单失败,请重试');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建下载链接
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = `牛只发车验收单_${row.deliveryNumber || row.id}_${new Date().getTime()}.html`;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(url);
|
||||
|
||||
// 同时在新窗口中打开,方便预览
|
||||
const newWindow = window.open('', '_blank');
|
||||
if (newWindow) {
|
||||
newWindow.document.write(htmlText);
|
||||
newWindow.document.close();
|
||||
}
|
||||
|
||||
ElMessage.success('验收单生成成功');
|
||||
} catch (error) {
|
||||
console.error('生成验收单失败:', error);
|
||||
ElMessage.error(error.message || '生成验收单失败,请重试');
|
||||
} finally {
|
||||
downLoading[row.id] = false;
|
||||
}
|
||||
|
||||
@@ -224,17 +224,6 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="预估重量(kg)" prop="estimatedWeight">
|
||||
<el-input-number
|
||||
v-model="formData.estimatedWeight"
|
||||
:min="0.01"
|
||||
:precision="2"
|
||||
placeholder="请输入预估重量"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
@@ -671,7 +660,6 @@ const formData = reactive({
|
||||
endLat: '',
|
||||
endLon: '',
|
||||
cattleCount: 1,
|
||||
estimatedWeight: null,
|
||||
quarantineCertNo: '',
|
||||
remark: '',
|
||||
// 装车相关字段
|
||||
@@ -746,7 +734,6 @@ const rules = {
|
||||
startLocation: [{ required: true, message: '请输入起点地址', trigger: 'blur' }],
|
||||
endLocation: [{ required: true, message: '请输入目的地地址', trigger: 'blur' }],
|
||||
cattleCount: [{ required: true, message: '请输入牛只数量', trigger: 'blur' }],
|
||||
estimatedWeight: [{ required: true, message: '请输入预估重量', trigger: 'blur' }],
|
||||
};
|
||||
|
||||
// 计算耳标设备列表(包含体重输入)
|
||||
@@ -817,7 +804,6 @@ const buildSubmitData = () => {
|
||||
|
||||
// 其他信息
|
||||
cattleCount: formData.cattleCount,
|
||||
estimatedWeight: formData.estimatedWeight,
|
||||
quarantineCertNo: formData.quarantineCertNo,
|
||||
remark: formData.remark,
|
||||
|
||||
@@ -850,12 +836,24 @@ const buildSubmitData = () => {
|
||||
}
|
||||
|
||||
// 将所有undefined值转换为空字符串,确保字段被提交
|
||||
// 但是对于数值类型的字段(如cattleCount),保留null或原值,不要转换为空字符串
|
||||
Object.keys(data).forEach(key => {
|
||||
if (data[key] === undefined) {
|
||||
data[key] = '';
|
||||
// 数值类型字段保留null,其他字段转换为空字符串
|
||||
if (key === 'cattleCount' ||
|
||||
key === 'orderId' || key === 'shipperId' || key === 'buyerId' ||
|
||||
key === 'driverId' || key === 'serverId') {
|
||||
data[key] = null;
|
||||
} else {
|
||||
data[key] = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 确保cattleCount不为空字符串,如果是空字符串则转换为null
|
||||
if (data.cattleCount === '') {
|
||||
data.cattleCount = null;
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
@@ -955,6 +953,9 @@ const fillFormWithEditData = (editData) => {
|
||||
formData.entruckWeight = delivery.entruckWeight || null;
|
||||
formData.landingEntruckWeight = delivery.landingEntruckWeight || null;
|
||||
|
||||
// 牛只数量(从ratedQuantity字段映射)
|
||||
formData.cattleCount = delivery.ratedQuantity || 1;
|
||||
|
||||
// 照片
|
||||
formData.quarantineTickeyUrl = delivery.quarantineTickeyUrl || '';
|
||||
formData.poundListImg = delivery.poundListImg || '';
|
||||
@@ -1246,7 +1247,8 @@ const handleSubmit = () => {
|
||||
// 判断是编辑还是新增
|
||||
if (formData.editId) {
|
||||
// 编辑模式:调用更新接口
|
||||
|
||||
// 将cattleCount映射为ratedQuantity(后端DeliveryEditDto使用ratedQuantity字段)
|
||||
submitData.ratedQuantity = submitData.cattleCount;
|
||||
submitData.deliveryId = formData.editId; // 添加deliveryId字段(后端需要)
|
||||
res = await shippingApi.updateDeliveryInfo(submitData);
|
||||
} else {
|
||||
|
||||
@@ -76,6 +76,17 @@
|
||||
<el-option label="按肉价结算" :value="3"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="约定价格(元/斤)" prop="firmPrice">
|
||||
<el-input-number
|
||||
v-model="ruleForm.firmPrice"
|
||||
:precision="2"
|
||||
:min="0"
|
||||
:max="9999999.99"
|
||||
placeholder="请输入约定价格"
|
||||
style="width: 100%"
|
||||
></el-input-number>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
@@ -89,6 +100,7 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { orderAddNew, orderUpdate } from '@/api/shipping.js';
|
||||
import { memberListByType } from '@/api/userManage.js';
|
||||
|
||||
@@ -119,12 +131,17 @@ const ruleForm = reactive({
|
||||
buyerId: [], // 买方ID数组
|
||||
sellerId: [], // 卖方ID数组
|
||||
settlementType: 1, // 结算方式:1-上车重量,2-下车重量,3-按肉价结算
|
||||
firmPrice: null, // 约定价格(元/斤)
|
||||
});
|
||||
|
||||
const rules = reactive({
|
||||
buyerId: [{ required: true, message: '请选择买方', trigger: 'change' }],
|
||||
sellerId: [{ required: true, message: '请选择卖方', trigger: 'change' }],
|
||||
settlementType: [{ required: true, message: '请选择结算方式', trigger: 'change' }],
|
||||
firmPrice: [
|
||||
{ required: true, message: '请输入约定价格', trigger: 'blur' },
|
||||
{ type: 'number', min: 0, message: '约定价格不能小于0', trigger: 'blur' }
|
||||
],
|
||||
});
|
||||
|
||||
const handleClose = () => {
|
||||
@@ -136,6 +153,7 @@ const handleClose = () => {
|
||||
ruleForm.buyerId = [];
|
||||
ruleForm.sellerId = [];
|
||||
ruleForm.settlementType = 1;
|
||||
ruleForm.firmPrice = null;
|
||||
data.dialogVisible = false;
|
||||
};
|
||||
|
||||
@@ -217,6 +235,7 @@ const onClickSave = () => {
|
||||
buyerId: ruleForm.buyerId.join(','), // 将数组转为逗号分隔的字符串
|
||||
sellerId: ruleForm.sellerId.join(','), // 将数组转为逗号分隔的字符串
|
||||
settlementType: ruleForm.settlementType,
|
||||
firmPrice: ruleForm.firmPrice, // 约定价格(元/斤)
|
||||
};
|
||||
|
||||
data.saveLoading = true;
|
||||
@@ -282,6 +301,7 @@ const onShowDialog = (orderData) => {
|
||||
}
|
||||
|
||||
ruleForm.settlementType = orderData?.settlementType || 1;
|
||||
ruleForm.firmPrice = orderData?.firmPrice != null ? orderData.firmPrice : null;
|
||||
|
||||
data.dialogVisible = true;
|
||||
getSupplierList();
|
||||
|
||||
@@ -19,9 +19,18 @@ import com.aiotagro.cattletrade.business.service.IXqClientService;
|
||||
import com.aiotagro.common.core.context.SecurityContextHolder;
|
||||
import com.aiotagro.common.core.utils.SecurityUtil;
|
||||
import com.aiotagro.common.core.constant.RoleConstants;
|
||||
import com.aiotagro.common.core.constant.TencentCloudConstants;
|
||||
import com.aiotagro.common.core.web.domain.AjaxResult;
|
||||
import com.qcloud.cos.COSClient;
|
||||
import com.qcloud.cos.model.GetObjectRequest;
|
||||
import com.aiotagro.common.core.web.domain.PageResultResponse;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.aiotagro.cattletrade.business.mapper.MemberMapper;
|
||||
import com.aiotagro.cattletrade.business.mapper.OrderMapper;
|
||||
import com.aiotagro.cattletrade.business.entity.Order;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@@ -31,8 +40,10 @@ import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -51,6 +62,8 @@ import java.util.zip.ZipOutputStream;
|
||||
@RequestMapping("/delivery")
|
||||
public class DeliveryController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DeliveryController.class);
|
||||
|
||||
@Autowired
|
||||
private IDeliveryService deliveryService;
|
||||
|
||||
@@ -66,6 +79,12 @@ public class DeliveryController {
|
||||
@Autowired
|
||||
private com.aiotagro.cattletrade.business.mapper.IotDeviceDataMapper iotDeviceDataMapper;
|
||||
|
||||
@Autowired
|
||||
private MemberMapper memberMapper;
|
||||
|
||||
@Autowired
|
||||
private OrderMapper orderMapper;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@@ -700,10 +719,12 @@ public class DeliveryController {
|
||||
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
ZipOutputStream zipOut = null;
|
||||
COSClient cosClient = null;
|
||||
|
||||
try {
|
||||
if (id == null) {
|
||||
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
|
||||
response.setContentType("text/plain;charset=UTF-8");
|
||||
response.getWriter().write("运单ID不能为空");
|
||||
return;
|
||||
}
|
||||
@@ -712,6 +733,7 @@ public class DeliveryController {
|
||||
Delivery delivery = deliveryService.getById(id);
|
||||
if (delivery == null) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
response.setContentType("text/plain;charset=UTF-8");
|
||||
response.getWriter().write("运单不存在");
|
||||
return;
|
||||
}
|
||||
@@ -723,29 +745,47 @@ public class DeliveryController {
|
||||
// 1. 创建信息文本文件
|
||||
addDeliveryInfoToZip(zipOut, delivery);
|
||||
|
||||
// 创建COS客户端(如果URL是COS URL,将在addFileToZip中创建)
|
||||
// 预先创建COS客户端,避免重复创建
|
||||
if (delivery.getQuarantineTickeyUrl() != null &&
|
||||
delivery.getQuarantineTickeyUrl().contains(TencentCloudConstants.TENCENT_CLOUD_BUCKET + ".cos")) {
|
||||
com.qcloud.cos.auth.BasicCOSCredentials cred = new com.qcloud.cos.auth.BasicCOSCredentials(
|
||||
TencentCloudConstants.TENCENT_CLOUD_SECRETID,
|
||||
TencentCloudConstants.TENCENT_CLOUD_SECRETKEY
|
||||
);
|
||||
com.qcloud.cos.region.Region region = new com.qcloud.cos.region.Region(TencentCloudConstants.TENCENT_CLOUD_REGION);
|
||||
com.qcloud.cos.ClientConfig clientConfig = new com.qcloud.cos.ClientConfig(region);
|
||||
clientConfig.setHttpProtocol(com.qcloud.cos.http.HttpProtocol.https);
|
||||
cosClient = new COSClient(cred, clientConfig);
|
||||
logger.debug("创建COS客户端用于下载文件");
|
||||
}
|
||||
|
||||
// 2. 下载并添加所有照片
|
||||
int photoCount = 0;
|
||||
photoCount += addFileToZip(zipOut, delivery.getQuarantineTickeyUrl(), "照片/检疫票.jpg");
|
||||
photoCount += addFileToZip(zipOut, delivery.getPoundListImg(), "照片/纸质磅单.jpg");
|
||||
photoCount += addFileToZip(zipOut, delivery.getEmptyVehicleFrontPhoto(), "照片/空车过磅车头照片.jpg");
|
||||
photoCount += addFileToZip(zipOut, delivery.getLoadedVehicleFrontPhoto(), "照片/装车过磅车头照片.jpg");
|
||||
photoCount += addFileToZip(zipOut, delivery.getLoadedVehicleWeightPhoto(), "照片/装车过磅磅单.jpg");
|
||||
photoCount += addFileToZip(zipOut, delivery.getCarFrontPhoto(), "照片/车头照片.jpg");
|
||||
photoCount += addFileToZip(zipOut, delivery.getCarBehindPhoto(), "照片/车尾照片.jpg");
|
||||
photoCount += addFileToZip(zipOut, delivery.getDriverIdCardPhoto(), "照片/司机身份证照片.jpg");
|
||||
photoCount += addFileToZip(zipOut, delivery.getDestinationPoundListImg(), "照片/到地磅单.jpg");
|
||||
photoCount += addFileToZip(zipOut, delivery.getDestinationVehicleFrontPhoto(), "照片/到地车辆过重磅车头照片.jpg");
|
||||
|
||||
logger.info("开始添加照片到ZIP,运单ID: {}", id);
|
||||
photoCount += addFileToZip(zipOut, delivery.getQuarantineTickeyUrl(), "照片/检疫票.jpg", "检疫票", cosClient);
|
||||
photoCount += addFileToZip(zipOut, delivery.getPoundListImg(), "照片/纸质磅单.jpg", "纸质磅单", cosClient);
|
||||
photoCount += addFileToZip(zipOut, delivery.getEmptyVehicleFrontPhoto(), "照片/空车过磅车头照片.jpg", "空车过磅车头照片", cosClient);
|
||||
photoCount += addFileToZip(zipOut, delivery.getLoadedVehicleFrontPhoto(), "照片/装车过磅车头照片.jpg", "装车过磅车头照片", cosClient);
|
||||
photoCount += addFileToZip(zipOut, delivery.getLoadedVehicleWeightPhoto(), "照片/装车过磅磅单.jpg", "装车过磅磅单", cosClient);
|
||||
photoCount += addFileToZip(zipOut, delivery.getCarFrontPhoto(), "照片/车头照片.jpg", "车头照片", cosClient);
|
||||
photoCount += addFileToZip(zipOut, delivery.getCarBehindPhoto(), "照片/车尾照片.jpg", "车尾照片", cosClient);
|
||||
photoCount += addFileToZip(zipOut, delivery.getDriverIdCardPhoto(), "照片/司机身份证照片.jpg", "司机身份证照片", cosClient);
|
||||
photoCount += addFileToZip(zipOut, delivery.getDestinationPoundListImg(), "照片/到地磅单.jpg", "到地磅单", cosClient);
|
||||
photoCount += addFileToZip(zipOut, delivery.getDestinationVehicleFrontPhoto(), "照片/到地车辆过重磅车头照片.jpg", "到地车辆过重磅车头照片", cosClient);
|
||||
logger.info("照片添加完成,成功添加 {} 张照片", photoCount);
|
||||
|
||||
// 3. 下载并添加所有视频
|
||||
int videoCount = 0;
|
||||
videoCount += addFileToZip(zipOut, delivery.getEmptyWeightVideo(), "视频/空车过磅视频.mp4");
|
||||
videoCount += addFileToZip(zipOut, delivery.getEntruckWeightVideo(), "视频/装车过磅视频.mp4");
|
||||
videoCount += addFileToZip(zipOut, delivery.getEntruckVideo(), "视频/装车视频.mp4");
|
||||
videoCount += addFileToZip(zipOut, delivery.getControlSlotVideo(), "视频/消毒槽视频.mp4");
|
||||
videoCount += addFileToZip(zipOut, delivery.getCattleLoadingCircleVideo(), "视频/牛只装车环视视频.mp4");
|
||||
videoCount += addFileToZip(zipOut, delivery.getUnloadCattleVideo(), "视频/卸牛视频.mp4");
|
||||
videoCount += addFileToZip(zipOut, delivery.getDestinationWeightVideo(), "视频/到地过磅视频.mp4");
|
||||
logger.info("开始添加视频到ZIP,运单ID: {}", id);
|
||||
videoCount += addFileToZip(zipOut, delivery.getEmptyWeightVideo(), "视频/空车过磅视频.mp4", "空车过磅视频", cosClient);
|
||||
videoCount += addFileToZip(zipOut, delivery.getEntruckWeightVideo(), "视频/装车过磅视频.mp4", "装车过磅视频", cosClient);
|
||||
videoCount += addFileToZip(zipOut, delivery.getEntruckVideo(), "视频/装车视频.mp4", "装车视频", cosClient);
|
||||
videoCount += addFileToZip(zipOut, delivery.getControlSlotVideo(), "视频/消毒槽视频.mp4", "消毒槽视频", cosClient);
|
||||
videoCount += addFileToZip(zipOut, delivery.getCattleLoadingCircleVideo(), "视频/牛只装车环视视频.mp4", "牛只装车环视视频", cosClient);
|
||||
videoCount += addFileToZip(zipOut, delivery.getUnloadCattleVideo(), "视频/卸牛视频.mp4", "卸牛视频", cosClient);
|
||||
videoCount += addFileToZip(zipOut, delivery.getDestinationWeightVideo(), "视频/到地过磅视频.mp4", "到地过磅视频", cosClient);
|
||||
logger.info("视频添加完成,成功添加 {} 个视频", videoCount);
|
||||
|
||||
|
||||
zipOut.finish();
|
||||
@@ -767,12 +807,16 @@ public class DeliveryController {
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
logger.error("打包文件失败,运单ID: {}", id, e);
|
||||
try {
|
||||
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
response.getWriter().write("打包文件失败:" + e.getMessage());
|
||||
// 确保响应头已设置,避免被错误处理为登录过期
|
||||
if (!response.isCommitted()) {
|
||||
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
response.setContentType("text/plain;charset=UTF-8");
|
||||
response.getWriter().write("打包文件失败:" + e.getMessage());
|
||||
}
|
||||
} catch (IOException ioException) {
|
||||
ioException.printStackTrace();
|
||||
logger.error("写入错误响应失败", ioException);
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
@@ -780,8 +824,12 @@ public class DeliveryController {
|
||||
zipOut.close();
|
||||
}
|
||||
byteArrayOutputStream.close();
|
||||
// 关闭COS客户端
|
||||
if (cosClient != null) {
|
||||
cosClient.shutdown();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
logger.warn("关闭资源失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -822,45 +870,663 @@ public class DeliveryController {
|
||||
|
||||
/**
|
||||
* 从URL下载文件并添加到ZIP
|
||||
* 优先使用腾讯云COS SDK下载,如果URL不是COS URL则使用HttpURLConnection
|
||||
* @param zipOut ZIP输出流
|
||||
* @param fileUrl 文件URL
|
||||
* @param fileName ZIP中的文件名
|
||||
* @param fileDescription 文件描述(用于日志)
|
||||
* @param cosClient COS客户端(如果已创建,可复用;如果为null,会在需要时创建)
|
||||
* @return 成功添加返回1,失败或URL为空返回0
|
||||
*/
|
||||
private int addFileToZip(ZipOutputStream zipOut, String fileUrl, String fileName) {
|
||||
private int addFileToZip(ZipOutputStream zipOut, String fileUrl, String fileName, String fileDescription, COSClient cosClient) {
|
||||
if (fileUrl == null || fileUrl.trim().isEmpty()) {
|
||||
logger.debug("跳过添加文件: {} (URL为空)", fileDescription);
|
||||
return 0;
|
||||
}
|
||||
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
logger.debug("开始下载文件: {},URL: {}", fileDescription, fileUrl);
|
||||
|
||||
URL url = new URL(fileUrl);
|
||||
inputStream = url.openStream();
|
||||
// 检查是否是腾讯云COS的URL
|
||||
if (fileUrl.contains(TencentCloudConstants.TENCENT_CLOUD_BUCKET + ".cos")) {
|
||||
// 使用COS SDK下载文件
|
||||
try {
|
||||
// 从URL中提取COS对象键(key)
|
||||
// URL格式: https://smart-1251449951.cos.ap-guangzhou.myqcloud.com/iotPlateform/2025/11/04/文件名.jpg
|
||||
String cosKey = extractCosKeyFromUrl(fileUrl);
|
||||
if (cosKey == null) {
|
||||
logger.warn("无法从URL中提取COS键: {},URL: {}", fileDescription, fileUrl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 使用COS客户端下载文件
|
||||
// 如果传入的cosClient为null,则创建新的客户端
|
||||
COSClient clientToUse = cosClient;
|
||||
if (clientToUse == null) {
|
||||
com.qcloud.cos.auth.BasicCOSCredentials cred = new com.qcloud.cos.auth.BasicCOSCredentials(
|
||||
TencentCloudConstants.TENCENT_CLOUD_SECRETID,
|
||||
TencentCloudConstants.TENCENT_CLOUD_SECRETKEY
|
||||
);
|
||||
com.qcloud.cos.region.Region region = new com.qcloud.cos.region.Region(TencentCloudConstants.TENCENT_CLOUD_REGION);
|
||||
com.qcloud.cos.ClientConfig clientConfig = new com.qcloud.cos.ClientConfig(region);
|
||||
clientConfig.setHttpProtocol(com.qcloud.cos.http.HttpProtocol.https);
|
||||
clientToUse = new COSClient(cred, clientConfig);
|
||||
}
|
||||
|
||||
GetObjectRequest getObjectRequest = new GetObjectRequest(
|
||||
TencentCloudConstants.TENCENT_CLOUD_BUCKET,
|
||||
cosKey
|
||||
);
|
||||
com.qcloud.cos.model.COSObject cosObject = clientToUse.getObject(getObjectRequest);
|
||||
inputStream = cosObject.getObjectContent();
|
||||
|
||||
logger.debug("使用COS SDK下载文件: {},COS键: {}", fileDescription, cosKey);
|
||||
} catch (Exception e) {
|
||||
logger.warn("使用COS SDK下载文件失败: {},URL: {},错误: {},尝试使用HttpURLConnection",
|
||||
fileDescription, fileUrl, e.getMessage());
|
||||
// 如果COS SDK失败,回退到HttpURLConnection
|
||||
inputStream = downloadViaHttp(fileUrl);
|
||||
}
|
||||
} else {
|
||||
// 非COS URL,使用HttpURLConnection
|
||||
inputStream = downloadViaHttp(fileUrl);
|
||||
}
|
||||
|
||||
if (inputStream == null) {
|
||||
logger.warn("无法获取文件输入流: {},URL: {}", fileDescription, fileUrl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ZipEntry entry = new ZipEntry(fileName);
|
||||
zipOut.putNextEntry(entry);
|
||||
|
||||
byte[] buffer = new byte[8192];
|
||||
int bytesRead;
|
||||
int totalBytes = 0;
|
||||
long totalBytes = 0;
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
zipOut.write(buffer, 0, bytesRead);
|
||||
totalBytes += bytesRead;
|
||||
}
|
||||
|
||||
zipOut.closeEntry();
|
||||
logger.debug("成功添加文件: {},大小: {} 字节", fileDescription, totalBytes);
|
||||
return 1;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("添加文件到ZIP时发生错误: {},URL: {}", fileDescription, fileUrl, e);
|
||||
return 0;
|
||||
} finally {
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
logger.warn("关闭输入流失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从COS URL中提取对象键(key)
|
||||
* @param fileUrl COS文件URL
|
||||
* @return COS对象键,如果无法提取则返回null
|
||||
*/
|
||||
private String extractCosKeyFromUrl(String fileUrl) {
|
||||
try {
|
||||
// URL格式: https://smart-1251449951.cos.ap-guangzhou.myqcloud.com/iotPlateform/2025/11/04/文件名.jpg
|
||||
URL url = new URL(fileUrl);
|
||||
String path = url.getPath();
|
||||
if (path != null && path.startsWith("/")) {
|
||||
return path.substring(1); // 移除开头的 "/"
|
||||
}
|
||||
return path;
|
||||
} catch (Exception e) {
|
||||
logger.warn("提取COS键失败,URL: {},错误: {}", fileUrl, e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用HttpURLConnection下载文件
|
||||
* @param fileUrl 文件URL
|
||||
* @return 输入流,如果失败返回null
|
||||
*/
|
||||
private InputStream downloadViaHttp(String fileUrl) {
|
||||
try {
|
||||
// 对URL进行编码处理(特别是中文字符)
|
||||
URL url = new URL(fileUrl);
|
||||
java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();
|
||||
connection.setConnectTimeout(10000); // 10秒连接超时
|
||||
connection.setReadTimeout(30000); // 30秒读取超时
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setRequestProperty("User-Agent", "Mozilla/5.0");
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
if (responseCode != java.net.HttpURLConnection.HTTP_OK) {
|
||||
logger.warn("HTTP下载失败,响应码: {},URL: {}", responseCode, fileUrl);
|
||||
return null;
|
||||
}
|
||||
|
||||
return connection.getInputStream();
|
||||
} catch (Exception e) {
|
||||
logger.warn("HttpURLConnection下载失败,URL: {},错误: {}", fileUrl, e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载验收单(生成HTML格式的牛只发车验收单)
|
||||
*
|
||||
* @param id 运单ID
|
||||
* @param response HTTP响应
|
||||
*/
|
||||
@SaCheckPermission("delivery:export")
|
||||
@GetMapping("/downloadAcceptanceForm")
|
||||
public void downloadAcceptanceForm(@RequestParam Integer id, HttpServletResponse response) {
|
||||
logger.info("开始生成验收单,运单ID: {}", id);
|
||||
|
||||
try {
|
||||
if (id == null) {
|
||||
logger.warn("运单ID不能为空");
|
||||
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
|
||||
response.setContentType("text/plain;charset=UTF-8");
|
||||
response.getWriter().write("运单ID不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
// 查询运单详情
|
||||
Delivery delivery = deliveryService.getById(id);
|
||||
if (delivery == null) {
|
||||
logger.warn("运单不存在,ID: {}", id);
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
response.setContentType("text/plain;charset=UTF-8");
|
||||
response.getWriter().write("运单不存在");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("查询到运单信息,运单号: {}, 车牌号: {}", delivery.getDeliveryNumber(), delivery.getLicensePlate());
|
||||
|
||||
// 查询供应商名称(如果supplierName为空)
|
||||
if (delivery.getSupplierName() == null || delivery.getSupplierName().isEmpty()) {
|
||||
if (delivery.getSupplierId() != null && !delivery.getSupplierId().isEmpty()) {
|
||||
try {
|
||||
String[] supplierIds = delivery.getSupplierId().split(",");
|
||||
List<String> supplierNames = new ArrayList<>();
|
||||
for (String supplierId : supplierIds) {
|
||||
if (StringUtils.isNotEmpty(supplierId.trim())) {
|
||||
try {
|
||||
Integer sid = Integer.parseInt(supplierId.trim());
|
||||
Map<String, Object> supplierInfo = memberMapper.selectMemberUserById(sid);
|
||||
if (supplierInfo != null) {
|
||||
String username = (String) supplierInfo.get("username");
|
||||
if (StringUtils.isNotEmpty(username)) {
|
||||
supplierNames.add(username);
|
||||
}
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
logger.warn("供应商ID格式错误: {}", supplierId);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!supplierNames.isEmpty()) {
|
||||
delivery.setSupplierName(String.join(",", supplierNames));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("查询供应商信息失败,运单ID: {}", id, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 查询采购商名称(如果buyerName为空)
|
||||
if (delivery.getBuyerName() == null || delivery.getBuyerName().isEmpty()) {
|
||||
if (delivery.getBuyerId() != null) {
|
||||
try {
|
||||
Map<String, Object> buyerInfo = memberMapper.selectMemberUserById(delivery.getBuyerId());
|
||||
if (buyerInfo != null) {
|
||||
String username = (String) buyerInfo.get("username");
|
||||
String mobile = (String) buyerInfo.get("mobile");
|
||||
if (StringUtils.isNotEmpty(username) && StringUtils.isNotEmpty(mobile)) {
|
||||
delivery.setBuyerName(username + "/" + mobile);
|
||||
} else if (StringUtils.isNotEmpty(username)) {
|
||||
delivery.setBuyerName(username);
|
||||
} else if (StringUtils.isNotEmpty(mobile)) {
|
||||
delivery.setBuyerName(mobile);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("查询采购商信息失败,运单ID: {}", id, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 准备数据 - 供货单位(卖方字段,只取姓名,不要手机号)
|
||||
String supplierName = delivery.getSupplierName();
|
||||
if (supplierName != null && !supplierName.isEmpty()) {
|
||||
// 如果包含"/",取"/"前面的部分(姓名部分)
|
||||
if (supplierName.contains("/")) {
|
||||
supplierName = supplierName.split("/")[0].trim();
|
||||
}
|
||||
// 如果包含逗号分隔(多个供应商),取第一个并处理"/"
|
||||
if (supplierName.contains(",")) {
|
||||
String[] parts = supplierName.split(",");
|
||||
supplierName = parts[0].trim();
|
||||
if (supplierName.contains("/")) {
|
||||
supplierName = supplierName.split("/")[0].trim();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 如果supplierName为空,使用supplierMobile作为备选(但通常不应该这样做,因为这是姓名字段)
|
||||
supplierName = "";
|
||||
}
|
||||
|
||||
// 准备数据 - 收货单位(买方字段,只取姓名,不要手机号)
|
||||
String buyerName = delivery.getBuyerName();
|
||||
if (buyerName != null && !buyerName.isEmpty()) {
|
||||
// 如果包含"/",取"/"前面的部分(姓名部分)
|
||||
if (buyerName.contains("/")) {
|
||||
buyerName = buyerName.split("/")[0].trim();
|
||||
}
|
||||
// 如果包含逗号分隔(多个采购商),取第一个并处理"/"
|
||||
if (buyerName.contains(",")) {
|
||||
String[] parts = buyerName.split(",");
|
||||
buyerName = parts[0].trim();
|
||||
if (buyerName.contains("/")) {
|
||||
buyerName = buyerName.split("/")[0].trim();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 如果buyerName为空,使用buyerMobile作为备选(但通常不应该这样做,因为这是姓名字段)
|
||||
buyerName = "";
|
||||
}
|
||||
|
||||
logger.info("提取的供货单位: {}, 收货单位: {}", supplierName, buyerName);
|
||||
|
||||
String startLocation = delivery.getStartLocation() != null ? delivery.getStartLocation() : "";
|
||||
String endLocation = delivery.getEndLocation() != null ? delivery.getEndLocation() : "";
|
||||
String licensePlate = delivery.getLicensePlate() != null ? delivery.getLicensePlate() : "";
|
||||
|
||||
// 格式化发车时间
|
||||
String departureTime = "";
|
||||
if (delivery.getEstimatedDepartureTime() != null) {
|
||||
try {
|
||||
// Date 转 LocalDateTime
|
||||
java.time.LocalDateTime localDateTime = delivery.getEstimatedDepartureTime().toInstant()
|
||||
.atZone(java.time.ZoneId.systemDefault())
|
||||
.toLocalDateTime();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd");
|
||||
departureTime = localDateTime.format(formatter);
|
||||
} catch (Exception e) {
|
||||
logger.warn("格式化发车时间失败,使用默认格式,运单ID: {}", id, e);
|
||||
// 如果格式化失败,使用默认格式
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd");
|
||||
departureTime = sdf.format(delivery.getEstimatedDepartureTime());
|
||||
}
|
||||
}
|
||||
|
||||
// 处理司机姓名和联系方式
|
||||
String driverName = delivery.getDriverName() != null ? delivery.getDriverName() : "";
|
||||
String driverMobile = delivery.getDriverMobile() != null ? delivery.getDriverMobile() : "";
|
||||
String driverInfo = "";
|
||||
if (!driverName.isEmpty() && driverName.contains("/")) {
|
||||
// 如果司机姓名包含"/",取前面部分作为姓名
|
||||
String[] parts = driverName.split("/");
|
||||
driverInfo = parts[0];
|
||||
if (parts.length > 1) {
|
||||
driverInfo += " " + parts[1];
|
||||
} else if (!driverMobile.isEmpty()) {
|
||||
driverInfo += " " + driverMobile;
|
||||
}
|
||||
} else {
|
||||
driverInfo = driverName;
|
||||
if (!driverMobile.isEmpty()) {
|
||||
driverInfo += " " + driverMobile;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取检疫证号(从检疫票URL中提取,如果有的话)
|
||||
String quarantineCertNo = "";
|
||||
if (delivery.getQuarantineTickeyUrl() != null && !delivery.getQuarantineTickeyUrl().isEmpty()) {
|
||||
// 可以根据实际情况提取检疫证号,这里暂时留空
|
||||
quarantineCertNo = "";
|
||||
}
|
||||
|
||||
// 获取订单编号(优先使用deliveryNumber,如果没有则使用orderId)
|
||||
String orderNumber = delivery.getDeliveryNumber() != null ? delivery.getDeliveryNumber() : "";
|
||||
if (orderNumber.isEmpty() && delivery.getOrderId() != null) {
|
||||
orderNumber = "ORDER-" + delivery.getOrderId();
|
||||
}
|
||||
|
||||
// 查询订单信息,获取结算方式描述、单价等信息
|
||||
String weightColumnTitle = "下车总重量(斤)"; // 默认值
|
||||
Integer settlementType = null; // 结算方式:1-上车重量,2-下车重量,3-按肉价结算
|
||||
java.math.BigDecimal unitPrice = null; // 单价(元/斤)
|
||||
|
||||
if (delivery.getOrderId() != null) {
|
||||
try {
|
||||
Order order = orderMapper.selectById(delivery.getOrderId());
|
||||
if (order != null) {
|
||||
// 获取结算方式
|
||||
if (order.getSettlementType() != null) {
|
||||
settlementType = order.getSettlementType();
|
||||
if (settlementType == 1) {
|
||||
// 上车重量 -> 上车总重量(斤)
|
||||
weightColumnTitle = "上车总重量(斤)";
|
||||
logger.info("订单结算方式为上车重量,使用重量字段标题: {}", weightColumnTitle);
|
||||
} else if (settlementType == 2) {
|
||||
// 下车重量 -> 下车总重量(斤)
|
||||
weightColumnTitle = "下车总重量(斤)";
|
||||
logger.info("订单结算方式为下车重量,使用重量字段标题: {}", weightColumnTitle);
|
||||
} else if (settlementType == 3) {
|
||||
// 按肉价结算 -> 保持默认"下车总重量(斤)"
|
||||
weightColumnTitle = "下车总重量(斤)";
|
||||
logger.info("订单结算方式为按肉价结算,使用默认重量字段标题: {}", weightColumnTitle);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取单价(元/斤)
|
||||
if (order.getFirmPrice() != null) {
|
||||
unitPrice = order.getFirmPrice();
|
||||
logger.info("获取订单单价: firmPrice={}", unitPrice);
|
||||
} else {
|
||||
logger.warn("订单单价为空,订单ID: {}", delivery.getOrderId());
|
||||
}
|
||||
} else {
|
||||
logger.warn("订单不存在,订单ID: {}, 使用默认值", delivery.getOrderId());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("查询订单信息失败,订单ID: {}", delivery.getOrderId(), e);
|
||||
}
|
||||
} else {
|
||||
logger.warn("运单没有关联订单,使用默认值");
|
||||
}
|
||||
|
||||
// 计算总重量(斤)和总金额(元)
|
||||
Integer ratedQuantity = delivery.getRatedQuantity(); // 下车总数量(头)
|
||||
Double totalWeightJin = null; // 总重量(斤)
|
||||
Double totalAmount = null; // 总金额(元)
|
||||
|
||||
// 解析重量字段(String类型,如"1000.00")
|
||||
Double emptyWeightKg = null;
|
||||
Double entruckWeightKg = null;
|
||||
Double landingEntruckWeightKg = null;
|
||||
|
||||
try {
|
||||
if (delivery.getEmptyWeight() != null && !delivery.getEmptyWeight().trim().isEmpty()) {
|
||||
emptyWeightKg = Double.parseDouble(delivery.getEmptyWeight().trim());
|
||||
}
|
||||
if (delivery.getEntruckWeight() != null && !delivery.getEntruckWeight().trim().isEmpty()) {
|
||||
entruckWeightKg = Double.parseDouble(delivery.getEntruckWeight().trim());
|
||||
}
|
||||
if (delivery.getLandingEntruckWeight() != null && !delivery.getLandingEntruckWeight().trim().isEmpty()) {
|
||||
landingEntruckWeightKg = Double.parseDouble(delivery.getLandingEntruckWeight().trim());
|
||||
}
|
||||
|
||||
// 根据结算方式计算总重量(斤)
|
||||
// 注意:重量字段存储的是公斤(kg),需要转换为斤(斤 = kg * 2)
|
||||
if (settlementType != null && emptyWeightKg != null) {
|
||||
if (settlementType == 1) {
|
||||
// 上车重量:计算 (entruckWeight - emptyWeight) * 2
|
||||
if (entruckWeightKg != null) {
|
||||
totalWeightJin = (entruckWeightKg - emptyWeightKg) * 2;
|
||||
logger.info("计算上车总重量: entruckWeight={}kg, emptyWeight={}kg, 总重量={}斤",
|
||||
entruckWeightKg, emptyWeightKg, totalWeightJin);
|
||||
}
|
||||
} else if (settlementType == 2) {
|
||||
// 下车重量:计算 (landingEntruckWeight - emptyWeight) * 2
|
||||
if (landingEntruckWeightKg != null) {
|
||||
totalWeightJin = (landingEntruckWeightKg - emptyWeightKg) * 2;
|
||||
logger.info("计算下车总重量: landingEntruckWeight={}kg, emptyWeight={}kg, 总重量={}斤",
|
||||
landingEntruckWeightKg, emptyWeightKg, totalWeightJin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 计算总金额(元) = 总重量(斤) * 单价(元/斤)
|
||||
if (totalWeightJin != null && unitPrice != null) {
|
||||
totalAmount = totalWeightJin * unitPrice.doubleValue();
|
||||
// 四舍五入保留2位小数
|
||||
totalAmount = java.math.BigDecimal.valueOf(totalAmount)
|
||||
.setScale(2, java.math.RoundingMode.HALF_UP)
|
||||
.doubleValue();
|
||||
logger.info("计算总金额: 总重量={}斤, 单价={}元/斤, 总金额={}元",
|
||||
totalWeightJin, unitPrice, totalAmount);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
logger.error("解析重量字段失败: emptyWeight={}, entruckWeight={}, landingEntruckWeight={}",
|
||||
delivery.getEmptyWeight(), delivery.getEntruckWeight(),
|
||||
delivery.getLandingEntruckWeight(), e);
|
||||
}
|
||||
|
||||
// 格式化数值为字符串(保留2位小数)
|
||||
String ratedQuantityStr = ratedQuantity != null ? String.valueOf(ratedQuantity) : "";
|
||||
String totalWeightStr = totalWeightJin != null ?
|
||||
String.format("%.2f", totalWeightJin) : "";
|
||||
String unitPriceStr = unitPrice != null ?
|
||||
unitPrice.setScale(2, java.math.RoundingMode.HALF_UP).toString() : "";
|
||||
String totalAmountStr = totalAmount != null ?
|
||||
String.format("%.2f", totalAmount) : "";
|
||||
|
||||
// 生成HTML内容
|
||||
String htmlContent = generateAcceptanceFormHtml(
|
||||
orderNumber, supplierName, buyerName, startLocation,
|
||||
departureTime, endLocation, quarantineCertNo,
|
||||
driverInfo, licensePlate, weightColumnTitle,
|
||||
ratedQuantityStr, totalWeightStr, unitPriceStr, totalAmountStr
|
||||
);
|
||||
|
||||
// 设置响应头
|
||||
response.setContentType("text/html;charset=UTF-8");
|
||||
response.setHeader("Content-Disposition",
|
||||
"inline; filename=\"" + URLEncoder.encode("牛只发车验收单_" + orderNumber + ".html", "UTF-8") + "\"");
|
||||
|
||||
// 写入响应
|
||||
response.getWriter().write(htmlContent);
|
||||
response.getWriter().flush();
|
||||
|
||||
logger.info("验收单生成成功,运单ID: {}, 运单号: {}", id, orderNumber);
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("生成验收单失败,运单ID: {}", id, e);
|
||||
try {
|
||||
if (!response.isCommitted()) {
|
||||
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
response.setContentType("text/plain;charset=UTF-8");
|
||||
response.getWriter().write("生成验收单失败:" + e.getMessage());
|
||||
}
|
||||
} catch (IOException ioException) {
|
||||
logger.error("写入错误响应失败", ioException);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成验收单HTML内容(严格按照图片格式要求)
|
||||
* @param weightColumnTitle 重量字段标题(根据结算方式动态设置:上车总重量(斤) 或 下车总重量(斤))
|
||||
* @param ratedQuantity 下车总数量(头)
|
||||
* @param totalWeight 总重量(斤)
|
||||
* @param unitPrice 单价(元/斤)
|
||||
* @param totalAmount 总金额(元)
|
||||
*/
|
||||
private String generateAcceptanceFormHtml(String orderNumber, String supplierName,
|
||||
String buyerName, String startLocation,
|
||||
String departureTime, String endLocation,
|
||||
String quarantineCertNo, String driverInfo,
|
||||
String licensePlate, String weightColumnTitle,
|
||||
String ratedQuantity, String totalWeight,
|
||||
String unitPrice, String totalAmount) {
|
||||
|
||||
StringBuilder html = new StringBuilder();
|
||||
html.append("<!DOCTYPE html>\n");
|
||||
html.append("<html>\n");
|
||||
html.append("<head>\n");
|
||||
html.append(" <meta charset=\"UTF-8\">\n");
|
||||
html.append(" <title>牛只发车验收单</title>\n");
|
||||
html.append(" <style>\n");
|
||||
html.append(" @media print {\n");
|
||||
html.append(" body { margin: 0; }\n");
|
||||
html.append(" .no-print { display: none; }\n");
|
||||
html.append(" }\n");
|
||||
html.append(" body { \n");
|
||||
html.append(" font-family: \"Microsoft YaHei\", \"SimSun\", Arial, sans-serif; \n");
|
||||
html.append(" margin: 20px; \n");
|
||||
html.append(" line-height: 1.4;\n");
|
||||
html.append(" }\n");
|
||||
html.append(" .header { \n");
|
||||
html.append(" text-align: center; \n");
|
||||
html.append(" font-size: 20px; \n");
|
||||
html.append(" font-weight: bold; \n");
|
||||
html.append(" margin-bottom: 30px;\n");
|
||||
html.append(" border-bottom: 2px solid #000;\n");
|
||||
html.append(" padding-bottom: 10px;\n");
|
||||
html.append(" }\n");
|
||||
html.append(" .order-number { \n");
|
||||
html.append(" text-align: right; \n");
|
||||
html.append(" margin-bottom: 10px; \n");
|
||||
html.append(" font-size: 14px;\n");
|
||||
html.append(" }\n");
|
||||
html.append(" table { \n");
|
||||
html.append(" width: 100%; \n");
|
||||
html.append(" border-collapse: collapse; \n");
|
||||
html.append(" margin-bottom: 20px; \n");
|
||||
html.append(" }\n");
|
||||
html.append(" table td, table th { \n");
|
||||
html.append(" border: 1px solid #000; \n");
|
||||
html.append(" padding: 8px; \n");
|
||||
html.append(" text-align: left; \n");
|
||||
html.append(" }\n");
|
||||
html.append(" table th { \n");
|
||||
html.append(" background-color: #f0f0f0; \n");
|
||||
html.append(" font-weight: bold; \n");
|
||||
html.append(" }\n");
|
||||
html.append(" .print-button { \n");
|
||||
html.append(" padding: 10px 20px; \n");
|
||||
html.append(" background-color: #409EFF; \n");
|
||||
html.append(" color: white; \n");
|
||||
html.append(" border: none; \n");
|
||||
html.append(" border-radius: 4px; \n");
|
||||
html.append(" cursor: pointer; \n");
|
||||
html.append(" font-size: 14px; \n");
|
||||
html.append(" }\n");
|
||||
html.append(" .print-button:hover { \n");
|
||||
html.append(" background-color: #66b1ff; \n");
|
||||
html.append(" }\n");
|
||||
html.append(" </style>\n");
|
||||
html.append("</head>\n");
|
||||
html.append("<body>\n");
|
||||
|
||||
// 附件2(左上角)
|
||||
html.append(" <div style=\"text-align: left; margin-bottom: 10px;\">附件2</div>\n");
|
||||
|
||||
// 标题(居中,带下划线)
|
||||
html.append(" <div class=\"header\">牛只发车验收单</div>\n");
|
||||
|
||||
// 订单编号(右上角)
|
||||
html.append(" <div class=\"order-number\">订单编号: ").append(orderNumber).append("</div>\n");
|
||||
|
||||
// 基本信息表格(4行2列)
|
||||
html.append(" <table>\n");
|
||||
html.append(" <tr>\n");
|
||||
html.append(" <td style=\"width: 15%;\">供货单位</td>\n");
|
||||
html.append(" <td style=\"width: 35%;\">").append(supplierName).append("</td>\n");
|
||||
html.append(" <td style=\"width: 15%;\">收货单位</td>\n");
|
||||
html.append(" <td style=\"width: 35%;\">").append(buyerName).append("</td>\n");
|
||||
html.append(" </tr>\n");
|
||||
html.append(" <tr>\n");
|
||||
html.append(" <td>发车地点</td>\n");
|
||||
html.append(" <td>").append(startLocation).append("</td>\n");
|
||||
html.append(" <td>发车时间</td>\n");
|
||||
html.append(" <td>").append(departureTime).append("</td>\n");
|
||||
html.append(" </tr>\n");
|
||||
html.append(" <tr>\n");
|
||||
html.append(" <td>到达地点</td>\n");
|
||||
html.append(" <td>").append(endLocation).append("</td>\n");
|
||||
html.append(" <td>动物检疫合格证明编号</td>\n");
|
||||
html.append(" <td>").append(quarantineCertNo).append("</td>\n");
|
||||
html.append(" </tr>\n");
|
||||
html.append(" <tr>\n");
|
||||
html.append(" <td>司机姓名及联系方式</td>\n");
|
||||
html.append(" <td>").append(driverInfo).append("</td>\n");
|
||||
html.append(" <td>装车车牌号</td>\n");
|
||||
html.append(" <td>").append(licensePlate).append("</td>\n");
|
||||
html.append(" </tr>\n");
|
||||
html.append(" </table>\n");
|
||||
|
||||
// 牛只明细表格(完整保留,但数据为空)
|
||||
html.append(" <table>\n");
|
||||
html.append(" <tr>\n");
|
||||
html.append(" <th style=\"width: 5%;\">序号</th>\n");
|
||||
html.append(" <th style=\"width: 15%;\">活牛品种</th>\n");
|
||||
html.append(" <th style=\"width: 15%;\">单只体重范围</th>\n");
|
||||
html.append(" <th style=\"width: 12%;\">下车总数量(头)</th>\n");
|
||||
html.append(" <th style=\"width: 12%;\">").append(weightColumnTitle).append("</th>\n");
|
||||
html.append(" <th style=\"width: 10%;\">单价(元/斤)</th>\n");
|
||||
html.append(" <th style=\"width: 15%;\">总金额(元)</th>\n");
|
||||
html.append(" <th style=\"width: 16%;\">备注</th>\n");
|
||||
html.append(" </tr>\n");
|
||||
html.append(" <tr>\n");
|
||||
html.append(" <td></td>\n");
|
||||
html.append(" <td></td>\n");
|
||||
html.append(" <td></td>\n");
|
||||
html.append(" <td>").append(ratedQuantity).append("</td>\n");
|
||||
html.append(" <td>").append(totalWeight).append("</td>\n");
|
||||
html.append(" <td>").append(unitPrice).append("</td>\n");
|
||||
html.append(" <td>").append(totalAmount).append("</td>\n");
|
||||
html.append(" <td></td>\n");
|
||||
html.append(" </tr>\n");
|
||||
html.append(" </table>\n");
|
||||
|
||||
// 支付信息表格(完整保留,但数据为空)
|
||||
html.append(" <table>\n");
|
||||
html.append(" <tr>\n");
|
||||
html.append(" <td style=\"width: 15%;\">已支付货款时间</td>\n");
|
||||
html.append(" <td style=\"width: 35%;\"></td>\n");
|
||||
html.append(" <td style=\"width: 15%;\">已支付货款金额</td>\n");
|
||||
html.append(" <td style=\"width: 35%;\"></td>\n");
|
||||
html.append(" </tr>\n");
|
||||
html.append(" <tr>\n");
|
||||
html.append(" <td>应支付尾款时间</td>\n");
|
||||
html.append(" <td></td>\n");
|
||||
html.append(" <td>应支付尾款金额</td>\n");
|
||||
html.append(" <td></td>\n");
|
||||
html.append(" </tr>\n");
|
||||
html.append(" </table>\n");
|
||||
|
||||
// 验收结论和签字盖章区域(完整保留,但数据为空)
|
||||
html.append(" <table>\n");
|
||||
html.append(" <tr>\n");
|
||||
html.append(" <td style=\"width: 15%;\">验收结论</td>\n");
|
||||
html.append(" <td style=\"width: 35%;\"></td>\n");
|
||||
html.append(" <td style=\"width: 15%;\">验收时间</td>\n");
|
||||
html.append(" <td style=\"width: 35%;\"></td>\n");
|
||||
html.append(" </tr>\n");
|
||||
html.append(" <tr>\n");
|
||||
html.append(" <td>供货单位指定验收人签字及联系方式</td>\n");
|
||||
html.append(" <td></td>\n");
|
||||
html.append(" <td>收货单位指定验收人签字及联系方式</td>\n");
|
||||
html.append(" <td></td>\n");
|
||||
html.append(" </tr>\n");
|
||||
html.append(" <tr>\n");
|
||||
html.append(" <td>供货单位盖章</td>\n");
|
||||
html.append(" <td></td>\n");
|
||||
html.append(" <td>收货单位盖章</td>\n");
|
||||
html.append(" <td></td>\n");
|
||||
html.append(" </tr>\n");
|
||||
html.append(" </table>\n");
|
||||
|
||||
// 打印按钮(不打印时显示)
|
||||
html.append(" <div class=\"no-print\">\n");
|
||||
html.append(" <button class=\"print-button\" onclick=\"window.print()\">打印/保存为PDF</button>\n");
|
||||
html.append(" <p style=\"color: #666; font-size: 12px;\">\n");
|
||||
html.append(" 提示:点击\"打印/保存为PDF\"按钮可以将此文档打印或保存为PDF格式。\n");
|
||||
html.append(" 在打印对话框中,您也可以选择\"另存为PDF\"来保存文档。\n");
|
||||
html.append(" </p>\n");
|
||||
html.append(" </div>\n");
|
||||
html.append("</body>\n");
|
||||
html.append("</html>\n");
|
||||
|
||||
return html.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -107,13 +107,6 @@ public class DeliveryCreateDto {
|
||||
@Min(value = 1, message = "牛只数量至少为1")
|
||||
private Integer cattleCount;
|
||||
|
||||
/**
|
||||
* 预估重量(公斤)
|
||||
*/
|
||||
@NotNull(message = "预估重量不能为空")
|
||||
@DecimalMin(value = "0.01", message = "预估重量必须大于0")
|
||||
private Double estimatedWeight;
|
||||
|
||||
/**
|
||||
* 检疫证号
|
||||
*/
|
||||
|
||||
@@ -47,6 +47,12 @@ public class Order implements Serializable {
|
||||
@TableField("settlement_type")
|
||||
private Integer settlementType;
|
||||
|
||||
/**
|
||||
* 约定价格(元/斤)
|
||||
*/
|
||||
@TableField("firm_price")
|
||||
private java.math.BigDecimal firmPrice;
|
||||
|
||||
/**
|
||||
* 逻辑删除标记(0-正常,1-已删除)
|
||||
*/
|
||||
|
||||
@@ -468,8 +468,8 @@ public class DeliveryServiceImpl extends ServiceImpl<DeliveryMapper, Delivery> i
|
||||
@Override
|
||||
public AjaxResult createDelivery(DeliveryCreateDto dto) {
|
||||
// 入参日志
|
||||
logger.info("创建装车订单: shipperId={}, buyerId={}, plateNumber={}, driverName={}",
|
||||
dto.getShipperId(), dto.getBuyerId(), dto.getPlateNumber(), dto.getDriverName());
|
||||
logger.info("创建装车订单: shipperId={}, buyerId={}, plateNumber={}, driverName={}, cattleCount={}",
|
||||
dto.getShipperId(), dto.getBuyerId(), dto.getPlateNumber(), dto.getDriverName(), dto.getCattleCount());
|
||||
// 校验时间逻辑
|
||||
if (dto.getEstimatedArrivalTime().before(dto.getEstimatedDepartureTime())) {
|
||||
return AjaxResult.error("预计到达时间必须晚于出发时间");
|
||||
@@ -546,6 +546,14 @@ public class DeliveryServiceImpl extends ServiceImpl<DeliveryMapper, Delivery> i
|
||||
delivery.setEstimatedDepartureTime(dto.getEstimatedDepartureTime());
|
||||
// 预计到达时间
|
||||
delivery.setEstimatedDeliveryTime(dto.getEstimatedArrivalTime());
|
||||
// 装车数量(牛只数量)
|
||||
if (dto.getCattleCount() != null && dto.getCattleCount() > 0) {
|
||||
delivery.setRatedQuantity(dto.getCattleCount());
|
||||
logger.info("设置装车数量: ratedQuantity={}", dto.getCattleCount());
|
||||
} else {
|
||||
logger.warn("装车数量为空或无效: cattleCount={}, 跳过设置", dto.getCattleCount());
|
||||
delivery.setRatedQuantity(null);
|
||||
}
|
||||
// 过磅重量:将空字符串转换为null,避免数据库DECIMAL字段类型错误
|
||||
delivery.setEmptyWeight(StringUtils.isNotEmpty(dto.getEmptyWeight()) ? dto.getEmptyWeight() : null);
|
||||
delivery.setEntruckWeight(StringUtils.isNotEmpty(dto.getEntruckWeight()) ? dto.getEntruckWeight() : null);
|
||||
@@ -592,8 +600,9 @@ public class DeliveryServiceImpl extends ServiceImpl<DeliveryMapper, Delivery> i
|
||||
|
||||
// 保存运送清单
|
||||
boolean saved = this.save(delivery);
|
||||
logger.info("保存装车订单: deliveryNumber={}, licensePlate={}, driverName={}, status={}",
|
||||
delivery.getDeliveryNumber(), delivery.getLicensePlate(), delivery.getDriverName(), delivery.getStatus());
|
||||
logger.info("保存装车订单: deliveryNumber={}, licensePlate={}, driverName={}, status={}, ratedQuantity={}",
|
||||
delivery.getDeliveryNumber(), delivery.getLicensePlate(), delivery.getDriverName(),
|
||||
delivery.getStatus(), delivery.getRatedQuantity());
|
||||
if (!saved) {
|
||||
return AjaxResult.error("创建失败");
|
||||
}
|
||||
@@ -679,6 +688,9 @@ public class DeliveryServiceImpl extends ServiceImpl<DeliveryMapper, Delivery> i
|
||||
// 获取当前登录用户
|
||||
Integer userId = SecurityUtil.getCurrentUserId();
|
||||
|
||||
// 入参日志
|
||||
logger.info("更新运送清单: deliveryId={}, ratedQuantity={}", dto.getDeliveryId(), dto.getRatedQuantity());
|
||||
|
||||
// 查询运送清单是否存在
|
||||
Delivery delivery = this.getById(dto.getDeliveryId());
|
||||
if (delivery == null) {
|
||||
@@ -694,8 +706,11 @@ public class DeliveryServiceImpl extends ServiceImpl<DeliveryMapper, Delivery> i
|
||||
if (dto.getDeliveryTitle() != null) {
|
||||
delivery.setDeliveryTitle(dto.getDeliveryTitle());
|
||||
}
|
||||
if (dto.getRatedQuantity() != null) {
|
||||
if (dto.getRatedQuantity() != null && dto.getRatedQuantity() > 0) {
|
||||
delivery.setRatedQuantity(dto.getRatedQuantity());
|
||||
logger.info("更新装车数量: ratedQuantity={}", dto.getRatedQuantity());
|
||||
} else if (dto.getRatedQuantity() != null && dto.getRatedQuantity() <= 0) {
|
||||
logger.warn("装车数量无效: ratedQuantity={}, 跳过更新", dto.getRatedQuantity());
|
||||
}
|
||||
if (dto.getStartLocation() != null) {
|
||||
delivery.setStartLocation(dto.getStartLocation());
|
||||
@@ -775,6 +790,7 @@ public class DeliveryServiceImpl extends ServiceImpl<DeliveryMapper, Delivery> i
|
||||
|
||||
// 保存更新
|
||||
boolean updated = this.updateById(delivery);
|
||||
logger.info("更新运送清单成功: deliveryId={}, ratedQuantity={}", dto.getDeliveryId(), delivery.getRatedQuantity());
|
||||
if (updated) {
|
||||
return AjaxResult.success("编辑成功");
|
||||
} else {
|
||||
|
||||
@@ -119,7 +119,8 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
||||
@Override
|
||||
@Transactional
|
||||
public AjaxResult addOrder(Order order) {
|
||||
logger.info("开始新增订单,买方:{},卖方:{},结算方式:{}", order.getBuyerId(), order.getSellerId(), order.getSettlementType());
|
||||
logger.info("开始新增订单,买方:{},卖方:{},结算方式:{},约定价格:{}",
|
||||
order.getBuyerId(), order.getSellerId(), order.getSettlementType(), order.getFirmPrice());
|
||||
|
||||
try {
|
||||
// 验证结算方式
|
||||
@@ -128,6 +129,17 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
||||
return AjaxResult.error("结算方式无效");
|
||||
}
|
||||
|
||||
// 验证约定价格(firm_price 字段是 NOT NULL)
|
||||
if (order.getFirmPrice() == null) {
|
||||
logger.error("约定价格不能为空");
|
||||
return AjaxResult.error("约定价格不能为空");
|
||||
}
|
||||
// 确保约定价格不为负数
|
||||
if (order.getFirmPrice().compareTo(java.math.BigDecimal.ZERO) < 0) {
|
||||
logger.error("约定价格不能小于0:{}", order.getFirmPrice());
|
||||
return AjaxResult.error("约定价格不能小于0");
|
||||
}
|
||||
|
||||
// 设置创建人和创建时间
|
||||
Integer userId = SecurityUtil.getCurrentUserId();
|
||||
order.setCreatedBy(userId);
|
||||
@@ -160,7 +172,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
||||
return AjaxResult.error("订单ID不能为空");
|
||||
}
|
||||
|
||||
logger.info("开始更新订单,订单ID:{}", order.getId());
|
||||
logger.info("开始更新订单,订单ID:{},约定价格:{}", order.getId(), order.getFirmPrice());
|
||||
|
||||
try {
|
||||
// 验证订单是否存在
|
||||
@@ -176,6 +188,18 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
||||
return AjaxResult.error("结算方式无效");
|
||||
}
|
||||
|
||||
// 验证约定价格(如果提供了)
|
||||
if (order.getFirmPrice() != null) {
|
||||
// 确保约定价格不为负数
|
||||
if (order.getFirmPrice().compareTo(java.math.BigDecimal.ZERO) < 0) {
|
||||
logger.error("约定价格不能小于0:{}", order.getFirmPrice());
|
||||
return AjaxResult.error("约定价格不能小于0");
|
||||
}
|
||||
} else {
|
||||
// 如果更新时没有提供 firm_price,保留原有值(因为字段是 NOT NULL)
|
||||
order.setFirmPrice(existingOrder.getFirmPrice());
|
||||
}
|
||||
|
||||
// 设置更新人和更新时间
|
||||
try {
|
||||
Integer userId = SecurityUtil.getCurrentUserId();
|
||||
|
||||
@@ -81,5 +81,12 @@ public class TencentCloudUtil {
|
||||
return targetUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取COS客户端
|
||||
* @return COS客户端实例
|
||||
*/
|
||||
public static COSClient getCosClient() {
|
||||
return cosClient;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user