Merge remote-tracking branch 'origin/master'

liweidong
李涛 17 hours ago
commit 97e6c51edc
  1. 29
      blade-ops/blade-job/src/main/java/org/springblade/job/processor/dashboard/TaskAutomaticDispatchingProcessor.java
  2. 7
      blade-service-api/blade-desk-api/src/main/java/org/springblade/desk/dashboard/feign/IPartClient.java
  3. 62
      blade-service-api/blade-desk-api/src/main/java/org/springblade/desk/dashboard/pojo/entity/DsDispatchPointerEntity.java
  4. 3
      blade-service/blade-desk/src/main/java/org/springblade/desk/basic/mapper/PlatingMapper.java
  5. 5
      blade-service/blade-desk/src/main/java/org/springblade/desk/basic/mapper/PlatingMapper.xml
  6. 7
      blade-service/blade-desk/src/main/java/org/springblade/desk/basic/service/IPlatingService.java
  7. 4
      blade-service/blade-desk/src/main/java/org/springblade/desk/basic/service/impl/PlatingServiceImpl.java
  8. 4
      blade-service/blade-desk/src/main/java/org/springblade/desk/dashboard/controller/DsTaskingController.java
  9. 5
      blade-service/blade-desk/src/main/java/org/springblade/desk/dashboard/feign/PartClient.java
  10. 10
      blade-service/blade-desk/src/main/java/org/springblade/desk/dashboard/mapper/DispatchPointerMapper.xml
  11. 42
      blade-service/blade-desk/src/main/java/org/springblade/desk/dashboard/mapper/DsDispatchPointerMapper.java
  12. 3
      blade-service/blade-desk/src/main/java/org/springblade/desk/dashboard/mapper/DsTaskingMapper.java
  13. 6
      blade-service/blade-desk/src/main/java/org/springblade/desk/dashboard/mapper/TaskingMapper.xml
  14. 46
      blade-service/blade-desk/src/main/java/org/springblade/desk/dashboard/service/IDsDispatchPointerService.java
  15. 4
      blade-service/blade-desk/src/main/java/org/springblade/desk/dashboard/service/IDsTaskingService.java
  16. 69
      blade-service/blade-desk/src/main/java/org/springblade/desk/dashboard/service/impl/DsDispatchPointerServiceImpl.java
  17. 168
      blade-service/blade-desk/src/main/java/org/springblade/desk/dashboard/service/impl/DsTaskingServiceImpl.java

@ -0,0 +1,29 @@
package org.springblade.job.processor.dashboard;
import jakarta.annotation.Resource;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springblade.desk.dashboard.feign.IPartClient;
import org.springframework.stereotype.Component;
import tech.powerjob.worker.core.processor.ProcessResult;
import tech.powerjob.worker.core.processor.TaskContext;
import tech.powerjob.worker.core.processor.sdk.BasicProcessor;
/**
* 工艺模块-返工任务
* @author liweidong
*/
@Component
@Data
@Slf4j
public class TaskAutomaticDispatchingProcessor implements BasicProcessor {
@Resource
private IPartClient client;
@Override
public ProcessResult process(TaskContext context) throws Exception {
client.automaticDispatching();
return new ProcessResult(true);
}
}

@ -26,6 +26,7 @@ public interface IPartClient {
String BATCH_PARTS = API_PREFIX + "/batchParts";
String REWORK_TASK = API_PREFIX + "/reworkTask";
String SYNC_TASKING = API_PREFIX + "/syncTasking";
String AUTOMATIC_DISPATCHING = API_PREFIX + "/automaticDispatching";
/**
* 获取零件信息
@ -70,4 +71,10 @@ public interface IPartClient {
*/
@GetMapping(SYNC_TASKING)
void syncTasking();
/**
* 任务分派
*/
@GetMapping(AUTOMATIC_DISPATCHING)
void automaticDispatching();
}

@ -0,0 +1,62 @@
package org.springblade.desk.dashboard.pojo.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springblade.core.mp.base.BaseEntity;
import java.io.Serial;
/**
* 任务分派轮循指针表 实体类
*
* @author BladeX
* @since 2025-12-03
*/
@Data
@TableName("DS_DISPATCH_POINTER")
@Schema(description = "DsDispatchPointer对象")
@EqualsAndHashCode(callSuper = true)
public class DsDispatchPointerEntity extends BaseEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 镀种分类ID
*/
@Schema(description = "镀种分类ID")
private Long platingAssortmentId;
/**
* 指针类型NORMAL-普通任务NEW_DRAWING-新图任务
*/
@Schema(description = "指针类型:NORMAL-普通任务,NEW_DRAWING-新图任务")
private String pointerType;
/**
* 上次分派的人员队列下标-1表示未开始
*/
@Schema(description = "上次分派的人员队列下标,-1表示未开始")
private Integer lastIndex;
/**
* 乐观锁版本号
*/
@Schema(description = "乐观锁版本号")
private Integer version;
/**
* 备注
*/
@Schema(description = "备注")
private String remarks;
/**
* 状态1-正常0-禁用
*/
@Schema(description = "状态:1-正常,0-禁用")
private Integer status;
}

