Merge branch 'refs/heads/dev' into dev_820

This commit is contained in:
王奎兴
2026-08-18 16:36:28 +08:00
73 changed files with 1628 additions and 349 deletions
@@ -147,6 +147,14 @@ public class BusinessDocumentOrderApplicationService {
{
return businessDocumentOrderDomainService.audit(businessAuditDTO);
}
/**
* 业务单反审核
*/
public Boolean cancelAudit(BusinessAuditDTO businessAuditDTO)
{
return businessDocumentOrderDomainService.cancelAudit(businessAuditDTO);
}
public boolean execute(List<Long> ids) {
return businessDocumentOrderDomainService.execute(ids);
}
@@ -49,6 +49,11 @@ public interface IBusinessDocumentOrderService extends IService<BusinessDocument
public Boolean audit( List<Long> ids, Long reviewStatus, String SalesManager,String reviewOpinion);
/**
* 业务单反审核(将已审核订单恢复到待审核)
*/
public Boolean cancelAudit(List<Long> ids);
String selectByMakerCodeNc(String salesmanCodeNc);
String selectByMakerCodeNc1(String salesmanCodeNc);
@@ -37,4 +37,10 @@ public interface BusinessDocumentOrderMapper extends BaseMapper<BusinessDocument
TmsTransportNote getTmsTransportNote(@Param("tmsOrderId") String tmsOrderId);
String getPushVerifyByOrgId(@Param("organizationId") Long organizationId);
/**
* 统计指定日期前缀(yyMMdd)下已生成的新格式业务单号数量
* 仅统计新格式(长度<=12),过滤历史16位旧数据,保证序号每日从001正确累计
*/
int countByGoodsNumberPrefix(@Param("prefix") String prefix);
}
@@ -5,6 +5,7 @@ import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mhd.common.core.domain.po.UserPo;
import com.mhd.common.core.utils.OrderSequence;
@@ -19,6 +20,7 @@ import com.mhd.oms.domain.businessDocumentOrder.repository.facade.IBusinessDocum
import com.mhd.oms.domain.businessDocumentOrder.repository.mapper.BusinessDocumentOrderMapper;
import com.mhd.oms.domain.businessDocumentOrder.repository.po.BusinessDocumentOrderPO;
import com.mhd.oms.domain.businessDocumentOrder.repository.todo.BusinessDocumentOrderDO;
import com.mhd.oms.domain.businessDocumentOrder.service.GoodsNumberGenerator;
import com.mhd.oms.domain.executeOrder.entity.ExecuteOrder;
import com.mhd.oms.domain.executeOrder.repository.mapper.ExecuteOrderMapper;
import com.mhd.oms.domain.executeOrder.repository.todo.ExecuteOrderDO;
@@ -88,6 +90,9 @@ public class BusinessDocumentOrderImpl extends ServiceImpl<BusinessDocumentOrder
@Autowired
private WlhyServiceFeign wlhyServiceFeign;
@Autowired
private GoodsNumberGenerator goodsNumberGenerator;
/**
* 查询【请填写功能名称】列表
@@ -173,28 +178,6 @@ public class BusinessDocumentOrderImpl extends ServiceImpl<BusinessDocumentOrder
}
public static String getOrderCodeByDate(Date date){
return new StringBuffer()
.append(getSecondPartByDate(date))
.append(getThreePart())
.toString();
}
private static String getSecondPartByDate(Date date) {
if (date == null) {
date = new Date();
}
String temp_current_time = DateUtil.format(date, datePattern());
return temp_current_time.substring(2, temp_current_time.length());
}
private static String datePattern() {
return "yyyyMMddHHmmss";
}
private static String getThreePart() {
return RandomUtil.randomNumbers(4);
}
@Override
public Boolean audit(List<Long> ids, Long reviewStatus, String SalesManager,String reviewOpinion ) {
LoginUser loginUser = SecurityUtils.getLoginUser();//获取登录人
@@ -225,7 +208,6 @@ public class BusinessDocumentOrderImpl extends ServiceImpl<BusinessDocumentOrder
BeanUtils.copyProperties(businessDocumentOrder, subOrder);
subOrder.setId(null);
subOrder.setGoodsNumber(OrderSequence.getOrderCode()); // 生成新的子单号
// 设置子单的拼单类型和主单关系
subOrder.setSplicingOrderType(3L); // 合单-子单
@@ -255,31 +237,10 @@ public class BusinessDocumentOrderImpl extends ServiceImpl<BusinessDocumentOrder
subOrder.setRecipientName(user.getUserName());
String orderCode = null;
int maxRetryCount = 100;
int retryCount = 0;
while (retryCount < maxRetryCount) {
orderCode = getOrderCodeByDate(subOrder.getDocumentDate());
retryCount++;
List<BusinessDocumentOrder> checkOrderCodes = businessDocumentOrderMapper.selectList(
new QueryWrapper<BusinessDocumentOrder>().eq("goods_number", orderCode)
);
if (CollectionUtils.isEmpty(checkOrderCodes)) {
break;
}
if (retryCount >= maxRetryCount) {
throw new com.aliyun.oss.ServiceException("生成唯一订单编号失败,已达到最大重试次数:" + maxRetryCount);
}
}
//生成货源单号
//生成子单货源单号(yyMMdd + 当日顺序号)
String orderCode = goodsNumberGenerator.generateUniqueGoodsNumber(subOrder.getDocumentDate());
subOrder.setGoodsNumber(orderCode);
businessDocumentOrderMapper.insert(subOrder);
subOrderCount++; // 增加子单计数
@@ -338,6 +299,88 @@ public class BusinessDocumentOrderImpl extends ServiceImpl<BusinessDocumentOrder
return null;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean cancelAudit(List<Long> ids) { // 取消审核 后端暂时未加鉴权
if (CollectionUtils.isEmpty(ids)) {
throw new ServiceException("未选择反审核的业务单");
}
for (Long id : ids) {
BusinessDocumentOrder order = businessDocumentOrderMapper.selectById(id);
if (ObjectUtil.isNull(order)) {
throw new ServiceException("未查询到业务单信息");
}
// 校验1:子单(splicingOrderType=3)不允许单独反审核,需对主单操作
if (order.getSplicingOrderType() != null && order.getSplicingOrderType() == 3L) {
throw new ServiceException("业务单【" + order.getGoodsNumber() + "】为子单,请对主单进行反审核");
}
// 校验2:仅"审核通过(reviewStatus=1)"可反审核
if (order.getReviewStatus() == null || order.getReviewStatus() != 1L) {
throw new ServiceException("业务单【" + order.getGoodsNumber() + "】非已审核状态,无法反审核");
}
// 校验3:已生成执行单的不允许反审核,需先取消执行
ExecuteOrder existExec = executeOrderMapper.selectOne(new LambdaQueryWrapper<ExecuteOrder>()
.eq(ExecuteOrder::getBusinessDocumentId, id)
.eq(ExecuteOrder::getDelFlag, 1).last("LIMIT 1"));
if (ObjectUtil.isNotNull(existExec)) {
throw new ServiceException("业务单【" + order.getGoodsNumber() + "】已生成执行单,无法进行反审核");
}
boolean isMain = order.getSplicingOrderType() != null && order.getSplicingOrderType() == 4L;
// 拆单主单(isMain):清理审核时生成的子单、子单地址、以及回填到配送明细的 subOrderId
if (isMain) {
List<BusinessDocumentOrder> subOrders = businessDocumentOrderMapper.selectList(
new LambdaQueryWrapper<BusinessDocumentOrder>()
.eq(BusinessDocumentOrder::getMainOrderId, String.valueOf(id))
.eq(BusinessDocumentOrder::getDelFlag, 1));
if (subOrders != null && !subOrders.isEmpty()) {
List<String> subIdStrs = new ArrayList<>();
for (BusinessDocumentOrder sub : subOrders) {
// 校验4:子单若已生成执行单,主单不可反审核
ExecuteOrder subExec = executeOrderMapper.selectOne(new LambdaQueryWrapper<ExecuteOrder>()
.eq(ExecuteOrder::getBusinessDocumentId, sub.getId())
.eq(ExecuteOrder::getDelFlag, 1).last("LIMIT 1"));
if (ObjectUtil.isNotNull(subExec)) {
throw new ServiceException("子单【" + sub.getGoodsNumber() + "】已生成执行单,无法反审核主单");
}
subIdStrs.add(String.valueOf(sub.getId()));
// 逻辑删除子单(与 delete() 方法一致:delFlag 2)
sub.setDelFlag(2);
businessDocumentOrderMapper.updateById(sub);
}
// 逻辑删除子单的地址(审核时按 orderId=子单id 生成)
businessDocumentAddressMultiMapper.update(null,
new LambdaUpdateWrapper<BusinessDocumentAddressMulti>()
.in(BusinessDocumentAddressMulti::getOrderId, subIdStrs)
.set(BusinessDocumentAddressMulti::getDelFlag, 2));
// 清空审核时回填到 business_delivery_details_link.sub_order_id 的子单id
businessDeliveryDetailsLinkMapper.update(null,
new LambdaUpdateWrapper<BusinessDeliveryDetailsLink>()
.eq(BusinessDeliveryDetailsLink::getOrderId, id)
.isNotNull(BusinessDeliveryDetailsLink::getSubOrderId)
.set(BusinessDeliveryDetailsLink::getSubOrderId, null));
}
}
// 状态复位 + 清空审核信息(用 UpdateWrapper.set 确保 null 能写入;updateById 默认不更新 null)
LambdaUpdateWrapper<BusinessDocumentOrder> updateWrapper = new LambdaUpdateWrapper<BusinessDocumentOrder>()
.eq(BusinessDocumentOrder::getId, id)
.set(BusinessDocumentOrder::getReviewStatus, 0L) // 未审核
.set(BusinessDocumentOrder::getOrderStatus, 0L) // 待审核
.set(BusinessDocumentOrder::getExecuteStatus, 0L) // 无状态
.set(BusinessDocumentOrder::getReviewId, null)
.set(BusinessDocumentOrder::getReview, null)
.set(BusinessDocumentOrder::getReviewTime, null)
.set(BusinessDocumentOrder::getReviewOpinion, null)
.set(BusinessDocumentOrder::getSalesManager, null);
if (isMain) {
updateWrapper.set(BusinessDocumentOrder::getDeliveryProgress, null); // 清空主单拆单数
}
businessDocumentOrderMapper.update(null, updateWrapper);
}
return true;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean execute(List<Long> ids) {
@@ -102,6 +102,9 @@ public class BusinessDocumentOrderDomainService {
@Autowired
private UserServiceFeign userServiceFeign;
@Autowired
private GoodsNumberGenerator goodsNumberGenerator;
/**
* 分页查询【请填写功能名称】列表
*/
@@ -109,26 +112,6 @@ public class BusinessDocumentOrderDomainService {
return businessDocumentOrderService.queryList(businessDocumentOrderDO);
}
public static String getOrderCodeByDate(Date date){
return new StringBuffer()
.append(getSecondPartByDate(date))
.append(getThreePart())
.toString();
}
private static String getSecondPartByDate(Date date) {
if (date == null) {
date = new Date();
}
String temp_current_time = DateUtil.format(date, datePattern());
return temp_current_time.substring(2, temp_current_time.length());
}
private static String datePattern() {
return "yyyyMMddHHmmss";
}
private static String getThreePart() {
return RandomUtil.randomNumbers(4);
}
/**
* 新增【请填写功能名称】
*/
@@ -302,30 +285,8 @@ public class BusinessDocumentOrderDomainService {
}
}
//生成货源单号
String orderCode = null;
int maxRetryCount = 100;
int retryCount = 0;
while (retryCount < maxRetryCount) {
orderCode = getOrderCodeByDate(tmsOrder.getDocumentDate());
retryCount++;
List<BusinessDocumentOrder> checkOrderCodes = businessDocumentOrderMapper.selectList(
new QueryWrapper<BusinessDocumentOrder>().eq("goods_number", orderCode)
);
if (CollectionUtils.isEmpty(checkOrderCodes)) {
break;
}
if (retryCount >= maxRetryCount) {
throw new ServiceException("生成唯一订单编号失败,已达到最大重试次数:" + maxRetryCount);
}
}
//生成货源单号
//生成货源单号(yyMMdd + 当日顺序号,每日重置)
String orderCode = goodsNumberGenerator.generateUniqueGoodsNumber(tmsOrder.getDocumentDate());
tmsOrder.setGoodsNumber(orderCode);
ReservationStockOutOrder reservationStockOutOrder = reservationStockOutOrderMapper.selectOne(new LambdaQueryWrapper<ReservationStockOutOrder>()
@@ -513,6 +474,8 @@ public class BusinessDocumentOrderDomainService {
tmsOrderAccount.setOrderId(String.valueOf(tmsOrder.getId()));
tmsOrderAccount.setCreateBy(String.valueOf(u.getUserid()));
tmsOrderAccount.setCreateTime(new Date());
// 复制新增时科目行会带原订单的id,避免报错
tmsOrderAccount.setId(null);
// 设置组织信息
if (businessDocumentOrderDO.getOrganizationId() != null) {
tmsOrderAccount.setOrganizationId(businessDocumentOrderDO.getOrganizationId());
@@ -774,28 +737,8 @@ public class BusinessDocumentOrderDomainService {
// boolean istrue=businessDocumentOrderService.update(businessDocumentOrderDO);
String orderCode = null;
int maxRetryCount = 100;
int retryCount = 0;
while (retryCount < maxRetryCount) {
orderCode = getOrderCodeByDate(tmsOrder.getDocumentDate());
retryCount++;
List<BusinessDocumentOrder> checkOrderCodes = businessDocumentOrderMapper.selectList(
new QueryWrapper<BusinessDocumentOrder>().eq("goods_number", orderCode)
);
if (CollectionUtils.isEmpty(checkOrderCodes)) {
break;
}
if (retryCount >= maxRetryCount) {
throw new ServiceException("生成唯一订单编号失败,已达到最大重试次数:" + maxRetryCount);
}
}
//生成货源单号
//生成货源单号(yyMMdd + 当日顺序号,每日重置) 原更新操作会将 电脑更细为当日的日期。默认和之前的逻辑一样保持不变
String orderCode = goodsNumberGenerator.generateUniqueGoodsNumber(tmsOrder.getDocumentDate());
tmsOrder.setGoodsNumber(orderCode);
ReservationStockOutOrder reservationStockOutOrder = reservationStockOutOrderMapper.selectOne(new LambdaQueryWrapper<ReservationStockOutOrder>()
@@ -989,6 +932,15 @@ public class BusinessDocumentOrderDomainService {
return businessDocumentOrderService.audit(businessAuditDTO.getIds(),businessAuditDTO.getReviewStatus(),businessAuditDTO.getSalesManager(),businessAuditDTO.getReviewOpinion());
}
/**
* 业务单反审核
*/
public Boolean cancelAudit(BusinessAuditDTO businessAuditDTO)
{
return businessDocumentOrderService.cancelAudit(businessAuditDTO.getIds());
}
public Boolean execute(List<Long> ids) {
return businessDocumentOrderService.execute(ids);
}
@@ -0,0 +1,53 @@
package com.mhd.oms.domain.businessDocumentOrder.service;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.mhd.common.core.exception.ServiceException;
import com.mhd.oms.domain.businessDocumentOrder.entity.BusinessDocumentOrder;
import com.mhd.oms.domain.businessDocumentOrder.repository.mapper.BusinessDocumentOrderMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.Date;
import java.util.List;
/**
* 业务单据订单(goodsNumber)单号生成器
* 规则: yyMMdd(取自单据日期) + 当日顺序号(每日重置, 3位起步, 超999自动扩位)
* 示例: 260804001、260804999、2608041000
*/
@Component
public class GoodsNumberGenerator {
private static final int MAX_RETRY = 100;
@Autowired
private BusinessDocumentOrderMapper businessDocumentOrderMapper;
/**
* 生成唯一业务单号
* @param documentDate 单据日期(为空取当前日期)
*/
public String generateUniqueGoodsNumber(Date documentDate) {
Date date = (documentDate == null) ? new Date() : documentDate;
String datePart = DateUtil.format(date, "yyMMdd");
// count 只查一次作为序号基线
long seq = (long) businessDocumentOrderMapper.countByGoodsNumberPrefix(datePart) + 1;
int retryCount = 0;
while (retryCount < MAX_RETRY) {
String orderCode = datePart + String.format("%03d", seq);
retryCount++;
List<BusinessDocumentOrder> exist = businessDocumentOrderMapper.selectList(
new QueryWrapper<BusinessDocumentOrder>().eq("goods_number", orderCode)
);
if (CollectionUtils.isEmpty(exist)) {
return orderCode;
}
seq++; // ← 关键:查重失败,序号往后递增找空位,而不是重新count
}
throw new ServiceException("生成唯一业务单号失败,已达到最大重试次数:" + MAX_RETRY);
}
}
@@ -163,6 +163,13 @@ public class ReservationStockInOrderPO extends BaseVOEntity {
@Excel(name = "入库数量")
private BigDecimal inQuantity;
/**
* 申报数量:入库单明细中申报数量(DECLARED_QUANTITY)汇总
*/
@ApiModelProperty("申报数量")
@Excel(name = "申报数量")
private BigDecimal declaredQuantity;
/**
* 计划入库数量:取入库单明细中的入库数量汇总
*/
@@ -553,6 +553,31 @@ public class ReservationStockInOrderDomainService {
*/
@Transactional(rollbackFor = Exception.class)
public Boolean issueOrder(ReservationStockInOrderDO stockInOrderDO) {
// ===== 新增校验 =====
List<Long> inOrderIds = stockInOrderDO.getInOrderIds();
if (inOrderIds == null || inOrderIds.isEmpty()) {
throw new ServiceException("下发失败:未选择单据");
}
List<ReservationStockInOrder> checkOrders = stockInOrderService.list(
new QueryWrapper<ReservationStockInOrder>().lambda()
.in(ReservationStockInOrder::getInOrderId, inOrderIds));
for (ReservationStockInOrder order : checkOrders) {
// 校验1:必须已审核(issueStatus==2 表示审核成功)
if (order.getIssueStatus() == null || order.getIssueStatus() != 2) {
throw new ServiceException("下发失败:单据[" + order.getInOrderNumber() + "]未审核,无法下发");
}
// 校验2:防止重复下发(issueTime 有值表示已下发过)
if (order.getIssueTime() != null) {
throw new ServiceException("下发失败:单据[" + order.getInOrderNumber() + "]已下发,不可重复下发");
}
// 校验3WMS 必填字段(warehouseId 不能为空)
if (order.getWarehouseId() == null) {
throw new ServiceException("下发失败:单据[" + order.getInOrderNumber() + "]缺少入库仓库,无法下发");
}
}
// ===== 校验结束 =====
LoginUser loginUser = SecurityUtils.getLoginUser();
// 获取用户姓名(优先从UserPo获取,如果为空则使用realname,最后使用username作为兜底)
@@ -570,26 +595,34 @@ public class ReservationStockInOrderDomainService {
.in(ReservationStockInOrder::getInOrderId, stockInOrderDO.getInOrderIds()));
//todo下发推送到wms
// List<Long> inOrderIds = stockInOrderDO.getInOrderIds();
// for (Long inOrderId : inOrderIds) {
// ReservationStockInOrder byId = stockInOrderService.getById(inOrderId);
// StockInOrder stockInOrder = new StockInOrder();
// BeanUtils.copyProperties(byId, stockInOrder,"inOrderId");
// stockInOrder.setOmsStockInOrderId(String.valueOf(byId.getInOrderId()));
// wmsServiceFeign.omsInOrderAdd(stockInOrder);
// String inOrderNumber = stockInOrder.getInOrderNumber();
// List<ReservationInMaterialDetail> materialDetailList = materialDetailService.list(new QueryWrapper<ReservationInMaterialDetail>().lambda().eq(ReservationInMaterialDetail::getInOrderNumber, inOrderNumber));
// List<InMaterialDetail> inMaterialDetailList = new ArrayList<>();
// for (ReservationInMaterialDetail reservationInMaterialDetail : materialDetailList) {
// InMaterialDetail inMaterialDetail = new InMaterialDetail();
// BeanUtils.copyProperties(reservationInMaterialDetail, inMaterialDetail,"materialDetailId");
// inMaterialDetail.setOmsInMaterialDetail(String.valueOf(reservationInMaterialDetail.getMaterialDetailId()));
// inMaterialDetailList.add(inMaterialDetail);
// }
// wmsServiceFeign.omsInOrderAddDetail(inMaterialDetailList);
// }
for (Long inOrderId : inOrderIds) {
ReservationStockInOrder byId = stockInOrderService.getById(inOrderId);
StockInOrder stockInOrder = new StockInOrder();
BeanUtils.copyProperties(byId, stockInOrder, "inOrderId");
stockInOrder.setOmsStockInOrderId(String.valueOf(byId.getInOrderId()));
try {
wmsServiceFeign.omsInOrderAdd(stockInOrder); // 推主单
String inOrderNumber = stockInOrder.getInOrderNumber();
List<ReservationInMaterialDetail> materialDetailList = materialDetailService.list(
new QueryWrapper<ReservationInMaterialDetail>().lambda()
.eq(ReservationInMaterialDetail::getInOrderNumber, inOrderNumber));
List<InMaterialDetail> inMaterialDetailList = new ArrayList<>();
for (ReservationInMaterialDetail reservationInMaterialDetail : materialDetailList) {
InMaterialDetail inMaterialDetail = new InMaterialDetail();
BeanUtils.copyProperties(reservationInMaterialDetail, inMaterialDetail, "materialDetailId");
inMaterialDetail.setOmsInMaterialDetail(String.valueOf(reservationInMaterialDetail.getMaterialDetailId()));
inMaterialDetailList.add(inMaterialDetail);
}
wmsServiceFeign.omsInOrderAddDetail(inMaterialDetailList); // 推明细(会触发WMS自动审核)
log.info("下发到WMS成功,单据号:{}", byId.getInOrderNumber());
} catch (Exception e) {
log.error("下发到WMS失败,单据号:{}", byId.getInOrderNumber(), e);
throw new ServiceException("下发到WMS失败:" + e.getMessage());
}
}
//todo下发推送到入库业务单
List<Long> inOrderIds = stockInOrderDO.getInOrderIds();
for (Long inOrderId : inOrderIds) {
// Long inOrderId = stockInOrderDO.getInOrderId();
ReservationStockInOrder byId = stockInOrderService.getById(inOrderId);
@@ -22,7 +22,7 @@ import java.util.Map;
/**
* 三方服务 Feign
*/
@FeignClient(contextId = "thirdPartyServiceFeign",url = "http://10.102.192.31:8012", value = ServiceNameConstants.THIRDPARTY_CENTER, configuration = FeignAutoConfiguration.class)
@FeignClient(contextId = "thirdPartyServiceFeign",url = "http://10.102.192.30:8012", value = ServiceNameConstants.THIRDPARTY_CENTER, configuration = FeignAutoConfiguration.class)
public interface ThirdPartyServiceFeign {
//查询当前组织所有三方接口信息
@@ -108,6 +108,16 @@ public class BusinessDocumentOrderApi extends BaseController{
return AjaxResult.success(businessDocumentOrderApplicationService.audit(businessAuditDTO));
}
/**
* 业务单反审核(将已审核订单恢复到待审核,便于修改)
*/
@ApiOperation("业务单反审核")
@PostMapping(value = "/cancelAudit")
public AjaxResult cancelAudit(@RequestBody BusinessAuditDTO businessAuditDTO)
{
return toAjax(businessDocumentOrderApplicationService.cancelAudit(businessAuditDTO));
}
/**
* 批量执行【业务单】
*/