This commit is contained in:
zt
2025-11-10 18:57:07 +08:00
parent b905320413
commit 733b95632b
20 changed files with 619 additions and 16 deletions

View File

@ -0,0 +1,14 @@
package org.dromara.common.constant;
public class RoleIdConstant {
/**
* 分包管理审核
*/
public static final Long FB_SH = 6L;
/**
* 管理审核
*/
public static final Long SYS_SH = 7L;
}

View File

@ -211,6 +211,13 @@ public class SubConstructionUserController extends BaseController {
return toAjax(constructionUserService.batchUpdateStatus(req)); return toAjax(constructionUserService.batchUpdateStatus(req));
} }
@SaCheckPermission("contractor:constructionUser:updateClock")
@PutMapping("updateClockBatch")
@RepeatSubmit()
public R<Void> updateClockBatch(@Validated(EditGroup.class) @RequestBody updateClockBatchDto dto) {
return toAjax(constructionUserService.updateClockBatch(dto));
}
/** /**
* 根据项目id批量修改施工人员打卡状态 * 根据项目id批量修改施工人员打卡状态
*/ */

View File

@ -0,0 +1,23 @@
package org.dromara.contractor.domain.dto.constructionuser;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
@Data
public class updateClockBatchDto {
/**
* 用户id
*/
private List<Long> sysUserIds;
/**
* 打卡(0启用打卡 1禁止打卡)
*/
@NotNull(message = "打卡状态不能为空")
private String clock;
@NotNull(message = "项目id不能为空")
private Long projectId;
}

View File

@ -109,6 +109,14 @@ public interface ISubConstructionUserService extends IService<SubConstructionUse
*/ */
Boolean updateClock(SubConstructionUserUpdateClockReq req); Boolean updateClock(SubConstructionUserUpdateClockReq req);
/**
* 批量修改施工人员打卡状态
*
* @param dto 待修改的主键集合
* @return 是否修改成功
*/
Boolean updateClockBatch(updateClockBatchDto dto);
/** /**
* 修改施工人员项目(人员迁移) * 修改施工人员项目(人员迁移)
* *

View File

@ -615,6 +615,15 @@ public class SubConstructionUserServiceImpl extends ServiceImpl<SubConstructionU
return this.updateById(constructionUser); return this.updateById(constructionUser);
} }
@Override
public Boolean updateClockBatch(updateClockBatchDto dto) {
this.lambdaUpdate().set(SubConstructionUser::getClock, dto.getClock())
.eq(SubConstructionUser::getProjectId, dto.getProjectId())
.in(CollectionUtil.isNotEmpty(dto.getSysUserIds()), SubConstructionUser::getSysUserId, dto.getSysUserIds())
.update();
return true;
}
/** /**
* 修改施工人员项目(人员迁移) * 修改施工人员项目(人员迁移)
* *

View File

@ -25,11 +25,9 @@ import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@Component @Component
public class ClarityPmAsyncMethod { public class ClarityPmAsyncMethod {
@ -127,7 +125,7 @@ public class ClarityPmAsyncMethod {
} }
public void transmitAttendanceRecord(List<HseRecognizeRecord> recognizeRecords) { public void transmitViolationRecord(List<HseRecognizeRecord> recognizeRecords) {
List<ViolationRecord> records = new ArrayList<>(); List<ViolationRecord> records = new ArrayList<>();
for (HseRecognizeRecord record : recognizeRecords) { for (HseRecognizeRecord record : recognizeRecords) {
String violationType = record.getViolationType(); String violationType = record.getViolationType();

View File

@ -115,4 +115,16 @@ public class BusAttendanceRuleController extends BaseController {
return R.ok(busAttendanceRuleService.queryByProjectId(projectId)); return R.ok(busAttendanceRuleService.queryByProjectId(projectId));
} }
/**
* 获取考勤打卡规则详细信息
*
* @param projectId 主键
*/
@SaCheckPermission("project:attendanceRule:query")
@GetMapping("/byProjectIdV1/{projectId}")
public R<List<BusAttendanceRuleVo>> getInfoByProjectIdV1(@NotNull(message = "项目Id不能为空")
@PathVariable Long projectId) {
return R.ok(busAttendanceRuleService.queryListByProjectId(projectId));
}
} }

View File

@ -70,6 +70,15 @@ public class BusAttendanceAppController extends BaseController {
return R.ok(attendanceService.punchCardByFace(file, req)); return R.ok(attendanceService.punchCardByFace(file, req));
} }
/**
* 人脸坐标打卡
*/
@RepeatSubmit(interval = 3, timeUnit = TimeUnit.MINUTES,message = "3分钟内禁止重复打卡")
@PostMapping("/punch/card/faceV1")
public R<Boolean> punchCardByFaceV1(@RequestPart("file") MultipartFile file, BusAttendancePunchCardByFaceReq req) {
return R.ok(attendanceService.punchCardByFaceV1(file, req));
}
/** /**
* 获取考勤打卡规则详细信息 * 获取考勤打卡规则详细信息
*/ */
@ -79,6 +88,15 @@ public class BusAttendanceAppController extends BaseController {
return R.ok(attendanceRuleService.queryByProjectId(projectId)); return R.ok(attendanceRuleService.queryByProjectId(projectId));
} }
/**
* 获取考勤打卡规则详细信息
*/
@GetMapping("/ruleInfoV1/{projectId}")
public R<BusAttendanceRuleVo> getInfoV1(@NotNull(message = "项目不能为空")
@PathVariable Long projectId) {
return R.ok(attendanceRuleService.queryByProjectIdV1(projectId,LoginHelper.getUserId()));
}
/** /**
* 查询项目考勤范围列表 * 查询项目考勤范围列表
*/ */
@ -107,6 +125,14 @@ public class BusAttendanceAppController extends BaseController {
return R.ok(attendanceService.checkInRange(req)); return R.ok(attendanceService.checkInRange(req));
} }
/**
* 判断是否在打卡范围内
*/
@PostMapping("/checkInRangeV1")
public R<Boolean> checkInRangeV1(@RequestBody BusAttendancePunchCardByFaceReq req) {
return R.ok(attendanceService.checkInRangeV1(req));
}
/** /**
* 获取用户当天打卡记录 * 获取用户当天打卡记录
*/ */

