备料计划

This commit is contained in:
jiashuai 2025-09-30 18:05:20 +08:00
parent 6388114190
commit 556849701b
3 changed files with 668 additions and 0 deletions

View File

@ -1,6 +1,7 @@
package nc.bs.mmpac.pickm.bp;
import nc.bs.mmpac.pickm.bp.rule.AfterApproveRuleHighpressureIms;
import nc.bs.mmpac.pickm.bp.rule.AfterApproveRuleHighpressureMes;
import nc.bs.mmpac.pickm.bp.rule.AfterApproveRuleSyncRZWMS;
import nc.bs.mmpac.pickm.plugin.PickmPluginPoint;
@ -28,6 +29,8 @@ public class PickmApproveBP {
processer.addAfterRule(pickmstatusFilterRule);
//备料计划审批后推送高压MES
processer.addAfterRule(new AfterApproveRuleHighpressureMes());
//备料计划审批后推送高压IMS
processer.addAfterRule(new AfterApproveRuleHighpressureIms());
}
private void addBeforeRule(CompareAroundProcesser<AggPickmVO> processer) {

View File

@ -2,6 +2,7 @@ package nc.bs.mmpac.pickm.bp;
import java.util.Map;
import nc.bs.mmpac.pickm.bp.rule.AfterApproveRuleHighpressureIms;
import nc.bs.mmpac.pickm.bp.rule.AfterupdateSyncEpicMesRule;
import nc.bs.mmpac.pickm.plugin.PickmPluginPoint;
import nc.bs.mmpac.pickm.rule.PickmCheckItemMaterialPermissionRule;
@ -226,6 +227,9 @@ public class PickmUpdateForMOBP {
aroundProcesser.addAfterRule(new AfterupdateSyncEpicMesRule());
//备料计划重算后推送高压IMS
aroundProcesser.addAfterRule(new AfterApproveRuleHighpressureIms());
}
}

View File

