代码暂存
This commit is contained in:
parent
c8ff345c9f
commit
6d0965f3f3
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.wms.constants.enums;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 入库验证信息枚举
|
||||||
|
*/
|
||||||
|
public enum TaskOutValidationEnum {
|
||||||
|
OK(""),
|
||||||
|
NO_REQUEST_BODY("请求参数为空"),
|
||||||
|
LACK_REQUIRED_PARAM("缺少必须参数:物料、载具、起始库位至少需要一个"),
|
||||||
|
ERROR_VEHICLE_ID("不存在的载具号"),
|
||||||
|
ERROR_ORIGIN_POINT("错误的起始库位"),
|
||||||
|
ERROR_DESTINATION_POINT("错误的终点站台"),
|
||||||
|
NO_PICK_STAND("拣选任务无拣选站台"),
|
||||||
|
ERROR_PICK_STAND("错误的拣选站台"),
|
||||||
|
NO_STOCK("请求的物料无库存"),
|
||||||
|
ERROR_GOODS_NUM("错误的出库数量");
|
||||||
|
|
||||||
|
private final String errorMessage;
|
||||||
|
TaskOutValidationEnum(String errorMessage) {
|
||||||
|
this.errorMessage = errorMessage;
|
||||||
|
}
|
||||||
|
public String getErrorMessage() {
|
||||||
|
return errorMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,14 +1,23 @@
|
||||||
package com.wms.controller;
|
package com.wms.controller;
|
||||||
|
|
||||||
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
import com.wms.annotation.MyLog;
|
import com.wms.annotation.MyLog;
|
||||||
import com.wms.constants.enums.*;
|
import com.wms.constants.enums.*;
|
||||||
import com.wms.entity.app.*;
|
import com.wms.entity.app.*;
|
||||||
|
import com.wms.entity.app.dto.extend.TaskDetailInfo;
|
||||||
import com.wms.entity.app.request.GoodsInRequest;
|
import com.wms.entity.app.request.GoodsInRequest;
|
||||||
import com.wms.entity.app.request.TaskInRequest;
|
import com.wms.entity.app.request.TaskInRequest;
|
||||||
|
import com.wms.entity.app.request.TaskOutRequest;
|
||||||
|
import com.wms.entity.table.Location;
|
||||||
|
import com.wms.entity.table.Stock;
|
||||||
|
import com.wms.entity.table.Task;
|
||||||
|
import com.wms.entity.table.Vehicle;
|
||||||
import com.wms.service.*;
|
import com.wms.service.*;
|
||||||
import com.wms.utils.HttpUtils;
|
import com.wms.utils.HttpUtils;
|
||||||
import com.wms.utils.StringUtils;
|
import com.wms.utils.StringUtils;
|
||||||
|
import com.wms.utils.WmsUtils;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
@ -22,10 +31,12 @@ import org.springframework.transaction.interceptor.TransactionAspectSupport;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.Objects;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
import static com.wms.config.InitLocalConfig.configMap;
|
import static com.wms.config.InitLocalConfig.configMap;
|
||||||
import static com.wms.utils.StringUtils.convertJsonString;
|
import static com.wms.utils.StringUtils.convertJsonString;
|
||||||
|
import static com.wms.utils.WmsUtils.generateId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WMS任务控制类
|
* WMS任务控制类
|
||||||
|
|
@ -62,7 +73,11 @@ public class TaskController {
|
||||||
/**
|
/**
|
||||||
* 物料服务
|
* 物料服务
|
||||||
*/
|
*/
|
||||||
private GoodsService goodsService;
|
private final GoodsService goodsService;
|
||||||
|
/**
|
||||||
|
* 载具服务
|
||||||
|
*/
|
||||||
|
private final VehicleService vehicleService;
|
||||||
/**
|
/**
|
||||||
* 请求头部信息
|
* 请求头部信息
|
||||||
*/
|
*/
|
||||||
|
|
@ -95,12 +110,39 @@ public class TaskController {
|
||||||
response.setMessage("入库请求验证错误!" + validationInfo);
|
response.setMessage("入库请求验证错误!" + validationInfo);
|
||||||
return convertJsonString(response);
|
return convertJsonString(response);
|
||||||
}
|
}
|
||||||
// 请求可用库位---悲观锁
|
Map<String, String> nextLocationMap = new HashMap<>();
|
||||||
|
// 请求可用库位
|
||||||
|
for (int i = 0; i < locationService.count(new LambdaQueryWrapper<Location>().eq(Location::getLocationStatus, LocationStatus.EMPTY.getCode())); i++) {
|
||||||
|
nextLocationMap = locationService.getOneLocation(taskInRequest.getOriginPoint(), null);
|
||||||
|
if (nextLocationMap == null || nextLocationMap.isEmpty() || !nextLocationMap.containsKey("nextLocationId")) {
|
||||||
|
logger.error("暂无可用库位");
|
||||||
|
response.setCode(ResponseCode.ERROR.getCode());
|
||||||
|
response.setMessage("入库请求验证错误!" + validationInfo);
|
||||||
|
return convertJsonString(response);
|
||||||
|
} else {
|
||||||
|
Location nextLocation = locationService.getOne(new LambdaQueryWrapper<Location>().eq(Location::getLocationId, nextLocationMap.get("nextLocationId")));
|
||||||
|
LambdaUpdateWrapper<Location> updateLocationWrapper = new LambdaUpdateWrapper<Location>()
|
||||||
|
.set(Location::getLocationStatus, LocationStatus.OCCUPY.getCode())
|
||||||
|
.eq(Location::getLocationId, nextLocation.getLocationId())
|
||||||
|
.eq(Location::getLocationStatus, LocationStatus.EMPTY.getCode());
|
||||||
|
if (locationService.update(updateLocationWrapper)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// 生成入库任务
|
// 生成入库任务
|
||||||
|
String saveTaskResult = "";
|
||||||
if (taskInRequest.getGoodsList().isEmpty()) {// 空托入库
|
if (taskInRequest.getGoodsList().isEmpty()) {// 空托入库
|
||||||
|
saveTaskResult = genEmptyInTask(taskInRequest, nextLocationMap.get("nextLocationId"), nextLocationMap.get("preTaskId"));
|
||||||
} else {// 带料入库
|
} else {// 带料入库
|
||||||
|
saveTaskResult = genGoodsInTask(taskInRequest, nextLocationMap.get("nextLocationId"), nextLocationMap.get("preTaskId"));
|
||||||
|
}
|
||||||
|
if (!saveTaskResult.equals("")) {
|
||||||
|
// 回退库位锁定
|
||||||
|
locationService.update(new LambdaUpdateWrapper<Location>()
|
||||||
|
.set(Location::getLocationStatus, LocationStatus.EMPTY.getCode())
|
||||||
|
.eq(Location::getLocationId, nextLocationMap.get("nextLocationId"))
|
||||||
|
.eq(Location::getLocationStatus, LocationStatus.OCCUPY.getCode()));
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("接收入库请求成功!");
|
logger.info("接收入库请求成功!");
|
||||||
|
|
@ -118,6 +160,87 @@ public class TaskController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加空入库任务
|
||||||
|
* @param taskInRequest 入库请求
|
||||||
|
* @param locationId 库位id
|
||||||
|
* @param preTaskId 前置任务
|
||||||
|
* @return 添加结果
|
||||||
|
*/
|
||||||
|
private String genEmptyInTask (TaskInRequest taskInRequest, String locationId, String preTaskId) {
|
||||||
|
String result = "";
|
||||||
|
Task tempInTask = new Task();
|
||||||
|
tempInTask.setTaskId(generateId("RK_"));
|
||||||
|
tempInTask.setTaskType(TaskType.IN.getCode());
|
||||||
|
tempInTask.setTaskStatus(WmsTaskStatus.NEW.getCode());
|
||||||
|
tempInTask.setTaskGroup(generateId(""));
|
||||||
|
tempInTask.setTaskPriority(1);
|
||||||
|
tempInTask.setVehicleId(taskInRequest.getVehicleId());
|
||||||
|
tempInTask.setOrigin(taskInRequest.getOriginPoint());
|
||||||
|
tempInTask.setDestination(locationId);
|
||||||
|
tempInTask.setVehicleSize(1);
|
||||||
|
tempInTask.setWeight(taskInRequest.getTotalWeight());
|
||||||
|
tempInTask.setCreateTime(LocalDateTime.now());
|
||||||
|
tempInTask.setUserName(taskInRequest.getUserName());
|
||||||
|
tempInTask.setPreTask(preTaskId);
|
||||||
|
try {
|
||||||
|
if (!taskService.save(tempInTask)) {
|
||||||
|
return "添加空入库任务失败";
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("添加空入库任务异常:{}", convertJsonString(e));
|
||||||
|
return "添加空入库任务失败";
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加入库任务
|
||||||
|
* @param taskInRequest 入库请求
|
||||||
|
* @param locationId 库位号
|
||||||
|
* @param preTaskId 前置任务
|
||||||
|
* @return 添加结果
|
||||||
|
*/
|
||||||
|
private String genGoodsInTask (TaskInRequest taskInRequest, String locationId, String preTaskId) {
|
||||||
|
String result = "";
|
||||||
|
String taskGroupId = generateId("");
|
||||||
|
List<Task> tempTasks = new ArrayList<>();
|
||||||
|
for (GoodsInRequest goodsInRequest : taskInRequest.getGoodsList()) {
|
||||||
|
Task tempInTask = new Task();
|
||||||
|
tempInTask.setTaskId(generateId("RK_"));
|
||||||
|
tempInTask.setTaskType(TaskType.IN.getCode());
|
||||||
|
tempInTask.setTaskStatus(WmsTaskStatus.NEW.getCode());
|
||||||
|
tempInTask.setTaskGroup(taskGroupId);
|
||||||
|
tempInTask.setTaskPriority(1);
|
||||||
|
tempInTask.setVehicleId(taskInRequest.getVehicleId());
|
||||||
|
tempInTask.setOrigin(taskInRequest.getOriginPoint());
|
||||||
|
tempInTask.setDestination(locationId);
|
||||||
|
tempInTask.setVehicleSize(1);
|
||||||
|
tempInTask.setWeight(taskInRequest.getTotalWeight());
|
||||||
|
tempInTask.setCreateTime(LocalDateTime.now());
|
||||||
|
tempInTask.setUserName(taskInRequest.getUserName());
|
||||||
|
tempInTask.setPreTask(preTaskId);
|
||||||
|
// 物料相关信息
|
||||||
|
TaskDetailInfo goodsRelatedInfo = new TaskDetailInfo();
|
||||||
|
// TODO 物料信息需要完善
|
||||||
|
goodsRelatedInfo.setGoodsId(goodsInRequest.getGoodsId());
|
||||||
|
goodsRelatedInfo.setGoodsName("");
|
||||||
|
tempInTask.setGoodsRelated(goodsRelatedInfo);
|
||||||
|
|
||||||
|
tempTasks.add(tempInTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!taskService.saveBatch(tempTasks)) {
|
||||||
|
return "添加入库任务失败";
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("添加入库任务异常:{}", convertJsonString(e));
|
||||||
|
return "添加入库任务失败";
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 验证入库请求
|
* 验证入库请求
|
||||||
* @param taskInRequest 入库请求
|
* @param taskInRequest 入库请求
|
||||||
|
|
@ -133,7 +256,7 @@ public class TaskController {
|
||||||
return TaskInValidationEnum.NO_VEHICLE_ID.getErrorMessage();
|
return TaskInValidationEnum.NO_VEHICLE_ID.getErrorMessage();
|
||||||
}
|
}
|
||||||
// 验证载具号是否重复入库
|
// 验证载具号是否重复入库
|
||||||
|
// TODO
|
||||||
// 验证重量
|
// 验证重量
|
||||||
if (taskInRequest.getTotalWeight() == null) {
|
if (taskInRequest.getTotalWeight() == null) {
|
||||||
return TaskInValidationEnum.NO_WEIGHT.getErrorMessage();
|
return TaskInValidationEnum.NO_WEIGHT.getErrorMessage();
|
||||||
|
|
@ -175,4 +298,116 @@ public class TaskController {
|
||||||
}
|
}
|
||||||
return TaskInValidationEnum.OK.getErrorMessage();
|
return TaskInValidationEnum.OK.getErrorMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接收出库任务请求
|
||||||
|
*
|
||||||
|
* @param taskOutRequest 出库任务
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@PostMapping("/requestOut")
|
||||||
|
@ResponseBody
|
||||||
|
@Transactional(isolation = Isolation.REPEATABLE_READ, propagation = Propagation.REQUIRED)
|
||||||
|
@MyLog(logTitle = "出库请求", logMethod = "requestOut")
|
||||||
|
public String receiveTaskOutRequest(@RequestBody TaskOutRequest taskOutRequest) {
|
||||||
|
logger.info("接收到出库请求:{},ip地址:{}", JSON.toJSONString(taskOutRequest), HttpUtils.getIpAddr(servletRequest));
|
||||||
|
// 创建响应信息
|
||||||
|
ResponseEntity response = new ResponseEntity();
|
||||||
|
try {
|
||||||
|
// 验证出库请求
|
||||||
|
String validationInfo = validateTaskOutRequest(taskOutRequest);
|
||||||
|
if (!Objects.equals(validationInfo, TaskInValidationEnum.OK.getErrorMessage())) {
|
||||||
|
logger.error("出库请求验证错误!{}", validationInfo);
|
||||||
|
response.setCode(ResponseCode.ERROR.getCode());
|
||||||
|
response.setMessage("出库请求验证错误!" + validationInfo);
|
||||||
|
return convertJsonString(response);
|
||||||
|
}
|
||||||
|
// 没有物料,单纯载具或者库位出库
|
||||||
|
if (StringUtils.isEmpty(taskOutRequest.getGoodsId())) {
|
||||||
|
// 生成载具出库任务
|
||||||
|
|
||||||
|
}
|
||||||
|
// 查询库存
|
||||||
|
LambdaQueryWrapper<Stock> stockLambdaQueryWrapper = new LambdaQueryWrapper<Stock>()
|
||||||
|
.eq(Stock::getGoodsId, taskOutRequest.getGoodsId());
|
||||||
|
// 生成出库任务
|
||||||
|
|
||||||
|
logger.info("接收入库请求成功!");
|
||||||
|
response.setCode(ResponseCode.OK.getCode());
|
||||||
|
response.setMessage("接收入库请求成功!");
|
||||||
|
return convertJsonString(response);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 回滚事务
|
||||||
|
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
|
||||||
|
logger.error("发生异常:{}", e.getMessage());
|
||||||
|
// 返回其他异常
|
||||||
|
response.setCode(ResponseCode.ERROR.getCode());
|
||||||
|
response.setMessage("发生意料之外的错误");
|
||||||
|
return convertJsonString(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成载具或库位出库任务
|
||||||
|
* @param taskOutRequest 出库请求
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
private String genVehicleLocationOutTask(TaskOutRequest taskOutRequest) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证出库请求
|
||||||
|
* @param taskOutRequest 出库请求
|
||||||
|
* @return 验证结果---空为验证通过,否则输出错误信息
|
||||||
|
*/
|
||||||
|
private String validateTaskOutRequest(TaskOutRequest taskOutRequest) {
|
||||||
|
// 验证任务
|
||||||
|
if (taskOutRequest == null) {
|
||||||
|
return TaskOutValidationEnum.NO_REQUEST_BODY.getErrorMessage();
|
||||||
|
}
|
||||||
|
// 验证是否包含请求信息
|
||||||
|
if (StringUtils.isEmpty(taskOutRequest.getGoodsId())
|
||||||
|
&& StringUtils.isEmpty(taskOutRequest.getVehicleId())
|
||||||
|
&& StringUtils.isEmpty(taskOutRequest.getOriginPoint())) {
|
||||||
|
return TaskOutValidationEnum.LACK_REQUIRED_PARAM.getErrorMessage();
|
||||||
|
}
|
||||||
|
// 验证载具号
|
||||||
|
if (StringUtils.isNotEmpty(taskOutRequest.getVehicleId())
|
||||||
|
&& vehicleService.exists(new LambdaQueryWrapper<Vehicle>().eq(Vehicle::getVehicleId, taskOutRequest.getVehicleId()))) {
|
||||||
|
return TaskOutValidationEnum.ERROR_VEHICLE_ID.getErrorMessage();
|
||||||
|
}
|
||||||
|
// 验证库位
|
||||||
|
if (StringUtils.isNotEmpty(taskOutRequest.getOriginPoint())
|
||||||
|
&& locationService.exists(new LambdaQueryWrapper<Location>().eq(Location::getLocationId, taskOutRequest.getOriginPoint()))) {
|
||||||
|
return TaskOutValidationEnum.ERROR_ORIGIN_POINT.getErrorMessage();
|
||||||
|
}
|
||||||
|
// 验证终点站台
|
||||||
|
if (taskOutRequest.getIsPicking() == 0) {// 出库
|
||||||
|
if (standService.validateStand(taskOutRequest.getDestinationPoint(), 2)) {
|
||||||
|
return TaskOutValidationEnum.ERROR_DESTINATION_POINT.getErrorMessage();
|
||||||
|
}
|
||||||
|
} else {// 拣选出库
|
||||||
|
if (standService.validateStand(taskOutRequest.getDestinationPoint(), 3)) {
|
||||||
|
return TaskOutValidationEnum.ERROR_PICK_STAND.getErrorMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 验证物料信息
|
||||||
|
if (StringUtils.isNotEmpty(taskOutRequest.getGoodsId())) {
|
||||||
|
if (taskOutRequest.getGoodsNum() == null || taskOutRequest.getGoodsNum().compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
return TaskOutValidationEnum.ERROR_GOODS_NUM.getErrorMessage();
|
||||||
|
}
|
||||||
|
// 查询库存信息
|
||||||
|
LambdaQueryWrapper<Stock> stockQueryWrapper = new LambdaQueryWrapper<Stock>()
|
||||||
|
.apply("goods_related -> '$.goodsId' = {0}", taskOutRequest.getGoodsId())
|
||||||
|
.apply("goods_related -> '$.remainNum' > 0")
|
||||||
|
.eq(StringUtils.isNotEmpty(taskOutRequest.getVehicleId()), Stock::getVehicleId, taskOutRequest.getVehicleId())
|
||||||
|
.eq(StringUtils.isNotEmpty(taskOutRequest.getOriginPoint()), Stock::getLocationId, taskOutRequest.getOriginPoint());
|
||||||
|
if (!stockService.exists(stockQueryWrapper)) {
|
||||||
|
return TaskOutValidationEnum.NO_STOCK.getErrorMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TaskOutValidationEnum.OK.getErrorMessage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -67,4 +67,16 @@ public class TaskDto {
|
||||||
* 物料相关信息
|
* 物料相关信息
|
||||||
*/
|
*/
|
||||||
private TaskDetailInfo goodsRelated;
|
private TaskDetailInfo goodsRelated;
|
||||||
|
/**
|
||||||
|
* 前置任务号
|
||||||
|
*/
|
||||||
|
private String preTask;
|
||||||
|
/**
|
||||||
|
* 是否拣选
|
||||||
|
*/
|
||||||
|
private Integer isPicking;
|
||||||
|
/**
|
||||||
|
* 拣选站台
|
||||||
|
*/
|
||||||
|
private String pickStand;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,4 +64,16 @@ public class TaskRecordDto {
|
||||||
* 物料相关
|
* 物料相关
|
||||||
*/
|
*/
|
||||||
private TaskDetailInfo goodsRelated;
|
private TaskDetailInfo goodsRelated;
|
||||||
|
/**
|
||||||
|
* 前置任务号
|
||||||
|
*/
|
||||||
|
private String preTask;
|
||||||
|
/**
|
||||||
|
* 是否拣选
|
||||||
|
*/
|
||||||
|
private Integer isPicking;
|
||||||
|
/**
|
||||||
|
* 拣选站台
|
||||||
|
*/
|
||||||
|
private String pickStand;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
28
src/main/java/com/wms/entity/app/request/TaskOutRequest.java
Normal file
28
src/main/java/com/wms/entity/app/request/TaskOutRequest.java
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
package com.wms.entity.app.request;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@ApiModel(value = "出库请求")
|
||||||
|
public class TaskOutRequest {
|
||||||
|
@ApiModelProperty(value ="物料编号")
|
||||||
|
private String goodsId;
|
||||||
|
@ApiModelProperty(value ="数量")
|
||||||
|
private BigDecimal goodsNum;
|
||||||
|
@ApiModelProperty(value ="载具号")
|
||||||
|
private String vehicleId;
|
||||||
|
@ApiModelProperty(value ="起始库位")
|
||||||
|
private String originPoint;
|
||||||
|
@ApiModelProperty(value ="终点站台")
|
||||||
|
private String destinationPoint;
|
||||||
|
@ApiModelProperty(value ="用户名")
|
||||||
|
private String userName;
|
||||||
|
@ApiModelProperty(value ="是否拣选")
|
||||||
|
private Integer isPicking;
|
||||||
|
@ApiModelProperty(value ="拣选站台")
|
||||||
|
private String pickStand;
|
||||||
|
}
|
||||||
|
|
@ -86,4 +86,19 @@ public class Task {
|
||||||
*/
|
*/
|
||||||
@TableField(value = "goods_related", typeHandler = Fastjson2TypeHandler.class)
|
@TableField(value = "goods_related", typeHandler = Fastjson2TypeHandler.class)
|
||||||
private TaskDetailInfo goodsRelated;
|
private TaskDetailInfo goodsRelated;
|
||||||
|
/**
|
||||||
|
* 前置任务
|
||||||
|
*/
|
||||||
|
@TableField(value = "pre_task")
|
||||||
|
private String preTask;
|
||||||
|
/**
|
||||||
|
* 是否拣选
|
||||||
|
*/
|
||||||
|
@TableField(value = "is_picking")
|
||||||
|
private Integer isPicking;
|
||||||
|
/**
|
||||||
|
* 拣选站台
|
||||||
|
*/
|
||||||
|
@TableField(value = "pick_stand")
|
||||||
|
private String pickStand;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,4 +83,19 @@ public class TaskRecord {
|
||||||
*/
|
*/
|
||||||
@TableField(value = "goods_related", typeHandler = Fastjson2TypeHandler.class)
|
@TableField(value = "goods_related", typeHandler = Fastjson2TypeHandler.class)
|
||||||
private TaskDetailInfo goodsRelated;
|
private TaskDetailInfo goodsRelated;
|
||||||
|
/**
|
||||||
|
* 前置任务
|
||||||
|
*/
|
||||||
|
@TableField(value = "pre_task")
|
||||||
|
private String preTask;
|
||||||
|
/**
|
||||||
|
* 是否拣选
|
||||||
|
*/
|
||||||
|
@TableField(value = "is_picking")
|
||||||
|
private Integer isPicking;
|
||||||
|
/**
|
||||||
|
* 拣选站台
|
||||||
|
*/
|
||||||
|
@TableField(value = "pick_stand")
|
||||||
|
private String pickStand;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@ public interface LocationService extends IService<Location> {
|
||||||
/**
|
/**
|
||||||
* 查找一个可用库位
|
* 查找一个可用库位
|
||||||
* @param inPoint 入库站点
|
* @param inPoint 入库站点
|
||||||
* @return 结果
|
* @param goodsId 物料编号--可选
|
||||||
* nextLocationId, preTaskId
|
* @return 结果 nextLocationId, preTaskId
|
||||||
*/
|
*/
|
||||||
Map<String, String> getOneLocation(String inPoint);
|
Map<String, String> getOneLocation(String inPoint, String goodsId);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,17 @@
|
||||||
package com.wms.service;
|
package com.wms.service;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.wms.entity.app.dto.TaskDto;
|
||||||
import com.wms.entity.table.Task;
|
import com.wms.entity.table.Task;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务服务
|
||||||
|
*/
|
||||||
public interface TaskService extends IService<Task> {
|
public interface TaskService extends IService<Task> {
|
||||||
|
/**
|
||||||
|
* 生成移库任务
|
||||||
|
* @param locationId 要出库的库位号
|
||||||
|
* @return 深度-1的移库任务
|
||||||
|
*/
|
||||||
|
TaskDto genMoveTask(String locationId);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,22 +2,22 @@ package com.wms.service.serviceImplements;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.wms.constants.enums.StockStatus;
|
||||||
import com.wms.entity.table.Location;
|
import com.wms.entity.table.Location;
|
||||||
import com.wms.entity.table.Stand;
|
import com.wms.entity.table.Stand;
|
||||||
|
import com.wms.entity.table.Stock;
|
||||||
import com.wms.entity.table.Task;
|
import com.wms.entity.table.Task;
|
||||||
import com.wms.mapper.LocationMapper;
|
import com.wms.mapper.LocationMapper;
|
||||||
import com.wms.service.LocationService;
|
import com.wms.service.LocationService;
|
||||||
import com.wms.service.StandService;
|
import com.wms.service.StandService;
|
||||||
|
import com.wms.service.StockService;
|
||||||
import com.wms.service.TaskService;
|
import com.wms.service.TaskService;
|
||||||
import com.wms.utils.StringUtils;
|
import com.wms.utils.StringUtils;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||||
|
|
@ -34,15 +34,20 @@ public class LocationServiceImplements extends ServiceImpl<LocationMapper, Locat
|
||||||
* 站台
|
* 站台
|
||||||
*/
|
*/
|
||||||
private final StandService standService;
|
private final StandService standService;
|
||||||
|
/**
|
||||||
|
* 库存
|
||||||
|
*/
|
||||||
|
private final StockService stockService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查找一个可用库位
|
* 查找一个可用库位
|
||||||
* @param inPoint 入库站点
|
* @param inPoint 入库站点
|
||||||
|
* @param goodsId 物料编号--可选
|
||||||
* @return 结果
|
* @return 结果
|
||||||
* nextLocationId, preTaskId
|
* nextLocationId, preTaskId
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> getOneLocation(String inPoint) {
|
public Map<String, String> getOneLocation(String inPoint, String goodsId) {
|
||||||
Map<String, String> resultMap = new HashMap<>();
|
Map<String, String> resultMap = new HashMap<>();
|
||||||
// 查找对应站台
|
// 查找对应站台
|
||||||
LambdaQueryWrapper<Location> locationQueryWrapper = new LambdaQueryWrapper<Location>()
|
LambdaQueryWrapper<Location> locationQueryWrapper = new LambdaQueryWrapper<Location>()
|
||||||
|
|
@ -60,85 +65,127 @@ public class LocationServiceImplements extends ServiceImpl<LocationMapper, Locat
|
||||||
Stand queryStand = standService.getOne(standQueryWrapper);
|
Stand queryStand = standService.getOne(standQueryWrapper);
|
||||||
if (queryStand != null && queryStand.getEquipmentId() != null) {
|
if (queryStand != null && queryStand.getEquipmentId() != null) {
|
||||||
equipmentId = queryStand.getEquipmentId();
|
equipmentId = queryStand.getEquipmentId();
|
||||||
locationQueryWrapper.eq(Location::getEquipmentId, queryStand.getEquipmentId());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 指定设备号
|
// 指定设备号
|
||||||
if (equipmentId != -1) {
|
if (equipmentId != -1) {
|
||||||
locationQueryWrapper.eq(Location::getEquipmentId, equipmentId);
|
return getOneLocationByEquipmentId(resultMap, locationQueryWrapper, equipmentId);
|
||||||
locationQueryWrapper.orderByDesc(Location::getWDepth)
|
|
||||||
.orderByAsc(List.of(Location::getWCol, Location::getWLayer, Location::getWRow));
|
|
||||||
List<Location> availableLocations = locationMapper.selectList(locationQueryWrapper);
|
|
||||||
for (Location oneAvailableLocation : availableLocations) {
|
|
||||||
LambdaQueryWrapper<Location> haveTaskQueryWrapper = new LambdaQueryWrapper<Location>()
|
|
||||||
.select(Location::getLocationId)
|
|
||||||
.eq(Location::getWRow, oneAvailableLocation.getWRow())
|
|
||||||
.eq(Location::getWCol, oneAvailableLocation.getWCol())
|
|
||||||
.eq(Location::getWLayer, oneAvailableLocation.getWLayer());
|
|
||||||
List<Location> haveTaskLocations = locationMapper.selectList(haveTaskQueryWrapper);
|
|
||||||
// 判断当前排列层的库位是否有出库或者移库任务
|
|
||||||
for (Location havaTaskLocation : haveTaskLocations) {
|
|
||||||
LambdaQueryWrapper<Task> taskQueryWrapper = new LambdaQueryWrapper<Task>()
|
|
||||||
.select(Task::getTaskId)
|
|
||||||
.eq(Task::getOrigin, havaTaskLocation.getLocationId());
|
|
||||||
if (taskService.count(taskQueryWrapper) > 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 判断是否深度+1有入库任务
|
|
||||||
haveTaskQueryWrapper.clear();
|
|
||||||
LambdaQueryWrapper<Location> plusOneDepthLocationQueryWrapper = new LambdaQueryWrapper<Location>()
|
|
||||||
.select(Location::getLocationId)
|
|
||||||
.eq(Location::getWDepth, oneAvailableLocation.getWDepth() + 1)
|
|
||||||
.eq(Location::getWRow, oneAvailableLocation.getWRow())
|
|
||||||
.eq(Location::getWCol, oneAvailableLocation.getWCol())
|
|
||||||
.eq(Location::getWLayer, oneAvailableLocation.getWLayer());
|
|
||||||
Location plusOneDepthLocation = locationMapper.selectOne(plusOneDepthLocationQueryWrapper);
|
|
||||||
if (plusOneDepthLocation == null) {
|
|
||||||
resultMap.put("nextLocationId", oneAvailableLocation.getLocationId());
|
|
||||||
return resultMap;
|
|
||||||
} else {
|
|
||||||
LambdaQueryWrapper<Task> taskQueryWrapper = new LambdaQueryWrapper<Task>()
|
|
||||||
.select(Task::getTaskId)
|
|
||||||
.eq(Task::getDestination, plusOneDepthLocation.getLocationId());
|
|
||||||
Task plusOneDepthTask = taskService.getOne(taskQueryWrapper);
|
|
||||||
if (plusOneDepthTask != null) {
|
|
||||||
resultMap.put("nextLocationId", oneAvailableLocation.getLocationId());
|
|
||||||
resultMap.put("preTaskId", plusOneDepthTask.getTaskId());
|
|
||||||
return resultMap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {// 未指定设备号
|
} else {// 未指定设备号
|
||||||
// TODO
|
// TODO
|
||||||
// 选择最近未使用的设备
|
// 选择最近未使用的设备
|
||||||
LambdaQueryWrapper<Stand> standQueryWrapper = new LambdaQueryWrapper<Stand>()
|
LambdaQueryWrapper<Stand> LRUStandQueryWrapper = new LambdaQueryWrapper<Stand>()
|
||||||
.select(Stand::getStandId, Stand::getEquipmentId)
|
.select(Stand::getStandId, Stand::getEquipmentId)
|
||||||
.eq(Stand::getStandStatus, 0)
|
.eq(Stand::getStandStatus, 0)
|
||||||
.eq(Stand::getIsLock, 0)
|
.eq(Stand::getIsLock, 0)
|
||||||
.eq(Stand::getStandType, 3)
|
.eq(Stand::getStandType, 3)
|
||||||
.orderByAsc(Stand::getLastUseTime);
|
.orderByAsc(Stand::getLastUseTime);
|
||||||
Stand queryStand = standService.getOne(standQueryWrapper);
|
List<Stand> LRUStands = standService.list(LRUStandQueryWrapper);
|
||||||
if (queryStand != null && queryStand.getEquipmentId() != null) {
|
if (LRUStands.isEmpty()) {
|
||||||
locationQueryWrapper.eq(Location::getEquipmentId, queryStand.getEquipmentId());
|
return null;
|
||||||
} else {
|
} else {
|
||||||
locationQueryWrapper.eq(Location::getEquipmentId, 1);
|
if (StringUtils.isNotEmpty(goodsId)) {// 需要根据物料编号做出区分
|
||||||
|
equipmentId = selectEquipmentByLRUAndGoods(LRUStands, goodsId);
|
||||||
|
} else {
|
||||||
|
equipmentId = LRUStands.get(0).getEquipmentId();
|
||||||
|
}
|
||||||
|
return getOneLocationByEquipmentId(resultMap, locationQueryWrapper, equipmentId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据设备号查找库位
|
||||||
|
* @param resultMap 结果集
|
||||||
|
* @param locationQueryWrapper 查询条件
|
||||||
|
* @param equipmentId 设备号
|
||||||
|
* @return 查询结果
|
||||||
|
*/
|
||||||
|
private Map<String, String> getOneLocationByEquipmentId(Map<String, String> resultMap, LambdaQueryWrapper<Location> locationQueryWrapper, int equipmentId) {
|
||||||
|
locationQueryWrapper.eq(Location::getEquipmentId, equipmentId);
|
||||||
locationQueryWrapper.orderByDesc(Location::getWDepth)
|
locationQueryWrapper.orderByDesc(Location::getWDepth)
|
||||||
.orderByAsc(List.of(Location::getWCol, Location::getWLayer, Location::getWRow));
|
.orderByAsc(List.of(Location::getWCol, Location::getWLayer, Location::getWRow));
|
||||||
|
List<Location> availableLocations = locationMapper.selectList(locationQueryWrapper);
|
||||||
Location oneAvailableLocation = locationMapper.selectOne(locationQueryWrapper);
|
for (Location oneAvailableLocation : availableLocations) {
|
||||||
if (oneAvailableLocation != null) {
|
LambdaQueryWrapper<Location> haveTaskQueryWrapper = new LambdaQueryWrapper<Location>()
|
||||||
|
.select(Location::getLocationId)
|
||||||
} else {
|
.eq(Location::getWRow, oneAvailableLocation.getWRow())
|
||||||
|
.eq(Location::getWCol, oneAvailableLocation.getWCol())
|
||||||
|
.eq(Location::getWLayer, oneAvailableLocation.getWLayer());
|
||||||
|
List<Location> haveTaskLocations = locationMapper.selectList(haveTaskQueryWrapper);
|
||||||
|
// 判断当前排列层的库位是否有出库或者移库任务
|
||||||
|
for (Location havaTaskLocation : haveTaskLocations) {
|
||||||
|
LambdaQueryWrapper<Task> taskQueryWrapper = new LambdaQueryWrapper<Task>()
|
||||||
|
.select(Task::getTaskId)
|
||||||
|
.eq(Task::getOrigin, havaTaskLocation.getLocationId());
|
||||||
|
if (taskService.count(taskQueryWrapper) > 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 判断是否深度+1有入库任务
|
||||||
|
haveTaskQueryWrapper.clear();
|
||||||
|
LambdaQueryWrapper<Location> plusOneDepthLocationQueryWrapper = new LambdaQueryWrapper<Location>()
|
||||||
|
.select(Location::getLocationId)
|
||||||
|
.eq(Location::getWDepth, oneAvailableLocation.getWDepth() + 1)
|
||||||
|
.eq(Location::getWRow, oneAvailableLocation.getWRow())
|
||||||
|
.eq(Location::getWCol, oneAvailableLocation.getWCol())
|
||||||
|
.eq(Location::getWLayer, oneAvailableLocation.getWLayer());
|
||||||
|
Location plusOneDepthLocation = locationMapper.selectOne(plusOneDepthLocationQueryWrapper);
|
||||||
|
if (plusOneDepthLocation == null) {
|
||||||
|
resultMap.put("nextLocationId", oneAvailableLocation.getLocationId());
|
||||||
|
return resultMap;
|
||||||
|
} else {
|
||||||
|
LambdaQueryWrapper<Task> taskQueryWrapper = new LambdaQueryWrapper<Task>()
|
||||||
|
.select(Task::getTaskId)
|
||||||
|
.eq(Task::getDestination, plusOneDepthLocation.getLocationId());
|
||||||
|
Task plusOneDepthTask = taskService.getOne(taskQueryWrapper);
|
||||||
|
if (plusOneDepthTask != null) {
|
||||||
|
resultMap.put("nextLocationId", oneAvailableLocation.getLocationId());
|
||||||
|
resultMap.put("preTaskId", plusOneDepthTask.getTaskId());
|
||||||
|
return resultMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 查找低一个深度的库位
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据最近最久未使用加物料来判断应该使用哪个设备
|
||||||
|
* @param LRUStands 符合的设备
|
||||||
|
* @param goodsId 物料编号
|
||||||
|
* @return 设备号
|
||||||
|
*/
|
||||||
|
private int selectEquipmentByLRUAndGoods(List<Stand> LRUStands, String goodsId) {
|
||||||
|
int equipmentId = -1;
|
||||||
|
List<Integer> equipmentIds = new ArrayList<>();
|
||||||
|
List<Task> tasks = taskService.list(new LambdaQueryWrapper<Task>()
|
||||||
|
.select(Task::getDestination)
|
||||||
|
.apply("goods_related -> '$.goods_id' = {0}", goodsId)
|
||||||
|
.eq(Task::getTaskType, 1));
|
||||||
|
for (Task task : tasks) {
|
||||||
|
Location tempLocation = locationMapper.selectOne(new LambdaQueryWrapper<Location>().eq(Location::getLocationId, task.getDestination()));
|
||||||
|
if (tempLocation != null && !equipmentIds.contains(tempLocation.getEquipmentId())) {
|
||||||
|
equipmentIds.add(tempLocation.getEquipmentId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<Stock> stocks = stockService.list(new LambdaQueryWrapper<Stock>()
|
||||||
|
.select(Stock::getLocationId)
|
||||||
|
.apply("goods_related -> '$.goods_id' = {0}", goodsId)
|
||||||
|
.eq(Stock::getStockStatus, StockStatus.OK.getCode()));
|
||||||
|
for (Stock stock : stocks) {
|
||||||
|
Location tempLocation = locationMapper.selectOne(new LambdaQueryWrapper<Location>().eq(Location::getLocationId, stock.getLocationId()));
|
||||||
|
if (tempLocation != null && !equipmentIds.contains(tempLocation.getEquipmentId())) {
|
||||||
|
equipmentIds.add(tempLocation.getEquipmentId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Stand lruStand : LRUStands) {
|
||||||
|
if (!equipmentIds.contains(lruStand.getEquipmentId())) {
|
||||||
|
equipmentId = lruStand.getEquipmentId();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (equipmentId == -1) {
|
||||||
|
equipmentId = LRUStands.get(0).getEquipmentId();
|
||||||
|
}
|
||||||
|
return equipmentId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,10 +35,7 @@ public class StandServiceImplements extends ServiceImpl<StandMapper, Stand> impl
|
||||||
lambdaQueryWrapper.eq(Stand::getAllowIn, 1);// 入库
|
lambdaQueryWrapper.eq(Stand::getAllowIn, 1);// 入库
|
||||||
} else if (taskType == 2) {// 出库
|
} else if (taskType == 2) {// 出库
|
||||||
lambdaQueryWrapper.eq(Stand::getAllowOut, 1);// 出库
|
lambdaQueryWrapper.eq(Stand::getAllowOut, 1);// 出库
|
||||||
} else if (taskType == 4) {// 盘点
|
} else if (taskType == 3 || taskType == 4) {// 拣选或者盘点
|
||||||
lambdaQueryWrapper.eq(Stand::getAllowIn, 1);// 入库
|
|
||||||
lambdaQueryWrapper.eq(Stand::getAllowOut, 1);// 出库
|
|
||||||
} else if (taskType == 3) {// 拣选
|
|
||||||
lambdaQueryWrapper.eq(Stand::getStandType, 2);
|
lambdaQueryWrapper.eq(Stand::getStandType, 2);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,52 @@
|
||||||
package com.wms.service.serviceImplements;
|
package com.wms.service.serviceImplements;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.wms.entity.app.dto.TaskDto;
|
||||||
|
import com.wms.entity.table.Location;
|
||||||
import com.wms.entity.table.Task;
|
import com.wms.entity.table.Task;
|
||||||
import com.wms.mapper.TaskMapper;
|
import com.wms.mapper.TaskMapper;
|
||||||
|
import com.wms.service.LocationService;
|
||||||
import com.wms.service.TaskService;
|
import com.wms.service.TaskService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务服务的实现类
|
||||||
|
*/
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||||
public class TaskServiceImplements extends ServiceImpl<TaskMapper, Task> implements TaskService {
|
public class TaskServiceImplements extends ServiceImpl<TaskMapper, Task> implements TaskService {
|
||||||
|
/**
|
||||||
|
* 任务mapper
|
||||||
|
*/
|
||||||
private final TaskMapper taskMapper;
|
private final TaskMapper taskMapper;
|
||||||
|
/**
|
||||||
|
* 库位服务
|
||||||
|
*/
|
||||||
|
private final LocationService locationService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TaskDto genMoveTask(String locationId) {
|
||||||
|
Location outLocation = locationService.getOne(new LambdaQueryWrapper<Location>().eq(Location::getLocationId, locationId));
|
||||||
|
if (outLocation != null) {
|
||||||
|
if (outLocation.getWDepth() == 1) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
// 生成当前深度-1的库位的移库任务
|
||||||
|
Location depthMinus1Location = locationService.getOne(new LambdaQueryWrapper<Location>().eq(Location::getLocationId, outLocation.getLocationId()));
|
||||||
|
// 查询这个位置
|
||||||
|
// 生成当前深度-2的库位的移库任务,即当前深度-1的库位作为参数
|
||||||
|
genMoveTask(depthMinus1Location.getLocationId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user