Compare commits

...

5 Commits

Author SHA1 Message Date
zhangke 4a1909a745 调整打包收货的逻辑 2026-03-06 08:37:02 +08:00
曹鹏飞 a4f3dff2e1 Merge remote-tracking branch '惠信/dev_zhangke' into develop 2026-03-05 18:55:19 +08:00
曹鹏飞 9788e08fcf feat(warehouse): 添加大箱二维码一键收货功能字段
- 在WarehouseAddQO中新增bigBoxFastReceipt字段用于控制大箱二维码一键收货功能
- 在WarehouseVO中新增bigBoxFastReceipt字段用于返回大箱二维码一键收货状态
- 在WmsWarehouse实体中新增bigBoxFastReceipt字段用于数据库存储该功能配置
- 为新增字段添加NotNull验证注解确保数据完整性
- 为新增字段添加中文文档注释说明功能用途
2026-03-05 15:47:46 +08:00
曹鹏飞 7ff1ab70b7 fix(h5): 解决物料二维码查询和包装码关联查询问题
- 在H5Controller中添加物料不存在的业务异常检查
- 修复WmsShipmentPackagingCodeMapper.xml中的表别名冲突问题
- 将delivery_item表别名从di改为sdi以避免与dictionary_item表别名冲突
- 确保包装码与配送单关联查询的正确性
2026-03-05 15:44:51 +08:00
曹鹏飞 f13b529f72 拆分出测试环境和开发环境 2026-03-05 15:44:37 +08:00
31 changed files with 1355 additions and 69 deletions

View File

