Commit 4bf1304e authored by 刘斌's avatar 刘斌

fix: 修复业务问题

parent b5daf20e
......@@ -64,8 +64,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>17</source>
<target>17</target>
<source>21</source>
<target>21</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
......
......@@ -5,10 +5,12 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
@Slf4j
@EnableAsync
@EnableCaching
@EnableScheduling
@SpringBootApplication(scanBasePackages = {"top.binfast.daemon.codegen", "top.binfast.app", "com.example"})
public class Application {
......
package com.example.config;
import com.example.constant.HrCacheConstants;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import top.binfast.app.biz.sysapi.bean.params.sysDept.SysDeptParam;
import top.binfast.common.redis.util.CacheUtils;
/**
* @author 刘斌
* @date 2025/11/12 10:19
*/
@Aspect
@Component
@Slf4j
public class RefreshDeptCacheAspect {
/**
* 定义切入点:Controller包下的方法
*/
@Pointcut("execution(* top.binfast.app.admin.controller.SysDeptCtrl.add(..)) ||" +
" execution(* top.binfast.app.admin.controller.SysDeptCtrl.edit(..)) ||" +
" execution(* top.binfast.app.admin.controller.SysDeptCtrl.deleteOne(..))")
public void deptChangePointcut() {}
/**
* 方法一:基于注解的监听
*/
@AfterReturning(pointcut = "deptChangePointcut()", returning = "result")
public void afterOperation(JoinPoint joinPoint, Object result) {
try {
// 获取方法参数
Object[] args = joinPoint.getArgs();
if (args == null || args.length == 0) {
return;
}
Long deptId = null;
if (args[0] instanceof SysDeptParam param) {
deptId = param.getId();
} else if (args[0] instanceof Long) {
deptId = (Long) args[0];
}
// 清除部门缓存
CacheUtils.evict(HrCacheConstants.DEPT_AND_PARENT_NAME, deptId + "");
} catch (Exception e) {
log.error("清楚部门缓存异常", e);
}
}
}
package com.example.constant;
/**
* @author 刘斌
* @date 2025/11/12 10:30
*/
public interface HrCacheConstants {
/**
* 部门及以下权限
*/
String DEPT_AND_PARENT_NAME = "dept_and_parent_name#30d";
}
......@@ -4,7 +4,6 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
import com.alibaba.cola.dto.PageResponse;
import com.alibaba.cola.dto.Response;
import com.alibaba.cola.dto.SingleResponse;
import com.example.domain.EmployeeInfo;
import com.example.domain.params.*;
import com.example.domain.vo.EmployeeInfoImportVo;
import com.example.domain.vo.EmployeeInfoVo;
......@@ -15,16 +14,13 @@ import jakarta.validation.constraints.NotEmpty;
import org.dromara.core.trans.anno.TransMethodResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import top.binfast.app.biz.sysapi.bean.vo.sysUser.SysUserImportVo;
import org.springframework.web.multipart.MultipartFile;
import top.binfast.common.core.constant.BusinessType;
import top.binfast.common.core.util.Json;
import top.binfast.common.core.util.MapstructUtils;
import top.binfast.common.core.util.ResponseUtils;
import top.binfast.common.core.validate.AddGroup;
import top.binfast.common.core.validate.EditGroup;
import top.binfast.common.excel.annotion.ExcelExport;
import top.binfast.common.excel.annotion.ExcelStream;
import top.binfast.common.excel.core.ExcelDownHandler;
import top.binfast.common.log.annotation.PinSysLog;
import java.util.ArrayList;
......@@ -74,8 +70,8 @@ public class EmployeeInfoCtrl {
@PinSysLog(value = "系统用户", businessType = BusinessType.IMPORT)
@SaCheckPermission("employee:info:import")
@PostMapping(value = "/excel/import")
public Response importWithOptions(@ExcelStream(headRowNumber = 2) Stream<EmployeeInfoImportVo> stream) {
return employeeInfoServ.importEmployeeList(stream);
public Response importWithOptions(@ExcelStream(headRowNumber = 2) Stream<EmployeeInfoImportVo> stream, @RequestPart("file") MultipartFile file) {
return employeeInfoServ.importEmployeeList(stream, file);
}
/**
......
......@@ -5,7 +5,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Getter;
import lombok.Setter;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;;
import java.time.LocalDateTime;
import java.io.Serial;
......
......@@ -6,7 +6,7 @@ import lombok.Getter;
import lombok.Setter;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.time.LocalDate;;
import java.time.LocalDate;
import java.io.Serial;
......@@ -45,6 +45,11 @@ public class EmployeeInfo extends BaseModel {
*/
private String secondLevelDepartment;
/**
* 部门id
*/
private Long deptId;
/**
* 工号
*/
......@@ -75,6 +80,11 @@ public class EmployeeInfo extends BaseModel {
*/
private String gender;
/**
* 简历图片
*/
private String resumeImage;
/**
* 身份证号码
*/
......
package com.example.domain.params;
import cn.idev.excel.annotation.ExcelProperty;
import com.example.domain.EmployeeInfo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
......@@ -52,6 +53,12 @@ public class EmployeeInfoParam {
@NotBlank(message = "二级部门不能为空", groups = { AddGroup.class, EditGroup.class })
private String secondLevelDepartment;
/**
* 部门id
*/
@NotNull(message = "部门id不能为空", groups = { AddGroup.class, EditGroup.class })
private Long deptId;
/**
* 工号
*/
......@@ -88,6 +95,12 @@ public class EmployeeInfoParam {
@NotBlank(message = "性别不能为空", groups = { AddGroup.class, EditGroup.class })
private String gender;
/**
* 简历图片
*/
@NotBlank(message = "简历图片不能为空", groups = { AddGroup.class, EditGroup.class })
private String resumeImage;
/**
* 身份证号码
*/
......
package com.example.domain.params;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
......@@ -18,5 +19,8 @@ public class EmployeeTransferApplyParam {
@NotBlank(message = "二级部门不能为空")
private String secondLevelDepartment;
@NotNull(message = "部门id不能为空")
private Long deptId;
private String remark;
}
......@@ -23,7 +23,6 @@ import java.time.LocalDate;
* @date 2025/10/29 09:42
*/
@Getter
@Setter
@AutoMappers({
@AutoMapper(target = EmployeeInfo.class, reverseConvertGenerate = false),
@AutoMapper(target = EmployeeInfoParam.class, reverseConvertGenerate = false)
......@@ -100,6 +99,12 @@ public class EmployeeInfoImportVo extends ExcelBaseEntity implements Serializabl
@ExcelDictFormat(dictType = HrConstant.HR_USER_SEX)
private String gender;
/**
* 简历图片
*/
@ExcelProperty(value = "简历图片")
private String resumeImage;
// /**
// * 性别
// */
......@@ -385,4 +390,202 @@ public class EmployeeInfoImportVo extends ExcelBaseEntity implements Serializabl
*/
@ExcelProperty(value = "离职原因")
private String resignationReason;
public void setPlate(String plate) {
this.plate = plate == null ? null : plate.trim();
}
public void setProject(String project) {
this.project = project == null ? null : project.trim();
}
public void setFirstLevelDepartment(String firstLevelDepartment) {
this.firstLevelDepartment = firstLevelDepartment == null ? null : firstLevelDepartment.trim();
}
public void setSecondLevelDepartment(String secondLevelDepartment) {
this.secondLevelDepartment = secondLevelDepartment == null ? null : secondLevelDepartment.trim();
}
public void setEmployeeId(String employeeId) {
this.employeeId = employeeId == null ? null : employeeId.trim();
}
public void setJobLevel(String jobLevel) {
this.jobLevel = jobLevel == null ? null : jobLevel.trim();
}
public void setPosition(String position) {
this.position = position == null ? null : position.trim();
}
public void setPost(String post) {
this.post = post == null ? null : post.trim();
}
public void setName(String name) {
this.name = name == null ? null : name.trim();
}
public void setGender(String gender) {
this.gender = gender == null ? null : gender.trim();
}
public void setResumeImage(String resumeImage) {
this.resumeImage = resumeImage == null ? null : resumeImage.trim();
}
public void setIdCardNumber(String idCardNumber) {
this.idCardNumber = idCardNumber == null ? null : idCardNumber.trim();
}
public void setBirthDate(LocalDate birthDate) {
this.birthDate = birthDate;
}
public void setAge(Integer age) {
this.age = age;
}
public void setAgeGroup(String ageGroup) {
this.ageGroup = ageGroup == null ? null : ageGroup.trim();
}
public void setNativePlace(String nativePlace) {
this.nativePlace = nativePlace == null ? null : nativePlace.trim();
}
public void setEthnicity(String ethnicity) {
this.ethnicity = ethnicity == null ? null : ethnicity.trim();
}
public void setMaritalStatus(String maritalStatus) {
this.maritalStatus = maritalStatus == null ? null : maritalStatus.trim();
}
public void setPoliticalStatus(String politicalStatus) {
this.politicalStatus = politicalStatus == null ? null : politicalStatus.trim();
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber == null ? null : phoneNumber.trim();
}
public void setEmergencyContact(String emergencyContact) {
this.emergencyContact = emergencyContact == null ? null : emergencyContact.trim();
}
public void setEmergencyContactPhone(String emergencyContactPhone) {
this.emergencyContactPhone = emergencyContactPhone == null ? null : emergencyContactPhone.trim();
}
public void setHomeAddress(String homeAddress) {
this.homeAddress = homeAddress == null ? null : homeAddress.trim();
}
public void setHouseholdRegistrationAddress(String householdRegistrationAddress) {
this.householdRegistrationAddress = householdRegistrationAddress == null ? null : householdRegistrationAddress.trim();
}
public void setWorkStartDate(LocalDate workStartDate) {
this.workStartDate = workStartDate;
}
public void setEntryDate(LocalDate entryDate) {
this.entryDate = entryDate;
}
public void setYearsOfService(String yearsOfService) {
this.yearsOfService = yearsOfService == null ? null : yearsOfService.trim();
}
public void setYearsOfServiceSegment(String yearsOfServiceSegment) {
this.yearsOfServiceSegment = yearsOfServiceSegment == null ? null : yearsOfServiceSegment.trim();
}
public void setEducation(String education) {
this.education = education == null ? null : education.trim();
}
public void setDegree(String degree) {
this.degree = degree == null ? null : degree.trim();
}
public void setGraduationDate(LocalDate graduationDate) {
this.graduationDate = graduationDate;
}
public void setMajor(String major) {
this.major = major == null ? null : major.trim();
}
public void setGraduateSchool(String graduateSchool) {
this.graduateSchool = graduateSchool == null ? null : graduateSchool.trim();
}
public void setEmployeeType(String employeeType) {
this.employeeType = employeeType == null ? null : employeeType.trim();
}
public void setProfessionalTitle(String professionalTitle) {
this.professionalTitle = professionalTitle == null ? null : professionalTitle.trim();
}
public void setResume(String resume) {
this.resume = resume == null ? null : resume.trim();
}
public void setEmploymentForm(String employmentForm) {
this.employmentForm = employmentForm == null ? null : employmentForm.trim();
}
public void setContractTerm(String contractTerm) {
this.contractTerm = contractTerm == null ? null : contractTerm.trim();
}
public void setContractStartDate(LocalDate contractStartDate) {
this.contractStartDate = contractStartDate;
}
public void setContractEndDate(LocalDate contractEndDate) {
this.contractEndDate = contractEndDate;
}
public void setContractExpirationReminder(LocalDate contractExpirationReminder) {
this.contractExpirationReminder = contractExpirationReminder;
}
public void setContractSigningStatus(String contractSigningStatus) {
this.contractSigningStatus = contractSigningStatus == null ? null : contractSigningStatus.trim();
}
public void setContractEntity(String contractEntity) {
this.contractEntity = contractEntity == null ? null : contractEntity.trim();
}
public void setRegularizationDate(LocalDate regularizationDate) {
this.regularizationDate = regularizationDate;
}
public void setTransferStatus(String transferStatus) {
this.transferStatus = transferStatus == null ? null : transferStatus.trim();
}
public void setRewardPunishmentStatus(String rewardPunishmentStatus) {
this.rewardPunishmentStatus = rewardPunishmentStatus == null ? null : rewardPunishmentStatus.trim();
}
public void setRemarks(String remarks) {
this.remarks = remarks == null ? null : remarks.trim();
}
public void setResignationDate(LocalDate resignationDate) {
this.resignationDate = resignationDate;
}
public void setResignationReason(String resignationReason) {
this.resignationReason = resignationReason == null ? null : resignationReason.trim();
}
}
......@@ -16,6 +16,7 @@ import top.binfast.common.excel.annotion.ExcelDictFormat;
import top.binfast.common.excel.converters.ExcelDictConvert;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import top.binfast.common.excel.converters.ExcelUrlImageConverter;
import java.io.Serial;
import java.io.Serializable;
......@@ -103,6 +104,12 @@ public class EmployeeInfoVo implements TransPojo, Serializable {
@Trans(type = TransType.DICTIONARY, key = HrConstant.HR_USER_SEX)
private String gender;
/**
* 简历图片
*/
@ExcelProperty(value = "简历图片", converter = ExcelUrlImageConverter.class)
private String resumeImage;
/**
* 身份证号码
*/
......
......@@ -7,6 +7,7 @@ import com.example.domain.EmployeeInfo;
import com.example.domain.params.*;
import com.example.domain.vo.EmployeeInfoImportVo;
import com.example.domain.vo.EmployeeInfoVo;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.stream.Stream;
......@@ -42,7 +43,7 @@ public interface EmployeeInfoServ extends IService<EmployeeInfo> {
* @param stream 员工信息列表
* @return 是否导入成功
*/
Response importEmployeeList(Stream<EmployeeInfoImportVo> stream);
Response importEmployeeList(Stream<EmployeeInfoImportVo> stream, MultipartFile file);
/**
* 校验员工名称是否唯一
......@@ -50,7 +51,7 @@ public interface EmployeeInfoServ extends IService<EmployeeInfo> {
* @param param 员工信息
* @return 是否唯一
*/
Boolean checkEmployeeNameUnique(EmployeeInfoParam param);
Boolean checkEmployeeIdCardNumberUnique(EmployeeInfoParam param);
/**
* 查询员工信息
*
......
package com.example.service;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author 刘斌
* @date 2025/11/6 11:27
*/
public interface EmployeeSysDeptServ {
/**
* 根据部门id查询部门名称以及父级部门名称
*
* @param deptId 部门id
*/
List<String> selectDeptNameAncestorsById(Long deptId);
Map<String, Long> selectJoinDeptNames();
}
......@@ -19,23 +19,27 @@ import com.example.domain.params.*;
import com.example.domain.vo.EmployeeInfoImportVo;
import com.example.domain.vo.EmployeeInfoVo;
import com.example.mapper.EmployeeInfoMapper;
import com.example.service.EmployeeAuditLogServ;
import com.example.service.EmployeeDeptServ;
import com.example.service.EmployeeFlowServ;
import com.example.service.EmployeeInfoServ;
import com.example.service.*;
import lombok.RequiredArgsConstructor;
import org.dromara.trans.service.impl.TransService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import top.binfast.common.core.enums.ResultCode;
import top.binfast.common.core.util.LambdaUtil;
import top.binfast.common.core.util.MapstructUtils;
import top.binfast.common.excel.core.ExcelContextHolder;
import top.binfast.common.excel.image.CellImageData;
import top.binfast.common.excel.image.ExcelProcessingResult;
import top.binfast.common.excel.image.TempFileExcelImageImporter;
import top.binfast.common.log.model.SysAuditLog;
import top.binfast.common.mybatis.util.QueryUtil;
import top.binfast.common.oss.core.OssService;
import top.binfast.common.oss.entity.UploadResult;
import top.binfast.common.satoken.util.SecurityUtils;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Stream;
/**
......@@ -49,10 +53,11 @@ import java.util.stream.Stream;
public class EmployeeInfoServImpl extends ServiceImpl<EmployeeInfoMapper, EmployeeInfo> implements EmployeeInfoServ {
private final EmployeeInfoMapper employeeInfoMapper;
private final EmployeeDeptServ employeeDeptServ;
private final EmployeeSysDeptServ employeeSysDeptServ;
private final EmployeeFlowServ employeeFlowServ;
private final EmployeeAuditLogServ employeeAuditLogServ;
private final TransService transService;
private final OssService ossService;
/**
* 分页查询员工信息列表
......@@ -145,10 +150,16 @@ public class EmployeeInfoServImpl extends ServiceImpl<EmployeeInfoMapper, Employ
* @return 是否导入成功
*/
@Override
public Response importEmployeeList(Stream<EmployeeInfoImportVo> list) {
public Response importEmployeeList(Stream<EmployeeInfoImportVo> list, MultipartFile file) {
List<EmployeeInfoImportVo> errorList = new ArrayList<>();
List<EmployeeInfo> successList = new ArrayList<>();
Set<String> deptNameGroups = new HashSet<>();
List<EmployeeInfoImportVo> successList = new ArrayList<>();
CompletableFuture<ExcelProcessingResult> future = CompletableFuture.supplyAsync(() -> {
try {
return TempFileExcelImageImporter.importExcelWithAllImages(file);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
list.forEach(item -> {
// System.out.println(item.getNickName());
// item.validGroup(AddGroup.class);
......@@ -158,22 +169,34 @@ public class EmployeeInfoServImpl extends ServiceImpl<EmployeeInfoMapper, Employ
errorList.add(item);
return;
}
// transService.unTransOne(item);
EmployeeInfoParam employeeInfoParam = MapstructUtils.convert(item, EmployeeInfoParam.class);
if (!checkEmployeeNameUnique(employeeInfoParam)) {
if (!checkEmployeeIdCardNumberUnique(employeeInfoParam)) {
item.addError("姓名已存在");
errorList.add(item);
return;
}
EmployeeInfo employeeInfo = MapstructUtils.convert(employeeInfoParam, EmployeeInfo.class);
successList.add(employeeInfo);
deptNameGroups.add(employeeInfo.getFirstLevelDepartment() + StrUtil.DASHED + employeeInfo.getSecondLevelDepartment());
successList.add(item);
});
if (CollUtil.isNotEmpty(successList)) {
employeeInfoMapper.insert(successList);
EmployeeDeptCheckAndSaveParam checkAndSaveParam =
new EmployeeDeptCheckAndSaveParam(deptNameGroups, SecurityUtils.getCurrentUserId(), SecurityUtils.getTenantId());
employeeDeptServ.checkAndSaveDept(checkAndSaveParam);
Map<String, Long> deptNamesIdMap = employeeSysDeptServ.selectJoinDeptNames();
ExcelProcessingResult excelProcessingResult = future.join();
List<EmployeeInfo> insertList = new ArrayList<>(successList.size());
for (EmployeeInfoImportVo importVo : successList) {
this.handleImageToUrl(excelProcessingResult, importVo);
Long leafDeptId = deptNamesIdMap.get(buildDeptNameStr(importVo));
if (leafDeptId == null) {
importVo.addError("部门不存在");
errorList.add(importVo);
continue;
}
EmployeeInfo employeeInfo = MapstructUtils.convert(importVo, EmployeeInfo.class);
employeeInfo.setDeptId(leafDeptId);
insertList.add(employeeInfo);
}
employeeInfoMapper.insert(insertList);
// EmployeeDeptCheckAndSaveParam checkAndSaveParam =
// new EmployeeDeptCheckAndSaveParam(deptNameGroups, SecurityUtils.getCurrentUserId(), SecurityUtils.getTenantId());
// employeeDeptServ.checkAndSaveDept(checkAndSaveParam);
}
StringBuilder message;
if (CollUtil.isNotEmpty(errorList)) {
......@@ -189,10 +212,36 @@ public class EmployeeInfoServImpl extends ServiceImpl<EmployeeInfoMapper, Employ
}
}
private void handleImageToUrl(ExcelProcessingResult excelProcessingResult, EmployeeInfoImportVo importVo) {
// 处理图片,保存到OSS,替换成url
List<CellImageData> imageData = null;
if (excelProcessingResult.checkWpsOrOffice(importVo.getResumeImage())) {
imageData = excelProcessingResult.getWpsImageData(importVo.getResumeImage());
} else {
imageData = excelProcessingResult.getOfficeImageData(importVo.getLineNum());
}
if (CollUtil.isNotEmpty(imageData)) {
CellImageData cellImageData = imageData.getFirst();
UploadResult uploadResult = ossService.upload(cellImageData.getImageName(), cellImageData.getImageData());
importVo.setResumeImage(uploadResult.getUrl());
}
}
private String buildDeptNameStr(EmployeeInfoImportVo importVo) {
StringBuilder builder = new StringBuilder(importVo.getPlate() + StrUtil.SLASH + importVo.getProject());
if (StrUtil.isNotBlank(importVo.getFirstLevelDepartment())) {
builder.append(StrUtil.SLASH).append(importVo.getFirstLevelDepartment());
}
if (StrUtil.isNotBlank(importVo.getSecondLevelDepartment())) {
builder.append(StrUtil.SLASH).append(importVo.getSecondLevelDepartment());
}
return builder.toString();
}
@Override
public Boolean checkEmployeeNameUnique(EmployeeInfoParam employee) {
public Boolean checkEmployeeIdCardNumberUnique(EmployeeInfoParam employee) {
boolean exist = employeeInfoMapper.exists(new LambdaQueryWrapper<EmployeeInfo>()
.eq(EmployeeInfo::getName, employee.getName())
.eq(EmployeeInfo::getIdCardNumber, employee.getIdCardNumber())
.ne(ObjectUtil.isNotNull(employee.getId()), EmployeeInfo::getId, employee.getId()));
return !exist;
}
......@@ -235,9 +284,9 @@ public class EmployeeInfoServImpl extends ServiceImpl<EmployeeInfoMapper, Employ
// 检查或增加部门
Set<String> deptNameGroups = new HashSet<>();
deptNameGroups.add(employeeInfo.getFirstLevelDepartment() + StrUtil.DASHED + employeeInfo.getSecondLevelDepartment());
EmployeeDeptCheckAndSaveParam checkAndSaveParam =
new EmployeeDeptCheckAndSaveParam(deptNameGroups, SecurityUtils.getCurrentUserId(), SecurityUtils.getTenantId());
employeeDeptServ.checkAndSaveDept(checkAndSaveParam);
// EmployeeDeptCheckAndSaveParam checkAndSaveParam =
// new EmployeeDeptCheckAndSaveParam(deptNameGroups, SecurityUtils.getCurrentUserId(), SecurityUtils.getTenantId());
// employeeDeptServ.checkAndSaveDept(checkAndSaveParam);
employeeInfo.setEntryApplyStatus(HrFlowEnum.WAITING.getStatus());
return this.save(employeeInfo);
}
......
package com.example.service.impl;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.example.constant.HrFlowEnum;
import com.example.domain.EmployeeInfo;
import com.example.mapper.EmployeeInfoMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;
import java.util.List;
/**
* @author 刘斌
* @date 2025/11/5 17:01
*/
@Component
@RequiredArgsConstructor
@Slf4j
public class EmployeeScheduleService {
private final EmployeeInfoMapper employeeInfoMapper;
/**
* 方式1:使用cron表达式
* 每天凌晨1点执行
*/
@Scheduled(cron = "0 0 1 * * ?")
public void midnightTask() {
executeSafely("刷新员工工龄以及年龄", this::processRefreshInfo);
}
/**
* 刷新员工工龄以及工龄组
*/
private void processRefreshInfo() {
List<EmployeeInfo> employeeInfos = employeeInfoMapper.selectList(new LambdaUpdateWrapper<EmployeeInfo>()
.eq(EmployeeInfo::getEntryApplyStatus, HrFlowEnum.FINISH.getStatus())
.ne(EmployeeInfo::getResignationApplyStatus, HrFlowEnum.FINISH.getStatus()));
for (EmployeeInfo employeeInfo : employeeInfos) {
Period period = LocalDateTimeUtil.betweenPeriod(employeeInfo.getEntryDate(), LocalDate.from(LocalDateTime.now()));
int years = period.getYears();
int months = period.getMonths();
employeeInfo.setYearsOfService(years > 0 ? years + "年" + months + "个月" : months + "个月");
// TODO 更新员工工龄组
precessRefreshAge(employeeInfo);
}
}
private void precessRefreshAge(EmployeeInfo employeeInfo) {
Period period = LocalDateTimeUtil.betweenPeriod(employeeInfo.getBirthDate(), LocalDate.from(LocalDateTime.now()));
if (employeeInfo.getAge() != period.getYears()) {
employeeInfo.setAge(period.getYears());
// TODO 更新员工年龄组
}
}
/**
* 安全执行方法,包含异常处理
*/
private void executeSafely(String taskName, Runnable task) {
log.info("开始执行{}", taskName);
long startTime = System.currentTimeMillis();
try {
task.run();
long cost = System.currentTimeMillis() - startTime;
log.info("{}执行完成,耗时:{}ms", taskName, cost);
} catch (Exception e) {
log.error("{}执行失败", taskName, e);
// 这里可以添加告警逻辑,如发送邮件、短信等
}
}
}
package com.example.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.example.constant.HrCacheConstants;
import com.example.service.EmployeeSysDeptServ;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import top.binfast.app.biz.sysapi.bean.model.auth.SysDept;
import top.binfast.app.biz.sysapi.bean.params.sysDept.SysDeptParam;
import top.binfast.app.biz.sysapi.bean.vo.sysDept.SysDeptDTO;
import top.binfast.app.biz.sysapi.dao.auth.SysDeptMapper;
import top.binfast.app.biz.sysbiz.service.SysDeptServ;
import top.binfast.common.core.bean.dto.TreeDTO;
import top.binfast.common.core.constant.CacheNames;
import top.binfast.common.core.util.LambdaUtil;
import top.binfast.common.core.util.StringUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author 刘斌
* @date 2025/11/6 11:30
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class EmployeeSysDeptServImpl implements EmployeeSysDeptServ {
private final SysDeptMapper sysDeptMapper;
private final SysDeptServ sysDeptServ;
@Cacheable(cacheNames = HrCacheConstants.DEPT_AND_PARENT_NAME, key = "#deptId")
@Override
public List<String> selectDeptNameAncestorsById(Long deptId) {
SysDeptDTO dept = sysDeptServ.selectDeptById(deptId);
List<Long> split = StringUtils.splitTo(dept.getNodePath(), StrUtil.SLASH, Convert::toLong);
if (split.size() <= 2) {
return List.of(dept.getName());
}
List<SysDept> deptNameList = sysDeptMapper.selectByIds(split);
return LambdaUtil.mapToList(deptNameList, SysDept::getName);
}
@Override
public Map<String, Long> selectJoinDeptNames() {
// if (CollUtil.isEmpty(joinNameSet)) {
// return new HashMap<>();
// }
Map<String, Long> result = new HashMap<>();
List<SysDeptDTO> sysDeptDTOS = sysDeptServ.deptTreeForOptions();
traverseDeptTree(sysDeptDTOS, "", result);
// for (String joinName : joinNameSet) {
// List<String> deptNameList = StrUtil.split(joinName, StrUtil.SLASH);
//
// }
return result;
}
// 遍历根节点列表
private <T extends TreeDTO> void traverseDeptTree(List<T> deptList, String parentPath, Map<String, Long> result) {
if (CollUtil.isEmpty(deptList)) {
return;
}
for (TreeDTO dept : deptList) {
String currentPath = StrUtil.isEmpty(parentPath) ? dept.getLabel() : parentPath + StrUtil.SLASH + dept.getLabel();
// 递归处理子节点
if (CollUtil.isNotEmpty(dept.getChildren())) {
traverseDeptTree(dept.getChildren(), currentPath, result);
} else {
result.put(currentPath, dept.getId());
}
}
}
}
......@@ -8,7 +8,7 @@
2. 注意修改 <property name="${log-path}/log.path" value=""/> 的value模块
-->
<configuration debug="false" scan="false">
<property name="log.path" value="./logs/${project.artifactId}"/>
<property name="log.path" value="./logs"/>
<!-- 日志最大的历史 30天 -->
<property name="log.maxHistory" value="30"/>
<property name="log.level" value="debug"/>
......@@ -126,4 +126,4 @@
<appender-ref ref="info"/>
<appender-ref ref="error"/>
</root>
</configuration>
</configuration>
\ No newline at end of file
package com.example;
import com.example.service.EmployeeSysDeptServ;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.HashSet;
@SpringBootTest
class ApplicationTests {
@Resource
private EmployeeSysDeptServ employeeSysDeptServ;
@Test
void contextLoads() {
employeeSysDeptServ.selectJoinDeptNames();
}
}
......@@ -10,7 +10,7 @@
<properties>
<java.version>21</java.version>
<spring-boot.version>3.5.6</spring-boot.version>
<spring-boot.version>3.5.7</spring-boot.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven-jar-plugin.version>3.4.2</maven-jar-plugin.version>
......@@ -18,7 +18,7 @@
<mapstruct-plus.version>1.4.8</mapstruct-plus.version>
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
<therapi-javadoc.version>0.15.0</therapi-javadoc.version>
<binfast.version>1.2.3</binfast.version>
<binfast.version>1.2.5</binfast.version>
<lombok.version>1.18.38</lombok.version>
</properties>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment