复核页面修改调整

This commit is contained in:
秦鸿展
2026-04-07 15:59:40 +08:00
parent d3870556af
commit cf5f955868
5 changed files with 513 additions and 0 deletions
@@ -1,9 +1,21 @@
package com.mhd.wms.application.service.reviewOrder;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mhd.common.core.exception.ServiceException;
import com.mhd.common.core.utils.DateUtils;
import com.mhd.common.core.utils.StringUtils;
import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.common.core.web.domain.AjaxResult;
import com.mhd.system.api.SystemServiceFeign;
import com.mhd.system.api.domain.BatchDetailFeignPO;
import com.mhd.system.api.model.LoginUser;
import com.mhd.wms.domain.materialMoreDetail.repository.po.MaterialMoreDetailPO;
import com.mhd.wms.domain.materialMoreDetail.repository.todo.MaterialMoreDetailDO;
import com.mhd.wms.domain.materialMoreDetail.service.MaterialMoreDetailDomainService;
import com.mhd.wms.domain.reviewOrder.entity.ReviewMaterialDetail;
import com.mhd.wms.domain.reviewOrder.entity.ReviewOrder;
import com.mhd.wms.domain.reviewOrder.repository.mapper.ReviewMaterialDetailMapper;
import com.mhd.wms.domain.reviewOrder.repository.mapper.ReviewOrderMapper;
import com.mhd.wms.domain.reviewOrder.repository.po.ReviewMaterialDetailPO;
import com.mhd.wms.domain.reviewOrder.repository.po.ReviewOrderPO;
@@ -11,8 +23,17 @@ import com.mhd.wms.domain.reviewOrder.repository.todo.ReviewOrderDO;
import com.mhd.wms.interfaces.dto.reviewOrder.ReviewOrderDetailDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 复核单应用服务:列表、详情
@@ -23,6 +44,15 @@ public class ReviewOrderApplicationService {
@Autowired
private ReviewOrderMapper reviewOrderMapper;
@Autowired
private SystemServiceFeign systemServiceFeign;
@Autowired
private MaterialMoreDetailDomainService materialMoreDetailDomainService;
@Autowired
private ReviewMaterialDetailMapper reviewMaterialDetailMapper;
public List<ReviewOrderPO> queryList(ReviewOrderDO query) {
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser != null && loginUser.getUserPo() != null) {
@@ -43,6 +73,7 @@ public class ReviewOrderApplicationService {
throw new ServiceException("复核单不存在或已删除");
}
List<ReviewMaterialDetailPO> details = reviewOrderMapper.queryDetailByReviewOrderId(reviewOrderId);
fillDynamicBatchDisplayFields(details);
ReviewOrderDetailDTO result = new ReviewOrderDetailDTO();
result.setHeader(header);
result.setDetails(details);
@@ -58,9 +89,399 @@ public class ReviewOrderApplicationService {
throw new ServiceException("复核单不存在或已删除");
}
List<ReviewMaterialDetailPO> details = reviewOrderMapper.queryDetailByReviewOrderNumber(reviewOrderNumber);
fillDynamicBatchDisplayFields(details);
ReviewOrderDetailDTO result = new ReviewOrderDetailDTO();
result.setHeader(header);
result.setDetails(details);
return result;
}
@Transactional(rollbackFor = Exception.class)
public Boolean batchCheck(ReviewOrderDO cmd) {
if (cmd == null || cmd.getReviewOrderId() == null) {
throw new ServiceException("复核单ID不能为空");
}
List<ReviewMaterialDetailPO> inputDetails = cmd.getDetails();
if (CollectionUtils.isEmpty(inputDetails)) {
throw new ServiceException("复核明细不能为空");
}
for (ReviewMaterialDetailPO i : inputDetails) {
if (i == null || i.getReviewMaterialDetailId() == null) {
continue;
}
ReviewMaterialDetail db = reviewMaterialDetailMapper.selectById(i.getReviewMaterialDetailId());
if (db == null || db.getDelFlag() == null || db.getDelFlag() != 1) {
continue;
}
if (!cmd.getReviewOrderId().equals(db.getReviewOrderId())) {
continue;
}
BigDecimal pending = nz(db.getPendingReviewQty());
BigDecimal reviewed = nz(db.getReviewedQty());
BigDecimal qty = i.getReviewQtyEa() != null ? i.getReviewQtyEa() :
(i.getReviewedQty() != null ? i.getReviewedQty() : pending);
if (qty.compareTo(BigDecimal.ZERO) <= 0) {
continue;
}
if (qty.compareTo(pending) > 0) {
qty = pending;
}
db.setPendingReviewQty(pending.subtract(qty));
db.setReviewedQty(reviewed.add(qty));
fillOperator(db);
reviewMaterialDetailMapper.updateById(db);
}
refreshReviewOrderStatus(cmd.getReviewOrderId());
return true;
}
@Transactional(rollbackFor = Exception.class)
public Boolean batchCancelCheck(ReviewOrderDO cmd) {
if (cmd == null || cmd.getReviewOrderId() == null) {
throw new ServiceException("复核单ID不能为空");
}
List<ReviewMaterialDetailPO> inputDetails = cmd.getDetails();
if (CollectionUtils.isEmpty(inputDetails)) {
throw new ServiceException("复核明细不能为空");
}
for (ReviewMaterialDetailPO i : inputDetails) {
if (i == null || i.getReviewMaterialDetailId() == null) {
continue;
}
ReviewMaterialDetail db = reviewMaterialDetailMapper.selectById(i.getReviewMaterialDetailId());
if (db == null || db.getDelFlag() == null || db.getDelFlag() != 1) {
continue;
}
if (!cmd.getReviewOrderId().equals(db.getReviewOrderId())) {
continue;
}
BigDecimal reviewed = nz(db.getReviewedQty());
BigDecimal pending = nz(db.getPendingReviewQty());
BigDecimal cancel = nz(db.getCancelQty());
BigDecimal qty = i.getReviewQtyEa() != null ? i.getReviewQtyEa() :
(i.getCancelQty() != null ? i.getCancelQty() : reviewed);
if (qty.compareTo(BigDecimal.ZERO) <= 0) {
continue;
}
if (qty.compareTo(reviewed) > 0) {
qty = reviewed;
}
db.setReviewedQty(reviewed.subtract(qty));
db.setPendingReviewQty(pending.add(qty));
db.setCancelQty(cancel.add(qty));
fillOperator(db);
reviewMaterialDetailMapper.updateById(db);
}
refreshReviewOrderStatus(cmd.getReviewOrderId());
return true;
}
@Transactional(rollbackFor = Exception.class)
public Boolean checkConfirm(ReviewOrderDO cmd) {
if (cmd == null) {
throw new ServiceException("参数不能为空");
}
List<Long> ids = cmd.getReviewOrderIds();
if (CollectionUtils.isEmpty(ids)) {
ids = new ArrayList<>();
if (cmd.getReviewOrderId() != null) {
ids.add(cmd.getReviewOrderId());
}
}
if (CollectionUtils.isEmpty(ids)) {
throw new ServiceException("复核单ID不能为空");
}
for (Long reviewOrderId : ids) {
if (reviewOrderId == null) {
continue;
}
List<ReviewMaterialDetailPO> rows = reviewOrderMapper.queryDetailByReviewOrderId(reviewOrderId);
boolean hasPending = rows.stream().anyMatch(r -> nz(r.getPendingReviewQty()).compareTo(BigDecimal.ZERO) > 0);
if (hasPending) {
throw new ServiceException("存在待复核数量,无法确认");
}
ReviewOrder order = reviewOrderMapper.selectById(reviewOrderId);
if (order == null) {
continue;
}
order.setReviewStatus(3);
fillOperator(order);
reviewOrderMapper.updateById(order);
}
return true;
}
private void refreshReviewOrderStatus(Long reviewOrderId) {
List<ReviewMaterialDetailPO> rows = reviewOrderMapper.queryDetailByReviewOrderId(reviewOrderId);
boolean allReviewed = rows.stream().allMatch(r -> nz(r.getPendingReviewQty()).compareTo(BigDecimal.ZERO) == 0);
boolean noneReviewed = rows.stream().allMatch(r -> nz(r.getReviewedQty()).compareTo(BigDecimal.ZERO) == 0);
Integer targetStatus = allReviewed ? 3 : (noneReviewed ? 1 : 2);
ReviewOrder order = reviewOrderMapper.selectById(reviewOrderId);
if (order == null) {
return;
}
order.setReviewStatus(targetStatus);
fillOperator(order);
reviewOrderMapper.updateById(order);
}
private static BigDecimal nz(BigDecimal v) {
return v == null ? BigDecimal.ZERO : v;
}
private static String resolveLoginDisplayName(LoginUser loginUser) {
if (loginUser == null) {
return null;
}
if (loginUser.getUserPo() != null && StringUtils.isNotBlank(loginUser.getUserPo().getUserName())) {
return loginUser.getUserPo().getUserName();
}
if (StringUtils.isNotBlank(loginUser.getRealname())) {
return loginUser.getRealname();
}
return loginUser.getUsername();
}
private void fillOperator(ReviewOrder order) {
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser == null) {
return;
}
order.setUpdateBy(loginUser.getUserid());
order.setUpdateByName(resolveLoginDisplayName(loginUser));
order.setUpdateTime(new Date());
}
private void fillOperator(ReviewMaterialDetail detail) {
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser == null) {
return;
}
detail.setUpdateBy(loginUser.getUserid());
detail.setUpdateByName(resolveLoginDisplayName(loginUser));
detail.setUpdateTime(new Date());
}
/**
* 复核单详情:动态批次属性反显(参考收货详情)
* - 批次管理维护 isDisplay=1 的属性才参与显示
* - 每行按 outUniqueId 作为 material_detail_unique_id 去 material_more_detail 取 attribute_value
* - 将属性值映射到 productionDate/expiryDate/inventoryDate/batchRefNo/sheetRefNo/boxPalletNo/extAttr1-4
*/
private void fillDynamicBatchDisplayFields(List<ReviewMaterialDetailPO> details) {
if (CollectionUtils.isEmpty(details)) {
return;
}
// 1) 批量获取“批次管理”配置(只取 isDisplay=1
Set<Long> materialBaseInfoIdSet = details.stream()
.map(ReviewMaterialDetailPO::getMaterialBaseInfoId)
.filter(v -> v != null)
.collect(Collectors.toSet());
Map<Long, List<BatchDetailFeignPO>> batchDetailMap = new HashMap<>();
for (Long materialBaseInfoId : materialBaseInfoIdSet) {
try {
AjaxResult ajaxResult = systemServiceFeign.getBatchDetailListByMaterialBaseInfoId(materialBaseInfoId);
if (ajaxResult != null && "200".equals(String.valueOf(ajaxResult.get("code")))) {
List<BatchDetailFeignPO> batchDetailFeignPOList = JSON.parseArray(
JSONObject.toJSONString(ajaxResult.get("data")), BatchDetailFeignPO.class);
if (!CollectionUtils.isEmpty(batchDetailFeignPOList)) {
List<BatchDetailFeignPO> filteredList = batchDetailFeignPOList.stream()
.filter(b -> b.getIsDisplay() != null && b.getIsDisplay() == 1)
.collect(Collectors.toList());
batchDetailMap.put(materialBaseInfoId, filteredList);
}
}
} catch (Exception e) {
// 配置拉取失败不影响详情主流程
}
}
// 2) 每行按 outUniqueId + batchDetailId 到 material_more_detail 取值并映射回字段
for (ReviewMaterialDetailPO d : details) {
if (d == null || d.getMaterialBaseInfoId() == null) {
continue;
}
List<BatchDetailFeignPO> displayBatchDetails = batchDetailMap.get(d.getMaterialBaseInfoId());
if (CollectionUtils.isEmpty(displayBatchDetails)) {
continue;
}
Long uniqueId = d.getOutUniqueId();
if (uniqueId == null) {
continue;
}
for (BatchDetailFeignPO batchDetail : displayBatchDetails) {
if (batchDetail == null || batchDetail.getBatchDetailId() == null) {
continue;
}
String targetField = null;
if (StringUtils.isNotBlank(batchDetail.getFieldName())) {
targetField = normalizeFieldName(batchDetail.getFieldName());
}
if (targetField == null && StringUtils.isNotBlank(batchDetail.getBatchLabels())) {
targetField = getFieldNameByBatchLabel(batchDetail.getBatchLabels());
}
if (targetField == null) {
continue;
}
String attrValue = getAttributeValueFromMaterialMoreDetail(uniqueId, batchDetail.getBatchDetailId());
if (StringUtils.isBlank(attrValue)) {
continue;
}
applyAttrValueToDetail(d, targetField, attrValue.trim());
}
}
}
private String getAttributeValueFromMaterialMoreDetail(Long materialDetailUniqueId, Long batchDetailId) {
if (materialDetailUniqueId == null || batchDetailId == null) {
return null;
}
try {
MaterialMoreDetailDO q = new MaterialMoreDetailDO();
q.setMaterialDetailUniqueId(materialDetailUniqueId);
q.setBatchDetailId(batchDetailId);
List<MaterialMoreDetailPO> list = materialMoreDetailDomainService.queryList(q);
if (!CollectionUtils.isEmpty(list)) {
return list.get(0).getAttributeValue();
}
} catch (Exception e) {
return null;
}
return null;
}
/**
* 批次管理 fieldName 可能为下划线或驼峰,统一成驼峰目标字段
*/
private static String normalizeFieldName(String fieldName) {
if (StringUtils.isBlank(fieldName)) {
return null;
}
String f = fieldName.trim();
// 下划线 -> 驼峰(只处理本次需要的字段)
switch (f) {
case "production_date":
return "productionDate";
case "expiry_date":
return "expiryDate";
case "inventory_date":
return "inventoryDate";
case "batch_ref_no":
return "batchRefNo";
case "sheet_ref_no":
return "sheetRefNo";
case "box_pallet_no":
return "boxPalletNo";
case "ext_attr_1":
return "extAttr1";
case "ext_attr_2":
return "extAttr2";
case "ext_attr_3":
return "extAttr3";
case "ext_attr_4":
return "extAttr4";
default:
return f;
}
}
/**
* 批次属性标签 -> 目标字段名(仅覆盖需要反显的字段)
*/
private static String getFieldNameByBatchLabel(String batchLabels) {
if (StringUtils.isBlank(batchLabels)) {
return null;
}
String label = batchLabels.trim();
switch (label) {
case "生产日期":
return "productionDate";
case "过期日期":
return "expiryDate";
case "存货日期":
return "inventoryDate";
case "Batch Ref NO's":
return "batchRefNo";
case "Sheet Ref NO's":
return "sheetRefNo";
case "箱号/卡板号":
return "boxPalletNo";
case "扩展字段1":
case "扩展属性1":
return "extAttr1";
case "扩展字段2":
case "扩展属性2":
return "extAttr2";
case "扩展字段3":
case "扩展属性3":
return "extAttr3";
case "扩展字段4":
case "扩展属性4":
return "extAttr4";
default:
return null;
}
}
private static void applyAttrValueToDetail(ReviewMaterialDetailPO d, String fieldName, String value) {
if (d == null || fieldName == null || value == null) {
return;
}
switch (fieldName) {
case "productionDate":
setDateIfParsable(value, d::setProductionDate);
break;
case "expiryDate":
setDateIfParsable(value, d::setExpiryDate);
break;
case "inventoryDate":
setDateIfParsable(value, d::setInventoryDate);
break;
case "batchRefNo":
d.setBatchRefNo(value);
break;
case "sheetRefNo":
d.setSheetRefNo(value);
break;
case "boxPalletNo":
d.setBoxPalletNo(value);
break;
case "extAttr1":
d.setExtAttr1(value);
break;
case "extAttr2":
d.setExtAttr2(value);
break;
case "extAttr3":
setDateIfParsable(value, d::setExtAttr3);
break;
case "extAttr4":
setDateIfParsable(value, d::setExtAttr4);
break;
default:
break;
}
}
private interface DateConsumer {
void accept(Date v);
}
private static void setDateIfParsable(String v, DateConsumer setter) {
if (setter == null || StringUtils.isBlank(v)) {
return;
}
try {
if (v.matches("\\d{4}-\\d{2}-\\d{2}.*")) {
setter.accept(DateUtils.parseDate(v));
}
} catch (Exception ignored) {
}
}
}
@@ -218,6 +218,37 @@ public class ReviewOrderDomainService {
}
if (outMd != null) {
d.setMaterialDetailId(outMd.getMaterialDetailId());
// 批次属性:拣货明细未带出时,从出库明细兜底(项目内同口径)
if (d.getProductionDate() == null) {
d.setProductionDate(outMd.getProductionDate());
}
if (d.getExpiryDate() == null) {
d.setExpiryDate(outMd.getExpiryDate());
}
if (d.getInventoryDate() == null) {
d.setInventoryDate(outMd.getInventoryDate());
}
if (d.getBatchRefNo() == null) {
d.setBatchRefNo(outMd.getBatchRefNo());
}
if (d.getSheetRefNo() == null) {
d.setSheetRefNo(outMd.getSheetRefNo());
}
if (d.getBoxPalletNo() == null) {
d.setBoxPalletNo(outMd.getBoxPalletNo());
}
if (d.getExtAttr1() == null) {
d.setExtAttr1(outMd.getExtAttr1());
}
if (d.getExtAttr2() == null) {
d.setExtAttr2(outMd.getExtAttr2());
}
if (d.getExtAttr3() == null) {
d.setExtAttr3(outMd.getExtAttr3());
}
if (d.getExtAttr4() == null) {
d.setExtAttr4(outMd.getExtAttr4());
}
}
}
return d;
@@ -1,9 +1,12 @@
package com.mhd.wms.domain.reviewOrder.repository.todo;
import com.mhd.common.core.web.domain.BaseVOEntity;
import com.mhd.wms.domain.reviewOrder.repository.po.ReviewMaterialDetailPO;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
* 复核单列表查询条件(对应表 review_order
*/
@@ -48,4 +51,10 @@ public class ReviewOrderDO extends BaseVOEntity {
@ApiModelProperty("接收时间止")
private String receiveTimeEnd;
@ApiModelProperty("复核单ID集合")
private List<Long> reviewOrderIds;
@ApiModelProperty("复核明细列表(用于批量复核/取消复核)")
private List<ReviewMaterialDetailPO> details;
}
@@ -1,9 +1,12 @@
package com.mhd.wms.interfaces.dto.reviewOrder;
import com.mhd.common.core.web.domain.BaseVOEntity;
import com.mhd.wms.domain.reviewOrder.repository.po.ReviewMaterialDetailPO;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
* 复核单查询/入参 DTO(列表条件与主键)
*/
@@ -48,4 +51,10 @@ public class ReviewOrderDTO extends BaseVOEntity {
@ApiModelProperty("接收时间止 yyyy-MM-dd")
private String receiveTimeEnd;
@ApiModelProperty("复核单ID集合")
private List<Long> reviewOrderIds;
@ApiModelProperty("复核明细列表(用于批量复核/取消复核)")
private List<ReviewMaterialDetailPO> details;
}
@@ -12,6 +12,9 @@ import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -40,6 +43,25 @@ public class ReviewOrderApi extends BaseController {
return getDataTable(list);
}
/**
* 参考出库单详情接口命名:/getInfo/{id}
*/
@ApiOperation("获取复核单详情(按主键,含明细)")
@GetMapping(value = "/getInfo/{reviewOrderId}")
public AjaxResult getInfo(@PathVariable("reviewOrderId") Long reviewOrderId) {
return AjaxResult.success(reviewOrderApplicationService.getDetailByReviewOrderId(reviewOrderId));
}
/**
* 参考出库单按单号查询:用请求参数而非 PathVariable
*/
@ApiOperation("根据复核单号获取复核单详情(含明细)")
@GetMapping(value = "/getInfoReviewOrderNumber")
public AjaxResult getInfoReviewOrderNumber(@RequestParam("reviewOrderNumber") String reviewOrderNumber) {
return AjaxResult.success(reviewOrderApplicationService.getDetailByReviewOrderNumber(reviewOrderNumber));
}
// 兼容旧接口(前端如仍在调用可继续使用)
@ApiOperation("复核单详情(按主键,含明细)")
@GetMapping("/detail/{reviewOrderId}")
public AjaxResult detail(@PathVariable("reviewOrderId") Long reviewOrderId) {
@@ -51,4 +73,25 @@ public class ReviewOrderApi extends BaseController {
public AjaxResult detailByNumber(@PathVariable("reviewOrderNumber") String reviewOrderNumber) {
return AjaxResult.success(reviewOrderApplicationService.getDetailByReviewOrderNumber(reviewOrderNumber));
}
@ApiOperation("批量复核")
@PostMapping("/batchCheck")
public AjaxResult batchCheck(@RequestBody ReviewOrderDTO reviewOrderDTO) {
ReviewOrderDO cmd = reviewOrderAssembler.toDO(reviewOrderDTO);
return toAjax(reviewOrderApplicationService.batchCheck(cmd));
}
@ApiOperation("批量取消复核")
@PostMapping("/batchCancelCheck")
public AjaxResult batchCancelCheck(@RequestBody ReviewOrderDTO reviewOrderDTO) {
ReviewOrderDO cmd = reviewOrderAssembler.toDO(reviewOrderDTO);
return toAjax(reviewOrderApplicationService.batchCancelCheck(cmd));
}
@ApiOperation("复核确认")
@PostMapping("/checkConfirm")
public AjaxResult checkConfirm(@RequestBody ReviewOrderDTO reviewOrderDTO) {
ReviewOrderDO cmd = reviewOrderAssembler.toDO(reviewOrderDTO);
return toAjax(reviewOrderApplicationService.checkConfirm(cmd));
}
}