From 1c250607c4f44344e24e213677e9b2b9e6e0a20b Mon Sep 17 00:00:00 2001 From: "han\\hanst" Date: Tue, 31 Mar 2026 08:28:10 +0800 Subject: [PATCH] =?UTF-8?q?6=E3=80=81=E9=9D=9E=E8=8D=89=E7=A8=BF=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E5=AE=9E=E9=AA=8C=E5=8D=95=E7=9A=84=E5=8E=9F=E6=9D=90?= =?UTF-8?q?=E6=96=99=E6=B8=85=E5=8D=95=E5=A2=9E=E5=88=A0=E6=94=B9=E9=9C=80?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BF=AE=E6=94=B9=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ErfExpRawMaterialController.java | 23 +- .../entity/ErfExpRawMaterialChangeLog.java | 108 +++++ .../ErfExpRawMaterialChangeLogMapper.java | 26 ++ .../erf/service/ErfExpRawMaterialService.java | 9 + .../impl/ErfExpRawMaterialServiceImpl.java | 371 +++++++++++++++++- .../erf/ErfExpRawMaterialChangeLogMapper.xml | 41 ++ 6 files changed, 576 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/xujie/sys/modules/erf/entity/ErfExpRawMaterialChangeLog.java create mode 100644 src/main/java/com/xujie/sys/modules/erf/mapper/ErfExpRawMaterialChangeLogMapper.java create mode 100644 src/main/resources/mapper/erf/ErfExpRawMaterialChangeLogMapper.xml diff --git a/src/main/java/com/xujie/sys/modules/erf/controller/ErfExpRawMaterialController.java b/src/main/java/com/xujie/sys/modules/erf/controller/ErfExpRawMaterialController.java index 439bd39f..e5ba8c24 100644 --- a/src/main/java/com/xujie/sys/modules/erf/controller/ErfExpRawMaterialController.java +++ b/src/main/java/com/xujie/sys/modules/erf/controller/ErfExpRawMaterialController.java @@ -2,6 +2,7 @@ package com.xujie.sys.modules.erf.controller; import com.xujie.sys.common.utils.R; import com.xujie.sys.modules.erf.entity.ErfExpRawMaterial; +import com.xujie.sys.modules.erf.entity.ErfExpRawMaterialChangeLog; import com.xujie.sys.modules.erf.service.ErfExpRawMaterialService; import com.xujie.sys.modules.sys.controller.AbstractController; import lombok.extern.slf4j.Slf4j; @@ -10,7 +11,6 @@ import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.Map; -import java.util.HashMap; /** * 工程实验申请单原材料清单控制器 @@ -55,6 +55,27 @@ public class ErfExpRawMaterialController extends AbstractController { } } + /** + * 根据申请单号查询原材料修改记录 + * + * @param params 包含applyNo的参数 + * @return 原材料修改记录列表 + */ + @PostMapping("/getRawMaterialChangeLogList") + @ResponseBody + public R getRawMaterialChangeLogList(@RequestBody Map params) { + try { + String applyNo = (String) params.get("applyNo"); + log.info("查询原材料修改记录列表,申请单号: {}", applyNo); + + List list = erfExpRawMaterialService.getRawMaterialChangeLogList(applyNo); + return R.ok().put("list", list); + } catch (Exception e) { + log.error("查询原材料修改记录列表失败: " + e.getMessage(), e); + return R.error("查询失败: " + e.getMessage()); + } + } + /** * 根据ID查询单条原材料记录 * diff --git a/src/main/java/com/xujie/sys/modules/erf/entity/ErfExpRawMaterialChangeLog.java b/src/main/java/com/xujie/sys/modules/erf/entity/ErfExpRawMaterialChangeLog.java new file mode 100644 index 00000000..284e8b19 --- /dev/null +++ b/src/main/java/com/xujie/sys/modules/erf/entity/ErfExpRawMaterialChangeLog.java @@ -0,0 +1,108 @@ +package com.xujie.sys.modules.erf.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.util.Date; + +/** + * 原材料清单修改记录实体 + * + *

说明:

+ *
    + *
  • 仅记录非草稿状态下的原材料新增/修改/删除操作
  • + *
  • beforeContent/afterContent 保存操作前后快照(JSON)
  • + *
  • detailContent 保存字段级详细变更说明
  • + *
+ * + * @author System + * @since 2026-03-30 + */ +@Data +@TableName("erf_exp_raw_material_change_log") +public class ErfExpRawMaterialChangeLog implements Serializable { + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.AUTO) + private Integer id; + + /** + * 申请单号 + */ + @TableField("apply_no") + private String applyNo; + + /** + * 原材料主键ID + */ + @TableField("raw_material_id") + private Integer rawMaterialId; + + /** + * 申请单状态(操作发生时) + */ + @TableField("apply_status") + private String applyStatus; + + /** + * 操作类型(新增/修改/删除) + */ + @TableField("operation_type") + private String operationType; + + /** + * 操作摘要 + */ + @TableField("operation_desc") + private String operationDesc; + + /** + * 详细说明(字段级) + */ + @TableField("detail_content") + private String detailContent; + + /** + * 操作前快照(JSON) + */ + @TableField("before_content") + private String beforeContent; + + /** + * 操作后快照(JSON) + */ + @TableField("after_content") + private String afterContent; + + /** + * 操作人ID + */ + @TableField("operator_user_id") + private Long operatorUserId; + + /** + * 操作人用户名 + */ + @TableField("operator_user_name") + private String operatorUserName; + + /** + * 操作人显示名 + */ + @TableField("operator_display_name") + private String operatorDisplayName; + + /** + * 记录时间 + */ + @TableField("created_date") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date createdDate; +} diff --git a/src/main/java/com/xujie/sys/modules/erf/mapper/ErfExpRawMaterialChangeLogMapper.java b/src/main/java/com/xujie/sys/modules/erf/mapper/ErfExpRawMaterialChangeLogMapper.java new file mode 100644 index 00000000..d3a38d9c --- /dev/null +++ b/src/main/java/com/xujie/sys/modules/erf/mapper/ErfExpRawMaterialChangeLogMapper.java @@ -0,0 +1,26 @@ +package com.xujie.sys.modules.erf.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.xujie.sys.modules.erf.entity.ErfExpRawMaterialChangeLog; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 原材料清单修改记录 Mapper + * + * @author System + * @since 2026-03-30 + */ +@Mapper +public interface ErfExpRawMaterialChangeLogMapper extends BaseMapper { + + /** + * 根据申请单号查询修改记录(按时间倒序) + * + * @param applyNo 申请单号 + * @return 修改记录列表 + */ + List getChangeLogListByApplyNo(@Param("applyNo") String applyNo); +} diff --git a/src/main/java/com/xujie/sys/modules/erf/service/ErfExpRawMaterialService.java b/src/main/java/com/xujie/sys/modules/erf/service/ErfExpRawMaterialService.java index 09e8bff4..aa0d2217 100644 --- a/src/main/java/com/xujie/sys/modules/erf/service/ErfExpRawMaterialService.java +++ b/src/main/java/com/xujie/sys/modules/erf/service/ErfExpRawMaterialService.java @@ -1,6 +1,7 @@ package com.xujie.sys.modules.erf.service; import com.xujie.sys.modules.erf.entity.ErfExpRawMaterial; +import com.xujie.sys.modules.erf.entity.ErfExpRawMaterialChangeLog; import java.util.List; import java.util.Map; @@ -83,6 +84,14 @@ public interface ErfExpRawMaterialService { */ Map getPartDescByPartNo(String partNo, String site, String buNo); + /** + * 查询原材料修改记录 + * + * @param applyNo 申请单号 + * @return 修改记录列表(按时间倒序) + */ + List getRawMaterialChangeLogList(String applyNo); + /** * 复制原材料清单从一个申请单到另一个申请单 * diff --git a/src/main/java/com/xujie/sys/modules/erf/service/impl/ErfExpRawMaterialServiceImpl.java b/src/main/java/com/xujie/sys/modules/erf/service/impl/ErfExpRawMaterialServiceImpl.java index 7295b382..fdfdf4de 100644 --- a/src/main/java/com/xujie/sys/modules/erf/service/impl/ErfExpRawMaterialServiceImpl.java +++ b/src/main/java/com/xujie/sys/modules/erf/service/impl/ErfExpRawMaterialServiceImpl.java @@ -1,12 +1,19 @@ package com.xujie.sys.modules.erf.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.fasterxml.jackson.databind.ObjectMapper; import com.xujie.sys.common.exception.XJException; +import com.xujie.sys.common.utils.ShiroUtils; +import com.xujie.sys.modules.erf.entity.ErfExpApply; import com.xujie.sys.modules.erf.entity.ErfExpRawMaterial; +import com.xujie.sys.modules.erf.entity.ErfExpRawMaterialChangeLog; +import com.xujie.sys.modules.erf.mapper.ErfExpApplyMapper; +import com.xujie.sys.modules.erf.mapper.ErfExpRawMaterialChangeLogMapper; import com.xujie.sys.modules.erf.mapper.ErfExpRawMaterialMapper; import com.xujie.sys.modules.erf.service.ErfExpRawMaterialService; import com.xujie.sys.modules.part.entity.PartInformationEntity; import com.xujie.sys.modules.part.mapper.PartInformationMapper; +import com.xujie.sys.modules.sys.entity.SysUserEntity; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -14,9 +21,12 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; +import java.math.BigDecimal; +import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -47,9 +57,21 @@ import java.util.Map; @Transactional public class ErfExpRawMaterialServiceImpl implements ErfExpRawMaterialService { + private static final String STATUS_DRAFT = "草稿"; + private static final String OP_ADD = "新增"; + private static final String OP_UPDATE = "修改"; + private static final String OP_DELETE = "删除"; + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + @Autowired private ErfExpRawMaterialMapper erfExpRawMaterialMapper; + @Autowired + private ErfExpRawMaterialChangeLogMapper erfExpRawMaterialChangeLogMapper; + + @Autowired + private ErfExpApplyMapper erfExpApplyMapper; + @Autowired private PartInformationMapper partInformationMapper; @@ -77,6 +99,28 @@ public class ErfExpRawMaterialServiceImpl implements ErfExpRawMaterialService { } } + /** + * 根据申请单号查询原材料修改记录 + * + * @param applyNo 申请单号 + * @return 修改记录列表(按时间倒序) + */ + @Override + public List getRawMaterialChangeLogList(String applyNo) { + log.info("查询原材料修改记录,试验单号: {}", applyNo); + + if (!StringUtils.hasText(applyNo)) { + throw new XJException("试验单号不能为空"); + } + + try { + return erfExpRawMaterialChangeLogMapper.getChangeLogListByApplyNo(applyNo); + } catch (Exception e) { + log.error("查询原材料修改记录失败: " + e.getMessage(), e); + throw new XJException("查询原材料修改记录失败: " + e.getMessage()); + } + } + /** * 根据ID查询单条原材料记录 * @@ -114,18 +158,43 @@ public class ErfExpRawMaterialServiceImpl implements ErfExpRawMaterialService { validateRawMaterial(rawMaterial); try { - if (rawMaterial.getId() == null || rawMaterial.getId() == 0) { + boolean isNew = (rawMaterial.getId() == null || rawMaterial.getId() == 0); + ErfExpRawMaterial beforeData = null; + + if (!isNew) { + beforeData = erfExpRawMaterialMapper.getRawMaterialById(rawMaterial.getId()); + if (beforeData == null) { + throw new XJException("原材料记录不存在"); + } + // 防止前端篡改applyNo,更新时始终使用数据库原值 + rawMaterial.setApplyNo(beforeData.getApplyNo()); + } + + String applyStatus = getApplyStatus(rawMaterial.getApplyNo()); + boolean needRecordChangeLog = shouldRecordChangeLog(applyStatus); + + if (isNew) { // 新增 log.info("新增原材料记录"); rawMaterial.setIsDeleted("0"); erfExpRawMaterialMapper.insert(rawMaterial); log.info("新增原材料记录成功,ID: {}", rawMaterial.getId()); + + if (needRecordChangeLog) { + ErfExpRawMaterial afterData = erfExpRawMaterialMapper.getRawMaterialById(rawMaterial.getId()); + saveAddChangeLog(rawMaterial.getApplyNo(), applyStatus, afterData); + } } else { // 修改 log.info("修改原材料记录,ID: {}", rawMaterial.getId()); rawMaterial.setUpdatedDate(new Date()); erfExpRawMaterialMapper.updateById(rawMaterial); log.info("修改原材料记录成功"); + + if (needRecordChangeLog) { + ErfExpRawMaterial afterData = erfExpRawMaterialMapper.getRawMaterialById(rawMaterial.getId()); + saveUpdateChangeLog(rawMaterial.getApplyNo(), applyStatus, beforeData, afterData); + } } return rawMaterial.getId(); @@ -176,8 +245,33 @@ public class ErfExpRawMaterialServiceImpl implements ErfExpRawMaterialService { } try { + List deleteTargetList = new ArrayList<>(); + Map applyStatusCache = new HashMap<>(); + + for (Integer id : ids) { + ErfExpRawMaterial dbData = erfExpRawMaterialMapper.getRawMaterialById(id); + if (dbData == null) { + throw new XJException("原材料记录不存在,ID: " + id); + } + deleteTargetList.add(dbData); + + if (!applyStatusCache.containsKey(dbData.getApplyNo())) { + applyStatusCache.put(dbData.getApplyNo(), getApplyStatus(dbData.getApplyNo())); + } + } + int rows = erfExpRawMaterialMapper.batchDeleteByIds(ids, updatedBy); log.info("批量删除成功,影响行数: {}", rows); + + if (rows > 0) { + for (ErfExpRawMaterial beforeData : deleteTargetList) { + String applyStatus = applyStatusCache.get(beforeData.getApplyNo()); + if (shouldRecordChangeLog(applyStatus)) { + saveDeleteChangeLog(beforeData.getApplyNo(), applyStatus, beforeData); + } + } + } + return rows > 0; } catch (Exception e) { log.error("批量删除原材料记录失败: " + e.getMessage(), e); @@ -263,6 +357,266 @@ public class ErfExpRawMaterialServiceImpl implements ErfExpRawMaterialService { } } + /** + * 保存新增日志 + */ + private void saveAddChangeLog(String applyNo, String applyStatus, ErfExpRawMaterial afterData) { + if (afterData == null) { + return; + } + ErfExpRawMaterialChangeLog logData = buildBaseChangeLog(applyNo, applyStatus, afterData.getId(), OP_ADD); + logData.setOperationDesc(buildAddOperationDesc(afterData)); + logData.setDetailContent(buildAddDetailContent(applyStatus, afterData)); + logData.setBeforeContent(null); + logData.setAfterContent(buildMaterialSnapshot(afterData)); + erfExpRawMaterialChangeLogMapper.insert(logData); + } + + /** + * 保存修改日志 + */ + private void saveUpdateChangeLog(String applyNo, String applyStatus, ErfExpRawMaterial beforeData, ErfExpRawMaterial afterData) { + ErfExpRawMaterialChangeLog logData = buildBaseChangeLog(applyNo, applyStatus, afterData != null ? afterData.getId() : null, OP_UPDATE); + logData.setOperationDesc(buildUpdateOperationDesc(beforeData, afterData)); + logData.setDetailContent(buildUpdateDetailContent(applyStatus, beforeData, afterData)); + logData.setBeforeContent(buildMaterialSnapshot(beforeData)); + logData.setAfterContent(buildMaterialSnapshot(afterData)); + erfExpRawMaterialChangeLogMapper.insert(logData); + } + + /** + * 保存删除日志 + */ + private void saveDeleteChangeLog(String applyNo, String applyStatus, ErfExpRawMaterial beforeData) { + ErfExpRawMaterialChangeLog logData = buildBaseChangeLog(applyNo, applyStatus, beforeData != null ? beforeData.getId() : null, OP_DELETE); + logData.setOperationDesc(buildDeleteOperationDesc(beforeData)); + logData.setDetailContent(buildDeleteDetailContent(applyStatus, beforeData)); + logData.setBeforeContent(buildMaterialSnapshot(beforeData)); + logData.setAfterContent(null); + erfExpRawMaterialChangeLogMapper.insert(logData); + } + + /** + * 构建基础日志对象 + */ + private ErfExpRawMaterialChangeLog buildBaseChangeLog(String applyNo, String applyStatus, Integer rawMaterialId, String operationType) { + OperatorInfo operatorInfo = getCurrentOperatorInfo(); + ErfExpRawMaterialChangeLog logData = new ErfExpRawMaterialChangeLog(); + logData.setApplyNo(applyNo); + logData.setApplyStatus(applyStatus); + logData.setRawMaterialId(rawMaterialId); + logData.setOperationType(operationType); + logData.setOperatorUserId(operatorInfo.userId); + logData.setOperatorUserName(operatorInfo.userName); + logData.setOperatorDisplayName(operatorInfo.userDisplayName); + logData.setCreatedDate(new Date()); + return logData; + } + + /** + * 获取当前操作人 + */ + private OperatorInfo getCurrentOperatorInfo() { + try { + SysUserEntity user = ShiroUtils.getUserEntity(); + if (user != null) { + String userName = user.getUsername(); + String userDisplay = StringUtils.hasText(user.getUserDisplay()) ? user.getUserDisplay() : userName; + return new OperatorInfo(user.getUserId(), userName, userDisplay); + } + } catch (Exception e) { + log.warn("获取当前操作人失败,使用默认值: {}", e.getMessage()); + } + return new OperatorInfo(null, "system", "system"); + } + + /** + * 判断是否需要记录修改日志(仅非草稿状态) + */ + private boolean shouldRecordChangeLog(String applyStatus) { + return StringUtils.hasText(applyStatus) && !STATUS_DRAFT.equals(applyStatus.trim()); + } + + /** + * 查询申请单状态 + */ + private String getApplyStatus(String applyNo) { + if (!StringUtils.hasText(applyNo)) { + return ""; + } + ErfExpApply apply = erfExpApplyMapper.selectById(applyNo); + return apply != null && StringUtils.hasText(apply.getStatus()) ? apply.getStatus().trim() : ""; + } + + private String buildAddOperationDesc(ErfExpRawMaterial afterData) { + return String.format("非草稿状态新增原材料:工序[%s] 物料[%s]", + formatValue(afterData.getProcessStep()), + formatValue(afterData.getPartNo())); + } + + private String buildDeleteOperationDesc(ErfExpRawMaterial beforeData) { + return String.format("非草稿状态删除原材料:工序[%s] 物料[%s]", + formatValue(beforeData != null ? beforeData.getProcessStep() : null), + formatValue(beforeData != null ? beforeData.getPartNo() : null)); + } + + private String buildUpdateOperationDesc(ErfExpRawMaterial beforeData, ErfExpRawMaterial afterData) { + List changedFieldNameList = getChangedFieldNameList(beforeData, afterData); + if (changedFieldNameList.isEmpty()) { + return "非草稿状态保存原材料(未检测到字段变化)"; + } + String desc = "非草稿状态修改原材料,变更字段:" + String.join("、", changedFieldNameList); + return desc.length() > 300 ? desc.substring(0, 300) : desc; + } + + private String buildAddDetailContent(String applyStatus, ErfExpRawMaterial afterData) { + StringBuilder sb = new StringBuilder(); + //sb.append("操作场景:非草稿状态原材料新增").append("\n"); + sb.append("申请单状态:").append(formatValue(applyStatus)).append("\n"); + sb.append("新增内容:"); + sb.append(buildReadableSnapshot(afterData)); + return sb.toString(); + } + + private String buildDeleteDetailContent(String applyStatus, ErfExpRawMaterial beforeData) { + StringBuilder sb = new StringBuilder(); + //sb.append("操作场景:非草稿状态原材料删除").append("\n"); + sb.append("申请单状态:").append(formatValue(applyStatus)).append("\n"); + sb.append("删除前内容:"); + sb.append(buildReadableSnapshot(beforeData)); + return sb.toString(); + } + + private String buildUpdateDetailContent(String applyStatus, ErfExpRawMaterial beforeData, ErfExpRawMaterial afterData) { + List diffList = buildFieldDiffList(beforeData, afterData); + StringBuilder sb = new StringBuilder(); + //sb.append("操作场景:非草稿状态原材料修改").append("\n"); + sb.append("申请单状态:").append(formatValue(applyStatus)).append("\n"); + sb.append("字段变更数:").append(diffList.size()).append("\n"); + if (diffList.isEmpty()) { + sb.append("字段变更明细:未检测到实际字段变化(可能为重复保存)").append("\n"); + } else { + sb.append("字段变更明细:").append("\n"); + for (int i = 0; i < diffList.size(); i++) { + sb.append(i + 1).append(". ").append(diffList.get(i)).append("\n"); + } + } + sb.append("修改前快照:").append("\n").append(buildReadableSnapshot(beforeData)).append("\n"); + sb.append("修改后快照:").append("\n").append(buildReadableSnapshot(afterData)); + return sb.toString(); + } + + private String buildReadableSnapshot(ErfExpRawMaterial data) { + if (data == null) { + return "(空)"; + } + StringBuilder sb = new StringBuilder(); + sb.append("工序=").append(formatValue(data.getProcessStep())).append("; "); + sb.append("物料编码=").append(formatValue(data.getPartNo())).append("; "); + sb.append("物料描述=").append(formatValue(data.getPartDesc())).append("; "); + sb.append("数量=").append(formatBigDecimal(data.getQuantity())).append("; "); + sb.append("单位=").append(formatValue(data.getUmid())).append("; "); + sb.append("备注=").append(formatValue(data.getRemark())); + return sb.toString(); + } + + private List buildFieldDiffList(ErfExpRawMaterial beforeData, ErfExpRawMaterial afterData) { + List diffList = new ArrayList<>(); + appendDiff(diffList, "工序", beforeData != null ? beforeData.getProcessStep() : null, afterData != null ? afterData.getProcessStep() : null); + appendDiff(diffList, "物料编码", beforeData != null ? beforeData.getPartNo() : null, afterData != null ? afterData.getPartNo() : null); + appendDiff(diffList, "物料描述", beforeData != null ? beforeData.getPartDesc() : null, afterData != null ? afterData.getPartDesc() : null); + appendBigDecimalDiff(diffList, "数量", beforeData != null ? beforeData.getQuantity() : null, afterData != null ? afterData.getQuantity() : null); + appendDiff(diffList, "单位", beforeData != null ? beforeData.getUmid() : null, afterData != null ? afterData.getUmid() : null); + appendDiff(diffList, "备注", beforeData != null ? beforeData.getRemark() : null, afterData != null ? afterData.getRemark() : null); + return diffList; + } + + private List getChangedFieldNameList(ErfExpRawMaterial beforeData, ErfExpRawMaterial afterData) { + List fieldNameList = new ArrayList<>(); + appendChangedFieldName(fieldNameList, "工序", beforeData != null ? beforeData.getProcessStep() : null, afterData != null ? afterData.getProcessStep() : null); + appendChangedFieldName(fieldNameList, "物料编码", beforeData != null ? beforeData.getPartNo() : null, afterData != null ? afterData.getPartNo() : null); + appendChangedFieldName(fieldNameList, "物料描述", beforeData != null ? beforeData.getPartDesc() : null, afterData != null ? afterData.getPartDesc() : null); + appendChangedFieldName(fieldNameList, "单位", beforeData != null ? beforeData.getUmid() : null, afterData != null ? afterData.getUmid() : null); + appendChangedFieldName(fieldNameList, "备注", beforeData != null ? beforeData.getRemark() : null, afterData != null ? afterData.getRemark() : null); + if (!isBigDecimalSame(beforeData != null ? beforeData.getQuantity() : null, afterData != null ? afterData.getQuantity() : null)) { + fieldNameList.add("数量"); + } + return fieldNameList; + } + + private void appendDiff(List diffList, String fieldName, String oldValue, String newValue) { + if (!isTextSame(oldValue, newValue)) { + diffList.add(fieldName + ":" + formatValue(oldValue) + " -> " + formatValue(newValue)); + } + } + + private void appendBigDecimalDiff(List diffList, String fieldName, BigDecimal oldValue, BigDecimal newValue) { + if (!isBigDecimalSame(oldValue, newValue)) { + diffList.add(fieldName + ":" + formatBigDecimal(oldValue) + " -> " + formatBigDecimal(newValue)); + } + } + + private void appendChangedFieldName(List fieldNameList, String fieldName, String oldValue, String newValue) { + if (!isTextSame(oldValue, newValue)) { + fieldNameList.add(fieldName); + } + } + + private boolean isTextSame(String oldValue, String newValue) { + String oldNormalized = StringUtils.hasText(oldValue) ? oldValue.trim() : ""; + String newNormalized = StringUtils.hasText(newValue) ? newValue.trim() : ""; + return oldNormalized.equals(newNormalized); + } + + private boolean isBigDecimalSame(BigDecimal oldValue, BigDecimal newValue) { + if (oldValue == null && newValue == null) { + return true; + } + if (oldValue == null || newValue == null) { + return false; + } + return oldValue.compareTo(newValue) == 0; + } + + private String buildMaterialSnapshot(ErfExpRawMaterial data) { + if (data == null) { + return null; + } + Map snapshot = new LinkedHashMap<>(); + snapshot.put("id", data.getId()); + snapshot.put("applyNo", data.getApplyNo()); + snapshot.put("site", data.getSite()); + snapshot.put("processStep", data.getProcessStep()); + snapshot.put("partNo", data.getPartNo()); + snapshot.put("partDesc", data.getPartDesc()); + snapshot.put("quantity", data.getQuantity()); + snapshot.put("umid", data.getUmid()); + snapshot.put("remark", data.getRemark()); + snapshot.put("updatedBy", data.getUpdatedBy()); + snapshot.put("updatedDate", data.getUpdatedDate()); + return toJson(snapshot); + } + + private String toJson(Object data) { + if (data == null) { + return null; + } + try { + return OBJECT_MAPPER.writeValueAsString(data); + } catch (Exception e) { + log.warn("对象转JSON失败: {}", e.getMessage()); + return String.valueOf(data); + } + } + + private String formatValue(String value) { + return StringUtils.hasText(value) ? value.trim() : "(空)"; + } + + private String formatBigDecimal(BigDecimal value) { + return value == null ? "(空)" : value.stripTrailingZeros().toPlainString(); + } + /** * 验证原材料数据 * @@ -319,4 +673,19 @@ public class ErfExpRawMaterialServiceImpl implements ErfExpRawMaterialService { throw new XJException("复制原材料清单失败: " + e.getMessage()); } } + + /** + * 操作人信息 + */ + private static class OperatorInfo { + private final Long userId; + private final String userName; + private final String userDisplayName; + + private OperatorInfo(Long userId, String userName, String userDisplayName) { + this.userId = userId; + this.userName = userName; + this.userDisplayName = userDisplayName; + } + } } diff --git a/src/main/resources/mapper/erf/ErfExpRawMaterialChangeLogMapper.xml b/src/main/resources/mapper/erf/ErfExpRawMaterialChangeLogMapper.xml new file mode 100644 index 00000000..6daa9f13 --- /dev/null +++ b/src/main/resources/mapper/erf/ErfExpRawMaterialChangeLogMapper.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + +