生产管理接口开发

liweidong
李涛 2 months ago
parent 9db0a7f082
commit e96052c4ba
  1. 3
      blade-service-api/blade-desk-api/src/main/java/org/springblade/desk/produce/pojo/dto/UpdatePriorityDTO.java
  2. 2
      blade-service-api/blade-desk-api/src/main/java/org/springblade/desk/produce/pojo/dto/WorkOrderDTO.java
  3. 9
      blade-service-api/blade-desk-api/src/main/java/org/springblade/desk/produce/pojo/entity/WorkOrder.java
  4. 20
      blade-service-api/blade-desk-api/src/main/java/org/springblade/desk/produce/pojo/entity/WorkPlan.java
  5. 65
      blade-service-api/blade-desk-api/src/main/java/org/springblade/desk/produce/pojo/enums/WorkOrderEnum.java
  6. 101
      blade-service-api/blade-desk-api/src/main/java/org/springblade/desk/produce/pojo/vo/CacheWorkOrderVO.java
  7. 103
      blade-service-api/blade-desk-api/src/main/java/org/springblade/desk/produce/pojo/vo/CacheWorkPlanVO.java
  8. 30
      blade-service/blade-desk/src/main/java/org/springblade/desk/DeskApplication.java
  9. 5
      blade-service/blade-desk/src/main/java/org/springblade/desk/dashboard/service/IPrReworkProcessService.java
  10. 8
      blade-service/blade-desk/src/main/java/org/springblade/desk/dashboard/service/impl/PrReworkProcessServiceImpl.java
  11. 2
      blade-service/blade-desk/src/main/java/org/springblade/desk/produce/controller/DeductionPreserveController.java
  12. 13
      blade-service/blade-desk/src/main/java/org/springblade/desk/produce/controller/RbProduceManageController.java
  13. 49
      blade-service/blade-desk/src/main/java/org/springblade/desk/produce/controller/ReworkProcessController.java
  14. 24
      blade-service/blade-desk/src/main/java/org/springblade/desk/produce/controller/SjProduceManageController.java
  15. 28
      blade-service/blade-desk/src/main/java/org/springblade/desk/produce/listener/CacheInitListener.java
  16. 3
      blade-service/blade-desk/src/main/java/org/springblade/desk/produce/mapper/WorkOrderMapper.xml
  17. 9
      blade-service/blade-desk/src/main/java/org/springblade/desk/produce/service/IWorkOrderService.java
  18. 173
      blade-service/blade-desk/src/main/java/org/springblade/desk/produce/service/impl/OrderCacheService.java
  19. 1
      blade-service/blade-desk/src/main/java/org/springblade/desk/produce/service/impl/OrderDeclareServiceImpl.java
  20. 17
      blade-service/blade-desk/src/main/java/org/springblade/desk/produce/service/impl/WorkOrderServiceImpl.java
  21. 6
      blade-service/blade-desk/src/main/java/org/springblade/desk/produce/wrapper/WorkOrderWrapper.java
  22. 8
      blade-service/blade-desk/src/main/resources/application-dev.yml

@ -18,4 +18,7 @@ public class UpdatePriorityDTO {
@Schema(description = "需求交期")
private Date demandDate;
@Schema(description = "优先级;1.正常,2.项目要求日期急件,3.合同急件,4.绩效零件,5.调度标注急件")
private Integer priority;
}

@ -10,7 +10,7 @@ import lombok.Data;
@Data
public class WorkOrderDTO {
@Schema(description = "订单类型:1-热表,2-烧结,3-玻璃饼,4-壳体,5-插针,6-石墨模")
@Schema(description = "订单类型:12001-热表,12002-烧结,12003-玻璃饼,12004-壳体,12005-插针,12006-石墨模")
private String yieldType;
@Schema(description = "运行状态:1.正常,2.已下达,3.加工中,4.检验中,13.审理中,14.返工中,15.已完工,21已关闭")

@ -8,9 +8,9 @@ import org.springblade.core.mp.base.BaseEntity;
import java.io.Serial;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 车间订单表 实体类
@ -67,6 +67,9 @@ public class WorkOrder extends BaseEntity {
* 已关闭
*/
public static Integer RUN_STATUS_VOIDED = 21;
public static final Set<Integer> RUNNING_ORDER_STATUS = Set.of(RUN_STATUS_ISSUED, RUN_STATUS_RECEIVE);
/**
* 正常
*/

@ -162,27 +162,27 @@ public class WorkPlan extends BaseEntity {
/**
*
*/
@Schema(description = "")
@Schema(description = "绑定状态")
private Short bindStatus;
/**
*
*/
@Schema(description = "")
@Schema(description = "报工数量")
private Double workQty;
/**
*
*/
@Schema(description = "")
@Schema(description = "接收人")
private Long receiveMan;
/**
*
*/
@Schema(description = "")
@Schema(description = "加工工时")
private BigDecimal hours;
/**
*
*/
@Schema(description = "")
@Schema(description = "工艺能力")
private Long caId;
/**
* 打印标记类型1生产过程
@ -192,7 +192,7 @@ public class WorkPlan extends BaseEntity {
/**
*
*/
@Schema(description = "")
@Schema(description = "附属班组")
private Long subsidiaryTeam;
/**
* 检验颜色标识
@ -220,14 +220,14 @@ public class WorkPlan extends BaseEntity {
@Schema(description = "工艺文件编号/版本号")
private String papers;
/**
*
* 引用文件/版本号
*/
@Schema(description = "")
@Schema(description = "引用文件/版本号")
private String referenceFile;
/**
* 引用文件/版本号
* 外协订单
*/
@Schema(description = "引用文件/版本号")
@Schema(description = "外协订单")
private String wxNo;
/**

@ -0,0 +1,65 @@
package org.springblade.desk.produce.pojo.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springblade.core.tool.utils.ObjectUtil;
import org.springblade.core.tool.utils.StringPool;
import java.util.Arrays;
/**
* 车间订单枚举
*
* @author litao
* @date 2026-2-4
*/
@Getter
@AllArgsConstructor
public enum WorkOrderEnum {
EMPTY(StringPool.EMPTY, -1),
/**
* 状态枚举
*/
RUN_STATUS_NORMAL("未下达", 1),
RUN_STATUS_ISSUED("已下达", 2),
RUN_STATUS_RECEIVE("加工中", 3),
RUN_STATUS_CHECK("检验中", 4),
RUN_STATUS_CRAFT_CHANGE("工艺变更", 5),
RUN_STATUS_HEAR("审理中", 13),
RUN_STATUS_COMPLETED("已完工", 15),
RUN_STATUS_HANDOVER("已交接", 17),
RUN_STATUS_REWORK("返工", 19),
RUN_STATUS_SCRAP("报废", 20),
RUN_STATUS_VOIDED("已关闭", 21);
final String name;
final int category;
/**
* 匹配枚举值
*
* @param name 名称
* @return BladeUserEnum
*/
public static WorkOrderEnum of(String name) {
return Arrays.stream(WorkOrderEnum.values())
.filter(userEnum -> userEnum.getName().equalsIgnoreCase(name != null ? name : "web"))
.findFirst()
// 在没有找到匹配项时返回默认值
.orElse(WorkOrderEnum.EMPTY);
}
/**
* 根据值获取名称
*
* @param category
* @return
*/
public static String getName(int category) {
WorkOrderEnum item = Arrays.stream(WorkOrderEnum.values())
.filter(enumItem -> enumItem.getCategory() == category)
.findFirst()
.orElse(null);
return ObjectUtil.isEmpty(item) ? StringPool.EMPTY : item.getName();
}
}

@ -0,0 +1,101 @@
package org.springblade.desk.produce.pojo.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
/**
* 订单缓存 视图实体类
*
* @author litao
* @since 2026-2-3
*/
@Data
public class CacheWorkOrderVO {
@Schema(description = "车间订单ID")
private Long woId;
@Schema(description = "生产订单ID")
private Long yoId;
@Schema(description = "主加工单位:班组")
private String mainTsName;
@Schema(description = "主加工单位:外协")
private String mainOcName;
@Schema(description = "班组id")
private String makeTeam;
@Schema(description = "外协id")
private String ppsOcId;
@Schema(description = "班组")
private String tsName;
@Schema(description = "外协")
private String ocName;
@Schema(description = "工序")
private String ppsName;
@Schema(description = "生产订单号")
private String yoCode;
@Schema(description = "计划单号")
private String ypCode;
@Schema(description = "零件号")
private String partCode;
@Schema(description = "生产标识")
private String productIdent;
@Schema(description = "产品名称")
private String partName;
@Schema(description = "镀种信息")
private String plate;
@Schema(description = "产品型号")
private String productType;
@Schema(description = "需求部门")
private String useDept;
@Schema(description = "面积(dm²)")
private String totalArea;
@Schema(description = "单批次面积")
private String area;
@Schema(description = "调度员")
private String dispatcherName;
@Schema(description = "未入库数量")
private Integer notInQty;
@Schema(description = "下序id")
private Integer ppsIdNext;
@Schema(description = "下序名称")
private Integer ppsNameNext;
@Schema(description = "下一班组id")
private Integer makeTeamNext;
@Schema(description = "下一班组")
private Integer makeTeamNextName;
@Schema(description = "接收人员id")
private Integer receiveUser;
@Schema(description = "接收人员")
private Integer receiveUserNamr;
@Schema(description = "工序集合")
private List<CacheWorkPlanVO> processList;
}

@ -0,0 +1,103 @@
package org.springblade.desk.produce.pojo.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Date;
/**
* 工序缓存 视图实体类
*
* @author litao
* @since 2026-2-3
*/
@Data
public class CacheWorkPlanVO {
@Schema(description = "车间订单")
private Long woId;
@Schema(description = "工序")
private Long ppsId;
@Schema(description = "工序号")
private String orders;
@Schema(description = "工序描述")
private String makeMemo;
@Schema(description = "工时定额")
private BigDecimal hourQuota;
@Schema(description = "计划开始")
private LocalDateTime startTime;
@Schema(description = "计划结束")
private LocalDateTime endTime;
@Schema(description = "加工班组")
private Long makeTeam;
@Schema(description = "是否外协")
private String oem;
@Schema(description = "外协商")
private Long ocId;
@Schema(description = "上一道")
private Long frontWpId;
@Schema(description = "下一道")
private Long nextWpId;
@Schema(description = "试验数量")
private Double testQty;
@Schema(description = "合格数量")
private Double qualifiedQty;
@Schema(description = "报废数量")
private Double scrapQty;
@Schema(description = "不合格数量")
private Integer unqualifiedQty;
@Schema(description = "实际开始")
private Date factStartTime;
@Schema(description = "实际结束")
private Date factEndTime;
@Schema(description = "报工数量")
private Double workQty;
@Schema(description = "接收人")
private Long receiveMan;
@Schema(description = "加工工时")
private BigDecimal hours;
@Schema(description = "工艺能力")
private Long caId;
@Schema(description = "附属班组")
private Long subsidiaryTeam;
@Schema(description = "消耗数量")
private Double lossQty;
@Schema(description = "金额")
private BigDecimal wpMoney;
@Schema(description = "工艺文件编号/版本号")
private String papers;
@Schema(description = "引用文件/版本号")
private String referenceFile;
@Schema(description = "作业中心")
private Long workCenterId;
}

@ -1,34 +1,10 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.desk;
import org.springblade.core.cloud.client.BladeCloudApplication;
import org.springblade.core.launch.BladeApplication;
import org.springblade.core.launch.constant.AppConstant;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableAsync;
/**
* Desk启动器
@ -36,6 +12,8 @@ import org.springframework.scheduling.annotation.EnableScheduling;
* @author Chill
*/
@BladeCloudApplication
@EnableCaching
@EnableAsync
public class DeskApplication {
public static void main(String[] args) {

@ -25,12 +25,15 @@
*/
package org.springblade.desk.dashboard.service;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import org.springblade.desk.dashboard.pojo.entity.PrReworkProcessEntity;
import org.springblade.desk.dashboard.pojo.vo.PrReworkProcessVO;
import org.springblade.desk.dashboard.excel.PrReworkProcessExcel;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springblade.core.mp.base.BaseService;
import org.springblade.erpdata.pojo.dto.ReworkProcessDTO;
import java.util.List;
/**
@ -66,4 +69,6 @@ public interface IPrReworkProcessService extends BaseService<PrReworkProcessEnti
* @return
*/
List<PrReworkProcessEntity> selectPrReworkProcess(String reworkOrder, String partCode, String batchNo);
JSONArray treeProcess(ReworkProcessDTO prReworkProcess);
}

@ -25,12 +25,13 @@
*/
package org.springblade.desk.dashboard.service.impl;
import org.checkerframework.checker.units.qual.A;
import com.alibaba.fastjson.JSONArray;
import org.springblade.desk.dashboard.pojo.entity.PrReworkProcessEntity;
import org.springblade.desk.dashboard.pojo.vo.PrReworkProcessVO;
import org.springblade.desk.dashboard.excel.PrReworkProcessExcel;
import org.springblade.desk.dashboard.mapper.PrReworkProcessMapper;
import org.springblade.desk.dashboard.service.IPrReworkProcessService;
import org.springblade.erpdata.pojo.dto.ReworkProcessDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
@ -70,4 +71,9 @@ public class PrReworkProcessServiceImpl extends BaseServiceImpl<PrReworkProcessM
return reworkProcessMapper.selectPrReworkProcess(reworkOrder,partCode,batchNo);
}
@Override
public JSONArray treeProcess(ReworkProcessDTO prReworkProcess) {
return null;
}
}

@ -41,7 +41,7 @@ public class DeductionPreserveController extends BladeController {
@GetMapping("/workOrderByCardNo")
@ApiOperationSupport(order = 1)
@Operation(summary = "扣数维护查询", description = "cardNo")
public R workOrderByCardNo(String cardNo) {
public R<DeductionPreserveVO> workOrderByCardNo(String cardNo) {
return R.data(deductionPreserveService.workOrderByCardNo(cardNo));
}

@ -13,9 +13,11 @@ import org.springblade.core.tool.api.R;
import org.springblade.desk.produce.pojo.dto.*;
import org.springblade.desk.produce.pojo.entity.WorkOrder;
import org.springblade.desk.produce.pojo.vo.BatchPrepareVO;
import org.springblade.desk.produce.pojo.vo.CacheWorkOrderVO;
import org.springblade.desk.produce.pojo.vo.WorkOrderVO;
import org.springblade.desk.produce.pojo.vo.WorkPlanRunVO;
import org.springblade.desk.produce.service.IWorkOrderService;
import org.springblade.desk.produce.service.impl.OrderCacheService;
import org.springblade.desk.produce.wrapper.WorkOrderWrapper;
import org.springframework.web.bind.annotation.*;
@ -33,6 +35,8 @@ public class RbProduceManageController extends BladeController {
private final IWorkOrderService workOrderService;
private final OrderCacheService orderCacheService;
@GetMapping("/page")
@ApiOperationSupport(order = 1)
@Operation(summary = "车间订单列表分页", description = "传入WorkOrderVO")
@ -92,10 +96,17 @@ public class RbProduceManageController extends BladeController {
@Operation(summary = "更改车间订单优先级", description = "传入WorkOrder")
public R updatePriority(@RequestBody UpdatePriorityDTO updatePriorityDTO) {
WorkOrder wo = workOrderService.getById(updatePriorityDTO.getWoId());
wo.setPriority(WorkOrder.PRIORITY_SCH_IMP);
// wo.setPriority(WorkOrder.PRIORITY_SCH_IMP);
wo.setPriority(updatePriorityDTO.getPriority());
wo.setDemandDate(updatePriorityDTO.getDemandDate());
return R.data(workOrderService.updateById(wo));
}
@GetMapping(value = "/getWorkOrderCache/{woId}")
@ApiOperationSupport(order = 9)
@Operation(summary = "缓存获取订单相关数据", description = "传入车间订单ID")
public R<CacheWorkOrderVO> getWorkOrderCache(@PathVariable Long woId) {
return R.data(orderCacheService.getOrderFromCache(woId));
}
}

@ -0,0 +1,49 @@
package org.springblade.desk.produce.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import org.springblade.core.boot.ctrl.BladeController;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.core.tool.api.R;
import org.springblade.desk.dashboard.service.IPrReworkProcessService;
import org.springblade.erpdata.feign.IErpDataProduceClient;
import org.springblade.erpdata.pojo.dto.ReworkProcessDTO;
import org.springblade.erpdata.pojo.vo.ReworkProcessVO;
import org.springframework.web.bind.annotation.*;
/**
* 返工订单
*
* @author litao
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/ReworkProcess")
@Tag(name = "返工订单", description = "接口")
public class ReworkProcessController extends BladeController {
private final IErpDataProduceClient erpDataProduceClient;
private final IPrReworkProcessService reworkProcessService;
@GetMapping("/loadReworkOrder")
@ApiOperationSupport(order = 1)
@Operation(summary = "erp查询返工订单", description = "传入ReworkProcessDTO")
public R<IPage<ReworkProcessVO>> page(ReworkProcessDTO prReworkProcess, Query query) {
prReworkProcess.setQuery(query);
return erpDataProduceClient.loadReworkOrder(prReworkProcess);
}
// @GetMapping("/treeProcess")
// @ApiOperationSupport(order = 2)
// @Operation(summary = "查询返工工序树", description = "")
// public R taskComplete(@RequestBody ReworkProcessDTO prReworkProcess) {
// return R.data(reworkProcessService.treeProcess(prReworkProcess));
// }
}

@ -43,21 +43,21 @@ public class SjProduceManageController extends BladeController {
@ApiOperationSupport(order = 1)
@Operation(summary = "烧结配套齐套流转列表", description = "传入WorkOrderVO")
public R<IPage<YieldOrderVo>> page(YieldOrderDTO yieldOrder, Query query) {
Integer moldFlag = yieldOrder.getMoldFlag();
Integer kitFlag = yieldOrder.getKitFlag();
if (moldFlag == null || kitFlag == null) {
throw new ServiceException("缺少必要参数:moldFlag、kitFlag");
}
// Integer moldFlag = yieldOrder.getMoldFlag();
// Integer kitFlag = yieldOrder.getKitFlag();
// if (moldFlag == null || kitFlag == null) {
// throw new ServiceException("缺少必要参数:moldFlag、kitFlag");
// }
LambdaQueryWrapper<YieldOrder> queryWrapper = Wrappers.lambdaQuery();
queryWrapper.eq(YieldOrder::getStatus, 10000);
queryWrapper.and(wrapper -> wrapper.eq(YieldOrder::getYieldType, 12002).or().like(YieldOrder::getMemo, "铆卡钉"));
if (moldFlag == 0) {
queryWrapper.and(wrapper -> wrapper.isNull(YieldOrder::getSjMoldPreparation).or().ne(YieldOrder::getSjMoldPreparation, YieldOrder.SJ_MOLD_PREPARATION_2));
} else if (moldFlag == 1 && kitFlag == 0) {
queryWrapper.and(wrapper -> wrapper.isNull(YieldOrder::getSjKitPreparation).ne(YieldOrder::getSjKitPreparation, YieldOrder.SJ_KIT_PREPARATION_2));
} else if (moldFlag == 1 && kitFlag == 1) {
queryWrapper.eq(YieldOrder::getSjKitPreparation, YieldOrder.SJ_KIT_PREPARATION_2);
}
// if (moldFlag == 0) {
// queryWrapper.and(wrapper -> wrapper.isNull(YieldOrder::getSjMoldPreparation).or().ne(YieldOrder::getSjMoldPreparation, YieldOrder.SJ_MOLD_PREPARATION_2));
// } else if (moldFlag == 1 && kitFlag == 0) {
// queryWrapper.and(wrapper -> wrapper.isNull(YieldOrder::getSjKitPreparation).ne(YieldOrder::getSjKitPreparation, YieldOrder.SJ_KIT_PREPARATION_2));
// } else if (moldFlag == 1 && kitFlag == 1) {
// queryWrapper.eq(YieldOrder::getSjKitPreparation, YieldOrder.SJ_KIT_PREPARATION_2);
// }
queryWrapper.like(StringUtils.isNotBlank(yieldOrder.getYpCode()), YieldOrder::getYpCode, yieldOrder.getYpCode());
queryWrapper.like(StringUtils.isNotBlank(yieldOrder.getCardNo()), YieldOrder::getCardNo, yieldOrder.getCardNo());
queryWrapper.like(StringUtils.isNotBlank(yieldOrder.getPartCode()), YieldOrder::getPartCode, yieldOrder.getPartCode());

@ -0,0 +1,28 @@
package org.springblade.desk.produce.listener;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.desk.produce.service.impl.OrderCacheService;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
/**
* @author litao
*/
@Component
@RequiredArgsConstructor
@Slf4j
public class CacheInitListener {
private final OrderCacheService orderCacheService;
@Async
@EventListener(ApplicationReadyEvent.class)
public void initCacheAfterReady() {
log.info("===== SpringBoot3 ApplicationReadyEvent 触发:开始初始化缓存 =====");
orderCacheService.initRunningOrderCache();
log.info("===== SpringBoot3 ApplicationReadyEvent 触发:缓存初始化完成 =====");
}
}

@ -37,7 +37,7 @@
mwo.OC_ID ocId,
mwp.MAKE_TEAM makeTeam,
mwp.OC_ID ppsOcId,
mwp.PPS_ID ppsId,
bpe.NAME ppsName,
mwo.SEND_DOWN_TIME sendDownTime,
mwo.WO_CODE woCode,
myo.YO_CODE yoCode,
@ -75,6 +75,7 @@
LEFT JOIN MES_YIELD_ORDER myo ON mwo.YO_ID = myo.ID
LEFT JOIN MES_WORK_PLAN mwp ON mwo.WP_ID = mwp.ID
LEFT JOIN MES_WORK_PLAN nmwp ON mwp.NEXT_WP_ID = nmwp.ID
LEFT JOIN BS_PROCESS_SET bpe ON mwp.PPS_ID = bpe.ID
<where>
mwo.is_deleted = 0
<if test="workOrder.runStatus != null and workOrder.runStatus != ''">

@ -9,10 +9,7 @@ import org.springblade.desk.produce.pojo.dto.WorkOrderDTO;
import org.springblade.desk.produce.pojo.entity.WorkOrder;
import org.springblade.desk.produce.pojo.entity.WorkOrderRun;
import org.springblade.desk.produce.pojo.entity.WorkPlanRun;
import org.springblade.desk.produce.pojo.vo.BatchPrepareVO;
import org.springblade.desk.produce.pojo.vo.QueryByReadStatusVO;
import org.springblade.desk.produce.pojo.vo.WorkOrderVO;
import org.springblade.desk.produce.pojo.vo.WorkPlanRunVO;
import org.springblade.desk.produce.pojo.vo.*;
import java.util.List;
@ -55,4 +52,8 @@ public interface IWorkOrderService extends BaseService<WorkOrder> {
boolean setReadStatus(String woIds);
WorkOrder getWorkOrderByCardNo(String cardNo);
List<WorkOrder> selectAllRunningOrder();
List<CacheWorkPlanVO> selectProcessByOrderIds(List<Long> orderIds);
}

@ -0,0 +1,173 @@
package org.springblade.desk.produce.service.impl;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.desk.produce.pojo.entity.WorkOrder;
import org.springblade.desk.produce.pojo.vo.CacheWorkOrderVO;
import org.springblade.desk.produce.pojo.vo.CacheWorkPlanVO;
import org.springblade.desk.produce.service.IWorkOrderService;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
/**
* 订单本地缓存核心服务封装缓存初始化查询更新删除统一管理缓存操作
* @author litao
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class OrderCacheService {
private final IWorkOrderService workOrderService;
// 注入Spring缓存管理器(核心新增:用于手动操作缓存)
private final CacheManager cacheManager;
// 细粒度并发锁:key=订单ID,value=对应锁对象 → 仅对同一订单加锁,不影响其他订单(高性能)
private final Map<Long, ReentrantLock> orderLockMap = new ConcurrentHashMap<>();
// 声明订单缓存实例(初始化后赋值,避免多次获取)
private Cache runningOrderCache;
// 初始化缓存实例(项目启动后执行,仅一次)
@PostConstruct
public void initCacheInstance() {
// 获取配置文件中定义的runningOrderCache缓存实例
this.runningOrderCache = cacheManager.getCache("runningOrderCache");
}
// ====================== 核心缓存注解:贴合Caffeine配置 ======================
// 缓存名称:runningOrderCache(与application.yml一致)
// 缓存Key:#orderId(订单ID,精准映射)
/**
* 从缓存查询订单优先走缓存无则查库并加载到缓存
* @Cacheable缓存不存在时执行方法体存在则直接返回缓存
*/
@Cacheable(cacheNames = "runningOrderCache", key = "#orderId", unless = "#result == null")
public CacheWorkOrderVO getOrderFromCache(Long orderId) {
// 缓存不存在时,查库加载最新数据并返回(自动存入缓存)
return loadOrderFromDB(orderId);
}
/**
* 更新缓存订单/工序修改后同步更新缓存数据库已先更新
* @CachePut无论缓存是否存在都会执行方法体并将结果更新到缓存 保证缓存与数据库一致
*/
@CachePut(cacheNames = "runningOrderCache", key = "#orderId", unless = "#result == null")
public CacheWorkOrderVO updateOrderCache(Long orderId) {
// 先加锁,防止多线程同时更新同一订单缓存
ReentrantLock lock = getOrderLock(orderId);
lock.lock();
try {
// 查库获取最新数据,更新到缓存
return loadOrderFromDB(orderId);
} finally {
// 必须释放锁,避免死锁
lock.unlock();
}
}
/**
* 删除缓存订单变为非运行中状态完成/取消从缓存移除
* @CacheEvict删除指定Key的缓存
*/
@CacheEvict(cacheNames = "runningOrderCache", key = "#orderId")
public void removeOrderCache(Long orderId) {
// 方法体可留空,注解自动完成删除
log.info("订单[{}]已变为非运行中状态,从本地缓存移除", orderId);
}
/**
* 批量删除缓存可选如批量更新订单状态时使用
*/
@CacheEvict(cacheNames = "runningOrderCache", allEntries = false)
public void removeBatchOrderCache(Long[] orderIds) {
// 注解中#orderIds为数组,自动遍历删除对应Key的缓存
}
// ====================== 启动初始化缓存:加载所有运行中订单+工序 ======================
public void initRunningOrderCache() {
log.info("开始初始化运行中订单本地缓存...");
// 1. 查库:获取所有运行中订单
List<WorkOrder> runningOrderList = workOrderService.selectAllRunningOrder();
if (CollectionUtils.isEmpty(runningOrderList)) {
log.error("无运行中订单,缓存初始化完成");
return;
}
// 2. 提取订单ID列表,批量查询所有关联工序(避免N+1)
List<Long> orderIds = runningOrderList.stream().map(WorkOrder::getId).collect(Collectors.toList());
List<CacheWorkPlanVO> allProcessList = workOrderService.selectProcessByOrderIds(orderIds);
// 工序按订单ID分组:key=订单ID,value=对应工序列表
Map<Long, List<CacheWorkPlanVO>> processGroupMap = allProcessList.stream().collect(Collectors.groupingBy(CacheWorkPlanVO::getWoId));
// 3. 组装缓存VO,批量加载到本地缓存
for (WorkOrder order : runningOrderList) {
Long orderId = order.getId();
// 加锁初始化单个订单缓存,避免与启动后的业务更新并发
ReentrantLock lock = getOrderLock(orderId);
lock.lock();
try {
CacheWorkOrderVO cacheVO = assembleOrderCacheVO(order, processGroupMap.getOrDefault(orderId, new ArrayList<>()));
// 调用updateOrderCache,利用@CachePut将数据存入缓存
// updateOrderCache(orderId);
// 手动存入缓存:直接用缓存实例put,无二次查库(核心优化)
runningOrderCache.put(orderId, cacheVO);
} finally {
lock.unlock();
}
}
log.info("运行中订单本地缓存初始化完成,共加载[{}]个订单", runningOrderList.size());
}
// ====================== 私有工具方法:抽离通用逻辑 ======================
/**
* 根据订单ID查库加载订单+关联工序的最新数据
*/
private CacheWorkOrderVO loadOrderFromDB(Long orderId) {
// 1. 查订单主信息
WorkOrder order = workOrderService.getById(orderId);
if (order == null) {
return null;
}
// 非运行中订单,直接返回null(不会存入缓存)
Integer orderStatus = order.getRunStatus();
if (orderStatus == null || !WorkOrder.RUNNING_ORDER_STATUS.contains(orderStatus)) {
return null;
}
// 2. 查关联工序
List<CacheWorkPlanVO> processList = workOrderService.selectProcessByOrderIds(Collections.singletonList(orderId));
// 3. 组装VO
return assembleOrderCacheVO(order, processList);
}
/**
* 组装订单缓存VO
*/
private CacheWorkOrderVO assembleOrderCacheVO(WorkOrder order, List<CacheWorkPlanVO> processList) {
CacheWorkOrderVO cacheVO = new CacheWorkOrderVO();
BeanUtils.copyProperties(order, cacheVO);
cacheVO.setProcessList(processList);
return cacheVO;
}
/**
* 获取订单对应的锁对象ConcurrentHashMap保证线程安全
*/
private ReentrantLock getOrderLock(Long orderId) {
// 不存在则创建新锁,存在则返回已有锁
return orderLockMap.computeIfAbsent(orderId, k -> new ReentrantLock());
}
}

@ -5,7 +5,6 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.tool.api.R;
import org.springblade.desk.produce.mapper.OrderDeclareMapper;
import org.springblade.desk.produce.pojo.dto.SaveDeclareDto;
import org.springblade.desk.produce.pojo.entity.*;

@ -460,6 +460,23 @@ public class WorkOrderServiceImpl extends BaseServiceImpl<WorkOrderMapper, WorkO
return prWorkOrder;
}
@Override
public List<WorkOrder> selectAllRunningOrder() {
return this.list(Wrappers.lambdaQuery(WorkOrder.class).eq(WorkOrder::getRunStatus, WorkOrder.RUN_STATUS_RECEIVE));
}
@Override
public List<CacheWorkPlanVO> selectProcessByOrderIds(List<Long> orderIds) {
List<CacheWorkPlanVO> workPlanList = new ArrayList<>();
List<WorkPlan> workPlans = workPlanService.list(Wrappers.lambdaQuery(WorkPlan.class).in(WorkPlan::getWoId, orderIds));
for (WorkPlan workPlan : workPlans) {
CacheWorkPlanVO cacheWorkPlanVO = new CacheWorkPlanVO();
BeanUtils.copyProperties(workPlan, cacheWorkPlanVO);
workPlanList.add(cacheWorkPlanVO);
}
return workPlanList;
}
private boolean checkWp(WorkPlan wp, Integer runType) {
//不分派工序不能操作
BsProcessSetEntity processSet = bsProcessSetService.getById(wp.getPpsId());

@ -5,6 +5,8 @@ import org.springblade.core.mp.support.BaseEntityWrapper;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.desk.produce.pojo.entity.WorkOrder;
import org.springblade.desk.produce.pojo.vo.WorkOrderVO;
import org.springblade.system.cache.UserCache;
import org.springblade.system.pojo.entity.User;
import java.util.List;
import java.util.Objects;
@ -32,6 +34,10 @@ public class WorkOrderWrapper extends BaseEntityWrapper<WorkOrder, WorkOrderVO>
public IPage<WorkOrderVO> listWorkOrderVO(IPage<WorkOrderVO> pages) {
List<WorkOrderVO> workOrderVos = pages.getRecords();
for (WorkOrderVO workOrderVo : workOrderVos) {
User user = UserCache.getUser(workOrderVo.getDispatcher());
workOrderVo.setDispatcherName(user.getRealName());
}
pages.setRecords(workOrderVos);
return pages;
}

@ -8,6 +8,14 @@ spring:
url: ${blade.datasource.dev.url}
username: ${blade.datasource.dev.username}
password: ${blade.datasource.dev.password}
# Caffeine缓存核心配置(SpringBoot3)
cache:
type: caffeine
caffeine:
# 单个缓存自定义配置(优先级最高,贴合订单缓存)
cache-names: runningOrderCache
specs:
runningOrderCache: expireAfterWrite=1h,refreshAfterWrite=30m,maximumSize=5000,initialCapacity=100
business:
oldMes:

Loading…
Cancel
Save