@ -39,5 +39,6 @@ public interface PlatingMapper extends BaseMapper<Plating> {
*/
List<PlatingExcel> exportPlating(@Param("ew") Wrapper<Plating> queryWrapper);
Plating selectBsPlatingByPlating(@Param("plating") String plating);
List<Plating> selectBsPlatingByConfigNo(@Param("configCode") String configCode);
}

@ -28,8 +28,9 @@
<select id="exportPlating" resultType="org.springblade.desk.basic.excel.PlatingExcel">
SELECT * FROM BA_PLATING ${ew.customSqlSegment}
</select>
<select id="selectBsPlatingByPlating" resultType="org.springblade.desk.basic.pojo.entity.Plating">
SELECT * FROM BA_PLATING WHERE is_deleted = 0 and PLATING = #{plating}
<select id="selectBsPlatingByConfigNo" resultType="org.springblade.desk.basic.pojo.entity.Plating">
SELECT * FROM BS_PLATING WHERE is_deleted = 0 and CONFIG_NO = #{configCode}
</select>

@ -44,9 +44,10 @@ public interface IPlatingService extends BaseService<Plating> {
void setVOValue(PlatingVO vo);
/**
* 根据镀种查询
* @param plate
* 根据配置码查询
* @param configCode
* @return
*/
Plating selectBsPlatingByPlating(String plate);
List<Plating> selectBsPlatingByConfigNo(String configCode);
}

@ -61,7 +61,7 @@ public class PlatingServiceImpl extends BaseServiceImpl<PlatingMapper, Plating>
}
@Override
public Plating selectBsPlatingByPlating(String plating) {
return baseMapper.selectBsPlatingByPlating(plating);
public List<Plating> selectBsPlatingByConfigNo(String configCode) {
return baseMapper.selectBsPlatingByConfigNo(configCode);
}
}

