package com.ylx.attendanceconfig.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.ylx.attendanceconfig.domain.AttendanceDeductionRule; import com.ylx.attendanceconfig.domain.AttendanceRule; import com.ylx.attendanceconfig.domain.dto.AttendanceDeductionRuleDTO; import com.ylx.attendanceconfig.domain.dto.AttendanceRuleAddDTO; import com.ylx.attendanceconfig.domain.vo.AttendanceDeductionRuleVO; import com.ylx.attendanceconfig.domain.vo.AttendanceRuleDetailVO; import com.ylx.attendanceconfig.mapper.AttendanceDeductionRuleMapper; import com.ylx.attendanceconfig.mapper.AttendanceRuleMapper; import com.ylx.attendanceconfig.service.AttendanceConfigService; import com.ylx.common.exception.ServiceException; import com.ylx.common.utils.SecurityUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; @Slf4j @Service public class AttendanceConfigServiceImpl implements AttendanceConfigService { @Resource private AttendanceRuleMapper attendanceRuleMapper; @Resource private AttendanceDeductionRuleMapper attendanceDeductionRuleMapper; private static final int NOT_DELETE = 0; private static final int DELETE = 1; private static final int ENABLED = 1; private static final int DISABLED = 0; private static final int EARLY_LEAVE_DEDUCTION = 1; @Override @Transactional(rollbackFor = Exception.class) public void addAttendanceRule(AttendanceRuleAddDTO dto) { checkRuleParam(dto); AttendanceRule rule = new AttendanceRule(); rule.setRuleName(StrUtil.blankToDefault(StrUtil.trim(dto.getRuleName()), "默认考勤规则")); rule.setBasicWorkHours(dto.getBasicWorkHours()); rule.setWorkDurationRuleEnabled(dto.getWorkDurationRuleEnabled()); rule.setStatus(ObjectUtil.defaultIfNull(dto.getStatus(), ENABLED)); rule.setIsDelete(NOT_DELETE); rule.setCreateBy(SecurityUtils.getUsername()); rule.setUpdateBy(SecurityUtils.getUsername()); int insertRuleResult = attendanceRuleMapper.insert(rule); if (insertRuleResult <= 0 || ObjectUtil.isNull(rule.getId())) { throw new ServiceException("新增考勤规则失败"); } if (dto.getWorkDurationRuleEnabled() == ENABLED) { List sortedRules = sortDeductionRules(dto.getDeductionRules()); for (int i = 0; i < sortedRules.size(); i++) { AttendanceDeductionRuleDTO deductionDto = sortedRules.get(i); AttendanceDeductionRule deductionRule = new AttendanceDeductionRule(); deductionRule.setRuleId(rule.getId()); deductionRule.setRuleType(ObjectUtil.defaultIfNull(deductionDto.getRuleType(), EARLY_LEAVE_DEDUCTION)); deductionRule.setStartMinutes(deductionDto.getStartMinutes()); deductionRule.setEndMinutes(deductionDto.getEndMinutes()); deductionRule.setDeductAmount(deductionDto.getDeductAmount()); deductionRule.setSortOrder(i + 1); deductionRule.setIsDelete(NOT_DELETE); deductionRule.setCreateBy(SecurityUtils.getUsername()); deductionRule.setUpdateBy(SecurityUtils.getUsername()); int insertDeductionResult = attendanceDeductionRuleMapper.insert(deductionRule); if (insertDeductionResult <= 0) { throw new ServiceException("新增考勤扣款区间失败"); } } } } @Override public List listAttendanceRules() { List ruleList = attendanceRuleMapper.selectList(new LambdaQueryWrapper() .orderByDesc(AttendanceRule::getCreateTime)); return ruleList.stream() .map(this::toDetailVO) .collect(Collectors.toList()); } @Override @Transactional(rollbackFor = Exception.class) public void deleteAttendanceRule(Long id) { if (ObjectUtil.isNull(id) || id <= 0) { throw new ServiceException("考勤规则ID不正确"); } AttendanceRule rule = attendanceRuleMapper.selectOne(new LambdaQueryWrapper() .eq(AttendanceRule::getId, id) .eq(AttendanceRule::getIsDelete, NOT_DELETE)); if (ObjectUtil.isNull(rule)) { throw new ServiceException("考勤规则不存在或已删除"); } String username = SecurityUtils.getUsername(); LocalDateTime now = LocalDateTime.now(); int updateRuleResult = attendanceRuleMapper.update(null, new LambdaUpdateWrapper() .eq(AttendanceRule::getId, id) .eq(AttendanceRule::getIsDelete, NOT_DELETE) .set(AttendanceRule::getIsDelete, DELETE) .set(AttendanceRule::getUpdateBy, username) .set(AttendanceRule::getUpdateTime, now)); if (updateRuleResult <= 0) { throw new ServiceException("删除考勤规则失败"); } attendanceDeductionRuleMapper.update(null, new LambdaUpdateWrapper() .eq(AttendanceDeductionRule::getRuleId, id) .eq(AttendanceDeductionRule::getIsDelete, NOT_DELETE) .set(AttendanceDeductionRule::getIsDelete, DELETE) .set(AttendanceDeductionRule::getUpdateBy, username) .set(AttendanceDeductionRule::getUpdateTime, now)); } @Override @Transactional(rollbackFor = Exception.class) public void deleteDeductionRule(Long id) { if (ObjectUtil.isNull(id) || id <= 0) { throw new ServiceException("扣款区间ID不正确"); } AttendanceDeductionRule deductionRule = attendanceDeductionRuleMapper.selectOne( new LambdaQueryWrapper() .eq(AttendanceDeductionRule::getId, id)); if (ObjectUtil.isNull(deductionRule)) { throw new ServiceException("扣款区间不存在或已删除"); } int updateResult = attendanceDeductionRuleMapper.deleteById(id); if (updateResult <= 0) { throw new ServiceException("删除扣款区间失败"); } } private void checkRuleParam(AttendanceRuleAddDTO dto) { if (ObjectUtil.isNull(dto)) { throw new ServiceException("考勤规则参数不能为空"); } if (ObjectUtil.isNull(dto.getBasicWorkHours()) || dto.getBasicWorkHours().compareTo(BigDecimal.ZERO) <= 0) { throw new ServiceException("基本工作时长必须大于0"); } if (dto.getWorkDurationRuleEnabled() != ENABLED && dto.getWorkDurationRuleEnabled() != DISABLED) { throw new ServiceException("工作时长规则值不正确"); } if (ObjectUtil.isNotNull(dto.getStatus()) && dto.getStatus() != ENABLED && dto.getStatus() != DISABLED) { throw new ServiceException("考勤规则状态值不正确"); } if (dto.getWorkDurationRuleEnabled() == ENABLED) { checkDeductionRules(dto.getDeductionRules()); } } private void checkDeductionRules(List deductionRules) { if (CollectionUtil.isEmpty(deductionRules)) { throw new ServiceException("启用工作时长规则时,扣款区间不能为空"); } List sortedRules = sortDeductionRules(deductionRules); int expectedStartMinutes = 1; for (AttendanceDeductionRuleDTO deductionRule : sortedRules) { if (ObjectUtil.defaultIfNull(deductionRule.getRuleType(), EARLY_LEAVE_DEDUCTION) != EARLY_LEAVE_DEDUCTION) { throw new ServiceException("扣款规则类型不正确"); } if (ObjectUtil.isNull(deductionRule.getStartMinutes()) || deductionRule.getStartMinutes() <= 0) { throw new ServiceException("开始分钟数必须大于0"); } if (ObjectUtil.isNull(deductionRule.getEndMinutes()) || deductionRule.getEndMinutes() < deductionRule.getStartMinutes()) { throw new ServiceException("结束分钟数不能小于开始分钟数"); } if (ObjectUtil.isNull(deductionRule.getDeductAmount()) || deductionRule.getDeductAmount().compareTo(BigDecimal.ZERO) < 0) { throw new ServiceException("扣款金额不能小于0"); } if (deductionRule.getStartMinutes() != expectedStartMinutes) { throw new ServiceException("扣款区间必须从1分钟开始且连续"); } expectedStartMinutes = deductionRule.getEndMinutes() + 1; } } private List sortDeductionRules(List deductionRules) { return deductionRules.stream() .sorted(Comparator.comparing(AttendanceDeductionRuleDTO::getStartMinutes)) .collect(Collectors.toList()); } private AttendanceRuleDetailVO toDetailVO(AttendanceRule rule) { AttendanceRuleDetailVO vo = new AttendanceRuleDetailVO(); BeanUtil.copyProperties(rule, vo); vo.setDeductionRules(getDeductionRuleVOList(rule.getId())); return vo; } private List getDeductionRuleVOList(Long ruleId) { List deductionRules = attendanceDeductionRuleMapper.selectList( new LambdaQueryWrapper() .eq(AttendanceDeductionRule::getRuleId, ruleId) .orderByAsc(AttendanceDeductionRule::getSortOrder) .orderByAsc(AttendanceDeductionRule::getStartMinutes)); return deductionRules.stream().map(entity -> { AttendanceDeductionRuleVO vo = new AttendanceDeductionRuleVO(); BeanUtil.copyProperties(entity, vo); return vo; }).collect(Collectors.toList()); } }