From fe008bb747f60fddde5fd5a7b95be8a9dbed9000 Mon Sep 17 00:00:00 2001 From: "zangch@mesnac.com" Date: Mon, 16 Mar 2026 16:57:17 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E4=B8=9A=E5=8A=A1?= =?UTF-8?q?=E7=BC=96=E7=A0=81=E7=94=9F=E6=88=90=E5=8F=8A=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E4=BA=A7=E9=87=8F=E7=BB=9F=E8=AE=A1=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../base/domain/BaseBizCodeSequence.java | 92 +++++++++++++++ .../aucma/base/domain/RtDailyProdState.java | 104 +++++++++++++++++ .../mapper/BaseBizCodeSequenceMapper.java | 35 ++++++ .../base/mapper/RtDailyProdStateMapper.java | 20 ++++ .../IBusinessCodeGeneratorService.java | 18 +++ .../service/IRtDailyProdStateService.java | 12 ++ .../BusinessCodeGeneratorServiceImpl.java | 105 ++++++++++++++++++ .../impl/RtDailyProdStateServiceImpl.java | 79 +++++++++++++ .../mapper/base/BaseBizCodeSequenceMapper.xml | 65 +++++++++++ .../mapper/base/RtDailyProdStateMapper.xml | 50 +++++++++ 10 files changed, 580 insertions(+) create mode 100644 aucma-base/src/main/java/com/aucma/base/domain/BaseBizCodeSequence.java create mode 100644 aucma-base/src/main/java/com/aucma/base/domain/RtDailyProdState.java create mode 100644 aucma-base/src/main/java/com/aucma/base/mapper/BaseBizCodeSequenceMapper.java create mode 100644 aucma-base/src/main/java/com/aucma/base/mapper/RtDailyProdStateMapper.java create mode 100644 aucma-base/src/main/java/com/aucma/base/service/IBusinessCodeGeneratorService.java create mode 100644 aucma-base/src/main/java/com/aucma/base/service/IRtDailyProdStateService.java create mode 100644 aucma-base/src/main/java/com/aucma/base/service/impl/BusinessCodeGeneratorServiceImpl.java create mode 100644 aucma-base/src/main/java/com/aucma/base/service/impl/RtDailyProdStateServiceImpl.java create mode 100644 aucma-base/src/main/resources/mapper/base/BaseBizCodeSequenceMapper.xml create mode 100644 aucma-base/src/main/resources/mapper/base/RtDailyProdStateMapper.xml diff --git a/aucma-base/src/main/java/com/aucma/base/domain/BaseBizCodeSequence.java b/aucma-base/src/main/java/com/aucma/base/domain/BaseBizCodeSequence.java new file mode 100644 index 0000000..4abb243 --- /dev/null +++ b/aucma-base/src/main/java/com/aucma/base/domain/BaseBizCodeSequence.java @@ -0,0 +1,92 @@ +package com.aucma.base.domain; + +import java.util.Date; + +/** + * 业务编码序列表对象 base_biz_code_seq + * + * @author Codex + */ +public class BaseBizCodeSequence { + private Long objId; + + /** + * 业务类型编码 + */ + private String bizType; + + /** + * 当前编码周期,如 yyyyMM + */ + private String currentPeriod; + + /** + * 当前已使用序号 + */ + private Long currentSeq; + + /** + * 备注 + */ + private String remark; + + private Date createdTime; + + private Date updatedTime; + + public Long getObjId() { + return objId; + } + + public void setObjId(Long objId) { + this.objId = objId; + } + + public String getBizType() { + return bizType; + } + + public void setBizType(String bizType) { + this.bizType = bizType; + } + + public String getCurrentPeriod() { + return currentPeriod; + } + + public void setCurrentPeriod(String currentPeriod) { + this.currentPeriod = currentPeriod; + } + + public Long getCurrentSeq() { + return currentSeq; + } + + public void setCurrentSeq(Long currentSeq) { + this.currentSeq = currentSeq; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + public Date getCreatedTime() { + return createdTime; + } + + public void setCreatedTime(Date createdTime) { + this.createdTime = createdTime; + } + + public Date getUpdatedTime() { + return updatedTime; + } + + public void setUpdatedTime(Date updatedTime) { + this.updatedTime = updatedTime; + } +} diff --git a/aucma-base/src/main/java/com/aucma/base/domain/RtDailyProdState.java b/aucma-base/src/main/java/com/aucma/base/domain/RtDailyProdState.java new file mode 100644 index 0000000..c2e25c4 --- /dev/null +++ b/aucma-base/src/main/java/com/aucma/base/domain/RtDailyProdState.java @@ -0,0 +1,104 @@ +package com.aucma.base.domain; + +import com.aucma.common.core.domain.BaseEntity; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 设备当日实时产量状态 + */ +public class RtDailyProdState extends BaseEntity { + + private static final long serialVersionUID = 1L; + + private Date prodDate; + + private String deviceCode; + + private String paramName; + + private BigDecimal lastParamVal; + + private BigDecimal currentTotal; + + private Long resetCount; + + private Long dirtyFlag; + + private Date lastCollectTime; + + private Date updateTime; + + public Date getProdDate() { + return prodDate; + } + + public void setProdDate(Date prodDate) { + this.prodDate = prodDate; + } + + public String getDeviceCode() { + return deviceCode; + } + + public void setDeviceCode(String deviceCode) { + this.deviceCode = deviceCode; + } + + public String getParamName() { + return paramName; + } + + public void setParamName(String paramName) { + this.paramName = paramName; + } + + public BigDecimal getLastParamVal() { + return lastParamVal; + } + + public void setLastParamVal(BigDecimal lastParamVal) { + this.lastParamVal = lastParamVal; + } + + public BigDecimal getCurrentTotal() { + return currentTotal; + } + + public void setCurrentTotal(BigDecimal currentTotal) { + this.currentTotal = currentTotal; + } + + public Long getResetCount() { + return resetCount; + } + + public void setResetCount(Long resetCount) { + this.resetCount = resetCount; + } + + public Long getDirtyFlag() { + return dirtyFlag; + } + + public void setDirtyFlag(Long dirtyFlag) { + this.dirtyFlag = dirtyFlag; + } + + public Date getLastCollectTime() { + return lastCollectTime; + } + + public void setLastCollectTime(Date lastCollectTime) { + this.lastCollectTime = lastCollectTime; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } +} diff --git a/aucma-base/src/main/java/com/aucma/base/mapper/BaseBizCodeSequenceMapper.java b/aucma-base/src/main/java/com/aucma/base/mapper/BaseBizCodeSequenceMapper.java new file mode 100644 index 0000000..f563099 --- /dev/null +++ b/aucma-base/src/main/java/com/aucma/base/mapper/BaseBizCodeSequenceMapper.java @@ -0,0 +1,35 @@ +package com.aucma.base.mapper; + +import com.aucma.base.domain.BaseBizCodeSequence; +import org.apache.ibatis.annotations.Param; + +/** + * 业务编码序列表 Mapper + * + * @author Codex + */ +public interface BaseBizCodeSequenceMapper { + /** + * 按业务类型加锁查询编码序列 + * + * @param bizType 业务类型 + * @return 编码序列 + */ + BaseBizCodeSequence selectByBizTypeForUpdate(@Param("bizType") String bizType); + + /** + * 新增编码序列 + * + * @param baseBizCodeSequence 编码序列 + * @return 结果 + */ + int insertBaseBizCodeSequence(BaseBizCodeSequence baseBizCodeSequence); + + /** + * 更新编码序列 + * + * @param baseBizCodeSequence 编码序列 + * @return 结果 + */ + int updateBaseBizCodeSequence(BaseBizCodeSequence baseBizCodeSequence); +} diff --git a/aucma-base/src/main/java/com/aucma/base/mapper/RtDailyProdStateMapper.java b/aucma-base/src/main/java/com/aucma/base/mapper/RtDailyProdStateMapper.java new file mode 100644 index 0000000..439a2ea --- /dev/null +++ b/aucma-base/src/main/java/com/aucma/base/mapper/RtDailyProdStateMapper.java @@ -0,0 +1,20 @@ +package com.aucma.base.mapper; + +import com.aucma.base.domain.RtDailyProdState; +import org.apache.ibatis.annotations.Param; + +import java.util.Date; + +/** + * 设备当日实时产量状态Mapper + */ +public interface RtDailyProdStateMapper { + + RtDailyProdState selectForUpdate(@Param("prodDate") Date prodDate, + @Param("deviceCode") String deviceCode, + @Param("paramName") String paramName); + + int insertRtDailyProdState(RtDailyProdState state); + + int updateRtDailyProdState(RtDailyProdState state); +} diff --git a/aucma-base/src/main/java/com/aucma/base/service/IBusinessCodeGeneratorService.java b/aucma-base/src/main/java/com/aucma/base/service/IBusinessCodeGeneratorService.java new file mode 100644 index 0000000..767ce02 --- /dev/null +++ b/aucma-base/src/main/java/com/aucma/base/service/IBusinessCodeGeneratorService.java @@ -0,0 +1,18 @@ +package com.aucma.base.service; + +import java.util.Date; + +/** + * 统一业务编码生成服务 + * + * @author Codex + */ +public interface IBusinessCodeGeneratorService { + /** + * 生成 SAP 计划编号 + * + * @param businessDate 业务日期 + * @return SAP 计划编号 + */ + String generateSapPlanCode(Date businessDate); +} diff --git a/aucma-base/src/main/java/com/aucma/base/service/IRtDailyProdStateService.java b/aucma-base/src/main/java/com/aucma/base/service/IRtDailyProdStateService.java new file mode 100644 index 0000000..1f996c0 --- /dev/null +++ b/aucma-base/src/main/java/com/aucma/base/service/IRtDailyProdStateService.java @@ -0,0 +1,12 @@ +package com.aucma.base.service; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 设备当日实时产量状态Service + */ +public interface IRtDailyProdStateService { + + void incrementProduction(String deviceCode, String paramName, BigDecimal newVal, Date collectTime); +} diff --git a/aucma-base/src/main/java/com/aucma/base/service/impl/BusinessCodeGeneratorServiceImpl.java b/aucma-base/src/main/java/com/aucma/base/service/impl/BusinessCodeGeneratorServiceImpl.java new file mode 100644 index 0000000..8e7e21e --- /dev/null +++ b/aucma-base/src/main/java/com/aucma/base/service/impl/BusinessCodeGeneratorServiceImpl.java @@ -0,0 +1,105 @@ +package com.aucma.base.service.impl; + +import com.aucma.base.domain.BaseBizCodeSequence; +import com.aucma.base.mapper.BaseBizCodeSequenceMapper; +import com.aucma.base.service.IBusinessCodeGeneratorService; +import com.aucma.common.exception.ServiceException; +import com.aucma.common.utils.DateUtils; +import com.aucma.common.utils.StringUtils; +import com.aucma.system.service.ISysConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; + +/** + * 统一业务编码生成服务实现 + * + * @author Codex + */ +@Service +public class BusinessCodeGeneratorServiceImpl implements IBusinessCodeGeneratorService { + private static final String SAP_PLAN_BIZ_TYPE = "SAP_PLAN"; + private static final String SAP_PLAN_DATE_PATTERN_KEY = "mes.biz.code.sapPlan.datePattern"; + private static final String SAP_PLAN_SEQ_LENGTH_KEY = "mes.biz.code.sapPlan.seqLength"; + private static final String DEFAULT_DATE_PATTERN = "yyyyMM"; + private static final int DEFAULT_SEQ_LENGTH = 4; + + @Autowired + private BaseBizCodeSequenceMapper baseBizCodeSequenceMapper; + + @Autowired + private ISysConfigService sysConfigService; + + @Override + @Transactional(rollbackFor = Exception.class) + public String generateSapPlanCode(Date businessDate) { + Date currentBusinessDate = businessDate != null ? businessDate : DateUtils.getNowDate(); + String datePattern = getConfigValue(SAP_PLAN_DATE_PATTERN_KEY, DEFAULT_DATE_PATTERN); + int seqLength = parseSeqLength(getConfigValue(SAP_PLAN_SEQ_LENGTH_KEY, String.valueOf(DEFAULT_SEQ_LENGTH))); + String currentPeriod = DateUtils.parseDateToStr(datePattern, currentBusinessDate); + + BaseBizCodeSequence sequence = getLockedSequence(SAP_PLAN_BIZ_TYPE, currentPeriod); + Long nextSeq = calculateNextSeq(sequence, currentPeriod); + sequence.setCurrentPeriod(currentPeriod); + sequence.setCurrentSeq(nextSeq); + sequence.setUpdatedTime(DateUtils.getNowDate()); + baseBizCodeSequenceMapper.updateBaseBizCodeSequence(sequence); + + return currentPeriod + StringUtils.leftPad(String.valueOf(nextSeq), seqLength, '0'); + } + + private BaseBizCodeSequence getLockedSequence(String bizType, String currentPeriod) { + BaseBizCodeSequence sequence = baseBizCodeSequenceMapper.selectByBizTypeForUpdate(bizType); + if (sequence != null) { + return sequence; + } + + BaseBizCodeSequence initSequence = new BaseBizCodeSequence(); + initSequence.setBizType(bizType); + initSequence.setCurrentPeriod(currentPeriod); + initSequence.setCurrentSeq(0L); + initSequence.setRemark("统一业务编码序列"); + initSequence.setCreatedTime(DateUtils.getNowDate()); + initSequence.setUpdatedTime(DateUtils.getNowDate()); + try { + baseBizCodeSequenceMapper.insertBaseBizCodeSequence(initSequence); + } catch (DuplicateKeyException ex) { + // 为什么忽略重复插入异常:并发场景下允许只有一个事务初始化成功,其他事务随后重新加锁读取即可。 + } + + sequence = baseBizCodeSequenceMapper.selectByBizTypeForUpdate(bizType); + if (sequence == null) { + throw new ServiceException("业务编码序列初始化失败,请检查基础数据配置"); + } + return sequence; + } + + private Long calculateNextSeq(BaseBizCodeSequence sequence, String currentPeriod) { + if (!StringUtils.equals(sequence.getCurrentPeriod(), currentPeriod)) { + // 为什么跨周期要从 1 重新计数:编号规则要求前缀按年月分段,序号必须在新周期内重新累计。 + return 1L; + } + Long currentSeq = sequence.getCurrentSeq() == null ? 0L : sequence.getCurrentSeq(); + return currentSeq + 1L; + } + + private String getConfigValue(String configKey, String defaultValue) { + String configValue = sysConfigService.selectConfigByKey(configKey); + return StringUtils.isBlank(configValue) ? defaultValue : configValue.trim(); + } + + private int parseSeqLength(String seqLengthValue) { + try { + int seqLength = Integer.parseInt(seqLengthValue); + if (seqLength <= 0) { + return DEFAULT_SEQ_LENGTH; + } + return seqLength; + } catch (NumberFormatException ex) { + return DEFAULT_SEQ_LENGTH; + } + } +} diff --git a/aucma-base/src/main/java/com/aucma/base/service/impl/RtDailyProdStateServiceImpl.java b/aucma-base/src/main/java/com/aucma/base/service/impl/RtDailyProdStateServiceImpl.java new file mode 100644 index 0000000..7263283 --- /dev/null +++ b/aucma-base/src/main/java/com/aucma/base/service/impl/RtDailyProdStateServiceImpl.java @@ -0,0 +1,79 @@ +package com.aucma.base.service.impl; + +import com.aucma.base.domain.RtDailyProdState; +import com.aucma.base.mapper.RtDailyProdStateMapper; +import com.aucma.base.service.IRtDailyProdStateService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.Calendar; +import java.util.Date; + +/** + * 设备当日实时产量状态Service实现 + */ +@Service +public class RtDailyProdStateServiceImpl implements IRtDailyProdStateService { + + @Autowired + private RtDailyProdStateMapper rtDailyProdStateMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public void incrementProduction(String deviceCode, String paramName, BigDecimal newVal, Date collectTime) { + if (deviceCode == null || paramName == null || newVal == null || collectTime == null) { + return; + } + + Date prodDate = truncateToDay(collectTime); + RtDailyProdState state = rtDailyProdStateMapper.selectForUpdate(prodDate, deviceCode, paramName); + Date now = new Date(); + if (state == null) { + RtDailyProdState insertState = new RtDailyProdState(); + insertState.setProdDate(prodDate); + insertState.setDeviceCode(deviceCode); + insertState.setParamName(paramName); + // 为什么这样做:当天首条只建立基准值,避免把历史累计值误算成当天产量。 + insertState.setLastParamVal(newVal); + insertState.setCurrentTotal(BigDecimal.ZERO); + insertState.setResetCount(0L); + insertState.setDirtyFlag(0L); + insertState.setLastCollectTime(collectTime); + insertState.setUpdateTime(now); + rtDailyProdStateMapper.insertRtDailyProdState(insertState); + return; + } + + BigDecimal lastParamVal = state.getLastParamVal() == null ? BigDecimal.ZERO : state.getLastParamVal(); + BigDecimal currentTotal = state.getCurrentTotal() == null ? BigDecimal.ZERO : state.getCurrentTotal(); + Long resetCount = state.getResetCount() == null ? 0L : state.getResetCount(); + + BigDecimal delta = BigDecimal.ZERO; + if (newVal.compareTo(lastParamVal) > 0) { + delta = newVal.subtract(lastParamVal); + } else if (newVal.compareTo(lastParamVal) < 0) { + // 为什么这样做:计数器回退时把当前值视为重置后的新增量,避免丢产量。 + delta = newVal; + resetCount += 1; + } + + state.setLastParamVal(newVal); + state.setCurrentTotal(currentTotal.add(delta)); + state.setResetCount(resetCount); + state.setLastCollectTime(collectTime); + state.setUpdateTime(now); + rtDailyProdStateMapper.updateRtDailyProdState(state); + } + + private Date truncateToDay(Date collectTime) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(collectTime); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + return calendar.getTime(); + } +} diff --git a/aucma-base/src/main/resources/mapper/base/BaseBizCodeSequenceMapper.xml b/aucma-base/src/main/resources/mapper/base/BaseBizCodeSequenceMapper.xml new file mode 100644 index 0000000..77bdb77 --- /dev/null +++ b/aucma-base/src/main/resources/mapper/base/BaseBizCodeSequenceMapper.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + SELECT seq_base_biz_code_seq.NEXTVAL as objId FROM DUAL + + insert into base_biz_code_seq + + obj_id, + biz_type, + current_period, + current_seq, + remark, + created_time, + updated_time, + + + #{objId}, + #{bizType}, + #{currentPeriod}, + #{currentSeq}, + #{remark}, + #{createdTime}, + #{updatedTime}, + + + + + update base_biz_code_seq + + current_period = #{currentPeriod}, + current_seq = #{currentSeq}, + remark = #{remark}, + updated_time = #{updatedTime}, + + where obj_id = #{objId} + + diff --git a/aucma-base/src/main/resources/mapper/base/RtDailyProdStateMapper.xml b/aucma-base/src/main/resources/mapper/base/RtDailyProdStateMapper.xml new file mode 100644 index 0000000..bfe3901 --- /dev/null +++ b/aucma-base/src/main/resources/mapper/base/RtDailyProdStateMapper.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + INSERT INTO RT_DAILY_PROD_STATE ( + prod_date, device_code, param_name, last_param_val, current_total, + reset_count, dirty_flag, last_collect_time, update_time + ) VALUES ( + #{prodDate}, #{deviceCode}, #{paramName}, #{lastParamVal}, #{currentTotal}, + #{resetCount}, #{dirtyFlag}, #{lastCollectTime}, #{updateTime} + ) + + + + UPDATE RT_DAILY_PROD_STATE + SET last_param_val = #{lastParamVal}, + current_total = #{currentTotal}, + reset_count = #{resetCount}, + last_collect_time = #{lastCollectTime}, + update_time = #{updateTime} + WHERE prod_date = #{prodDate} + AND device_code = #{deviceCode} + AND param_name = #{paramName} + +