View File

@ -102,4 +102,9 @@ public class BusAttendanceRule extends BaseEntity {
*/ */
private Long clockOutJobId; private Long clockOutJobId;
/**
* 适用人群(0-施工人员 1-管理人员 2-分包人员)
*/
private String personType;
} }

View File

@ -94,5 +94,9 @@ public class BusAttendanceRuleBo extends BaseEntity {
*/ */
private String type; private String type;
/**
* 适用人群(0-施工人员 1-管理人员 2-分包人员)
*/
private String personType;
} }

View File

@ -76,4 +76,19 @@ public interface IBusAttendanceRuleService extends IService<BusAttendanceRule>{
*/ */
BusAttendanceRuleVo queryByProjectId(Long projectId); BusAttendanceRuleVo queryByProjectId(Long projectId);
/**
* 根据项目id查询
*/
BusAttendanceRuleVo queryByProjectIdV1(Long projectId,Long userId);
/**
* 根据项目id和type查询
*/
BusAttendanceRuleVo queryByProjectIdAndType(Long projectId,String type);
/**
* 根据项目id和type查询
*/
List<BusAttendanceRuleVo> queryListByProjectId(Long projectId);
} }

View File

@ -95,6 +95,15 @@ public interface IBusAttendanceService extends IService<BusAttendance>{
*/ */
Boolean punchCardByFace(MultipartFile file, BusAttendancePunchCardByFaceReq req); Boolean punchCardByFace(MultipartFile file, BusAttendancePunchCardByFaceReq req);
/**
* 人脸打卡V1
*
* @param file 人脸图片
* @param req 人脸打卡请求
* @return 是否打卡成功
*/
Boolean punchCardByFaceV1(MultipartFile file, BusAttendancePunchCardByFaceReq req);
/** /**
* 根据项目id查询出勤人列表 * 根据项目id查询出勤人列表
@ -118,11 +127,18 @@ public interface IBusAttendanceService extends IService<BusAttendance>{
*/ */
Boolean checkInRange(BusAttendancePunchCardByFaceReq req); Boolean checkInRange(BusAttendancePunchCardByFaceReq req);
Boolean checkInRangeV1(BusAttendancePunchCardByFaceReq req);
/** /**
* 获取用户当天打卡记录 * 获取用户当天打卡记录
*/ */
List<BusAttendanceVo> getTodayAttendance(Long projectId,Long userId); List<BusAttendanceVo> getTodayAttendance(Long projectId,Long userId);
/**
* 获取用户当天打卡记录
*/
List<BusAttendanceVo> getTodayAttendanceV1(Long projectId,Long userId);
/** /**
* 获取用户指定月份的打卡记录 * 获取用户指定月份的打卡记录
*/ */

View File

@ -10,6 +10,10 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.contractor.domain.SubConstructionUser;
import org.dromara.contractor.service.ISubConstructionUserService;
import org.dromara.contractor.service.impl.SubConstructionUserServiceImpl;
import org.dromara.job.attendance.AttendanceJobUtil; import org.dromara.job.attendance.AttendanceJobUtil;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.dromara.project.domain.bo.BusAttendanceRuleBo; import org.dromara.project.domain.bo.BusAttendanceRuleBo;
@ -37,6 +41,8 @@ public class BusAttendanceRuleServiceImpl extends ServiceImpl<BusAttendanceRuleM
private final BusAttendanceRuleMapper baseMapper; private final BusAttendanceRuleMapper baseMapper;
private final AttendanceJobUtil attendanceJobUtil; private final AttendanceJobUtil attendanceJobUtil;
private final ISubConstructionUserService constructionUserService;
/** /**
* 查询考勤打卡规则 * 查询考勤打卡规则
* *
@ -143,6 +149,20 @@ public class BusAttendanceRuleServiceImpl extends ServiceImpl<BusAttendanceRuleM
} }
} }
private void validEntityBeforeSaveV1(BusAttendanceRule entity) {
//TODO 做一些数据校验,如唯一约束
//暂时一个项目一个打卡规则
List<BusAttendanceRule> busAttendanceRules = baseMapper.selectList(Wrappers.<BusAttendanceRule>lambdaQuery()
.eq(BusAttendanceRule::getProjectId, entity.getProjectId())
.eq(BusAttendanceRule::getPersonType, entity.getPersonType())
.ne(entity.getId() != null, BusAttendanceRule::getId, entity.getId())
);
if (!busAttendanceRules.isEmpty()) {
throw new ServiceException("该项目已存在打卡规则");
}
}
private void setResultTime(BusAttendanceRule entity) { private void setResultTime(BusAttendanceRule entity) {
if (entity.getClockInEndTime() != null) { if (entity.getClockInEndTime() != null) {
@ -224,4 +244,28 @@ public class BusAttendanceRuleServiceImpl extends ServiceImpl<BusAttendanceRuleM
return baseMapper.selectVoOne(Wrappers.<BusAttendanceRule>lambdaQuery() return baseMapper.selectVoOne(Wrappers.<BusAttendanceRule>lambdaQuery()
.eq(BusAttendanceRule::getProjectId, projectId)); .eq(BusAttendanceRule::getProjectId, projectId));
} }
@Override
public BusAttendanceRuleVo queryByProjectIdV1(Long projectId,Long userId) {
SubConstructionUser constructionUser = constructionUserService.getBySysUserId(userId);
if(constructionUser == null){
throw new ServiceException("请先进行实名认证");
}
return baseMapper.selectVoOne(Wrappers.<BusAttendanceRule>lambdaQuery()
.eq(BusAttendanceRule::getProjectId, projectId)
.eq(BusAttendanceRule::getPersonType, constructionUser.getUserRole()));
}
@Override
public BusAttendanceRuleVo queryByProjectIdAndType(Long projectId, String type) {
return baseMapper.selectVoOne(Wrappers.<BusAttendanceRule>lambdaQuery()
.eq(BusAttendanceRule::getProjectId, projectId)
.eq(BusAttendanceRule::getPersonType, type));
}
@Override
public List<BusAttendanceRuleVo> queryListByProjectId(Long projectId) {
return baseMapper.selectVoList(Wrappers.<BusAttendanceRule>lambdaQuery()
.eq(BusAttendanceRule::getProjectId, projectId));
}
} }

View File

@ -566,6 +566,257 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
return false; return false;
} }
@Override
public Boolean punchCardByFaceV1(MultipartFile file, BusAttendancePunchCardByFaceReq req) {
// 获取当前用户
Long userId = LoginHelper.getUserId();
Long replaceId = null;
if (req.getUserId() != null) {
userId = req.getUserId();
replaceId = LoginHelper.getUserId();
}
synchronized (userId.toString().intern()) {
// 记录当前打卡时间
LocalDateTime now = LocalDateTime.now();
if(req.getPunchTime() != null){
now = req.getPunchTime();
}
//打卡范围
if (!"1".equals(req.getSource())){
if (!checkInRangeV1(req)) {
throw new ServiceException("打卡位置不在范围内", HttpStatus.ERROR);
}
}
//用户信息校验
SubConstructionUser constructionUser = constructionUserService.getBySysUserId(userId);
if ("1".equals(constructionUser.getStatus())) {
throw new ServiceException("当前用户已离职", HttpStatus.ERROR);
}
if ("1".equals(constructionUser.getClock())) {
throw new ServiceException("当前用户已被禁止打卡", HttpStatus.ERROR);
}
//施工人员需要判断工资
if ("0".equals(constructionUser.getUserRole())) {
String typeOfWork = constructionUser.getTypeOfWork();
BusWorkWage workWageByWorkType = workWageService.getWorkWageByWorkType(typeOfWork);
if (constructionUser.getSalary().compareTo(BigDecimal.ZERO) == 0 && workWageByWorkType == null) {
throw new ServiceException("当前用户没有设置工资,禁止打卡", HttpStatus.ERROR);
}
}
// 判断用户是否已经被拉黑
constructionBlacklistService.validUserInBlacklist(constructionUser.getSysUserId(), req.getProjectId());
Boolean result = false;
// 进行人脸比对
try {
result = constructionUserService.faceComparison(file, userId);
} catch (Exception e) {
throw new ServiceException(e.getMessage(), HttpStatus.ERROR);
}
if (!result) {
throw new ServiceException("人脸识别失败,请重新识别", HttpStatus.ERROR);
}
//打卡规则
BusAttendanceRuleVo busAttendanceRuleVo = attendanceRuleService.queryByProjectIdAndType(req.getProjectId(),constructionUser.getUserRole());
if (busAttendanceRuleVo == null) {
throw new ServiceException("未设置打卡规则", HttpStatus.ERROR);
}
// 考勤时间
//确定考勤日期
LocalDate localDate = calculateAttendanceDate(now, busAttendanceRuleVo);
// 判断当前用户打卡状态
int clockTypeByTime = getClockTypeByTime(localDate, now, busAttendanceRuleVo);
List<BusAttendance> attendances = this.lambdaQuery()
.eq(BusAttendance::getUserId, userId)
.eq(BusAttendance::getClockDate, localDate)
.list();
List<BusAttendance> inAttendances = attendances.stream().filter(attendance ->
BusAttendanceCommuterEnum.CLOCKIN.getValue().equals(attendance.getClockType())).toList();
List<BusAttendance> outAttendances = attendances.stream().filter(attendance ->
BusAttendanceCommuterEnum.CLOCKOUT.getValue().equals(attendance.getClockType())).toList();
if (clockTypeByTime == 1) {
if(CollectionUtils.isEmpty(inAttendances)){
BusAttendance attendance = new BusAttendance();
// 上班打卡
attendance.setClockType(BusAttendanceCommuterEnum.CLOCKIN.getValue());
//打卡时间
attendance.setRuleTime(busAttendanceRuleVo.getClockInTime());
// 判断是否为迟到
if (isLate(now, busAttendanceRuleVo)) {
attendance.setClockStatus(BusAttendanceClockStatusEnum.LATE.getValue());
attendance.setMinuteCount(getMinutesDifference(now, busAttendanceRuleVo.getClockInTime()));
} else {
attendance.setClockStatus(BusAttendanceClockStatusEnum.NORMAL.getValue());
}
//只要请假,直接归为请假
LocalDateTime localDateTime = localDate.atTime(busAttendanceRuleVo.getClockInTime());
if (leaveService.isLeave(localDateTime, userId)) {
attendance.setClockStatus(BusAttendanceClockStatusEnum.LEAVE.getValue());
}
// 填充信息
attendance.setUserId(userId);
attendance.setProjectId(req.getProjectId());
attendance.setClockDate(localDate);
attendance.setClockTime(now);
attendance.setUserName(constructionUser.getUserName());
attendance.setReplaceId(replaceId);
if(req.getSource() != null){
attendance.setSource(req.getSource());
attendance.setSn(req.getSn());
}
// 记录打卡坐标
attendance.setLat(req.getLat());
attendance.setLng(req.getLng());
try {
attendance.setClockLocation(JSTUtil.getLocationName(req.getLat(), req.getLng()));
} catch (Exception e) {
log.error("获取打卡位置失败", e);
}
// 上传人脸照
SysOssVo upload = ossService.upload(file);
attendance.setFacePic(upload.getOssId().toString());
Long finalUserId = userId;
CompletableFuture.runAsync(() -> {
try {
chatServerHandler.sendSystemMessageToUser(finalUserId, "打卡成功", "1");
} catch (Exception e) {
log.error("异步发送系统消息失败用户ID: {}, 消息: {}", finalUserId, "打卡成功", e);
}
});
//计算工资
attendance.setSalary(computeSalary(constructionUser, inAttendances));
return this.save(attendance);
}
//考勤机打卡会有历史记录,需要更新状态
if(CollectionUtil.isNotEmpty(outAttendances) && "1".equals(req.getSource())){
BusAttendance busAttendance = outAttendances.getFirst();
String oldStatus = busAttendance.getClockStatus();
//更新打卡时间
busAttendance.setClockTime(now);
// 判断是否为迟到
if (isLate(now, busAttendanceRuleVo)) {
busAttendance.setClockStatus(BusAttendanceClockStatusEnum.LATE.getValue());
busAttendance.setMinuteCount(getMinutesDifference(now, busAttendanceRuleVo.getClockInTime()));
} else {
busAttendance.setClockStatus(BusAttendanceClockStatusEnum.NORMAL.getValue());
}
//只要请假,直接归为请假
LocalDateTime localDateTime = localDate.atTime(busAttendanceRuleVo.getClockInTime());
if (leaveService.isLeave(localDateTime, userId)) {
busAttendance.setClockStatus(BusAttendanceClockStatusEnum.LEAVE.getValue());
}
busAttendance.setSource(req.getSource());
busAttendance.setSn(req.getSn());
//如果是缺卡需要上传人脸
if(oldStatus.equals(BusAttendanceClockStatusEnum.UNCLOCK.getValue())){
SysOssVo upload = ossService.upload(file);
busAttendance.setFacePic(upload.getOssId().toString());
}
updateById(busAttendance);
}
} else if (clockTypeByTime == 2 || CollectionUtils.isNotEmpty(inAttendances)) {
if (CollectionUtil.isNotEmpty(outAttendances)) {
BusAttendance busAttendance = outAttendances.getFirst();
if("1".equals(req.getSource())){
busAttendance.setSource(req.getSource());
busAttendance.setSn(req.getSn());
if(busAttendance.getClockStatus().equals(BusAttendanceClockStatusEnum.UNCLOCK.getValue())){
SysOssVo upload = ossService.upload(file);
busAttendance.setFacePic(upload.getOssId().toString());
}
}else {
if (busAttendance.getClockStatus().equals(BusAttendanceClockStatusEnum.UNCLOCK.getValue())) {
throw new ServiceException("下班缺卡记录已生成,不能更新");
}
}
//更新打卡时间
busAttendance.setClockTime(now);
// 判断是否为早退
if (isLeaveEarly(now, busAttendanceRuleVo, localDate)) {
busAttendance.setClockStatus(BusAttendanceClockStatusEnum.LEAVEEARLY.getValue());
busAttendance.setMinuteCount(getMinutesDifference(now, busAttendanceRuleVo.getClockOutTime()));
} else {
busAttendance.setClockStatus(BusAttendanceClockStatusEnum.NORMAL.getValue());
busAttendance.setMinuteCount(0);
}
//只要请假,直接归为请假
LocalDateTime localDateTime = localDate.atTime(busAttendanceRuleVo.getClockInTime());
if (leaveService.isLeave(localDateTime, userId)) {
busAttendance.setClockStatus(BusAttendanceClockStatusEnum.LEAVE.getValue());
}
updateById(busAttendance);
} else {
BusAttendance attendance = new BusAttendance();
// 下班打卡
attendance.setClockType(BusAttendanceCommuterEnum.CLOCKOUT.getValue());
attendance.setRuleTime(busAttendanceRuleVo.getClockOutTime());
// 判断是否为早退
if (isLeaveEarly(now, busAttendanceRuleVo, localDate)) {
attendance.setClockStatus(BusAttendanceClockStatusEnum.LEAVEEARLY.getValue());
attendance.setMinuteCount(getMinutesDifference(now, busAttendanceRuleVo.getClockOutTime()));
} else {
attendance.setClockStatus(BusAttendanceClockStatusEnum.NORMAL.getValue());
}
//只要请假,直接归为请假
LocalDateTime localDateTime = localDate.atTime(busAttendanceRuleVo.getClockInTime());
if (leaveService.isLeave(localDateTime, userId)) {
attendance.setClockStatus(BusAttendanceClockStatusEnum.LEAVE.getValue());
}
// 填充信息
attendance.setUserId(userId);
attendance.setProjectId(req.getProjectId());
attendance.setClockDate(localDate);
attendance.setClockTime(now);
attendance.setUserName(constructionUser.getUserName());
attendance.setReplaceId(replaceId);
if(req.getSource() != null){
attendance.setSource(req.getSource());
attendance.setSn(req.getSn());
}
// 记录打卡坐标
attendance.setLat(req.getLat());
attendance.setLng(req.getLng());
try {
attendance.setClockLocation(JSTUtil.getLocationName(req.getLat(), req.getLng()));
} catch (Exception e) {
log.error("获取打卡位置失败", e);
}
// 上传人脸照
SysOssVo upload = ossService.upload(file);
attendance.setFacePic(upload.getOssId().toString());
Long finalUserId1 = userId;
CompletableFuture.runAsync(() -> {
try {
chatServerHandler.sendSystemMessageToUser(finalUserId1, "打卡成功", "1");
} catch (Exception e) {
log.error("异步发送系统消息失败用户ID: {}, 消息: {}", finalUserId1, "打卡成功", e);
}
});
//计算工资
attendance.setSalary(computeSalary(constructionUser, inAttendances));
return this.save(attendance);
}
}
}
return false;
}
/** /**
* 根据项目id查询出勤人列表 * 根据项目id查询出勤人列表
@ -731,6 +982,70 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
} }
@Override
public Boolean checkInRangeV1(BusAttendancePunchCardByFaceReq req) {
// 获取当前用户
Long userId = LoginHelper.getUserId();
if (req.getUserId() != null) {
userId = req.getUserId();
}
Long projectId = req.getProjectId();
SysUserVo sysUserVo = userService.queryById(userId);
//判断是否要求范围内打卡
BusUserProjectRelevancy relevancy = userProjectRelevancyService.getOne(Wrappers.lambdaQuery(BusUserProjectRelevancy.class)
.eq(BusUserProjectRelevancy::getUserId, userId)
.eq(BusUserProjectRelevancy::getProjectId, projectId)
.last("limit 1"));
if (relevancy == null) {
throw new ServiceException("当前用户未加入项目", HttpStatus.BAD_REQUEST);
}
//判断是否是施工员 管理员返回项目全部打卡范围,施工人员返回班组打卡范围
boolean isConstruct = "0".equals(sysUserVo.getAppUserType());
List<Long> rangeIds = new ArrayList<>();
if (isConstruct) {
BusProjectTeamMember one = projectTeamMemberService.getOne(Wrappers.lambdaQuery(BusProjectTeamMember.class)
.eq(BusProjectTeamMember::getMemberId, userId)
.eq(BusProjectTeamMember::getProjectId, projectId)
.last("limit 1"));
if (one == null) {
throw new ServiceException("当前用户未加入班组", HttpStatus.BAD_REQUEST);
}
BusProjectTeam team = projectTeamService.getById(one.getTeamId());
//需要考虑班组不设置考勤范围
if ("1".equals(team.getIsClockIn())) {
return true;
}
try {
JSONArray jsonArray = JSONUtil.parseArray(team.getPunchRange());
rangeIds = jsonArray.toList(Long.class);
} catch (Exception e) {
}
}
// 再获取项目的规则
BusAttendanceRuleVo busAttendanceRuleVo = attendanceRuleService.queryByProjectIdAndType(projectId,"0");
if (busAttendanceRuleVo != null && "2".equals(busAttendanceRuleVo.getType())) {
return true;
}
List<String> punchRangeList = projectPunchrangeService.lambdaQuery()
.in(CollectionUtil.isNotEmpty(rangeIds), BusProjectPunchrange::getId, rangeIds)
.eq(BusProjectPunchrange::getProjectId, projectId)
.list()
.stream()
.map(BusProjectPunchrange::getPunchRange)
.toList();
if (CollUtil.isEmpty(punchRangeList)) {
throw new ServiceException(isConstruct ? "班组未配置考勤范围" : "项目未配置考勤范围", HttpStatus.ERROR);
}
List<GeoPoint> matchingRange = JSTUtil.findMatchingRange(req.getLat(), req.getLng(), punchRangeList);
return matchingRange != null;
}
@Override @Override
public List<BusAttendanceVo> getTodayAttendance(Long projectId, Long userId) { public List<BusAttendanceVo> getTodayAttendance(Long projectId, Long userId) {
@ -752,6 +1067,28 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
.eq(BusAttendance::getClockDate, localDate) .eq(BusAttendance::getClockDate, localDate)
); );
} }
@Override
public List<BusAttendanceVo> getTodayAttendanceV1(Long projectId, Long userId) {
if (userId == null) {
userId = LoginHelper.getUserId();
}
BusAttendanceRuleVo busAttendanceRuleVo = attendanceRuleService.queryByProjectIdV1(projectId,userId);
if (busAttendanceRuleVo == null) {
return List.of();
}
// 考勤时间
//确定考勤日期
LocalDate localDate = calculateAttendanceDate(LocalDateTime.now(), busAttendanceRuleVo);
return baseMapper.selectVoList(new LambdaQueryWrapper<BusAttendance>()
.eq(BusAttendance::getUserId, userId)
.eq(BusAttendance::getClockDate, localDate)
);
}
@Override @Override
public List<BusMonthAttendanceVo> getMonthAttendance(Long projectId, String month) { public List<BusMonthAttendanceVo> getMonthAttendance(Long projectId, String month) {

View File

@ -13,6 +13,7 @@ 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.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.constant.RoleIdConstant;
import org.dromara.common.core.constant.HttpStatus; import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.core.domain.event.ProcessDeleteEvent; import org.dromara.common.core.domain.event.ProcessDeleteEvent;
@ -24,6 +25,7 @@ import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.DateUtils; import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.ObjectUtils; import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.enums.AppUserTypeEnum;
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.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
@ -45,6 +47,7 @@ import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.service.ISysOssService; import org.dromara.system.service.ISysOssService;
import org.dromara.system.service.ISysRoleService; import org.dromara.system.service.ISysRoleService;
import org.dromara.system.service.ISysUserService; import org.dromara.system.service.ISysUserService;
import org.dromara.websocket.ChatServerHandler;
import org.dromara.workflow.domain.TestLeave; import org.dromara.workflow.domain.TestLeave;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
@ -56,6 +59,7 @@ import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.LocalTime; import java.time.LocalTime;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -97,6 +101,9 @@ public class BusLeaveServiceImpl extends ServiceImpl<BusLeaveMapper, BusLeave>
@Resource @Resource
private ISysOssService ossService; private ISysOssService ossService;
@Resource
private ChatServerHandler chatServerHandler;
/** /**
* 查询施工人员请假申请 * 查询施工人员请假申请
* *
@ -112,9 +119,9 @@ public class BusLeaveServiceImpl extends ServiceImpl<BusLeaveMapper, BusLeave>
List<SysUser> sysUsers = new ArrayList<>(); List<SysUser> sysUsers = new ArrayList<>();
if(busLeaveVo.getGangerId()==null){ if(busLeaveVo.getGangerId()==null){
if("1".equals(userType)){ if("1".equals(userType)){
sysUsers = userService.selectUserByRoleIdAndProjectId(6L, busLeaveVo.getProjectId()); sysUsers = userService.selectUserByRoleIdAndProjectId(RoleIdConstant.FB_SH, busLeaveVo.getProjectId());
} else if ("2".equals(userType)) { } else if ("2".equals(userType)) {
sysUsers = userService.selectUserByRoleIdAndProjectId(7L, busLeaveVo.getProjectId()); sysUsers = userService.selectUserByRoleIdAndProjectId(RoleIdConstant.SYS_SH, busLeaveVo.getProjectId());
} }
}else { }else {
SysUserVo sysUserVo = userService.selectUserById(busLeaveVo.getGangerId()); SysUserVo sysUserVo = userService.selectUserById(busLeaveVo.getGangerId());
@ -434,9 +441,9 @@ public class BusLeaveServiceImpl extends ServiceImpl<BusLeaveMapper, BusLeave>
List<SysUser> sysUsers = new ArrayList<>(); List<SysUser> sysUsers = new ArrayList<>();
if(leaveVo.getGangerId()==null){ if(leaveVo.getGangerId()==null){
if("1".equals(userType)){ if("1".equals(userType)){
sysUsers = userService.selectUserByRoleIdAndProjectId(6L, leaveVo.getProjectId()); sysUsers = userService.selectUserByRoleIdAndProjectId(RoleIdConstant.FB_SH, leaveVo.getProjectId());
} else if ("2".equals(userType)) { } else if ("2".equals(userType)) {
sysUsers = userService.selectUserByRoleIdAndProjectId(7L, leaveVo.getProjectId()); sysUsers = userService.selectUserByRoleIdAndProjectId(RoleIdConstant.SYS_SH, leaveVo.getProjectId());
} }
}else { }else {
SysUserVo sysUserVo = userService.selectUserById(leaveVo.getGangerId()); SysUserVo sysUserVo = userService.selectUserById(leaveVo.getGangerId());
@ -500,6 +507,30 @@ public class BusLeaveServiceImpl extends ServiceImpl<BusLeaveMapper, BusLeave>
leave.setContractorId(bySysUserId.getContractorId()); leave.setContractorId(bySysUserId.getContractorId());
} }
this.save(leave); this.save(leave);
//发送通知 - 异步执行
CompletableFuture.runAsync(() -> {
try {
SysUserVo sysUserVo = userService.selectUserById(leave.getUserId());
String appUserType = sysUserVo.getAppUserType();
if(AppUserTypeEnum.SG.getType().equals(appUserType)){
chatServerHandler.sendSystemMessageToUser(leave.getGangerId(),"["+leave.getUserName()+ "]"+"提交了新的请假申请","1");
}else if(AppUserTypeEnum.GL.getType().equals(appUserType)){
List<Long> longs = roleService.selectUserIdsByRoleId(RoleIdConstant.SYS_SH);
for (Long aLong : longs) {
chatServerHandler.sendSystemMessageToUser(aLong,"["+leave.getUserName()+ "]"+"提交了新的请假申请","1");
}
}else if(AppUserTypeEnum.FB.getType().equals(appUserType)){
List<Long> longs = roleService.selectUserIdsByRoleId(RoleIdConstant.FB_SH);
for (Long aLong : longs) {
chatServerHandler.sendSystemMessageToUser(aLong,"["+leave.getUserName()+ "]"+"提交了新的请假申请","1");
}
}
} catch (Exception e) {
log.error("异步发送系统消息失败用户ID: {}, 消息: {}", leave.getGangerId(), "提交了新的请假申请", e);
}
});
return leave.getId(); return leave.getId();
} }
@ -551,10 +582,10 @@ public class BusLeaveServiceImpl extends ServiceImpl<BusLeaveMapper, BusLeave>
List<Long> roleIds = roleService.selectRoleIdsByUserIdAndProjectId(userId, req.getProjectId()); List<Long> roleIds = roleService.selectRoleIdsByUserIdAndProjectId(userId, req.getProjectId());
List<String> type = new ArrayList<>(); List<String> type = new ArrayList<>();
if(roleIds.contains(7L)){ if(roleIds.contains(RoleIdConstant.SYS_SH)){
type.add("1"); type.add("1");
} }
if(roleIds.contains(6L)){ if(roleIds.contains(RoleIdConstant.FB_SH)){
type.add("2"); type.add("2");
} }
if(CollUtil.isEmpty(type)){ if(CollUtil.isEmpty(type)){
@ -643,6 +674,21 @@ public class BusLeaveServiceImpl extends ServiceImpl<BusLeaveMapper, BusLeave>
} }
} }
} }
CompletableFuture.runAsync(() -> {
try {
chatServerHandler.sendSystemMessageToUser(busLeave.getUserId(),"请假申请已通过","1");
} catch (Exception e) {
log.error("异步发送系统消息失败用户ID: {}, 消息: {}", busLeave.getUserId(), "请假申请已通过", e);
}
});
}else if(gangerOpinion.equals("3")){
CompletableFuture.runAsync(() -> {
try {
chatServerHandler.sendSystemMessageToUser(busLeave.getUserId(),"请假申请未通过","1");
} catch (Exception e) {
log.error("异步发送系统消息失败用户ID: {}, 消息: {}", busLeave.getUserId(), "请假申请未通过", e);
}
});
} }
return i>0; return i>0;
} }

View File

@ -11,6 +11,7 @@ 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.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.constant.RoleIdConstant;
import org.dromara.common.core.constant.HttpStatus; import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.domain.event.ProcessDeleteEvent; import org.dromara.common.core.domain.event.ProcessDeleteEvent;
import org.dromara.common.core.domain.event.ProcessEvent; import org.dromara.common.core.domain.event.ProcessEvent;
@ -21,6 +22,7 @@ import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.DateUtils; import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.ObjectUtils; import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.enums.AppUserTypeEnum;
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.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
@ -101,6 +103,7 @@ public class BusReissueCardServiceImpl extends ServiceImpl<BusReissueCardMapper,
@Resource @Resource
private ISysRoleService roleService; private ISysRoleService roleService;
/** /**
* 查询施工人员补卡申请 * 查询施工人员补卡申请
* *
@ -120,9 +123,9 @@ public class BusReissueCardServiceImpl extends ServiceImpl<BusReissueCardMapper,
List<SysUser> sysUsers = new ArrayList<>(); List<SysUser> sysUsers = new ArrayList<>();
if(busReissueCardVo.getGangerId()==null){ if(busReissueCardVo.getGangerId()==null){
if("1".equals(userType)){ if("1".equals(userType)){
sysUsers = userService.selectUserByRoleIdAndProjectId(6L, busReissueCardVo.getProjectId()); sysUsers = userService.selectUserByRoleIdAndProjectId(RoleIdConstant.FB_SH, busReissueCardVo.getProjectId());
} else if ("2".equals(userType)) { } else if ("2".equals(userType)) {
sysUsers = userService.selectUserByRoleIdAndProjectId(7L, busReissueCardVo.getProjectId()); sysUsers = userService.selectUserByRoleIdAndProjectId(RoleIdConstant.SYS_SH, busReissueCardVo.getProjectId());
} }
}else { }else {
SysUserVo sysUserVo = userService.selectUserById(busReissueCardVo.getGangerId()); SysUserVo sysUserVo = userService.selectUserById(busReissueCardVo.getGangerId());
@ -366,9 +369,9 @@ public class BusReissueCardServiceImpl extends ServiceImpl<BusReissueCardMapper,
List<SysUser> sysUsers = new ArrayList<>(); List<SysUser> sysUsers = new ArrayList<>();
if(reissueCardVo.getGangerId()==null){ if(reissueCardVo.getGangerId()==null){
if("1".equals(userType)){ if("1".equals(userType)){
sysUsers = userService.selectUserByRoleIdAndProjectId(6L, reissueCardVo.getProjectId()); sysUsers = userService.selectUserByRoleIdAndProjectId(RoleIdConstant.FB_SH, reissueCardVo.getProjectId());
} else if ("2".equals(userType)) { } else if ("2".equals(userType)) {
sysUsers = userService.selectUserByRoleIdAndProjectId(7L, reissueCardVo.getProjectId()); sysUsers = userService.selectUserByRoleIdAndProjectId(RoleIdConstant.SYS_SH, reissueCardVo.getProjectId());
} }
}else { }else {
SysUserVo sysUserVo = userService.selectUserById(reissueCardVo.getGangerId()); SysUserVo sysUserVo = userService.selectUserById(reissueCardVo.getGangerId());
@ -433,10 +436,10 @@ public class BusReissueCardServiceImpl extends ServiceImpl<BusReissueCardMapper,
//两个角色 一个审核分包的=6一个审核管理的=7 //两个角色 一个审核分包的=6一个审核管理的=7
List<String> type = new ArrayList<>(); List<String> type = new ArrayList<>();
if(roleIds.contains(7L)){ if(roleIds.contains(RoleIdConstant.SYS_SH)){
type.add("1"); type.add("1");
} }
if(roleIds.contains(6L)){ if(roleIds.contains(RoleIdConstant.FB_SH)){
type.add("2"); type.add("2");
} }
if(CollUtil.isEmpty(type)){ if(CollUtil.isEmpty(type)){
@ -477,7 +480,22 @@ public class BusReissueCardServiceImpl extends ServiceImpl<BusReissueCardMapper,
//发送通知 - 异步执行 //发送通知 - 异步执行
CompletableFuture.runAsync(() -> { CompletableFuture.runAsync(() -> {
try { try {
Long userId = bean.getUserId();
SysUserVo sysUserVo = userService.selectUserById(userId);
String appUserType = sysUserVo.getAppUserType();
if(AppUserTypeEnum.SG.getType().equals(appUserType)){
chatServerHandler.sendSystemMessageToUser(bean.getGangerId(),"["+bean.getUserName()+ "]"+"提交了新的补卡申请","1"); chatServerHandler.sendSystemMessageToUser(bean.getGangerId(),"["+bean.getUserName()+ "]"+"提交了新的补卡申请","1");
}else if(AppUserTypeEnum.GL.getType().equals(appUserType)){
List<Long> longs = roleService.selectUserIdsByRoleId(RoleIdConstant.SYS_SH);
for (Long aLong : longs) {
chatServerHandler.sendSystemMessageToUser(aLong,"["+bean.getUserName()+ "]"+"提交了新的补卡申请","1");
}
}else if(AppUserTypeEnum.FB.getType().equals(appUserType)){
List<Long> longs = roleService.selectUserIdsByRoleId(RoleIdConstant.FB_SH);
for (Long aLong : longs) {
chatServerHandler.sendSystemMessageToUser(aLong,"["+bean.getUserName()+ "]"+"提交了新的补卡申请","1");
}
}
} catch (Exception e) { } catch (Exception e) {
log.error("异步发送系统消息失败用户ID: {}, 消息: {}", bean.getGangerId(), "提交了新的补卡申请", e); log.error("异步发送系统消息失败用户ID: {}, 消息: {}", bean.getGangerId(), "提交了新的补卡申请", e);
} }

View File

@ -162,4 +162,8 @@ public class SysUserVo implements Serializable {
*/ */
private String contractorName; private String contractorName;
/**
* 打卡(0启用打卡 1禁止打卡)
*/
private String clock;
} }

