fix(watch): 修复手表设备绑定时部门归属逻辑错误

- 修改异步导入服务中手表设备的部门归属逻辑,以用户部门为准而非设备原有部门
- 为部门统计数据服务添加详细的JavaDoc注释说明业务逻辑
- 优化部门统计数据显示逻辑,支持一级、二级、三级部门的正确展示
- 移除手表绑定时的单位限制校验,允许跨单位绑定
- 在设备绑定时根据员工信息更新手表的单位组织编码
- 修复上传工具类中tar/apk文件上传的日志描述
- 调整Shiro配置中文件下载路径的安全策略
This commit is contained in:
2026-05-06 14:59:19 +08:00
parent 42c7d223c9
commit 86fa43c284
6 changed files with 130 additions and 18 deletions
@@ -336,8 +336,9 @@ public class AsyncImportService {
updatedWatchDevice.setBindDate(watchDevice.getBindDate()==null?new Date():watchDevice.getBindDate());
updatedWatchDevice.setUpdateDate(new Date());
updatedWatchDevice.setUpdateBy(sysUser.getId());
updatedWatchDevice.setDeptId(device.getDeptId());
updatedWatchDevice.setOrgCode(device.getOrgCode());
// 这里修改成以用户的部门为准,更新手表的所属部门
updatedWatchDevice.setDeptId(sysCache.getDepartIdByOrgCode(user.getOrgCode()));
updatedWatchDevice.setOrgCode(user.getOrgCode());
updateList.add(updatedWatchDevice);
}
info1.setHandleMsg(index + "/" + count);
@@ -440,7 +440,9 @@ public class WatchDeviceController extends JeecgController<WatchDevice, IWatchDe
return Result.error("已存在执行中的导入任务,请稍后导入或点击【查看导入任务】查看任务导出进度!");
}
if (!StringUtils.hasLength(deptId) || "undefined".equals(deptId)) {
throw new JeecgBootException("部门不能为空");
// throw new JeecgBootException("部门不能为空");
//可为空,空则算入新疆油田公司
deptId = sysCache.getDepartIdByOrgCode("A01");
}
String sheetName = "设备导入";
String fileName = sheetName + "_" + DateUtils.getDate("yyyyMMddHHmmss");
@@ -595,11 +595,32 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
filter.setOrgCodeLeaf("A99A99A99");
}
/**
* 查询部门统计数据(分页)
*
* @param filter 查询过滤条件,包含部门编码、时间范围、分页参数等
* @return 分页的部门统计数据列表
*/
@Override
public IPage<DeptStatData> getDeptData(DepartStatsFilter filter) {
return getDeptData(filter, true).pagedData();
}
/**
* 查询部门统计数据的核心实现
* <p>
* 执行流程:
* 1. 权限校验,补全过滤条件
* 2. 查询各部门设备总数 Map 和绑定/佩戴数 Map
* 3. 根据过滤条件确定最终需要展示的部门列表
* 4. 查询各部门异常预警数量
* 5. 按需分页,将部门列表转换为统计 DTO
* </p>
*
* @param filter 查询过滤条件
* @param paged 是否需要分页,true=分页查询,false=全量查询(用于导出等场景)
* @return 包含统计数据和中间结果的传输对象
*/
private DepartStatTransfer getDeptData(DepartStatsFilter filter, boolean paged) {
authCheck(filter);
DepartStatTransfer transfer = new DepartStatTransfer()
@@ -639,12 +660,29 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
return transfer;
}
/**
* 将单个部门实体转换为统计 DTO
* <p>
* orgCode 编码规则:3位一层,如 A01=一级,A01A01=二级,A01A01A01=三级
* 区分三种视图:
* - 一级部门(orgCode 长度为 3):展示自身信息,叶子名称置为 "-",取自身设备总数
* - 二级部门(orgCode 长度为 6):同一级,展示自身信息,叶子名称置为 "-",取自身设备总数
* - 三级部门(orgCode 长度为 9):展示所属上级名称 + 自身名称,绑定/佩戴数取自身数据
* 当 allSecond=true(全量视图)时,绑定/佩戴数始终取各部门自身数据
* </p>
*
* @param depart 部门实体
* @param transfer 包含所有统计中间数据的传输对象
* @return 填充完毕的部门统计 DTO
*/
private DeptStatData transferData(SysDepart depart, DepartStatTransfer transfer) {
DeptStatData deptStatData = new DeptStatData();
String orgCode = null != depart ? depart.getOrgCode() : null;
boolean allSecond = StrUtil.isEmpty(transfer.filter().getOrgCode());
boolean isFirst = StrUtil.isNotEmpty(orgCode) && orgCode.length() == 3;
boolean isSecond = StrUtil.isNotEmpty(orgCode) && orgCode.length() == 6;
if (isSecond) {
if (isFirst || isSecond) {
// 一级/二级部门行:展示自身信息,叶子名称占位
deptStatData.setOrgCode(orgCode);
deptStatData.setOrgCodeLeaf(orgCode);
deptStatData.setOrgCodeName(depart.getDepartName());
@@ -655,6 +693,7 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
.getDeviceCount()
);
} else {
// 三级部门行:展示所属上级名称 + 自身名称
deptStatData.setOrgCode(Optional.ofNullable(transfer.secondDepart()).map(SysDepart::getOrgCode).orElse(""));
deptStatData.setOrgCodeLeaf(orgCode);
deptStatData.setOrgCodeName(Optional.ofNullable(transfer.secondDepart()).map(SysDepart::getDepartName).orElse(""));
@@ -662,7 +701,7 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
}
fillWatchNums(
deptStatData,
(!allSecond && isSecond)
(!allSecond && (isFirst || isSecond))
?
transfer.secondDepartBindAndWearNum()
:
@@ -670,7 +709,7 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
);
fillWarningNums(
deptStatData,
(!allSecond && isSecond)
(!allSecond && (isFirst || isSecond))
?
transfer.secondDepartAbnormalCount()
:
@@ -679,12 +718,30 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
return deptStatData;
}
/**
* 填充手表绑定与佩戴相关数量
* <p>
* assignedDeviceNums = 已绑定设备数
* assignedUserNums = 已佩戴人数
* undistributedUserNums = 已绑定但未佩戴数(bindCount - wearCount
* </p>
*/
private void fillWatchNums(DeptStatData deptStatData, DepartDeviceNum departDeviceNum) {
deptStatData.setAssignedDeviceNums(Optional.ofNullable(departDeviceNum.getBindCount()).orElse(0));
deptStatData.setAssignedUserNums(Optional.ofNullable(departDeviceNum.getWearCount()).orElse(0));
deptStatData.setUndistributedUserNums(deptStatData.getAssignedDeviceNums() - deptStatData.getAssignedUserNums());
}
/**
* 填充各健康指标的预警数量
* <p>
* 从 abnormalCountMap 中按事件类型取值:
* stress → 压力预警数
* heart_rate → 心率预警数
* spo2 → 血氧预警数
* temperature_anomalies → 体温预警数
* </p>
*/
private void fillWarningNums(DeptStatData deptStatData, Map<String, AbnormalCount> abnormalCountMap) {
deptStatData.setStressNums(Optional.ofNullable(abnormalCountMap.get("stress")).map(AbnormalCount::getWarningCount).orElse(0));
deptStatData.setHeartRateNums(Optional.ofNullable(abnormalCountMap.get("heart_rate")).map(AbnormalCount::getWarningCount).orElse(0));
@@ -722,6 +779,10 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
return sysCache.getDepartByOrgCode(filter.getOrgCodeLeaf());
}
/**
* 查询各部门的设备总数 Map(orgCode → 设备数)
* 当过滤条件指定了三级部门(orgCodeLeaf 不为空)时,设备总数无需展示,直接返回空 Map
*/
private Map<String, DepartDeviceNum> getDepartDeviceNumMap(DepartStatsFilter filter) {
if (StrUtil.isNotEmpty(filter.getOrgCodeLeaf())) {
return Collections.emptyMap();
@@ -732,6 +793,9 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
.collect(Collectors.toMap(DepartDeviceNum::getOrgCode, Function.identity()));
}
/**
* 查询各部门的设备绑定数与佩戴数 Map(orgCode → 绑定/佩戴数)
*/
private Map<String, DepartDeviceNum> getDepartBindAndWearNumMap(DepartStatsFilter filter) {
return watchStatisticsMapper.deviceBindAndWearNums(filter)
.stream()
@@ -739,6 +803,10 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
.collect(Collectors.toMap(DepartDeviceNum::getOrgCode, Function.identity()));
}
/**
* 查询各部门各事件类型的异常预警数量,结构为双层 Map:
* orgCode → (eventType → AbnormalCount)
*/
private Map<String, Map<String, AbnormalCount>> getDepartAbnormalCountMap(DepartStatsFilter filter) {
return watchStatisticsMapper.abnormalWarningCount(filter)
.stream()
@@ -746,6 +814,12 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
.collect(Collectors.groupingBy(AbnormalCount::getOrgCode, Collectors.toMap(AbnormalCount::getEventType, Function.identity())));
}
/**
* 汇总二级部门下所有三级部门的绑定/佩戴数,用于二级部门行的数据展示
* <p>
* 仅在"指定了二级部门但未指定三级部门"时有效,其他情况返回 null
* </p>
*/
private DepartDeviceNum getSecondDepartBindAndWearNum(DepartStatsFilter filter,
Map<String, DepartDeviceNum> departBindAndWearNumMap) {
if (StrUtil.isNotEmpty(filter.getOrgCodeLeaf()) || StrUtil.isEmpty(filter.getOrgCode())) {
@@ -764,6 +838,13 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
return n1;
}
/**
* 汇总二级部门下所有三级部门的异常预警数,用于二级部门行的数据展示
* <p>
* 仅在"指定了二级部门但未指定三级部门"时有效,其他情况返回 null
* 合并时按 eventType 累加 warningCount
* </p>
*/
private Map<String, AbnormalCount> getSecondDepartAbnormalCount(DepartStatsFilter filter,
Map<String, Map<String, AbnormalCount>> departAbnormalCountMap) {
if (StrUtil.isNotEmpty(filter.getOrgCodeLeaf()) || StrUtil.isEmpty(filter.getOrgCode())) {
@@ -778,6 +859,14 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
return n1;
}
/**
* 根据过滤条件确定最终需要展示的部门列表,分三种情况:
* <ul>
* <li>指定三级部门(orgCodeLeaf 不为空):仅返回该三级部门(无绑定数据则返回空)</li>
* <li>指定二级部门(orgCode 不为空):返回该部门本身 + 其下有绑定数据的子部门,按层级(orgCode 长度)升序后再按排序字段排序</li>
* <li>全量(orgCode 为空):返回所有有设备数据的部门,同样按层级升序后再按排序字段排序,确保一级部门排在最前</li>
* </ul>
*/
private List<SysDepart> getFinalDeparts(DepartStatsFilter filter,
Map<String, DepartDeviceNum> departDeviceNumMap,
Map<String, DepartDeviceNum> departBindAndWearNumMap) {
@@ -804,7 +893,10 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
return departDeviceNumMap.keySet()
.stream()
.map(sysBaseAPI::getDepartIdsByOrgCodeNew)
.sorted(this::departComparator)
.sorted(
Comparator.<SysDepart>comparingInt(sysDepart -> sysDepart.getOrgCode().length())
.thenComparing(this::departComparator)
)
.collect(Collectors.toList());
}
}
@@ -825,6 +917,9 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
return order1.compareTo(order2);
}
/**
* 对内存中的部门列表执行分页截取
*/
private List<SysDepart> getPagedDeparts(List<SysDepart> sysDeparts, DepartStatsFilter filter) {
return getSubList(sysDeparts, filter.getPageNo(), filter.getPageSize());
}
@@ -2089,8 +2184,8 @@ public class DeptStatisticDataServiceImpl extends ServiceImpl<DeptStatisticDataM
? sysCache.getDepartByOrgCode(orgCodeLeaf).getDepartName()
: (
StrUtil.isNotBlank(orgCode)
? sysCache.getDepartByOrgCode(orgCode).getDepartName()
: "");
? sysCache.getDepartByOrgCode(orgCode).getDepartName()
: "");
LoginUser loginUser = GlobalUtils.getLoginUser();
Boolean flag = sysBaseAPI.getFinishFlagByCode(WATCH_DEPT_DATA_STAT_DETAIL, DOING);
if (!flag) {
@@ -300,11 +300,11 @@ public class WatchDeviceServiceImpl extends ServiceImpl<WatchDeviceMapper, Watch
// 新增手表只能绑定给本单位的人
LoginUser userById = sysBaseAPI.getUserById(dto.getBindUserId());
if (userById.getOrgCode() == null || watchDevice.getOrgCode() == null ||
userById.getOrgCode().length() < 6 || watchDevice.getOrgCode().length() < 6 ||
!userById.getOrgCode().substring(0, 6).equals(watchDevice.getOrgCode().substring(0, 6))) {
return Result.error("手表只能绑定给本单位人员");
}
// if (userById.getOrgCode() == null || watchDevice.getOrgCode() == null ||
// userById.getOrgCode().length() < 6 || watchDevice.getOrgCode().length() < 6 ||
// !userById.getOrgCode().substring(0, 6).equals(watchDevice.getOrgCode().substring(0, 6))) {
// return Result.error("手表只能绑定给本单位人员");
// }
if (isDeviceAlreadyBound(watchDevice, dto)) {
return Result.error("该设备已被绑定");
@@ -330,6 +330,13 @@ public class WatchDeviceServiceImpl extends ServiceImpl<WatchDeviceMapper, Watch
}
// 更新工具信息
watchDevice.setBindDate(dto.getBindDate());
// 根据员工更新手表单位信息(如果单位编码大于6位,取6位)
String orgCode = userById.getOrgCode();
if(StrUtil.isNotBlank(orgCode) && orgCode.length() > 6){
orgCode = orgCode.substring(0, 6);
}
watchDevice.setOrgCode(orgCode);
watchDevice.setDeptId(sysCache.getDepartIdByOrgCode(orgCode));
bindUserToDevice(watchDevice, dto);
if (StrUtil.isNotBlank(dto.getBindUserId())) {
// 初始化开关数据
@@ -488,6 +495,13 @@ public class WatchDeviceServiceImpl extends ServiceImpl<WatchDeviceMapper, Watch
updateWrapper.eq("id", watchDevice.getId());
updateWrapper.set("bind_user_id", dto.getBindUserId());
updateWrapper.set("bind_date", watchDevice.getBindDate());
if(StrUtil.isNotBlank(watchDevice.getOrgCode())){
updateWrapper.set("org_code", watchDevice.getOrgCode());
}
if (StrUtil.isNotBlank(watchDevice.getDeptId())){
updateWrapper.set("dept_id", watchDevice.getDeptId());
}
this.update(updateWrapper);
}
@@ -128,9 +128,9 @@ public class MyUploadUtil {
bizPath = StrAttackFilter.filter(bizPath);
// .tar 文件固定走 MyUpload
if (fileName != null && fileName.endsWith(".tar")) {
log.info("上传文件为tar文件,使用MyUploadUtil上传");
log.info("上传文件为tar/apk文件,使用MyUploadUtil上传");
try {
return upload(file.getInputStream(), bizPath, fileName);
return upload(file.getInputStream(), bizPath, fileName,true);
} catch (Exception e) {
log.error("upload error {}", e.getMessage(), e);
return null;
@@ -185,8 +185,8 @@ public class ShiroConfig {
filterChainDefinitionMap.put("/version/getSysVersionDetailByPackageName", "anon");
filterChainDefinitionMap.put("/sys/api/notice/selectIosUpdate", "anon");
filterChainDefinitionMap.put("/sys/upload/down/**", "anon");
filterChainDefinitionMap.put("/sys/upload/show/**", "anon");
// filterChainDefinitionMap.put("/sys/upload/down/**", "anon");
// filterChainDefinitionMap.put("/sys/upload/show/**", "anon");
// 添加自己的过滤器并且取名为jwt
Map<String, Filter> filterMap = new HashMap<String, Filter>(1);