@ -1274,7 +1274,8 @@ public class NormalPGIController extends BaseController {
* @param request
* @return
*/
@PostMapping("scanPackage")
@Deprecated
@PostMapping("scanPackage1")
@ApiMark(moduleName = "打包扫码", apiName = "通过打包码获取到打包码的信息")
public ApiResult<PacageScanVO> scanPackage(@Valid @RequestBody PackingPDASearchQO request) {
// 首先判断当前包是否已经收货了
@ -1291,6 +1292,7 @@ public class NormalPGIController extends BaseController {
* @param request
* @return
*/
@Deprecated
@PostMapping("getPackageOrders")
@ApiMark(moduleName = "获取打包码对应的送货单信息", apiName = "获取打包码对应的送货单信息")
public ApiResult<List<PacagePoVO>> getPackageOrders(@Valid @RequestBody FilterIdQO request) {
@ -1302,6 +1304,7 @@ public class NormalPGIController extends BaseController {
* @param request
* @return
*/
@Deprecated
@PostMapping("getPackageOrderItems")
@ApiMark(moduleName = "根据送货单单号和大码的ID获取到具体的物料信息", apiName = "根据送货单单号和大码的ID获取到具体的物料信息")
public ApiResult<List<PacagePoItemVO>> getPackageOrderItems(@Valid @RequestBody PackagePoSearchQO request) {
@ -1313,6 +1316,7 @@ public class NormalPGIController extends BaseController {
* @param request id= 大码的ID
* @return
*/
@Deprecated
@PostMapping("getPendingScanningItems")
@ApiMark(moduleName = "根据送货单单号和大码的ID获取到具体的物料信息", apiName = "根据送货单单号和大码的ID获取到具体的物料信息")
public ApiResult<List<PendingScanningVO>> getPendingScanningItems(@Valid @RequestBody FilterIdQO request) {
@ -1327,63 +1331,92 @@ public class NormalPGIController extends BaseController {
return ApiResult.success(list);
}
/**
* 通过打包码获取到物料信息
* @param request
* @return
*/
@PostMapping("scanPackage")
@ApiMark(moduleName = "打包扫码", apiName = "通过打包码获取到打包码的信息")
public ApiResult<List<PDAOrderItemVO>> getPackageContents(@Valid @RequestBody PackingPDASearchQO request) {
// 首先判断当前包是否已经收货了
PacageScanVO pacageScanVO = wmsPackageService.getPackageInfo(request.getPackageCode());
VUtil.trueThrowBusinessError(ObjectUtil.isNull(pacageScanVO)).throwMessage("打包码不存在");
VUtil.trueThrowBusinessError(pacageScanVO.getPackageStatus() == 0).throwMessage("此打包码尚未打包");
VUtil.trueThrowBusinessError(pacageScanVO.getPackageStatus() == 2).throwMessage("此打包码已完成收货");
List<PDAOrderItemVO> pdaOrderVO = normalPGIControllerService.getPackageContents(request.getPackageCode());
return ApiResult.success(pdaOrderVO);
}
/**
* 确认收货(无需扫码)
* @param request
* @return
*/
@PostMapping("takeDeliveryConfirmUnScan")
@ApiMark(moduleName = "无需扫码收货", apiName = "无需扫码收货")
public ApiResult<Void> takeDeliveryConfirmUnScan(@Valid @RequestBody PendingUnScanningQO request) {
VUtil.trueThrowBusinessError(request.getBarcodeIds().stream().distinct().count() != request.getBarcodeIds().size()).throwMessage("存在重复的码");
// 重新验证所有码的合法性 1是否是已打包未收货的状态2是否全部是已启用储位管理的状态
List<PacageScanVO> pacageScanVO = wmsPackageService.getPackageInfos(request.getBarcodeIds());
VUtil.trueThrowBusinessError(CollectionUtil.isEmpty(pacageScanVO)).throwMessage("打包码不存在");
long count = pacageScanVO.stream()
.filter(pkg -> Objects.equals(pkg.getPackageStatus(), (short) 0))
.count();
VUtil.trueThrowBusinessError(count >= 0).throwMessage("存在尚未打包的码");
long count1 = pacageScanVO.stream()
.filter(pkg -> Objects.equals(pkg.getPackageStatus(), (short) 2))
.count();
VUtil.trueThrowBusinessError(count1 >= 0).throwMessage("存在已经收货的码");
long count2 = pacageScanVO.stream()
.filter(pkg -> Objects.equals(pkg.getIsCounting(), 1))
.count();
VUtil.trueThrowBusinessError(count2 >= 0).throwMessage("存在必须扫码的包装");
List<PendingScanningItemDTO> items = wmsPackageService.getScanningItems(request.getBarcodeIds());
//对箱码要做出来将箱码换成对应的物料码
List<Long> materialCodes = items.stream().filter(item -> Objects.equals(item.getPackagingType(), 1))
.map(item -> item.getBarcodeId())
.distinct()
.collect(Collectors.toList());
// List<WmsQrCodeMaster> qrCodeMasters = wmsQrCodeMasterService.getByMaterialCodes(materialCodes);
// if(CollectionUtil.isEmpty(materialCodes))
// {
// //计算箱内物料的数量并判断是否是打包状态
// VUtil.trueThrowBusinessError(request.getBarcodeIds().stream().distinct().count() != request.getBarcodeIds().size()).throwMessage("存在重复的码");
// // 重新验证所有码的合法性 1是否是已打包未收货的状态2是否全部是已启用储位管理的状态
// List<PacageScanVO> pacageScanVO = wmsPackageService.getPackageInfos(request.getBarcodeIds());
// VUtil.trueThrowBusinessError(CollectionUtil.isEmpty(pacageScanVO)).throwMessage("打包码不存在");
//
// long count = pacageScanVO.stream()
// .filter(pkg -> Objects.equals(pkg.getPackageStatus(), (short) 0))
// .count();
// VUtil.trueThrowBusinessError(count >= 0).throwMessage("存在尚未打包的码");
// long count1 = pacageScanVO.stream()
// .filter(pkg -> Objects.equals(pkg.getPackageStatus(), (short) 2))
// .count();
// VUtil.trueThrowBusinessError(count1 >= 0).throwMessage("存在已经收货的码");
//
// long count2 = pacageScanVO.stream()
// .filter(pkg -> Objects.equals(pkg.getIsCounting(), 1))
// .count();
// VUtil.trueThrowBusinessError(count2 >= 0).throwMessage("存在必须扫码的包装");
// List<PendingScanningItemDTO> items = wmsPackageService.getScanningItems(request.getBarcodeIds());
// //对箱码要做出来将箱码换成对应的物料码
// List<Long> materialCodes = items.stream().filter(item -> Objects.equals(item.getPackagingType(), 1))
// .map(item -> item.getBarcodeId())
// .distinct()
// .collect(Collectors.toList());
//// List<WmsQrCodeMaster> qrCodeMasters = wmsQrCodeMasterService.getByMaterialCodes(materialCodes);
//// if(CollectionUtil.isEmpty(materialCodes))
//// {
//// //计算箱内物料的数量并判断是否是打包状态
////
////
////
//// }
//
//
// //根据包装信息获取对应的物料收货信息
// List<SrmMaterialReceiptQO> receiptQOS = new ArrayList<>();
// //首先要对上面的信息 按照 srmId进行分组汇总quantity
//
// for (PendingScanningItemDTO item : items) {
// SrmMaterialReceiptQO qo = new SrmMaterialReceiptQO()
// .setNoteNum(item.getDeliveryNo())
// .setLineNumber(item.getDeliveryLineNo())
// .setPoNum(item.getPoNumber())
// .setPoLineNumber(item.getPoLineNumber())
// .setItemCode(item.getMaterialCode())
// .setId(item.getSrmId())
// .setReceiptNum(item.getQuantity());
//
// // .setScanCodes(item.getScanCodes());
// }
//根据包装信息获取对应的物料收货信息
List<SrmMaterialReceiptQO> receiptQOS = new ArrayList<>();
//首先要对上面的信息 按照 srmId进行分组汇总quantity
for (PendingScanningItemDTO item : items) {
SrmMaterialReceiptQO qo = new SrmMaterialReceiptQO()
.setNoteNum(item.getDeliveryNo())
.setLineNumber(item.getDeliveryLineNo())
.setPoNum(item.getPoNumber())
.setPoLineNumber(item.getPoLineNumber())
.setItemCode(item.getMaterialCode())
.setId(item.getSrmId())
.setReceiptNum(item.getQuantity());
// .setScanCodes(item.getScanCodes());
}
return ApiResult.success();
}
/**
* 确认收货(需扫码)
* @param request
* @return
*/
@PostMapping("takeDeliveryConfirmByScan")
@ApiMark(moduleName = "需扫码收货", apiName = "需扫码收货")
public ApiResult<Void> takeDeliveryConfirmByScan(@Valid @RequestBody List<PendingScanningQO> request) {

View File

@ -379,6 +379,7 @@ public class PackingController extends BaseController {
item.setDeliveryNo(qo.getDeliveryNo());
item.setDeliveryLineNo(qo.getDeliveryLineNo());
item.setSrmOrderItemId(qo.getSrmOrderItemId());
item.setPackageStatus((short)0);
item.setCreateUserId(UserUtil.getUserId());
item.setCreateUserName(UserUtil.getUserName());
item.setCreateTime(LocalDateTime.now());

View File

@ -301,7 +301,7 @@ public class QrCodeMasterController extends BaseController {
public ApiResult<QrCodeVO> scan(@Valid @RequestBody QRCodeSearchQO request) {
WmsQrCodeMaster qrCodeMaster = qrCodeMasterService.lambdaQuery()
.eq(WmsQrCodeMaster::getBarcodeCode, request.getPackageCode())
.eq(WmsQrCodeMaster::getProcessStage, request.getProcessStage())
.in(WmsQrCodeMaster::getProcessStage, request.getProcessStage())
.one();
//判断是否为有效码
VUtil.trueThrowBusinessError(ObjectUtil.isNull(qrCodeMaster)).throwMessage("无效码");

View File

@ -20,6 +20,8 @@ import com.nflg.wms.common.util.VUtil;
import com.nflg.wms.repository.entity.*;
import com.nflg.wms.repository.service.*;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.data.redis.core.StringRedisTemplate;
@ -99,6 +101,9 @@ public class NormalPGIControllerService {
@Resource
private IWmsQcReceiveItemService wmsQcReceiveItemService;
@Resource
private IWmsPackageService wmsPackageService;
/**
* 根据订单编号获取订单详情信息包括订单头信息和订单行项目信息
* <p>
@ -111,6 +116,7 @@ public class NormalPGIControllerService {
* @throws Exception 查询或业务处理过程中可能抛出的异常
*/
public PDAOrderVO getOrderItemByOrderNo(@RequestParam String orderNo) {
/// TODO
// 根据订单编号查询订单主表信息
WmsSrmOrder order = wmsSrmOrderService.lambdaQuery().eq(WmsSrmOrder::getNoteNum, orderNo)
.eq(WmsSrmOrder::getIsCompleted, false).one();
@ -537,4 +543,62 @@ public class NormalPGIControllerService {
return srmToken;
}
public List<PDAOrderItemVO> getPackageContents(@NotNull @NotBlank String packageCode) {
// List<PackagePOItemDTO> packagePOItems = wmsPackageService.getPackagePOItems(packageCode);
// VUtil.trueThrowBusinessError(CollectionUtil.isNotEmpty(packagePOItems)).throwMessage("获取打包物料信息异常");
//
//
// // 根据订单编号查询订单主表信息
// WmsSrmOrder order = wmsSrmOrderService.lambdaQuery().eq(WmsSrmOrder::getNoteNum, orderNo)
// .eq(WmsSrmOrder::getIsCompleted, false).one();
// VUtil.trueThrowBusinessError(Objects.isNull(order)).throwMessage("此收货单[" + orderNo + "] 单号无效");
//
// PDAOrderVO pdaOrderVO = new PDAOrderVO();
// // 设置订单头基本信息
// pdaOrderVO.setNoteNum(order.getNoteNum());
// pdaOrderVO.setSupplierNum(order.getSupplierNum());
// pdaOrderVO.setSupplierName(order.getSupplierName());
//
// // 查询订单行项目列表
// List<WmsSrmOrderItem> orderItemList = wmsSrmOrderItemService.lambdaQuery()
// .eq(WmsSrmOrderItem::getOrderId, order.getId())
// .list();
// VUtil.trueThrowBusinessError(CollectionUtil.isEmpty(orderItemList)).throwMessage("此收货单[" + orderNo + "] 已完成收货,无需再次操作");
//
// List<PDAOrderItemVO> orderItemVOList = new ArrayList<>();
// // 遍历订单行项目填充详细信息并从 SAP 获取补充数据
// for (WmsSrmOrderItem item : orderItemList) {
// PDAOrderItemVO itemVO = new PDAOrderItemVO();
// itemVO.setId(item.getId());
// itemVO.setPoLineNumber(item.getPoLineNumber());
// itemVO.setPoNum(item.getPoNum());
// itemVO.setItemCode(item.getItemCode());
// itemVO.setItemName(item.getItemName());
// itemVO.setIsQuality(item.getIsQuality());
// itemVO.setOrderQty(item.getOrderQty());
// itemVO.setUomCode(item.getUomCode());
// itemVO.setLineNumber(item.getLineNumber());
// itemVO.setDeliveryQty(item.getDeliveryQty().subtract(item.getReceiptQty()));
// // SAP 系统获取物料相关信息
// SAPSyncParamsDTO syncParamsDTO = sapCommonService.getMaterialInfoInOrder(item.getPoNum(),
// order.getSupplierNum(), item.getItemCode(), item.getPoLineNumber());
// if (Objects.nonNull(syncParamsDTO)) {
// itemVO.setItemName(syncParamsDTO.getMaktx());
// itemVO.setUomCode(syncParamsDTO.getMeins());
// itemVO.setOrderQty(syncParamsDTO.getMenge());
// itemVO.setReceivedWarehouse(syncParamsDTO.getWarehouseNo());
// itemVO.setBinNos(syncParamsDTO.getBinNos());
// itemVO.setLbprt(syncParamsDTO.getLbprt());
// itemVO.setTransportNum(syncParamsDTO.getTransportNum());
// // 判断是否质检
// boolean isQuality = "X".equals(syncParamsDTO.getKzkri());
// itemVO.setIsQuality(isQuality);
// orderItemVOList.add(itemVO);
// }
// }
// // 设置订单行项目列表到返回对象中
// pdaOrderVO.setOrderItemVOList(orderItemVOList);
// return pdaOrderVO;
return null;
}
}

View File

@ -0,0 +1,30 @@
logging:
loki:
url: http://192.168.163.83:3100/loki/api/v1/push
level:
root: info
com:
nflg: trace
alibaba:
cloud:
nacos: debug
org:
springframework: debug
# sa-token配置
sa-token:
# SSO-相关配置
sso-client:
# SSO-Server 用户中心地址
server-url: http://sa-sso-server.com:9000
# LDAP
#spring:
# ldap:
# urls:
# - ldap://192.168.0.2:389
# base: dc=nflg
# username: commpub@nflg
# password: Nflg2019#
management:
otlp:
tracing:
endpoint: http://192.168.163.83:4318/v1/traces

View File

@ -20,10 +20,10 @@ import java.util.List;
@Slf4j
public class DeployDevTest {
private static final String serviceName = "admin-dev";
private static final String serviceName = "admin";
private static final String localPath = System.getProperty("user.dir") + "//target//";
private static final String remotePath = "/mnt/app/wms/" + serviceName + "/";
private static final String jarName = "nflg-wms-admin-1.0.0-SNAPSHOT.jar";
private static final String remotePath = "/mnt/app/wms-dev/" + serviceName + "/";
private static final String jarName = "nflg-wms-" + serviceName + "-1.0.0-SNAPSHOT.jar";
@Test
public void DeployToSit() throws Exception {
@ -34,7 +34,7 @@ public class DeployDevTest {
//处理字体目录
// handleDir(sshUtil, localPath, remotePath, "fonts");
//处理lib目录
handleDir(sshUtil, localPath, remotePath, "lib");
// handleDir(sshUtil, localPath, remotePath, "lib");
//执行脚本启动服务
sshUtil.exec("cd " + remotePath + " && ./restart.sh");
sshUtil.disconnect();

View File

@ -0,0 +1,10 @@
logging:
loki:
url: http://192.168.163.83:3100/loki/api/v1/push
level:
root: info
com:
nflg: debug
alibaba:
cloud:
nacos: debug

View File

@ -0,0 +1,332 @@
package com.nflg.wms.auth;
import cn.hutool.core.util.StrUtil;
import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.junit.jupiter.api.Test;
import java.io.*;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class DeployDevTest {
private static final String serviceName = "auth";
private static final String localPath = System.getProperty("user.dir") + "\\target\\";
private static final String remotePath = "/mnt/app/wms-dev/" + serviceName + "/";
private static final String jarName = "nflg-wms-" + serviceName + "-1.0.0-SNAPSHOT.jar";
@Test
public void DeployToSit() throws Exception {
SSHUtil sshUtil = new SSHUtil();
sshUtil.connect("192.168.163.84", 22, "root", "CMP2025nf");
//处理主jar包
handleFile(sshUtil, localPath + jarName, remotePath + jarName);
//处理lib目录
// handleDir(sshUtil, localPath, remotePath, "lib");
//执行脚本启动服务
sshUtil.exec("cd " + remotePath + " && ./restart.sh");
sshUtil.disconnect();
}
private void handleDir(SSHUtil sshUtil, String localPath, String remotePath, String dirName) throws Exception {
printInfo("处理目录:" + dirName);
List<Path> files = getFileList(localPath + "lib");
for (Path file : files) {
handleFile(sshUtil, file.toString(), remotePath + "lib/" + file.getFileName().toString());
}
printInfo("处理目录完成");
}
public List<Path> getFileList(String folderPath) throws IOException {
Path path = Paths.get(folderPath);
if (Files.exists(path) && Files.isDirectory(path)) {
List<Path> fileList = new ArrayList<>();
try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
for (Path entry : stream) {
fileList.add(entry);
}
}
return fileList;
}
return null;
}
private void handleFile(SSHUtil sshUtil, String localPath, String remotePath) throws Exception {
BasicFileAttributes localFileAttr = Files.readAttributes(Paths.get(localPath), BasicFileAttributes.class);
printInfo("处理文件:{},大小:{}", localPath, getSize(localFileAttr.size()));
if (sshUtil.fileExists(remotePath)) {
if (!StrUtil.equals(getRemoteFileMD5(sshUtil, remotePath), getLocalFileMD5(localPath), true)) {
printError("文件不一致,开始上传");
sshUtil.uploadFile(localPath, remotePath);
} else {
printInfo("文件一致");
}
} else {
printError("文件不存在,开始上传");
sshUtil.uploadFile(localPath, remotePath);
}
printInfo("处理完成");
}
private String getRemoteFileMD5(SSHUtil sshUtil, String remotePath) throws Exception {
String md5 = StrUtil.subPre(sshUtil.execWithReturn("md5sum " + remotePath),32);
printInfo("远程文件MD5为" + md5);
return md5;
}
private String getLocalFileMD5(String localPath) throws Exception {
String md5 = DigestUtils.md5Hex(new FileInputStream(localPath));
printInfo("本地文件MD5为" + md5);
return md5;
}
private String getSize(long size) {
long s = 1024;
if (size < s) {
return size + "B";
}
s = s * 1024;
if (size < s) {
return String.format("%.2f", size / 1.00 / 1024) + "KB";
}
s = s * 1024;
if (size < s) {
return String.format("%.2f", size / 1.00 / 1024 / 1024) + "MB";
}
s = s * 1024;
return String.format("%.2f", size / 1.00 / 1024 / 1024 / 1024) + "GB";
}
private static class SSHUtil {
private Session session;
private ChannelSftp channelSftp;
/**
* 建立SSH连接
*/
public void connect(String host, int port, String userName, String password) throws JSchException {
printInfo("开始连接服务器,ip:{},port:{},userName:{},password:{}", host, port, userName, password);
JSch jsch = new JSch();
session = jsch.getSession(userName, host, port);
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
printInfo("连接成功");
Channel channel = session.openChannel("sftp");
channel.connect();
channelSftp = (ChannelSftp) channel;
}
/**
* 执行远程命令并返回输出
*/
public void exec(String command) {
printInfo("开始执行命令:{}", command);
if (session == null || !session.isConnected()) {
throw new IllegalStateException("SSH未连接");
}
ChannelExec channel = null;
SshResultCallback callback = new SshResultCallback() {
@Override
public void onOutput(String output) {
printInfo(false, output);
}
@Override
public void onErrorOutput(String errorOutput) {
printError(false, errorOutput);
}
@Override
public void onCompleted(int exitStatus) {
printInfo("输出完毕,退出状态:" + exitStatus);
}
@Override
public void onError(Exception e) {
printError(false, e.getMessage());
}
};
try {
// 创建执行命令的通道
channel = (ChannelExec) session.openChannel("exec");
channel.setCommand(command);
// 获取输入流以读取命令输出
InputStream in = channel.getInputStream();
InputStream err = channel.getExtInputStream();
channel.connect();
// 读取命令输出
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
BufferedReader errReader = new BufferedReader(new InputStreamReader(err));
String line;
while ((line = reader.readLine()) != null) {
callback.onOutput(line);
}
// 读取错误输出
String errLine;
while ((errLine = errReader.readLine()) != null) {
callback.onErrorOutput(errLine);
}
// 获取退出状态
int exitStatus = channel.getExitStatus();
callback.onCompleted(exitStatus);
} catch (JSchException | IOException e) {
callback.onError(e);
} finally {
if (channel != null) {
channel.disconnect();
}
if (session != null) {
session.disconnect();
}
printInfo("执行命令完毕");
}
}
public String execWithReturn(String command) throws Exception {
printInfo("开始执行命令:{}", command);
if (session == null || !session.isConnected()) {
throw new IllegalStateException("SSH未连接");
}
ChannelExec channel = null;
BufferedReader reader = null;
try {
// 创建执行命令的通道
channel = (ChannelExec) session.openChannel("exec");
channel.setCommand(command);
// 获取输入流以读取命令输出
InputStream in = channel.getInputStream();
InputStream err = channel.getExtInputStream();
channel.connect();
reader = new BufferedReader(new InputStreamReader(in));
String data = reader.readLine();
// log.info("执行命令成功,返回数据:" + data);
BufferedReader errReader = new BufferedReader(new InputStreamReader(err));
String errData = errReader.readLine();
// log.info("执行命令失败,返回数据:" + errData);
if (StrUtil.isBlank(errData)) {
return data;
} else {
errReader.close();
throw new Exception("执行命令失败:" + errData);
}
} finally {
if (channel != null) {
channel.disconnect();
}
if (reader != null) {
try {
reader.close();
} catch (Exception ignored) {
}
}
printInfo("执行命令完毕");
}
}
/**
* 检查远程文件是否存在
*/
public boolean fileExists(String remotePath) throws SftpException {
try {
channelSftp.stat(remotePath);
return true;
} catch (SftpException e) {
if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
return false;
}
throw e;
}
}
/**
* 上传文件
*/
public void uploadFile(String localPath, String remotePath) throws SftpException {
printInfo("开始上传本地文件{}到远程{}", localPath, remotePath);
channelSftp.put(localPath, remotePath);
printInfo("上传完成");
}
/**
* 关闭连接
*/
public void disconnect() {
if (channelSftp != null) {
channelSftp.disconnect();
}
if (session != null) {
session.disconnect();
}
}
}
public interface SshResultCallback {
void onOutput(String output);
void onErrorOutput(String errorOutput);
void onCompleted(int exitStatus);
void onError(Exception e);
}
private static void printError(String msg) {
if (StrUtil.isNotBlank(msg)) {
System.out.println(red + msg + reset);
}
}
private static void printError(boolean addTime, String msg) {
if (addTime) {
System.out.println(red + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + " " + msg + reset);
} else {
System.out.println(red + msg + reset);
}
}
private static void printInfo(String msg) {
printInfo(true, msg);
}
private static void printInfo(boolean addTime, String msg) {
if (addTime) {
System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + " " + msg);
} else {
System.out.println(msg);
}
}
private static void printInfo(String format, Object... args) {
printInfo(true, format, args);
}
private static void printInfo(boolean addTime, String format, Object... args) {
if (addTime) {
System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + " " + StrUtil.format(format, args));
} else {
System.out.println(StrUtil.format(format, args));
}
}
public static final String red = "\u001B[31m";
public static final String reset = "\u001B[0m";
}

View File

@ -16,11 +16,9 @@ import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.TimeUnit;
@Slf4j
public class DeployTest {
public class DeploySitTest {
private static final String serviceName = "auth";
private static final String localPath = System.getProperty("user.dir") + "\\target\\";

View File

@ -0,0 +1,49 @@
package com.nflg.wms.common.pojo.dto;
import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;
import lombok.experimental.Accessors;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@Accessors(chain = true)
public class PackagePOItemDTO {
/**
* 物料编号
*/
private String materialCode;
/**
* 物料描述
*/
private String materialDescription;
/**
* 送货单数量
*/
private BigDecimal orderQty;
/**
* 打包数量
*/
private BigDecimal packingQuantity;
/**
* 采购单号
*/
private String poNumber;
/**
* 仓库
*/
private String storageLocation;
/**
* 送货单单号
*/
private String deliveryNo;
}

View File

@ -14,7 +14,17 @@ public class PendingScanningQO {
private Long barcodeId;
/**
* 待扫数量
* 物料二维码的编码
*/
private String barcodeCode;
/**
* 条码类型(物料码箱码) 0 物料码1 箱码
*/
private Short packagingType;
/**
* 扫码数量
*/
private BigDecimal pendingScanQuantity;

View File

@ -9,7 +9,7 @@ import java.util.List;
@Accessors(chain = true)
public class PendingUnScanningQO {
/**
* 包装物ID
* 包装的二维码编号
*/
private List<Long> barcodeIds;
private String packageCode;
}

View File

@ -5,6 +5,8 @@ import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
@Data
@Accessors(chain = true)
public class QRCodeSearchQO {
@ -26,5 +28,5 @@ public class QRCodeSearchQO {
* OutBound((short) 7, "已出库"), //已被使用只有退库的时候才会被重新激活
* Hold((short) 8, "已挂起");// 用于仓库转储出库之后此二维码还需要继续使用用一个特殊的状态标记
*/
private Short processStage;
private List<Short> processStage;
}

View File

@ -61,4 +61,10 @@ public class WarehouseAddQO {
*/
@NotNull
private Boolean isDisableLocation;
/**
* 是否使用大箱二维码一键收货
*/
@NotNull
private Boolean bigBoxFastReceipt;
}

View File

@ -72,4 +72,9 @@ public class WarehouseVO extends WarehouseSimpleVO {
* 是否启储位管理
*/
private Boolean isDisableLocation;
/**
* 是否使用大箱二维码一键收货
*/
private Boolean bigBoxFastReceipt;
}

View File

@ -0,0 +1,10 @@
logging:
loki:
url: http://192.168.163.83:3100/loki/api/v1/push
level:
root: info
com:
nflg: debug
alibaba:
cloud:
nacos: warn

View File

@ -0,0 +1,332 @@
package com.nflg.wms.gateway;
import cn.hutool.core.util.StrUtil;
import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.junit.jupiter.api.Test;
import java.io.*;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class DeployDevTest {
private static final String serviceName = "gateway";
private static final String localPath = System.getProperty("user.dir") + "\\target\\";
private static final String remotePath = "/mnt/app/wms-dev/" + serviceName + "/";
private static final String jarName = "nflg-wms-" + serviceName + "-1.0.0-SNAPSHOT.jar";
@Test
public void DeployToSit() throws Exception {
SSHUtil sshUtil = new SSHUtil();
sshUtil.connect("192.168.163.84", 22, "root", "CMP2025nf");
//处理主jar包
handleFile(sshUtil, localPath + jarName, remotePath + jarName);
//处理lib目录
// handleDir(sshUtil, localPath, remotePath, "lib");
//执行脚本启动服务
sshUtil.exec("cd " + remotePath + " && ./restart.sh");
sshUtil.disconnect();
}
private void handleDir(SSHUtil sshUtil, String localPath, String remotePath, String dirName) throws Exception {
printInfo("处理目录:" + dirName);
List<Path> files = getFileList(localPath + "lib");
for (Path file : files) {
handleFile(sshUtil, file.toString(), remotePath + "lib/" + file.getFileName().toString());
}
printInfo("处理目录完成");
}
public List<Path> getFileList(String folderPath) throws IOException {
Path path = Paths.get(folderPath);
if (Files.exists(path) && Files.isDirectory(path)) {
List<Path> fileList = new ArrayList<>();
try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
for (Path entry : stream) {
fileList.add(entry);
}
}
return fileList;
}
return null;
}
private void handleFile(SSHUtil sshUtil, String localPath, String remotePath) throws Exception {
BasicFileAttributes localFileAttr = Files.readAttributes(Paths.get(localPath), BasicFileAttributes.class);
printInfo("处理文件:{},大小:{}", localPath, getSize(localFileAttr.size()));
if (sshUtil.fileExists(remotePath)) {
if (!StrUtil.equals(getRemoteFileMD5(sshUtil, remotePath), getLocalFileMD5(localPath), true)) {
printError("文件不一致,开始上传");
sshUtil.uploadFile(localPath, remotePath);
} else {
printInfo("文件一致");
}
} else {
printError("文件不存在,开始上传");
sshUtil.uploadFile(localPath, remotePath);
}
printInfo("处理完成");
}
private String getRemoteFileMD5(SSHUtil sshUtil, String remotePath) throws Exception {
String md5 = StrUtil.subPre(sshUtil.execWithReturn("md5sum " + remotePath),32);
printInfo("远程文件MD5为" + md5);
return md5;
}
private String getLocalFileMD5(String localPath) throws Exception {
String md5 = DigestUtils.md5Hex(new FileInputStream(localPath));
printInfo("本地文件MD5为" + md5);
return md5;
}
private String getSize(long size) {
long s = 1024;
if (size < s) {
return size + "B";
}
s = s * 1024;
if (size < s) {
return String.format("%.2f", size / 1.00 / 1024) + "KB";
}
s = s * 1024;
if (size < s) {
return String.format("%.2f", size / 1.00 / 1024 / 1024) + "MB";
}
s = s * 1024;
return String.format("%.2f", size / 1.00 / 1024 / 1024 / 1024) + "GB";
}
private static class SSHUtil {
private Session session;
private ChannelSftp channelSftp;
/**
* 建立SSH连接
*/
public void connect(String host, int port, String userName, String password) throws JSchException {
printInfo("开始连接服务器,ip:{},port:{},userName:{},password:{}", host, port, userName, password);
JSch jsch = new JSch();
session = jsch.getSession(userName, host, port);
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
printInfo("连接成功");
Channel channel = session.openChannel("sftp");
channel.connect();
channelSftp = (ChannelSftp) channel;
}
/**
* 执行远程命令并返回输出
*/
public void exec(String command) {
printInfo("开始执行命令:{}", command);
if (session == null || !session.isConnected()) {
throw new IllegalStateException("SSH未连接");
}
ChannelExec channel = null;
SshResultCallback callback = new SshResultCallback() {
@Override
public void onOutput(String output) {
printInfo(false, output);
}
@Override
public void onErrorOutput(String errorOutput) {
printError(false, errorOutput);
}
@Override
public void onCompleted(int exitStatus) {
printInfo("输出完毕,退出状态:" + exitStatus);
}
@Override
public void onError(Exception e) {
printError(false, e.getMessage());
}
};
try {
// 创建执行命令的通道
channel = (ChannelExec) session.openChannel("exec");
channel.setCommand(command);
// 获取输入流以读取命令输出
InputStream in = channel.getInputStream();
InputStream err = channel.getExtInputStream();
channel.connect();
// 读取命令输出
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
BufferedReader errReader = new BufferedReader(new InputStreamReader(err));
String line;
while ((line = reader.readLine()) != null) {
callback.onOutput(line);
}
// 读取错误输出
String errLine;
while ((errLine = errReader.readLine()) != null) {
callback.onErrorOutput(errLine);
}
// 获取退出状态
int exitStatus = channel.getExitStatus();
callback.onCompleted(exitStatus);
} catch (JSchException | IOException e) {
callback.onError(e);
} finally {
if (channel != null) {
channel.disconnect();
}
if (session != null) {
session.disconnect();
}
printInfo("执行命令完毕");
}
}
public String execWithReturn(String command) throws Exception {
printInfo("开始执行命令:{}", command);
if (session == null || !session.isConnected()) {
throw new IllegalStateException("SSH未连接");
}
ChannelExec channel = null;
BufferedReader reader = null;
try {
// 创建执行命令的通道
channel = (ChannelExec) session.openChannel("exec");
channel.setCommand(command);
// 获取输入流以读取命令输出
InputStream in = channel.getInputStream();
InputStream err = channel.getExtInputStream();
channel.connect();
reader = new BufferedReader(new InputStreamReader(in));
String data = reader.readLine();
// log.info("执行命令成功,返回数据:" + data);
BufferedReader errReader = new BufferedReader(new InputStreamReader(err));
String errData = errReader.readLine();
// log.info("执行命令失败,返回数据:" + errData);
if (StrUtil.isBlank(errData)) {
return data;
} else {
errReader.close();
throw new Exception("执行命令失败:" + errData);
}
} finally {
if (channel != null) {
channel.disconnect();
}
if (reader != null) {
try {
reader.close();
} catch (Exception ignored) {
}
}
printInfo("执行命令完毕");
}
}
/**
* 检查远程文件是否存在
*/
public boolean fileExists(String remotePath) throws SftpException {
try {
channelSftp.stat(remotePath);
return true;
} catch (SftpException e) {
if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
return false;
}
throw e;
}
}
/**
* 上传文件
*/
public void uploadFile(String localPath, String remotePath) throws SftpException {
printInfo("开始上传本地文件{}到远程{}", localPath, remotePath);
channelSftp.put(localPath, remotePath);
printInfo("上传完成");
}
/**
* 关闭连接
*/
public void disconnect() {
if (channelSftp != null) {
channelSftp.disconnect();
}
if (session != null) {
session.disconnect();
}
}
}
public interface SshResultCallback {
void onOutput(String output);
void onErrorOutput(String errorOutput);
void onCompleted(int exitStatus);
void onError(Exception e);
}
private static void printError(String msg) {
if (StrUtil.isNotBlank(msg)) {
System.out.println(red + msg + reset);
}
}
private static void printError(boolean addTime, String msg) {
if (addTime) {
System.out.println(red + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + " " + msg + reset);
} else {
System.out.println(red + msg + reset);
}
}
private static void printInfo(String msg) {
printInfo(true, msg);
}
private static void printInfo(boolean addTime, String msg) {
if (addTime) {
System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + " " + msg);
} else {
System.out.println(msg);
}
}
private static void printInfo(String format, Object... args) {
printInfo(true, format, args);
}
private static void printInfo(boolean addTime, String format, Object... args) {
if (addTime) {
System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + " " + StrUtil.format(format, args));
} else {
System.out.println(StrUtil.format(format, args));
}
}
public static final String red = "\u001B[31m";
public static final String reset = "\u001B[0m";
}

View File

@ -16,11 +16,9 @@ import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.TimeUnit;
@Slf4j
public class DeployTest {
public class DeploySitTest {
private static final String serviceName = "gateway";
private static final String localPath = System.getProperty("user.dir") + "\\target\\";
@ -34,7 +32,7 @@ public class DeployTest {
//处理主jar包
handleFile(sshUtil, localPath + jarName, remotePath + jarName);
//处理lib目录
handleDir(sshUtil, localPath, remotePath, "lib");
// handleDir(sshUtil, localPath, remotePath, "lib");
//执行脚本启动服务
sshUtil.exec("cd " + remotePath + " && ./restart.sh");
sshUtil.disconnect();

View File

@ -42,7 +42,7 @@ public class WmsPackage implements Serializable {
private Short packageType;
/**
* 状态 0 未打包1 已打包2 已收货
* 状态 0 未打包1 已打包2 已收货3 部分收货
*/
private Short packageStatus;

View File

@ -67,7 +67,12 @@ public class WmsPackageItem implements Serializable {
private String deliveryLineNo;
/**
* SRM送货单对应的ID号,对应扫描的时候返回的id
* SRM送货单对应的ID号
*/
private Long srmOrderItemId;
/**
* 状态 0 未收货2 已收货
*/
private Short packageStatus;
}

View File

@ -110,4 +110,9 @@ public class WmsWarehouse implements Serializable {
* 是否启储位管理
*/
private Boolean isDisableLocation;
/**
* 是否使用大箱二维码一键收货
*/
private Boolean bigBoxFastReceipt;
}

View File

@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nflg.wms.common.pojo.PageData;
import com.nflg.wms.common.pojo.dto.PackagePOItemDTO;
import com.nflg.wms.common.pojo.dto.PendingScanningItemDTO;
import com.nflg.wms.common.pojo.qo.PackingQO;
import com.nflg.wms.common.pojo.qo.PackingSearchQO;
@ -39,4 +40,6 @@ public interface WmsPackageMapper extends BaseMapper<WmsPackage> {
List<PacageScanVO> getPackageInfos(List<Long> barcodeIds);
List<PendingScanningItemDTO> getScanningItems(List<Long> barcodeIds);
List<PackagePOItemDTO> getPackagePOItems(String packageCode);
}

View File

@ -2,6 +2,7 @@ package com.nflg.wms.repository.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.nflg.wms.common.pojo.PageData;
import com.nflg.wms.common.pojo.dto.PackagePOItemDTO;
import com.nflg.wms.common.pojo.dto.PendingScanningItemDTO;
import com.nflg.wms.common.pojo.qo.PackingQO;
import com.nflg.wms.common.pojo.qo.PackingSearchQO;
@ -11,6 +12,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
import com.nflg.wms.repository.entity.WmsPackageItem;
import com.nflg.wms.repository.entity.WmsQrCodeMaster;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.util.List;
@ -47,4 +49,6 @@ public interface IWmsPackageService extends IService<WmsPackage> {
List<PacageScanVO> getPackageInfos(List<Long> barcodeIds);
List<PendingScanningItemDTO> getScanningItems(List<Long> barcodeIds);
List<PackagePOItemDTO> getPackagePOItems(@NotNull @NotBlank String packageCode);
}

View File

@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nflg.wms.common.constant.BarCodeProcessStage;
import com.nflg.wms.common.pojo.PageData;
import com.nflg.wms.common.pojo.dto.PackagePOItemDTO;
import com.nflg.wms.common.pojo.dto.PendingScanningItemDTO;
import com.nflg.wms.common.pojo.qo.PackingQO;
import com.nflg.wms.common.pojo.qo.PackingSearchQO;
@ -137,5 +138,10 @@ public class WmsPackageServiceImpl extends ServiceImpl<WmsPackageMapper, WmsPack
return baseMapper.getScanningItems(barcodeIds);
}
@Override
public List<PackagePOItemDTO> getPackagePOItems(String packageCode) {
return baseMapper.getPackagePOItems(packageCode);
}
}

View File

@ -176,4 +176,22 @@
#{id}
</foreach>
</select>
<select id="getPackagePOItems" resultType="com.nflg.wms.common.pojo.dto.PackagePOItemDTO">
select
c.material_code,
c.material_description,
e.delivery_qty-e.receipt_qty as order_qty,
case when c.packaging_type = 0 then c.po_number else d.po_number end as po_number,
case when c.packaging_type = 0 then c.delivery_no else d.delivery_no end as delivery_no,
case when c.packaging_type = 0 then c.quantity else sum(d.quantity) end as packing_quantity
from wms_package a left join wms_package_item b on a.id=b.package_id
left join wms_qr_code_master c on b.barcode_code = c.barcode_code
left join wms_qr_code_master d on c.id = d.parent_barcode_id
left join wms_srm_order_item e on b.srm_order_item_id=e.id
where a.package_code=#{packageCode}
group by c.material_code,
c.material_description,
e.delivery_qty,e.receipt_qty,c.packaging_type,c.po_number,d.po_number,c.delivery_no,d.delivery_no,c.quantity
</select>
</mapper>

View File

@ -65,8 +65,8 @@
SELECT pc.*,di."name" AS "type_name"
FROM wms_shipment_packaging_code pc
LEFT JOIN dictionary_item di ON pc."type"=di."id"
INNER JOIN wms_shipment_delivery_item di ON di.packaging_code_id=pc."id"
INNER JOIN wms_shipment_delivery d ON di.delivery_id=d."id"
INNER JOIN wms_shipment_delivery_item sdi ON sdi.packaging_code_id=pc."id"
INNER JOIN wms_shipment_delivery d ON sdi.delivery_id=d."id"
where pc.status=3 and d.no=#{code}
</select>

View File

@ -33,7 +33,7 @@ public class CodeGeneratorTest {
)
.strategyConfig(builder -> {
builder
.addInclude("wms_warehouse") //只生成指定表
.addInclude("wms_package") //只生成指定表
.entityBuilder().idType(IdType.ASSIGN_ID)
.enableLombok()
.enableChainModel()

View File

@ -161,6 +161,7 @@ public class H5Controller extends BaseController {
return ApiResult.success(packagingCodeService.getForInstall(packagingCode.getId()));
} else {
ShipmentMaterialCodeQRVO vo = materialCodeItemQrService.getInfoByQRCode(code);
VUtil.trueThrowBusinessError(Objects.isNull(vo)).throwMessage("未找到该物料");
VUtil.trueThrowBusinessError(vo.getStatus() == 5).throwMessage("该物料已安装");
return ApiResult.success(List.of(vo));
}

View File

@ -0,0 +1,30 @@
logging:
loki:
url: http://192.168.163.83:3100/loki/api/v1/push
level:
root: info
com:
nflg: trace
alibaba:
cloud:
nacos: debug
org:
springframework: debug
# sa-token配置
sa-token:
# SSO-相关配置
sso-client:
# SSO-Server 用户中心地址
server-url: http://sa-sso-server.com:9000
# LDAP
#spring:
# ldap:
# urls:
# - ldap://192.168.0.2:389
# base: dc=nflg
# username: commpub@nflg
# password: Nflg2019#
management:
otlp:
tracing:
endpoint: http://192.168.163.83:4318/v1/traces

View File

@ -0,0 +1,334 @@
package com.nflg.wms.shipment;
import cn.hutool.core.util.StrUtil;
import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.junit.jupiter.api.Test;
import java.io.*;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class DeployDevTest {
private static final String serviceName = "shipment";
private static final String localPath = System.getProperty("user.dir") + "//target//";
private static final String remotePath = "/mnt/app/wms-dev/" + serviceName + "/";
private static final String jarName = "nflg-wms-" + serviceName + "-1.0.0-SNAPSHOT.jar";
@Test
public void DeployToSit() throws Exception {
SSHUtil sshUtil = new SSHUtil();
sshUtil.connect("192.168.163.84", 22, "root", "CMP2025nf");
//处理主jar包
handleFile(sshUtil, localPath + jarName, remotePath + jarName);
//处理字体目录
// handleDir(sshUtil, localPath, remotePath, "fonts");
//处理lib目录
// handleDir(sshUtil, localPath, remotePath, "lib");
//执行脚本启动服务
sshUtil.exec("cd " + remotePath + " && ./restart.sh");
sshUtil.disconnect();
}
private void handleDir(SSHUtil sshUtil, String localPath, String remotePath, String dirName) throws Exception {
printInfo("处理目录:" + dirName);
List<Path> files = getFileList(localPath + dirName);
for (Path file : files) {
handleFile(sshUtil, file.toString(), remotePath + dirName + "/" + file.getFileName().toString());
}
printInfo("处理目录完成");
}
public List<Path> getFileList(String folderPath) throws IOException {
Path path = Paths.get(folderPath);
if (Files.exists(path) && Files.isDirectory(path)) {
List<Path> fileList = new ArrayList<>();
try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
for (Path entry : stream) {
fileList.add(entry);
}
}
return fileList;
}
return null;
}
private void handleFile(SSHUtil sshUtil, String localPath, String remotePath) throws Exception {
BasicFileAttributes localFileAttr = Files.readAttributes(Paths.get(localPath), BasicFileAttributes.class);
printInfo("处理文件:{},大小:{}", localPath, getSize(localFileAttr.size()));
if (sshUtil.fileExists(remotePath)) {
if (!StrUtil.equals(getRemoteFileMD5(sshUtil, remotePath), getLocalFileMD5(localPath), true)) {
printError("文件不一致,开始上传");
sshUtil.uploadFile(localPath, remotePath);
} else {
printInfo("文件一致");
}
} else {
printError("文件不存在,开始上传");
sshUtil.uploadFile(localPath, remotePath);
}
printInfo("处理完成");
}
private String getRemoteFileMD5(SSHUtil sshUtil, String remotePath) throws Exception {
String md5 = StrUtil.subPre(sshUtil.execWithReturn("md5sum " + remotePath), 32);
printInfo("远程文件MD5为" + md5);
return md5;
}
private String getLocalFileMD5(String localPath) throws Exception {
String md5 = DigestUtils.md5Hex(new FileInputStream(localPath));
printInfo("本地文件MD5为" + md5);
return md5;
}
private String getSize(long size) {
long s = 1024;
if (size < s) {
return size + "B";
}
s = s * 1024;
if (size < s) {
return String.format("%.2f", size / 1.00 / 1024) + "KB";
}
s = s * 1024;
if (size < s) {
return String.format("%.2f", size / 1.00 / 1024 / 1024) + "MB";
}
s = s * 1024;
return String.format("%.2f", size / 1.00 / 1024 / 1024 / 1024) + "GB";
}
private static class SSHUtil {
private Session session;
private ChannelSftp channelSftp;
/**
* 建立SSH连接
*/
public void connect(String host, int port, String userName, String password) throws JSchException {
printInfo("开始连接服务器,ip:{},port:{},userName:{},password:{}", host, port, userName, password);
JSch jsch = new JSch();
session = jsch.getSession(userName, host, port);
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
printInfo("连接成功");
Channel channel = session.openChannel("sftp");
channel.connect();
channelSftp = (ChannelSftp) channel;
}
public String execWithReturn(String command) throws Exception {
printInfo("开始执行命令:{}", command);
if (session == null || !session.isConnected()) {
throw new IllegalStateException("SSH未连接");
}
ChannelExec channel = null;
BufferedReader reader = null;
try {
// 创建执行命令的通道
channel = (ChannelExec) session.openChannel("exec");
channel.setCommand(command);
// 获取输入流以读取命令输出
InputStream in = channel.getInputStream();
InputStream err = channel.getExtInputStream();
channel.connect();
reader = new BufferedReader(new InputStreamReader(in));
String data = reader.readLine();
// log.info("执行命令成功,返回数据:" + data);
BufferedReader errReader = new BufferedReader(new InputStreamReader(err));
String errData = errReader.readLine();
// log.info("执行命令失败,返回数据:" + errData);
if (StrUtil.isBlank(errData)) {
return data;
} else {
errReader.close();
throw new Exception("执行命令失败:" + errData);
}
} finally {
if (channel != null) {
channel.disconnect();
}
if (reader != null) {
try {
reader.close();
} catch (Exception ignored) {
}
}
printInfo("执行命令完毕");
}
}
/**
* 执行远程命令并返回输出
*/
public void exec(String command) {
printInfo("开始执行命令:{}", command);
if (session == null || !session.isConnected()) {
throw new IllegalStateException("SSH未连接");
}
ChannelExec channel = null;
SshResultCallback callback = new SshResultCallback() {
@Override
public void onOutput(String output) {
printInfo(false, output);
}
@Override
public void onErrorOutput(String errorOutput) {
printError(false, errorOutput);
}
@Override
public void onCompleted(int exitStatus) {
printInfo("输出完毕,退出状态:" + exitStatus);
}
@Override
public void onError(Exception e) {
printError(false, e.getMessage());
}
};
try {
// 创建执行命令的通道
channel = (ChannelExec) session.openChannel("exec");
channel.setCommand(command);
// 获取输入流以读取命令输出
InputStream in = channel.getInputStream();
InputStream err = channel.getExtInputStream();
channel.connect();
// 读取命令输出
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
BufferedReader errReader = new BufferedReader(new InputStreamReader(err));
String line;
while ((line = reader.readLine()) != null) {
callback.onOutput(line);
}
// 读取错误输出
String errLine;
while ((errLine = errReader.readLine()) != null) {
callback.onErrorOutput(errLine);
}
// 获取退出状态
int exitStatus = channel.getExitStatus();
callback.onCompleted(exitStatus);
} catch (JSchException | IOException e) {
callback.onError(e);
} finally {
if (channel != null) {
channel.disconnect();
}
if (session != null) {
session.disconnect();
}
printInfo("执行命令完毕");
}
}
/**
* 检查远程文件是否存在
*/
public boolean fileExists(String remotePath) throws SftpException {
try {
channelSftp.stat(remotePath);
return true;
} catch (SftpException e) {
if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
return false;
}
throw e;
}
}
/**
* 上传文件
*/
public void uploadFile(String localPath, String remotePath) throws SftpException {
printInfo("开始上传本地文件{}到远程{}", localPath, remotePath);
channelSftp.put(localPath, remotePath);
printInfo("上传完成");
}
/**
* 关闭连接
*/
public void disconnect() {
if (channelSftp != null) {
channelSftp.disconnect();
}
if (session != null) {
session.disconnect();
}
}
}
public interface SshResultCallback {
void onOutput(String output);
void onErrorOutput(String errorOutput);
void onCompleted(int exitStatus);
void onError(Exception e);
}
private static void printError(String msg) {
if (StrUtil.isNotBlank(msg)) {
System.out.println(red + msg + reset);
}
}
private static void printError(boolean addTime, String msg) {
if (addTime) {
System.out.println(red + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + " " + msg + reset);
} else {
System.out.println(red + msg + reset);
}
}
private static void printInfo(String msg) {
printInfo(true, msg);
}
private static void printInfo(boolean addTime, String msg) {
if (addTime) {
System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + " " + msg);
} else {
System.out.println(msg);
}
}
private static void printInfo(String format, Object... args) {
printInfo(true, format, args);
}
private static void printInfo(boolean addTime, String format, Object... args) {
if (addTime) {
System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + " " + StrUtil.format(format, args));
} else {
System.out.println(StrUtil.format(format, args));
}
}
public static final String red = "\u001B[31m";
public static final String reset = "\u001B[0m";
}

View File

@ -18,7 +18,7 @@ import java.util.ArrayList;
import java.util.List;
@Slf4j
public class DeployTest {
public class DeploySitTest {
private static final String serviceName = "shipment";
private static final String localPath = System.getProperty("user.dir") + "//target//";