Files
klp-oa/klp-wms/src/main/java/com/klp/service/impl/WmsCoilPendingActionServiceImpl.java
Joshi f47e1f529c feat(WmsCoilPendingActionService): 添加按更新人查询功能
- 根据更新人筛选待办事项查询条件
- 使用StringUtils判断更新人参数是否为空
- 将更新人条件添加到查询构建器中
2026-03-02 13:31:37 +08:00

523 lines
21 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.klp.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.page.TableDataInfo;
import com.klp.common.helper.LoginHelper;
import com.klp.common.utils.StringUtils;
import com.klp.domain.WmsCoilPendingAction;
import com.klp.domain.WmsMaterialCoil;
import com.klp.domain.bo.WmsCoilPendingActionBo;
import com.klp.domain.vo.TheoryCyclePointVo;
import com.klp.domain.vo.TheoryCycleRegressionResultVo;
import com.klp.domain.vo.TheoryCycleRegressionVo;
import com.klp.domain.vo.WmsCoilPendingActionVo;
import com.klp.mapper.WmsCoilPendingActionMapper;
import com.klp.mapper.WmsMaterialCoilMapper;
import com.klp.service.IWmsCoilPendingActionService;
import com.klp.system.service.ISysUserService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* 钢卷待操作Service业务层处理
*
* @author Joshi
* @date 2025-11-03
*/
@RequiredArgsConstructor
@Service
public class WmsCoilPendingActionServiceImpl implements IWmsCoilPendingActionService {
private final WmsCoilPendingActionMapper baseMapper;
private final ISysUserService userService;
private final WmsMaterialCoilMapper materialCoilMapper;
private final StringRedisTemplate stringRedisTemplate;
private static final String REDIS_KEY_IDEAL_CYCLE = "oee:ideal-cycle-time";
/**
* 查询钢卷待操作
*/
@Override
public WmsCoilPendingActionVo queryById(Long actionId){
return baseMapper.selectVoById(actionId);
}
/**
* 查询钢卷待操作列表
*/
@Override
public TableDataInfo<WmsCoilPendingActionVo> queryPageList(WmsCoilPendingActionBo bo, PageQuery pageQuery) {
QueryWrapper<WmsCoilPendingAction> lqw = buildQueryWrapperPlus(bo);
Page<WmsCoilPendingActionVo> result = baseMapper.selectVoPagePlus(pageQuery.build(), lqw);
List<WmsCoilPendingActionVo> records = result.getRecords();
Set<String> userNames = records.stream()
.flatMap(v -> java.util.stream.Stream.of(v.getCreateBy(), v.getOperatorName()))
.filter(StringUtils::isNotBlank)
.collect(Collectors.toSet());
if (!userNames.isEmpty()) {
Map<String, String> nickMap = userService.selectNickNameMapByUserNames(records.stream()
.flatMap(v -> java.util.stream.Stream.of(v.getCreateBy(), v.getOperatorName()))
.filter(StringUtils::isNotBlank)
.distinct()
.collect(Collectors.toList()));
records.forEach(item -> {
if (StringUtils.isNotBlank(item.getCreateBy())) {
item.setCreateByName(nickMap.getOrDefault(item.getCreateBy(), item.getCreateBy()));
}
if (StringUtils.isNotBlank(item.getOperatorName())) {
item.setOperatorByName(nickMap.getOrDefault(item.getOperatorName(), item.getOperatorName()));
}
});
}
return TableDataInfo.build(result);
}
private QueryWrapper<WmsCoilPendingAction> buildQueryWrapperPlus(WmsCoilPendingActionBo bo) {
QueryWrapper<WmsCoilPendingAction> qw = Wrappers.query();
qw.eq(bo.getCoilId() != null, "wcpa.coil_id", bo.getCoilId());
qw.like(StringUtils.isNotBlank(bo.getCurrentCoilNo()), "wcpa.current_coil_no", bo.getCurrentCoilNo());
qw.eq(bo.getActionType() != null, "wcpa.action_type", bo.getActionType());
qw.ne(bo.getActionStatus() != null, "wcpa.action_status",2);
qw.eq(bo.getWarehouseId() != null, "wcpa.warehouse_id", bo.getWarehouseId());
qw.eq(bo.getPriority() != null, "wcpa.priority", bo.getPriority());
qw.like(StringUtils.isNotBlank(bo.getSourceType()), "wcpa.source_type", bo.getSourceType());
qw.orderByDesc("wcpa.create_time");
qw.orderByDesc("wcpa.scan_time");
//根据开始时间和结束时间筛选修改时间
qw.ge(bo.getStartTime() != null, "wcpa.update_time", bo.getStartTime());
qw.le(bo.getEndTime() != null, "wcpa.update_time", bo.getEndTime());
// 根据更新人查询
qw.eq(StringUtils.isNotBlank(bo.getUpdateBy()), "wcpa.update_by", bo.getUpdateBy());
//逻辑删除
qw.eq("wcpa.del_flag", 0);
return qw;
}
/**
* 查询钢卷待操作列表
*/
@Override
public List<WmsCoilPendingActionVo> queryList(WmsCoilPendingActionBo bo) {
LambdaQueryWrapper<WmsCoilPendingAction> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<WmsCoilPendingAction> buildQueryWrapper(WmsCoilPendingActionBo bo) {
LambdaQueryWrapper<WmsCoilPendingAction> lqw = Wrappers.lambdaQuery();
lqw.eq(bo.getCoilId() != null, WmsCoilPendingAction::getCoilId, bo.getCoilId());
lqw.like(StringUtils.isNotBlank(bo.getCurrentCoilNo()), WmsCoilPendingAction::getCurrentCoilNo, bo.getCurrentCoilNo());
lqw.eq(bo.getActionType() != null, WmsCoilPendingAction::getActionType, bo.getActionType());
lqw.eq(bo.getActionStatus() != null, WmsCoilPendingAction::getActionStatus, bo.getActionStatus());
lqw.eq(bo.getWarehouseId() != null, WmsCoilPendingAction::getWarehouseId, bo.getWarehouseId());
lqw.eq(bo.getPriority() != null, WmsCoilPendingAction::getPriority, bo.getPriority());
lqw.like(StringUtils.isNotBlank(bo.getSourceType()), WmsCoilPendingAction::getSourceType, bo.getSourceType());
lqw.orderByDesc(WmsCoilPendingAction::getCreateTime);
lqw.orderByDesc(WmsCoilPendingAction::getScanTime);
return lqw;
}
/**
* 新增钢卷待操作
*/
@Override
public Boolean insertByBo(WmsCoilPendingActionBo bo) {
WmsCoilPendingAction add = BeanUtil.toBean(bo, WmsCoilPendingAction.class);
validEntityBeforeSave(add);
if (add.getCoilId() != null){
WmsMaterialCoil materialCoil = materialCoilMapper.selectById(add.getCoilId());
if (materialCoil.getDataType() == 0) {
throw new RuntimeException("该钢卷为历史钢卷不能被操作");
}
}
// 设置默认值
if (add.getActionStatus() == null) {
add.setActionStatus(0); // 默认待处理
}
if (StringUtils.isBlank(add.getSourceType())) {
add.setSourceType("manual"); // 默认手动创建
}
if (add.getScanTime() == null && "scan".equals(add.getSourceType())) {
add.setScanTime(new Date());
}
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setActionId(add.getActionId());
}
return flag;
}
/**
* 修改钢卷待操作
*/
@Override
public Boolean updateByBo(WmsCoilPendingActionBo bo) {
WmsCoilPendingAction update = BeanUtil.toBean(bo, WmsCoilPendingAction.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(WmsCoilPendingAction entity){
// TODO 做一些数据校验,如唯一约束
}
/**
* 批量删除钢卷待操作
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if(isValid){
// TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteBatchIds(ids) > 0;
}
/**
* 更新操作状态
*/
@Override
public Boolean updateStatus(Long actionId, Integer status) {
WmsCoilPendingAction action = new WmsCoilPendingAction();
action.setActionId(actionId);
action.setActionStatus(status);
return baseMapper.updateById(action) > 0;
}
/**
* 开始处理操作
*/
@Override
public Boolean startProcess(Long actionId) {
WmsCoilPendingAction action = new WmsCoilPendingAction();
action.setActionId(actionId);
action.setActionStatus(1); // 处理中
action.setProcessTime(new Date());
try {
action.setOperatorId(LoginHelper.getUserId());
action.setOperatorName(LoginHelper.getUsername());
} catch (Exception e) {
// 如果获取登录用户失败,不影响主流程
}
return baseMapper.updateById(action) > 0;
}
/**
* 完成操作
*/
@Override
public Boolean completeAction(Long actionId) {
// 先查询原记录,检查操作人是否为空
WmsCoilPendingAction oldAction = baseMapper.selectById(actionId);
if (oldAction == null) {
throw new RuntimeException("待操作记录不存在");
}
WmsCoilPendingAction action = new WmsCoilPendingAction();
action.setActionId(actionId);
action.setActionStatus(2); // 已完成
action.setCompleteTime(new Date());
// 如果操作人为空,设置当前登录用户为操作人
if (oldAction.getOperatorId() == null || oldAction.getOperatorName() == null) {
try {
action.setOperatorId(LoginHelper.getUserId());
action.setOperatorName(LoginHelper.getUsername());
action.setProcessTime(new Date());
} catch (Exception e) {
// 如果获取登录用户失败,不影响主流程
}
}
return baseMapper.updateById(action) > 0;
}
/**
* 取消操作
*/
@Override
public Boolean cancelAction(Long actionId) {
WmsCoilPendingAction action = new WmsCoilPendingAction();
action.setActionId(actionId);
action.setActionStatus(3); // 已取消
return baseMapper.updateById(action) > 0;
}
@Override
public TheoryCycleRegressionResultVo calcTheoryCycleRegression(Date startTime, Date endTime) {
return calcTheoryCycleRegression(startTime, endTime, true, 2000);
}
@Override
public TheoryCycleRegressionResultVo calcTheoryCycleRegression(Date startTime, Date endTime, Boolean includePoints, Integer maxPoints) {
LocalDateTime end = endTime == null ? LocalDateTime.now() : toLocalDateTime(endTime);
LocalDateTime start = startTime == null ? end.minusMonths(6) : toLocalDateTime(startTime);
boolean inc = includePoints != null && includePoints;
int limit = (maxPoints == null || maxPoints <= 0) ? 2000 : maxPoints;
TheoryCycleRegressionVo sy = buildRegression("SY", "酸轧线", 11, start, end, false, inc, limit);
TheoryCycleRegressionVo dx1 = buildRegression("DX1", "镀锌一线", 501, start, end, true, inc, limit);
cacheIdealCycle(sy);
cacheIdealCycle(dx1);
TheoryCycleRegressionResultVo result = new TheoryCycleRegressionResultVo();
result.setLines(Arrays.asList(sy, dx1));
return result;
}
private TheoryCycleRegressionVo buildRegression(String lineId, String lineName, int actionType,
LocalDateTime start, LocalDateTime end,
boolean parseRemarkIds,
boolean includePoints, int maxPoints) {
LambdaQueryWrapper<WmsCoilPendingAction> lqw = Wrappers.lambdaQuery();
lqw.eq(WmsCoilPendingAction::getActionType, actionType)
.eq(WmsCoilPendingAction::getDelFlag, 0)
.eq(WmsCoilPendingAction::getActionStatus, 2)
.ge(WmsCoilPendingAction::getCreateTime, Date.from(start.atZone(ZoneId.systemDefault()).toInstant()))
.le(WmsCoilPendingAction::getCreateTime, Date.from(end.atZone(ZoneId.systemDefault()).toInstant()));
List<WmsCoilPendingAction> actions = baseMapper.selectList(lqw);
if (actions == null || actions.isEmpty()) {
TheoryCycleRegressionVo vo = new TheoryCycleRegressionVo();
vo.setLineId(lineId);
vo.setLineName(lineName);
vo.setStartTime(Date.from(start.atZone(ZoneId.systemDefault()).toInstant()));
vo.setEndTime(Date.from(end.atZone(ZoneId.systemDefault()).toInstant()));
vo.setSampleCount(0);
vo.setPoints(Collections.emptyList());
vo.setLinePoints(Collections.emptyList());
return vo;
}
// 预先收集所有需要的钢卷 ID一次性批量查询避免在循环中逐条访问数据库
Set<Long> allCoilIds = new HashSet<>();
for (WmsCoilPendingAction action : actions) {
if (action.getCreateTime() == null || action.getCompleteTime() == null) {
continue;
}
if (parseRemarkIds) {
List<Long> ids = parseIdsFromRemark(action.getRemark());
allCoilIds.addAll(ids);
} else {
if (action.getCoilId() != null) {
allCoilIds.add(action.getCoilId());
}
}
}
Map<Long, Double> weightTonMap = new HashMap<>();
if (!allCoilIds.isEmpty()) {
List<WmsMaterialCoil> coils = materialCoilMapper.selectBatchIds(allCoilIds);
if (coils != null) {
for (WmsMaterialCoil coil : coils) {
if (coil == null || coil.getCoilId() == null) continue;
BigDecimal net = coil.getNetWeight();
BigDecimal gross = coil.getGrossWeight();
BigDecimal weightKg = net != null && net.compareTo(BigDecimal.ZERO) > 0 ? net : gross;
if (weightKg == null || weightKg.compareTo(BigDecimal.ZERO) <= 0) {
continue;
}
double ton = weightKg.divide(BigDecimal.valueOf(1000), 6, RoundingMode.HALF_UP).doubleValue();
weightTonMap.put(coil.getCoilId(), ton);
}
}
}
List<TheoryCyclePointVo> points = new ArrayList<>();
for (WmsCoilPendingAction action : actions) {
if (action.getCreateTime() == null || action.getCompleteTime() == null) {
continue;
}
long minutes = Duration.between(
toLocalDateTime(action.getCreateTime()),
toLocalDateTime(action.getCompleteTime())
).toMinutes();
if (minutes <= 0) {
continue;
}
double weightTon = 0D;
if (parseRemarkIds) {
List<Long> ids = parseIdsFromRemark(action.getRemark());
for (Long id : ids) {
Double wt = weightTonMap.get(id);
if (wt != null && wt > 0D) {
weightTon += wt;
}
}
} else {
if (action.getCoilId() != null) {
Double wt = weightTonMap.get(action.getCoilId());
if (wt != null && wt > 0D) {
weightTon = wt;
}
}
}
if (weightTon <= 0D) {
continue;
}
TheoryCyclePointVo p = new TheoryCyclePointVo();
p.setActionId(action.getActionId());
p.setCreateTime(action.getCreateTime());
p.setDurationMin((double) minutes);
p.setWeightTon(weightTon);
points.add(p);
}
TheoryCycleRegressionVo vo = new TheoryCycleRegressionVo();
vo.setLineId(lineId);
vo.setLineName(lineName);
vo.setStartTime(Date.from(start.atZone(ZoneId.systemDefault()).toInstant()));
vo.setEndTime(Date.from(end.atZone(ZoneId.systemDefault()).toInstant()));
vo.setSampleCount(points.size());
if (includePoints) {
vo.setPoints(samplePoints(points, maxPoints));
} else {
vo.setPoints(Collections.emptyList());
}
RegressionStat stat = linearRegression(points);
vo.setSlopeMinPerTon(stat.slope);
vo.setInterceptMin(stat.intercept);
vo.setR2(stat.r2);
vo.setLinePoints(stat.linePoints);
return vo;
}
/**
* 散点抽样:避免返回体过大导致网络/序列化问题。
*/
private List<TheoryCyclePointVo> samplePoints(List<TheoryCyclePointVo> points, int maxPoints) {
if (points == null || points.isEmpty()) {
return Collections.emptyList();
}
if (maxPoints <= 0 || points.size() <= maxPoints) {
return points;
}
int n = points.size();
double step = (double) n / (double) maxPoints;
List<TheoryCyclePointVo> sampled = new ArrayList<>(maxPoints);
for (int i = 0; i < maxPoints; i++) {
int idx = (int) Math.floor(i * step);
if (idx < 0) idx = 0;
if (idx >= n) idx = n - 1;
sampled.add(points.get(idx));
}
return sampled;
}
private void cacheIdealCycle(TheoryCycleRegressionVo vo) {
if (vo == null || vo.getSlopeMinPerTon() == null) {
return;
}
String field = vo.getLineId() == null ? "UNKNOWN" : vo.getLineId().toUpperCase();
stringRedisTemplate.opsForHash().put(REDIS_KEY_IDEAL_CYCLE, field, vo.getSlopeMinPerTon().toString());
if (vo.getInterceptMin() != null) {
stringRedisTemplate.opsForHash().put(REDIS_KEY_IDEAL_CYCLE + ":intercept", field, vo.getInterceptMin().toString());
}
}
private RegressionStat linearRegression(List<TheoryCyclePointVo> points) {
RegressionStat stat = new RegressionStat();
if (points == null || points.size() < 2) {
return stat;
}
double sumX = 0, sumY = 0, sumXX = 0, sumXY = 0;
for (TheoryCyclePointVo p : points) {
double x = p.getWeightTon();
double y = p.getDurationMin();
sumX += x;
sumY += y;
sumXX += x * x;
sumXY += x * y;
}
int n = points.size();
double denominator = n * sumXX - sumX * sumX;
if (denominator == 0) {
return stat;
}
double slope = (n * sumXY - sumX * sumY) / denominator;
double intercept = (sumY - slope * sumX) / n;
double ssTot = 0, ssRes = 0;
double meanY = sumY / n;
for (TheoryCyclePointVo p : points) {
double y = p.getDurationMin();
double yHat = slope * p.getWeightTon() + intercept;
ssTot += Math.pow(y - meanY, 2);
ssRes += Math.pow(y - yHat, 2);
}
double r2 = ssTot == 0 ? 0 : 1 - ssRes / ssTot;
stat.slope = slope;
stat.intercept = intercept;
stat.r2 = r2;
stat.linePoints = buildLinePoints(points, slope, intercept);
return stat;
}
private List<TheoryCyclePointVo> buildLinePoints(List<TheoryCyclePointVo> points, double slope, double intercept) {
if (points == null || points.isEmpty()) {
return Collections.emptyList();
}
double minX = points.stream().mapToDouble(TheoryCyclePointVo::getWeightTon).min().orElse(0D);
double maxX = points.stream().mapToDouble(TheoryCyclePointVo::getWeightTon).max().orElse(0D);
List<TheoryCyclePointVo> line = new ArrayList<>();
TheoryCyclePointVo p1 = new TheoryCyclePointVo();
p1.setWeightTon(minX);
p1.setDurationMin(slope * minX + intercept);
TheoryCyclePointVo p2 = new TheoryCyclePointVo();
p2.setWeightTon(maxX);
p2.setDurationMin(slope * maxX + intercept);
line.add(p1);
line.add(p2);
return line;
}
private List<Long> parseIdsFromRemark(String remark) {
if (StringUtils.isBlank(remark)) {
return Collections.emptyList();
}
Matcher matcher = Pattern.compile("\\d+").matcher(remark);
List<Long> ids = new ArrayList<>();
while (matcher.find()) {
try {
ids.add(Long.parseLong(matcher.group()));
} catch (NumberFormatException ignore) {
}
}
return ids;
}
private LocalDateTime toLocalDateTime(Date date) {
return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
}
private static class RegressionStat {
Double slope;
Double intercept;
Double r2;
List<TheoryCyclePointVo> linePoints = Collections.emptyList();
}
}