From 365731af39830f3c78698d176a67054321788dc1 Mon Sep 17 00:00:00 2001
From: zch
Date: Mon, 1 Jun 2026 13:41:45 +0800
Subject: [PATCH] =?UTF-8?q?feat(wcs):=20=E5=AE=8C=E6=88=90WCS=E6=A8=A1?=
=?UTF-8?q?=E5=9D=97=E5=A4=9A=E7=BB=B4=E5=BA=A6=E4=BC=98=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 统一Excel字段展示格式,简化表头并配置字典转换
- 新增实时任务明细下拉查询接口,避免分页全量问题
- 为核心业务service添加缓存注解,优化查询性能
- 完善MyBatis跨库用户表配置,支持动态schema
- 优化各业务service的校验逻辑与代码注释
---
.../dromara/wcs/config/WcsMybatisConfig.java | 77 ++++++++++++++++++-
.../controller/LiveTaskDetailController.java | 10 +++
.../wcs/domain/vo/BaseDeviceHostVo.java | 2 +-
.../wcs/domain/vo/BaseDeviceInfoVo.java | 11 ++-
.../wcs/domain/vo/BaseDeviceParamVo.java | 6 +-
.../wcs/domain/vo/BaseLocationInfoVo.java | 4 +-
.../wcs/domain/vo/BaseMaterialInfoVo.java | 2 +-
.../wcs/domain/vo/BasePathDetailsVo.java | 2 +-
.../dromara/wcs/domain/vo/BasePathInfoVo.java | 2 +-
.../wcs/domain/vo/BaseStoreInfoVo.java | 2 +-
.../impl/BaseDeviceHostServiceImpl.java | 16 ++++
.../impl/BaseDeviceInfoServiceImpl.java | 9 +++
.../impl/BaseDeviceParamServiceImpl.java | 9 +++
.../impl/BaseLocationInfoServiceImpl.java | 10 +++
.../impl/BaseMaterialInfoServiceImpl.java | 12 +++
.../impl/BasePathDetailsServiceImpl.java | 13 +++-
.../service/impl/BasePathInfoServiceImpl.java | 38 ++++++++-
.../impl/BaseStoreInfoServiceImpl.java | 16 ++++
.../impl/LiveTaskDetailServiceImpl.java | 34 +++++++-
.../impl/LiveTaskQueueServiceImpl.java | 51 +++++++++++-
.../mapper/wcs/BaseDeviceHostMapper.xml | 1 +
21 files changed, 309 insertions(+), 18 deletions(-)
diff --git a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/config/WcsMybatisConfig.java b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/config/WcsMybatisConfig.java
index db63b20..d3c0252 100644
--- a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/config/WcsMybatisConfig.java
+++ b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/config/WcsMybatisConfig.java
@@ -8,31 +8,103 @@ import org.springframework.context.annotation.Configuration;
import java.util.Properties;
/**
- * WCS MyBatis配置。
+ * WCS MyBatis 配置类。
+ *
+ * 背景与要解决的问题
+ * hw-wcs 模块使用 slave 数据源(即 wcs_core 业务库),其业务表中的 create_by、update_by
+ * 字段存储的是 bigint 类型的用户 ID。要在列表查询时展示"创建人/更新人"的姓名,
+ * 必须跨库连表到 master 数据源的 sys_user 表。
+ *
+ * 但 Mapper XML 运行在 slave 数据源上,不能将主库表名写死在 SQL 中,原因有二:
+ *
+ * - 生产环境主库 schema 名称可能变化(如 wcs_core_ruoyi → prod_ruoyi),写死则连表失效;
+ * - 直接拼接有 SQL 注入风险,需在启动期做白名单校验。
+ *
+ *
+ *
+ * 解决思路
+ * 从 Spring 配置文件读取主库用户表全名(格式:schema.table 或 table),
+ * 经正则白名单校验后,注入为 MyBatis 全局变量 {@code ${wcsSysUserTable}}。
+ * Mapper XML 中用该占位符引用,运行时由 MyBatis 自动替换为真实表名,
+ * 从而实现:环境隔离、配置可覆盖、防注入三重保障。
+ *
+ * 使用示例(Mapper XML)
+ * {@code
+ * left join ${wcsSysUserTable} cu on t.create_by = cu.user_id
+ * left join ${wcsSysUserTable} uu on t.update_by = uu.user_id
+ * }
*
* @author zch
*/
-@Configuration
+@Configuration // 标记为 Spring 配置类,容器启动时自动扫描并处理其中的 @Bean 方法
public class WcsMybatisConfig {
+ /**
+ * MyBatis 全局变量名,Mapper XML 中通过 ${wcsSysUserTable} 引用。
+ * 运行时会被替换为真实的用户表全名(如 wcs_core_ruoyi.sys_user)。
+ */
private static final String SYS_USER_TABLE_VARIABLE = "wcsSysUserTable";
+
+ /**
+ * 表名白名单正则:仅允许 "table" 或 "schema.table" 格式。
+ * - [A-Za-z0-9_]+ 匹配表名或 schema 名(字母、数字、下划线)
+ * - (\\.[A-Za-z0-9_]+)? 可选的 ".table" 部分,用于 schema.table 格式
+ *
+ * 合法示例:sys_user、wcs_core_ruoyi.sys_user
+ * 非法示例:sys-user(含横线)、DROP TABLE sys_user(含空格/关键字)
+ */
private static final String TABLE_NAME_PATTERN = "[A-Za-z0-9_]+(\\.[A-Za-z0-9_]+)?";
+ /**
+ * 注册 MyBatis ConfigurationCustomizer,将用户表全名注入为全局变量。
+ *
+ * Spring 容器初始化时,MyBatis-Plus 会收集所有 ConfigurationCustomizer Bean,
+ * 在创建 Configuration 对象后依次调用,本方法即为其中之一。
+ *
+ * @param systemUserTable 从配置文件读取的用户表全名。
+ * 格式:${wcs.system-user-table:默认值}
+ * - 配置键:wcs.system-user-table
+ * - 默认值:wcs_core_ruoyi.sys_user(开发环境)
+ * - 生产环境可通过环境变量 WCS_SYSTEM_USER_TABLE 覆盖
+ * (见 application-dev.yml 中的 ${WCS_SYSTEM_USER_TABLE:...})
+ * @return ConfigurationCustomizer Lambda,MyBatis 初始化完成后回调执行
+ */
@Bean
public ConfigurationCustomizer wcsSqlVariableCustomizer(
@Value("${wcs.system-user-table:wcs_core_ruoyi.sys_user}") String systemUserTable) {
+
+ // 启动期立即校验,不合法则直接抛异常阻止应用启动,防止非法表名进入 SQL
String validatedSystemUserTable = validateTableName(systemUserTable);
+
+ // 返回 Lambda:MyBatis Configuration 对象创建完毕后执行
return configuration -> {
+ // 获取 MyBatis 已有的全局变量集合(可能为 null)
Properties variables = configuration.getVariables();
if (variables == null) {
+ // 首次访问时初始化,避免 NPE
variables = new Properties();
configuration.setVariables(variables);
}
+ // 将校验后的用户表全名写入 MyBatis 全局变量:
+ // key = "wcsSysUserTable"
+ // value = "wcs_core_ruoyi.sys_user"(或其他通过校验的值)
+ // 之后 Mapper XML 中所有 ${wcsSysUserTable} 占位符都会被替换为此值
// 用户表全名交给环境配置,避免从库XML写死主库schema后在生产库名变化时直接失效。
variables.setProperty(SYS_USER_TABLE_VARIABLE, validatedSystemUserTable);
};
}
+ /**
+ * 校验表名字符串是否合法(仅允许字母、数字、下划线,可选带一个点号分隔的 schema 前缀)。
+ *
+ * 该值最终会通过 MyBatis ${} 占位符直接拼入 SQL 语句的表名位置,
+ * 属于"非参数化"拼接,因此必须在启动期做严格的白名单校验,
+ * 杜绝 SQL 注入风险。启动时失败(fail-fast)远比运行时暴露注入面更安全。
+ *
+ * @param tableName 待校验的表名字符串
+ * @return 校验通过的表名(原值返回)
+ * @throws IllegalArgumentException 若表名为 null 或不符合白名单正则
+ */
private String validateTableName(String tableName) {
if (tableName == null || !tableName.matches(TABLE_NAME_PATTERN)) {
// 该值会进入MyBatis ${} 表名占位,启动期失败比运行期暴露SQL注入面更稳妥。
@@ -40,4 +112,5 @@ public class WcsMybatisConfig {
}
return tableName;
}
+
}
diff --git a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/controller/LiveTaskDetailController.java b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/controller/LiveTaskDetailController.java
index 461361c..5d7876f 100644
--- a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/controller/LiveTaskDetailController.java
+++ b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/controller/LiveTaskDetailController.java
@@ -45,6 +45,16 @@ public class LiveTaskDetailController extends BaseController {
return liveTaskDetailService.queryPageList(bo, pageQuery);
}
+ /**
+ * 获取实时任务明细下拉列表
+ */
+ @SaCheckPermission("wcs:taskDetail:query")
+ @GetMapping("/getTaskDetailList")
+ public R> getTaskDetailList(LiveTaskDetailBo bo) {
+ // 表单下拉使用非分页主数据,避免 pageSize 伪全量在明细数量增长后漏选。
+ return R.ok(liveTaskDetailService.queryList(bo));
+ }
+
/**
* 导出实时任务明细列表
*/
diff --git a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BaseDeviceHostVo.java b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BaseDeviceHostVo.java
index dc27249..d7e8b35 100644
--- a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BaseDeviceHostVo.java
+++ b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BaseDeviceHostVo.java
@@ -67,7 +67,7 @@ public class BaseDeviceHostVo implements Serializable {
/**
* 是否标识:1-是;0-否
*/
- @ExcelProperty(value = "是否标识:1-是;0-否", converter = ExcelDictConvert.class)
+ @ExcelProperty(value = "是否标识", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "wcs_is_flag")
private Integer isFlag;
diff --git a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BaseDeviceInfoVo.java b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BaseDeviceInfoVo.java
index 8fded07..c9c7958 100644
--- a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BaseDeviceInfoVo.java
+++ b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BaseDeviceInfoVo.java
@@ -4,6 +4,8 @@ import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
import org.dromara.wcs.domain.BaseDeviceInfo;
import java.io.Serial;
@@ -53,19 +55,22 @@ public class BaseDeviceInfoVo implements Serializable {
/**
* 设备类型:0-输送线;1-AGV;2-提升机
*/
- @ExcelProperty(value = "设备类型:0-输送线;1-AGV;2-提升机")
+ @ExcelProperty(value = "设备类型", converter = ExcelDictConvert.class)
+ @ExcelDictFormat(dictType = "wcs_device_type")
private Integer deviceType;
/**
* 设备状态:0-正常;1-在忙;2-异常
*/
- @ExcelProperty(value = "设备状态:0-正常;1-在忙;2-异常")
+ @ExcelProperty(value = "设备状态", converter = ExcelDictConvert.class)
+ @ExcelDictFormat(dictType = "wcs_device_status")
private Integer deviceStatus;
/**
* 是否标识:1-是;0-否
*/
- @ExcelProperty(value = "是否标识:1-是;0-否")
+ @ExcelProperty(value = "是否标识", converter = ExcelDictConvert.class)
+ @ExcelDictFormat(dictType = "wcs_is_flag")
private Integer isFlag;
/**
diff --git a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BaseDeviceParamVo.java b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BaseDeviceParamVo.java
index 4396f46..1506f4a 100644
--- a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BaseDeviceParamVo.java
+++ b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BaseDeviceParamVo.java
@@ -67,7 +67,8 @@ public class BaseDeviceParamVo implements Serializable {
/**
* 操作类型:1-只读;2-只写;0-默认读写
*/
- @ExcelProperty(value = "操作类型:1-只读;2-只写;0-默认读写")
+ @ExcelProperty(value = "操作类型", converter = ExcelDictConvert.class)
+ @ExcelDictFormat(dictType = "wcs_operation_type")
private Integer operationType;
/**
@@ -80,7 +81,8 @@ public class BaseDeviceParamVo implements Serializable {
/**
* 是否标识:1-是;0-否
*/
- @ExcelProperty(value = "是否标识:1-是;0-否")
+ @ExcelProperty(value = "是否标识", converter = ExcelDictConvert.class)
+ @ExcelDictFormat(dictType = "wcs_is_flag")
private Integer isFlag;
/**
diff --git a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BaseLocationInfoVo.java b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BaseLocationInfoVo.java
index 9c7ed8e..65625ef 100644
--- a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BaseLocationInfoVo.java
+++ b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BaseLocationInfoVo.java
@@ -103,14 +103,14 @@ public class BaseLocationInfoVo implements Serializable {
/**
* 库位状态;0-未使用;1-已使用;2-锁库;3-异常
*/
- @ExcelProperty(value = "库位状态;0-未使用;1-已使用;2-锁库;3-异常", converter = ExcelDictConvert.class)
+ @ExcelProperty(value = "库位状态", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "wcs_location_status")
private Integer locationStatus;
/**
* 是否标识:1-是;0-否
*/
- @ExcelProperty(value = "是否标识:1-是;0-否", converter = ExcelDictConvert.class)
+ @ExcelProperty(value = "是否标识", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "wcs_is_flag")
private Integer isFlag;
diff --git a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BaseMaterialInfoVo.java b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BaseMaterialInfoVo.java
index 85f7499..c990413 100644
--- a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BaseMaterialInfoVo.java
+++ b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BaseMaterialInfoVo.java
@@ -73,7 +73,7 @@ public class BaseMaterialInfoVo implements Serializable {
/**
* 是否标识:1-是;0-否
*/
- @ExcelProperty(value = "是否标识:1-是;0-否", converter = ExcelDictConvert.class)
+ @ExcelProperty(value = "是否标识", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "wcs_is_flag")
private Integer isFlag;
diff --git a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BasePathDetailsVo.java b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BasePathDetailsVo.java
index b71125c..81b6b45 100644
--- a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BasePathDetailsVo.java
+++ b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BasePathDetailsVo.java
@@ -55,7 +55,7 @@ public class BasePathDetailsVo implements Serializable {
/**
* 是否标识:1-是;0-否
*/
- @ExcelProperty(value = "是否标识:1-是;0-否", converter = ExcelDictConvert.class)
+ @ExcelProperty(value = "是否标识", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "wcs_is_flag")
private Integer isFlag;
diff --git a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BasePathInfoVo.java b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BasePathInfoVo.java
index 1ee34a3..bbeaa42 100644
--- a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BasePathInfoVo.java
+++ b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BasePathInfoVo.java
@@ -50,7 +50,7 @@ public class BasePathInfoVo implements Serializable {
/**
* 是否标识:1-是;0-否
*/
- @ExcelProperty(value = "是否标识:1-是;0-否", converter = ExcelDictConvert.class)
+ @ExcelProperty(value = "是否标识", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "wcs_is_flag")
private Integer isFlag;
diff --git a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BaseStoreInfoVo.java b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BaseStoreInfoVo.java
index d1b9c8b..bae46b9 100644
--- a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BaseStoreInfoVo.java
+++ b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/domain/vo/BaseStoreInfoVo.java
@@ -49,7 +49,7 @@ public class BaseStoreInfoVo implements Serializable {
/**
* 是否标识:1-是;0-否
*/
- @ExcelProperty(value = "是否标识:1-是;0-否", converter = ExcelDictConvert.class)
+ @ExcelProperty(value = "是否标识", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "wcs_is_flag")
private Integer isFlag;
diff --git a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BaseDeviceHostServiceImpl.java b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BaseDeviceHostServiceImpl.java
index ead399c..7f2d4a0 100644
--- a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BaseDeviceHostServiceImpl.java
+++ b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BaseDeviceHostServiceImpl.java
@@ -12,6 +12,8 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.dromara.wcs.domain.bo.BaseDeviceHostBo;
import org.dromara.wcs.domain.vo.BaseDeviceHostVo;
@@ -44,6 +46,7 @@ public class BaseDeviceHostServiceImpl implements IBaseDeviceHostService {
* @return 设备主机
*/
@Override
+ @Cacheable(cacheNames = "wcs_cache", key = "#objId")
public BaseDeviceHostVo queryById(Long objId){
return baseMapper.selectCustomBaseDeviceHostVoById(objId);
}
@@ -142,10 +145,14 @@ public class BaseDeviceHostServiceImpl implements IBaseDeviceHostService {
@Override
@DSTransactional
public Boolean insertByBo(BaseDeviceHostBo bo) {
+ // 将 BO 对象转换为实体对象
BaseDeviceHost add = MapstructUtils.convert(bo, BaseDeviceHost.class);
+ // 保存前进行数据校验
validEntityBeforeSave(add);
+ // 执行插入操作
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
+ // 插入成功后将生成的主键回填到 BO 对象
bo.setObjId(add.getObjId());
}
return flag;
@@ -158,10 +165,14 @@ public class BaseDeviceHostServiceImpl implements IBaseDeviceHostService {
* @return 是否修改成功
*/
@Override
+ @CacheEvict(cacheNames = "wcs_cache", key = "#bo.objId")
@DSTransactional
public Boolean updateByBo(BaseDeviceHostBo bo) {
+ // 将 BO 对象转换为实体对象
BaseDeviceHost update = MapstructUtils.convert(bo, BaseDeviceHost.class);
+ // 保存前进行数据校验
validEntityBeforeSave(update);
+ // 执行更新操作
return baseMapper.updateById(update) > 0;
}
@@ -169,15 +180,19 @@ public class BaseDeviceHostServiceImpl implements IBaseDeviceHostService {
* 保存前的数据校验
*/
private void validEntityBeforeSave(BaseDeviceHost entity){
+ // 校验实体对象和主机编号
if (entity == null || StringUtils.isBlank(entity.getHostCode())) {
throw new ServiceException("主机编号不能为空");
}
+ // 查询相同主机编号的数量,排除当前记录(更新场景)
Long sameHostCodeCount = baseMapper.selectCount(Wrappers.lambdaQuery()
.eq(BaseDeviceHost::getHostCode, entity.getHostCode())
.ne(entity.getObjId() != null, BaseDeviceHost::getObjId, entity.getObjId()));
+ // 校验主机编号唯一性
if (sameHostCodeCount > 0) {
throw new ServiceException("主机编号已存在,请更换主机编号");
}
+ // 设置默认启用标识
if (entity.getIsFlag() == null) {
// 主数据新增默认启用,避免下拉选择时新建记录因空标识被业务过滤。
entity.setIsFlag(1);
@@ -192,6 +207,7 @@ public class BaseDeviceHostServiceImpl implements IBaseDeviceHostService {
* @return 是否删除成功
*/
@Override
+ @CacheEvict(cacheNames = "wcs_cache", allEntries = true)
@DSTransactional
public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) {
if(isValid){
diff --git a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BaseDeviceInfoServiceImpl.java b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BaseDeviceInfoServiceImpl.java
index cc08639..5bacb72 100644
--- a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BaseDeviceInfoServiceImpl.java
+++ b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BaseDeviceInfoServiceImpl.java
@@ -12,6 +12,8 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.dromara.wcs.domain.bo.BaseDeviceInfoBo;
import org.dromara.wcs.domain.vo.BaseDeviceInfoVo;
@@ -44,6 +46,7 @@ public class BaseDeviceInfoServiceImpl implements IBaseDeviceInfoService {
* @return 设备信息
*/
@Override
+ @Cacheable(cacheNames = "wcs_cache", key = "#objId")
public BaseDeviceInfoVo queryById(Long objId){
return baseMapper.selectCustomBaseDeviceInfoVoById(objId);
}
@@ -158,6 +161,7 @@ public class BaseDeviceInfoServiceImpl implements IBaseDeviceInfoService {
* @return 是否修改成功
*/
@Override
+ @CacheEvict(cacheNames = "wcs_cache", key = "#bo.objId")
@DSTransactional
public Boolean updateByBo(BaseDeviceInfoBo bo) {
BaseDeviceInfo update = MapstructUtils.convert(bo, BaseDeviceInfo.class);
@@ -169,15 +173,19 @@ public class BaseDeviceInfoServiceImpl implements IBaseDeviceInfoService {
* 保存前的数据校验
*/
private void validEntityBeforeSave(BaseDeviceInfo entity){
+ // 校验实体对象和设备编号
if (entity == null || StringUtils.isBlank(entity.getDeviceCode())) {
throw new ServiceException("设备编号不能为空");
}
+ // 查询相同设备编号的数量,排除当前记录(更新场景)
Long sameDeviceCodeCount = baseMapper.selectCount(Wrappers.lambdaQuery()
.eq(BaseDeviceInfo::getDeviceCode, entity.getDeviceCode())
.ne(entity.getObjId() != null, BaseDeviceInfo::getObjId, entity.getObjId()));
+ // 校验设备编号唯一性
if (sameDeviceCodeCount > 0) {
throw new ServiceException("设备编号已存在,请更换设备编号");
}
+ // 设置默认启用标识
if (entity.getIsFlag() == null) {
// 设备主数据默认有效,便于调度侧只按启用设备组装可选范围。
entity.setIsFlag(1);
@@ -192,6 +200,7 @@ public class BaseDeviceInfoServiceImpl implements IBaseDeviceInfoService {
* @return 是否删除成功
*/
@Override
+ @CacheEvict(cacheNames = "wcs_cache", allEntries = true)
@DSTransactional
public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) {
if(isValid){
diff --git a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BaseDeviceParamServiceImpl.java b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BaseDeviceParamServiceImpl.java
index f7ba85a..652de6a 100644
--- a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BaseDeviceParamServiceImpl.java
+++ b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BaseDeviceParamServiceImpl.java
@@ -12,6 +12,8 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.dromara.wcs.domain.bo.BaseDeviceParamBo;
import org.dromara.wcs.domain.vo.BaseDeviceParamVo;
@@ -44,6 +46,7 @@ public class BaseDeviceParamServiceImpl implements IBaseDeviceParamService {
* @return 设备参数
*/
@Override
+ @Cacheable(cacheNames = "wcs_cache", key = "#objId")
public BaseDeviceParamVo queryById(Long objId){
return baseMapper.selectCustomBaseDeviceParamVoById(objId);
}
@@ -160,6 +163,7 @@ public class BaseDeviceParamServiceImpl implements IBaseDeviceParamService {
* @return 是否修改成功
*/
@Override
+ @CacheEvict(cacheNames = "wcs_cache", key = "#bo.objId")
@DSTransactional
public Boolean updateByBo(BaseDeviceParamBo bo) {
BaseDeviceParam update = MapstructUtils.convert(bo, BaseDeviceParam.class);
@@ -171,15 +175,19 @@ public class BaseDeviceParamServiceImpl implements IBaseDeviceParamService {
* 保存前的数据校验
*/
private void validEntityBeforeSave(BaseDeviceParam entity){
+ // 校验实体对象和参数编号
if (entity == null || StringUtils.isBlank(entity.getParamCode())) {
throw new ServiceException("参数编号不能为空");
}
+ // 查询相同参数编号的数量,排除当前记录(更新场景)
Long sameParamCodeCount = baseMapper.selectCount(Wrappers.lambdaQuery()
.eq(BaseDeviceParam::getParamCode, entity.getParamCode())
.ne(entity.getObjId() != null, BaseDeviceParam::getObjId, entity.getObjId()));
+ // 校验参数编号唯一性
if (sameParamCodeCount > 0) {
throw new ServiceException("参数编号已存在,请更换参数编号");
}
+ // 设置默认启用标识
if (entity.getIsFlag() == null) {
// 参数默认启用,避免设备通讯配置保存后却不参与后续参数装载。
entity.setIsFlag(1);
@@ -194,6 +202,7 @@ public class BaseDeviceParamServiceImpl implements IBaseDeviceParamService {
* @return 是否删除成功
*/
@Override
+ @CacheEvict(cacheNames = "wcs_cache", allEntries = true)
@DSTransactional
public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) {
if(isValid){
diff --git a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BaseLocationInfoServiceImpl.java b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BaseLocationInfoServiceImpl.java
index e3af52c..1baff10 100644
--- a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BaseLocationInfoServiceImpl.java
+++ b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BaseLocationInfoServiceImpl.java
@@ -12,6 +12,8 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.dromara.wcs.domain.bo.BaseLocationInfoBo;
import org.dromara.wcs.domain.vo.BaseLocationInfoVo;
@@ -44,6 +46,7 @@ public class BaseLocationInfoServiceImpl implements IBaseLocationInfoService {
* @return 库位信息
*/
@Override
+ @Cacheable(cacheNames = "wcs_cache", key = "#objId")
public BaseLocationInfoVo queryById(Long objId){
return baseMapper.selectCustomBaseLocationInfoVoById(objId);
}
@@ -165,6 +168,7 @@ public class BaseLocationInfoServiceImpl implements IBaseLocationInfoService {
* @return 是否修改成功
*/
@Override
+ @CacheEvict(cacheNames = "wcs_cache", key = "#bo.objId")
@DSTransactional
public Boolean updateByBo(BaseLocationInfoBo bo) {
BaseLocationInfo update = MapstructUtils.convert(bo, BaseLocationInfo.class);
@@ -176,19 +180,24 @@ public class BaseLocationInfoServiceImpl implements IBaseLocationInfoService {
* 保存前的数据校验
*/
private void validEntityBeforeSave(BaseLocationInfo entity){
+ // 校验实体对象和库位编号
if (entity == null || StringUtils.isBlank(entity.getLocationCode())) {
throw new ServiceException("库位编号不能为空");
}
+ // 查询相同库位编号的数量,排除当前记录(更新场景)
Long sameLocationCodeCount = baseMapper.selectCount(Wrappers.lambdaQuery()
.eq(BaseLocationInfo::getLocationCode, entity.getLocationCode())
.ne(entity.getObjId() != null, BaseLocationInfo::getObjId, entity.getObjId()));
+ // 校验库位编号唯一性
if (sameLocationCodeCount > 0) {
throw new ServiceException("库位编号已存在,请更换库位编号");
}
+ // 设置默认库位状态
if (entity.getLocationStatus() == null) {
// 新库位默认未使用,避免空状态影响库位看板与分配策略判断。
entity.setLocationStatus(0);
}
+ // 设置默认启用标识
if (entity.getIsFlag() == null) {
entity.setIsFlag(1);
}
@@ -202,6 +211,7 @@ public class BaseLocationInfoServiceImpl implements IBaseLocationInfoService {
* @return 是否删除成功
*/
@Override
+ @CacheEvict(cacheNames = "wcs_cache", allEntries = true)
@DSTransactional
public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) {
if(isValid){
diff --git a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BaseMaterialInfoServiceImpl.java b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BaseMaterialInfoServiceImpl.java
index 0cc96aa..b248855 100644
--- a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BaseMaterialInfoServiceImpl.java
+++ b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BaseMaterialInfoServiceImpl.java
@@ -12,6 +12,8 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.dromara.wcs.domain.bo.BaseMaterialInfoBo;
import org.dromara.wcs.domain.vo.BaseMaterialInfoVo;
@@ -44,6 +46,7 @@ public class BaseMaterialInfoServiceImpl implements IBaseMaterialInfoService {
* @return 物料信息
*/
@Override
+ @Cacheable(cacheNames = "wcs_cache", key = "#objId")
public BaseMaterialInfoVo queryById(Long objId){
return baseMapper.selectCustomBaseMaterialInfoVoById(objId);
}
@@ -159,10 +162,14 @@ public class BaseMaterialInfoServiceImpl implements IBaseMaterialInfoService {
* @return 是否修改成功
*/
@Override
+ @CacheEvict(cacheNames = "wcs_cache", key = "#bo.objId")
@DSTransactional
public Boolean updateByBo(BaseMaterialInfoBo bo) {
+ // 将 BO 对象转换为实体对象
BaseMaterialInfo update = MapstructUtils.convert(bo, BaseMaterialInfo.class);
+ // 保存前进行数据校验
validEntityBeforeSave(update);
+ // 执行更新操作
return baseMapper.updateById(update) > 0;
}
@@ -170,15 +177,19 @@ public class BaseMaterialInfoServiceImpl implements IBaseMaterialInfoService {
* 保存前的数据校验
*/
private void validEntityBeforeSave(BaseMaterialInfo entity){
+ // 校验实体对象和物料编号
if (entity == null || StringUtils.isBlank(entity.getMaterialCode())) {
throw new ServiceException("物料编号不能为空");
}
+ // 查询相同物料编号的数量,排除当前记录(更新场景)
Long sameMaterialCodeCount = baseMapper.selectCount(Wrappers.lambdaQuery()
.eq(BaseMaterialInfo::getMaterialCode, entity.getMaterialCode())
.ne(entity.getObjId() != null, BaseMaterialInfo::getObjId, entity.getObjId()));
+ // 校验物料编号唯一性
if (sameMaterialCodeCount > 0) {
throw new ServiceException("物料编号已存在,请更换物料编号");
}
+ // 设置默认启用标识
if (entity.getIsFlag() == null) {
// 物料主数据默认有效,确保任务和库位表单可直接引用新建物料。
entity.setIsFlag(1);
@@ -193,6 +204,7 @@ public class BaseMaterialInfoServiceImpl implements IBaseMaterialInfoService {
* @return 是否删除成功
*/
@Override
+ @CacheEvict(cacheNames = "wcs_cache", allEntries = true)
@DSTransactional
public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) {
if(isValid){
diff --git a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BasePathDetailsServiceImpl.java b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BasePathDetailsServiceImpl.java
index d2905b0..4709eda 100644
--- a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BasePathDetailsServiceImpl.java
+++ b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BasePathDetailsServiceImpl.java
@@ -12,6 +12,8 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.dromara.wcs.domain.bo.BasePathDetailsBo;
import org.dromara.wcs.domain.vo.BasePathDetailsVo;
@@ -44,6 +46,7 @@ public class BasePathDetailsServiceImpl implements IBasePathDetailsService {
* @return 路径明细
*/
@Override
+ @Cacheable(cacheNames = "wcs_cache", key = "#objId")
public BasePathDetailsVo queryById(Long objId){
return baseMapper.selectCustomBasePathDetailsVoById(objId);
}
@@ -156,10 +159,14 @@ public class BasePathDetailsServiceImpl implements IBasePathDetailsService {
* @return 是否修改成功
*/
@Override
+ @CacheEvict(cacheNames = "wcs_cache", key = "#bo.objId")
@DSTransactional
public Boolean updateByBo(BasePathDetailsBo bo) {
+ // 将 BO 对象转换为实体对象
BasePathDetails update = MapstructUtils.convert(bo, BasePathDetails.class);
+ // 保存前进行数据校验
validEntityBeforeSave(update);
+ // 执行更新操作
return baseMapper.updateById(update) > 0;
}
@@ -167,9 +174,11 @@ public class BasePathDetailsServiceImpl implements IBasePathDetailsService {
* 保存前的数据校验
*/
private void validEntityBeforeSave(BasePathDetails entity){
+ // 校验实体对象和路径编号
if (entity == null || StringUtils.isBlank(entity.getPathCode())) {
throw new ServiceException("路径编号不能为空");
}
+ // 设置默认启用标识
if (entity.getIsFlag() == null) {
entity.setIsFlag(1);
}
@@ -183,11 +192,13 @@ public class BasePathDetailsServiceImpl implements IBasePathDetailsService {
* @return 是否删除成功
*/
@Override
+ @CacheEvict(cacheNames = "wcs_cache", allEntries = true)
@DSTransactional
public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) {
if(isValid){
- //TODO 做一些业务上的校验,判断是否需要校验
+ // TODO: 做一些业务上的校验,判断是否需要校验
}
+ // 执行批量删除操作
return baseMapper.deleteByIds(ids) > 0;
}
}
diff --git a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BasePathInfoServiceImpl.java b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BasePathInfoServiceImpl.java
index e258926..f4496bc 100644
--- a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BasePathInfoServiceImpl.java
+++ b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BasePathInfoServiceImpl.java
@@ -11,6 +11,8 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.wcs.domain.BasePathDetails;
@@ -50,6 +52,7 @@ public class BasePathInfoServiceImpl implements IBasePathInfoService {
* @return 路径信息
*/
@Override
+ @Cacheable(cacheNames = "wcs_cache", key = "#objId")
public BasePathInfoVo queryById(Long objId){
BasePathInfoVo vo = baseMapper.selectCustomBasePathInfoVoById(objId);
if (vo != null) {
@@ -151,11 +154,16 @@ public class BasePathInfoServiceImpl implements IBasePathInfoService {
@Override
@DSTransactional
public Boolean insertByBo(BasePathInfoBo bo) {
+ // 将 BO 对象转换为实体对象
BasePathInfo add = MapstructUtils.convert(bo, BasePathInfo.class);
+ // 保存前进行数据校验
validEntityBeforeSave(add);
+ // 执行插入操作
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
+ // 插入成功后将生成的主键回填到 BO 对象
bo.setObjId(add.getObjId());
+ // 保存路径明细
saveDetails(bo);
}
return flag;
@@ -168,18 +176,24 @@ public class BasePathInfoServiceImpl implements IBasePathInfoService {
* @return 是否修改成功
*/
@Override
+ @CacheEvict(cacheNames = "wcs_cache", key = "#bo.objId")
@DSTransactional
public Boolean updateByBo(BasePathInfoBo bo) {
+ // 查询原记录
BasePathInfo old = baseMapper.selectById(bo.getObjId());
if (old == null) {
throw new ServiceException("路径信息不存在,无法修改");
}
+ // 将 BO 对象转换为实体对象
BasePathInfo update = MapstructUtils.convert(bo, BasePathInfo.class);
+ // 保存前进行数据校验
validEntityBeforeSave(update);
// 路径编号是子表外键,先按旧编号删除旧明细,避免 ON UPDATE CASCADE 后删不到旧数据。
deleteDetailsByPathCode(old.getPathCode());
+ // 执行更新操作
boolean flag = baseMapper.updateById(update) > 0;
if (flag) {
+ // 更新成功后保存路径明细
saveDetails(bo);
}
return flag;
@@ -189,15 +203,19 @@ public class BasePathInfoServiceImpl implements IBasePathInfoService {
* 保存前的数据校验
*/
private void validEntityBeforeSave(BasePathInfo entity){
+ // 校验实体对象和路径编号
if (entity == null || StringUtils.isBlank(entity.getPathCode())) {
throw new ServiceException("路径编号不能为空");
}
+ // 查询相同路径编号的数量,排除当前记录(更新场景)
Long samePathCodeCount = baseMapper.selectCount(Wrappers.lambdaQuery()
.eq(BasePathInfo::getPathCode, entity.getPathCode())
.ne(entity.getObjId() != null, BasePathInfo::getObjId, entity.getObjId()));
+ // 校验路径编号唯一性
if (samePathCodeCount > 0) {
throw new ServiceException("路径编号已存在,请更换路径编号");
}
+ // 设置默认启用标识
if (entity.getIsFlag() == null) {
// 路径默认有效,避免新建后任务下拉因状态为空无法选择。
entity.setIsFlag(1);
@@ -208,19 +226,24 @@ public class BasePathInfoServiceImpl implements IBasePathInfoService {
* 保存路径明细。
*/
private void saveDetails(BasePathInfoBo bo) {
+ // 获取路径明细列表
List details = bo.getDetails();
if (details == null || details.isEmpty()) {
return;
}
+ // 遍历明细列表并保存
for (BasePathDetailsBo detailBo : details) {
+ // 将明细 BO 转换为实体对象
BasePathDetails detail = MapstructUtils.convert(detailBo, BasePathDetails.class);
// 主表整体保存时统一替换子表,清空明细主键避免误更新历史明细。
detail.setObjId(null);
// 明细始终跟随当前主表路径编号,避免用户改编号后子表仍挂在旧路径上。
detail.setPathCode(bo.getPathCode());
+ // 设置默认启用标识
if (detail.getIsFlag() == null) {
detail.setIsFlag(1);
}
+ // 插入明细记录
basePathDetailsMapper.insert(detail);
}
}
@@ -229,9 +252,11 @@ public class BasePathInfoServiceImpl implements IBasePathInfoService {
* 按路径编号删除子表明细。
*/
private void deleteDetailsByPathCode(String pathCode) {
+ // 路径编号为空时直接返回
if (StringUtils.isBlank(pathCode)) {
return;
}
+ // 根据路径编号删除明细
basePathDetailsMapper.delete(Wrappers.lambdaQuery()
.eq(BasePathDetails::getPathCode, pathCode));
}
@@ -240,9 +265,11 @@ public class BasePathInfoServiceImpl implements IBasePathInfoService {
* 按路径编号批量删除子表明细。
*/
private void deleteDetailsByPathCodes(Collection pathCodes) {
+ // 路径编号集合为空时直接返回
if (pathCodes == null || pathCodes.isEmpty()) {
return;
}
+ // 根据路径编号集合批量删除明细
basePathDetailsMapper.delete(Wrappers.lambdaQuery()
.in(BasePathDetails::getPathCode, pathCodes));
}
@@ -251,21 +278,26 @@ public class BasePathInfoServiceImpl implements IBasePathInfoService {
* 批量挂载路径明细,避免 XML 嵌套 collection 造成分页列表 N+1 查询。
*/
private void attachDetails(List pathInfoList) {
+ // 路径信息列表为空时直接返回
if (pathInfoList == null || pathInfoList.isEmpty()) {
return;
}
+ // 提取所有路径编号并去重
List pathCodes = pathInfoList.stream()
.map(BasePathInfoVo::getPathCode)
.filter(StringUtils::isNotBlank)
.distinct()
.toList();
+ // 路径编号为空时设置空明细列表
if (pathCodes.isEmpty()) {
pathInfoList.forEach(item -> item.setDetails(List.of()));
return;
}
+ // 批量查询明细并按路径编号分组
Map> detailMap = basePathDetailsMapper.selectCustomBasePathDetailsVoByPathCodes(pathCodes)
.stream()
.collect(Collectors.groupingBy(BasePathDetailsVo::getPathCode));
+ // 将明细挂载到对应的路径信息
pathInfoList.forEach(item -> item.setDetails(detailMap.getOrDefault(item.getPathCode(), List.of())));
}
@@ -277,21 +309,25 @@ public class BasePathInfoServiceImpl implements IBasePathInfoService {
* @return 是否删除成功
*/
@Override
+ @CacheEvict(cacheNames = "wcs_cache", allEntries = true)
@DSTransactional
public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) {
+ // 主键集合为空时直接返回
if (ids == null || ids.isEmpty()) {
// 删除入口明确要求主键集合,空集合直接返回,避免拼出无意义SQL。
return false;
}
if(isValid){
- //TODO 做一些业务上的校验,判断是否需要校验
+ // TODO: 做一些业务上的校验,判断是否需要校验
}
+ // 根据主键集合查询路径编号
List pathCodes = baseMapper.selectPathCodesByIds(ids).stream()
.filter(StringUtils::isNotBlank)
.distinct()
.toList();
// 主子表删除放在同一个本地多数据源事务里,避免只删主表后明细残留。
deleteDetailsByPathCodes(pathCodes);
+ // 执行主表批量删除
return baseMapper.deleteByIds(ids) > 0;
}
}
diff --git a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BaseStoreInfoServiceImpl.java b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BaseStoreInfoServiceImpl.java
index 1ac687d..38c6744 100644
--- a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BaseStoreInfoServiceImpl.java
+++ b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/BaseStoreInfoServiceImpl.java
@@ -12,6 +12,8 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.dromara.wcs.domain.bo.BaseStoreInfoBo;
import org.dromara.wcs.domain.vo.BaseStoreInfoVo;
@@ -44,6 +46,7 @@ public class BaseStoreInfoServiceImpl implements IBaseStoreInfoService {
* @return 仓库信息
*/
@Override
+ @Cacheable(cacheNames = "wcs_cache", key = "#objId")
public BaseStoreInfoVo queryById(Long objId){
return baseMapper.selectCustomBaseStoreInfoVoById(objId);
}
@@ -139,10 +142,14 @@ public class BaseStoreInfoServiceImpl implements IBaseStoreInfoService {
@Override
@DSTransactional
public Boolean insertByBo(BaseStoreInfoBo bo) {
+ // 将 BO 对象转换为实体对象
BaseStoreInfo add = MapstructUtils.convert(bo, BaseStoreInfo.class);
+ // 保存前进行数据校验
validEntityBeforeSave(add);
+ // 执行插入操作
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
+ // 插入成功后将生成的主键回填到 BO 对象
bo.setObjId(add.getObjId());
}
return flag;
@@ -155,10 +162,14 @@ public class BaseStoreInfoServiceImpl implements IBaseStoreInfoService {
* @return 是否修改成功
*/
@Override
+ @CacheEvict(cacheNames = "wcs_cache", key = "#bo.objId")
@DSTransactional
public Boolean updateByBo(BaseStoreInfoBo bo) {
+ // 将 BO 对象转换为实体对象
BaseStoreInfo update = MapstructUtils.convert(bo, BaseStoreInfo.class);
+ // 保存前进行数据校验
validEntityBeforeSave(update);
+ // 执行更新操作
return baseMapper.updateById(update) > 0;
}
@@ -166,15 +177,19 @@ public class BaseStoreInfoServiceImpl implements IBaseStoreInfoService {
* 保存前的数据校验
*/
private void validEntityBeforeSave(BaseStoreInfo entity){
+ // 校验实体对象和仓库编号
if (entity == null || StringUtils.isBlank(entity.getStoreCode())) {
throw new ServiceException("仓库编号不能为空");
}
+ // 查询相同仓库编号的数量,排除当前记录(更新场景)
Long sameStoreCodeCount = baseMapper.selectCount(Wrappers.lambdaQuery()
.eq(BaseStoreInfo::getStoreCode, entity.getStoreCode())
.ne(entity.getObjId() != null, BaseStoreInfo::getObjId, entity.getObjId()));
+ // 校验仓库编号唯一性
if (sameStoreCodeCount > 0) {
throw new ServiceException("仓库编号已存在,请更换仓库编号");
}
+ // 设置默认启用标识
if (entity.getIsFlag() == null) {
// 仓库默认启用,保证库位维护时可以立即作为所属仓库使用。
entity.setIsFlag(1);
@@ -189,6 +204,7 @@ public class BaseStoreInfoServiceImpl implements IBaseStoreInfoService {
* @return 是否删除成功
*/
@Override
+ @CacheEvict(cacheNames = "wcs_cache", allEntries = true)
@DSTransactional
public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) {
if(isValid){
diff --git a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/LiveTaskDetailServiceImpl.java b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/LiveTaskDetailServiceImpl.java
index 719ce55..904bfda 100644
--- a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/LiveTaskDetailServiceImpl.java
+++ b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/LiveTaskDetailServiceImpl.java
@@ -12,6 +12,8 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.dromara.wcs.domain.bo.LiveTaskDetailBo;
import org.dromara.wcs.domain.vo.LiveTaskDetailVo;
@@ -47,6 +49,7 @@ public class LiveTaskDetailServiceImpl implements ILiveTaskDetailService {
* @return 实时任务明细
*/
@Override
+ @Cacheable(cacheNames = "wcs_cache", key = "#objId")
public LiveTaskDetailVo queryById(Long objId){
return baseMapper.selectCustomLiveTaskDetailVoById(objId);
}
@@ -153,10 +156,14 @@ public class LiveTaskDetailServiceImpl implements ILiveTaskDetailService {
@Override
@DSTransactional
public Boolean insertByBo(LiveTaskDetailBo bo) {
+ // 将 BO 对象转换为实体对象
LiveTaskDetail add = MapstructUtils.convert(bo, LiveTaskDetail.class);
+ // 保存前进行数据校验
validEntityBeforeSave(add);
+ // 执行插入操作
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
+ // 插入成功后将生成的主键回填到 BO 对象
bo.setObjId(add.getObjId());
}
return flag;
@@ -169,17 +176,23 @@ public class LiveTaskDetailServiceImpl implements ILiveTaskDetailService {
* @return 是否修改成功
*/
@Override
+ @CacheEvict(cacheNames = "wcs_cache", key = "#bo.objId")
@DSTransactional
public Boolean updateByBo(LiveTaskDetailBo bo) {
+ // 将 BO 对象转换为实体对象
LiveTaskDetail update = MapstructUtils.convert(bo, LiveTaskDetail.class);
+ // 校验主键标识
if (update == null || update.getObjId() == null) {
throw new ServiceException("主键标识不能为空");
}
+ // 查询原记录
LiveTaskDetail old = baseMapper.selectById(update.getObjId());
if (old == null) {
throw new ServiceException("实时任务明细不存在,无法修改");
}
+ // 保存前进行数据校验
validEntityBeforeSave(update);
+ // 执行更新操作
return baseMapper.updateById(update) > 0;
}
@@ -187,19 +200,25 @@ public class LiveTaskDetailServiceImpl implements ILiveTaskDetailService {
* 保存前的数据校验
*/
private void validEntityBeforeSave(LiveTaskDetail entity){
+ // 校验实体对象和任务编号
if (entity == null || StringUtils.isBlank(entity.getTaskCode())) {
throw new ServiceException("任务编号不能为空");
}
+ // 查询任务队列主表记录
LiveTaskQueue taskQueue = liveTaskQueueMapper.selectOne(Wrappers.lambdaQuery()
.eq(LiveTaskQueue::getTaskCode, entity.getTaskCode())
.last("limit 1"));
+ // 校验任务编号是否存在
if (taskQueue == null) {
throw new ServiceException("任务编号不存在,请先维护实时任务队列");
}
+ // 从主表继承空字段
inheritMasterFieldsWhenBlank(entity, taskQueue);
+ // 设置默认校验标识
if (entity.getIsValidate() == null) {
entity.setIsValidate(1);
}
+ // 设置默认启用标识
if (entity.getIsFlag() == null) {
entity.setIsFlag(1);
}
@@ -209,33 +228,43 @@ public class LiveTaskDetailServiceImpl implements ILiveTaskDetailService {
* 明细独立维护时从父任务继承空字段,保证手工新增的子表也与队列主表保持同一业务口径。
*/
private void inheritMasterFieldsWhenBlank(LiveTaskDetail detail, LiveTaskQueue taskQueue) {
+ // 物料编码为空时从主表继承
if (StringUtils.isBlank(detail.getMaterialCode())) {
detail.setMaterialCode(taskQueue.getMaterialCode());
}
+ // 托盘条码为空时从主表继承
if (StringUtils.isBlank(detail.getPalletBarcode())) {
detail.setPalletBarcode(taskQueue.getPalletBarcode());
}
+ // 物料条码为空时从主表继承
if (StringUtils.isBlank(detail.getMaterialBarcode())) {
detail.setMaterialBarcode(taskQueue.getMaterialBarcode());
}
+ // 物料数量为空时从主表继承
if (detail.getMaterialCount() == null) {
detail.setMaterialCount(taskQueue.getMaterialCount());
}
+ // 任务类型为空时从主表继承
if (detail.getTaskType() == null) {
detail.setTaskType(taskQueue.getTaskType());
}
+ // 任务分类为空时从主表继承
if (detail.getTaskCategory() == null) {
detail.setTaskCategory(taskQueue.getTaskCategory());
}
+ // 起始点位为空时从主表继承
if (StringUtils.isBlank(detail.getStartPoint())) {
detail.setStartPoint(taskQueue.getStartPoint());
}
+ // 目标点位为空时从主表继承
if (StringUtils.isBlank(detail.getEndPoint())) {
detail.setEndPoint(taskQueue.getEndPoint());
}
+ // 路径编号为空时从主表继承
if (StringUtils.isBlank(detail.getPathCode())) {
detail.setPathCode(taskQueue.getPathCode());
}
+ // 任务状态为空时从主表继承
if (detail.getTaskStatus() == null) {
detail.setTaskStatus(taskQueue.getTaskStatus());
}
@@ -249,15 +278,18 @@ public class LiveTaskDetailServiceImpl implements ILiveTaskDetailService {
* @return 是否删除成功
*/
@Override
+ @CacheEvict(cacheNames = "wcs_cache", allEntries = true)
@DSTransactional
public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) {
+ // 主键集合为空时直接返回
if (ids == null || ids.isEmpty()) {
// 删除入口明确要求主键集合,空集合直接返回,避免拼出无意义SQL。
return false;
}
if(isValid){
- //TODO 做一些业务上的校验,判断是否需要校验
+ // TODO: 做一些业务上的校验,判断是否需要校验
}
+ // 执行批量删除操作
return baseMapper.deleteByIds(ids) > 0;
}
}
diff --git a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/LiveTaskQueueServiceImpl.java b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/LiveTaskQueueServiceImpl.java
index 900f0d5..980eaab 100644
--- a/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/LiveTaskQueueServiceImpl.java
+++ b/ruoyi-modules/hw-wcs/src/main/java/org/dromara/wcs/service/impl/LiveTaskQueueServiceImpl.java
@@ -12,6 +12,8 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.dromara.wcs.domain.LiveTaskDetail;
import org.dromara.wcs.domain.bo.LiveTaskDetailBo;
@@ -50,6 +52,7 @@ public class LiveTaskQueueServiceImpl implements ILiveTaskQueueService {
* @return 实时任务队列
*/
@Override
+ @Cacheable(cacheNames = "wcs_cache", key = "#objId")
public LiveTaskQueueVo queryById(Long objId){
LiveTaskQueueVo vo = baseMapper.selectCustomLiveTaskQueueVoById(objId);
if (vo != null) {
@@ -162,11 +165,16 @@ public class LiveTaskQueueServiceImpl implements ILiveTaskQueueService {
@Override
@DSTransactional
public Boolean insertByBo(LiveTaskQueueBo bo) {
+ // 将 BO 对象转换为实体对象
LiveTaskQueue add = MapstructUtils.convert(bo, LiveTaskQueue.class);
+ // 保存前进行数据校验
validEntityBeforeSave(add);
+ // 执行插入操作
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
+ // 插入成功后将生成的主键回填到 BO 对象
bo.setObjId(add.getObjId());
+ // 保存任务明细
saveDetails(bo);
}
return flag;
@@ -179,18 +187,24 @@ public class LiveTaskQueueServiceImpl implements ILiveTaskQueueService {
* @return 是否修改成功
*/
@Override
+ @CacheEvict(cacheNames = "wcs_cache", key = "#bo.objId")
@DSTransactional
public Boolean updateByBo(LiveTaskQueueBo bo) {
+ // 查询原记录
LiveTaskQueue old = baseMapper.selectById(bo.getObjId());
if (old == null) {
throw new ServiceException("实时任务队列不存在,无法修改");
}
+ // 将 BO 对象转换为实体对象
LiveTaskQueue update = MapstructUtils.convert(bo, LiveTaskQueue.class);
+ // 保存前进行数据校验
validEntityBeforeSave(update);
// 任务编号是子表外键,先按旧编号删除旧明细,避免 ON UPDATE CASCADE 后删不到旧数据。
deleteDetailsByTaskCode(old.getTaskCode());
+ // 执行更新操作
boolean flag = baseMapper.updateById(update) > 0;
if (flag) {
+ // 更新成功后保存任务明细
saveDetails(bo);
}
return flag;
@@ -200,19 +214,24 @@ public class LiveTaskQueueServiceImpl implements ILiveTaskQueueService {
* 保存前的数据校验
*/
private void validEntityBeforeSave(LiveTaskQueue entity){
+ // 校验实体对象和任务编号
if (entity == null || StringUtils.isBlank(entity.getTaskCode())) {
throw new ServiceException("任务编号不能为空");
}
+ // 查询相同任务编号的数量,排除当前记录(更新场景)
Long sameTaskCodeCount = baseMapper.selectCount(Wrappers.lambdaQuery()
.eq(LiveTaskQueue::getTaskCode, entity.getTaskCode())
.ne(entity.getObjId() != null, LiveTaskQueue::getObjId, entity.getObjId()));
+ // 校验任务编号唯一性
if (sameTaskCodeCount > 0) {
throw new ServiceException("任务编号已存在,请更换任务编号");
}
+ // 设置默认任务状态
if (entity.getTaskStatus() == null) {
// 新建任务默认进入待执行,避免空状态在调度看板中无法归类。
entity.setTaskStatus(1);
}
+ // 设置默认启用标识
if (entity.getIsFlag() == null) {
entity.setIsFlag(1);
}
@@ -222,17 +241,22 @@ public class LiveTaskQueueServiceImpl implements ILiveTaskQueueService {
* 保存任务明细。
*/
private void saveDetails(LiveTaskQueueBo bo) {
+ // 获取任务明细列表
List details = bo.getDetails();
if (details == null || details.isEmpty()) {
return;
}
+ // 遍历明细列表并保存
for (LiveTaskDetailBo detailBo : details) {
+ // 将明细 BO 转换为实体对象
LiveTaskDetail detail = MapstructUtils.convert(detailBo, LiveTaskDetail.class);
// 子表跟随主表整体替换,清空旧主键避免误把历史明细主键带入新增流程。
detail.setObjId(null);
+ // 设置任务编号
detail.setTaskCode(bo.getTaskCode());
// 明细常用字段默认继承主表,减少人工重复录入导致的主子表业务信息不一致。
inheritMasterFieldsWhenBlank(detail, bo);
+ // 插入明细记录
liveTaskDetailMapper.insert(detail);
}
}
@@ -241,39 +265,51 @@ public class LiveTaskQueueServiceImpl implements ILiveTaskQueueService {
* 明细字段为空时继承主表字段。
*/
private void inheritMasterFieldsWhenBlank(LiveTaskDetail detail, LiveTaskQueueBo bo) {
+ // 物料编码为空时从主表继承
if (StringUtils.isBlank(detail.getMaterialCode())) {
detail.setMaterialCode(bo.getMaterialCode());
}
+ // 托盘条码为空时从主表继承
if (StringUtils.isBlank(detail.getPalletBarcode())) {
detail.setPalletBarcode(bo.getPalletBarcode());
}
+ // 物料条码为空时从主表继承
if (StringUtils.isBlank(detail.getMaterialBarcode())) {
detail.setMaterialBarcode(bo.getMaterialBarcode());
}
+ // 物料数量为空时从主表继承
if (detail.getMaterialCount() == null) {
detail.setMaterialCount(bo.getMaterialCount());
}
+ // 任务类型为空时从主表继承
if (detail.getTaskType() == null) {
detail.setTaskType(bo.getTaskType());
}
+ // 任务分类为空时从主表继承
if (detail.getTaskCategory() == null) {
detail.setTaskCategory(bo.getTaskCategory());
}
+ // 起始点位为空时从主表继承
if (StringUtils.isBlank(detail.getStartPoint())) {
detail.setStartPoint(bo.getStartPoint());
}
+ // 目标点位为空时从主表继承
if (StringUtils.isBlank(detail.getEndPoint())) {
detail.setEndPoint(bo.getEndPoint());
}
+ // 路径编号为空时从主表继承
if (StringUtils.isBlank(detail.getPathCode())) {
detail.setPathCode(bo.getPathCode());
}
+ // 任务状态为空时从主表继承
if (detail.getTaskStatus() == null) {
detail.setTaskStatus(bo.getTaskStatus());
}
+ // 设置默认校验标识
if (detail.getIsValidate() == null) {
detail.setIsValidate(1);
}
+ // 设置默认启用标识
if (detail.getIsFlag() == null) {
detail.setIsFlag(1);
}
@@ -283,9 +319,11 @@ public class LiveTaskQueueServiceImpl implements ILiveTaskQueueService {
* 按任务编号删除子表明细。
*/
private void deleteDetailsByTaskCode(String taskCode) {
+ // 任务编号为空时直接返回
if (StringUtils.isBlank(taskCode)) {
return;
}
+ // 根据任务编号删除明细
liveTaskDetailMapper.delete(Wrappers.lambdaQuery()
.eq(LiveTaskDetail::getTaskCode, taskCode));
}
@@ -294,9 +332,11 @@ public class LiveTaskQueueServiceImpl implements ILiveTaskQueueService {
* 按任务编号批量删除子表明细。
*/
private void deleteDetailsByTaskCodes(Collection taskCodes) {
+ // 任务编号集合为空时直接返回
if (taskCodes == null || taskCodes.isEmpty()) {
return;
}
+ // 根据任务编号集合批量删除明细
liveTaskDetailMapper.delete(Wrappers.lambdaQuery()
.in(LiveTaskDetail::getTaskCode, taskCodes));
}
@@ -305,21 +345,26 @@ public class LiveTaskQueueServiceImpl implements ILiveTaskQueueService {
* 批量挂载任务明细,避免 XML 嵌套 collection 造成分页列表 N+1 查询。
*/
private void attachDetails(List taskQueueList) {
+ // 任务队列列表为空时直接返回
if (taskQueueList == null || taskQueueList.isEmpty()) {
return;
}
+ // 提取所有任务编号并去重
List taskCodes = taskQueueList.stream()
.map(LiveTaskQueueVo::getTaskCode)
.filter(StringUtils::isNotBlank)
.distinct()
.toList();
+ // 任务编号为空时设置空明细列表
if (taskCodes.isEmpty()) {
taskQueueList.forEach(item -> item.setDetails(List.of()));
return;
}
+ // 批量查询明细并按任务编号分组
Map> detailMap = liveTaskDetailMapper.selectCustomLiveTaskDetailVoByTaskCodes(taskCodes)
.stream()
.collect(Collectors.groupingBy(LiveTaskDetailVo::getTaskCode));
+ // 将明细挂载到对应的任务队列
taskQueueList.forEach(item -> item.setDetails(detailMap.getOrDefault(item.getTaskCode(), List.of())));
}
@@ -331,21 +376,25 @@ public class LiveTaskQueueServiceImpl implements ILiveTaskQueueService {
* @return 是否删除成功
*/
@Override
+ @CacheEvict(cacheNames = "wcs_cache", allEntries = true)
@DSTransactional
public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) {
+ // 主键集合为空时直接返回
if (ids == null || ids.isEmpty()) {
// 删除入口明确要求主键集合,空集合直接返回,避免拼出无意义SQL。
return false;
}
if(isValid){
- //TODO 做一些业务上的校验,判断是否需要校验
+ // TODO: 做一些业务上的校验,判断是否需要校验
}
+ // 根据主键集合查询任务编号
List taskCodes = baseMapper.selectTaskCodesByIds(ids).stream()
.filter(StringUtils::isNotBlank)
.distinct()
.toList();
// 主子表删除放在同一个本地多数据源事务里,避免只删主表后明细残留。
deleteDetailsByTaskCodes(taskCodes);
+ // 执行主表批量删除
return baseMapper.deleteByIds(ids) > 0;
}
}
diff --git a/ruoyi-modules/hw-wcs/src/main/resources/mapper/wcs/BaseDeviceHostMapper.xml b/ruoyi-modules/hw-wcs/src/main/resources/mapper/wcs/BaseDeviceHostMapper.xml
index 087c993..e566697 100644
--- a/ruoyi-modules/hw-wcs/src/main/resources/mapper/wcs/BaseDeviceHostMapper.xml
+++ b/ruoyi-modules/hw-wcs/src/main/resources/mapper/wcs/BaseDeviceHostMapper.xml
@@ -72,4 +72,5 @@
from base_device_host t
${ew.getCustomSqlSegment}
+