@ -0,0 +1,661 @@
package nc.bs.mmpac.pickm.bp.rule;
import com.alibaba.fastjson.JSONObject;
import nc.bs.dao.BaseDAO;
import nc.bs.dao.DAOException;
import nc.bs.logging.Log;
import nc.bs.trade.business.HYPubBO;
import nc.bs.uapbd.util.MyHelper;
import nc.impl.pubapp.pattern.rule.IRule;
import nc.jdbc.framework.JdbcSession;
import nc.jdbc.framework.PersistenceManager;
import nc.jdbc.framework.exception.DbException;
import nc.jdbc.framework.processor.MapProcessor;
import nc.uif.pub.exception.UifException;
import nc.vo.bd.material.MaterialVO;
import nc.vo.mmpac.pickm.entity.AggPickmVO;
import nc.vo.mmpac.pickm.entity.PickmHeadVO;
import nc.vo.mmpac.pickm.entity.PickmItemVO;
import nc.vo.org.OrgVO;
import nc.vo.pub.BusinessException;
import nc.vo.pub.lang.UFDate;
import nc.vo.pub.lang.UFDateTime;
import nc.vo.pubapp.pattern.exception.ExceptionUtils;
import nc.vo.scmpub.util.ArrayUtil;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* 备料计划审批后同步高压MES系统规则类
* 功能主表(BIPPlanMainTab)子表(BIPPlanDetailTab)的新增/更新同步
*/
public class AfterApproveRuleHighpressureIms implements IRule<AggPickmVO> {
// 日志配置固定
private static final String LOG_INFO_NAME = "gyimslog";
private static final Log logger = Log.getInstance(LOG_INFO_NAME);
// 配置参数同步目标组织等
private Map<String, String> configParams;
// -------------------------- 线程安全的日期格式化工具解决SimpleDateFormat线程安全问题 --------------------------
private static final ThreadLocal<SimpleDateFormat> DATETIME_FORMATTER = ThreadLocal.withInitial(
() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
);
private static final ThreadLocal<SimpleDateFormat> DATE_FORMATTER = ThreadLocal.withInitial(
() -> new SimpleDateFormat("yyyy-MM-dd")
);
@Override
public void process(AggPickmVO[] aggPickmVOS) {
// 1. 空值快速返回避免后续NPE
if (ArrayUtil.isEmpty(aggPickmVOS)) {
logger.info("待同步的备料计划数组为空,直接返回");
return;
}
// 2. 加载配置参数失败直接抛业务异常阻断流程
try {
configParams = MyHelper.getConfigParams("gy-config", null);
if (configParams == null || configParams.isEmpty()) {
throw new BusinessException("同步高压MES系统失败未加载到\"gy-config\"配置参数");
}
} catch (Exception e) {
logger.error("加载同步配置参数失败", e);
ExceptionUtils.wrappException(new BusinessException("加载配置参数异常:" + e.getMessage(), e));
return;
}
// 3. 构建同步数据并执行同步统一异常捕获
try {
List<Map<String, Object>> mainDataList = new ArrayList<>(); // 主表数据
List<Map<String, Object>> detailDataList = new ArrayList<>(); // 子表数据
buildSyncData(aggPickmVOS, mainDataList, detailDataList);
// 4. 若有数据则执行同步取第一个主表的cpickmid作为关键标识若主表为空则不同步
String targetCpickmid = mainDataList.isEmpty() ? null : (String) mainDataList.get(0).get("cpickmid");
pushIms(mainDataList, detailDataList, targetCpickmid);
} catch (BusinessException e) {
// 业务异常直接包装抛出NC框架会处理
logger.error("备料计划同步高压MES业务异常关键备料单号" + getCpickmidFromAgg(aggPickmVOS) + "");
ExceptionUtils.wrappException(e);
} catch (Exception e) {
// 未知异常转为业务异常避免上游捕获到RuntimeException
String errorMsg = "备料计划同步高压MES系统未知异常关键备料单号" + getCpickmidFromAgg(aggPickmVOS);
logger.error(errorMsg, e);
ExceptionUtils.wrappException(new BusinessException(errorMsg, e));
}
}
/**
* 构建同步数据主表+子表
* 修复原问题数据覆盖 改为add元素增强空值校验统一JSON转Map逻辑
*/
private void buildSyncData(AggPickmVO[] aggPickmVOS, List<Map<String, Object>> mainDataList, List<Map<String, Object>> detailDataList) throws BusinessException {
for (AggPickmVO aggVO : aggPickmVOS) {
// 1. 校验AggVO非空及获取表头/表体
if (aggVO == null) {
logger.warn("跳过空的备料计划AggVO");
continue;
}
PickmHeadVO headVO = aggVO.getParentVO();
PickmItemVO[] itemVOs = (PickmItemVO[]) aggVO.getChildrenVO();
if (headVO == null) {
logger.warn("备料计划AggVO的表头为空跳过该记录");
continue;
}
// 2. 组织过滤仅同步目标组织原逻辑保留并优化日志
String pkOrg = headVO.getPk_org();
String orgCode = MyHelper.transferField(OrgVO.getDefaultTableName(), OrgVO.CODE, OrgVO.PK_ORG, pkOrg);
if (checkIfSkipOrg(orgCode,configParams)) {
logger.info("备料计划组织编码"+ orgCode +"非目标同步组织,跳过同步,备料单号:"+headVO.getVbillcode()+"");
continue;
}
// 3. 构建主表数据JSON Map添加到列表
JSONObject mainJson = buildMainJsonObject(headVO);
List<Map<String, Object>> mainMapList = JsonUtil.jsonObjectToListMap(mainJson);
if (!mainMapList.isEmpty()) {
mainDataList.add(mainMapList.get(0)); // 单个JSON转List后取第一个元素
}
// 4. 构建子表数据循环表体添加到列表
if (itemVOs != null && itemVOs.length > 0) {
for (PickmItemVO itemVO : itemVOs) {
if (itemVO == null) {
logger.warn("备料计划" + headVO.getVbillcode() + "存在空表体,跳过该表体");
continue;
}
JSONObject detailJson = buildDetailJsonObject(itemVO, headVO.getVbillcode());
List<Map<String, Object>> detailMapList = JsonUtil.jsonObjectToListMap(detailJson);
if (!detailMapList.isEmpty()) {
detailDataList.add(detailMapList.get(0));
}
}
} else {
logger.info("备料计划" + headVO.getVbillcode() + "无表体数据,仅同步主表");
}
}
}
private void pushIms(List<Map<String, Object>> mainDataList, List<Map<String, Object>> detailDataList, String targetCpickmid) throws BusinessException {
// 1. 关键参数校验
if (targetCpickmid == null || targetCpickmid.trim().isEmpty()) {
logger.warn("同步目标备料计划主键cpickmid为空终止同步");
return;
}
if (mainDataList.isEmpty()) {
logger.warn("备料计划主表数据为空终止同步cpickmid" + targetCpickmid + "");
return;
}
// 2. 直接通过静态工厂方法获取 PersistenceManager
PersistenceManager pm = null;
JdbcSession jdbcSession = null;
Connection conn = null;
boolean isSuccess = false;
try {
// 直接调用 PersistenceManager 的静态方法 getInstance()
pm = PersistenceManager.getInstance("gyims");
pm.setAddTimeStamp(false);
// 2.1 获取 JdbcSession Connection
jdbcSession = pm.getJdbcSession();
jdbcSession.setSQLTranslator(true); // 保持 SQL 转换
conn = jdbcSession.getConnection();
conn.setAutoCommit(false);
logger.info("直接获取PersistenceManager手动事务开启cpickmid" + targetCpickmid + "");
// 3. 判断新增/更新 (checkMainExists 方法逻辑不变)
boolean isUpdate = checkMainExists(pm, targetCpickmid);
// 4. 执行主表+子表操作 (传入共享的 pm)
if (isUpdate) {
logger.info("事务内执行更新cpickmid" + targetCpickmid + "");
updatePlanMain(mainDataList, pm, targetCpickmid);
if (!detailDataList.isEmpty()) {
updatePlanDetail(detailDataList, pm, targetCpickmid);
}
} else {
logger.info("事务内执行新增cpickmid" + targetCpickmid + "");
insertPlanMain(mainDataList, pm, targetCpickmid);
if (!detailDataList.isEmpty()) {
insertPlanDetail(detailDataList, pm, targetCpickmid);
}
}
isSuccess = true;
logger.info("事务内操作全部成功准备提交cpickmid" + targetCpickmid + "");
} catch (DbException | SQLException e) { // 捕获 DbException SQLException
logger.error("事务执行失败触发回滚cpickmid" + targetCpickmid + "");
if (conn != null) {
try {
conn.rollback();
logger.info("事务回滚完成cpickmid" + targetCpickmid + "");
} catch (SQLException rollbackE) {
logger.error("事务回滚异常cpickmid" + targetCpickmid + "");
}
}
throw new BusinessException("备料计划同步事务失败:" + e.getMessage(), e);
} finally {
if (conn != null) {
try {
if (isSuccess) {
conn.commit();
logger.info("事务提交成功cpickmid" + targetCpickmid + "");
}
conn.setAutoCommit(true);
} catch (SQLException commitE) {
logger.error("事务提交异常cpickmid" + targetCpickmid + "");
}
}
// 5. 释放 PersistenceManager (至关重要)
if (pm != null) {
try {
pm.release(); // 必须调用 release() 将连接归还给连接池
logger.info("PersistenceManager 释放完成cpickmid" + targetCpickmid + "");
} catch (Exception releaseE) {
logger.error("PersistenceManager 释放异常", releaseE);
}
}
}
}
/**
* 校验主表是否存在使用共享的 PersistenceManager
*/
private boolean checkMainExists(PersistenceManager pm, String cpickmid) throws DAOException, DbException {
String safeCpickmid = cpickmid.replace("'", "''");
String sql = "SELECT cpickmid FROM BIPPlanMainTab WHERE cpickmid = '" + safeCpickmid + "'";
// 使用共享 pm JdbcSession 执行查询避免新建连接
Map<String, Object> existMain = (Map<String, Object>) pm.getJdbcSession().executeQuery(sql, new MapProcessor());
return existMain != null && !existMain.isEmpty();
}
// -------------------------- 主表/子表JSON构建拆分逻辑增强可读性 --------------------------
/**
* 构建备料计划主表JSON
*/
private JSONObject buildMainJsonObject(PickmHeadVO headVO) throws UifException {
JSONObject mainJson = new JSONObject();
mainJson.put("cpickmid", headVO.getCpickmid());// 主键ID
Object pk_org = new HYPubBO().findColValue("org_factory", "code", "nvl(dr,0) = 0 and pk_factory='" + headVO.getPk_org() + "' ");
mainJson.put("pk_org", pk_org);//组织_业务单元_工厂
mainJson.put("vbillcode", headVO.getVbillcode());// 备料计划单号
mainJson.put("fbillstatus", headVO.getFbillstatus());//备料状态
mainJson.put("vbusitypeid", headVO.getVbusitypeid());//备料类型
mainJson.put("vbusitype", headVO.getVbusitype());//备料类型编码
Object cmaterialvid = new HYPubBO().findColValue("bd_material", "code", "nvl(dr,0) = 0 and pk_material='" + headVO.getCmaterialvid() + "' ");
mainJson.put("cmaterialvid", cmaterialvid.toString());//产品编码
mainJson.put("nastnum", headVO.getNastnum() != null ? headVO.getNastnum().doubleValue() : null);//计划数量;
mainJson.put("cdeptid", headVO.getCdeptid());//用料部门最新版本
mainJson.put("cdeptvid", headVO.getCdeptid());//用料部门
mainJson.put("vsalebillcode", headVO.getVsalebillcode());//销售订单号
mainJson.put("ccustmaterialid", headVO.getCcustmaterialid());//客户物料码
mainJson.put("cemployeeid", headVO.getCemployeeid());//业务员
mainJson.put("vfirstmoid", headVO.getVfirstmoid());//源头生产订单
mainJson.put("vfirstmocode", headVO.getVfirstmocode());//源头生产订单号
mainJson.put("vfirstmorowid", headVO.getVfirstmorowid());//源头生产订单明细
mainJson.put("vfirstmorowcode", headVO.getVfirstmorowcode());//源头生产订单行号
mainJson.put("vsourcemoid", headVO.getVsourcemoid());//来源生产订单
mainJson.put("vsourcemocode", headVO.getVsourcemocode());//来源生产订单号
mainJson.put("vsourcemorowid", headVO.getVsourcemorowid());//来源生产订单明细
mainJson.put("vsourcemorowcode", headVO.getVsourcemorowcode());//来源生产订单行号
mainJson.put("cfirstbillid", headVO.getCfirstbillid());//源头单据
mainJson.put("vfirstbillcode", headVO.getVfirstbillcode());//源头单据号
mainJson.put("vfirstbilltype", headVO.getVfirstbilltype());//源头单据类型
mainJson.put("vfirsttrantypeid", headVO.getVfirsttrantypeid());//源头交易类型
mainJson.put("vfirsttrantype", headVO.getVfirsttrantype());//源头交易类型编码
mainJson.put("cfirstbillrowid", headVO.getCfirstbillrowid());//源头单据明细
mainJson.put("vfirstbillrowno", headVO.getVfirstbillrowno());//源头单据行号
mainJson.put("csourcebillid", headVO.getCsourcebillid());//来源单据
mainJson.put("vsourcebillcode", headVO.getVsourcebillcode());//来源单据号
mainJson.put("vsourcebilltype", headVO.getVsourcebilltype());//来源单据类型
mainJson.put("vsrctrantypeid", headVO.getVsrctrantypeid());//来源交易类型
mainJson.put("vsrctrantype", headVO.getVsrctrantype());//来源交易类型编码
mainJson.put("csourcebillrowid", headVO.getCsourcebillrowid());//来源单据明细
mainJson.put("vsourcebillrowno", headVO.getVsourcebillrowno());//来源单据行号
mainJson.put("vnote", headVO.getVnote());//备注
mainJson.put("status", headVO.getStatus());//同步状态
mainJson.put("billmaker", headVO.getBillmaker());
;//制单人
mainJson.put("dmakedate", headVO.getDmakedate()); // UFDate制单时间
mainJson.put("creator", headVO.getCreator());//创建人
mainJson.put("creationtime", headVO.getCreationtime()); // UFDateTime创建时间
return mainJson;
}
/**
* 构建备料计划子表JSON
*/
private JSONObject buildDetailJsonObject(PickmItemVO itemVO, String mainBillCode) throws UifException {
JSONObject detailJson = new JSONObject();
detailJson.put("cpickm_bid", itemVO.getCpickm_bid());// 备料计划明细
detailJson.put("cpickmid", itemVO.getCpickmid());// 备料计划单表头主键
Object pk_org = new HYPubBO().findColValue("org_factory", "code", "nvl(dr,0) = 0 and pk_factory='" + itemVO.getPk_org() + "' ");
detailJson.put("pk_org", pk_org); // 工厂最新版本
detailJson.put("vbillcode", mainBillCode); // 备料计划单表
detailJson.put("vrowno", itemVO.getVrowno());// 行号
detailJson.put("fitemtype", itemVO.getFitemtype());// 子项类型
detailJson.put("fitemsource", itemVO.getFitemsource());// 备料来源
Object cmaterialvid = new HYPubBO().findColValue("bd_material", "code", "nvl(dr,0) = 0 and pk_material='" + itemVO.getCbmaterialvid() + "' ");
detailJson.put("cbmaterialvid", cmaterialvid.toString());// 材料编码
Object cbunitid = new HYPubBO().findColValue("bd_measdoc", "code", "nvl(dr,0) = 0 and pk_measdoc='" + itemVO.getCbunitid() + "' ");
detailJson.put("cbunitid", cbunitid.toString());// 主单位
Object cbastunitid = new HYPubBO().findColValue("bd_measdoc", "code", "nvl(dr,0) = 0 and pk_measdoc='" + itemVO.getCbastunitid() + "' ");
detailJson.put("cbastunitid", cbastunitid.toString());// 单位
detailJson.put("vbchangerate", itemVO.getVbchangerate());// 换算率
detailJson.put("nquotastnum", itemVO.getNquotastnum() != null ? itemVO.getNquotastnum().doubleValue() : null);// 定额用量
detailJson.put("nplanoutastnum", itemVO.getNplanoutastnum() != null ? itemVO.getNplanoutastnum().doubleValue() : null); // 计划出库数量
detailJson.put("nplanoutnum", itemVO.getNplanoutnum() != null ? itemVO.getNplanoutnum().doubleValue() : null);// 计划出库主数量
detailJson.put("vbdef22", itemVO.getVbdef20()); // 是否备件
return detailJson;
}
// -------------------------- 新增/更新逻辑提取通用SQL值处理减少冗余 --------------------------
/**
* 主表新增使用共享的 PersistenceManager
*
* @param mainDataList 主表数据列表
* @param pm 共享的 PersistenceManager 实例
* @param cpickmid 主表业务主键
*/
private void insertPlanMain(List<Map<String, Object>> mainDataList, PersistenceManager pm, String cpickmid) throws DAOException, DbException, DbException {
// 1. 构建 SQL逻辑与原代码一致仅执行方式改变
String[] mainFields = {
"cpickmid", "pk_org", "vbillcode", "fbillstatus", "vbusitypeid", "vbusitype",
"cmaterialvid", "nastnum", "cdeptid", "cdeptvid", "vsalebillcode", "ccustmaterialid",
"cemployeeid", "vfirstmoid", "vfirstmocode", "vfirstmorowid", "vfirstmorowcode",
"vsourcemoid", "vsourcemocode", "vsourcemorowid", "vsourcemorowcode", "cfirstbillid",
"vfirstbillcode", "vfirstbilltype", "vfirsttrantypeid", "vfirsttrantype", "cfirstbillrowid",
"vfirstbillrowno", "csourcebillid", "vsourcebillcode", "vsourcebilltype", "vsrctrantypeid",
"vsrctrantype", "csourcebillrowid", "vsourcebillrowno", "vnote", "status",
"billmaker", "dmakedate", "creator", "creationtime"
};
String fieldStr = String.join(", ", mainFields);
StringBuilder valuesSb = new StringBuilder();
for (Map<String, Object> data : mainDataList) {
data.put("status", "C");
valuesSb.append("(");
for (int i = 0; i < mainFields.length; i++) {
Object value = data.get(mainFields[i]);
valuesSb.append(SqlValueUtil.processSqlValue(value));
if (i < mainFields.length - 1) {
valuesSb.append(", ");
}
}
valuesSb.append("), ");
}
// 2. 执行 SQL使用共享 pm JdbcSession避免新建连接
if (valuesSb.length() > 0) {
String valuesStr = valuesSb.substring(0, valuesSb.length() - 2);
String insertSql = "INSERT INTO BIPPlanMainTab (" + fieldStr + ") VALUES " + valuesStr;
// 关键用共享 pm JdbcSession 执行而非 BaseDAO executeUpdate
int rows = pm.getJdbcSession().executeUpdate(insertSql);
logger.info("主表新增成功cpickmid" + cpickmid + ",影响行数:" + rows + "");
}
}
/**
* 子表新增使用共享的 PersistenceManager
*
* @param detailDataList 子表数据列表
* @param pm 共享的 PersistenceManager 实例
* @param cpickmid 子表业务主键
*/
private void insertPlanDetail(List<Map<String, Object>> detailDataList, PersistenceManager pm, String cpickmid) throws DAOException, DbException {
String[] detailFields = {
"cpickm_bid", "cpickmid", "pk_org", "vbillcode", "vrowno", "fitemtype",
"fitemsource", "cbmaterialvid", "cbunitid", "cbastunitid", "vbchangerate", "nquotastnum",
"nplanoutastnum", "nplanoutnum", "vbdef22"
};
String fieldStr = String.join(", ", detailFields);
StringBuilder valuesSb = new StringBuilder();
for (Map<String, Object> data : detailDataList) {
valuesSb.append("(");
for (int i = 0; i < detailFields.length; i++) {
Object value = data.get(detailFields[i]);
valuesSb.append(SqlValueUtil.processSqlValue(value));
if (i < detailFields.length - 1) {
valuesSb.append(", ");
}
}
valuesSb.append("), ");
}
if (valuesSb.length() > 0) {
String valuesStr = valuesSb.substring(0, valuesSb.length() - 2);
String insertSql = "INSERT INTO BIPPlanDetailTab (" + fieldStr + ") VALUES " + valuesStr;
// 关键用共享 pm JdbcSession 执行
int rows = pm.getJdbcSession().executeUpdate(insertSql);
logger.info("子表新增成功子表cpickmid" + cpickmid + ",影响行数:" + rows + "");
}
}
/**
* 主表更新使用共享的 PersistenceManager
*
* @param mainDataList 主表数据列表
* @param pm 共享的 PersistenceManager 实例
* @param cpickmid 主表业务主键
*/
private void updatePlanMain(List<Map<String, Object>> mainDataList, PersistenceManager pm, String cpickmid) throws DAOException, DbException {
String[] mainFields = {
"pk_org", "vbillcode", "fbillstatus", "vbusitypeid", "vbusitype",
"cmaterialvid", "nastnum", "cdeptid", "cdeptvid", "vsalebillcode", "ccustmaterialid",
"cemployeeid", "vfirstmoid", "vfirstmocode", "vfirstmorowid", "vfirstmorowcode",
"vsourcemoid", "vsourcemocode", "vsourcemorowid", "vsourcemorowcode", "cfirstbillid",
"vfirstbillcode", "vfirstbilltype", "vfirsttrantypeid", "vfirsttrantype", "cfirstbillrowid",
"vfirstbillrowno", "csourcebillid", "vsourcebillcode", "vsourcebilltype", "vsrctrantypeid",
"vsrctrantype", "csourcebillrowid", "vsourcebillrowno", "vnote", "status",
"billmaker", "dmakedate", "creator", "creationtime"
};
Map<String, Object> mainData = mainDataList.get(0);
mainData.put("status", "U");
StringBuilder setSb = new StringBuilder();
for (String field : mainFields) {
Object value = mainData.get(field);
setSb.append(field).append(" = ").append(SqlValueUtil.processSqlValue(value)).append(", ");
}
if (setSb.length() > 0) {
String setStr = setSb.substring(0, setSb.length() - 2);
String safeCpickmid = cpickmid.replace("'", "''");
String updateSql = "UPDATE BIPPlanMainTab SET " + setStr + " WHERE cpickmid = '" + safeCpickmid + "'";
// 关键用共享 pm JdbcSession 执行
int rows = pm.getJdbcSession().executeUpdate(updateSql);
logger.info("主表更新成功cpickmid" + cpickmid + ",影响行数:" + rows + "");
}
}
/**
* 子表更新使用共享的 PersistenceManager
*
* @param detailDataList 子表数据列表
* @param pm 共享的 PersistenceManager 实例
* @param cpickmid 子表业务主键
*/
private void updatePlanDetail(List<Map<String, Object>> detailDataList, PersistenceManager pm, String cpickmid) throws BusinessException {
if (detailDataList.isEmpty()) {
logger.warn("备料计划" + cpickmid + "子表同步数据为空,跳过更新");
return;
}
String[] detailFields = {
"cpickmid", "pk_org", "vbillcode", "vrowno", "fitemtype",
"fitemsource", "cbmaterialvid", "cbunitid", "cbastunitid", "vbchangerate", "nquotastnum",
"nplanoutastnum", "nplanoutnum", "vbdef22"
};
// 循环更新每条子表数据
for (Map<String, Object> detailData : detailDataList) {
String cpickmBid = (String) detailData.get("cpickm_bid");
if (cpickmBid == null || cpickmBid.trim().isEmpty()) {
logger.warn("备料计划" + cpickmid + "子表主键cpickm_bid为空跳过该子表更新");
continue;
}
// 构建SET语句
StringBuilder setSb = new StringBuilder();
for (String field : detailFields) {
Object value = detailData.get(field);
setSb.append(field).append(" = ").append(SqlValueUtil.processSqlValue(value)).append(", ");
}
// 执行SQL
if (setSb.length() > 0) {
String setStr = setSb.substring(0, setSb.length() - 2);
String safeBid = cpickmBid.replace("'", "''");
String updateSql = "UPDATE BIPPlanDetailTab SET " + setStr + " WHERE cpickm_bid = '" + safeBid + "'";
// 关键使用共享的 PersistenceManager 执行 SQL
try {
int rows = pm.getJdbcSession().executeUpdate(updateSql);
logger.info("子表更新成功,子表主键:" + cpickmBid + ",影响行数:" + rows + "");
} catch (DbException e) {
throw new BusinessException("子表更新失败SQL: " + updateSql, e);
}
}
}
}
// -------------------------- 通用工具方法提取后复用减少冗余 --------------------------
/**
* 执行SQL并处理DAO异常
*
* @param sql SQL语句
* @param dao BaseDAO实例
* @param operType 操作类型"主表新增"
* @param bizKey 业务标识如cpickmid
*/
private void executeSql(String sql, BaseDAO dao, String operType, String bizKey) throws BusinessException {
try {
logger.debug("执行" + operType + "SQL业务标识" + bizKey + "SQL" + sql + "");
int rows = dao.executeUpdate(sql);
logger.info("" + operType + "成功,业务标识:" + bizKey + ",影响行数:" + rows + "");
} catch (DAOException e) {
String errorMsg = operType + "失败,业务标识:" + bizKey + "SQL" + sql;
logger.error(errorMsg, e);
throw new BusinessException(errorMsg, e);
}
}
/**
* 判断是否跳过该组织
*/
private boolean checkIfSkipOrg(String code, Map<String, String> configParams) throws BusinessException{
String targetCode = configParams.get("imsOrg");
if (targetCode == null || nc.vo.am.common.util.StringUtils.isEmpty(targetCode)) {
throw new BusinessException("未配置组织参数");
}
String[] orgItem = targetCode.split(",");
for (String orgCode : orgItem) {
if (!orgCode.isEmpty() && orgCode.equals(code)) {
return false;
}
}
return true;
}
/**
* 从Agg数组中提取关键备料单号用于日志
*/
private String getCpickmidFromAgg(AggPickmVO[] aggPickmVOS) {
if (aggPickmVOS != null && aggPickmVOS[0] != null && aggPickmVOS[0].getParentVO() != null) {
return aggPickmVOS[0].getParentVO().getCpickmid();
}
return "未知";
}
// -------------------------- 内部工具类封装通用逻辑避免污染外部类 --------------------------
/**
* JSON处理工具类
*/
private static class JsonUtil {
/**
* JSONObject转List<Map<String, Object>>单个JSON对象
*/
public static List<Map<String, Object>> jsonObjectToListMap(JSONObject jsonObject) {
List<Map<String, Object>> resultList = new ArrayList<>();
if (jsonObject == null || jsonObject.isEmpty()) {
return resultList;
}
// 指定初始容量优化性能
Map<String, Object> dataMap = new HashMap<>(jsonObject.size());
for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
dataMap.put(entry.getKey(), entry.getValue());
}
resultList.add(dataMap);
return resultList;
}
}
/**
* SQL值处理工具类封装日期转换字符串转义等
*/
private static class SqlValueUtil {
/**
* 处理SQL值自动识别类型转义特殊字符加单引号
*/
public static String processSqlValue(Object value) {
if (value == null) {
return "NULL";
}
// 处理UFDateTime类型
if (value instanceof UFDateTime) {
return formatUFDateTime((UFDateTime) value);
}
// 处理UFDate类型
if (value instanceof UFDate) {
return formatUFDate((UFDate) value);
}
// 处理标准日期类型
if (value instanceof Date || value instanceof Timestamp) {
return formatDate((Date) value, true);
}
// 处理字符串类型转义单引号
if (value instanceof String) {
String safeStr = ((String) value).replace("'", "''");
return "'" + safeStr + "'";
}
// 其他类型数字布尔等直接转字符串
return String.valueOf(value);
}
/**
* 格式化UFDateTime转SQL字符串
*/
private static String formatUFDateTime(UFDateTime ufDateTime) {
try {
Date date = ufDateTime.getDate().toDate();
return formatDate(date, true);
} catch (Exception e) {
logger.error("格式化UFDateTime失败" + ufDateTime + "");
return "NULL";
}
}
/**
* 格式化UFDate转SQL字符串
*/
private static String formatUFDate(UFDate ufDate) {
try {
Date date = ufDate.toDate(); // 依赖UFDate的toDate()方法返回Date
return formatDate(date, true);
} catch (Exception e) {
logger.error("格式化UFDate失败" + ufDate + "");
return "NULL";
}
}
/**
* 格式化Date为SQL字符串
*
* @param withTime 是否包含时间true: yyyy-MM-dd HH:mm:ss; false: yyyy-MM-dd
*/
private static String formatDate(Date date, boolean withTime) {
if (date == null) {
return "NULL";
}
SimpleDateFormat sdf = withTime ? DATETIME_FORMATTER.get() : DATE_FORMATTER.get();
try {
String dateStr = sdf.format(date);
return "'" + dateStr + "'";
} finally {
// 移除ThreadLocal中的Formatter避免内存泄漏
if (withTime) {
DATETIME_FORMATTER.remove();
} else {
DATE_FORMATTER.remove();
}
}
}
}
}