原材料入库

This commit is contained in:
2026-05-15 11:34:12 +08:00
parent f5ba828eff
commit 70dced7d2c
1700 changed files with 24156 additions and 33 deletions

View File

@@ -1,6 +1,7 @@
package org.jeecg.modules.xslmes.controller;
import java.util.Arrays;
import java.util.List;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
@@ -10,7 +11,6 @@ import org.jeecg.modules.xslmes.service.IMesXslRawMaterialEntryService;
import org.jeecg.modules.xslmes.service.MesXslStompNotifyService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
@@ -84,15 +84,17 @@ public class MesXslRawMaterialEntryController extends JeecgController<MesXslRawM
}
@AutoLog(value = "原料入场记录-批量结存入库")
@Operation(summary = "原料入场记录-批量结存入库:将选中记录的入库结存改为「是」")
@Operation(summary = "原料入场记录-批量结存入库:将选中记录结存并自动汇总原材料库存")
@RequiresPermissions("xslmes:mes_xsl_raw_material_entry:stockIn")
@PutMapping(value = "/batchStockIn")
public Result<String> batchStockIn(@RequestParam(name = "ids", required = true) String ids) {
UpdateWrapper<MesXslRawMaterialEntry> uw = new UpdateWrapper<>();
uw.in("id", Arrays.asList(ids.split(","))).set("stock_balance", "1");
mesXslRawMaterialEntryService.update(uw);
List<String> idList = Arrays.stream(ids.split(","))
.map(String::trim)
.filter(s -> !s.isEmpty())
.toList();
mesXslRawMaterialEntryService.batchStockInAndSyncInventory(idList);
stompNotify.publishRawMaterialEntryChanged("batchStockIn", ids);
return Result.OK("结存入库成功!");
return Result.OK("结存入库成功,库存已自动汇总");
}
@AutoLog(value = "原料入场记录-批量删除")

View File

