Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
2025-11-21 20:29:30 +08:00
22 changed files with 944 additions and 236 deletions

View File

@ -176,7 +176,7 @@ mybatis-plus:
# 数据加密 # 数据加密
mybatis-encryptor: mybatis-encryptor:
# 是否开启加密 # 是否开启加密
enable: false enable: true
# 默认加密算法 # 默认加密算法
algorithm: BASE64 algorithm: BASE64
# 编码方式 BASE64/HEX。默认BASE64 # 编码方式 BASE64/HEX。默认BASE64

View File

@ -0,0 +1,65 @@
package org.dromara.contractor.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.web.core.BaseController;
import org.dromara.contractor.domain.dto.attendancemachineuser.SubAttendanceMachineSendInfoUserReq;
import org.dromara.contractor.domain.dto.attendancemachineuser.SubAttendanceMachineUserQueryReq;
import org.dromara.contractor.domain.dto.attendancemachineuser.SubAttendanceMachineUserRemoveReq;
import org.dromara.contractor.domain.vo.SubAttendanceMachineUserVo;
import org.dromara.contractor.service.ISubAttendanceMachineUserService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 分包考勤机用户
*
* @author lilemy
* @date 2025-11-21 15:38
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/contractor/attendanceMachineUser")
public class SubAttendanceMachineUserController extends BaseController {
private final ISubAttendanceMachineUserService subAttendanceMachineUserService;
/**
* 查询考勤机用户列表
*/
@SaCheckPermission("contractor:attendanceMachineUser:list")
@GetMapping("/list")
public R<List<SubAttendanceMachineUserVo>> list(SubAttendanceMachineUserQueryReq req) {
return R.ok(subAttendanceMachineUserService.queryList(req));
}
/**
* 下发用户到考勤机中
*/
@SaCheckPermission("contractor:attendanceMachineUser:add")
@Log(title = "分包考勤机用户", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/send")
public R<String> sendPersonInfo(@Validated @RequestBody SubAttendanceMachineSendInfoUserReq req) {
return R.ok(subAttendanceMachineUserService.sendPersonInfo(req));
}
/**
* 删除考勤机用户
*/
@SaCheckPermission("contractor:attendanceMachineUser:remove")
@Log(title = "分包考勤机用户", businessType = BusinessType.DELETE)
@RepeatSubmit()
@DeleteMapping()
public R<Void> remove(@Validated @RequestBody SubAttendanceMachineUserRemoveReq req) {
return toAjax(subAttendanceMachineUserService.remove(req));
}
}

View File

@ -0,0 +1,32 @@
package org.dromara.contractor.domain.dto.attendancemachineuser;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* @author lilemy
* @date 2025-11-21 15:36
*/
@Data
public class SubAttendanceMachineSendInfoUserReq implements Serializable {
@Serial
private static final long serialVersionUID = -6517799483848691652L;
/**
* 考勤机主键ID
*/
@NotNull(message = "考勤机不能为空")
private Long machineId;
/**
* 用户ID列表
*/
@NotNull(message = "用户列表不能为空")
private List<Long> userIds;
}

View File

@ -0,0 +1,46 @@
package org.dromara.contractor.domain.dto.attendancemachineuser;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @author lilemy
* @date 2025-11-21 15:36
*/
@Data
public class SubAttendanceMachineUserQueryReq implements Serializable {
@Serial
private static final long serialVersionUID = -1629819732452840391L;
/**
* 考勤机主键ID
*/
@NotNull(message = "考勤机不能为空")
private Long machineId;
/**
* 项目ID
*/
@NotNull(message = "项目不能为空")
private Long projectId;
/**
* 分包ID
*/
@NotNull(message = "分包不能为空")
private Long contractorId;
/**
* 用户名称
*/
private String userName;
/**
* 标识(是否下发)
*/
private Integer identifying;
}

View File

@ -0,0 +1,31 @@
package org.dromara.contractor.domain.dto.attendancemachineuser;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* @author lilemy
* @date 2025-11-21 15:36
*/
@Data
public class SubAttendanceMachineUserRemoveReq implements Serializable {
@Serial
private static final long serialVersionUID = -8880884070612972760L;
/**
* 考勤机主键ID
*/
@NotNull(message = "考勤机不能为空")
private Long machineId;
/**
* 用户ID列表
*/
@NotNull(message = "用户列表不能为空")
private List<Long> userIds;
}

View File

@ -1,28 +1,23 @@
package org.dromara.project.domain; package org.dromara.contractor.domain.vo;
import lombok.Data; import lombok.Data;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.Date;
/** /**
* 考勤机用户对象 bus_attendance_machine_user * 考勤机用户视图对象
* *
* @author lilemy * @author lilemy
* @date 2025-10-15 * @date 2025-11-21
*/ */
@Data @Data
public class BusAttendanceMachineUser implements Serializable { public class SubAttendanceMachineUserVo implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
private Long id;
/** /**
* 考勤机主键ID * 考勤机主键ID
*/ */
@ -31,26 +26,21 @@ public class BusAttendanceMachineUser implements Serializable {
/** /**
* 班组ID * 班组ID
*/ */
private Long teamId; private Long contractorId;
/** /**
* 用户ID * 用户ID
*/ */
private Long userId; private Long userId;
/**
* 用户名称
*/
private String userName;
/** /**
* 标识是否下发 * 标识是否下发
*/ */
private Integer identifying; private Integer identifying;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
} }

View File

@ -0,0 +1,41 @@
package org.dromara.contractor.service;
import org.dromara.contractor.domain.dto.attendancemachineuser.SubAttendanceMachineSendInfoUserReq;
import org.dromara.contractor.domain.dto.attendancemachineuser.SubAttendanceMachineUserQueryReq;
import org.dromara.contractor.domain.dto.attendancemachineuser.SubAttendanceMachineUserRemoveReq;
import org.dromara.contractor.domain.vo.SubAttendanceMachineUserVo;
import java.util.List;
/**
* 分包考勤机用户Service接口
*
* @author lilemy
* @date 2025-11-21 15:33
*/
public interface ISubAttendanceMachineUserService {
/**
* 查询符合条件的考勤机用户列表
*
* @param req 查询条件
* @return 考勤机用户列表
*/
List<SubAttendanceMachineUserVo> queryList(SubAttendanceMachineUserQueryReq req);
/**
* 下发用户到考勤机中
*
* @param req 下发用户参数
* @return 下发结果
*/
String sendPersonInfo(SubAttendanceMachineSendInfoUserReq req);
/**
* 删除考勤机用户
*
* @param req 删除参数
* @return 删除结果
*/
Boolean remove(SubAttendanceMachineUserRemoveReq req);
}

View File

@ -0,0 +1,182 @@
package org.dromara.contractor.service.impl;
import cn.hutool.core.collection.CollUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.contractor.domain.SubConstructionUser;
import org.dromara.contractor.domain.dto.attendancemachineuser.SubAttendanceMachineSendInfoUserReq;
import org.dromara.contractor.domain.dto.attendancemachineuser.SubAttendanceMachineUserQueryReq;
import org.dromara.contractor.domain.dto.attendancemachineuser.SubAttendanceMachineUserRemoveReq;
import org.dromara.contractor.domain.vo.SubAttendanceMachineUserVo;
import org.dromara.contractor.service.ISubAttendanceMachineUserService;
import org.dromara.contractor.service.ISubConstructionUserService;
import org.dromara.mobileAttendanceMachine.DeviceMessageSender;
import org.dromara.mobileAttendanceMachine.KqjEntity;
import org.dromara.project.domain.BusAttendanceMachine;
import org.dromara.project.service.IBusAttendanceMachineService;
import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.service.ISysOssService;
import org.dromara.system.service.ISysUserService;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
/**
* 分包考勤机用户Service业务层处理
*
* @author lilemy
* @date 2025-11-21 15:35
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class SubAttendanceMachineUserServiceImpl implements ISubAttendanceMachineUserService {
private final IBusAttendanceMachineService attendanceMachineService;
private final DeviceMessageSender deviceMessageSender;
private final ISubConstructionUserService constructionUserService;
private final ISysUserService userService;
private final ISysOssService ossService;
/**
* 查询符合条件的考勤机用户列表
*
* @param req 查询条件
* @return 考勤机用户列表
*/
@Override
public List<SubAttendanceMachineUserVo> queryList(SubAttendanceMachineUserQueryReq req) {
List<SysUserVo> userVoList = userService.selectFbUserList(req.getProjectId(), List.of(req.getContractorId()), req.getUserName());
if (CollUtil.isEmpty(userVoList)) {
return CollUtil.newArrayList();
}
BusAttendanceMachine machine = attendanceMachineService.getById(req.getMachineId());
if (machine == null) {
throw new ServiceException("考勤机不存在", HttpStatus.NOT_FOUND);
}
// 判断选择分包和考勤机是否一致
if (StringUtils.isBlank(machine.getContractors())) {
throw new ServiceException("考勤机未绑定分包", HttpStatus.NOT_FOUND);
}
if (!Arrays.stream(machine.getContractors().split(","))
.map(Long::parseLong)
.collect(Collectors.toSet()).contains(req.getContractorId())) {
throw new ServiceException("所选分包与考勤机不匹配", HttpStatus.NOT_FOUND);
}
// 获取考勤机里的用户
Set<String> userIdList = new HashSet<>();
try {
KqjEntity.CommonResponse response = deviceMessageSender.getAllUsers(machine.getSn());
int code = response.getData().getCode();
if (code == 0 || code == 200) {
log.info("获取考勤机数据成功:{}", response.getData());
String[] userIds = response.getData().getUserIds();
userIdList = Arrays.stream(userIds).collect(Collectors.toSet());
}
} catch (Exception e) {
log.error("获取考勤机用户失败sn{}", machine.getSn(), e);
throw new ServiceException("获取考勤机用户失败", HttpStatus.ERROR);
}
Set<String> finalUserIdList = userIdList;
return userVoList.stream().map(userVo -> {
SubAttendanceMachineUserVo vo = new SubAttendanceMachineUserVo();
vo.setMachineId(req.getMachineId());
vo.setContractorId(req.getContractorId());
vo.setUserId(userVo.getUserId());
vo.setUserName(userVo.getUserName());
if (CollUtil.isEmpty(finalUserIdList)) {
vo.setIdentifying(0);
} else if (finalUserIdList.contains(userVo.getUserId().toString())) {
vo.setIdentifying(1);
} else {
vo.setIdentifying(0);
}
return vo;
}).toList();
}
/**
* 下发用户到考勤机中
*
* @param req 下发用户参数
* @return 下发结果
*/
@Override
public String sendPersonInfo(SubAttendanceMachineSendInfoUserReq req) {
Long machineId = req.getMachineId();
List<Long> userIds = req.getUserIds();
BusAttendanceMachine machine = attendanceMachineService.getById(machineId);
if (machine == null) {
throw new ServiceException("考勤机不存在");
}
List<SubConstructionUser> userList = constructionUserService.lambdaQuery()
.in(SubConstructionUser::getSysUserId, userIds)
.list();
// 获取用户人脸照信息
List<Long> faceIds = userList.stream().map(SubConstructionUser::getFacePic).map(Long::parseLong).distinct().toList();
List<SysOssVo> ossVos = ossService.listByIds(faceIds);
Map<Long, SysOssVo> ossVoMap = ossVos.stream()
.collect(Collectors.toMap(SysOssVo::getOssId, v -> v));
// 返回数据
StringBuilder sb = new StringBuilder();
sb.append("用户:[");
int count = 0;
for (SubConstructionUser user : userList) {
String facePic = user.getFacePic();
if (StringUtils.isBlank(facePic)) {
sb.append(user.getUserName()).append(" ");
count++;
continue;
}
SysOssVo ossVo = ossVoMap.get(Long.parseLong(facePic));
if (ossVo == null) {
sb.append(user.getUserName()).append(" ");
count++;
continue;
}
Boolean result = deviceMessageSender.sendPersonnelInformation(machine.getSn(),
user.getSysUserId().toString(), user.getUserName(), ossVo.getUrl());
log.info("考勤机 Sn{} ,用户:{} 下发成功", machine.getSn(), user.getUserName());
if (!result) {
sb.append(user.getUserName()).append(" ");
count++;
}
}
sb.append("] 下发失败");
return count == 0 ? "下发成功" : sb.toString();
}
/**
* 删除考勤机用户
*
* @param req 删除参数
* @return 删除结果
*/
@Override
public Boolean remove(SubAttendanceMachineUserRemoveReq req) {
Long machineId = req.getMachineId();
List<Long> userIds = req.getUserIds();
BusAttendanceMachine machine = attendanceMachineService.getById(machineId);
if (machine == null) {
throw new ServiceException("考勤机不存在", HttpStatus.NOT_FOUND);
}
String[] userIdArray = userIds.stream()
.map(String::valueOf) // 将 Long 转成 String
.toArray(String[]::new);
try {
KqjEntity.CommonResponse response = deviceMessageSender.batchDeleteUsers(machine.getSn(), userIdArray);
return response.getData().getCode() == 0 || response.getData().getCode() == 200;
} catch (Exception e) {
throw new ServiceException("删除考勤机用户失败", HttpStatus.ERROR);
}
}
}

