feat: 新增业务编码生成及设备产量统计服务

master
zangch@mesnac.com 3 weeks ago
parent 41f4b8f6fb
commit fe008bb747

@ -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;
}
}

@ -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;
}
}

@ -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);
}

@ -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);
}

@ -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);
}

@ -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);
}

@ -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;
}
}
}

@ -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();
}
}

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.aucma.base.mapper.BaseBizCodeSequenceMapper">
<resultMap id="BaseBizCodeSequenceResult" type="BaseBizCodeSequence">
<result property="objId" column="obj_id"/>
<result property="bizType" column="biz_type"/>
<result property="currentPeriod" column="current_period"/>
<result property="currentSeq" column="current_seq"/>
<result property="remark" column="remark"/>
<result property="createdTime" column="created_time"/>
<result property="updatedTime" column="updated_time"/>
</resultMap>
<select id="selectByBizTypeForUpdate" resultMap="BaseBizCodeSequenceResult">
select obj_id,
biz_type,
current_period,
current_seq,
remark,
created_time,
updated_time
from base_biz_code_seq
where biz_type = #{bizType}
for update
</select>
<insert id="insertBaseBizCodeSequence" parameterType="BaseBizCodeSequence">
<selectKey keyProperty="objId" resultType="long" order="BEFORE">
SELECT seq_base_biz_code_seq.NEXTVAL as objId FROM DUAL
</selectKey>
insert into base_biz_code_seq
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="objId != null">obj_id,</if>
<if test="bizType != null">biz_type,</if>
<if test="currentPeriod != null">current_period,</if>
<if test="currentSeq != null">current_seq,</if>
<if test="remark != null">remark,</if>
<if test="createdTime != null">created_time,</if>
<if test="updatedTime != null">updated_time,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="objId != null">#{objId},</if>
<if test="bizType != null">#{bizType},</if>
<if test="currentPeriod != null">#{currentPeriod},</if>
<if test="currentSeq != null">#{currentSeq},</if>
<if test="remark != null">#{remark},</if>
<if test="createdTime != null">#{createdTime},</if>
<if test="updatedTime != null">#{updatedTime},</if>
</trim>
</insert>
<update id="updateBaseBizCodeSequence" parameterType="BaseBizCodeSequence">
update base_biz_code_seq
<trim prefix="set" suffixOverrides=",">
<if test="currentPeriod != null">current_period = #{currentPeriod},</if>
<if test="currentSeq != null">current_seq = #{currentSeq},</if>
<if test="remark != null">remark = #{remark},</if>
<if test="updatedTime != null">updated_time = #{updatedTime},</if>
</trim>
where obj_id = #{objId}
</update>
</mapper>

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.aucma.base.mapper.RtDailyProdStateMapper">
<resultMap id="RtDailyProdStateResult" type="com.aucma.base.domain.RtDailyProdState">
<result property="prodDate" column="prod_date"/>
<result property="deviceCode" column="device_code"/>
<result property="paramName" column="param_name"/>
<result property="lastParamVal" column="last_param_val"/>
<result property="currentTotal" column="current_total"/>
<result property="resetCount" column="reset_count"/>
<result property="dirtyFlag" column="dirty_flag"/>
<result property="lastCollectTime" column="last_collect_time"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<select id="selectForUpdate" resultMap="RtDailyProdStateResult">
SELECT prod_date, device_code, param_name, last_param_val, current_total,
reset_count, dirty_flag, last_collect_time, update_time
FROM RT_DAILY_PROD_STATE
WHERE prod_date = #{prodDate}
AND device_code = #{deviceCode}
AND param_name = #{paramName}
FOR UPDATE
</select>
<insert id="insertRtDailyProdState" parameterType="com.aucma.base.domain.RtDailyProdState">
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}
)
</insert>
<update id="updateRtDailyProdState" parameterType="com.aucma.base.domain.RtDailyProdState">
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}
</update>
</mapper>
Loading…
Cancel
Save