@@ -0,0 +1,158 @@
package org.jeecg.modules.xslmes.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialInventory;
import org.jeecg.modules.xslmes.service.IMesXslRawMaterialInventoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 原材料库存
*/
@Tag(name = "原材料库存")
@RestController
@RequestMapping("/xslmes/mesXslRawMaterialInventory")
@Slf4j
public class MesXslRawMaterialInventoryController extends JeecgController<MesXslRawMaterialInventory, IMesXslRawMaterialInventoryService> {
@Autowired
private IMesXslRawMaterialInventoryService mesXslRawMaterialInventoryService;
@Autowired
private JdbcTemplate jdbcTemplate;
@Operation(summary = "原材料库存-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<MesXslRawMaterialInventory>> queryPageList(
MesXslRawMaterialInventory mesXslRawMaterialInventory,
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<MesXslRawMaterialInventory> queryWrapper = QueryGenerator.initQueryWrapper(mesXslRawMaterialInventory, req.getParameterMap());
Page<MesXslRawMaterialInventory> page = new Page<>(pageNo, pageSize);
IPage<MesXslRawMaterialInventory> pageList = mesXslRawMaterialInventoryService.page(page, queryWrapper);
fillMaterialDisplay(pageList.getRecords());
return Result.OK(pageList);
}
@Operation(summary = "原材料库存-通过id查询")
@GetMapping(value = "/queryById")
public Result<MesXslRawMaterialInventory> queryById(@RequestParam(name = "id", required = true) String id) {
MesXslRawMaterialInventory entity = mesXslRawMaterialInventoryService.getById(id);
if (entity == null) {
return Result.error("未找到对应数据");
}
fillMaterialDisplay(Collections.singletonList(entity));
return Result.OK(entity);
}
@RequiresPermissions("xslmes:mes_xsl_raw_material_inventory:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, MesXslRawMaterialInventory mesXslRawMaterialInventory) {
return super.exportXls(request, mesXslRawMaterialInventory, MesXslRawMaterialInventory.class, "原材料库存");
}
private void fillMaterialDisplay(List<MesXslRawMaterialInventory> rows) {
if (rows == null || rows.isEmpty()) {
return;
}
Set<String> materialIds = rows.stream()
.map(MesXslRawMaterialInventory::getMaterialId)
.filter(s -> s != null && !s.isBlank())
.collect(Collectors.toSet());
if (materialIds.isEmpty()) {
fillWarehouseDisplay(rows);
return;
}
List<String> idList = materialIds.stream().toList();
String placeholders = String.join(",", Collections.nCopies(idList.size(), "?"));
String sql = "SELECT id, material_code, material_name FROM mes_mixer_material WHERE id IN (" + placeholders + ") "
+ "AND (del_flag = 0 OR del_flag IS NULL)";
Map<String, String> codeMap = new HashMap<>();
Map<String, String> nameMap = new HashMap<>();
jdbcTemplate.query(sql, rs -> {
String id = trim(rs.getString("id"));
if (id.isEmpty()) {
return;
}
codeMap.put(id, trim(rs.getString("material_code")));
nameMap.put(id, trim(rs.getString("material_name")));
}, idList.toArray());
for (MesXslRawMaterialInventory row : rows) {
String id = trim(row.getMaterialId());
if (id.isEmpty()) {
continue;
}
String code = codeMap.get(id);
String name = nameMap.get(id);
if (code != null && !code.isBlank()) {
row.setMaterialCode(code);
}
if (name != null && !name.isBlank()) {
row.setMaterialName(name);
}
}
fillWarehouseDisplay(rows);
}
private void fillWarehouseDisplay(List<MesXslRawMaterialInventory> rows) {
if (rows == null || rows.isEmpty()) {
return;
}
Set<String> warehouseIds = rows.stream()
.map(MesXslRawMaterialInventory::getWarehouseId)
.filter(s -> s != null && !s.isBlank())
.collect(Collectors.toSet());
if (warehouseIds.isEmpty()) {
return;
}
List<String> idList = warehouseIds.stream().toList();
String placeholders = String.join(",", Collections.nCopies(idList.size(), "?"));
String sql = "SELECT id, warehouse_name FROM mes_xsl_warehouse WHERE id IN (" + placeholders + ") "
+ "AND (del_flag = 0 OR del_flag IS NULL)";
Map<String, String> nameMap = new HashMap<>();
jdbcTemplate.query(sql, rs -> {
String id = trim(rs.getString("id"));
if (id.isEmpty()) {
return;
}
nameMap.put(id, trim(rs.getString("warehouse_name")));
}, idList.toArray());
for (MesXslRawMaterialInventory row : rows) {
String id = trim(row.getWarehouseId());
if (id.isEmpty()) {
continue;
}
String name = nameMap.get(id);
if (name != null && !name.isBlank()) {
row.setWarehouseName(name);
}
}
}
private String trim(String value) {
return value == null ? "" : value.trim();
}
}

View File

@@ -0,0 +1,59 @@
package org.jeecg.modules.xslmes.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.jeecg.common.aspect.annotation.Dict;
import org.jeecg.common.system.base.entity.JeecgEntity;
import org.jeecgframework.poi.excel.annotation.Excel;
import java.math.BigDecimal;
/**
* 原材料库存
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("mes_xsl_raw_material_inventory")
@Schema(description = "原材料库存")
public class MesXslRawMaterialInventory extends JeecgEntity {
private static final long serialVersionUID = 1L;
@Excel(name = "所在仓库", width = 20)
@Schema(description = "所在仓库")
private String warehouseName;
@Schema(description = "所在仓库ID关联 mes_xsl_warehouse.id")
private String warehouseId;
@Schema(description = "物料ID关联 mes_mixer_material.id")
private String materialId;
@Excel(name = "物料名称", width = 20)
@Schema(description = "物料名称")
private String materialName;
@Excel(name = "物料编码", width = 20)
@Schema(description = "物料编码")
private String materialCode;
@Excel(name = "状态检验", width = 12, dicCode = "xslmes_test_result")
@Dict(dicCode = "xslmes_test_result")
@Schema(description = "状态检验(字典 xslmes_test_result")
private String testResult;
@Excel(name = "总包数", width = 12)
@Schema(description = "总包数")
private BigDecimal totalPackages;
@Excel(name = "总重量", width = 12)
@Schema(description = "总重量")
private BigDecimal totalWeight;
@Schema(description = "租户ID")
private Integer tenantId;
}

View File

@@ -0,0 +1,12 @@
package org.jeecg.modules.xslmes.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialInventory;
/**
* 原材料库存 Mapper
*/
@Mapper
public interface MesXslRawMaterialInventoryMapper extends BaseMapper<MesXslRawMaterialInventory> {
}

