同步规程同步代码和录入监测代码

This commit is contained in:
2026-05-23 19:34:52 +08:00
parent 6b58f37616
commit 35ad50a79d
29 changed files with 2357 additions and 329 deletions

View File

@@ -361,6 +361,59 @@ public class SqlServerApiClient {
);
}
public ExecuteSqlResponse queryPlanListByHotCoilIdLike(String hotCoilId, int page, int pageSize) {
int endRow = page * pageSize;
int startRow = endRow - pageSize;
Map<String, Object> params = new java.util.HashMap<>();
params.put("startRow", startRow);
params.put("endRow", endRow);
params.put("hotCoilId", "%" + hotCoilId + "%");
return executeSql(
"oracle",
"select * from (select t.*, ROWNUM rn from (select * from JXPLTCM.PLTCM_PDI_PLAN where HOT_COILID LIKE :hotCoilId order by INSDATE desc) t where ROWNUM <= :endRow) where rn > :startRow",
params
);
}
public ExecuteSqlResponse queryPlanCountByHotCoilIdLike(String hotCoilId) {
return executeSql(
"oracle",
"select count(*) as total from JXPLTCM.PLTCM_PDI_PLAN where HOT_COILID LIKE :hotCoilId",
singletonParam("hotCoilId", "%" + hotCoilId + "%")
);
}
/**
* 批量按 EXCOILID 查询出口卷上线/下线时间PLTCM_PDO_EXCOIL
*/
public ExecuteSqlResponse queryExcoilTimesByCoilIds(List<String> coilIds) {
if (coilIds == null || coilIds.isEmpty()) {
return new ExecuteSqlResponse();
}
String inList = coilIds.stream()
.filter(id -> id != null && !id.trim().isEmpty())
.map(id -> "'" + id.replace("'", "''") + "'")
.collect(java.util.stream.Collectors.joining(", "));
if (inList.isEmpty()) return new ExecuteSqlResponse();
String sql = "SELECT EXCOILID, START_DATE, END_DATE FROM JXPLTCM.PLTCM_PDO_EXCOIL WHERE EXCOILID IN (" + inList + ")";
return executeSql("oracle", sql, emptyParams());
}
/**
* 批量按 HOT_COILID 查询计划数据IN 子句,一次 Oracle 调用获取整页数据)
*/
public ExecuteSqlResponse queryPlanDimsByHotCoilIds(List<String> hotCoilIds) {
if (hotCoilIds == null || hotCoilIds.isEmpty()) {
return new ExecuteSqlResponse();
}
String inList = hotCoilIds.stream()
.map(id -> "'" + id.replace("'", "''") + "'")
.collect(java.util.stream.Collectors.joining(", "));
String sql = "SELECT HOT_COILID, ENTRY_THICK, EXIT_THICK, ENTRY_WIDTH, EXIT_WIDTH, GRADE, PROCESS_CODE, COILID " +
"FROM JXPLTCM.PLTCM_PDI_PLAN WHERE HOT_COILID IN (" + inList + ")";
return executeSql("oracle", sql, emptyParams());
}
public ExecuteSqlResponse queryProSegByEncoilId(String encoilId) {
return executeSql(
"oracle",

View File

@@ -66,6 +66,51 @@ public class SqlServerApiBusinessService {
return PlanListView.fromExecuteSqlResponse(client.queryPlanListByStatus(status));
}
public PlanListView getPlanListByHotCoilIdLike(String hotCoilId, int page, int pageSize) {
return PlanListView.fromExecuteSqlResponse(client.queryPlanListByHotCoilIdLike(hotCoilId, page, pageSize));
}
public long getPlanCountByHotCoilIdLike(String hotCoilId) {
List<Map<String, Object>> rows = asRowList(client.queryPlanCountByHotCoilIdLike(hotCoilId));
if (rows.isEmpty()) return 0L;
Object total = rows.get(0).get("total");
Number n = asNumber(total);
return n == null ? 0L : n.longValue();
}
/**
* 批量获取 L2 计划维度数据(来回料厚/宽、钢种),一次 Oracle 调用覆盖整页。
* 返回 Map&lt;hotCoilId, firstRow&gt;,同一 HOT_COILID 只保留第一行。
*/
/**
* 批量查询出口卷上线/下线时间,返回 Map&lt;excoilId, {START_DATE, END_DATE}&gt;
*/
public Map<String, Map<String, Object>> getExcoilTimesByCoilIds(List<String> coilIds) {
if (coilIds == null || coilIds.isEmpty()) return Collections.emptyMap();
List<Map<String, Object>> rows = asRowList(client.queryExcoilTimesByCoilIds(coilIds));
Map<String, Map<String, Object>> result = new LinkedHashMap<>();
for (Map<String, Object> row : rows) {
Object id = row.getOrDefault("EXCOILID", row.get("excoilid"));
if (id != null) result.putIfAbsent(id.toString().trim(), row);
}
return result;
}
public Map<String, Map<String, Object>> getPlanDimsByHotCoilIds(List<String> hotCoilIds) {
if (hotCoilIds == null || hotCoilIds.isEmpty()) {
return Collections.emptyMap();
}
List<Map<String, Object>> rows = asRowList(client.queryPlanDimsByHotCoilIds(hotCoilIds));
Map<String, Map<String, Object>> result = new LinkedHashMap<>();
for (Map<String, Object> row : rows) {
Object hcId = row.getOrDefault("HOT_COILID", row.get("hot_coilid"));
if (hcId != null) {
result.putIfAbsent(hcId.toString().trim(), row);
}
}
return result;
}
/**
* 计划详情:按成品卷号查询单条计划。
*/

View File

@@ -0,0 +1,502 @@
package com.klp.wms;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.klp.common.core.controller.BaseController;
import com.klp.common.core.domain.R;
import com.klp.domain.WmsCoilPendingAction;
import com.klp.domain.WmsProductionLine;
import com.klp.domain.bo.WmsMaterialCoilBo;
import com.klp.domain.vo.WmsCoilPendingActionVo;
import com.klp.domain.vo.WmsMaterialCoilVo;
import com.klp.domain.vo.WmsProcessSpecVersionVo;
import com.klp.framework.service.SqlServerApiBusinessService;
import com.klp.mapper.WmsCoilPendingActionMapper;
import com.klp.mapper.WmsProductionLineMapper;
import com.klp.service.IWmsMaterialCoilService;
import com.klp.service.IWmsProcessSpecVersionService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
/**
* 规程同步接口(跨模块:同时依赖 klp-admin 的 L2 服务和 klp-wms 的规程/钢卷服务)
*/
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/wms/specSync")
public class WmsSpecSyncController extends BaseController {
private final SqlServerApiBusinessService sqlServerApiBusinessService;
private final IWmsProcessSpecVersionService specVersionService;
private final IWmsMaterialCoilService materialCoilService;
private final WmsProductionLineMapper productionLineMapper;
private final WmsCoilPendingActionMapper pendingActionMapper;
/**
* 根据热卷号匹配最佳规程版本(供 typing.vue 在 L2 填入后调用)
*/
@GetMapping("/matchBest")
public R<WmsProcessSpecVersionVo> matchBest(@RequestParam String hotCoilId,
@RequestParam(required = false) Long lineId) {
SqlServerApiBusinessService.PlanDetailView plan =
sqlServerApiBusinessService.getPlanByHotCoilId(hotCoilId);
if (plan == null || plan.getFirstRow().isEmpty()) {
return R.ok(null);
}
Map<String, Object> row = plan.getFirstRow();
BigDecimal entryThick = toBigDecimal(row, "ENTRY_THICK", "entry_thick");
BigDecimal exitThick = toBigDecimal(row, "EXIT_THICK", "exit_thick");
BigDecimal entryWidth = toBigDecimal(row, "ENTRY_WIDTH", "entry_width");
BigDecimal exitWidth = toBigDecimal(row, "EXIT_WIDTH", "exit_width");
String grade = toStr(row, "GRADE", "grade");
WmsProcessSpecVersionVo best = specVersionService.matchBestVersion(
entryThick, exitThick, entryWidth, exitWidth, grade, lineId);
return R.ok(best);
}
/**
* 规程同步分页列表。
* 以 L3WMS MySQL为主维度只展示在 wms_coil_pending_action.processed_coil_ids
* 中出现过的钢卷(即生产后处理过的),再按 enter_coil_no = HOT_COILID 从 L2Oracle
* 批量富化计划维度(入口/出口厚宽、钢种等)及上线/下线时间。
*/
@GetMapping("/pageList")
public R<Map<String, Object>> pageList(
@RequestParam(defaultValue = "1") int pageNum,
@RequestParam(defaultValue = "40") int pageSize,
@RequestParam(required = false) String enterCoilNo,
@RequestParam(required = false) String currentCoilNo,
@RequestParam(required = false) String material,
@RequestParam(required = false) String qualityStatus,
@RequestParam(required = false) String specCode,
@RequestParam(required = false) String syncStatus,
@RequestParam(required = false) Long lineId) {
// ── 1. 预加载规程版本 ──────────────────────────────────────────────────
// 活跃版本:仅用于推荐候选匹配
List<WmsProcessSpecVersionVo> allActive = specVersionService.queryActiveVersionsEnriched(null);
List<WmsProcessSpecVersionVo> globalCandidates = lineId != null
? allActive.stream().filter(v -> lineId.equals(v.getLineId())).collect(Collectors.toList())
: allActive;
// 全部版本(含历史版本):用于展示已绑定规程名称,避免旧版本查不到
Map<Long, WmsProcessSpecVersionVo> allVersionById = specVersionService.queryAllVersionsEnriched().stream()
.collect(Collectors.toMap(WmsProcessSpecVersionVo::getVersionId, v -> v, (a, b) -> a));
// specCode 过滤 → specId 集合(后置内存过滤,从全部版本里搜)
final Set<Long> filterSpecIds;
if (StringUtils.hasText(specCode)) {
final String kw = specCode.trim().toLowerCase();
filterSpecIds = allVersionById.values().stream()
.filter(v -> (v.getSpecCode() != null && v.getSpecCode().toLowerCase().contains(kw))
|| (v.getSpecName() != null && v.getSpecName().toLowerCase().contains(kw)))
.map(WmsProcessSpecVersionVo::getSpecId)
.collect(Collectors.toSet());
} else {
filterSpecIds = null;
}
// ── 2. 预加载产线表 → actionType(int) → Set<lineId> ───────────────────
// 有效产线wms_production_line.action_type IS NOT NULL即配置了操作类型的产线
List<WmsProductionLine> allLines = productionLineMapper.selectList(
Wrappers.<WmsProductionLine>lambdaQuery()
.isNotNull(WmsProductionLine::getActionType));
Map<Integer, Set<Long>> actionTypeToLineIds = new HashMap<>();
for (WmsProductionLine pl : allLines) {
if (!StringUtils.hasText(pl.getActionType())) continue;
for (String part : pl.getActionType().split(",")) {
try {
int at = Integer.parseInt(part.trim());
actionTypeToLineIds.computeIfAbsent(at, k -> new HashSet<>()).add(pl.getLineId());
} catch (NumberFormatException ignore) { /* 跳过非数字 */ }
}
}
// ── 3. 展开已录入action_status=2待操作的 processed_coil_ids → 输出钢卷 ID ──
Set<Integer> validActionTypes = actionTypeToLineIds.keySet();
Set<Long> typedCoilIds = new LinkedHashSet<>();
Map<Long, Integer> allCoilIdToActionType = new HashMap<>();
if (!validActionTypes.isEmpty()) {
List<WmsCoilPendingAction> allPaList =
pendingActionMapper.selectAllProcessedCoilIdsAndActionStatus();
for (WmsCoilPendingAction pa : allPaList) {
if (pa.getActionType() == null || !validActionTypes.contains(pa.getActionType())) continue;
if (!StringUtils.hasText(pa.getProcessedCoilIds())) continue;
for (String idStr : pa.getProcessedCoilIds().split(",")) {
try {
long pid = Long.parseLong(idStr.trim());
typedCoilIds.add(pid);
allCoilIdToActionType.putIfAbsent(pid, pa.getActionType());
} catch (NumberFormatException ignore) { /* 跳过非数字 */ }
}
}
}
final Map<Long, Integer> coilIdToActionType = allCoilIdToActionType;
// ── 4. 计数分页(仅已录入钢卷,按规程绑定状态过滤)────────────────────────
final String effectiveSyncStatus;
if ("synced".equals(syncStatus)) effectiveSyncStatus = "synced";
else if ("unsynced".equals(syncStatus)) effectiveSyncStatus = "unsynced";
else effectiveSyncStatus = null;
if (typedCoilIds.isEmpty()) {
Map<String, Object> emptyResult = new LinkedHashMap<>();
emptyResult.put("rows", Collections.emptyList());
emptyResult.put("total", 0L);
return R.ok(emptyResult);
}
// ── 4a. 整体汇总统计(忽略 syncStatus 过滤,体现全量分布) ─────────────────
Map<String, Long> overallStats = materialCoilService.getOverallSyncStats(
typedCoilIds, enterCoilNo, currentCoilNo, material, qualityStatus, filterSpecIds);
int offset = (pageNum - 1) * pageSize;
long total = materialCoilService.countByProcessedCoilIds(
typedCoilIds, enterCoilNo, currentCoilNo, material, qualityStatus,
effectiveSyncStatus, filterSpecIds);
List<WmsMaterialCoilVo> l3Coils = materialCoilService.queryByProcessedCoilIds(
typedCoilIds, enterCoilNo, currentCoilNo, material, qualityStatus,
effectiveSyncStatus, filterSpecIds, offset, pageSize);
if (l3Coils.isEmpty()) {
Map<String, Object> emptyResult = new LinkedHashMap<>();
emptyResult.put("rows", Collections.emptyList());
emptyResult.put("total", total);
emptyResult.put("overallStats", overallStats);
return R.ok(emptyResult);
}
// ── 5. L2 富化:批量按 enter_coil_no= HOT_COILID查询计划维度 ──────────
List<String> enterCoilNos = new ArrayList<>();
Set<String> seenNos = new HashSet<>();
for (WmsMaterialCoilVo c : l3Coils) {
String primary = primaryEnterCoilNo(c.getEnterCoilNo());
if (primary != null && seenNos.add(primary)) enterCoilNos.add(primary);
}
Map<String, Map<String, Object>> l2DimMap =
sqlServerApiBusinessService.getPlanDimsByHotCoilIds(enterCoilNos);
// ── 6. 上线/下线时间PLTCM_PDO_EXCOIL.EXCOILID = PLTCM_PDI_PLAN.COILID ──
List<String> l2CoilIds = new ArrayList<>();
for (Map<String, Object> dims : l2DimMap.values()) {
String cid = toStr(dims, "COILID", "coilid");
if (StringUtils.hasText(cid)) l2CoilIds.add(cid);
}
Map<String, Map<String, Object>> excoilTimeMap =
sqlServerApiBusinessService.getExcoilTimesByCoilIds(l2CoilIds);
// ── 7. 组装行 ──────────────────────────────────────────────────────────────
List<Map<String, Object>> rows = new ArrayList<>();
for (WmsMaterialCoilVo coil : l3Coils) {
Map<String, Object> row = new LinkedHashMap<>();
String primaryNo = primaryEnterCoilNo(coil.getEnterCoilNo());
// ── L3 基础字段 ──
row.put("wmsCoilId", coil.getCoilId());
row.put("enterCoilNo", primaryNo);
row.put("currentCoilNo", coil.getCurrentCoilNo());
row.put("supplierCoilNo", coil.getSupplierCoilNo());
row.put("netWeight", coil.getNetWeight());
row.put("actualThickness", coil.getActualThickness());
row.put("actualWidth", coil.getActualWidth());
row.put("qualityStatus", coil.getQualityStatus());
row.put("specification", coil.getSpecification());
row.put("material", coil.getMaterial());
// 是否已流转data_type=0
boolean movedOn = Integer.valueOf(0).equals(coil.getDataType());
row.put("movedOn", movedOn);
row.put("dataType", coil.getDataType());
// ── L2 数据有则富化,无则留空(不跳过,避免破坏分页偏移)──
Map<String, Object> l2row = primaryNo != null ? l2DimMap.get(primaryNo) : null;
BigDecimal entryThick = null, exitThick = null, entryWidth = null, exitWidth = null;
String grade = null, l2CoilId = null;
if (l2row != null) {
entryThick = toBigDecimal(l2row, "ENTRY_THICK", "entry_thick");
exitThick = toBigDecimal(l2row, "EXIT_THICK", "exit_thick");
entryWidth = toBigDecimal(l2row, "ENTRY_WIDTH", "entry_width");
exitWidth = toBigDecimal(l2row, "EXIT_WIDTH", "exit_width");
grade = toStr(l2row, "GRADE", "grade");
l2CoilId = toStr(l2row, "COILID", "coilid");
row.put("entryThick", entryThick);
row.put("exitThick", exitThick);
row.put("entryWidth", entryWidth);
row.put("exitWidth", exitWidth);
row.put("grade", grade);
row.put("processCode", toStr(l2row, "PROCESS_CODE", "process_code"));
row.put("l2CoilId", l2CoilId);
row.put("l2Found", true);
}
// 上线/下线时间
Map<String, Object> excoilTimes = StringUtils.hasText(l2CoilId)
? excoilTimeMap.get(l2CoilId) : null;
if (excoilTimes != null) {
row.put("onlineTime", excoilTimes.getOrDefault("START_DATE", excoilTimes.get("start_date")));
row.put("offlineTime", excoilTimes.getOrDefault("END_DATE", excoilTimes.get("end_date")));
}
// ── 规程绑定状态 ──
if (coil.getVersionId() != null) {
WmsProcessSpecVersionVo bound = allVersionById.get(coil.getVersionId());
if (bound != null) {
row.put("specCode", bound.getSpecCode());
row.put("specName", bound.getSpecName());
row.put("versionCode", bound.getVersionCode());
putMatchConds(row, bound);
}
row.put("syncStatus", "synced");
} else {
row.put("syncStatus", "unsynced");
// 按产线限定候选规程lineId=null 的规程不限定产线,始终纳入)
Integer pendingAt = coilIdToActionType.get(coil.getCoilId());
Set<Long> allowedLineIds = pendingAt != null
? actionTypeToLineIds.getOrDefault(pendingAt, Collections.emptySet())
: null;
// 优先用同产线规程;若过滤后为空则兜底到全量活跃版本
List<WmsProcessSpecVersionVo> candidates;
if (allowedLineIds == null || allowedLineIds.isEmpty()) {
candidates = globalCandidates;
} else {
candidates = globalCandidates.stream()
.filter(v -> v.getLineId() == null || allowedLineIds.contains(v.getLineId()))
.collect(Collectors.toList());
if (candidates.isEmpty()) {
candidates = globalCandidates; // 兜底:产线无匹配规程时展示全部
}
}
WmsProcessSpecVersionVo best = null;
int bestScore = -1;
for (WmsProcessSpecVersionVo v : candidates) {
int s = scoreVersion(v, entryThick, exitThick, entryWidth, exitWidth, grade);
if (s > bestScore) { bestScore = s; best = v; }
}
// bestScore >= 0只要存在候选规程就展示推荐0 = 无维度条件匹配但仍有版本可绑)
if (best != null && bestScore >= 0) {
row.put("candidateSpecCode", best.getSpecCode());
row.put("candidateSpecName", best.getSpecName());
row.put("candidateVersionCode", best.getVersionCode());
row.put("candidateVersionId", best.getVersionId());
row.put("candidateSpecId", best.getSpecId());
putMatchConds(row, best);
}
}
rows.add(row);
}
Map<String, Object> result = new LinkedHashMap<>();
result.put("rows", rows);
result.put("total", total);
result.put("overallStats", overallStats);
return R.ok(result);
}
// ── 私有工具方法 ─────────────────────────────────────────────────────────────
/**
* enter_coil_no 字段可能存储了多个逗号分隔的热卷号(如 "26032550,26032550")。
* 取第一个非空值作为主键,用于展示和 L2 关联查询。
*/
private String primaryEnterCoilNo(String raw) {
if (!StringUtils.hasText(raw)) return null;
String first = raw.split(",")[0].trim();
return first.isEmpty() ? null : first;
}
private int scoreVersion(WmsProcessSpecVersionVo v,
BigDecimal entryThick, BigDecimal exitThick,
BigDecimal entryWidth, BigDecimal exitWidth, String grade) {
int score = 0;
if (entryThick != null && v.getMatchEntryThickMin() != null && v.getMatchEntryThickMax() != null
&& entryThick.compareTo(v.getMatchEntryThickMin()) >= 0
&& entryThick.compareTo(v.getMatchEntryThickMax()) <= 0) score++;
if (exitThick != null && v.getMatchExitThickMin() != null && v.getMatchExitThickMax() != null
&& exitThick.compareTo(v.getMatchExitThickMin()) >= 0
&& exitThick.compareTo(v.getMatchExitThickMax()) <= 0) score++;
if (entryWidth != null && v.getMatchEntryWidthMin() != null && v.getMatchEntryWidthMax() != null
&& entryWidth.compareTo(v.getMatchEntryWidthMin()) >= 0
&& entryWidth.compareTo(v.getMatchEntryWidthMax()) <= 0) score++;
if (exitWidth != null && v.getMatchExitWidthMin() != null && v.getMatchExitWidthMax() != null
&& exitWidth.compareTo(v.getMatchExitWidthMin()) >= 0
&& exitWidth.compareTo(v.getMatchExitWidthMax()) <= 0) score++;
if (StringUtils.hasText(grade) && StringUtils.hasText(v.getMatchSteelGrade())
&& grade.toLowerCase().contains(v.getMatchSteelGrade().toLowerCase())) score++;
return score;
}
private void putMatchConds(Map<String, Object> row, WmsProcessSpecVersionVo v) {
row.put("matchEntryThickMin", v.getMatchEntryThickMin());
row.put("matchEntryThickMax", v.getMatchEntryThickMax());
row.put("matchExitThickMin", v.getMatchExitThickMin());
row.put("matchExitThickMax", v.getMatchExitThickMax());
row.put("matchEntryWidthMin", v.getMatchEntryWidthMin());
row.put("matchEntryWidthMax", v.getMatchEntryWidthMax());
row.put("matchExitWidthMin", v.getMatchExitWidthMin());
row.put("matchExitWidthMax", v.getMatchExitWidthMax());
row.put("matchSteelGrade", v.getMatchSteelGrade());
}
/**
* 待录入核查分页列表。
* 数据源wms_production_line 有效产线对应的待操作记录中 action_status != 2尚未完成 typing的条目。
* 额外按入场卷号从 L2Oracle查询计划/实绩维度,供核查人员判断二级是否已有实绩。
*/
@GetMapping("/untypedPageList")
public R<Map<String, Object>> untypedPageList(
@RequestParam(defaultValue = "1") int pageNum,
@RequestParam(defaultValue = "40") int pageSize,
@RequestParam(required = false) String enterCoilNo,
@RequestParam(required = false) String currentCoilNo,
@RequestParam(required = false) String operatorName) {
// ── 1. 有效产线 actionType ──────────────────────────────────────────────
List<WmsProductionLine> lines = productionLineMapper.selectList(
Wrappers.<WmsProductionLine>lambdaQuery().isNotNull(WmsProductionLine::getActionType));
Set<Integer> validAts = new HashSet<>();
for (WmsProductionLine pl : lines) {
if (!StringUtils.hasText(pl.getActionType())) continue;
for (String part : pl.getActionType().split(",")) {
try { validAts.add(Integer.parseInt(part.trim())); } catch (NumberFormatException ignore) {}
}
}
if (validAts.isEmpty()) {
Map<String, Object> empty = new LinkedHashMap<>();
empty.put("rows", Collections.emptyList());
empty.put("total", 0L);
return R.ok(empty);
}
// ── 2. 分页查询未录入待操作 ────────────────────────────────────────────────
// 条件1action_status IN (0,1) —— 0=待处理/1=进行中2=已完成/3=已取消均排除
// 条件2processed_coil_ids 为空 —— 只有未写入产出卷 ID 的记录才是尚未录入的
QueryWrapper<WmsCoilPendingAction> qw = new QueryWrapper<>();
qw.eq("wcpa.del_flag", 0);
qw.in("wcpa.action_type", validAts);
qw.in("wcpa.action_status", java.util.Arrays.asList(0, 1));
qw.and(w -> w.isNull("wcpa.processed_coil_ids")
.or().eq("wcpa.processed_coil_ids", ""));
if (StringUtils.hasText(enterCoilNo)) qw.like("wmc.enter_coil_no", enterCoilNo);
if (StringUtils.hasText(currentCoilNo)) qw.like("wcpa.current_coil_no", currentCoilNo);
if (StringUtils.hasText(operatorName)) qw.like("wcpa.operator_name", operatorName);
qw.orderByDesc("wcpa.scan_time");
Page<WmsCoilPendingActionVo> voPage =
pendingActionMapper.selectVoPagePlus(Page.of(pageNum, pageSize), qw);
List<WmsCoilPendingActionVo> paList = voPage.getRecords();
if (paList.isEmpty()) {
Map<String, Object> empty = new LinkedHashMap<>();
empty.put("rows", Collections.emptyList());
empty.put("total", voPage.getTotal());
return R.ok(empty);
}
// ── 3. L2 富化 ─────────────────────────────────────────────────────────
List<String> nos = new ArrayList<>();
Set<String> seen = new HashSet<>();
for (WmsCoilPendingActionVo pa : paList) {
String primary = primaryEnterCoilNo(pa.getEnterCoilNo());
if (primary != null && seen.add(primary)) nos.add(primary);
}
Map<String, Map<String, Object>> l2DimMap = nos.isEmpty()
? Collections.emptyMap()
: sqlServerApiBusinessService.getPlanDimsByHotCoilIds(nos);
// ── 4. 组装行 ──────────────────────────────────────────────────────────
List<Map<String, Object>> rows = new ArrayList<>();
for (WmsCoilPendingActionVo pa : paList) {
String primaryNo = primaryEnterCoilNo(pa.getEnterCoilNo());
Map<String, Object> l2row = primaryNo != null ? l2DimMap.get(primaryNo) : null;
Map<String, Object> row = new LinkedHashMap<>();
row.put("actionId", pa.getActionId());
row.put("coilId", pa.getCoilId());
row.put("actionType", pa.getActionType());
row.put("actionStatus", pa.getActionStatus());
row.put("scanTime", pa.getScanTime());
row.put("createTime", pa.getCreateTime());
row.put("operatorName", pa.getOperatorName());
row.put("warehouseName", pa.getWarehouseName());
row.put("enterCoilNo", primaryNo);
row.put("currentCoilNo", pa.getCurrentCoilNo());
row.put("supplierCoilNo", pa.getSupplierCoilNo());
row.put("material", pa.getMaterial());
row.put("specification", pa.getSpecification());
row.put("itemName", pa.getItemName());
if (l2row != null) {
row.put("entryThick", toBigDecimal(l2row, "ENTRY_THICK", "entry_thick"));
row.put("exitThick", toBigDecimal(l2row, "EXIT_THICK", "exit_thick"));
row.put("entryWidth", toBigDecimal(l2row, "ENTRY_WIDTH", "entry_width"));
row.put("exitWidth", toBigDecimal(l2row, "EXIT_WIDTH", "exit_width"));
row.put("grade", toStr(l2row, "GRADE", "grade"));
row.put("processCode", toStr(l2row, "PROCESS_CODE", "process_code"));
row.put("l2Found", true);
} else {
row.put("l2Found", false);
}
rows.add(row);
}
Map<String, Object> result = new LinkedHashMap<>();
result.put("rows", rows);
result.put("total", voPage.getTotal());
return R.ok(result);
}
/**
* 批量同步规程绑定。
* 请求体:{ "bindings": [{ "coilId": 123, "specId": 456, "versionId": 789 }, ...] }
* 前端已完成匹配计算,直接写入指定的 specId/versionId不重新跑匹配算法。
*/
@PostMapping("/syncSpec")
@SuppressWarnings("unchecked")
public R<Void> syncSpec(@RequestBody Map<String, Object> body) {
List<Map<String, Object>> bindings = (List<Map<String, Object>>) body.get("bindings");
if (bindings == null || bindings.isEmpty()) {
return R.fail("bindings 不能为空");
}
for (Map<String, Object> b : bindings) {
try {
Long coilId = toLong(b, "coilId");
Long specId = toLong(b, "specId");
Long versionId = toLong(b, "versionId");
if (coilId == 0L || specId == 0L || versionId == 0L) continue;
WmsMaterialCoilBo bo = new WmsMaterialCoilBo();
bo.setCoilId(coilId);
bo.setSpecId(specId);
bo.setVersionId(versionId);
materialCoilService.updateSimple(bo);
} catch (Exception e) {
log.warn("规程同步失败 binding={}", b, e);
}
}
return R.ok();
}
private BigDecimal toBigDecimal(Map<String, Object> row, String upperKey, String lowerKey) {
Object val = row.getOrDefault(upperKey, row.get(lowerKey));
if (val == null) return null;
try { return new BigDecimal(val.toString()); } catch (NumberFormatException e) { return null; }
}
private String toStr(Map<String, Object> row, String upperKey, String lowerKey) {
Object val = row.getOrDefault(upperKey, row.get(lowerKey));
return val == null ? null : val.toString().trim();
}
private long toLong(Map<String, Object> row, String key) {
if (row == null) return 0L;
Object v = row.get(key);
if (v == null) return 0L;
try { return Long.parseLong(v.toString()); } catch (NumberFormatException e) { return 0L; }
}
}