View File

@ -221,4 +221,6 @@ public interface ISysRoleService {
List<Long> selectRoleIdsByName(String roleName); List<Long> selectRoleIdsByName(String roleName);
List<Long> selectUserIdsByRoleId(Long roleId);
} }

View File

@ -660,4 +660,9 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService {
.eq(SysRole::getRoleName, roleName) .eq(SysRole::getRoleName, roleName)
).stream().map(SysRole::getRoleId).toList(); ).stream().map(SysRole::getRoleId).toList();
} }
@Override
public List<Long> selectUserIdsByRoleId(Long roleId) {
return userRoleMapper.selectUserIdsByRoleId(roleId);
}
} }

View File

@ -163,9 +163,19 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
List<Long> list1 = userVoList.stream().map(SysUserVo::getContractorId).toList(); List<Long> list1 = userVoList.stream().map(SysUserVo::getContractorId).toList();
List<SubContractor> subContractors = contractorService.listByIds(list1); List<SubContractor> subContractors = contractorService.listByIds(list1);
Map<Long, String> collect = subContractors.stream().collect(Collectors.toMap(SubContractor::getId, SubContractor::getName)); Map<Long, String> collect = subContractors.stream().collect(Collectors.toMap(SubContractor::getId, SubContractor::getName));
List<Long> sysUserIds = userVoList.stream().map(SysUserVo::getUserId).toList();
List<SubConstructionUser> constructionUsers = constructionUserService.lambdaQuery()
.select(SubConstructionUser::getSysUserId, SubConstructionUser::getClock)
.in(SubConstructionUser::getSysUserId, sysUserIds)
.list();
Map<Long, String> collect2 = constructionUsers.stream().collect(Collectors.toMap(SubConstructionUser::getSysUserId, SubConstructionUser::getClock));
userVoList.forEach(userVo -> { userVoList.forEach(userVo -> {
Long contractorId = userVo.getContractorId(); Long contractorId = userVo.getContractorId();
userVo.setContractorName(collect.get(contractorId)); userVo.setContractorName(collect.get(contractorId));
userVo.setClock(collect2.get(userVo.getUserId()));
}); });
page.setRecords(userVoList); page.setRecords(userVoList);
} }