View File

@@ -34,4 +34,15 @@ public interface IMesXslRawMaterialEntryService extends IService<MesXslRawMateri
* @return billNo -&gt; 累计已入场重量;查不到的 billNo 不会出现在 map 中
*/
Map<String, BigDecimal> sumEnteredWeightByBillNos(Collection<String> billNos);
/**
* 结存入库并汇总原材料库存。
* <p>
* 对本次选中的入场记录(仅处理 stock_balance != 1
* 「所在仓库 + 物料编码 + 检验状态」汇总总包数/总重量并累加到库存表,
* 同时将入场记录 stock_balance 更新为 1。
*
* @param ids 入场记录主键集合
*/
void batchStockInAndSyncInventory(Collection<String> ids);
}

View File

@@ -0,0 +1,10 @@
package org.jeecg.modules.xslmes.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialInventory;
/**
* 原材料库存
*/
public interface IMesXslRawMaterialInventoryService extends IService<MesXslRawMaterialInventory> {
}

View File

@@ -1,14 +1,25 @@
package org.jeecg.modules.xslmes.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialCard;
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialInventory;
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialEntry;
import org.jeecg.modules.xslmes.entity.MesXslWarehouseArea;
import org.jeecg.modules.xslmes.mapper.MesXslRawMaterialEntryMapper;
import org.jeecg.modules.xslmes.service.IMesXslRawMaterialCardService;
import org.jeecg.modules.xslmes.service.IMesXslRawMaterialInventoryService;
import org.jeecg.modules.xslmes.service.IMesXslRawMaterialEntryService;
import org.jeecg.modules.xslmes.service.IMesXslWarehouseAreaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
@@ -30,6 +41,15 @@ public class MesXslRawMaterialEntryServiceImpl
extends ServiceImpl<MesXslRawMaterialEntryMapper, MesXslRawMaterialEntry>
implements IMesXslRawMaterialEntryService {
@Autowired
private IMesXslRawMaterialInventoryService mesXslRawMaterialInventoryService;
@Autowired
private IMesXslRawMaterialCardService mesXslRawMaterialCardService;
@Autowired
private IMesXslWarehouseAreaService mesXslWarehouseAreaService;
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public String generateBarcode(String materialCode) {
if (materialCode == null) materialCode = "";
@@ -76,6 +96,166 @@ public class MesXslRawMaterialEntryServiceImpl
return result;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void batchStockInAndSyncInventory(Collection<String> ids) {
if (ids == null || ids.isEmpty()) {
return;
}
Set<String> distinctIds = ids.stream()
.filter(s -> s != null && !s.isBlank())
.collect(Collectors.toCollection(HashSet::new));
if (distinctIds.isEmpty()) {
return;
}
// 仅处理未结存记录,避免重复点击导致库存重复累加。
List<MesXslRawMaterialEntry> entries = this.lambdaQuery()
.in(MesXslRawMaterialEntry::getId, distinctIds)
.and(w -> w.ne(MesXslRawMaterialEntry::getStockBalance, "1")
.or()
.isNull(MesXslRawMaterialEntry::getStockBalance))
.list();
if (entries.isEmpty()) {
return;
}
Map<String, WarehouseRef> warehouseByEntryId = resolveWarehouseByEntry(entries);
Set<String> materialIds = entries.stream()
.map(MesXslRawMaterialEntry::getMaterialId)
.filter(s -> s != null && !s.isBlank())
.collect(Collectors.toSet());
Set<String> materialCodes = entries.stream()
.map(e -> {
String code = normalizeText(e.getMaterialCode());
return code.isEmpty() ? parseMaterialCodeFromBarcode(e.getBarcode()) : code;
})
.filter(s -> s != null && !s.isBlank())
.collect(Collectors.toSet());
Map<String, MaterialBaseInfo> materialInfoById = loadMaterialInfoByIds(materialIds);
Map<String, MaterialBaseInfo> materialInfoByCode = loadMaterialInfoByCodes(materialCodes);
Map<String, InventoryAgg> aggMap = new HashMap<>();
for (MesXslRawMaterialEntry entry : entries) {
WarehouseRef warehouseRef = warehouseByEntryId.get(entry.getId());
String warehouseId = warehouseRef == null ? "" : normalizeText(warehouseRef.id);
String warehouseName = warehouseRef == null ? "" : normalizeText(warehouseRef.name);
if (warehouseName.isEmpty()) {
warehouseName = normalizeText(entry.getWarehouseLocation());
}
String materialId = normalizeText(entry.getMaterialId());
String materialCode = normalizeText(entry.getMaterialCode());
if (materialCode.isEmpty()) {
materialCode = parseMaterialCodeFromBarcode(entry.getBarcode());
}
MaterialBaseInfo materialInfo = null;
if (!materialId.isEmpty()) {
materialInfo = materialInfoById.get(materialId);
}
if (materialInfo == null && !materialCode.isEmpty()) {
materialInfo = materialInfoByCode.get(materialCode);
if (materialInfo != null && materialId.isEmpty()) {
materialId = normalizeText(materialInfo.id);
}
}
if (materialInfo != null) {
if (!normalizeText(materialInfo.materialCode).isEmpty()) {
materialCode = normalizeText(materialInfo.materialCode);
}
}
String materialName = materialInfo == null ? normalizeText(entry.getMaterialName()) : normalizeText(materialInfo.materialName);
String testResult = normalizeText(entry.getTestResult());
String materialKey = materialId.isEmpty() ? ("CODE:" + materialCode) : ("ID:" + materialId);
String warehouseKey = warehouseId.isEmpty() ? ("NAME:" + warehouseName) : ("ID:" + warehouseId);
String key = warehouseKey + "|" + materialKey;
BigDecimal packageCount = resolvePackageCount(entry);
BigDecimal weight = resolveWeight(entry);
InventoryAgg agg = aggMap.computeIfAbsent(key, k -> new InventoryAgg());
if (agg.warehouseId == null || agg.warehouseId.isBlank()) {
agg.warehouseId = warehouseId;
}
agg.warehouseName = warehouseName;
if (agg.materialId == null || agg.materialId.isBlank()) {
agg.materialId = materialId;
}
agg.materialCode = materialCode;
agg.testResult = testResult;
if (agg.materialName == null || agg.materialName.isBlank()) {
agg.materialName = materialName;
}
agg.totalPackages = agg.totalPackages.add(packageCount);
agg.totalWeight = agg.totalWeight.add(weight);
}
List<MesXslRawMaterialInventory> toSave = new ArrayList<>();
for (InventoryAgg agg : aggMap.values()) {
MesXslRawMaterialInventory existing = mesXslRawMaterialInventoryService.lambdaQuery()
.eq(agg.warehouseId != null && !agg.warehouseId.isBlank(), MesXslRawMaterialInventory::getWarehouseId, agg.warehouseId)
.eq(agg.warehouseId == null || agg.warehouseId.isBlank(), MesXslRawMaterialInventory::getWarehouseName, agg.warehouseName)
.eq(agg.materialId != null && !agg.materialId.isBlank(), MesXslRawMaterialInventory::getMaterialId, agg.materialId)
.eq(agg.materialId == null || agg.materialId.isBlank(), MesXslRawMaterialInventory::getMaterialCode, agg.materialCode)
.one();
if (existing == null && agg.warehouseId != null && !agg.warehouseId.isBlank()) {
existing = mesXslRawMaterialInventoryService.lambdaQuery()
.eq(MesXslRawMaterialInventory::getWarehouseName, agg.warehouseName)
.eq(agg.materialId != null && !agg.materialId.isBlank(), MesXslRawMaterialInventory::getMaterialId, agg.materialId)
.eq(agg.materialId == null || agg.materialId.isBlank(), MesXslRawMaterialInventory::getMaterialCode, agg.materialCode)
.and(w -> w.isNull(MesXslRawMaterialInventory::getWarehouseId)
.or()
.eq(MesXslRawMaterialInventory::getWarehouseId, ""))
.one();
}
if (existing == null && agg.materialId != null && !agg.materialId.isBlank()) {
existing = mesXslRawMaterialInventoryService.lambdaQuery()
.eq(MesXslRawMaterialInventory::getWarehouseName, agg.warehouseName)
.eq(MesXslRawMaterialInventory::getTestResult, agg.testResult)
.eq(MesXslRawMaterialInventory::getMaterialCode, agg.materialCode)
.and(w -> w.isNull(MesXslRawMaterialInventory::getMaterialId)
.or()
.eq(MesXslRawMaterialInventory::getMaterialId, ""))
.one();
}
if (existing == null) {
MesXslRawMaterialInventory inventory = new MesXslRawMaterialInventory();
inventory.setWarehouseName(agg.warehouseName);
inventory.setWarehouseId(agg.warehouseId);
inventory.setMaterialId(agg.materialId);
inventory.setMaterialName(agg.materialName);
inventory.setMaterialCode(agg.materialCode);
inventory.setTestResult(agg.testResult);
inventory.setTotalPackages(agg.totalPackages);
inventory.setTotalWeight(agg.totalWeight);
toSave.add(inventory);
continue;
}
if (normalizeText(existing.getWarehouseId()).isEmpty() && agg.warehouseId != null && !agg.warehouseId.isBlank()) {
existing.setWarehouseId(agg.warehouseId);
}
existing.setWarehouseName((agg.warehouseName == null || agg.warehouseName.isBlank()) ? existing.getWarehouseName() : agg.warehouseName);
if (normalizeText(existing.getMaterialId()).isEmpty() && agg.materialId != null && !agg.materialId.isBlank()) {
existing.setMaterialId(agg.materialId);
}
existing.setMaterialCode((agg.materialCode == null || agg.materialCode.isBlank()) ? existing.getMaterialCode() : agg.materialCode);
existing.setMaterialName((agg.materialName == null || agg.materialName.isBlank()) ? existing.getMaterialName() : agg.materialName);
if (normalizeText(existing.getTestResult()).isEmpty() && agg.testResult != null && !agg.testResult.isBlank()) {
existing.setTestResult(agg.testResult);
}
existing.setTotalPackages(nullSafe(existing.getTotalPackages()).add(agg.totalPackages));
existing.setTotalWeight(nullSafe(existing.getTotalWeight()).add(agg.totalWeight));
mesXslRawMaterialInventoryService.updateById(existing);
}
if (!toSave.isEmpty()) {
mesXslRawMaterialInventoryService.saveBatch(toSave);
}
List<String> processedIds = entries.stream().map(MesXslRawMaterialEntry::getId).toList();
LambdaUpdateWrapper<MesXslRawMaterialEntry> uw = new LambdaUpdateWrapper<>();
uw.in(MesXslRawMaterialEntry::getId, processedIds)
.set(MesXslRawMaterialEntry::getStockBalance, "1");
this.update(uw);
}
/**
* 把一条入场记录的 totalPortions/portionWeight 按 "x/y/z/" 解析后,逐位 (份数 × 每份重量) 累加。
* 任一位置解析失败或缺失,跳过该位置(不抛异常,确保前端展示降级可用)。
@@ -114,4 +294,295 @@ public class MesXslRawMaterialEntryServiceImpl
return null;
}
}
private static BigDecimal resolvePackageCount(MesXslRawMaterialEntry entry) {
BigDecimal fromSplit = sumByPortion(entry.getTotalPortions(), entry.getPortionPackages());
if (fromSplit.compareTo(BigDecimal.ZERO) > 0) {
return fromSplit;
}
BigDecimal fallback = tryParseBigDecimal(entry.getTotalPortions());
return fallback == null ? BigDecimal.ZERO : fallback;
}
private static BigDecimal resolveWeight(MesXslRawMaterialEntry entry) {
BigDecimal fromSplit = sumByPortion(entry.getTotalPortions(), entry.getPortionWeight());
if (fromSplit.compareTo(BigDecimal.ZERO) > 0) {
return fromSplit;
}
return nullSafe(entry.getTotalWeight());
}
private static BigDecimal sumByPortion(String portionsRaw, String valueRaw) {
String[] portionsArr = splitJoined(portionsRaw);
String[] valuesArr = splitJoined(valueRaw);
if (portionsArr.length == 0 || valuesArr.length == 0) {
return BigDecimal.ZERO;
}
int n = Math.min(portionsArr.length, valuesArr.length);
BigDecimal sum = BigDecimal.ZERO;
for (int i = 0; i < n; i++) {
BigDecimal portions = tryParseBigDecimal(portionsArr[i]);
BigDecimal val = tryParseBigDecimal(valuesArr[i]);
if (portions == null || val == null) {
continue;
}
sum = sum.add(portions.multiply(val));
}
return sum;
}
private static String normalizeText(String value) {
return value == null ? "" : value.trim();
}
private static BigDecimal nullSafe(BigDecimal value) {
return value == null ? BigDecimal.ZERO : value;
}
private static class InventoryAgg {
private String warehouseId;
private String warehouseName;
private String materialId;
private String materialName;
private String materialCode;
private String testResult;
private BigDecimal totalPackages = BigDecimal.ZERO;
private BigDecimal totalWeight = BigDecimal.ZERO;
}
/**
* 从入场记录关联的「原材料卡片 -> 库区 -> 所属仓库」反查库存仓库名称。
* 优先通过 splitDetailId 关联;若无 splitDetailId再降级按 barcode+batchNo 取卡片。
*/
private Map<String, WarehouseRef> resolveWarehouseByEntry(List<MesXslRawMaterialEntry> entries) {
Map<String, WarehouseRef> result = new HashMap<>();
if (entries == null || entries.isEmpty()) {
return result;
}
Map<String, List<String>> splitIdsByEntryId = new HashMap<>();
Set<String> allSplitIds = new HashSet<>();
for (MesXslRawMaterialEntry entry : entries) {
String[] splitIds = splitJoined(entry.getPortionDetailIds());
if (splitIds.length == 0) {
continue;
}
List<String> list = java.util.Arrays.stream(splitIds).toList();
splitIdsByEntryId.put(entry.getId(), list);
allSplitIds.addAll(list);
}
Map<String, WarehouseRef> splitIdToWarehouse = new HashMap<>();
if (!allSplitIds.isEmpty()) {
List<MesXslRawMaterialCard> cards = mesXslRawMaterialCardService.lambdaQuery()
.in(MesXslRawMaterialCard::getSplitDetailId, allSplitIds)
.select(MesXslRawMaterialCard::getSplitDetailId, MesXslRawMaterialCard::getWarehouseArea)
.list();
Map<String, WarehouseRef> areaToWarehouse = resolveAreaToWarehouseMap(cards.stream()
.map(MesXslRawMaterialCard::getWarehouseArea)
.filter(s -> s != null && !s.isBlank())
.collect(Collectors.toSet()));
for (MesXslRawMaterialCard card : cards) {
String splitId = normalizeText(card.getSplitDetailId());
if (splitId.isEmpty()) {
continue;
}
WarehouseRef warehouse = resolveWarehouse(card.getWarehouseArea(), areaToWarehouse);
if (warehouse == null || (warehouse.id.isEmpty() && warehouse.name.isEmpty())) {
continue;
}
splitIdToWarehouse.putIfAbsent(splitId, warehouse);
}
}
for (MesXslRawMaterialEntry entry : entries) {
List<String> splitIds = splitIdsByEntryId.get(entry.getId());
if (splitIds == null || splitIds.isEmpty()) {
continue;
}
for (String splitId : splitIds) {
WarehouseRef warehouse = splitIdToWarehouse.get(splitId);
if (warehouse != null && (!warehouse.id.isBlank() || !warehouse.name.isBlank())) {
result.put(entry.getId(), warehouse);
break;
}
}
}
// 降级:没有 splitDetailId 时,按 barcode+batchNo 找一张关联卡片取仓库。
List<MesXslRawMaterialEntry> unresolved = entries.stream()
.filter(e -> !result.containsKey(e.getId()))
.filter(e -> !normalizeText(e.getBarcode()).isEmpty() || !normalizeText(e.getBatchNo()).isEmpty())
.toList();
if (unresolved.isEmpty()) {
return result;
}
Set<String> barcodes = unresolved.stream()
.map(MesXslRawMaterialEntry::getBarcode)
.filter(s -> s != null && !s.isBlank())
.collect(Collectors.toSet());
Set<String> batchNos = unresolved.stream()
.map(MesXslRawMaterialEntry::getBatchNo)
.filter(s -> s != null && !s.isBlank())
.collect(Collectors.toSet());
if (barcodes.isEmpty() && batchNos.isEmpty()) {
return result;
}
LambdaQueryWrapper<MesXslRawMaterialCard> cardQw = new LambdaQueryWrapper<>();
if (!barcodes.isEmpty() && !batchNos.isEmpty()) {
cardQw.and(w -> w.in(MesXslRawMaterialCard::getBarcode, barcodes)
.or()
.in(MesXslRawMaterialCard::getBatchNo, batchNos));
} else if (!barcodes.isEmpty()) {
cardQw.in(MesXslRawMaterialCard::getBarcode, barcodes);
} else {
cardQw.in(MesXslRawMaterialCard::getBatchNo, batchNos);
}
cardQw.select(MesXslRawMaterialCard::getBarcode, MesXslRawMaterialCard::getBatchNo, MesXslRawMaterialCard::getWarehouseArea);
List<MesXslRawMaterialCard> cards = mesXslRawMaterialCardService.list(cardQw);
Map<String, WarehouseRef> areaToWarehouse = resolveAreaToWarehouseMap(cards.stream()
.map(MesXslRawMaterialCard::getWarehouseArea)
.filter(s -> s != null && !s.isBlank())
.collect(Collectors.toSet()));
for (MesXslRawMaterialEntry entry : unresolved) {
String barcode = normalizeText(entry.getBarcode());
String batchNo = normalizeText(entry.getBatchNo());
for (MesXslRawMaterialCard card : cards) {
boolean match = false;
if (!barcode.isEmpty() && barcode.equals(normalizeText(card.getBarcode()))) {
match = true;
}
if (!match && !batchNo.isEmpty() && batchNo.equals(normalizeText(card.getBatchNo()))) {
match = true;
}
if (!match) {
continue;
}
WarehouseRef warehouse = resolveWarehouse(card.getWarehouseArea(), areaToWarehouse);
if (warehouse != null && (!warehouse.id.isBlank() || !warehouse.name.isBlank())) {
result.put(entry.getId(), warehouse);
break;
}
}
}
return result;
}
private Map<String, WarehouseRef> resolveAreaToWarehouseMap(Set<String> areaRefs) {
if (areaRefs == null || areaRefs.isEmpty()) {
return Collections.emptyMap();
}
List<MesXslWarehouseArea> areas = mesXslWarehouseAreaService.lambdaQuery()
.and(w -> w.in(MesXslWarehouseArea::getId, areaRefs)
.or()
.in(MesXslWarehouseArea::getAreaName, areaRefs))
.select(MesXslWarehouseArea::getId, MesXslWarehouseArea::getAreaName, MesXslWarehouseArea::getWarehouseId, MesXslWarehouseArea::getWarehouseName)
.list();
Map<String, WarehouseRef> map = new HashMap<>();
for (MesXslWarehouseArea area : areas) {
WarehouseRef warehouse = new WarehouseRef();
warehouse.id = normalizeText(area.getWarehouseId());
warehouse.name = normalizeText(area.getWarehouseName());
if (warehouse.id.isEmpty() && warehouse.name.isEmpty()) {
continue;
}
String id = normalizeText(area.getId());
if (!id.isEmpty()) {
map.putIfAbsent(id, warehouse);
}
String name = normalizeText(area.getAreaName());
if (!name.isEmpty()) {
map.putIfAbsent(name, warehouse);
}
}
return map;
}
private WarehouseRef resolveWarehouse(String areaRef, Map<String, WarehouseRef> areaToWarehouse) {
String area = normalizeText(areaRef);
if (area.isEmpty()) {
return null;
}
return areaToWarehouse.get(area);
}
private static String parseMaterialCodeFromBarcode(String barcodeRaw) {
String barcode = normalizeText(barcodeRaw);
// 规则QH + 物料编码 + yyMMdd + 3位流水最短长度 QH+1+6+3=12
if (!barcode.startsWith("QH") || barcode.length() < 12) {
return "";
}
int endExclusive = barcode.length() - 9;
if (endExclusive <= 2) {
return "";
}
return barcode.substring(2, endExclusive).trim();
}
private Map<String, MaterialBaseInfo> loadMaterialInfoByIds(Set<String> ids) {
if (ids == null || ids.isEmpty()) {
return Collections.emptyMap();
}
List<String> idList = ids.stream().filter(s -> s != null && !s.isBlank()).toList();
if (idList.isEmpty()) {
return Collections.emptyMap();
}
String placeholders = String.join(",", Collections.nCopies(idList.size(), "?"));
String sql = "SELECT id, material_code, material_name FROM mes_mixer_material WHERE id IN (" + placeholders + ") "
+ "AND (del_flag = 0 OR del_flag IS NULL)";
List<MaterialBaseInfo> rows = jdbcTemplate.query(sql, (rs, rowNum) -> {
MaterialBaseInfo info = new MaterialBaseInfo();
info.id = normalizeText(rs.getString("id"));
info.materialCode = normalizeText(rs.getString("material_code"));
info.materialName = normalizeText(rs.getString("material_name"));
return info;
}, idList.toArray());
Map<String, MaterialBaseInfo> map = new HashMap<>();
for (MaterialBaseInfo row : rows) {
if (row.id.isEmpty()) {
continue;
}
map.putIfAbsent(row.id, row);
}
return map;
}
private Map<String, MaterialBaseInfo> loadMaterialInfoByCodes(Set<String> codes) {
if (codes == null || codes.isEmpty()) {
return Collections.emptyMap();
}
List<String> codeList = codes.stream().filter(s -> s != null && !s.isBlank()).toList();
if (codeList.isEmpty()) {
return Collections.emptyMap();
}
String placeholders = String.join(",", Collections.nCopies(codeList.size(), "?"));
String sql = "SELECT id, material_code, material_name FROM mes_mixer_material WHERE material_code IN (" + placeholders + ") "
+ "AND (del_flag = 0 OR del_flag IS NULL)";
List<MaterialBaseInfo> rows = jdbcTemplate.query(sql, (rs, rowNum) -> {
MaterialBaseInfo info = new MaterialBaseInfo();
info.id = normalizeText(rs.getString("id"));
info.materialCode = normalizeText(rs.getString("material_code"));
info.materialName = normalizeText(rs.getString("material_name"));
return info;
}, codeList.toArray());
Map<String, MaterialBaseInfo> map = new HashMap<>();
for (MaterialBaseInfo row : rows) {
if (row.materialCode.isEmpty()) {
continue;
}
map.putIfAbsent(row.materialCode, row);
}
return map;
}
private static class MaterialBaseInfo {
private String id;
private String materialCode;
private String materialName;
}
private static class WarehouseRef {
private String id = "";
private String name = "";
}
}

View File

@@ -0,0 +1,15 @@
package org.jeecg.modules.xslmes.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialInventory;
import org.jeecg.modules.xslmes.mapper.MesXslRawMaterialInventoryMapper;
import org.jeecg.modules.xslmes.service.IMesXslRawMaterialInventoryService;
import org.springframework.stereotype.Service;
/**
* 原材料库存
*/
@Service
public class MesXslRawMaterialInventoryServiceImpl extends ServiceImpl<MesXslRawMaterialInventoryMapper, MesXslRawMaterialInventory>
implements IMesXslRawMaterialInventoryService {
}