View File

@ -3,49 +3,49 @@ package org.dromara.gps.service.impl;
import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONObject; import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.vehicle.domain.VehVehicleInfo;
import org.dromara.vehicle.domain.vo.VehVehicleInfoVo;
import org.dromara.vehicle.service.IVehVehicleInfoService;
import org.dromara.vehicle.service.IVehVehicleTripService;
import org.dromara.websocket.websocket.service.InitOnStartWebSocketServer;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import org.dromara.common.mybatis.core.page.TableDataInfo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.common.redis.utils.RedisUtils; import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.websocket.dto.WebSocketMessageDto; import org.dromara.common.websocket.dto.WebSocketMessageDto;
import org.dromara.common.websocket.holder.WebSocketSessionHolder; import org.dromara.common.websocket.holder.WebSocketSessionHolder;
import org.dromara.common.websocket.utils.WebSocketUtils; import org.dromara.common.websocket.utils.WebSocketUtils;
import org.dromara.gps.domain.GpsEquipment;
import org.dromara.gps.domain.GpsManmachine; import org.dromara.gps.domain.GpsManmachine;
import org.dromara.gps.domain.bo.GpsEquipmentBo;
import org.dromara.gps.domain.bo.GpsEquipmentSonBo; import org.dromara.gps.domain.bo.GpsEquipmentSonBo;
import org.dromara.gps.domain.vo.GpsEquipmentSonVo; import org.dromara.gps.domain.vo.GpsEquipmentSonVo;
import org.dromara.gps.domain.vo.GpsEquipmentVo;
import org.dromara.gps.domain.vo.GpsProjectVo; import org.dromara.gps.domain.vo.GpsProjectVo;
import org.dromara.gps.domain.vo.GpsUserVo; import org.dromara.gps.domain.vo.GpsUserVo;
import org.dromara.gps.mapper.GpsEquipmentMapper;
import org.dromara.gps.mapper.GpsManmachineMapper; import org.dromara.gps.mapper.GpsManmachineMapper;
import org.dromara.gps.service.IGpsEquipmentService;
import org.dromara.gps.service.IGpsEquipmentSonService; import org.dromara.gps.service.IGpsEquipmentSonService;
import org.dromara.project.domain.vo.project.BusProjectVo; import org.dromara.project.domain.vo.project.BusProjectVo;
import org.dromara.project.service.IBusProjectService; import org.dromara.project.service.IBusProjectService;
import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.service.ISysUserService; import org.dromara.system.service.ISysUserService;
import org.dromara.vehicle.domain.VehVehicleInfo;
import org.dromara.vehicle.domain.vo.VehVehicleInfoVo;
import org.dromara.vehicle.service.IVehVehicleInfoService;
import org.dromara.vehicle.service.IVehVehicleTripService;
import org.dromara.websocket.websocket.service.InitOnStartWebSocketServer;
import org.dromara.websocket.websocket.service.VehicleWebSocketServer; import org.dromara.websocket.websocket.service.VehicleWebSocketServer;
import org.redisson.api.DeletedObjectListener; import org.redisson.api.DeletedObjectListener;
import org.redisson.api.ExpiredObjectListener; import org.redisson.api.ExpiredObjectListener;
import org.redisson.api.listener.SetObjectListener; import org.redisson.api.listener.SetObjectListener;
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 org.dromara.gps.domain.bo.GpsEquipmentBo;
import org.dromara.gps.domain.vo.GpsEquipmentVo;
import org.dromara.gps.domain.GpsEquipment;
import org.dromara.gps.mapper.GpsEquipmentMapper;
import org.dromara.gps.service.IGpsEquipmentService;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal; import java.math.BigDecimal;
@ -63,7 +63,8 @@ import java.util.*;
@RequiredArgsConstructor @RequiredArgsConstructor
@Service @Service
@Slf4j @Slf4j
public class GpsEquipmentServiceImpl extends ServiceImpl<GpsEquipmentMapper, GpsEquipment> implements IGpsEquipmentService { public class GpsEquipmentServiceImpl extends ServiceImpl<GpsEquipmentMapper, GpsEquipment>
implements IGpsEquipmentService {
private final GpsEquipmentMapper baseMapper; private final GpsEquipmentMapper baseMapper;
@ -289,7 +290,6 @@ public class GpsEquipmentServiceImpl extends ServiceImpl<GpsEquipmentMapper, Gps
} }
Set<Long> sessionsAll = WebSocketSessionHolder.getSessionsAll(); Set<Long> sessionsAll = WebSocketSessionHolder.getSessionsAll();
if (!sessionsAll.isEmpty()) { if (!sessionsAll.isEmpty()) {
@ -319,17 +319,17 @@ public class GpsEquipmentServiceImpl extends ServiceImpl<GpsEquipmentMapper, Gps
private String buildPushMessage(GpsEquipmentSonBo sonBo) { private String buildPushMessage(GpsEquipmentSonBo sonBo) {
// 构造消息对象(包含关键信息) // 构造消息对象(包含关键信息)
JSONObject messageObj = new JSONObject(); JSONObject messageObj = new JSONObject();
messageObj.put("type", "GPS_DATA_UPDATE"); // 消息类型 messageObj.set("type", "GPS_DATA_UPDATE"); // 消息类型
messageObj.put("clientId", sonBo.getClientId()); // 设备唯一标识 messageObj.set("clientId", sonBo.getClientId()); // 设备唯一标识
messageObj.put("projectId", sonBo.getProjectId().toString()); // 项目ID messageObj.set("projectId", sonBo.getProjectId().toString()); // 项目ID
messageObj.put("userId", sonBo.getUserId().toString()); // 关联用户ID messageObj.set("userId", sonBo.getUserId().toString()); // 关联用户ID
// 位置信息 // 位置信息
JSONObject locationObj = new JSONObject(); JSONObject locationObj = new JSONObject();
locationObj.put("latitude", sonBo.getLocLatitude().toString()); // 纬度 locationObj.set("latitude", sonBo.getLocLatitude().toString()); // 纬度
locationObj.put("longitude", sonBo.getLocLongitude().toString()); // 经度 locationObj.set("longitude", sonBo.getLocLongitude().toString()); // 经度
locationObj.put("altitude", sonBo.getLocAltitude().toString()); // 海拔 locationObj.set("altitude", sonBo.getLocAltitude().toString()); // 海拔
messageObj.put("location", locationObj); messageObj.set("location", locationObj);
// 转换为String类型返回 // 转换为String类型返回
return messageObj.toString(); return messageObj.toString();
@ -341,19 +341,19 @@ public class GpsEquipmentServiceImpl extends ServiceImpl<GpsEquipmentMapper, Gps
private String ueStructureJsonMessage(GpsEquipmentSonBo sonBo, String modelId) { private String ueStructureJsonMessage(GpsEquipmentSonBo sonBo, String modelId) {
// 构造消息对象(包含关键信息) // 构造消息对象(包含关键信息)
JSONObject messageObj = new JSONObject(); JSONObject messageObj = new JSONObject();
messageObj.put("type", "location"); // 消息类型 messageObj.set("type", "location"); // 消息类型
JSONObject data = new JSONObject(); JSONObject data = new JSONObject();
data.put("id", modelId); data.set("id", modelId);
// 位置信息 // 位置信息
JSONObject position = new JSONObject(); JSONObject position = new JSONObject();
position.put("lat", sonBo.getLocLatitude().toString()); // 纬度 position.set("lat", sonBo.getLocLatitude().toString()); // 纬度
position.put("lng", sonBo.getLocLongitude().toString()); // 经度 position.set("lng", sonBo.getLocLongitude().toString()); // 经度
position.put("alt", sonBo.getLocAltitude().toString()); // 海拔 position.set("alt", sonBo.getLocAltitude().toString()); // 海拔
data.put("position", position); data.set("position", position);
messageObj.put("data", data); // 设备唯一标识 messageObj.set("data", data); // 设备唯一标识
// 转换为String类型返回 // 转换为String类型返回
return messageObj.toString(); return messageObj.toString();
@ -366,16 +366,18 @@ public class GpsEquipmentServiceImpl extends ServiceImpl<GpsEquipmentMapper, Gps
private String vehicleStructureJsonMessage(GpsEquipmentSonBo sonBo) { private String vehicleStructureJsonMessage(GpsEquipmentSonBo sonBo) {
// 构造消息对象(包含关键信息) // 构造消息对象(包含关键信息)
JSONObject messageObj = new JSONObject(); JSONObject messageObj = new JSONObject();
messageObj.put("locLatitude", sonBo.getLocLatitude().toString()); // 消息类型 messageObj.set("locLatitude", sonBo.getLocLatitude().toString()); // 消息类型
messageObj.put("locLongitude", sonBo.getLocLongitude().toString()); // 消息类型 messageObj.set("locLongitude", sonBo.getLocLongitude().toString()); // 消息类型
// 转换为String类型返回 // 转换为String类型返回
return messageObj.toString(); return messageObj.toString();
} }
private static final int DEVICE_ALIVE_TIMEOUT = 120; // 5分钟 private static final int DEVICE_ALIVE_TIMEOUT = 120; // 5分钟
/** /**
* 更新设备存活状态到Redis并添加过期监听 * 更新设备存活状态到Redis并添加过期监听
*
* @param clientId 设备ID * @param clientId 设备ID
*/ */
private void updateDeviceAliveStatus(String clientId) { private void updateDeviceAliveStatus(String clientId) {
@ -412,10 +414,9 @@ public class GpsEquipmentServiceImpl extends ServiceImpl<GpsEquipmentMapper, Gps
} }
/** /**
* 处理设备离线 * 处理设备离线
*
* @param clientId 设备ID * @param clientId 设备ID
*/ */
private void handleDeviceOffline(String clientId) { private void handleDeviceOffline(String clientId) {
@ -434,9 +435,12 @@ public class GpsEquipmentServiceImpl extends ServiceImpl<GpsEquipmentMapper, Gps
} }
} }
/**
* GPS人机关联绑定
*
* @param bo
* @return
*/
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public Boolean bindManmachine(GpsEquipmentBo bo) { public Boolean bindManmachine(GpsEquipmentBo bo) {
@ -488,8 +492,9 @@ public class GpsEquipmentServiceImpl extends ServiceImpl<GpsEquipmentMapper, Gps
/** /**
* 获取设备绑定用户列表 * 获取设备绑定用户列表
* @param bo *
* @return * @param bo 设备信息
* @return GPS人机关联视图对象列表
*/ */
@Override @Override
public List<GpsUserVo> getUserList(GpsEquipmentBo bo) { public List<GpsUserVo> getUserList(GpsEquipmentBo bo) {

View File

@ -3,6 +3,7 @@ package org.dromara.job.project;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.project.constant.BusProjectConstant; import org.dromara.project.constant.BusProjectConstant;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions; import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate;
@ -35,12 +36,17 @@ public class DeleteProjectCache {
.count(1000) // 每次 scan 的数量 .count(1000) // 每次 scan 的数量
.build(); .build();
Set<String> keysToDelete = new HashSet<>(); Set<String> keysToDelete = new HashSet<>();
Cursor<byte[]> cursor = stringRedisTemplate.getConnectionFactory() RedisConnectionFactory connectionFactory = stringRedisTemplate.getConnectionFactory();
.getConnection() if (connectionFactory == null) {
.scan(scanOptions); log.error("Redis 连接工厂异常");
return;
}
try (Cursor<byte[]> cursor = connectionFactory.getConnection()
.keyCommands().scan(scanOptions)) {
while (cursor.hasNext()) { while (cursor.hasNext()) {
keysToDelete.add(new String(cursor.next())); keysToDelete.add(new String(cursor.next()));
} }
}
// 批量删除 // 批量删除
if (!keysToDelete.isEmpty()) { if (!keysToDelete.isEmpty()) {
log.info("清理项目缓存,共 {} 条:{}", keysToDelete.size(), keysToDelete); log.info("清理项目缓存,共 {} 条:{}", keysToDelete.size(), keysToDelete);

View File

@ -13,8 +13,9 @@ import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController; import org.dromara.common.web.core.BaseController;
import org.dromara.project.domain.dto.attendancemachine.BusAttendanceMachineContractorUpdateReq;
import org.dromara.project.domain.dto.attendancemachine.BusAttendanceMachineQueryReq; import org.dromara.project.domain.dto.attendancemachine.BusAttendanceMachineQueryReq;
import org.dromara.project.domain.dto.attendancemachine.BusAttendanceMachineUpdateReq; import org.dromara.project.domain.dto.attendancemachine.BusAttendanceMachineTeamUpdateReq;
import org.dromara.project.domain.vo.BusAttendanceMachineVo; import org.dromara.project.domain.vo.BusAttendanceMachineVo;
import org.dromara.project.service.IBusAttendanceMachineService; import org.dromara.project.service.IBusAttendanceMachineService;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@ -69,14 +70,25 @@ public class BusAttendanceMachineController extends BaseController {
} }
/** /**
* 修改考勤机 * 修改考勤机班组
*/ */
@SaCheckPermission("project:attendanceMachine:edit") @SaCheckPermission("project:attendanceMachine:edit")
@Log(title = "考勤机", businessType = BusinessType.UPDATE) @Log(title = "考勤机", businessType = BusinessType.UPDATE)
@RepeatSubmit() @RepeatSubmit()
@PutMapping() @PutMapping("/team")
public R<Void> edit(@Validated @RequestBody BusAttendanceMachineUpdateReq req) { public R<Void> edit(@Validated @RequestBody BusAttendanceMachineTeamUpdateReq req) {
return toAjax(busAttendanceMachineService.updateByBo(req)); return toAjax(busAttendanceMachineService.updateTeam(req));
}
/**
* 修改考勤机分包
*/
@SaCheckPermission("project:attendanceMachine:edit")
@Log(title = "考勤机", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping("/contractor")
public R<Void> editContractor(@Validated @RequestBody BusAttendanceMachineContractorUpdateReq req) {
return toAjax(busAttendanceMachineService.updateContractor(req));
} }
/** /**

View File

@ -18,7 +18,7 @@ import org.springframework.web.bind.annotation.*;
import java.util.List; import java.util.List;
/** /**
* 考勤机用户 * 班组考勤机用户
* *
* @author lilemy * @author lilemy
* @date 2025-10-15 * @date 2025-10-15
@ -44,7 +44,7 @@ public class BusAttendanceMachineUserController extends BaseController {
* 下发用户到考勤机中 * 下发用户到考勤机中
*/ */
@SaCheckPermission("project:attendanceMachineUser:add") @SaCheckPermission("project:attendanceMachineUser:add")
@Log(title = "考勤机用户", businessType = BusinessType.INSERT) @Log(title = "班组考勤机用户", businessType = BusinessType.INSERT)
@RepeatSubmit() @RepeatSubmit()
@PostMapping("/send") @PostMapping("/send")
public R<String> sendPersonInfo(@Validated @RequestBody BusAttendanceMachineSendInfoUserReq req) { public R<String> sendPersonInfo(@Validated @RequestBody BusAttendanceMachineSendInfoUserReq req) {
@ -55,7 +55,7 @@ public class BusAttendanceMachineUserController extends BaseController {
* 删除考勤机用户 * 删除考勤机用户
*/ */
@SaCheckPermission("project:attendanceMachineUser:remove") @SaCheckPermission("project:attendanceMachineUser:remove")
@Log(title = "考勤机用户", businessType = BusinessType.DELETE) @Log(title = "班组考勤机用户", businessType = BusinessType.DELETE)
@RepeatSubmit() @RepeatSubmit()
@DeleteMapping() @DeleteMapping()
public R<Void> remove(@Validated @RequestBody BusAttendanceMachineUserRemoveReq req) { public R<Void> remove(@Validated @RequestBody BusAttendanceMachineUserRemoveReq req) {

View File

@ -47,6 +47,11 @@ public class BusAttendanceMachine implements Serializable {
*/ */
private String teams; private String teams;
/**
* 分包id多个逗号分隔
*/
private String contractors;
/** /**
* 备注 * 备注
*/ */

View File

@ -0,0 +1,40 @@
package org.dromara.project.domain.dto.attendancemachine;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @author lilemy
* @date 2025-11-21 10:56
*/
@Data
public class BusAttendanceMachineContractorUpdateReq implements Serializable {
@Serial
private static final long serialVersionUID = 4141889040329671715L;
/**
* 主键ID
*/
@NotNull(message = "主键ID不能为空")
private Long id;
/**
* 项目ID
*/
@NotNull(message = "项目ID不能为空")
private Long projectId;
/**
* 分包id多个逗号分隔
*/
private String contractors;
/**
* 备注
*/
private String remark;
}

View File

@ -11,7 +11,7 @@ import java.io.Serializable;
* @date 2025-10-15 16:15 * @date 2025-10-15 16:15
*/ */
@Data @Data
public class BusAttendanceMachineUpdateReq implements Serializable { public class BusAttendanceMachineTeamUpdateReq implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 4764015594443520162L; private static final long serialVersionUID = 4764015594443520162L;

View File

@ -8,6 +8,7 @@ import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert; import org.dromara.common.excel.convert.ExcelDictConvert;
import org.dromara.common.translation.annotation.Translation; import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.constant.TransConstant; import org.dromara.common.translation.constant.TransConstant;
import org.dromara.contractor.domain.vo.contractor.SubContractorVo;
import org.dromara.project.domain.BusAttendanceMachine; import org.dromara.project.domain.BusAttendanceMachine;
import org.dromara.project.domain.vo.projectteam.BusProjectTeamVo; import org.dromara.project.domain.vo.projectteam.BusProjectTeamVo;
@ -68,11 +69,22 @@ public class BusAttendanceMachineVo implements Serializable {
@ExcelProperty(value = "班组id多个逗号分隔") @ExcelProperty(value = "班组id多个逗号分隔")
private String teams; private String teams;
/**
* 分包id多个逗号分隔
*/
@ExcelProperty(value = "分包id多个逗号分隔")
private String contractors;
/** /**
* 班组列表 * 班组列表
*/ */
private List<BusProjectTeamVo> teamsList; private List<BusProjectTeamVo> teamsList;
/**
* 分包列表
*/
private List<SubContractorVo> contractorsList;
/** /**
* 备注 * 备注
*/ */

View File

@ -1,15 +1,13 @@
package org.dromara.project.service; package org.dromara.project.service;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import jakarta.validation.constraints.NotEmpty;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.project.domain.BusAttendanceMachine; import org.dromara.project.domain.BusAttendanceMachine;
import org.dromara.project.domain.dto.attendancemachine.BusAttendanceMachineContractorUpdateReq;
import org.dromara.project.domain.dto.attendancemachine.BusAttendanceMachineQueryReq; import org.dromara.project.domain.dto.attendancemachine.BusAttendanceMachineQueryReq;
import org.dromara.project.domain.dto.attendancemachine.BusAttendanceMachineUpdateReq; import org.dromara.project.domain.dto.attendancemachine.BusAttendanceMachineTeamUpdateReq;
import org.dromara.project.domain.vo.BusAttendanceMachineVo; import org.dromara.project.domain.vo.BusAttendanceMachineVo;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -55,12 +53,20 @@ public interface IBusAttendanceMachineService extends IService<BusAttendanceMach
void insertBySn(String sn); void insertBySn(String sn);
/** /**
* 修改考勤机 * 修改考勤机(班组)
* *
* @param req 考勤机 * @param req 考勤机
* @return 是否修改成功 * @return 是否修改成功
*/ */
Boolean updateByBo(BusAttendanceMachineUpdateReq req); Boolean updateTeam(BusAttendanceMachineTeamUpdateReq req);
/**
* 修改考勤机(分包)
*
* @param req 考勤机
* @return 是否修改成功
*/
Boolean updateContractor(BusAttendanceMachineContractorUpdateReq req);
/** /**
* 修改考勤机状态 * 修改考勤机状态
@ -79,6 +85,10 @@ public interface IBusAttendanceMachineService extends IService<BusAttendanceMach
*/ */
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid); Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 重发下发考勤机所有人员
*
* @param ids 待重发考勤机的主键集合
*/
void reissue(Collection<Long> ids); void reissue(Collection<Long> ids);
} }

View File

@ -8,7 +8,7 @@ import org.dromara.project.domain.vo.BusAttendanceMachineUserVo;
import java.util.List; import java.util.List;
/** /**
* 考勤机用户Service接口 * 班组考勤机用户Service接口
* *
* @author lilemy * @author lilemy
* @date 2025-10-15 * @date 2025-10-15

View File

@ -8,20 +8,25 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.HttpStatus; import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.contractor.domain.SubConstructionUser; import org.dromara.contractor.domain.SubConstructionUser;
import org.dromara.contractor.domain.SubContractor;
import org.dromara.contractor.domain.vo.contractor.SubContractorVo;
import org.dromara.contractor.service.ISubConstructionUserService; import org.dromara.contractor.service.ISubConstructionUserService;
import org.dromara.contractor.service.ISubContractorService;
import org.dromara.mobileAttendanceMachine.DeviceMessageSender; import org.dromara.mobileAttendanceMachine.DeviceMessageSender;
import org.dromara.project.domain.BusAttendanceMachine; import org.dromara.project.domain.BusAttendanceMachine;
import org.dromara.project.domain.BusProject; import org.dromara.project.domain.BusProject;
import org.dromara.project.domain.BusProjectTeam; import org.dromara.project.domain.BusProjectTeam;
import org.dromara.project.domain.BusProjectTeamMember; import org.dromara.project.domain.BusProjectTeamMember;
import org.dromara.project.domain.dto.attendancemachine.BusAttendanceMachineContractorUpdateReq;
import org.dromara.project.domain.dto.attendancemachine.BusAttendanceMachineQueryReq; import org.dromara.project.domain.dto.attendancemachine.BusAttendanceMachineQueryReq;
import org.dromara.project.domain.dto.attendancemachine.BusAttendanceMachineUpdateReq; import org.dromara.project.domain.dto.attendancemachine.BusAttendanceMachineTeamUpdateReq;
import org.dromara.project.domain.vo.BusAttendanceMachineVo; import org.dromara.project.domain.vo.BusAttendanceMachineVo;
import org.dromara.project.domain.vo.projectteam.BusProjectTeamVo; import org.dromara.project.domain.vo.projectteam.BusProjectTeamVo;
import org.dromara.project.mapper.BusAttendanceMachineMapper; import org.dromara.project.mapper.BusAttendanceMachineMapper;
@ -30,7 +35,9 @@ import org.dromara.project.service.IBusProjectService;
import org.dromara.project.service.IBusProjectTeamMemberService; import org.dromara.project.service.IBusProjectTeamMemberService;
import org.dromara.project.service.IBusProjectTeamService; import org.dromara.project.service.IBusProjectTeamService;
import org.dromara.system.domain.vo.SysOssVo; import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.service.ISysOssService; import org.dromara.system.service.ISysOssService;
import org.dromara.system.service.ISysUserService;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
@ -45,6 +52,7 @@ import java.util.stream.Collectors;
* @author lilemy * @author lilemy
* @date 2025-10-15 * @date 2025-10-15
*/ */
@Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@Service @Service
public class BusAttendanceMachineServiceImpl extends ServiceImpl<BusAttendanceMachineMapper, BusAttendanceMachine> public class BusAttendanceMachineServiceImpl extends ServiceImpl<BusAttendanceMachineMapper, BusAttendanceMachine>
@ -59,6 +67,9 @@ public class BusAttendanceMachineServiceImpl extends ServiceImpl<BusAttendanceMa
@Resource @Resource
private IBusProjectTeamMemberService projectTeamMemberService; private IBusProjectTeamMemberService projectTeamMemberService;
@Resource
private ISubContractorService contractorService;
@Resource @Resource
private DeviceMessageSender deviceMessageSender; private DeviceMessageSender deviceMessageSender;
@ -70,6 +81,9 @@ public class BusAttendanceMachineServiceImpl extends ServiceImpl<BusAttendanceMa
@Lazy @Lazy
private ISysOssService ossService; private ISysOssService ossService;
@Resource
private ISysUserService userService;
/** /**
* 查询考勤机 * 查询考勤机
@ -94,18 +108,37 @@ public class BusAttendanceMachineServiceImpl extends ServiceImpl<BusAttendanceMa
LambdaQueryWrapper<BusAttendanceMachine> lqw = buildQueryWrapper(req); LambdaQueryWrapper<BusAttendanceMachine> lqw = buildQueryWrapper(req);
Page<BusAttendanceMachineVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw); Page<BusAttendanceMachineVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
List<BusAttendanceMachineVo> records = result.getRecords(); List<BusAttendanceMachineVo> records = result.getRecords();
// 获取班组id列表
List<Long> teamIds = records.stream() List<Long> teamIds = records.stream()
.map(BusAttendanceMachineVo::getTeams) .map(BusAttendanceMachineVo::getTeams)
.filter(StringUtils::isNotBlank) .filter(StringUtils::isNotBlank)
.flatMap(team -> Arrays.stream(team.split(",")) // flatMap 展开内部 stream .flatMap(team -> Arrays.stream(team.split(",")) // flatMap 展开内部 stream
.map(Long::parseLong)) .map(Long::parseLong))
.toList(); .toList();
if (CollUtil.isEmpty(teamIds)) { // 获取分包id列表
List<Long> contractorIds = records.stream()
.map(BusAttendanceMachineVo::getContractors)
.filter(StringUtils::isNotBlank)
.flatMap(contractor -> Arrays.stream(contractor.split(",")) // flatMap 展开内部 stream
.map(Long::parseLong))
.toList();
if (CollUtil.isEmpty(teamIds) && CollUtil.isEmpty(contractorIds)) {
return TableDataInfo.build(result); return TableDataInfo.build(result);
} }
Map<Long, String> teamMap = new HashMap<>();
Map<Long, String> contractorMap = new HashMap<>();
if (CollUtil.isNotEmpty(teamIds)) {
List<BusProjectTeam> projectTeams = projectTeamService.listByIds(teamIds); List<BusProjectTeam> projectTeams = projectTeamService.listByIds(teamIds);
Map<Long, String> teamMap = projectTeams.stream() teamMap = projectTeams.stream()
.collect(Collectors.toMap(BusProjectTeam::getId, BusProjectTeam::getTeamName)); .collect(Collectors.toMap(BusProjectTeam::getId, BusProjectTeam::getTeamName));
}
if (CollUtil.isNotEmpty(contractorIds)) {
List<SubContractor> contractors = contractorService.listByIds(contractorIds);
contractorMap = contractors.stream()
.collect(Collectors.toMap(SubContractor::getId, SubContractor::getName));
}
Map<Long, String> finalTeamMap = teamMap;
Map<Long, String> finalContractorMap = contractorMap;
records.forEach(machine -> { records.forEach(machine -> {
String teams = machine.getTeams(); String teams = machine.getTeams();
if (StringUtils.isNotBlank(teams)) { if (StringUtils.isNotBlank(teams)) {
@ -113,11 +146,22 @@ public class BusAttendanceMachineServiceImpl extends ServiceImpl<BusAttendanceMa
List<BusProjectTeamVo> teamVoList = t.stream().map(teamId -> { List<BusProjectTeamVo> teamVoList = t.stream().map(teamId -> {
BusProjectTeamVo vo = new BusProjectTeamVo(); BusProjectTeamVo vo = new BusProjectTeamVo();
vo.setId(teamId); vo.setId(teamId);
vo.setTeamName(teamMap.getOrDefault(teamId, "未命名")); vo.setTeamName(finalTeamMap.getOrDefault(teamId, "未命名"));
return vo; return vo;
}).toList(); }).toList();
machine.setTeamsList(teamVoList); machine.setTeamsList(teamVoList);
} }
String contractors = machine.getContractors();
if (StringUtils.isNotBlank(contractors)) {
List<Long> c = Arrays.stream(contractors.split(",")).map(Long::parseLong).toList();
List<SubContractorVo> contractorVoList = c.stream().map(contractorId -> {
SubContractorVo vo = new SubContractorVo();
vo.setId(contractorId);
vo.setName(finalContractorMap.getOrDefault(contractorId, "未命名"));
return vo;
}).toList();
machine.setContractorsList(contractorVoList);
}
}); });
result.setRecords(records); result.setRecords(records);
return TableDataInfo.build(result); return TableDataInfo.build(result);
@ -173,7 +217,7 @@ public class BusAttendanceMachineServiceImpl extends ServiceImpl<BusAttendanceMa
* @return 是否修改成功 * @return 是否修改成功
*/ */
@Override @Override
public Boolean updateByBo(BusAttendanceMachineUpdateReq req) { public Boolean updateTeam(BusAttendanceMachineTeamUpdateReq req) {
Long id = req.getId(); Long id = req.getId();
BusAttendanceMachine oldMachine = this.getById(id); BusAttendanceMachine oldMachine = this.getById(id);
if (oldMachine == null) { if (oldMachine == null) {
@ -188,7 +232,7 @@ public class BusAttendanceMachineServiceImpl extends ServiceImpl<BusAttendanceMa
throw new ServiceException("所选项目不存在", HttpStatus.NOT_FOUND); throw new ServiceException("所选项目不存在", HttpStatus.NOT_FOUND);
} }
String teams = req.getTeams(); String teams = req.getTeams();
if (StringUtils.isBlank(teams) && StringUtils.isBlank(oldMachine.getTeams())) { if (StringUtils.isBlank(teams)) {
throw new ServiceException("请选择班组", HttpStatus.BAD_REQUEST); throw new ServiceException("请选择班组", HttpStatus.BAD_REQUEST);
} }
if (StringUtils.isNotBlank(teams)) { if (StringUtils.isNotBlank(teams)) {
@ -200,6 +244,11 @@ public class BusAttendanceMachineServiceImpl extends ServiceImpl<BusAttendanceMa
if (projectTeams.size() != teamIds.size()) { if (projectTeams.size() != teamIds.size()) {
throw new ServiceException("所选班组不存在", HttpStatus.NOT_FOUND); throw new ServiceException("所选班组不存在", HttpStatus.NOT_FOUND);
} }
projectTeams.forEach(team -> {
if (!team.getProjectId().equals(projectId)) {
throw new ServiceException(team.getTeamName() + "不属于所选项目", HttpStatus.BAD_REQUEST);
}
});
String oldTeams = oldMachine.getTeams(); String oldTeams = oldMachine.getTeams();
if (StringUtils.isNotBlank(oldTeams)) { if (StringUtils.isNotBlank(oldTeams)) {
List<Long> oldTeamIds = Arrays.stream(oldTeams.split(",")) List<Long> oldTeamIds = Arrays.stream(oldTeams.split(","))
@ -247,7 +296,12 @@ public class BusAttendanceMachineServiceImpl extends ServiceImpl<BusAttendanceMa
.in(SubConstructionUser::getSysUserId, userIds) .in(SubConstructionUser::getSysUserId, userIds)
.list(); .list();
for (SubConstructionUser user : users) { for (SubConstructionUser user : users) {
SysOssVo ossVo = ossService.getById(Long.valueOf(user.getFacePic()));
if (ossVo != null) {
// 添加考勤机中用户 // 添加考勤机中用户
deviceMessageSender.sendPersonnelInformation(machine.getSn(), user.getSysUserId().toString(),
user.getUserName(), ossVo.getUrl());
}
} }
} }
} else { } else {
@ -264,13 +318,141 @@ public class BusAttendanceMachineServiceImpl extends ServiceImpl<BusAttendanceMa
.list(); .list();
for (SubConstructionUser user : users) { for (SubConstructionUser user : users) {
// 添加考勤机中用户 // 添加考勤机中用户
SysOssVo ossVo = ossService.getById(Long.valueOf(user.getFacePic()));
if (ossVo != null) {
// 添加考勤机中用户
deviceMessageSender.sendPersonnelInformation(machine.getSn(), user.getSysUserId().toString(),
user.getUserName(), ossVo.getUrl());
} }
} }
projectTeams.forEach(team -> { }
if (!team.getProjectId().equals(projectId)) { }
throw new ServiceException(team.getTeamName() + "不属于所选项目", HttpStatus.BAD_REQUEST); return this.updateById(machine);
}
/**
* 修改考勤机(分包)
*
* @param req 考勤机
* @return 是否修改成功
*/
@Override
public Boolean updateContractor(BusAttendanceMachineContractorUpdateReq req) {
Long id = req.getId();
BusAttendanceMachine oldMachine = this.getById(id);
if (oldMachine == null) {
throw new ServiceException("考勤机不存在", HttpStatus.NOT_FOUND);
}
BusAttendanceMachine machine = new BusAttendanceMachine();
BeanUtils.copyProperties(req, machine);
// 判断分包是否属于所选项目
Long projectId = req.getProjectId();
BusProject project = projectService.getById(projectId);
if (project == null) {
throw new ServiceException("所选项目不存在", HttpStatus.NOT_FOUND);
}
String contractors = req.getContractors();
if (StringUtils.isNotBlank(contractors)) {
List<Long> contractorIds = Arrays.stream(contractors.split(","))
.map(Long::parseLong)
.distinct()
.toList();
List<SubContractor> subContractors = contractorService.listByIds(contractorIds);
if (subContractors.size() != contractorIds.size()) {
throw new ServiceException("所选分包不存在", HttpStatus.NOT_FOUND);
}
subContractors.forEach(contractor -> {
if (!contractor.getProjectId().equals(projectId)) {
throw new ServiceException(contractor.getName() + "不属于所选项目", HttpStatus.BAD_REQUEST);
} }
}); });
String oldContractors = oldMachine.getContractors();
if (StringUtils.isNotBlank(oldContractors)) {
List<Long> oldContractorIds = Arrays.stream(oldContractors.split(","))
.map(Long::parseLong)
.distinct()
.toList();
// 获取新增的分包ID
List<Long> added = contractorIds.stream()
.filter(t -> !oldContractorIds.contains(t))
.toList();
// 获取删除的分包ID
List<Long> deleted = oldContractorIds.stream()
.filter(t -> !contractorIds.contains(t))
.toList();
// 删除的分包不为空时,删除考勤机中对应的人数
if (CollUtil.isNotEmpty(deleted)) {
// 获取待删除的分包的所有用户id
List<Long> userIds = userService.selectFbUserList(projectId, deleted, null)
.stream().map(SysUserVo::getUserId).toList();
if (CollUtil.isNotEmpty(userIds)) {
String[] userIdArray = userIds.stream()
.map(String::valueOf) // 将 Long 转成 String
.toArray(String[]::new);
try {
deviceMessageSender.batchDeleteUsers(machine.getSn(), userIdArray);
} catch (Exception e) {
throw new ServiceException("删除考勤机用户失败", HttpStatus.ERROR);
}
}
}
// 新增的分包不为空时,添加考勤机中对应人
if (CollUtil.isNotEmpty(added)) {
// 获取待添加到考勤机的分包的所有用户id
List<Long> userIds = userService.selectFbUserList(projectId, deleted, null)
.stream().map(SysUserVo::getUserId).toList();
// 获取待添加到考勤机的用户信息
List<SubConstructionUser> users = constructionUserService.lambdaQuery()
.in(SubConstructionUser::getSysUserId, userIds)
.list();
// 获取用户的头像
List<Long> faceIds = users.stream().map(SubConstructionUser::getFacePic).map(Long::valueOf).distinct().toList();
List<SysOssVo> faceList = ossService.listByIds(faceIds);
Map<Long, String> faceMap = new HashMap<>();
if (faceList != null) {
faceMap = faceList.stream().collect(Collectors.toMap(SysOssVo::getOssId, SysOssVo::getUrl));
}
for (SubConstructionUser user : users) {
// 添加考勤机中用户
String facePic = user.getFacePic();
String faceUrl = StringUtils.isNotBlank(facePic) ? faceMap.get(Long.valueOf(facePic)) : "";
if (StringUtils.isNotBlank(faceUrl)) {
// 添加考勤机中用户
deviceMessageSender.sendPersonnelInformation(machine.getSn(), user.getSysUserId().toString(),
user.getUserName(), faceUrl);
}
}
}
} else {
// 添加考勤机中对应人
List<Long> userIds = userService.selectFbUserList(machine.getProjectId(), contractorIds, null)
.stream().map(SysUserVo::getUserId)
.toList();
// 获取待添加到考勤机的用户信息
List<SubConstructionUser> users = constructionUserService.lambdaQuery()
.in(SubConstructionUser::getSysUserId, userIds)
.list();
if (CollUtil.isEmpty(users)) {
throw new ServiceException("所选分包没有用户", HttpStatus.NOT_FOUND);
}
// 获取用户的头像
List<Long> faceIds = users.stream().map(SubConstructionUser::getFacePic).map(Long::valueOf).distinct().toList();
List<SysOssVo> faceList = ossService.listByIds(faceIds);
Map<Long, String> faceMap = new HashMap<>();
if (faceList != null) {
faceMap = faceList.stream().collect(Collectors.toMap(SysOssVo::getOssId, SysOssVo::getUrl));
}
for (SubConstructionUser user : users) {
// 添加考勤机中用户
String facePic = user.getFacePic();
String faceUrl = StringUtils.isNotBlank(facePic) ? faceMap.get(Long.valueOf(facePic)) : "";
if (StringUtils.isNotBlank(faceUrl)) {
// 添加考勤机中用户
deviceMessageSender.sendPersonnelInformation(machine.getSn(), user.getSysUserId().toString(),
user.getUserName(), faceUrl);
}
}
}
} }
return this.updateById(machine); return this.updateById(machine);
} }
@ -303,12 +485,29 @@ public class BusAttendanceMachineServiceImpl extends ServiceImpl<BusAttendanceMa
*/ */
@Override @Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) { public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
List<BusAttendanceMachine> machines = this.listByIds(ids);
if (CollUtil.isEmpty(machines) || machines.size() != ids.size()) {
throw new ServiceException("数据不存在");
}
if (isValid) { if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验 // 删除考勤机下发的人员
for (BusAttendanceMachine machine : machines) {
try {
deviceMessageSender.deleteAllUsers(machine.getSn());
} catch (Exception e) {
log.error("删除考勤人员异常", e);
throw new ServiceException("删除考勤人员异常");
}
}
} }
return baseMapper.deleteByIds(ids) > 0; return baseMapper.deleteByIds(ids) > 0;
} }
/**
* 重发下发考勤机所有人员
*
* @param ids 待重发考勤机的主键集合
*/
@Async @Async
@Override @Override
public void reissue(Collection<Long> ids) { public void reissue(Collection<Long> ids) {
@ -323,28 +522,38 @@ public class BusAttendanceMachineServiceImpl extends ServiceImpl<BusAttendanceMa
} }
String teams = machine.getTeams(); String teams = machine.getTeams();
if (StrUtil.isBlank(teams)) { String contractors = machine.getContractors();
if (StrUtil.isBlank(teams) && StrUtil.isBlank(contractors)) {
continue; continue;
} }
List<Long> userIds = new ArrayList<>();
if (StrUtil.isNotBlank(teams)) {
List<Long> oldTeamIds = Arrays.stream(teams.split(",")) List<Long> oldTeamIds = Arrays.stream(teams.split(","))
.map(Long::parseLong) .map(Long::parseLong)
.distinct() .distinct()
.toList(); .toList();
List<Long> teamUserIds = projectTeamMemberService.lambdaQuery()
List<Long> userIds = projectTeamMemberService.lambdaQuery()
.select(BusProjectTeamMember::getMemberId) .select(BusProjectTeamMember::getMemberId)
.in(BusProjectTeamMember::getTeamId, oldTeamIds) .in(BusProjectTeamMember::getTeamId, oldTeamIds)
.list() .list()
.stream().map(BusProjectTeamMember::getMemberId) .stream().map(BusProjectTeamMember::getMemberId)
.toList(); .toList();
userIds.addAll(teamUserIds);
}
if (StrUtil.isNotBlank(contractors)) {
List<Long> oldContractorIds = Arrays.stream(contractors.split(","))
.map(Long::parseLong)
.distinct()
.toList();
List<SysUserVo> userVos = userService.selectFbUserList(machine.getProjectId(), oldContractorIds, null);
userIds.addAll(userVos.stream().map(SysUserVo::getUserId).distinct().toList());
}
if (CollUtil.isEmpty(userIds)) { if (CollUtil.isEmpty(userIds)) {
continue; continue;
} }
List<SubConstructionUser> users = constructionUserService.lambdaQuery() List<SubConstructionUser> users = constructionUserService.lambdaQuery()
.in(SubConstructionUser::getSysUserId, userIds) .in(SubConstructionUser::getSysUserId, userIds)
.list(); .list();
Map<Long, String> faceMap = new HashMap<>(); Map<Long, String> faceMap = new HashMap<>();
List<String> list = users.stream().map(SubConstructionUser::getFacePic).filter(StrUtil::isNotBlank).toList(); List<String> list = users.stream().map(SubConstructionUser::getFacePic).filter(StrUtil::isNotBlank).toList();
if (CollUtil.isNotEmpty(list)) { if (CollUtil.isNotEmpty(list)) {

View File

@ -28,7 +28,7 @@ import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* 考勤机用户Service业务层处理 * 班组考勤机用户Service业务层处理
* *
* @author lilemy * @author lilemy
* @date 2025-10-15 * @date 2025-10-15

View File

@ -5,9 +5,6 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.project.domain.dto.attendance.SubTodayUserDto; import org.dromara.project.domain.dto.attendance.SubTodayUserDto;
import org.dromara.project.domain.dto.attendance.SubTwoWeekDto; import org.dromara.project.domain.dto.attendance.SubTwoWeekDto;
import org.dromara.project.domain.dto.attendance.SubUserAttendanceQueryReq; import org.dromara.project.domain.dto.attendance.SubUserAttendanceQueryReq;
import org.dromara.project.domain.dto.attendance.TodayUserDto;
import org.dromara.project.domain.vo.attendance.AttendanceTodayUserVo;
import org.dromara.project.domain.vo.attendance.SubUserAttendanceTotalVo;
import org.dromara.system.domain.SysUser; import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.bo.SysUserBo; import org.dromara.system.domain.bo.SysUserBo;
import org.dromara.system.domain.dto.AppUserUpdatePasswordDto; import org.dromara.system.domain.dto.AppUserUpdatePasswordDto;
@ -15,7 +12,6 @@ import org.dromara.system.domain.dto.FbUserListDto;
import org.dromara.system.domain.dto.role.SysRoleProjectDto; import org.dromara.system.domain.dto.role.SysRoleProjectDto;
import org.dromara.system.domain.vo.SysUserExportVo; import org.dromara.system.domain.vo.SysUserExportVo;
import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.domain.vo.SysUserVo;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -32,6 +28,16 @@ public interface ISysUserService {
TableDataInfo<SysUserVo> selectPageFbUserList(FbUserListDto user, PageQuery pageQuery); TableDataInfo<SysUserVo> selectPageFbUserList(FbUserListDto user, PageQuery pageQuery);
/**
* 获取分包用户列表
*
* @param projectId 项目id
* @param contractorIds 分包id列表
* @param nickName 用户昵称
* @return 分包用户列表
*/
List<SysUserVo> selectFbUserList(Long projectId, List<Long> contractorIds, String nickName);
/** /**
* 根据条件分页查询用户列表 * 根据条件分页查询用户列表
* *
@ -260,6 +266,7 @@ public interface ISysUserService {
/** /**
* 根据部门 ID 获取用户 ID -> 名称 映射 * 根据部门 ID 获取用户 ID -> 名称 映射
*
* @param deptId * @param deptId
* @return * @return
*/ */

View File

@ -16,7 +16,6 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.aspectj.apache.bcel.generic.RET;
import org.dromara.common.core.constant.CacheNames; import org.dromara.common.core.constant.CacheNames;
import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.domain.dto.UserDTO; import org.dromara.common.core.domain.dto.UserDTO;
@ -43,8 +42,6 @@ import org.dromara.project.domain.BusUserProjectRelevancy;
import org.dromara.project.domain.dto.attendance.SubTodayUserDto; import org.dromara.project.domain.dto.attendance.SubTodayUserDto;
import org.dromara.project.domain.dto.attendance.SubTwoWeekDto; import org.dromara.project.domain.dto.attendance.SubTwoWeekDto;
import org.dromara.project.domain.dto.attendance.SubUserAttendanceQueryReq; import org.dromara.project.domain.dto.attendance.SubUserAttendanceQueryReq;
import org.dromara.project.domain.dto.attendance.TodayUserDto;
import org.dromara.project.domain.vo.attendance.SubUserAttendanceTotalVo;
import org.dromara.project.domain.vo.projectteam.BusProjectTeamAppVo; import org.dromara.project.domain.vo.projectteam.BusProjectTeamAppVo;
import org.dromara.project.service.IBusProjectTeamService; import org.dromara.project.service.IBusProjectTeamService;
import org.dromara.project.service.IBusUserProjectRelevancyService; import org.dromara.project.service.IBusUserProjectRelevancyService;
@ -58,14 +55,12 @@ import org.dromara.system.domain.vo.SysRoleVo;
import org.dromara.system.domain.vo.SysUserExportVo; import org.dromara.system.domain.vo.SysUserExportVo;
import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.mapper.*; import org.dromara.system.mapper.*;
import org.dromara.system.service.ISysOssService;
import org.dromara.system.service.ISysRoleService; import org.dromara.system.service.ISysRoleService;
import org.dromara.system.service.ISysUserFileService; import org.dromara.system.service.ISysUserFileService;
import org.dromara.system.service.ISysUserService; import org.dromara.system.service.ISysUserService;
import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -188,6 +183,26 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
return TableDataInfo.build(page); return TableDataInfo.build(page);
} }
/**
* 获取分包用户列表
*
* @param projectId 项目id
* @param contractorIds 分包id列表
* @param nickName 用户昵称
* @return 分包用户列表
*/
@Override
public List<SysUserVo> selectFbUserList(Long projectId, List<Long> contractorIds, String nickName) {
LambdaQueryWrapper<SysUser> lqw = new LambdaQueryWrapper<>();
lqw.eq(SysUser::getAppUserType, "2");
lqw.exists(projectId != null, "SELECT 1 FROM bus_user_project_relevancy " +
"WHERE bus_user_project_relevancy.user_id = u.user_id " +
"AND project_id = {0}", projectId);
lqw.in(CollUtil.isNotEmpty(contractorIds), SysUser::getContractorId, contractorIds);
lqw.like(StringUtils.isNotBlank(nickName), SysUser::getNickName, nickName);
return baseMapper.selectUserList(lqw);
}
/** /**
* 根据条件分页查询用户列表 * 根据条件分页查询用户列表
* *