@ -405,8 +405,8 @@ public class DsTaskingController extends BladeController {
@GetMapping("/automaticDispatching")
@ApiOperationSupport(order = 1)
@Operation(summary = "工艺任务-自动分派", description = "taskingId")
public R automaticDispatching(@RequestParam String taskingId) {
boolean b = dsTaskingService.automaticDispatching(taskingId);
public R automaticDispatching() {
boolean b = dsTaskingService.automaticDispatching();
return R.data(b);
}

@ -50,5 +50,10 @@ public class PartClient implements IPartClient {
public void syncTasking() {
taskingService.syncTasking();
}
@Override
public void automaticDispatching() {
taskingService.automaticDispatching();
}
}

@ -0,0 +1,10 @@
<?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="org.springblade.desk.dashboard.mapper.DsDispatchPointerMapper">
<select id="getOrCreatePointer"
resultType="org.springblade.desk.dashboard.pojo.entity.DsDispatchPointerEntity">
select * from ds_dispatch_pointer where IS_DELETED = 0 and PLATING_ASSORTMENT_ID =#{platingAssortmentId} and POINTER_TYPE =#{pointerType}
</select>
</mapper>

@ -0,0 +1,42 @@
/**
* 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.dashboard.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.springblade.desk.dashboard.pojo.entity.DsDispatchPointerEntity;
/**
* 工艺能力表 Mapper 接口
*
* @author BladeX
* @since 2025-11-12
*/
public interface DsDispatchPointerMapper extends BaseMapper<DsDispatchPointerEntity> {
DsDispatchPointerEntity getOrCreatePointer(@Param("platingAssortmentId") Long platingAssortmentId, @Param("pointerType")String pointerType);
}

@ -144,4 +144,7 @@ public interface DsTaskingMapper extends BaseMapper<DsTaskingEntity> {
List<DsTaskingEntity> selectTaskingByPartCode(@Param("partCode") String partCode,
@Param("taskStatus") Integer taskStatus,
@Param("taskType")String taskType);
List<DsTaskingEntity> selectTaskingByTaskStatus(@Param("taskTatus") Integer taskTatus);
}

@ -165,4 +165,10 @@
AND TASK_TYPE = #{taskType}
</select>
<select id="selectTaskingByTaskStatus"
resultType="org.springblade.desk.dashboard.pojo.entity.DsTaskingEntity">
SELECT * FROM DS_TASKING
WHERE IS_DELETED = 0 and TASK_STATUS = #{taskTatus}
</select>
</mapper>

@ -0,0 +1,46 @@
/**
* 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.dashboard.service;
import org.springblade.core.mp.base.BaseService;
import org.springblade.desk.dashboard.pojo.entity.DsDispatchPointerEntity;
/**
* 自动分派 服务类
*
* @author BladeX
* @since 2025-11-12
*/
public interface IDsDispatchPointerService extends BaseService<DsDispatchPointerEntity> {
/**
* 根据镀种分类ID和任务类型查询
* @param platingAssortmentId
* @param pointerType
* @return
*/
DsDispatchPointerEntity getOrCreatePointer(Long platingAssortmentId, String pointerType);
}

@ -247,8 +247,8 @@ public interface IDsTaskingService extends BaseService<DsTaskingEntity> {
/**
* 自动分派
* @param taskingId
* @param
* @return
*/
boolean automaticDispatching(String taskingId);
boolean automaticDispatching();
}

@ -0,0 +1,69 @@
/**
* 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.dashboard.service.impl;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.desk.dashboard.mapper.DsDispatchPointerMapper;
import org.springblade.desk.dashboard.pojo.entity.DsDispatchPointerEntity;
import org.springblade.desk.dashboard.service.IDsDispatchPointerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
/**
* 自动分派 服务实现类
*
* @author BladeX
* @since 2025-11-12
*/
@Service
public class DsDispatchPointerServiceImpl extends BaseServiceImpl<DsDispatchPointerMapper, DsDispatchPointerEntity> implements IDsDispatchPointerService {
@Autowired
DsDispatchPointerMapper dispatchPointerMapper;
@Override
public DsDispatchPointerEntity getOrCreatePointer(Long platingAssortmentId, String pointerType) {
DsDispatchPointerEntity pointer = dispatchPointerMapper.getOrCreatePointer(platingAssortmentId, pointerType);
// 2. 不存在则创建
if (pointer == null) {
pointer = new DsDispatchPointerEntity();
pointer.setPlatingAssortmentId(platingAssortmentId);
pointer.setPointerType(pointerType);
pointer.setLastIndex(-1);
pointer.setVersion(0);
pointer.setStatus(1);
pointer.setIsDeleted(0);
pointer.setCreateTime(new Date());
// 保存到数据库
save(pointer);
}
return pointer;
}
}

@ -27,6 +27,7 @@ package org.springblade.desk.dashboard.service.impl;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
@ -39,10 +40,8 @@ import org.springblade.core.tool.utils.SpringUtil;
import org.springblade.desk.basic.pojo.entity.BasicClazz;
import org.springblade.desk.basic.pojo.entity.LocallyPlatedPart;
import org.springblade.desk.basic.pojo.entity.Plating;
import org.springblade.desk.basic.service.IBasicClazzService;
import org.springblade.desk.basic.service.ICraftAbilityService;
import org.springblade.desk.basic.service.ILocallyPlatedPartService;
import org.springblade.desk.basic.service.IPlatingService;
import org.springblade.desk.basic.pojo.entity.PlatingAssortment;
import org.springblade.desk.basic.service.*;
import org.springblade.desk.common.constant.BizTypeConstant;
import org.springblade.desk.common.service.IMesNotifyMessageService;
import org.springblade.desk.dashboard.constant.DsPartConstant;
@ -187,8 +186,10 @@ public class DsTaskingServiceImpl extends BaseServiceImpl<DsTaskingMapper, DsTas
IMeasurementRecordsService measurementRecordsService;
@Autowired
IBasicClazzService basicClazzService;
IPlatingAssortmentService platingAssortmentService;
@Autowired
IDsDispatchPointerService dsDispatchPointerService;
@Override
public IPage<DsTaskingVO> selectAssignList(IPage<DsTaskingVO> page, DsTaskingVO dsTasking) {
@ -1859,29 +1860,146 @@ public class DsTaskingServiceImpl extends BaseServiceImpl<DsTaskingMapper, DsTas
}
@Override
public boolean automaticDispatching(String taskingId) {
DsTaskingEntity tasking = this.getById(taskingId);
if(tasking == null){
return false;
}
//获取镀种
if(StringUtils.isEmpty(tasking.getPlate())){
return false;
public boolean automaticDispatching() {
//查询所有未分派任务
List<DsTaskingEntity> dsTaskingList = taskingMapper.selectTaskingByTaskStatus(TaskingConstant.TASK_STATUS_WAIT);
if(!CollectionUtils.isEmpty(dsTaskingList)){
for (DsTaskingEntity tasking : dsTaskingList) {
// 根据零件配置码查询镀种分类
List<DsPartEntity> dsPartEntityList = partService.selectDsPartByPatCode(tasking.getPartCode());
if (CollectionUtils.isEmpty(dsPartEntityList)) {
continue;
}
DsPartEntity part = dsPartEntityList.get(0);
String configCode = part.getConfigCode();
if(StringUtils.isEmpty(configCode)){
continue;
}
List<Plating> platingList = platingService.selectBsPlatingByConfigNo(configCode);
if(CollectionUtils.isEmpty(platingList)){
log.warn("未找到镀种分类,零件配置码: {}", configCode);
continue;
}
Plating plating = platingList.get(0);
if ( plating.getPlatingAssortmentId() == null) {
log.warn("未找到镀种分类,镀种: {}", tasking.getPlate());
continue;
}
PlatingAssortment platingAssortment = platingAssortmentService.getById(plating.getPlatingAssortmentId());
if (platingAssortment == null) {
log.warn("镀种分类不存在: {}", plating.getPlatingAssortmentId());
continue;
}
// 获取镀种分类对应的人员
String teamId = platingAssortment.getTeamMemberId();
List<User> userList = new ArrayList<>();
if (!StringUtils.isEmpty(teamId)) {
userList = userClient.userListByIds(teamId);
userList.sort(Comparator.comparing(User::getCreateTime, Comparator.nullsLast(Comparator.naturalOrder())));
}
if (userList.isEmpty()) {
log.warn("镀种分类[{}]下没有可分配的人员", platingAssortment.getName());
continue;
}
// 判断指针类型(烧结新图任务使用独立指针)
String pointerType = "NORMAL";
if (isSinteringNewDrawingTask(tasking,part)) {
pointerType = "NEW_DRAWING";
}
// 获取或创建指针
DsDispatchPointerEntity pointer = dsDispatchPointerService.getOrCreatePointer(
platingAssortment.getId(), pointerType);
// 边界处理:如果上次下标超出当前列表长度
int lastIndex = pointer.getLastIndex();
if (lastIndex >= userList.size()) {
lastIndex = userList.size() - 1;
pointer.setLastIndex(lastIndex);
dsDispatchPointerService.updateById(pointer);
}
// ========== 带重试的分派逻辑 ==========
int maxRetries = userList.size();
int currentIndex = (lastIndex + 1) % userList.size();
User assignedUser = null;
int successIndex = -1;
boolean assignedSuccess = false;
for (int retryCount = 0; retryCount < maxRetries; retryCount++) {
User candidate = userList.get(currentIndex);
try {
if (this.assignTechnician(tasking.getId().toString(), candidate.getId().toString(), null)) {
assignedSuccess = true;
assignedUser = candidate;
successIndex = currentIndex;
break;
}
} catch (Exception e) {
log.error("分派任务给员工[{}]异常", candidate.getId(), e);
}
log.warn("分派任务[{}]给人员[{}]失败,尝试下一个人", tasking.getId(), candidate.getName());
currentIndex = (currentIndex + 1) % userList.size();
}
if (!assignedSuccess) {
log.error("任务[{}]分派失败,所有可分配人员都无法接收", tasking.getId());
continue;
}
// ========== 重试逻辑结束 ==========
// 更新指针(带乐观锁重试3次)
boolean updateSuccess = false;
for (int retryCount = 0; retryCount < 3; retryCount++) {
DsDispatchPointerEntity latestPointer = dsDispatchPointerService.getById(pointer.getId());
boolean success = dsDispatchPointerService.lambdaUpdate()
.eq(DsDispatchPointerEntity::getId, pointer.getId())
.eq(DsDispatchPointerEntity::getVersion, latestPointer.getVersion())
.set(DsDispatchPointerEntity::getLastIndex, successIndex)
.set(DsDispatchPointerEntity::getVersion, latestPointer.getVersion() + 1)
.set(DsDispatchPointerEntity::getUpdateTime, new Date())
.update();
if (success) {
updateSuccess = true;
break;
}
log.warn("更新分派指针失败,第{}次重试", retryCount + 1);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
if (!updateSuccess) {
log.warn("更新分派指针失败,但任务已分派给[{}],下次分派可能重复", assignedUser.getName());
}
log.info("任务[{}]自动分派成功,工艺员:{},类型:{},镀种分类:{}",
tasking.getId(), assignedUser.getName(), pointerType, platingAssortment.getName());
}
}
//根据镀种查询镀种分类
// Plating plating = platingService.selectBsPlatingByPlating(tasking.getPlate());
// if(plating == null && plating.getBcId() == null){
// return false;
// }
//
// BasicClazz basicClazz = basicClazzService.getById(plating.getBcId());
// if(basicClazz == null){
// return false;
// }
//获取镀种分类对应的人员
return true;
}
return false;
/**
* 判断是否为烧结新图任务
*/
private boolean isSinteringNewDrawingTask(DsTaskingEntity tasking,DsPartEntity part) {
return TaskingConstant.IS_SINTERING.equals(part.getIsSintering())
&& TaskingConstant.NEW_MAP_TASKS.equals(tasking.getTaskType());
}
/**

Loading…
Cancel
Save