mirror of
https://gitee.com/yudaocode/yudao-boot-mini.git
synced 2026-10-06 14:13:14 +08:00
feat(hrm): consolidate human resources management
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
<module>yudao-module-system</module>
|
||||
<module>yudao-module-infra</module>
|
||||
<!-- <module>yudao-module-member</module>-->
|
||||
<!-- <module>yudao-module-bpm</module>-->
|
||||
<module>yudao-module-bpm</module>
|
||||
<!-- <module>yudao-module-report</module>-->
|
||||
<!-- <module>yudao-module-mp</module>-->
|
||||
<!-- <module>yudao-module-pay</module>-->
|
||||
@@ -26,7 +26,9 @@
|
||||
<!-- <module>yudao-module-iot</module>-->
|
||||
<!-- <module>yudao-module-mes</module>-->
|
||||
<!-- <module>yudao-module-wms</module>-->
|
||||
<!-- <module>yudao-module-im</module>-->
|
||||
<module>yudao-module-hrm</module>
|
||||
<module>yudao-module-fms</module>
|
||||
<!-- <module>yudao-module-im</module>-->
|
||||
<!-- 请参考 https://doc.iocoder.cn/ai/build/ 文档,完成 AI 模块的启动!!! -->
|
||||
<!-- <module>yudao-module-ai</module>-->
|
||||
</modules>
|
||||
@@ -36,7 +38,7 @@
|
||||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
||||
|
||||
<properties>
|
||||
<revision>2026.06-SNAPSHOT</revision>
|
||||
<revision>2026.07-SNAPSHOT</revision>
|
||||
<!-- Maven 相关 -->
|
||||
<java.version>17</java.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
<hutool-5.version>5.8.46</hutool-5.version>
|
||||
<hutool-6.version>6.0.0-M22</hutool-6.version>
|
||||
<fastexcel.version>1.3.0</fastexcel.version>
|
||||
<aviator.version>5.3.3</aviator.version>
|
||||
<velocity.version>2.4.1</velocity.version>
|
||||
<fastjson.version>1.2.83</fastjson.version>
|
||||
<fastjson2.version>2.0.62</fastjson2.version>
|
||||
@@ -487,6 +488,12 @@
|
||||
<version>${fastexcel.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.googlecode.aviator</groupId>
|
||||
<artifactId>aviator</artifactId>
|
||||
<version>${aviator.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.tika</groupId>
|
||||
<artifactId>tika-core</artifactId> <!-- 文件类型的识别 -->
|
||||
|
||||
+40
-1
@@ -4,8 +4,10 @@ import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import java.util.function.*;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -33,6 +35,34 @@ public class CollectionUtils {
|
||||
return from.stream().anyMatch(predicate);
|
||||
}
|
||||
|
||||
public static <T> long sum(Collection<T> from, ToLongFunction<T> valueFunc) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return 0L;
|
||||
}
|
||||
return from.stream().mapToLong(valueFunc).sum();
|
||||
}
|
||||
|
||||
public static <T> BigDecimal sumBigDecimal(Collection<T> from, Function<T, BigDecimal> valueFunc) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
return from.stream().map(valueFunc).map(NumberUtils::zeroIfNull).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
}
|
||||
|
||||
public static <T> long count(Collection<T> from, Predicate<T> predicate) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return 0L;
|
||||
}
|
||||
return from.stream().filter(predicate).count();
|
||||
}
|
||||
|
||||
public static <T, R> long distinctCount(Collection<T> from, Function<T, R> keyMapper) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return 0L;
|
||||
}
|
||||
return from.stream().map(keyMapper).filter(Objects::nonNull).distinct().count();
|
||||
}
|
||||
|
||||
public static <T> List<T> filterList(Collection<T> from, Predicate<T> predicate) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new ArrayList<>();
|
||||
@@ -124,6 +154,15 @@ public class CollectionUtils {
|
||||
return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static <T, U, S extends Set<U>> S convertSetBySupplier(
|
||||
Collection<T> from, Function<T, U> func, Supplier<S> supplier) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return supplier.get();
|
||||
}
|
||||
return from.stream().map(func).filter(Objects::nonNull)
|
||||
.collect(Collectors.toCollection(supplier));
|
||||
}
|
||||
|
||||
public static <T, U> Set<U> convertLinkedSet(Collection<T> from, Function<T, U> func) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new LinkedHashSet<>();
|
||||
@@ -398,4 +437,4 @@ public class CollectionUtils {
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+496
-2
@@ -6,6 +6,9 @@ import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.time.*;
|
||||
@@ -14,13 +17,16 @@ import java.time.format.DateTimeParseException;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static cn.hutool.core.date.DatePattern.*;
|
||||
|
||||
/**
|
||||
* 时间工具类,用于 {@link LocalDateTime}
|
||||
* 时间工具类,用于 {@link LocalDate}、{@link LocalDateTime}
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@@ -54,6 +60,16 @@ public class LocalDateTimeUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析年月字符串为 {@link YearMonth},格式为 yyyy-MM
|
||||
*
|
||||
* @param month 年月字符串
|
||||
* @return 年月
|
||||
*/
|
||||
public static YearMonth parseYearMonth(String month) {
|
||||
return YearMonth.parse(month, NORM_MONTH_FORMATTER);
|
||||
}
|
||||
|
||||
public static LocalDateTime addTime(Duration duration) {
|
||||
return LocalDateTime.now().plus(duration);
|
||||
}
|
||||
@@ -70,6 +86,319 @@ public class LocalDateTimeUtils {
|
||||
return date.isAfter(LocalDateTime.now());
|
||||
}
|
||||
|
||||
public static boolean beforeOrEqualNow(LocalDateTime time) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
return time.isBefore(now) || time.isEqual(now);
|
||||
}
|
||||
|
||||
public static boolean beforeNow(LocalDate date) {
|
||||
return date.isBefore(LocalDate.now());
|
||||
}
|
||||
|
||||
public static boolean beforeOrEqualNow(LocalDate date) {
|
||||
LocalDate today = LocalDate.now();
|
||||
return date.isBefore(today) || date.isEqual(today);
|
||||
}
|
||||
|
||||
public static boolean afterNow(LocalDate date) {
|
||||
return date.isAfter(LocalDate.now());
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断时间是否处于一月
|
||||
*
|
||||
* @param time 时间
|
||||
* @return 是否处于一月
|
||||
*/
|
||||
public static boolean isJanuary(LocalDateTime time) {
|
||||
return time.getMonthValue() == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断第一个日期是否早于或等于第二个日期
|
||||
*
|
||||
* @param firstDate 第一个日期
|
||||
* @param secondDate 第二个日期
|
||||
* @return 是否早于或等于
|
||||
*/
|
||||
public static boolean isBeforeOrEqual(LocalDate firstDate, LocalDate secondDate) {
|
||||
return firstDate.isBefore(secondDate) || firstDate.isEqual(secondDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断第一个日期是否晚于或等于第二个日期
|
||||
*
|
||||
* @param firstDate 第一个日期
|
||||
* @param secondDate 第二个日期
|
||||
* @return 是否晚于或等于
|
||||
*/
|
||||
public static boolean isAfterOrEqual(LocalDate firstDate, LocalDate secondDate) {
|
||||
return firstDate.isAfter(secondDate) || firstDate.isEqual(secondDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断第一个时间是否早于或等于第二个时间
|
||||
*
|
||||
* @param firstTime 第一个时间
|
||||
* @param secondTime 第二个时间
|
||||
* @return 是否早于或等于
|
||||
*/
|
||||
public static boolean isBeforeOrEqual(LocalDateTime firstTime, LocalDateTime secondTime) {
|
||||
return firstTime.isBefore(secondTime) || firstTime.isEqual(secondTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断第一个时间是否晚于或等于第二个时间
|
||||
*
|
||||
* @param firstTime 第一个时间
|
||||
* @param secondTime 第二个时间
|
||||
* @return 是否晚于或等于
|
||||
*/
|
||||
public static boolean isAfterOrEqual(LocalDateTime firstTime, LocalDateTime secondTime) {
|
||||
return firstTime.isAfter(secondTime) || firstTime.isEqual(secondTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断时间范围是否有效
|
||||
*
|
||||
* @param beginTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @return 开始、结束时间非空,且结束时间晚于开始时间时返回 {@code true}
|
||||
*/
|
||||
public static boolean isTimeRangeValid(LocalDateTime beginTime, LocalDateTime endTime) {
|
||||
return beginTime != null && endTime != null && endTime.isAfter(beginTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断时间范围是否存在有效边界
|
||||
*
|
||||
* @param times 时间范围
|
||||
* @return 任一边界非空时返回 {@code true}
|
||||
*/
|
||||
public static boolean isTimeRangePresent(LocalDateTime[] times) {
|
||||
return ArrayUtils.get(times, 0) != null || ArrayUtils.get(times, 1) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断指定日期是否位于闭区间内
|
||||
*
|
||||
* @param beginDate 开始日期
|
||||
* @param endDate 结束日期
|
||||
* @param date 指定日期
|
||||
* @return 是否位于闭区间内
|
||||
*/
|
||||
public static boolean isBetween(LocalDate beginDate, LocalDate endDate, LocalDate date) {
|
||||
return beginDate != null && endDate != null && date != null
|
||||
&& isAfterOrEqual(date, beginDate) && isBeforeOrEqual(date, endDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断指定日期是否不在闭区间内
|
||||
*
|
||||
* @param beginDate 开始日期
|
||||
* @param endDate 结束日期
|
||||
* @param date 指定日期
|
||||
* @return 是否不在闭区间内;任一参数为空时返回 {@code true}
|
||||
*/
|
||||
public static boolean isNotBetween(LocalDate beginDate, LocalDate endDate, LocalDate date) {
|
||||
return !isBetween(beginDate, endDate, date);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断指定时间是否位于闭区间内
|
||||
*
|
||||
* @param beginTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @param time 指定时间
|
||||
* @return 是否位于闭区间内
|
||||
*/
|
||||
public static boolean isBetween(LocalDateTime beginTime, LocalDateTime endTime, LocalDateTime time) {
|
||||
return beginTime != null && endTime != null && time != null
|
||||
&& isAfterOrEqual(time, beginTime) && isBeforeOrEqual(time, endTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断指定时间是否不在闭区间内
|
||||
*
|
||||
* @param beginTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @param time 指定时间
|
||||
* @return 是否不在闭区间内;任一参数为空时返回 {@code true}
|
||||
*/
|
||||
public static boolean isNotBetween(LocalDateTime beginTime, LocalDateTime endTime, LocalDateTime time) {
|
||||
return !isBetween(beginTime, endTime, time);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个日期之间经过的完整年数
|
||||
*
|
||||
* @param beginDate 开始日期
|
||||
* @param endDate 结束日期
|
||||
* @return 完整年数;日期为空或开始日期晚于结束日期时,返回 0
|
||||
*/
|
||||
public static int getYearsBetween(LocalDate beginDate, LocalDate endDate) {
|
||||
if (beginDate == null || endDate == null || beginDate.isAfter(endDate)) {
|
||||
return 0;
|
||||
}
|
||||
return Period.between(beginDate, endDate).getYears();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从时间范围列表中扣除指定时间范围
|
||||
*
|
||||
* @param sourceRanges 原时间范围列表
|
||||
* @param excludedRange 需要扣除的时间范围
|
||||
* @return 扣除后的时间范围列表
|
||||
*/
|
||||
public static List<TimeRange> subtractTimeRanges(List<TimeRange> sourceRanges, TimeRange excludedRange) {
|
||||
List<TimeRange> result = new ArrayList<>();
|
||||
for (TimeRange sourceRange : sourceRanges) {
|
||||
TimeRange overlap = intersectTimeRange(sourceRange, excludedRange);
|
||||
if (overlap == null) {
|
||||
result.add(sourceRange);
|
||||
continue;
|
||||
}
|
||||
if (sourceRange.getStartTime().isBefore(overlap.getStartTime())) {
|
||||
result.add(new TimeRange(sourceRange.getStartTime(), overlap.getStartTime()));
|
||||
}
|
||||
if (overlap.getEndTime().isBefore(sourceRange.getEndTime())) {
|
||||
result.add(new TimeRange(overlap.getEndTime(), sourceRange.getEndTime()));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并重叠或首尾相接的时间范围
|
||||
*
|
||||
* @param timeRanges 时间范围列表
|
||||
* @return 合并后的时间范围列表
|
||||
*/
|
||||
public static List<TimeRange> mergeTimeRanges(List<TimeRange> timeRanges) {
|
||||
if (CollUtil.isEmpty(timeRanges)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<TimeRange> sortedRanges = timeRanges.stream()
|
||||
.sorted(Comparator.comparing(TimeRange::getStartTime))
|
||||
.collect(Collectors.toList());
|
||||
List<TimeRange> result = new ArrayList<>();
|
||||
TimeRange currentRange = CollUtil.getFirst(sortedRanges);
|
||||
for (int i = 1; i < sortedRanges.size(); i++) {
|
||||
TimeRange nextRange = sortedRanges.get(i);
|
||||
if (nextRange.getStartTime().isAfter(currentRange.getEndTime())) {
|
||||
result.add(currentRange);
|
||||
currentRange = nextRange;
|
||||
continue;
|
||||
}
|
||||
LocalDateTime endTime = currentRange.getEndTime().isAfter(nextRange.getEndTime())
|
||||
? currentRange.getEndTime() : nextRange.getEndTime();
|
||||
currentRange = new TimeRange(currentRange.getStartTime(), endTime);
|
||||
}
|
||||
result.add(currentRange);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个时间范围列表的交集
|
||||
*
|
||||
* @param firstRanges 第一组时间范围
|
||||
* @param secondRanges 第二组时间范围
|
||||
* @return 合并后的交集时间范围列表
|
||||
*/
|
||||
public static List<TimeRange> intersectTimeRanges(
|
||||
List<TimeRange> firstRanges, List<TimeRange> secondRanges) {
|
||||
List<TimeRange> result = new ArrayList<>();
|
||||
for (TimeRange firstRange : firstRanges) {
|
||||
for (TimeRange secondRange : secondRanges) {
|
||||
TimeRange overlap = intersectTimeRange(firstRange, secondRange);
|
||||
if (overlap != null) {
|
||||
result.add(overlap);
|
||||
}
|
||||
}
|
||||
}
|
||||
return mergeTimeRanges(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个时间范围的交集
|
||||
*
|
||||
* @param firstRange 第一个时间范围
|
||||
* @param secondRange 第二个时间范围
|
||||
* @return 交集时间范围;没有交集时返回 {@code null}
|
||||
*/
|
||||
public static TimeRange intersectTimeRange(TimeRange firstRange, TimeRange secondRange) {
|
||||
LocalDateTime startTime = firstRange.getStartTime().isAfter(secondRange.getStartTime())
|
||||
? firstRange.getStartTime() : secondRange.getStartTime();
|
||||
LocalDateTime endTime = firstRange.getEndTime().isBefore(secondRange.getEndTime())
|
||||
? firstRange.getEndTime() : secondRange.getEndTime();
|
||||
return endTime.isAfter(startTime) ? new TimeRange(startTime, endTime) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算时间范围的总分钟数
|
||||
*
|
||||
* @param timeRanges 时间范围列表
|
||||
* @return 总分钟数
|
||||
*/
|
||||
public static int calculateDurationMinutes(List<TimeRange> timeRanges) {
|
||||
return timeRanges.stream()
|
||||
.mapToInt(range -> Math.toIntExact(Duration.between(
|
||||
range.getStartTime(), range.getEndTime()).toMinutes()))
|
||||
.sum();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建日期范围内每天对应的时间范围
|
||||
*
|
||||
* 当结束时刻早于或等于开始时刻时,按跨天时间范围处理。
|
||||
*
|
||||
* @param beginDate 开始日期
|
||||
* @param endDate 结束日期
|
||||
* @param beginTime 每日开始时刻
|
||||
* @param endTime 每日结束时刻
|
||||
* @return 每日时间范围列表
|
||||
*/
|
||||
public static List<TimeRange> buildDailyTimeRanges(
|
||||
LocalDate beginDate, LocalDate endDate, LocalTime beginTime, LocalTime endTime) {
|
||||
if (beginDate == null || endDate == null || beginTime == null || endTime == null
|
||||
|| beginDate.isAfter(endDate)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<TimeRange> result = new ArrayList<>();
|
||||
for (LocalDate date = beginDate; isBeforeOrEqual(date, endDate); date = date.plusDays(1)) {
|
||||
LocalDateTime rangeBeginTime = date.atTime(beginTime);
|
||||
LocalDateTime rangeEndTime = date.atTime(endTime);
|
||||
if (rangeEndTime.isBefore(rangeBeginTime) || rangeEndTime.isEqual(rangeBeginTime)) {
|
||||
rangeEndTime = rangeEndTime.plusDays(1);
|
||||
}
|
||||
result.add(new TimeRange(rangeBeginTime, rangeEndTime));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找包含指定时刻的每日时间范围
|
||||
*
|
||||
* 每日时间范围允许跨天,因此会同时检查基准日期的前一天、当天和后一天。
|
||||
* 范围匹配使用左右闭区间。
|
||||
*
|
||||
* @param date 基准日期
|
||||
* @param beginTime 每日开始时刻
|
||||
* @param endTime 每日结束时刻
|
||||
* @param time 指定时刻
|
||||
* @return 包含指定时刻的时间范围;不存在时返回 {@code null}
|
||||
*/
|
||||
public static TimeRange findDailyTimeRange(
|
||||
LocalDate date, LocalTime beginTime, LocalTime endTime, LocalDateTime time) {
|
||||
if (date == null || time == null) {
|
||||
return null;
|
||||
}
|
||||
List<TimeRange> timeRanges = buildDailyTimeRanges(
|
||||
date.minusDays(1), date.plusDays(1), beginTime, endTime);
|
||||
return CollUtil.findOne(timeRanges, timeRange -> isBetween(
|
||||
timeRange.getStartTime(), timeRange.getEndTime(), time));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Unix 秒时间戳转换为默认时区的本地时间
|
||||
*
|
||||
@@ -184,6 +513,62 @@ public class LocalDateTimeUtils {
|
||||
LocalDateTime.of(nowDate, startTime2), LocalDateTime.of(nowDate, endTime2));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定日期的开始时间
|
||||
*
|
||||
* @param date 日期
|
||||
* @return 当日开始时间;日期为空时返回 {@code null}
|
||||
*/
|
||||
public static LocalDateTime getDayBeginTime(LocalDate date) {
|
||||
return date == null ? null : date.atStartOfDay();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定时间所在日期的开始时间
|
||||
*
|
||||
* @param time 时间
|
||||
* @return 当日开始时间;时间为空时返回 {@code null}
|
||||
*/
|
||||
public static LocalDateTime getDayBeginTime(LocalDateTime time) {
|
||||
return time == null ? null : getDayBeginTime(time.toLocalDate());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定日期的结束时间
|
||||
*
|
||||
* @param date 日期
|
||||
* @return 当日结束时间;日期为空时返回 {@code null}
|
||||
*/
|
||||
public static LocalDateTime getDayEndTime(LocalDate date) {
|
||||
return date == null ? null : date.atTime(LocalTime.MAX);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得自然日期范围对应的日期时间闭区间
|
||||
*
|
||||
* @param beginDate 开始日期
|
||||
* @param endDate 结束日期
|
||||
* @return 从开始日期零点到结束日期最后时刻的闭区间;两个日期都为空时返回 {@code null}
|
||||
*/
|
||||
public static LocalDateTime[] getDateTimeRange(LocalDate beginDate, LocalDate endDate) {
|
||||
if (beginDate == null && endDate == null) {
|
||||
return null;
|
||||
}
|
||||
return new LocalDateTime[]{getDayBeginTime(beginDate), getDayEndTime(endDate)};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定月份的日期时间闭区间
|
||||
*
|
||||
* @param year 年份
|
||||
* @param month 月份
|
||||
* @return 从当月月初零点到月末最后时刻的闭区间
|
||||
*/
|
||||
public static LocalDateTime[] getMonthDateTimeRange(int year, int month) {
|
||||
LocalDate monthBeginDate = LocalDate.of(year, month, 1);
|
||||
return getDateTimeRange(monthBeginDate, monthBeginDate.with(TemporalAdjusters.lastDayOfMonth()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定日期所在的月份的开始时间
|
||||
* 例如:2023-09-30 00:00:00,000
|
||||
@@ -195,6 +580,16 @@ public class LocalDateTimeUtils {
|
||||
return date.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定日期的开始时间
|
||||
*
|
||||
* @param date 日期
|
||||
* @return 当日开始时间
|
||||
*/
|
||||
public static LocalDateTime beginOfDay(LocalDateTime date) {
|
||||
return date.with(LocalTime.MIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定日期所在的月份的最后时间
|
||||
* 例如:2023-09-30 23:59:59,999
|
||||
@@ -206,6 +601,82 @@ public class LocalDateTimeUtils {
|
||||
return date.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定年月的月初时间
|
||||
*
|
||||
* @param year 年份
|
||||
* @param month 月份
|
||||
* @return 月初时间
|
||||
*/
|
||||
public static LocalDateTime getMonthBeginTime(int year, int month) {
|
||||
return LocalDateTime.of(year, month, 1, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定年月的月初时间
|
||||
*
|
||||
* @param month 年月
|
||||
* @return 月初时间
|
||||
*/
|
||||
public static LocalDateTime getMonthBeginTime(YearMonth month) {
|
||||
return getMonthBeginTime(month.getYear(), month.getMonthValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定年月的下月月初时间
|
||||
*
|
||||
* @param year 年份
|
||||
* @param month 月份
|
||||
* @return 下月月初时间
|
||||
*/
|
||||
public static LocalDateTime getNextMonthBeginTime(int year, int month) {
|
||||
return getMonthBeginTime(year, month).plusMonths(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定年月的下月月初时间
|
||||
*
|
||||
* @param month 年月
|
||||
* @return 下月月初时间
|
||||
*/
|
||||
public static LocalDateTime getNextMonthBeginTime(YearMonth month) {
|
||||
return getMonthBeginTime(month).plusMonths(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断两个时间段是否存在正时长的重叠
|
||||
*
|
||||
* @param beginTime1 第一个范围的开始时间
|
||||
* @param endTime1 第一个范围的结束时间
|
||||
* @param beginTime2 第二个范围的开始时间
|
||||
* @param endTime2 第二个范围的结束时间
|
||||
* @return 是否存在正时长的重叠
|
||||
*/
|
||||
public static boolean isOverlap(LocalDateTime beginTime1, LocalDateTime endTime1,
|
||||
LocalDateTime beginTime2, LocalDateTime endTime2) {
|
||||
if (beginTime1 == null || endTime1 == null || beginTime2 == null || endTime2 == null) {
|
||||
return false;
|
||||
}
|
||||
return beginTime1.isBefore(endTime2) && endTime1.isAfter(beginTime2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断两个左右闭合的时间范围是否重叠
|
||||
*
|
||||
* @param beginTime1 第一个范围的开始时间
|
||||
* @param endTime1 第一个范围的结束时间
|
||||
* @param beginTime2 第二个范围的开始时间
|
||||
* @param endTime2 第二个范围的结束时间
|
||||
* @return 是否重叠
|
||||
*/
|
||||
public static boolean isClosedRangeOverlap(LocalDateTime beginTime1, LocalDateTime endTime1,
|
||||
LocalDateTime beginTime2, LocalDateTime endTime2) {
|
||||
if (beginTime1 == null || endTime1 == null || beginTime2 == null || endTime2 == null) {
|
||||
return false;
|
||||
}
|
||||
return isBeforeOrEqual(beginTime1, endTime2) && isAfterOrEqual(endTime1, beginTime2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定日期所在季度
|
||||
*
|
||||
@@ -353,7 +824,11 @@ public class LocalDateTimeUtils {
|
||||
* @return 日期列表,包含开始日期
|
||||
*/
|
||||
public static List<LocalDate> getDateList(LocalDate startDate, int days) {
|
||||
return startDate.datesUntil(startDate.plusDays(days)).toList();
|
||||
List<LocalDate> dateList = new ArrayList<>(days);
|
||||
for (int i = 0; i < days; i++) {
|
||||
dateList.add(startDate.plusDays(i));
|
||||
}
|
||||
return dateList;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -433,4 +908,23 @@ public class LocalDateTimeUtils {
|
||||
return sourceDateTime.atZone(zoneId).toEpochSecond();
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间范围
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
@SuppressWarnings("ClassCanBeRecord")
|
||||
public static class TimeRange {
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private final LocalDateTime startTime;
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
private final LocalDateTime endTime;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+34
-1
@@ -96,6 +96,39 @@ public class MoneyUtils {
|
||||
return new Money(0, fen).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化金额,默认进行四舍五入
|
||||
*
|
||||
* 位数:{@link #PRICE_SCALE}。金额为空时,按 0 处理。
|
||||
*
|
||||
* @param price 金额
|
||||
* @return 格式化后的金额
|
||||
*/
|
||||
public static BigDecimal priceScale(BigDecimal price) {
|
||||
return (price == null ? BigDecimal.ZERO : price).setScale(PRICE_SCALE, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
/**
|
||||
* 金额相加,默认进行四舍五入
|
||||
*
|
||||
* 位数:{@link #PRICE_SCALE}。忽略空金额,没有有效金额时返回 0。
|
||||
*
|
||||
* @param prices 金额数组
|
||||
* @return 金额相加结果
|
||||
*/
|
||||
public static BigDecimal priceAdd(BigDecimal... prices) {
|
||||
BigDecimal result = BigDecimal.ZERO;
|
||||
if (prices == null) {
|
||||
return priceScale(result);
|
||||
}
|
||||
for (BigDecimal price : prices) {
|
||||
if (price != null) {
|
||||
result = result.add(price);
|
||||
}
|
||||
}
|
||||
return priceScale(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 金额相乘,默认进行四舍五入
|
||||
*
|
||||
@@ -109,7 +142,7 @@ public class MoneyUtils {
|
||||
if (price == null || count == null) {
|
||||
return null;
|
||||
}
|
||||
return price.multiply(count).setScale(PRICE_SCALE, RoundingMode.HALF_UP);
|
||||
return priceScale(price.multiply(count));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+33
@@ -2,10 +2,12 @@ package cn.iocoder.yudao.framework.common.util.number;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
import cn.hutool.core.util.ReUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 数字的工具类,补全 {@link cn.hutool.core.util.NumberUtil} 的功能
|
||||
@@ -14,6 +16,13 @@ import java.util.List;
|
||||
*/
|
||||
public class NumberUtils {
|
||||
|
||||
private static final Pattern FIRST_BIG_DECIMAL_PATTERN = Pattern.compile("-?\\d+(?:\\.\\d+)?");
|
||||
|
||||
/**
|
||||
* 一百,用于百分数和比例计算
|
||||
*/
|
||||
public static final BigDecimal ONE_HUNDRED = new BigDecimal("100");
|
||||
|
||||
public static Long parseLong(String str) {
|
||||
return StrUtil.isNotEmpty(str) ? Long.valueOf(str) : null;
|
||||
}
|
||||
@@ -22,6 +31,30 @@ public class NumberUtils {
|
||||
return StrUtil.isNotEmpty(str) ? Integer.valueOf(str) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得数字,为 null 时返回 {@link BigDecimal#ZERO}
|
||||
*
|
||||
* @param value 数字
|
||||
* @return 数字
|
||||
*/
|
||||
public static BigDecimal zeroIfNull(BigDecimal value) {
|
||||
return value == null ? BigDecimal.ZERO : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文本中解析首个数字
|
||||
*
|
||||
* @param text 文本
|
||||
* @return 数字;不存在时返回 {@link BigDecimal#ZERO}
|
||||
*/
|
||||
public static BigDecimal parseFirstBigDecimal(String text) {
|
||||
if (StrUtil.isEmpty(text)) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
String number = ReUtil.getGroup0(FIRST_BIG_DECIMAL_PATTERN, text.replace(",", ""));
|
||||
return NumberUtil.toBigDecimal(number);
|
||||
}
|
||||
|
||||
public static boolean isAllNumber(List<String> values) {
|
||||
if (CollUtil.isEmpty(values)) {
|
||||
return false;
|
||||
|
||||
+24
@@ -5,10 +5,13 @@ import cn.hutool.core.lang.func.Func1;
|
||||
import cn.hutool.core.lang.func.LambdaUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.SortablePageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.SortingField;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
/**
|
||||
@@ -24,6 +27,27 @@ public class PageUtils {
|
||||
return (pageParam.getPageNo() - 1) * pageParam.getPageSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 对内存列表进行分页
|
||||
*
|
||||
* @param list 数据列表
|
||||
* @param pageParam 分页参数
|
||||
* @param <T> 数据类型
|
||||
* @return 分页结果
|
||||
*/
|
||||
public static <T> PageResult<T> buildPageResult(List<T> list, PageParam pageParam) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return PageResult.empty();
|
||||
}
|
||||
long total = list.size();
|
||||
if (PageParam.PAGE_SIZE_NONE.equals(pageParam.getPageSize())) {
|
||||
return new PageResult<>(list, total);
|
||||
}
|
||||
int fromIndex = Math.toIntExact(Math.min(getStart(pageParam), total));
|
||||
int toIndex = Math.toIntExact(Math.min((long) fromIndex + pageParam.getPageSize(), total));
|
||||
return new PageResult<>(list.subList(fromIndex, toIndex), total);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建排序字段(默认倒序)
|
||||
*
|
||||
|
||||
+9
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.common.util.servlet;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.servlet.JakartaServletUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
@@ -11,6 +12,7 @@ import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -102,4 +104,11 @@ public class ServletUtils {
|
||||
return JakartaServletUtil.getHeaderMap(request);
|
||||
}
|
||||
|
||||
public static void writeAttachment(HttpServletResponse response, String filename, byte[] content)
|
||||
throws IOException {
|
||||
response.getOutputStream().write(content);
|
||||
response.addHeader("Content-Disposition", "attachment;filename=" + HttpUtils.encodeUtf8(filename));
|
||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+34
@@ -6,7 +6,9 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@@ -26,6 +28,38 @@ public class CollectionUtilsTest {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSum() {
|
||||
assertEquals(6L, CollectionUtils.sum(Arrays.asList(1L, 2L, 3L), Long::longValue));
|
||||
assertEquals(0L, CollectionUtils.sum(Collections.<Long>emptyList(), Long::longValue));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCount() {
|
||||
assertEquals(2L, CollectionUtils.count(Arrays.asList(1, 2, 3, 4), value -> value % 2 == 0));
|
||||
assertEquals(0L, CollectionUtils.count(Collections.emptyList(), value -> true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDistinctCount() {
|
||||
assertEquals(3L, CollectionUtils.distinctCount(Arrays.asList("a", "b", "a", "c"), value -> value));
|
||||
assertEquals(0L, CollectionUtils.distinctCount(Collections.emptyList(), value -> value));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertSet_withSupplier() {
|
||||
// 调用,并断言:按指定 Set 类型完成转换、去重和排序
|
||||
TreeSet<Integer> result = CollectionUtils.convertSetBySupplier(
|
||||
Arrays.asList("3", "1", null, "3", "2"),
|
||||
value -> value == null ? null : Integer.valueOf(value), TreeSet::new);
|
||||
assertEquals(Arrays.asList(1, 2, 3), Arrays.asList(result.toArray(new Integer[0])));
|
||||
|
||||
// 调用,并断言:空集合仍返回指定 Set 类型
|
||||
result = CollectionUtils.convertSetBySupplier(Collections.<String>emptyList(),
|
||||
Integer::valueOf, TreeSet::new);
|
||||
assertEquals(TreeSet.class, result.getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDiffList() {
|
||||
// 准备参数
|
||||
|
||||
+342
@@ -0,0 +1,342 @@
|
||||
package cn.iocoder.yudao.framework.common.util.date;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.TimeRange;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.YearMonth;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* {@link LocalDateTimeUtils} 的单元测试
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class LocalDateTimeUtilsTest {
|
||||
|
||||
@Test
|
||||
public void testDateCompare() {
|
||||
// 准备参数
|
||||
LocalDate firstDate = LocalDate.of(2026, 7, 23);
|
||||
LocalDate secondDate = LocalDate.of(2026, 7, 24);
|
||||
|
||||
// 调用,并断言
|
||||
assertTrue(LocalDateTimeUtils.isBeforeOrEqual(firstDate, secondDate));
|
||||
assertTrue(LocalDateTimeUtils.isBeforeOrEqual(secondDate, secondDate));
|
||||
assertFalse(LocalDateTimeUtils.isBeforeOrEqual(secondDate, firstDate));
|
||||
assertTrue(LocalDateTimeUtils.isAfterOrEqual(secondDate, firstDate));
|
||||
assertTrue(LocalDateTimeUtils.isAfterOrEqual(secondDate, secondDate));
|
||||
assertFalse(LocalDateTimeUtils.isAfterOrEqual(firstDate, secondDate));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDateTimeCompare() {
|
||||
// 准备参数
|
||||
LocalDateTime firstTime = LocalDateTime.of(2026, 7, 24, 9, 0);
|
||||
LocalDateTime secondTime = LocalDateTime.of(2026, 7, 24, 18, 0);
|
||||
|
||||
// 调用,并断言
|
||||
assertTrue(LocalDateTimeUtils.isBeforeOrEqual(firstTime, secondTime));
|
||||
assertTrue(LocalDateTimeUtils.isBeforeOrEqual(secondTime, secondTime));
|
||||
assertFalse(LocalDateTimeUtils.isBeforeOrEqual(secondTime, firstTime));
|
||||
assertTrue(LocalDateTimeUtils.isAfterOrEqual(secondTime, firstTime));
|
||||
assertTrue(LocalDateTimeUtils.isAfterOrEqual(secondTime, secondTime));
|
||||
assertFalse(LocalDateTimeUtils.isAfterOrEqual(firstTime, secondTime));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsTimeRangeValid() {
|
||||
// 准备参数
|
||||
LocalDateTime beginTime = LocalDateTime.of(2026, 7, 24, 9, 0);
|
||||
LocalDateTime endTime = LocalDateTime.of(2026, 7, 24, 18, 0);
|
||||
|
||||
// 调用,并断言
|
||||
assertTrue(LocalDateTimeUtils.isTimeRangeValid(beginTime, endTime));
|
||||
assertFalse(LocalDateTimeUtils.isTimeRangeValid(beginTime, beginTime));
|
||||
assertFalse(LocalDateTimeUtils.isTimeRangeValid(endTime, beginTime));
|
||||
assertFalse(LocalDateTimeUtils.isTimeRangeValid(null, endTime));
|
||||
assertFalse(LocalDateTimeUtils.isTimeRangeValid(beginTime, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsTimeRangePresent() {
|
||||
// 准备参数
|
||||
LocalDateTime time = LocalDateTime.of(2026, 7, 24, 9, 0);
|
||||
|
||||
// 调用,并断言:空数组和双空边界都不是有效范围
|
||||
assertFalse(LocalDateTimeUtils.isTimeRangePresent(null));
|
||||
assertFalse(LocalDateTimeUtils.isTimeRangePresent(new LocalDateTime[0]));
|
||||
assertFalse(LocalDateTimeUtils.isTimeRangePresent(new LocalDateTime[]{null, null}));
|
||||
|
||||
// 调用,并断言:单边或双边存在即为有效范围
|
||||
assertTrue(LocalDateTimeUtils.isTimeRangePresent(new LocalDateTime[]{time}));
|
||||
assertTrue(LocalDateTimeUtils.isTimeRangePresent(new LocalDateTime[]{null, time}));
|
||||
assertTrue(LocalDateTimeUtils.isTimeRangePresent(new LocalDateTime[]{time, time.plusHours(1)}));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDayTime() {
|
||||
// 准备参数
|
||||
LocalDate date = LocalDate.of(2026, 7, 24);
|
||||
LocalDateTime time = date.atTime(15, 30);
|
||||
|
||||
// 调用,并断言
|
||||
assertEquals(LocalDateTime.of(2026, 7, 24, 0, 0),
|
||||
LocalDateTimeUtils.getDayBeginTime(date));
|
||||
assertEquals(LocalDateTime.of(2026, 7, 24, 0, 0),
|
||||
LocalDateTimeUtils.getDayBeginTime(time));
|
||||
assertNull(LocalDateTimeUtils.getDayBeginTime((LocalDateTime) null));
|
||||
assertEquals(LocalDateTime.of(date, LocalTime.MAX),
|
||||
LocalDateTimeUtils.getDayEndTime(date));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDateTimeRange() {
|
||||
// 准备参数
|
||||
LocalDate beginDate = LocalDate.of(2026, 7, 1);
|
||||
LocalDate endDate = LocalDate.of(2026, 7, 31);
|
||||
|
||||
// 调用,并断言:左右两端均为闭区间边界
|
||||
LocalDateTime[] result = LocalDateTimeUtils.getDateTimeRange(beginDate, endDate);
|
||||
assertEquals(beginDate.atStartOfDay(), result[0]);
|
||||
assertEquals(endDate.atTime(LocalTime.MAX), result[1]);
|
||||
|
||||
// 调用,并断言:支持单边范围
|
||||
result = LocalDateTimeUtils.getDateTimeRange(beginDate, null);
|
||||
assertEquals(beginDate.atStartOfDay(), result[0]);
|
||||
assertNull(result[1]);
|
||||
result = LocalDateTimeUtils.getDateTimeRange(null, endDate);
|
||||
assertNull(result[0]);
|
||||
assertEquals(endDate.atTime(LocalTime.MAX), result[1]);
|
||||
|
||||
// 调用,并断言:两个边界都为空时不生成范围
|
||||
assertNull(LocalDateTimeUtils.getDateTimeRange(null, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMonthDateTimeRange() {
|
||||
// 调用:闰年二月
|
||||
LocalDateTime[] result = LocalDateTimeUtils.getMonthDateTimeRange(2024, 2);
|
||||
|
||||
// 断言:完整包含闰年二月的首尾时刻
|
||||
assertEquals(LocalDateTime.of(2024, 2, 1, 0, 0), result[0]);
|
||||
assertEquals(LocalDate.of(2024, 2, 29).atTime(LocalTime.MAX), result[1]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMonthBeginTime() {
|
||||
// 准备参数
|
||||
YearMonth month = YearMonth.of(2026, 12);
|
||||
|
||||
// 调用,并断言
|
||||
assertEquals(LocalDateTime.of(2026, 12, 1, 0, 0),
|
||||
LocalDateTimeUtils.getMonthBeginTime(month));
|
||||
assertEquals(LocalDateTime.of(2027, 1, 1, 0, 0),
|
||||
LocalDateTimeUtils.getNextMonthBeginTime(month));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsBetween() {
|
||||
// 准备参数
|
||||
LocalDate beginDate = LocalDate.of(2026, 7, 23);
|
||||
LocalDate endDate = LocalDate.of(2026, 7, 25);
|
||||
LocalDateTime beginTime = beginDate.atTime(9, 0);
|
||||
LocalDateTime endTime = beginDate.atTime(18, 0);
|
||||
|
||||
// 调用,并断言:日期闭区间
|
||||
assertTrue(LocalDateTimeUtils.isBetween(beginDate, endDate, beginDate));
|
||||
assertTrue(LocalDateTimeUtils.isBetween(beginDate, endDate, LocalDate.of(2026, 7, 24)));
|
||||
assertTrue(LocalDateTimeUtils.isBetween(beginDate, endDate, endDate));
|
||||
assertFalse(LocalDateTimeUtils.isBetween(beginDate, endDate, LocalDate.of(2026, 7, 26)));
|
||||
assertFalse(LocalDateTimeUtils.isBetween(beginDate, endDate, null));
|
||||
assertFalse(LocalDateTimeUtils.isNotBetween(beginDate, endDate, beginDate));
|
||||
assertTrue(LocalDateTimeUtils.isNotBetween(beginDate, endDate, LocalDate.of(2026, 7, 26)));
|
||||
assertTrue(LocalDateTimeUtils.isNotBetween(beginDate, endDate, null));
|
||||
|
||||
// 调用,并断言:时间闭区间
|
||||
assertTrue(LocalDateTimeUtils.isBetween(beginTime, endTime, beginTime));
|
||||
assertTrue(LocalDateTimeUtils.isBetween(beginTime, endTime, beginDate.atTime(12, 0)));
|
||||
assertTrue(LocalDateTimeUtils.isBetween(beginTime, endTime, endTime));
|
||||
assertFalse(LocalDateTimeUtils.isBetween(beginTime, endTime, beginDate.atTime(18, 1)));
|
||||
assertFalse(LocalDateTimeUtils.isBetween(beginTime, endTime, (LocalDateTime) null));
|
||||
assertFalse(LocalDateTimeUtils.isNotBetween(beginTime, endTime, endTime));
|
||||
assertTrue(LocalDateTimeUtils.isNotBetween(beginTime, endTime, beginDate.atTime(18, 1)));
|
||||
assertTrue(LocalDateTimeUtils.isNotBetween(beginTime, endTime, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsClosedRangeOverlap() {
|
||||
// 准备参数
|
||||
LocalDateTime firstBeginTime = LocalDateTime.of(2026, 7, 24, 9, 0);
|
||||
LocalDateTime firstEndTime = LocalDateTime.of(2026, 7, 24, 12, 0);
|
||||
|
||||
// 调用,并断言:首尾相接属于闭区间重叠
|
||||
assertTrue(LocalDateTimeUtils.isClosedRangeOverlap(
|
||||
firstBeginTime, firstEndTime, firstEndTime, firstEndTime.plusHours(1)));
|
||||
assertTrue(LocalDateTimeUtils.isClosedRangeOverlap(
|
||||
firstBeginTime, firstEndTime, firstBeginTime.minusHours(1), firstBeginTime));
|
||||
assertFalse(LocalDateTimeUtils.isClosedRangeOverlap(
|
||||
firstBeginTime, firstEndTime, firstEndTime.plusNanos(1), firstEndTime.plusHours(1)));
|
||||
assertFalse(LocalDateTimeUtils.isClosedRangeOverlap(
|
||||
firstBeginTime, firstEndTime, null, firstEndTime));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubtractTimeRanges() {
|
||||
// 准备参数
|
||||
LocalDate date = LocalDate.of(2026, 7, 24);
|
||||
TimeRange sourceRange = new TimeRange(date.atTime(9, 0), date.atTime(18, 0));
|
||||
|
||||
// 调用:扣除原时段中间的一段
|
||||
List<TimeRange> result = LocalDateTimeUtils.subtractTimeRanges(
|
||||
Collections.singletonList(sourceRange),
|
||||
new TimeRange(date.atTime(12, 0), date.atTime(13, 0)));
|
||||
|
||||
// 断言:原时段被拆分为两段
|
||||
assertEquals(2, result.size());
|
||||
assertTimeRange(CollUtil.getFirst(result), date.atTime(9, 0), date.atTime(12, 0));
|
||||
assertTimeRange(result.get(1), date.atTime(13, 0), date.atTime(18, 0));
|
||||
|
||||
// 调用,并断言:扣除时段完整覆盖原时段
|
||||
result = LocalDateTimeUtils.subtractTimeRanges(
|
||||
Collections.singletonList(sourceRange),
|
||||
new TimeRange(date.atTime(8, 0), date.atTime(19, 0)));
|
||||
assertTrue(result.isEmpty());
|
||||
|
||||
// 调用:扣除时段与原时段首尾相接
|
||||
result = LocalDateTimeUtils.subtractTimeRanges(
|
||||
Collections.singletonList(sourceRange),
|
||||
new TimeRange(date.atTime(18, 0), date.atTime(19, 0)));
|
||||
// 断言:两个时段仅首尾相接,没有正时长交集,原时段保持不变
|
||||
assertEquals(1, result.size());
|
||||
assertTimeRange(CollUtil.getFirst(result), date.atTime(9, 0), date.atTime(18, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMergeTimeRanges() {
|
||||
// 准备参数
|
||||
LocalDate date = LocalDate.of(2026, 7, 24);
|
||||
List<TimeRange> timeRanges = Arrays.asList(
|
||||
new TimeRange(date.atTime(13, 0), date.atTime(14, 0)),
|
||||
new TimeRange(date.atTime(9, 0), date.atTime(11, 0)),
|
||||
new TimeRange(date.atTime(10, 0), date.atTime(13, 0)),
|
||||
new TimeRange(date.atTime(16, 0), date.atTime(17, 0)));
|
||||
|
||||
// 调用
|
||||
List<TimeRange> result = LocalDateTimeUtils.mergeTimeRanges(timeRanges);
|
||||
|
||||
// 断言:重叠或首尾相接的时段被合并,独立时段保持不变
|
||||
assertEquals(2, result.size());
|
||||
assertTimeRange(CollUtil.getFirst(result), date.atTime(9, 0), date.atTime(14, 0));
|
||||
assertTimeRange(result.get(1), date.atTime(16, 0), date.atTime(17, 0));
|
||||
// 调用,并断言:空列表
|
||||
assertTrue(LocalDateTimeUtils.mergeTimeRanges(Collections.emptyList()).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntersectTimeRanges() {
|
||||
// 准备参数
|
||||
LocalDate date = LocalDate.of(2026, 7, 24);
|
||||
List<TimeRange> firstRanges = Arrays.asList(
|
||||
new TimeRange(date.atTime(9, 0), date.atTime(12, 0)),
|
||||
new TimeRange(date.atTime(13, 0), date.atTime(18, 0)));
|
||||
List<TimeRange> secondRanges = Arrays.asList(
|
||||
new TimeRange(date.atTime(10, 0), date.atTime(14, 0)),
|
||||
new TimeRange(date.atTime(17, 0), date.atTime(19, 0)));
|
||||
|
||||
// 调用
|
||||
List<TimeRange> result = LocalDateTimeUtils.intersectTimeRanges(firstRanges, secondRanges);
|
||||
|
||||
// 断言
|
||||
assertEquals(3, result.size());
|
||||
assertTimeRange(CollUtil.getFirst(result), date.atTime(10, 0), date.atTime(12, 0));
|
||||
assertTimeRange(result.get(1), date.atTime(13, 0), date.atTime(14, 0));
|
||||
assertTimeRange(result.get(2), date.atTime(17, 0), date.atTime(18, 0));
|
||||
// 调用,并断言:两个时段仅首尾相接时不生成零时长交集
|
||||
assertNull(LocalDateTimeUtils.intersectTimeRange(
|
||||
new TimeRange(date.atTime(9, 0), date.atTime(10, 0)),
|
||||
new TimeRange(date.atTime(10, 0), date.atTime(11, 0))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateDurationMinutes() {
|
||||
// 准备参数
|
||||
LocalDate date = LocalDate.of(2026, 7, 24);
|
||||
List<TimeRange> timeRanges = Arrays.asList(
|
||||
new TimeRange(date.atTime(9, 0), date.atTime(10, 0)),
|
||||
new TimeRange(date.atTime(13, 0), date.atTime(14, 30)));
|
||||
|
||||
// 调用,并断言
|
||||
assertEquals(150, LocalDateTimeUtils.calculateDurationMinutes(timeRanges));
|
||||
// 调用,并断言:空列表
|
||||
assertEquals(0, LocalDateTimeUtils.calculateDurationMinutes(Collections.emptyList()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildDailyTimeRanges() {
|
||||
// 准备参数
|
||||
LocalDate beginDate = LocalDate.of(2026, 7, 24);
|
||||
LocalDate endDate = LocalDate.of(2026, 7, 25);
|
||||
|
||||
// 调用:构建跨天时间范围
|
||||
List<TimeRange> result = LocalDateTimeUtils.buildDailyTimeRanges(
|
||||
beginDate, endDate, LocalTime.of(22, 0), LocalTime.of(6, 0));
|
||||
|
||||
// 断言
|
||||
assertEquals(2, result.size());
|
||||
assertTimeRange(CollUtil.getFirst(result), beginDate.atTime(22, 0), beginDate.plusDays(1).atTime(6, 0));
|
||||
assertTimeRange(result.get(1), endDate.atTime(22, 0), endDate.plusDays(1).atTime(6, 0));
|
||||
|
||||
// 调用:每日开始和结束时刻相同
|
||||
result = LocalDateTimeUtils.buildDailyTimeRanges(
|
||||
beginDate, beginDate, LocalTime.of(9, 0), LocalTime.of(9, 0));
|
||||
// 断言:按完整一天处理
|
||||
assertEquals(1, result.size());
|
||||
assertTimeRange(CollUtil.getFirst(result), beginDate.atTime(9, 0), beginDate.plusDays(1).atTime(9, 0));
|
||||
|
||||
// 调用,并断言:开始日期晚于结束日期
|
||||
assertTrue(LocalDateTimeUtils.buildDailyTimeRanges(
|
||||
endDate, beginDate, LocalTime.of(9, 0), LocalTime.of(18, 0)).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindDailyTimeRange() {
|
||||
// 准备参数
|
||||
LocalDate date = LocalDate.of(2026, 7, 8);
|
||||
|
||||
// 调用,并断言普通时间范围的左右边界
|
||||
TimeRange result = LocalDateTimeUtils.findDailyTimeRange(
|
||||
date, LocalTime.of(8, 0), LocalTime.of(10, 0), date.atTime(8, 0));
|
||||
assertEquals(date.atTime(8, 0), result.getStartTime());
|
||||
assertEquals(date.atTime(10, 0), result.getEndTime());
|
||||
assertNotNull(LocalDateTimeUtils.findDailyTimeRange(
|
||||
date, LocalTime.of(8, 0), LocalTime.of(10, 0), date.atTime(10, 0)));
|
||||
|
||||
// 调用,并断言跨日时间范围
|
||||
result = LocalDateTimeUtils.findDailyTimeRange(
|
||||
date, LocalTime.of(20, 0), LocalTime.of(6, 0), date.plusDays(1).atTime(5, 0));
|
||||
assertEquals(date.atTime(20, 0), result.getStartTime());
|
||||
assertEquals(date.plusDays(1).atTime(6, 0), result.getEndTime());
|
||||
assertNull(LocalDateTimeUtils.findDailyTimeRange(
|
||||
date, LocalTime.of(8, 0), LocalTime.of(10, 0), date.atTime(12, 0)));
|
||||
}
|
||||
|
||||
private static void assertTimeRange(TimeRange timeRange,
|
||||
LocalDateTime expectedStartTime,
|
||||
LocalDateTime expectedEndTime) {
|
||||
assertEquals(expectedStartTime, timeRange.getStartTime());
|
||||
assertEquals(expectedEndTime, timeRange.getEndTime());
|
||||
}
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package cn.iocoder.yudao.framework.excel.core.convert;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.idev.excel.converters.Converter;
|
||||
import cn.idev.excel.metadata.GlobalConfiguration;
|
||||
import cn.idev.excel.metadata.data.WriteCellData;
|
||||
import cn.idev.excel.metadata.property.ExcelContentProperty;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Excel 字符串列表转换器
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class StringListConvert implements Converter<List<String>> {
|
||||
|
||||
@Override
|
||||
public WriteCellData<String> convertToExcelData(List<String> value,
|
||||
ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
|
||||
return new WriteCellData<>(CollUtil.isEmpty(value) ? "" : String.join("/", value));
|
||||
}
|
||||
|
||||
}
|
||||
+91
@@ -1,7 +1,9 @@
|
||||
package cn.iocoder.yudao.framework.excel.core.util;
|
||||
|
||||
import cn.idev.excel.FastExcelFactory;
|
||||
import cn.idev.excel.context.AnalysisContext;
|
||||
import cn.idev.excel.converters.longconverter.LongStringConverter;
|
||||
import cn.idev.excel.read.listener.ReadListener;
|
||||
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
|
||||
import cn.iocoder.yudao.framework.excel.core.handler.ColumnWidthMatchStyleStrategy;
|
||||
import cn.iocoder.yudao.framework.excel.core.handler.SelectSheetWriteHandler;
|
||||
@@ -10,7 +12,10 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Excel 工具类
|
||||
@@ -44,7 +49,33 @@ public class ExcelUtils {
|
||||
response.setContentType("application/vnd.ms-excel;charset=UTF-8");
|
||||
}
|
||||
|
||||
/**
|
||||
* 将动态表头和数据以 Excel 响应给前端
|
||||
*
|
||||
* @param response 响应
|
||||
* @param filename 文件名
|
||||
* @param sheetName Excel sheet 名
|
||||
* @param head Excel head 头
|
||||
* @param data 数据列表
|
||||
* @throws IOException 写入失败的情况
|
||||
*/
|
||||
public static void write(HttpServletResponse response, String filename, String sheetName,
|
||||
List<List<String>> head, List<List<Object>> data) throws IOException {
|
||||
// 输出 Excel
|
||||
FastExcelFactory.write(response.getOutputStream())
|
||||
.autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理
|
||||
.registerWriteHandler(new ColumnWidthMatchStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度
|
||||
.registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度
|
||||
.head(head).sheet(sheetName).doWrite(data);
|
||||
// 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了
|
||||
response.addHeader("Content-Disposition", "attachment;filename=" + HttpUtils.encodeUtf8(filename));
|
||||
response.setContentType("application/vnd.ms-excel;charset=UTF-8");
|
||||
}
|
||||
|
||||
public static <T> List<T> read(MultipartFile file, Class<T> head) throws IOException {
|
||||
if (file == null || file.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 参考 https://t.zsxq.com/zM77F 帖子,增加 try 处理,兼容 windows 场景
|
||||
try (InputStream inputStream = file.getInputStream()) {
|
||||
return FastExcelFactory.read(inputStream, head, null)
|
||||
@@ -53,4 +84,64 @@ public class ExcelUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取指定数量以内的 Excel 数据,达到上限后停止继续解析。
|
||||
*
|
||||
* @param file Excel 文件,可为空
|
||||
* @param head Excel 表头类型
|
||||
* @param maxRowCount 最大读取行数
|
||||
* @return Excel 数据
|
||||
*/
|
||||
public static <T> List<T> read(MultipartFile file, Class<T> head, int maxRowCount) throws IOException {
|
||||
if (file == null || file.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (maxRowCount <= 0) {
|
||||
throw new IllegalArgumentException("maxRowCount must be greater than 0");
|
||||
}
|
||||
List<T> rows = new ArrayList<>(Math.min(maxRowCount, 1024));
|
||||
try (InputStream inputStream = file.getInputStream()) {
|
||||
FastExcelFactory.read(inputStream, head, new ReadListener<T>() {
|
||||
|
||||
@Override
|
||||
public void invoke(T data, AnalysisContext context) {
|
||||
rows.add(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doAfterAllAnalysed(AnalysisContext context) {
|
||||
// 无需处理
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext(AnalysisContext context) {
|
||||
return rows.size() < maxRowCount;
|
||||
}
|
||||
|
||||
}).autoCloseStream(false).doReadAll();
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取动态表头 Excel 数据
|
||||
*
|
||||
* @param file Excel 文件,可为空
|
||||
* @return 按列下标存储的行数据
|
||||
* @throws IOException 读取失败
|
||||
*/
|
||||
public static List<Map<Integer, String>> read(MultipartFile file) throws IOException {
|
||||
if (file == null || file.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 参考 https://t.zsxq.com/zM77F 帖子,增加 try 处理,兼容 windows 场景
|
||||
try (InputStream inputStream = file.getInputStream()) {
|
||||
return FastExcelFactory.read(inputStream)
|
||||
.autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理
|
||||
.headRowNumber(1)
|
||||
.sheet()
|
||||
.doReadSync();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+226
@@ -0,0 +1,226 @@
|
||||
package cn.iocoder.yudao.framework.excel.core.util;
|
||||
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.apache.poi.ss.usermodel.BorderStyle;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.CellStyle;
|
||||
import org.apache.poi.ss.usermodel.ClientAnchor;
|
||||
import org.apache.poi.ss.usermodel.Comment;
|
||||
import org.apache.poi.ss.usermodel.CreationHelper;
|
||||
import org.apache.poi.ss.usermodel.DataFormatter;
|
||||
import org.apache.poi.ss.usermodel.FillPatternType;
|
||||
import org.apache.poi.ss.usermodel.Font;
|
||||
import org.apache.poi.ss.usermodel.FormulaEvaluator;
|
||||
import org.apache.poi.ss.usermodel.HorizontalAlignment;
|
||||
import org.apache.poi.ss.usermodel.IndexedColors;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.VerticalAlignment;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.ss.util.CellRangeAddress;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 原生 POI 的 Excel 工具类
|
||||
*
|
||||
* 全局 {@link ExcelUtils}(FastExcel)无法表达单元格锁定、公式、动态列等复杂模板时,各模块基于本类实现自定义 Excel 读写
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@UtilityClass
|
||||
public class PoiExcelUtils {
|
||||
|
||||
/**
|
||||
* 读取公式计算值必须 DataFormatter + FormulaEvaluator,hutool 没有更简的等价写法
|
||||
*/
|
||||
private static final DataFormatter DATA_FORMATTER = new DataFormatter();
|
||||
|
||||
// ==================== 读取 ====================
|
||||
|
||||
/**
|
||||
* 读取单元格文本,公式单元格取公式计算值
|
||||
*
|
||||
* @param row 行
|
||||
* @param column 列
|
||||
* @param evaluator 公式计算器,不需要求值时传 null
|
||||
* @return 文本,空单元格返回空字符串
|
||||
*/
|
||||
public static String getCellText(Row row, Integer column, FormulaEvaluator evaluator) {
|
||||
if (row == null || column == null || row.getCell(column) == null) {
|
||||
return "";
|
||||
}
|
||||
return evaluator == null
|
||||
? DATA_FORMATTER.formatCellValue(row.getCell(column))
|
||||
: DATA_FORMATTER.formatCellValue(row.getCell(column), evaluator);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取单元格数字,兼容千分位文本
|
||||
*
|
||||
* @param row 行
|
||||
* @param column 列
|
||||
* @param evaluator 公式计算器,不需要求值时传 null
|
||||
* @return 数字,空单元格返回 null;格式不正确时抛出 {@link NumberFormatException}
|
||||
*/
|
||||
public static BigDecimal getDecimal(Row row, Integer column, FormulaEvaluator evaluator) {
|
||||
String value = getCellText(row, column, evaluator).replace(",", "");
|
||||
return StrUtil.isBlank(value) ? null : NumberUtil.toBigDecimal(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断指定列是否全部为空
|
||||
*
|
||||
* @param row 行
|
||||
* @param evaluator 公式计算器,不需要求值时传 null
|
||||
* @param columns 列数组
|
||||
* @return 是否全部为空
|
||||
*/
|
||||
public static boolean isEmptyRow(Row row, FormulaEvaluator evaluator, Collection<Integer> columns) {
|
||||
if (row == null) {
|
||||
return true;
|
||||
}
|
||||
for (Integer column : columns) {
|
||||
if (StrUtil.isNotBlank(getCellText(row, column, evaluator))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ==================== 写入 ====================
|
||||
|
||||
/**
|
||||
* 写入文本单元格,null 按空字符串处理
|
||||
*/
|
||||
public static void writeText(Row row, int column, String value, CellStyle style) {
|
||||
Cell cell = row.createCell(column);
|
||||
cell.setCellValue(StrUtil.nullToEmpty(value));
|
||||
cell.setCellStyle(style);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入数字单元格,null 时留空
|
||||
*/
|
||||
public static void writeNumber(Row row, int column, BigDecimal value, CellStyle style) {
|
||||
Cell cell = row.createCell(column);
|
||||
if (value != null) {
|
||||
cell.setCellValue(value.doubleValue());
|
||||
}
|
||||
cell.setCellStyle(style);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入公式单元格
|
||||
*/
|
||||
public static void writeFormula(Row row, int column, String formula, CellStyle style) {
|
||||
Cell cell = row.createCell(column);
|
||||
cell.setCellFormula(formula);
|
||||
cell.setCellStyle(style);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入跨两行的合并表头
|
||||
*/
|
||||
public static void mergeHeader(Sheet sheet, Row firstRow, Row secondRow,
|
||||
int beginColumn, int endColumn, String title, CellStyle style) {
|
||||
sheet.addMergedRegion(new CellRangeAddress(firstRow.getRowNum(), secondRow.getRowNum(),
|
||||
beginColumn, endColumn));
|
||||
Cell cell = firstRow.createCell(beginColumn);
|
||||
cell.setCellValue(title);
|
||||
cell.setCellStyle(style);
|
||||
secondRow.createCell(beginColumn).setCellStyle(style);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加单元格批注
|
||||
*
|
||||
* @param cell 单元格
|
||||
* @param text 批注内容
|
||||
*/
|
||||
public static void addComment(Cell cell, String text) {
|
||||
CreationHelper helper = cell.getSheet().getWorkbook().getCreationHelper();
|
||||
ClientAnchor anchor = helper.createClientAnchor();
|
||||
anchor.setCol1(cell.getColumnIndex());
|
||||
anchor.setCol2(cell.getColumnIndex() + 3);
|
||||
anchor.setRow1(cell.getRowIndex());
|
||||
anchor.setRow2(cell.getRowIndex() + 3);
|
||||
Comment comment = cell.getSheet().createDrawingPatriarch().createCellComment(anchor);
|
||||
comment.setString(helper.createRichTextString(text));
|
||||
cell.setCellComment(comment);
|
||||
}
|
||||
|
||||
// ==================== 样式 ====================
|
||||
|
||||
/**
|
||||
* 创建细边框样式
|
||||
*/
|
||||
public static CellStyle createBorderStyle(Workbook workbook) {
|
||||
CellStyle style = workbook.createCellStyle();
|
||||
style.setBorderTop(BorderStyle.THIN);
|
||||
style.setBorderRight(BorderStyle.THIN);
|
||||
style.setBorderBottom(BorderStyle.THIN);
|
||||
style.setBorderLeft(BorderStyle.THIN);
|
||||
return style;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建数据单元格样式:细边框、垂直居中,可指定锁定和填充色
|
||||
*
|
||||
* @param workbook 工作簿
|
||||
* @param locked 是否锁定,配合 protectSheet 使用
|
||||
* @param fillColor 填充色,传 null 时不填充
|
||||
*/
|
||||
public static CellStyle createDataStyle(Workbook workbook, boolean locked, IndexedColors fillColor) {
|
||||
CellStyle style = createBorderStyle(workbook);
|
||||
style.setLocked(locked);
|
||||
if (fillColor != null) {
|
||||
style.setFillForegroundColor(fillColor.getIndex());
|
||||
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
||||
}
|
||||
style.setVerticalAlignment(VerticalAlignment.CENTER);
|
||||
return style;
|
||||
}
|
||||
|
||||
/**
|
||||
* 基于已有样式创建数字格式样式
|
||||
*/
|
||||
public static CellStyle createNumberStyle(Workbook workbook, CellStyle base, String format) {
|
||||
CellStyle style = workbook.createCellStyle();
|
||||
style.cloneStyleFrom(base);
|
||||
style.setDataFormat(workbook.createDataFormat().getFormat(format));
|
||||
return style;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建表头样式:细边框、指定填充色、加粗居中
|
||||
*/
|
||||
public static CellStyle createHeaderStyle(Workbook workbook, IndexedColors fillColor) {
|
||||
CellStyle style = createBorderStyle(workbook);
|
||||
style.setFillForegroundColor(fillColor.getIndex());
|
||||
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
||||
style.setAlignment(HorizontalAlignment.CENTER);
|
||||
style.setVerticalAlignment(VerticalAlignment.CENTER);
|
||||
Font font = workbook.createFont();
|
||||
font.setBold(true);
|
||||
style.setFont(font);
|
||||
return style;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建标题样式:大号加粗居中
|
||||
*/
|
||||
public static CellStyle createTitleStyle(Workbook workbook) {
|
||||
CellStyle style = workbook.createCellStyle();
|
||||
style.setAlignment(HorizontalAlignment.CENTER);
|
||||
Font font = workbook.createFont();
|
||||
font.setBold(true);
|
||||
font.setFontHeightInPoints((short) 20);
|
||||
style.setFont(font);
|
||||
return style;
|
||||
}
|
||||
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package cn.iocoder.yudao.framework.excel.core.util;
|
||||
|
||||
import cn.idev.excel.FastExcelFactory;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* {@link ExcelUtils} 的单元测试
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class ExcelUtilsTest {
|
||||
|
||||
@Test
|
||||
public void testRead_maxRowCount() throws Exception {
|
||||
// mock 数据
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
FastExcelFactory.write(outputStream, TestRow.class).sheet().doWrite(Arrays.asList(
|
||||
new TestRow("员工一"), new TestRow("员工二"), new TestRow("员工三")));
|
||||
MockMultipartFile file = new MockMultipartFile(
|
||||
"file", "employees.xlsx", null, outputStream.toByteArray());
|
||||
|
||||
// 调用
|
||||
List<TestRow> rows = ExcelUtils.read(file, TestRow.class, 2);
|
||||
|
||||
// 断言
|
||||
assertEquals(2, rows.size());
|
||||
assertEquals("员工一", rows.get(0).getName());
|
||||
assertEquals("员工二", rows.get(1).getName());
|
||||
}
|
||||
|
||||
public static class TestRow {
|
||||
|
||||
@ExcelProperty("姓名")
|
||||
private String name;
|
||||
|
||||
public TestRow() {
|
||||
}
|
||||
|
||||
public TestRow(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+12
@@ -144,6 +144,18 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> {
|
||||
return selectOneForUpdate(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2).eq(field3, value3));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得满足条件的第 1 条记录,并使用 FOR UPDATE 锁定。
|
||||
*
|
||||
* 注意:需要在事务中调用,否则锁会立即释放。
|
||||
*
|
||||
* @param queryWrapper 查询条件
|
||||
* @return 实体
|
||||
*/
|
||||
default T selectFirstOneForUpdate(LambdaQueryWrapper<T> queryWrapper) {
|
||||
return selectOne(queryWrapper.last("LIMIT 1 FOR UPDATE"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取满足条件的第 1 条记录
|
||||
*
|
||||
|
||||
+19
@@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
@@ -107,6 +108,24 @@ public class LambdaQueryWrapperX<T> extends LambdaQueryWrapper<T> {
|
||||
return betweenIfPresent(column, val1, val2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接两个闭区间的重叠条件
|
||||
*
|
||||
* <p>记录区间与查询区间重叠的条件为:记录开始值小于等于查询结束值,
|
||||
* 且记录结束值大于等于查询开始值。不能使用单字段 {@code BETWEEN} 代替,
|
||||
* 否则会漏掉完整覆盖查询区间的记录。</p>
|
||||
*
|
||||
* @param beginColumn 记录开始值字段
|
||||
* @param endColumn 记录结束值字段
|
||||
* @param times 查询闭区间
|
||||
* @return 查询条件
|
||||
*/
|
||||
public LambdaQueryWrapperX<T> betweenIfPresent(
|
||||
SFunction<T, ?> beginColumn, SFunction<T, ?> endColumn, LocalDateTime[] times) {
|
||||
return leIfPresent(beginColumn, ArrayUtils.get(times, 1))
|
||||
.geIfPresent(endColumn, ArrayUtils.get(times, 0));
|
||||
}
|
||||
|
||||
// ========== 重写父类方法,方便链式调用 ==========
|
||||
|
||||
@Override
|
||||
|
||||
+3
-10
@@ -102,16 +102,9 @@ public class QueryWrapperX<T> extends QueryWrapper<T> {
|
||||
}
|
||||
|
||||
public QueryWrapperX<T> betweenIfPresent(String column, Object[] values) {
|
||||
if (values != null && values.length != 0 && values[0] != null && values[1] != null) {
|
||||
return (QueryWrapperX<T>) super.between(column, values[0], values[1]);
|
||||
}
|
||||
if (values != null && values.length != 0 && values[0] != null) {
|
||||
return (QueryWrapperX<T>) ge(column, values[0]);
|
||||
}
|
||||
if (values != null && values.length != 0 && values[1] != null) {
|
||||
return (QueryWrapperX<T>) le(column, values[1]);
|
||||
}
|
||||
return this;
|
||||
Object val1 = values != null && values.length > 0 ? values[0] : null;
|
||||
Object val2 = values != null && values.length > 1 ? values[1] : null;
|
||||
return betweenIfPresent(column, val1, val2);
|
||||
}
|
||||
|
||||
// ========== 重写父类方法,方便链式调用 ==========
|
||||
|
||||
+24
@@ -24,6 +24,8 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/**
|
||||
* MyBatis 工具类
|
||||
@@ -179,6 +181,28 @@ public class MyBatisUtils {
|
||||
return findInSet(columnName, paramIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 跨数据库的 find_in_set 实现,适用于同一字段匹配多个参数的场景
|
||||
*
|
||||
* 每个参数生成一个 find_in_set 条件,并使用 OR 连接。
|
||||
*
|
||||
* @param columnName 字段名称
|
||||
* @param values 参数集合
|
||||
* @return sql
|
||||
*/
|
||||
public static String findInSet(String columnName, Collection<?> values) {
|
||||
return findInSet(JdbcUtils.getDbType(), columnName, values);
|
||||
}
|
||||
|
||||
static String findInSet(DbType dbType, String columnName, Collection<?> values) {
|
||||
if (CollUtil.isEmpty(values)) {
|
||||
throw new IllegalArgumentException("Values cannot be empty");
|
||||
}
|
||||
return IntStream.range(0, values.size())
|
||||
.mapToObj(index -> findInSet(dbType, columnName, index))
|
||||
.collect(Collectors.joining(" OR "));
|
||||
}
|
||||
|
||||
private static String findInSet(String columnName, int paramIndex) {
|
||||
DbType dbType = JdbcUtils.getDbType();
|
||||
return findInSet(dbType, columnName, paramIndex);
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
package cn.iocoder.yudao.framework.mybatis.core.query;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||
import lombok.Data;
|
||||
import org.apache.ibatis.builder.MapperBuilderAssistant;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* {@link LambdaQueryWrapperX} 的单元测试
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class LambdaQueryWrapperXTest {
|
||||
|
||||
@BeforeAll
|
||||
public static void initTableInfo() {
|
||||
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""), TestDO.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBetweenIfPresent_overlap() {
|
||||
// 调用,并断言:空范围不追加条件
|
||||
assertEquals("", new LambdaQueryWrapperX<TestDO>()
|
||||
.betweenIfPresent(TestDO::getStartTime, TestDO::getEndTime, null)
|
||||
.getSqlSegment());
|
||||
|
||||
// 调用,并断言:双边范围使用闭区间重叠条件
|
||||
LocalDateTime beginTime = LocalDateTime.of(2026, 8, 1, 0, 0);
|
||||
LocalDateTime endTime = LocalDateTime.of(2026, 8, 31, 23, 59);
|
||||
String sqlSegment = new LambdaQueryWrapperX<TestDO>()
|
||||
.betweenIfPresent(TestDO::getStartTime, TestDO::getEndTime,
|
||||
new LocalDateTime[]{beginTime, endTime})
|
||||
.getSqlSegment();
|
||||
assertTrue(sqlSegment.contains("start_time <="));
|
||||
assertTrue(sqlSegment.contains("end_time >="));
|
||||
|
||||
// 调用,并断言:单边范围只追加对应的重叠边界
|
||||
sqlSegment = new LambdaQueryWrapperX<TestDO>()
|
||||
.betweenIfPresent(TestDO::getStartTime, TestDO::getEndTime,
|
||||
new LocalDateTime[]{beginTime})
|
||||
.getSqlSegment();
|
||||
assertTrue(sqlSegment.contains("end_time >="));
|
||||
sqlSegment = new LambdaQueryWrapperX<TestDO>()
|
||||
.betweenIfPresent(TestDO::getStartTime, TestDO::getEndTime,
|
||||
new LocalDateTime[]{null, endTime})
|
||||
.getSqlSegment();
|
||||
assertTrue(sqlSegment.contains("start_time <="));
|
||||
}
|
||||
|
||||
@Data
|
||||
@TableName("test_data")
|
||||
private static class TestDO {
|
||||
|
||||
private LocalDateTime startTime;
|
||||
private LocalDateTime endTime;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package cn.iocoder.yudao.framework.mybatis.core.query;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* {@link QueryWrapperX} 的单元测试
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class QueryWrapperXTest {
|
||||
|
||||
@Test
|
||||
public void testBetweenIfPresent_array() {
|
||||
// 调用,并断言:空范围不追加条件
|
||||
assertEquals("", new QueryWrapperX<>().betweenIfPresent("create_time", (Object[]) null).getSqlSegment());
|
||||
assertEquals("", new QueryWrapperX<>().betweenIfPresent("create_time", new Object[0]).getSqlSegment());
|
||||
|
||||
// 调用,并断言:单边范围安全降级为大于等于或小于等于
|
||||
assertTrue(new QueryWrapperX<>().betweenIfPresent("create_time", new Object[]{1})
|
||||
.getSqlSegment().contains("create_time >="));
|
||||
assertTrue(new QueryWrapperX<>().betweenIfPresent("create_time", new Object[]{null, 2})
|
||||
.getSqlSegment().contains("create_time <="));
|
||||
|
||||
// 调用,并断言:双边范围使用左右闭合的 BETWEEN
|
||||
assertTrue(new QueryWrapperX<>().betweenIfPresent("create_time", new Object[]{1, 2})
|
||||
.getSqlSegment().contains("create_time BETWEEN"));
|
||||
}
|
||||
|
||||
}
|
||||
+9
@@ -10,6 +10,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@@ -128,6 +129,14 @@ public class MyBatisUtilsTest {
|
||||
() -> MyBatisUtils.findInSet(DbType.MYSQL, "websites", -1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindInSet_multipleValues() {
|
||||
assertEquals("FIND_IN_SET({0}, tag_ids) <> 0 OR FIND_IN_SET({1}, tag_ids) <> 0",
|
||||
MyBatisUtils.findInSet(DbType.MYSQL, "tag_ids", Arrays.asList(1L, 2L)));
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> MyBatisUtils.findInSet(DbType.MYSQL, "tag_ids", Collections.emptyList()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindInSet_applyBindsValue() {
|
||||
// 准备参数
|
||||
|
||||
+8
-1
@@ -19,6 +19,13 @@ public interface BpmProcessInstanceApi {
|
||||
*/
|
||||
String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO reqDTO);
|
||||
|
||||
|
||||
/**
|
||||
* 发起人取消运行中的流程实例(提供给内部)
|
||||
*
|
||||
* @param userId 发起人用户编号
|
||||
* @param processInstanceId 流程实例编号
|
||||
* @param reason 取消原因
|
||||
*/
|
||||
void cancelProcessInstanceByStartUser(Long userId, String processInstanceId, String reason);
|
||||
|
||||
}
|
||||
|
||||
+7
@@ -1,6 +1,7 @@
|
||||
package cn.iocoder.yudao.module.bpm.api.task;
|
||||
|
||||
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCancelReqVO;
|
||||
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
@@ -25,4 +26,10 @@ public class BpmProcessInstanceApiImpl implements BpmProcessInstanceApi {
|
||||
return processInstanceService.createProcessInstance(userId, reqDTO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelProcessInstanceByStartUser(Long userId, String processInstanceId, String reason) {
|
||||
processInstanceService.cancelProcessInstanceByStartUser(userId,
|
||||
new BpmProcessInstanceCancelReqVO().setId(processInstanceId).setReason(reason));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>yudao-module-hrm</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
hrm 包下,人力资源管理(Human Resource Management)
|
||||
例如说:员工、招聘、考勤、薪资、社保、绩效等等
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-system</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-bpm</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- DB 相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-mybatis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 工具类相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-excel</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 定时任务相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-job</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Test 测试相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.TimeRange;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.clock.HrmAttendanceClockPageReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.clock.HrmAttendanceClockRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.clock.HrmAttendanceClockSaveReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.clock.HrmAttendanceClockShiftReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.clock.HrmAttendanceClockShiftRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.record.HrmAttendanceClockDO;
|
||||
import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.config.HrmAttendanceGroupDO;
|
||||
import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO;
|
||||
import cn.iocoder.yudao.module.hrm.service.attendance.record.HrmAttendanceClockService;
|
||||
import cn.iocoder.yudao.module.hrm.service.attendance.config.HrmAttendanceGroupService;
|
||||
import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||
import cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
|
||||
@Tag(name = "管理后台 - HRM 打卡记录")
|
||||
@RestController
|
||||
@RequestMapping("/hrm/attendance/clock")
|
||||
@Validated
|
||||
public class HrmAttendanceClockController {
|
||||
|
||||
@Resource
|
||||
private HrmAttendanceClockService attendanceClockService;
|
||||
@Resource
|
||||
private HrmAttendanceGroupService attendanceGroupService;
|
||||
@Resource
|
||||
private HrmEmployeeService employeeService;
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建打卡记录")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:clock:create')")
|
||||
public CommonResult<Long> createAttendanceClock(@Valid @RequestBody HrmAttendanceClockSaveReqVO createReqVO) {
|
||||
return success(attendanceClockService.createAttendanceClock(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "修改打卡记录")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:clock:update')")
|
||||
public CommonResult<Boolean> updateAttendanceClock(@Valid @RequestBody HrmAttendanceClockSaveReqVO updateReqVO) {
|
||||
attendanceClockService.updateAttendanceClock(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除打卡记录")
|
||||
@Parameter(name = "id", description = "打卡记录编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:clock:delete')")
|
||||
public CommonResult<Boolean> deleteAttendanceClock(@RequestParam("id") Long id) {
|
||||
attendanceClockService.deleteAttendanceClock(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@Operation(summary = "批量删除打卡记录")
|
||||
@Parameter(name = "ids", description = "打卡记录编号列表", required = true, example = "1024,1025")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:clock:delete')")
|
||||
public CommonResult<Boolean> deleteAttendanceClockList(
|
||||
@RequestParam("ids") @NotEmpty(message = "打卡记录不能为空") List<Long> ids) {
|
||||
attendanceClockService.deleteAttendanceClockList(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得打卡记录")
|
||||
@Parameter(name = "id", description = "打卡记录编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:clock:query')")
|
||||
public CommonResult<HrmAttendanceClockRespVO> getAttendanceClock(@RequestParam("id") Long id) {
|
||||
HrmAttendanceClockDO attendanceClock = attendanceClockService.getAttendanceClock(id);
|
||||
return success(attendanceClock == null ? null
|
||||
: CollUtil.getFirst(buildAttendanceClockRespVOList(Collections.singletonList(attendanceClock))));
|
||||
}
|
||||
|
||||
@GetMapping("/get-shift")
|
||||
@Operation(summary = "获得员工实际班次和允许打卡时间")
|
||||
@PreAuthorize("@ss.hasAnyPermissions('hrm:attendance:clock:create', 'hrm:attendance:clock:update')")
|
||||
public CommonResult<HrmAttendanceClockShiftRespVO> getAttendanceClockShift(
|
||||
@Valid HrmAttendanceClockShiftReqVO reqVO) {
|
||||
HrmAttendanceGroupDO.Shift shift = attendanceGroupService.getEmployeeAttendanceShift(
|
||||
reqVO.getEmployeeId(), reqVO.getAttendanceTime());
|
||||
return success(shift == null ? null : buildAttendanceClockShiftRespVO(reqVO, shift));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得打卡记录分页")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:clock:query')")
|
||||
public CommonResult<PageResult<HrmAttendanceClockRespVO>> getAttendanceClockPage(
|
||||
@Validated HrmAttendanceClockPageReqVO pageReqVO) {
|
||||
PageResult<HrmAttendanceClockDO> pageResult = attendanceClockService.getAttendanceClockPage(pageReqVO);
|
||||
return success(new PageResult<>(buildAttendanceClockRespVOList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出打卡记录")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:clock:export')")
|
||||
@ApiAccessLog(operateType = OperateTypeEnum.EXPORT)
|
||||
public void exportAttendanceClock(@Validated HrmAttendanceClockPageReqVO exportReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
List<HrmAttendanceClockRespVO> list = buildAttendanceClockRespVOList(
|
||||
attendanceClockService.getAttendanceClockList(exportReqVO));
|
||||
ExcelUtils.write(response, "打卡记录.xls", "数据", HrmAttendanceClockRespVO.class, list);
|
||||
}
|
||||
|
||||
// ==================== 拼接 VO ====================
|
||||
|
||||
private HrmAttendanceClockShiftRespVO buildAttendanceClockShiftRespVO(
|
||||
HrmAttendanceClockShiftReqVO reqVO, HrmAttendanceGroupDO.Shift shift) {
|
||||
if (shift.getStartTime() == null || shift.getEndTime() == null
|
||||
|| shift.getClockInStartTime() == null || shift.getClockInEndTime() == null
|
||||
|| shift.getClockOutStartTime() == null || shift.getClockOutEndTime() == null) {
|
||||
return null;
|
||||
}
|
||||
LocalDate attendanceDate = reqVO.getAttendanceTime().toLocalDate();
|
||||
TimeRange shiftTimeRange = CollUtil.getFirst(LocalDateTimeUtils.buildDailyTimeRanges(
|
||||
attendanceDate, attendanceDate, shift.getStartTime(), shift.getEndTime()));
|
||||
TimeRange clockInTimeRange = LocalDateTimeUtils.findDailyTimeRange(
|
||||
attendanceDate, shift.getClockInStartTime(),
|
||||
shift.getClockInEndTime(), shiftTimeRange.getStartTime());
|
||||
TimeRange clockOutTimeRange = LocalDateTimeUtils.findDailyTimeRange(
|
||||
attendanceDate, shift.getClockOutStartTime(),
|
||||
shift.getClockOutEndTime(), shiftTimeRange.getEndTime());
|
||||
if (clockInTimeRange == null || clockOutTimeRange == null) {
|
||||
return null;
|
||||
}
|
||||
return new HrmAttendanceClockShiftRespVO()
|
||||
.setStartTime(shiftTimeRange.getStartTime()).setEndTime(shiftTimeRange.getEndTime())
|
||||
.setClockInStartTime(clockInTimeRange.getStartTime()).setClockInEndTime(clockInTimeRange.getEndTime())
|
||||
.setClockOutStartTime(clockOutTimeRange.getStartTime()).setClockOutEndTime(clockOutTimeRange.getEndTime());
|
||||
}
|
||||
|
||||
private List<HrmAttendanceClockRespVO> buildAttendanceClockRespVOList(List<HrmAttendanceClockDO> clocks) {
|
||||
if (CollUtil.isEmpty(clocks)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 1. 获得员工和部门信息
|
||||
Map<Long, HrmEmployeeDO> employeeMap = employeeService.getEmployeeMap(
|
||||
convertSet(clocks, HrmAttendanceClockDO::getEmployeeId));
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(employeeMap.values(), HrmEmployeeDO::getDeptId));
|
||||
|
||||
// 2. 拼接响应
|
||||
return BeanUtils.toBean(clocks, HrmAttendanceClockRespVO.class, respVO -> {
|
||||
MapUtils.findAndThen(employeeMap, respVO.getEmployeeId(), employee -> {
|
||||
respVO.setEmployeeName(employee.getName()).setJobNumber(employee.getJobNumber())
|
||||
.setDeptId(employee.getDeptId()).setPostName(employee.getPostName());
|
||||
});
|
||||
MapUtils.findAndThen(deptMap, respVO.getDeptId(), dept -> respVO.setDeptName(dept.getName()));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.group.HrmAttendanceGroupPageReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.group.HrmAttendanceGroupRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.group.HrmAttendanceGroupSaveReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.config.HrmAttendanceGroupDO;
|
||||
import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO;
|
||||
import cn.iocoder.yudao.module.hrm.service.attendance.config.HrmAttendanceGroupService;
|
||||
import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
|
||||
|
||||
@Tag(name = "管理后台 - HRM 考勤组")
|
||||
@RestController
|
||||
@RequestMapping("/hrm/attendance/group")
|
||||
@Validated
|
||||
public class HrmAttendanceGroupController {
|
||||
|
||||
@Resource
|
||||
private HrmAttendanceGroupService attendanceGroupService;
|
||||
@Resource
|
||||
private HrmEmployeeService employeeService;
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建考勤组")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:group:create')")
|
||||
public CommonResult<Long> createAttendanceGroup(@Valid @RequestBody HrmAttendanceGroupSaveReqVO createReqVO) {
|
||||
return success(attendanceGroupService.createAttendanceGroup(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "修改考勤组")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:group:update')")
|
||||
public CommonResult<Boolean> updateAttendanceGroup(@Valid @RequestBody HrmAttendanceGroupSaveReqVO updateReqVO) {
|
||||
attendanceGroupService.updateAttendanceGroup(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除考勤组")
|
||||
@Parameter(name = "id", description = "考勤组编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:group:delete')")
|
||||
public CommonResult<Boolean> deleteAttendanceGroup(@RequestParam("id") Long id) {
|
||||
attendanceGroupService.deleteAttendanceGroup(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得考勤组")
|
||||
@Parameter(name = "id", description = "考勤组编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:group:query')")
|
||||
public CommonResult<HrmAttendanceGroupRespVO> getAttendanceGroup(@RequestParam("id") Long id) {
|
||||
return success(buildAttendanceGroupRespVO(attendanceGroupService.getAttendanceGroup(id)));
|
||||
}
|
||||
|
||||
@GetMapping("/my")
|
||||
@Operation(summary = "获得员工所在考勤组")
|
||||
@Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:group:query')")
|
||||
public CommonResult<HrmAttendanceGroupRespVO> getMyAttendanceGroup(@RequestParam("employeeId") Long employeeId) {
|
||||
return success(buildAttendanceGroupRespVO(attendanceGroupService.getMyAttendanceGroup(employeeId)));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得考勤组分页")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:group:query')")
|
||||
public CommonResult<PageResult<HrmAttendanceGroupRespVO>> getAttendanceGroupPage(
|
||||
@Validated HrmAttendanceGroupPageReqVO pageReqVO) {
|
||||
PageResult<HrmAttendanceGroupDO> pageResult = attendanceGroupService.getAttendanceGroupPage(pageReqVO);
|
||||
return success(new PageResult<>(
|
||||
buildAttendanceGroupRespVOList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/simple-list")
|
||||
@Operation(summary = "获得考勤组精简列表")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:group:query')")
|
||||
public CommonResult<List<HrmAttendanceGroupRespVO>> getAttendanceGroupSimpleList() {
|
||||
return success(convertList(attendanceGroupService.getAttendanceGroupList(),
|
||||
group -> new HrmAttendanceGroupRespVO().setId(group.getId()).setName(group.getName())));
|
||||
}
|
||||
|
||||
// ==================== 拼接 VO ====================
|
||||
|
||||
private HrmAttendanceGroupRespVO buildAttendanceGroupRespVO(HrmAttendanceGroupDO group) {
|
||||
if (group == null) {
|
||||
return null;
|
||||
}
|
||||
return buildAttendanceGroupRespVOList(Collections.singletonList(group)).get(0);
|
||||
}
|
||||
|
||||
private List<HrmAttendanceGroupRespVO> buildAttendanceGroupRespVOList(List<HrmAttendanceGroupDO> list) {
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
|
||||
convertSetByFlatMap(list, HrmAttendanceGroupDO::getDeptIds, Collection::stream));
|
||||
Map<Long, HrmEmployeeDO> employeeMap = employeeService.getEmployeeMap(
|
||||
convertSetByFlatMap(list, HrmAttendanceGroupDO::getEmployeeIds, Collection::stream));
|
||||
return convertList(list, group -> BeanUtils.toBean(group, HrmAttendanceGroupRespVO.class)
|
||||
.setDeptNames(convertList(group.getDeptIds(),
|
||||
deptId -> deptMap.containsKey(deptId) ? deptMap.get(deptId).getName() : null))
|
||||
.setEmployeeNames(convertList(group.getEmployeeIds(),
|
||||
employeeId -> employeeMap.containsKey(employeeId)
|
||||
? employeeMap.get(employeeId).getName() : null)));
|
||||
}
|
||||
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.holiday.HrmAttendanceHolidayPageReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.holiday.HrmAttendanceHolidayRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.holiday.HrmAttendanceHolidaySaveReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.config.HrmAttendanceHolidayDO;
|
||||
import cn.iocoder.yudao.module.hrm.service.attendance.config.HrmAttendanceHolidayService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Tag(name = "管理后台 - HRM 考勤节假日")
|
||||
@RestController
|
||||
@RequestMapping("/hrm/attendance/holiday")
|
||||
@Validated
|
||||
public class HrmAttendanceHolidayController {
|
||||
|
||||
@Resource
|
||||
private HrmAttendanceHolidayService attendanceHolidayService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建考勤节假日")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:holiday:create')")
|
||||
public CommonResult<Long> createAttendanceHoliday(@Valid @RequestBody HrmAttendanceHolidaySaveReqVO createReqVO) {
|
||||
return success(attendanceHolidayService.createAttendanceHoliday(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "修改考勤节假日")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:holiday:update')")
|
||||
public CommonResult<Boolean> updateAttendanceHoliday(@Valid @RequestBody HrmAttendanceHolidaySaveReqVO updateReqVO) {
|
||||
attendanceHolidayService.updateAttendanceHoliday(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除考勤节假日")
|
||||
@Parameter(name = "id", description = "考勤节假日编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:holiday:delete')")
|
||||
public CommonResult<Boolean> deleteAttendanceHoliday(@RequestParam("id") Long id) {
|
||||
attendanceHolidayService.deleteAttendanceHoliday(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得考勤节假日")
|
||||
@Parameter(name = "id", description = "考勤节假日编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:holiday:query')")
|
||||
public CommonResult<HrmAttendanceHolidayRespVO> getAttendanceHoliday(@RequestParam("id") Long id) {
|
||||
HrmAttendanceHolidayDO holiday = attendanceHolidayService.getAttendanceHoliday(id);
|
||||
return success(BeanUtils.toBean(holiday, HrmAttendanceHolidayRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/check")
|
||||
@Operation(summary = "检查日期是否为考勤节假日")
|
||||
@Parameter(name = "day", description = "日期", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:holiday:query')")
|
||||
public CommonResult<HrmAttendanceHolidayRespVO> checkAttendanceHoliday(
|
||||
@RequestParam("day")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) LocalDateTime day) {
|
||||
HrmAttendanceHolidayDO holiday = attendanceHolidayService.checkAttendanceHoliday(day);
|
||||
return success(BeanUtils.toBean(holiday, HrmAttendanceHolidayRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得考勤节假日分页")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:holiday:query')")
|
||||
public CommonResult<PageResult<HrmAttendanceHolidayRespVO>> getAttendanceHolidayPage(
|
||||
@Validated HrmAttendanceHolidayPageReqVO pageReqVO) {
|
||||
PageResult<HrmAttendanceHolidayDO> pageResult = attendanceHolidayService.getAttendanceHolidayPage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, HrmAttendanceHolidayRespVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance;
|
||||
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.leave.HrmAttendanceLeavePageReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.leave.HrmAttendanceLeaveRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.record.HrmAttendanceLeaveDO;
|
||||
import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO;
|
||||
import cn.iocoder.yudao.module.hrm.service.attendance.record.HrmAttendanceLeaveService;
|
||||
import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||
import cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
|
||||
@Tag(name = "管理后台 - HRM 请假")
|
||||
@RestController
|
||||
@RequestMapping("/hrm/attendance/leave")
|
||||
@Validated
|
||||
public class HrmAttendanceLeaveController {
|
||||
|
||||
@Resource
|
||||
private HrmAttendanceLeaveService attendanceLeaveService;
|
||||
@Resource
|
||||
private HrmEmployeeService employeeService;
|
||||
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得请假")
|
||||
@Parameter(name = "id", description = "请假记录编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasAnyPermissions('hrm:attendance:leave:query', "
|
||||
+ "'bpm:process-instance:query', 'bpm:task:query')")
|
||||
public CommonResult<HrmAttendanceLeaveRespVO> getAttendanceLeave(@RequestParam("id") Long id) {
|
||||
HrmAttendanceLeaveDO list = attendanceLeaveService.getAttendanceLeave(id);
|
||||
return success(buildAttendanceLeaveRespVO(list));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得请假分页")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:leave:query')")
|
||||
public CommonResult<PageResult<HrmAttendanceLeaveRespVO>> getAttendanceLeavePage(
|
||||
@Validated HrmAttendanceLeavePageReqVO pageReqVO) {
|
||||
PageResult<HrmAttendanceLeaveDO> pageResult = attendanceLeaveService.getAttendanceLeavePage(pageReqVO);
|
||||
return success(new PageResult<>(buildAttendanceLeaveRespVOList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出请假")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:leave:export')")
|
||||
@ApiAccessLog(operateType = OperateTypeEnum.EXPORT)
|
||||
public void exportAttendanceLeave(@Validated HrmAttendanceLeavePageReqVO exportReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
List<HrmAttendanceLeaveRespVO> list = buildAttendanceLeaveRespVOList(
|
||||
attendanceLeaveService.getAttendanceLeaveList(exportReqVO));
|
||||
ExcelUtils.write(response, "请假记录.xls", "数据", HrmAttendanceLeaveRespVO.class, list);
|
||||
}
|
||||
|
||||
// ==================== 拼接 VO ====================
|
||||
|
||||
private HrmAttendanceLeaveRespVO buildAttendanceLeaveRespVO(HrmAttendanceLeaveDO attendanceLeave) {
|
||||
if (attendanceLeave == null) {
|
||||
return null;
|
||||
}
|
||||
return buildAttendanceLeaveRespVOList(Collections.singletonList(attendanceLeave)).get(0);
|
||||
}
|
||||
|
||||
private List<HrmAttendanceLeaveRespVO> buildAttendanceLeaveRespVOList(List<HrmAttendanceLeaveDO> list) {
|
||||
Map<Long, HrmEmployeeDO> employeeMap = employeeService.getEmployeeMap(
|
||||
convertSet(list, HrmAttendanceLeaveDO::getEmployeeId));
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(employeeMap.values(), HrmEmployeeDO::getDeptId));
|
||||
return convertList(list, attendanceLeave -> {
|
||||
HrmAttendanceLeaveRespVO respVO = BeanUtils.toBean(attendanceLeave, HrmAttendanceLeaveRespVO.class);
|
||||
HrmEmployeeDO employee = employeeMap.get(attendanceLeave.getEmployeeId());
|
||||
if (employee == null) {
|
||||
return respVO;
|
||||
}
|
||||
respVO.setEmployeeName(employee.getName()).setJobNumber(employee.getJobNumber())
|
||||
.setDeptId(employee.getDeptId()).setPostName(employee.getPostName());
|
||||
MapUtils.findAndThen(deptMap, employee.getDeptId(), dept -> respVO.setDeptName(dept.getName()));
|
||||
return respVO;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.clock.HrmAttendanceClockRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics.HrmAttendanceDailyDetailReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics.HrmAttendanceDailyDetailRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics.HrmAttendanceDailyOverviewRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics.HrmAttendanceMonthDailyOverviewRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics.HrmAttendanceMonthDetailRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics.HrmAttendanceMonthRecordPageReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics.HrmAttendanceMonthRecordRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.enums.DictTypeConstants;
|
||||
import cn.iocoder.yudao.module.hrm.service.attendance.statistics.HrmAttendanceStatisticsService;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||
import cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Parameters;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.YearMonth;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
|
||||
@Tag(name = "管理后台 - HRM 考勤统计")
|
||||
@RestController
|
||||
@RequestMapping("/hrm/attendance/statistics")
|
||||
@Validated
|
||||
public class HrmAttendanceStatisticsController {
|
||||
|
||||
@Resource
|
||||
private HrmAttendanceStatisticsService attendanceStatisticsService;
|
||||
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
|
||||
@GetMapping("/daily-detail")
|
||||
@Operation(summary = "查询员工每日打卡明细")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:clock:query')")
|
||||
public CommonResult<HrmAttendanceDailyDetailRespVO> getAttendanceDailyDetail(
|
||||
@Valid HrmAttendanceDailyDetailReqVO reqVO) {
|
||||
return success(attendanceStatisticsService.getAttendanceDailyDetail(reqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/month-record-page")
|
||||
@Operation(summary = "查询员工月度考勤汇总")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:statistics:query')")
|
||||
public CommonResult<PageResult<HrmAttendanceMonthRecordRespVO>> getAttendanceMonthRecordPage(
|
||||
@Valid HrmAttendanceMonthRecordPageReqVO reqVO) {
|
||||
PageResult<HrmAttendanceMonthRecordRespVO> pageResult =
|
||||
attendanceStatisticsService.getAttendanceMonthRecordPage(reqVO);
|
||||
fillAttendanceMonthRecordDeptName(pageResult.getList());
|
||||
return success(pageResult);
|
||||
}
|
||||
|
||||
@GetMapping("/month-daily-page")
|
||||
@Operation(summary = "查询员工月度打卡概况")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:clock:query')")
|
||||
public CommonResult<PageResult<HrmAttendanceMonthDailyOverviewRespVO>> getAttendanceMonthDailyOverviewPage(
|
||||
@Valid HrmAttendanceMonthRecordPageReqVO reqVO) {
|
||||
PageResult<HrmAttendanceMonthDailyOverviewRespVO> pageResult =
|
||||
attendanceStatisticsService.getAttendanceMonthDailyOverviewPage(reqVO);
|
||||
fillAttendanceMonthDailyOverview(pageResult.getList());
|
||||
return success(pageResult);
|
||||
}
|
||||
|
||||
@GetMapping("/month-record-export-excel")
|
||||
@Operation(summary = "导出员工月度考勤汇总")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:statistics:export')")
|
||||
@ApiAccessLog(operateType = OperateTypeEnum.EXPORT)
|
||||
public void exportAttendanceMonthRecord(@Validated HrmAttendanceMonthRecordPageReqVO exportReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
List<HrmAttendanceMonthRecordRespVO> list =
|
||||
attendanceStatisticsService.getAttendanceMonthRecordList(exportReqVO);
|
||||
fillAttendanceMonthRecordDeptName(list);
|
||||
ExcelUtils.write(response, "员工月度考勤汇总.xls", "数据", HrmAttendanceMonthRecordRespVO.class, list);
|
||||
}
|
||||
|
||||
@GetMapping("/month-daily-export-excel")
|
||||
@Operation(summary = "导出员工月度打卡概况")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:clock:export')")
|
||||
@ApiAccessLog(operateType = OperateTypeEnum.EXPORT)
|
||||
public void exportAttendanceMonthDailyOverview(@Validated HrmAttendanceMonthRecordPageReqVO exportReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
List<HrmAttendanceMonthDailyOverviewRespVO> list =
|
||||
attendanceStatisticsService.getAttendanceMonthDailyOverviewList(exportReqVO);
|
||||
fillAttendanceMonthDailyOverview(list);
|
||||
writeAttendanceMonthDailyOverview(response,
|
||||
YearMonth.of(exportReqVO.getYear(), exportReqVO.getMonth()), list);
|
||||
}
|
||||
|
||||
@GetMapping("/month-detail")
|
||||
@Operation(summary = "查询员工月度考勤详情")
|
||||
@Parameters({
|
||||
@Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024"),
|
||||
@Parameter(name = "year", description = "年份", required = true, example = "2026"),
|
||||
@Parameter(name = "month", description = "月份", required = true, example = "8")
|
||||
})
|
||||
@PreAuthorize("@ss.hasPermission('hrm:attendance:statistics:query')")
|
||||
public CommonResult<HrmAttendanceMonthDetailRespVO> getAttendanceMonthDetail(
|
||||
@RequestParam("employeeId") Long employeeId,
|
||||
@RequestParam("year") Integer year,
|
||||
@RequestParam("month") Integer month) {
|
||||
HrmAttendanceMonthDetailRespVO detail =
|
||||
attendanceStatisticsService.getAttendanceMonthDetail(employeeId, year, month);
|
||||
fillAttendanceMonthRecordDeptName(Collections.singletonList(detail.getSummary()));
|
||||
return success(detail);
|
||||
}
|
||||
|
||||
private void writeAttendanceMonthDailyOverview(HttpServletResponse response, YearMonth month,
|
||||
List<HrmAttendanceMonthDailyOverviewRespVO> list)
|
||||
throws IOException {
|
||||
List<List<String>> head = convertList(
|
||||
Arrays.asList("员工编号", "员工姓名", "工号", "部门", "岗位"), Collections::singletonList);
|
||||
for (int day = 1; day <= month.lengthOfMonth(); day++) {
|
||||
head.add(Collections.singletonList(month.atDay(day).toString()));
|
||||
}
|
||||
ExcelUtils.write(response, "员工月度打卡概况.xls", "打卡概况", head,
|
||||
buildAttendanceMonthDailyOverviewData(month, list));
|
||||
}
|
||||
|
||||
private List<List<Object>> buildAttendanceMonthDailyOverviewData(
|
||||
YearMonth month, List<HrmAttendanceMonthDailyOverviewRespVO> list) {
|
||||
List<List<Object>> result = new ArrayList<>(list.size());
|
||||
for (HrmAttendanceMonthDailyOverviewRespVO row : list) {
|
||||
List<Object> data = new ArrayList<>(Arrays.asList(row.getEmployeeId(), row.getEmployeeName(),
|
||||
row.getJobNumber(), row.getDeptName(), row.getPostName()));
|
||||
for (int day = 1; day <= month.lengthOfMonth(); day++) {
|
||||
data.add(formatDailyClockOverview(row.getDailyClockMap().get(month.atDay(day))));
|
||||
}
|
||||
result.add(data);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<HrmAttendanceDailyOverviewRespVO.OverviewItem> buildDailyClockOverviewList(
|
||||
HrmAttendanceDailyOverviewRespVO dailyOverview) {
|
||||
if (CollUtil.isEmpty(dailyOverview.getClocks())) {
|
||||
return Collections.singletonList(new HrmAttendanceDailyOverviewRespVO.OverviewItem()
|
||||
.setText(StrUtil.emptyIfNull(dailyOverview.getAttendanceResult())));
|
||||
}
|
||||
// 按打卡时间升序展示每日打卡概况
|
||||
dailyOverview.getClocks().sort(Comparator.comparing(HrmAttendanceClockRespVO::getClockTime));
|
||||
return convertList(dailyOverview.getClocks(),
|
||||
clock -> new HrmAttendanceDailyOverviewRespVO.OverviewItem()
|
||||
.setType(StrUtil.removeSuffix(StrUtil.emptyIfNull(DictFrameworkUtils.parseDictDataLabel(
|
||||
DictTypeConstants.HRM_ATTENDANCE_CLOCK_TYPE, clock.getType())), "打卡"))
|
||||
.setTime(clock.getClockTime() == null ? "-" : clock.getClockTime().toLocalTime().toString())
|
||||
.setStatus(StrUtil.emptyIfNull(DictFrameworkUtils.parseDictDataLabel(
|
||||
DictTypeConstants.HRM_ATTENDANCE_CLOCK_STATUS, clock.getStatus()))));
|
||||
}
|
||||
|
||||
private String formatDailyClockOverview(HrmAttendanceDailyOverviewRespVO dailyOverview) {
|
||||
if (dailyOverview == null) {
|
||||
return "";
|
||||
}
|
||||
return dailyOverview.getOverviews().stream()
|
||||
.map(item -> StrUtil.isNotEmpty(item.getText()) ? item.getText()
|
||||
: String.format("%s %s %s", item.getType(), item.getTime(), item.getStatus()))
|
||||
.collect(Collectors.joining(";"));
|
||||
}
|
||||
|
||||
// ==================== 拼接 VO ====================
|
||||
|
||||
private void fillAttendanceMonthDailyOverview(List<HrmAttendanceMonthDailyOverviewRespVO> list) {
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
|
||||
convertSet(list, HrmAttendanceMonthDailyOverviewRespVO::getDeptId));
|
||||
list.forEach(vo -> {
|
||||
MapUtils.findAndThen(deptMap, vo.getDeptId(), dept -> vo.setDeptName(dept.getName()));
|
||||
MapUtil.emptyIfNull(vo.getDailyClockMap()).values().forEach(
|
||||
overview -> overview.setOverviews(buildDailyClockOverviewList(overview)));
|
||||
});
|
||||
}
|
||||
|
||||
private void fillAttendanceMonthRecordDeptName(List<HrmAttendanceMonthRecordRespVO> list) {
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
|
||||
convertSet(list, HrmAttendanceMonthRecordRespVO::getDeptId));
|
||||
list.forEach(vo -> MapUtils.findAndThen(deptMap, vo.getDeptId(), dept -> vo.setDeptName(dept.getName())));
|
||||
}
|
||||
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.clock;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.attendance.record.HrmAttendanceClockSourceEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.attendance.record.HrmAttendanceClockStatusEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.attendance.record.HrmAttendanceClockTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 打卡记录分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class HrmAttendanceClockPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "员工姓名或工号,模糊匹配", example = "张三")
|
||||
private String search;
|
||||
|
||||
@Schema(description = "员工编号", example = "1024")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "部门编号数组", example = "100,101")
|
||||
private List<Long> deptIds;
|
||||
|
||||
@Schema(hidden = true)
|
||||
private Collection<Long> employeeIds;
|
||||
|
||||
@Schema(description = "考勤打卡类型", example = "1")
|
||||
@InEnum(value = HrmAttendanceClockTypeEnum.class, message = "打卡类型必须是 {value}")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "考勤打卡状态", example = "1")
|
||||
@InEnum(value = HrmAttendanceClockStatusEnum.class, message = "打卡状态必须是 {value}")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "来源类型数组", example = "[1, 2]")
|
||||
@InEnum(value = HrmAttendanceClockSourceEnum.class, message = "打卡来源必须是 {value}")
|
||||
private List<Integer> sourceTypes;
|
||||
|
||||
@Schema(description = "打卡地点,模糊匹配", example = "文三路")
|
||||
private String address;
|
||||
|
||||
@Schema(description = "上班日期范围")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] attendanceTime;
|
||||
|
||||
@Schema(description = "实际打卡时间范围")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] clockTime;
|
||||
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.clock;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
|
||||
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
|
||||
import cn.iocoder.yudao.module.hrm.enums.DictTypeConstants;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 打卡记录 Response VO")
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class HrmAttendanceClockRespVO {
|
||||
|
||||
@Schema(description = "打卡记录编号", example = "1024")
|
||||
@ExcelProperty(value = "记录编号", index = 0)
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "打卡员工编号", example = "1001")
|
||||
@ExcelProperty(value = "员工编号", index = 1)
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "员工姓名", example = "芋道")
|
||||
@ExcelProperty(value = "员工姓名", index = 2)
|
||||
private String employeeName;
|
||||
|
||||
@Schema(description = "工号", example = "HRM001")
|
||||
@ExcelProperty(value = "工号", index = 3)
|
||||
private String jobNumber;
|
||||
|
||||
@Schema(description = "部门编号", example = "100")
|
||||
private Long deptId;
|
||||
|
||||
@Schema(description = "部门名称", example = "研发部")
|
||||
@ExcelProperty(value = "部门", index = 4)
|
||||
private String deptName;
|
||||
|
||||
@Schema(description = "职位名称", example = "Java 工程师")
|
||||
@ExcelProperty(value = "岗位", index = 5)
|
||||
private String postName;
|
||||
|
||||
@Schema(description = "打卡时间")
|
||||
@ExcelProperty(value = "打卡时间", index = 8)
|
||||
private LocalDateTime clockTime;
|
||||
|
||||
@Schema(description = "打卡类型", example = "1")
|
||||
@ExcelProperty(value = "打卡类型", index = 6, converter = DictConvert.class)
|
||||
@DictFormat(DictTypeConstants.HRM_ATTENDANCE_CLOCK_TYPE)
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "应打卡时间")
|
||||
@ExcelProperty(value = "应打卡时间", index = 7)
|
||||
private LocalDateTime attendanceTime;
|
||||
|
||||
@Schema(description = "打卡来源")
|
||||
@ExcelProperty(value = "来源", index = 10, converter = DictConvert.class)
|
||||
@DictFormat(DictTypeConstants.HRM_ATTENDANCE_CLOCK_SOURCE)
|
||||
private Integer sourceType;
|
||||
|
||||
@Schema(description = "打卡状态")
|
||||
@ExcelProperty(value = "打卡状态", index = 9, converter = DictConvert.class)
|
||||
@DictFormat(DictTypeConstants.HRM_ATTENDANCE_CLOCK_STATUS)
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "打卡阶段")
|
||||
private Integer stage;
|
||||
|
||||
@Schema(description = "打卡地址", example = "总部大楼")
|
||||
@ExcelProperty(value = "地点", index = 11)
|
||||
private String address;
|
||||
|
||||
@Schema(description = "经度", example = "121.473701")
|
||||
private BigDecimal longitude;
|
||||
|
||||
@Schema(description = "纬度", example = "31.230416")
|
||||
private BigDecimal latitude;
|
||||
|
||||
@Schema(description = "WiFi 名称", example = "office_wifi")
|
||||
private String ssid;
|
||||
|
||||
@Schema(description = "WiFi MAC 地址", example = "00:11:22:33:44:55")
|
||||
private String mac;
|
||||
|
||||
@Schema(description = "备注", example = "手工补卡")
|
||||
@ExcelProperty(value = "备注", index = 12)
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.clock;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.attendance.record.HrmAttendanceClockTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.DecimalMax;
|
||||
import jakarta.validation.constraints.DecimalMin;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 打卡记录保存 Request VO")
|
||||
@Data
|
||||
public class HrmAttendanceClockSaveReqVO {
|
||||
|
||||
@Schema(description = "打卡记录编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "打卡员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1001")
|
||||
@NotNull(message = "打卡员工不能为空")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "打卡时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "打卡时间不能为空")
|
||||
private LocalDateTime clockTime;
|
||||
|
||||
@Schema(description = "打卡类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "打卡类型不能为空")
|
||||
@InEnum(value = HrmAttendanceClockTypeEnum.class, message = "打卡类型必须是 {value}")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "应打卡时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "应打卡时间不能为空")
|
||||
private LocalDateTime attendanceTime;
|
||||
|
||||
@Schema(description = "打卡地址", example = "总部大楼")
|
||||
@Size(max = 255, message = "打卡地址不能超过 255 个字符")
|
||||
private String address;
|
||||
|
||||
@Schema(description = "经度", example = "121.473701")
|
||||
@DecimalMin(value = "-180", message = "经度必须在 -180 到 180 之间")
|
||||
@DecimalMax(value = "180", message = "经度必须在 -180 到 180 之间")
|
||||
private BigDecimal longitude;
|
||||
|
||||
@Schema(description = "纬度", example = "31.230416")
|
||||
@DecimalMin(value = "-90", message = "纬度必须在 -90 到 90 之间")
|
||||
@DecimalMax(value = "90", message = "纬度必须在 -90 到 90 之间")
|
||||
private BigDecimal latitude;
|
||||
|
||||
@Schema(description = "WiFi 名称", example = "office_wifi")
|
||||
@Size(max = 50, message = "WiFi 名称不能超过 50 个字符")
|
||||
private String ssid;
|
||||
|
||||
@Schema(description = "WiFi MAC 地址", example = "00:11:22:33:44:55")
|
||||
@Size(max = 50, message = "WiFi MAC 地址不能超过 50 个字符")
|
||||
private String mac;
|
||||
|
||||
@Schema(description = "备注", example = "手工补卡")
|
||||
@Size(max = 255, message = "备注不能超过 255 个字符")
|
||||
private String remark;
|
||||
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.clock;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工实际班次 Request VO")
|
||||
@Data
|
||||
public class HrmAttendanceClockShiftReqVO {
|
||||
|
||||
@Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "员工不能为空")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "考勤时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "考勤时间不能为空")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime attendanceTime;
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.clock;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工实际班次 Response VO")
|
||||
@Data
|
||||
public class HrmAttendanceClockShiftRespVO {
|
||||
|
||||
@Schema(description = "上班时间")
|
||||
private LocalDateTime startTime;
|
||||
|
||||
@Schema(description = "下班时间")
|
||||
private LocalDateTime endTime;
|
||||
|
||||
@Schema(description = "上班打卡开始时间")
|
||||
private LocalDateTime clockInStartTime;
|
||||
|
||||
@Schema(description = "上班打卡结束时间")
|
||||
private LocalDateTime clockInEndTime;
|
||||
|
||||
@Schema(description = "下班打卡开始时间")
|
||||
private LocalDateTime clockOutStartTime;
|
||||
|
||||
@Schema(description = "下班打卡结束时间")
|
||||
private LocalDateTime clockOutEndTime;
|
||||
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.group;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 考勤组分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class HrmAttendanceGroupPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "考勤组名称", example = "默认考勤组")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "是否默认考勤组", example = "true")
|
||||
private Boolean defaultStatus;
|
||||
|
||||
}
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.group;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 考勤组 Response VO")
|
||||
@Data
|
||||
public class HrmAttendanceGroupRespVO {
|
||||
|
||||
@Schema(description = "考勤组编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "名称", example = "总部考勤组")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "是否启用 WiFi 打卡")
|
||||
private Boolean openWifiCard;
|
||||
|
||||
@Schema(description = "是否启用定位打卡")
|
||||
private Boolean openPointCard;
|
||||
|
||||
@Schema(description = "是否法定节假日休息")
|
||||
private Boolean rest;
|
||||
|
||||
@Schema(description = "是否默认配置")
|
||||
private Boolean defaultStatus;
|
||||
|
||||
@Schema(description = "特殊日期设置数组")
|
||||
private List<SpecialDate> specialDates;
|
||||
|
||||
@Schema(description = "部门范围")
|
||||
private List<Long> deptIds;
|
||||
|
||||
@Schema(description = "适用部门名称")
|
||||
private List<String> deptNames;
|
||||
|
||||
@Schema(description = "员工范围")
|
||||
private List<Long> employeeIds;
|
||||
|
||||
@Schema(description = "适用员工名称")
|
||||
private List<String> employeeNames;
|
||||
|
||||
@Schema(description = "班次配置")
|
||||
private List<Shift> shifts;
|
||||
|
||||
@Schema(description = "打卡地点")
|
||||
private List<Point> points;
|
||||
|
||||
@Schema(description = "打卡 WiFi")
|
||||
private List<Wifi> wifis;
|
||||
|
||||
@Schema(description = "扣款规则")
|
||||
private DeductRule deductRule;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 考勤组特殊日期 Response VO")
|
||||
@Data
|
||||
public static class SpecialDate {
|
||||
|
||||
@Schema(description = "日期类型", example = "1")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "特殊日期")
|
||||
private LocalDateTime date;
|
||||
}
|
||||
|
||||
@Schema(description = "管理后台 - HRM 考勤组班次配置 Response VO")
|
||||
@Data
|
||||
public static class Shift {
|
||||
|
||||
@Schema(description = "工作日数组", example = "[1, 2, 3, 4, 5]")
|
||||
private List<Integer> weeks;
|
||||
|
||||
@Schema(description = "上班时间", example = "09:00")
|
||||
@JsonFormat(pattern = "HH:mm")
|
||||
private LocalTime startTime;
|
||||
|
||||
@Schema(description = "下班时间", example = "18:00")
|
||||
@JsonFormat(pattern = "HH:mm")
|
||||
private LocalTime endTime;
|
||||
|
||||
@Schema(description = "上班打卡开始时间", example = "08:00")
|
||||
@JsonFormat(pattern = "HH:mm")
|
||||
private LocalTime clockInStartTime;
|
||||
|
||||
@Schema(description = "上班打卡结束时间", example = "10:00")
|
||||
@JsonFormat(pattern = "HH:mm")
|
||||
private LocalTime clockInEndTime;
|
||||
|
||||
@Schema(description = "下班打卡开始时间", example = "17:00")
|
||||
@JsonFormat(pattern = "HH:mm")
|
||||
private LocalTime clockOutStartTime;
|
||||
|
||||
@Schema(description = "下班打卡结束时间", example = "20:00")
|
||||
@JsonFormat(pattern = "HH:mm")
|
||||
private LocalTime clockOutEndTime;
|
||||
|
||||
@Schema(description = "休息开始时间", example = "12:00")
|
||||
@JsonFormat(pattern = "HH:mm")
|
||||
private LocalTime restStartTime;
|
||||
|
||||
@Schema(description = "休息结束时间", example = "13:00")
|
||||
@JsonFormat(pattern = "HH:mm")
|
||||
private LocalTime restEndTime;
|
||||
|
||||
@Schema(description = "休息时间是否不计入工作时长")
|
||||
private Boolean excludeRestTime;
|
||||
}
|
||||
|
||||
@Schema(description = "管理后台 - HRM 考勤组打卡地点 Response VO")
|
||||
@Data
|
||||
public static class Point {
|
||||
|
||||
@Schema(description = "地点名称", example = "总部办公区")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "定位地址", example = "北京市海淀区中关村大街")
|
||||
private String address;
|
||||
|
||||
@Schema(description = "纬度", example = "39.983424")
|
||||
private BigDecimal latitude;
|
||||
|
||||
@Schema(description = "经度", example = "116.322987")
|
||||
private BigDecimal longitude;
|
||||
|
||||
@Schema(description = "有效打卡半径,单位:米", example = "300")
|
||||
private Integer radius;
|
||||
}
|
||||
|
||||
@Schema(description = "管理后台 - HRM 考勤组 WiFi 配置 Response VO")
|
||||
@Data
|
||||
public static class Wifi {
|
||||
|
||||
@Schema(description = "WiFi 名称", example = "Office-WiFi")
|
||||
private String ssid;
|
||||
|
||||
@Schema(description = "MAC 地址", example = "00:11:22:33:44:55")
|
||||
private String mac;
|
||||
}
|
||||
|
||||
@Schema(description = "管理后台 - HRM 考勤组扣款规则 Response VO")
|
||||
@Data
|
||||
public static class DeductRule {
|
||||
|
||||
@Schema(description = "迟到扣款方式", example = "1")
|
||||
private Integer lateMethod;
|
||||
|
||||
@Schema(description = "迟到扣款金额", example = "10.00")
|
||||
private BigDecimal lateDeductMoney;
|
||||
|
||||
@Schema(description = "早退扣款方式", example = "1")
|
||||
private Integer earlyMethod;
|
||||
|
||||
@Schema(description = "早退扣款金额", example = "10.00")
|
||||
private BigDecimal earlyDeductMoney;
|
||||
|
||||
@Schema(description = "旷工扣款方式", example = "1")
|
||||
private Integer absenteeismMethod;
|
||||
|
||||
@Schema(description = "旷工扣款金额", example = "100.00")
|
||||
private BigDecimal absenteeismDeductMoney;
|
||||
|
||||
@Schema(description = "缺卡扣款方式", example = "1")
|
||||
private Integer misscardMethod;
|
||||
|
||||
@Schema(description = "缺卡扣款金额", example = "20.00")
|
||||
private BigDecimal misscardDeductMoney;
|
||||
}
|
||||
|
||||
}
|
||||
+396
@@ -0,0 +1,396 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.group;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.TimeRange;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.attendance.config.HrmAttendanceAbsenteeismDeductMethodEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.attendance.config.HrmAttendanceHolidayTypeEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.attendance.config.HrmAttendanceLateEarlyDeductMethodEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.attendance.config.HrmAttendanceMisscardDeductMethodEnum;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.mzt.logapi.starter.annotation.DiffLogField;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.DecimalMax;
|
||||
import jakarta.validation.constraints.DecimalMin;
|
||||
import jakarta.validation.constraints.Digits;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 考勤组保存 Request VO")
|
||||
@Data
|
||||
public class HrmAttendanceGroupSaveReqVO {
|
||||
|
||||
private static final Set<Integer> POINT_RADIUS_VALUES = new HashSet<>(
|
||||
Arrays.asList(100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 2000, 3000));
|
||||
|
||||
@Schema(description = "考勤组编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "总部考勤组")
|
||||
@NotBlank(message = "考勤组名称不能为空")
|
||||
@Size(max = 50, message = "考勤组名称不能超过 50 个字符")
|
||||
@DiffLogField(name = "考勤组名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "是否启用 WiFi 打卡", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
@NotNull(message = "是否启用 WiFi 打卡不能为空")
|
||||
@DiffLogField(name = "是否启用 WiFi 打卡")
|
||||
private Boolean openWifiCard;
|
||||
|
||||
@Schema(description = "是否启用定位打卡", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
@NotNull(message = "是否启用定位打卡不能为空")
|
||||
@DiffLogField(name = "是否启用定位打卡")
|
||||
private Boolean openPointCard;
|
||||
|
||||
@Schema(description = "是否法定节假日休息", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
@NotNull(message = "是否法定节假日休息不能为空")
|
||||
@DiffLogField(name = "是否法定节假日休息")
|
||||
private Boolean rest;
|
||||
|
||||
@Schema(description = "特殊日期设置数组", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@Valid
|
||||
@NotNull(message = "特殊日期设置不能为空")
|
||||
@DiffLogField(name = "特殊日期设置")
|
||||
private List<SpecialDate> specialDates;
|
||||
|
||||
@Schema(description = "部门范围", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1024, 2048]")
|
||||
@NotNull(message = "部门范围不能为空")
|
||||
@DiffLogField(name = "适用部门")
|
||||
private List<@NotNull(message = "部门编号不能为空") Long> deptIds;
|
||||
|
||||
@Schema(description = "员工范围", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1024, 2048]")
|
||||
@NotNull(message = "员工范围不能为空")
|
||||
@DiffLogField(name = "适用员工")
|
||||
private List<@NotNull(message = "员工编号不能为空") Long> employeeIds;
|
||||
|
||||
@Schema(description = "班次配置", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@Valid
|
||||
@NotEmpty(message = "班次配置不能为空")
|
||||
@DiffLogField(name = "班次配置")
|
||||
private List<Shift> shifts;
|
||||
|
||||
@Schema(description = "打卡地点", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@Valid
|
||||
@NotNull(message = "打卡地点不能为空")
|
||||
@DiffLogField(name = "打卡地点")
|
||||
private List<Point> points;
|
||||
|
||||
@Schema(description = "打卡 WiFi", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@Valid
|
||||
@NotNull(message = "打卡 WiFi 不能为空")
|
||||
@DiffLogField(name = "打卡 WiFi")
|
||||
private List<Wifi> wifis;
|
||||
|
||||
@Schema(description = "扣款规则", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@Valid
|
||||
@NotNull(message = "扣款规则不能为空")
|
||||
@DiffLogField(name = "扣款规则")
|
||||
private DeductRule deductRule;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 考勤组特殊日期")
|
||||
@Data
|
||||
public static class SpecialDate {
|
||||
|
||||
@Schema(description = "日期类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "特殊日期类型不能为空")
|
||||
@InEnum(value = HrmAttendanceHolidayTypeEnum.class, message = "特殊日期类型必须是 {value}")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "特殊日期", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "特殊日期不能为空")
|
||||
private LocalDateTime date;
|
||||
}
|
||||
|
||||
@Schema(description = "管理后台 - HRM 考勤组班次配置")
|
||||
@Data
|
||||
public static class Shift {
|
||||
|
||||
@Schema(description = "工作日数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2, 3, 4, 5]")
|
||||
@NotEmpty(message = "班次工作日不能为空")
|
||||
private List<@NotNull(message = "班次工作日不能为空")
|
||||
@Min(value = 1, message = "班次工作日必须在 1 到 7 之间")
|
||||
@Max(value = 7, message = "班次工作日必须在 1 到 7 之间") Integer> weeks;
|
||||
|
||||
@Schema(description = "上班时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "09:00")
|
||||
@JsonFormat(pattern = "HH:mm")
|
||||
@NotNull(message = "上班时间不能为空")
|
||||
private LocalTime startTime;
|
||||
|
||||
@Schema(description = "下班时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "18:00")
|
||||
@JsonFormat(pattern = "HH:mm")
|
||||
@NotNull(message = "下班时间不能为空")
|
||||
private LocalTime endTime;
|
||||
|
||||
@Schema(description = "上班打卡开始时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "08:00")
|
||||
@JsonFormat(pattern = "HH:mm")
|
||||
@NotNull(message = "上班打卡开始时间不能为空")
|
||||
private LocalTime clockInStartTime;
|
||||
|
||||
@Schema(description = "上班打卡结束时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "10:00")
|
||||
@JsonFormat(pattern = "HH:mm")
|
||||
@NotNull(message = "上班打卡结束时间不能为空")
|
||||
private LocalTime clockInEndTime;
|
||||
|
||||
@Schema(description = "下班打卡开始时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "17:00")
|
||||
@JsonFormat(pattern = "HH:mm")
|
||||
@NotNull(message = "下班打卡开始时间不能为空")
|
||||
private LocalTime clockOutStartTime;
|
||||
|
||||
@Schema(description = "下班打卡结束时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "20:00")
|
||||
@JsonFormat(pattern = "HH:mm")
|
||||
@NotNull(message = "下班打卡结束时间不能为空")
|
||||
private LocalTime clockOutEndTime;
|
||||
|
||||
@Schema(description = "休息开始时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "12:00")
|
||||
@JsonFormat(pattern = "HH:mm")
|
||||
@NotNull(message = "休息开始时间不能为空")
|
||||
private LocalTime restStartTime;
|
||||
|
||||
@Schema(description = "休息结束时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "13:00")
|
||||
@JsonFormat(pattern = "HH:mm")
|
||||
@NotNull(message = "休息结束时间不能为空")
|
||||
private LocalTime restEndTime;
|
||||
|
||||
@Schema(description = "休息时间是否不计入工作时长", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "休息时间计算方式不能为空")
|
||||
private Boolean excludeRestTime;
|
||||
}
|
||||
|
||||
@Schema(description = "管理后台 - HRM 考勤组打卡地点")
|
||||
@Data
|
||||
public static class Point {
|
||||
|
||||
@Schema(description = "地点名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "总部办公区")
|
||||
@NotBlank(message = "地点名称不能为空")
|
||||
@Size(max = 50, message = "地点名称不能超过 50 个字符")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "定位地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "北京市海淀区中关村大街")
|
||||
@NotBlank(message = "定位地址不能为空")
|
||||
@Size(max = 255, message = "定位地址不能超过 255 个字符")
|
||||
private String address;
|
||||
|
||||
@Schema(description = "纬度", requiredMode = Schema.RequiredMode.REQUIRED, example = "39.983424")
|
||||
@NotNull(message = "纬度不能为空")
|
||||
@DecimalMin(value = "-90", message = "纬度必须在 -90 到 90 之间")
|
||||
@DecimalMax(value = "90", message = "纬度必须在 -90 到 90 之间")
|
||||
private BigDecimal latitude;
|
||||
|
||||
@Schema(description = "经度", requiredMode = Schema.RequiredMode.REQUIRED, example = "116.322987")
|
||||
@NotNull(message = "经度不能为空")
|
||||
@DecimalMin(value = "-180", message = "经度必须在 -180 到 180 之间")
|
||||
@DecimalMax(value = "180", message = "经度必须在 -180 到 180 之间")
|
||||
private BigDecimal longitude;
|
||||
|
||||
@Schema(description = "有效打卡半径,单位:米", requiredMode = Schema.RequiredMode.REQUIRED, example = "300")
|
||||
@NotNull(message = "有效打卡半径不能为空")
|
||||
@Positive(message = "有效打卡半径必须大于 0")
|
||||
private Integer radius;
|
||||
}
|
||||
|
||||
@Schema(description = "管理后台 - HRM 考勤组 WiFi 配置")
|
||||
@Data
|
||||
public static class Wifi {
|
||||
|
||||
@Schema(description = "WiFi 名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "Office-WiFi")
|
||||
@NotBlank(message = "WiFi 名称不能为空")
|
||||
@Size(max = 50, message = "WiFi 名称不能超过 50 个字符")
|
||||
private String ssid;
|
||||
|
||||
@Schema(description = "MAC 地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "00:11:22:33:44:55")
|
||||
@NotBlank(message = "MAC 地址不能为空")
|
||||
@Pattern(regexp = "^((([0-9a-fA-F]{2}:){5})|(([0-9a-fA-F]{2}-){5}))[0-9a-fA-F]{2}$",
|
||||
message = "MAC 地址格式不正确")
|
||||
private String mac;
|
||||
}
|
||||
|
||||
@Schema(description = "管理后台 - HRM 考勤组扣款规则")
|
||||
@Data
|
||||
public static class DeductRule {
|
||||
|
||||
@Schema(description = "迟到扣款方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "迟到扣款方式不能为空")
|
||||
@InEnum(value = HrmAttendanceLateEarlyDeductMethodEnum.class, message = "迟到扣款方式必须是 {value}")
|
||||
private Integer lateMethod;
|
||||
|
||||
@Schema(description = "迟到扣款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "10.00")
|
||||
@NotNull(message = "迟到扣款金额不能为空")
|
||||
@DecimalMin(value = "0", message = "迟到扣款金额不能小于 0")
|
||||
@Digits(integer = 8, fraction = 2, message = "迟到扣款金额最多 8 位整数和 2 位小数")
|
||||
private BigDecimal lateDeductMoney;
|
||||
|
||||
@Schema(description = "早退扣款方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "早退扣款方式不能为空")
|
||||
@InEnum(value = HrmAttendanceLateEarlyDeductMethodEnum.class, message = "早退扣款方式必须是 {value}")
|
||||
private Integer earlyMethod;
|
||||
|
||||
@Schema(description = "早退扣款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "10.00")
|
||||
@NotNull(message = "早退扣款金额不能为空")
|
||||
@DecimalMin(value = "0", message = "早退扣款金额不能小于 0")
|
||||
@Digits(integer = 8, fraction = 2, message = "早退扣款金额最多 8 位整数和 2 位小数")
|
||||
private BigDecimal earlyDeductMoney;
|
||||
|
||||
@Schema(description = "旷工扣款方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "旷工扣款方式不能为空")
|
||||
@InEnum(value = HrmAttendanceAbsenteeismDeductMethodEnum.class, message = "旷工扣款方式必须是 {value}")
|
||||
private Integer absenteeismMethod;
|
||||
|
||||
@Schema(description = "旷工扣款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
|
||||
@NotNull(message = "旷工扣款金额不能为空")
|
||||
@DecimalMin(value = "0", message = "旷工扣款金额不能小于 0")
|
||||
@Digits(integer = 8, fraction = 2, message = "旷工扣款金额最多 8 位整数和 2 位小数")
|
||||
private BigDecimal absenteeismDeductMoney;
|
||||
|
||||
@Schema(description = "缺卡扣款方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "缺卡扣款方式不能为空")
|
||||
@InEnum(value = HrmAttendanceMisscardDeductMethodEnum.class, message = "缺卡扣款方式必须是 {value}")
|
||||
private Integer misscardMethod;
|
||||
|
||||
@Schema(description = "缺卡扣款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "20.00")
|
||||
@NotNull(message = "缺卡扣款金额不能为空")
|
||||
@DecimalMin(value = "0", message = "缺卡扣款金额不能小于 0")
|
||||
@Digits(integer = 8, fraction = 2, message = "缺卡扣款金额最多 8 位整数和 2 位小数")
|
||||
private BigDecimal misscardDeductMoney;
|
||||
}
|
||||
|
||||
@AssertTrue(message = "不同班次不能配置重复工作日")
|
||||
@JsonIgnore
|
||||
public boolean isShiftWeeksUnique() {
|
||||
if (CollUtil.isEmpty(shifts)) {
|
||||
return true;
|
||||
}
|
||||
Set<Integer> weeks = new HashSet<>();
|
||||
return shifts.stream().allMatch(shift -> shift != null && CollUtil.isNotEmpty(shift.getWeeks())
|
||||
&& shift.getWeeks().stream().allMatch(weeks::add));
|
||||
}
|
||||
|
||||
@AssertTrue(message = "班次上下班、打卡窗口或休息时间配置不正确")
|
||||
@JsonIgnore
|
||||
public boolean isShiftTimeValid() {
|
||||
if (CollUtil.isEmpty(shifts)) {
|
||||
return true;
|
||||
}
|
||||
return shifts.stream().allMatch(this::isShiftTimeValid);
|
||||
}
|
||||
|
||||
private boolean isShiftTimeValid(Shift shift) {
|
||||
if (shift == null || shift.getStartTime() == null || shift.getEndTime() == null
|
||||
|| shift.getClockInStartTime() == null || shift.getClockInEndTime() == null
|
||||
|| shift.getClockOutStartTime() == null || shift.getClockOutEndTime() == null
|
||||
|| shift.getRestStartTime() == null || shift.getRestEndTime() == null
|
||||
|| shift.getExcludeRestTime() == null) {
|
||||
// 交由内嵌字段的 @NotNull 输出具体校验错误
|
||||
return true;
|
||||
}
|
||||
if (shift.getStartTime().equals(shift.getEndTime())
|
||||
|| shift.getClockInStartTime().equals(shift.getClockInEndTime())
|
||||
|| shift.getClockOutStartTime().equals(shift.getClockOutEndTime())) {
|
||||
return false;
|
||||
}
|
||||
LocalDate attendanceDate = LocalDate.of(2000, 1, 3);
|
||||
TimeRange shiftTimeRange = CollUtil.getFirst(LocalDateTimeUtils.buildDailyTimeRanges(
|
||||
attendanceDate, attendanceDate, shift.getStartTime(), shift.getEndTime()));
|
||||
return LocalDateTimeUtils.findDailyTimeRange(
|
||||
attendanceDate, shift.getClockInStartTime(), shift.getClockInEndTime(),
|
||||
shiftTimeRange.getStartTime()) != null
|
||||
&& LocalDateTimeUtils.findDailyTimeRange(
|
||||
attendanceDate, shift.getClockOutStartTime(), shift.getClockOutEndTime(),
|
||||
shiftTimeRange.getEndTime()) != null
|
||||
&& isRestTimeValid(attendanceDate, shift, shiftTimeRange);
|
||||
}
|
||||
|
||||
private boolean isRestTimeValid(LocalDate attendanceDate, Shift shift,
|
||||
TimeRange shiftTimeRange) {
|
||||
if (Boolean.FALSE.equals(shift.getExcludeRestTime())) {
|
||||
return true;
|
||||
}
|
||||
if (shift.getRestStartTime().equals(shift.getRestEndTime())) {
|
||||
return false;
|
||||
}
|
||||
List<TimeRange> restTimeRanges = LocalDateTimeUtils.buildDailyTimeRanges(
|
||||
attendanceDate.minusDays(1), attendanceDate.plusDays(1),
|
||||
shift.getRestStartTime(), shift.getRestEndTime());
|
||||
return CollUtil.findOne(restTimeRanges, restTimeRange ->
|
||||
LocalDateTimeUtils.isAfterOrEqual(restTimeRange.getStartTime(), shiftTimeRange.getStartTime())
|
||||
&& LocalDateTimeUtils.isBeforeOrEqual(
|
||||
restTimeRange.getEndTime(), shiftTimeRange.getEndTime())) != null;
|
||||
}
|
||||
|
||||
@AssertTrue(message = "特殊日期不能重复")
|
||||
@JsonIgnore
|
||||
public boolean isSpecialDatesUnique() {
|
||||
if (CollUtil.isEmpty(specialDates)) {
|
||||
return true;
|
||||
}
|
||||
Set<LocalDate> dates = new HashSet<>();
|
||||
return specialDates.stream().allMatch(specialDate -> specialDate != null && specialDate.getDate() != null
|
||||
&& dates.add(specialDate.getDate().toLocalDate()));
|
||||
}
|
||||
|
||||
@AssertTrue(message = "考勤组至少需要选择一个适用部门或员工")
|
||||
@JsonIgnore
|
||||
public boolean isScopeValid() {
|
||||
if (deptIds == null || employeeIds == null) {
|
||||
return true;
|
||||
}
|
||||
return CollUtil.isNotEmpty(deptIds) || CollUtil.isNotEmpty(employeeIds);
|
||||
}
|
||||
|
||||
@AssertTrue(message = "考勤组至少需要启用定位打卡或 WiFi 打卡")
|
||||
@JsonIgnore
|
||||
public boolean isClockMethodValid() {
|
||||
if (openPointCard == null || openWifiCard == null) {
|
||||
return true;
|
||||
}
|
||||
return openPointCard || openWifiCard;
|
||||
}
|
||||
|
||||
@AssertTrue(message = "启用定位打卡时,打卡地点不能为空")
|
||||
@JsonIgnore
|
||||
public boolean isPointSettingValid() {
|
||||
if (Boolean.TRUE.equals(openPointCard)) {
|
||||
return CollUtil.isNotEmpty(points);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@AssertTrue(message = "启用 WiFi 打卡时,打卡 WiFi 不能为空")
|
||||
@JsonIgnore
|
||||
public boolean isWifiSettingValid() {
|
||||
if (Boolean.TRUE.equals(openWifiCard)) {
|
||||
return CollUtil.isNotEmpty(wifis);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@AssertTrue(message = "有效打卡半径必须是 100 至 1000 米(间隔 100 米),或 2000、3000 米")
|
||||
@JsonIgnore
|
||||
public boolean isPointRadiusValid() {
|
||||
if (CollUtil.isEmpty(points)) {
|
||||
return true;
|
||||
}
|
||||
return points.stream().allMatch(point -> point != null
|
||||
&& point.getRadius() != null && POINT_RADIUS_VALUES.contains(point.getRadius()));
|
||||
}
|
||||
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.holiday;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.attendance.config.HrmAttendanceHolidayTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 考勤节假日分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class HrmAttendanceHolidayPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "日期范围")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] date;
|
||||
|
||||
@Schema(description = "节假日类型", example = "1")
|
||||
@InEnum(value = HrmAttendanceHolidayTypeEnum.class, message = "节假日类型必须是 {value}")
|
||||
private Integer type;
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.holiday;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 考勤节假日 Response VO")
|
||||
@Data
|
||||
public class HrmAttendanceHolidayRespVO {
|
||||
|
||||
@Schema(description = "编号")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "日期")
|
||||
private LocalDateTime date;
|
||||
|
||||
@Schema(description = "考勤假期类型")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.holiday;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.attendance.config.HrmAttendanceHolidayTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 考勤节假日保存 Request VO")
|
||||
@Data
|
||||
public class HrmAttendanceHolidaySaveReqVO {
|
||||
|
||||
@Schema(description = "节假日编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "日期", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "日期不能为空")
|
||||
private LocalDateTime date;
|
||||
|
||||
@Schema(description = "节假日类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
@NotNull(message = "类型不能为空")
|
||||
@InEnum(value = HrmAttendanceHolidayTypeEnum.class, message = "节假日类型必须是 {value}")
|
||||
private Integer type;
|
||||
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.leave;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工请假申请取消 Request VO")
|
||||
@Data
|
||||
public class HrmAttendanceLeaveCancelReqVO {
|
||||
|
||||
@Schema(description = "请假记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "请假记录编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "取消原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "行程取消")
|
||||
@NotBlank(message = "取消原因不能为空")
|
||||
@Size(max = 500, message = "取消原因不能超过 500 个字符")
|
||||
private String reason;
|
||||
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.leave;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.DecimalMin;
|
||||
import jakarta.validation.constraints.Digits;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工请假申请创建 Request VO")
|
||||
@Data
|
||||
public class HrmAttendanceLeaveCreateReqVO {
|
||||
|
||||
@Schema(description = "请假类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "事假")
|
||||
@NotBlank(message = "请假类型不能为空")
|
||||
@Size(max = 64, message = "请假类型不能超过 64 个字符")
|
||||
private String type;
|
||||
|
||||
@Schema(description = "请假开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "请假开始时间不能为空")
|
||||
private LocalDateTime startTime;
|
||||
|
||||
@Schema(description = "请假结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "请假结束时间不能为空")
|
||||
private LocalDateTime endTime;
|
||||
|
||||
@Schema(description = "请假天数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.00")
|
||||
@NotNull(message = "请假天数不能为空")
|
||||
@DecimalMin(value = "0.01", message = "请假天数必须大于 0")
|
||||
@Digits(integer = 8, fraction = 2, message = "请假天数最多保留两位小数")
|
||||
private BigDecimal day;
|
||||
|
||||
@Schema(description = "请假事由", requiredMode = Schema.RequiredMode.REQUIRED, example = "个人事务")
|
||||
@NotBlank(message = "请假事由不能为空")
|
||||
@Size(max = 300, message = "请假事由不能超过 300 个字符")
|
||||
private String reason;
|
||||
|
||||
@Schema(description = "备注", example = "请审批")
|
||||
@Size(max = 500, message = "备注不能超过 500 个字符")
|
||||
private String remark;
|
||||
|
||||
@Schema(hidden = true)
|
||||
@AssertTrue(message = "请假结束时间必须晚于请假开始时间")
|
||||
@JsonIgnore
|
||||
public boolean isTimeRangeValid() {
|
||||
return startTime == null || endTime == null || endTime.isAfter(startTime);
|
||||
}
|
||||
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.leave;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.SortablePageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 请假记录分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class HrmAttendanceLeavePageReqVO extends SortablePageParam {
|
||||
|
||||
@Schema(description = "员工编号", example = "1024")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "部门编号数组", example = "[100, 101]")
|
||||
private List<Long> deptIds;
|
||||
|
||||
@Schema(description = "员工姓名或工号", example = "张三")
|
||||
private String employeeKeyword;
|
||||
|
||||
@Schema(description = "请假类型数组", example = "[事假, 病假]")
|
||||
private List<String> types;
|
||||
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
@Schema(description = "开始时间")
|
||||
private LocalDateTime[] startTime;
|
||||
|
||||
@Schema(description = "审批状态", example = "2")
|
||||
private Integer approvalStatus;
|
||||
|
||||
@Schema(hidden = true)
|
||||
private Collection<Long> employeeIds;
|
||||
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.leave;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
|
||||
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
|
||||
import cn.iocoder.yudao.module.hrm.enums.DictTypeConstants;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 请假记录 Response VO")
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class HrmAttendanceLeaveRespVO {
|
||||
|
||||
@Schema(description = "请假记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@ExcelProperty(value = "记录编号", index = 0)
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1001")
|
||||
@ExcelProperty(value = "员工编号", index = 1)
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "员工姓名", example = "张三")
|
||||
@ExcelProperty(value = "员工姓名", index = 2)
|
||||
private String employeeName;
|
||||
|
||||
@Schema(description = "工号", example = "HRM001")
|
||||
@ExcelProperty(value = "工号", index = 3)
|
||||
private String jobNumber;
|
||||
|
||||
@Schema(description = "部门编号", example = "100")
|
||||
private Long deptId;
|
||||
|
||||
@Schema(description = "部门名称", example = "研发部")
|
||||
@ExcelProperty(value = "部门", index = 4)
|
||||
private String deptName;
|
||||
|
||||
@Schema(description = "职位名称", example = "Java 工程师")
|
||||
@ExcelProperty(value = "岗位", index = 5)
|
||||
private String postName;
|
||||
|
||||
@Schema(description = "请假类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "事假")
|
||||
@ExcelProperty(value = "请假类型", index = 6, converter = DictConvert.class)
|
||||
@DictFormat(DictTypeConstants.HRM_ATTENDANCE_LEAVE_TYPE)
|
||||
private String type;
|
||||
|
||||
@Schema(description = "开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty(value = "开始时间", index = 7)
|
||||
private LocalDateTime startTime;
|
||||
|
||||
@Schema(description = "结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty(value = "结束时间", index = 8)
|
||||
private LocalDateTime endTime;
|
||||
|
||||
@Schema(description = "请假天数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0")
|
||||
@ExcelProperty(value = "请假天数", index = 9)
|
||||
private BigDecimal day;
|
||||
|
||||
@Schema(description = "请假事由", example = "个人事务")
|
||||
@ExcelProperty(value = "请假事由", index = 10)
|
||||
private String reason;
|
||||
|
||||
@Schema(description = "备注")
|
||||
@ExcelProperty(value = "备注", index = 11)
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty(value = "创建时间", index = 12)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "审批状态", example = "1")
|
||||
private Integer approvalStatus;
|
||||
|
||||
@Schema(description = "流程实例编号")
|
||||
private String processInstanceId;
|
||||
|
||||
@Schema(description = "审批时间")
|
||||
private LocalDateTime approvalTime;
|
||||
|
||||
@Schema(description = "审批意见")
|
||||
private String approvalReason;
|
||||
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工每日考勤明细 Request VO")
|
||||
@Data
|
||||
public class HrmAttendanceDailyDetailReqVO {
|
||||
|
||||
@Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "员工不能为空")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "考勤日期", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "考勤日期不能为空")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime attendanceTime;
|
||||
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.clock.HrmAttendanceClockRespVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工每日考勤明细 Response VO")
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class HrmAttendanceDailyDetailRespVO {
|
||||
|
||||
@Schema(description = "员工编号")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "员工姓名")
|
||||
private String employeeName;
|
||||
|
||||
@Schema(description = "工号")
|
||||
private String jobNumber;
|
||||
|
||||
@Schema(description = "部门编号")
|
||||
private Long deptId;
|
||||
|
||||
@Schema(description = "部门名称")
|
||||
private String deptName;
|
||||
|
||||
@Schema(description = "职位名称")
|
||||
private String postName;
|
||||
|
||||
@ExcelProperty(value = "日期", index = 0)
|
||||
@Schema(description = "日期")
|
||||
private LocalDateTime attendanceTime;
|
||||
|
||||
@ExcelProperty(value = "班次", index = 1)
|
||||
@Schema(description = "班次")
|
||||
private String shiftName;
|
||||
|
||||
@Schema(description = "是否排班")
|
||||
private Boolean scheduled;
|
||||
|
||||
@Schema(description = "应打卡次数")
|
||||
private Integer requiredClockCount;
|
||||
|
||||
@Schema(description = "排班分钟数")
|
||||
private Integer scheduledMinutes;
|
||||
|
||||
@ExcelProperty(value = "缺卡次数", index = 8)
|
||||
@Schema(description = "缺卡次数")
|
||||
private Integer misscardCount;
|
||||
|
||||
@Schema(description = "是否旷工")
|
||||
private Boolean absenteeism;
|
||||
|
||||
@Schema(description = "旷工分钟数")
|
||||
private Integer absenteeismMinutes;
|
||||
|
||||
@ExcelProperty(value = "旷工天数", index = 9)
|
||||
@Schema(description = "旷工天数")
|
||||
private BigDecimal absenteeismDays;
|
||||
|
||||
@Schema(description = "是否请假")
|
||||
private Boolean leaveStatus;
|
||||
|
||||
@Schema(description = "请假分钟数")
|
||||
private Integer leaveMinutes;
|
||||
|
||||
@ExcelProperty(value = "请假天数", index = 10)
|
||||
@Schema(description = "请假天数")
|
||||
private BigDecimal leaveDays;
|
||||
|
||||
@ExcelProperty(value = "考勤结果", index = 2)
|
||||
@Schema(description = "考勤结果")
|
||||
private String attendanceResult;
|
||||
|
||||
@ExcelProperty(value = "打卡次数", index = 3)
|
||||
@Schema(description = "打卡次数")
|
||||
private Integer clockCount;
|
||||
|
||||
@ExcelProperty(value = "迟到次数", index = 4)
|
||||
@Schema(description = "迟到次数")
|
||||
private Integer lateCount;
|
||||
|
||||
@ExcelProperty(value = "迟到分钟", index = 5)
|
||||
@Schema(description = "迟到分钟")
|
||||
private Integer lateMinutes;
|
||||
|
||||
@ExcelProperty(value = "早退次数", index = 6)
|
||||
@Schema(description = "早退次数")
|
||||
private Integer earlyCount;
|
||||
|
||||
@ExcelProperty(value = "早退分钟", index = 7)
|
||||
@Schema(description = "早退分钟")
|
||||
private Integer earlyMinutes;
|
||||
|
||||
@Schema(description = "打卡列表")
|
||||
private List<HrmAttendanceClockRespVO> clockList;
|
||||
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics;
|
||||
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.clock.HrmAttendanceClockRespVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工每日打卡概况 Response VO")
|
||||
@Data
|
||||
public class HrmAttendanceDailyOverviewRespVO {
|
||||
|
||||
@Schema(description = "打卡记录")
|
||||
private List<HrmAttendanceClockRespVO> clocks;
|
||||
|
||||
@Schema(description = "考勤结果")
|
||||
private String attendanceResult;
|
||||
|
||||
@Schema(description = "打卡概况展示项")
|
||||
private List<OverviewItem> overviews;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工每日打卡概况展示项 Response VO")
|
||||
@Data
|
||||
public static class OverviewItem {
|
||||
|
||||
@Schema(description = "打卡类型")
|
||||
private String type;
|
||||
|
||||
@Schema(description = "打卡时间")
|
||||
private String time;
|
||||
|
||||
@Schema(description = "打卡状态")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "考勤结果")
|
||||
private String text;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Map;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工月度打卡概况 Response VO")
|
||||
@Data
|
||||
public class HrmAttendanceMonthDailyOverviewRespVO {
|
||||
|
||||
@Schema(description = "员工编号")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "员工姓名")
|
||||
private String employeeName;
|
||||
|
||||
@Schema(description = "工号")
|
||||
private String jobNumber;
|
||||
|
||||
@Schema(description = "部门编号")
|
||||
private Long deptId;
|
||||
|
||||
@Schema(description = "部门名称")
|
||||
private String deptName;
|
||||
|
||||
@Schema(description = "职位名称")
|
||||
private String postName;
|
||||
|
||||
@Schema(description = "年份")
|
||||
private Integer year;
|
||||
|
||||
@Schema(description = "月份")
|
||||
private Integer month;
|
||||
|
||||
@Schema(description = "每日打卡概况,Key 为日期")
|
||||
private Map<LocalDate, HrmAttendanceDailyOverviewRespVO> dailyClockMap;
|
||||
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics;
|
||||
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.leave.HrmAttendanceLeaveRespVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工月度考勤详情 Response VO")
|
||||
@Data
|
||||
public class HrmAttendanceMonthDetailRespVO {
|
||||
|
||||
@Schema(description = "月度汇总")
|
||||
private HrmAttendanceMonthRecordRespVO summary;
|
||||
|
||||
@Schema(description = "每日打卡明细")
|
||||
private List<HrmAttendanceDailyDetailRespVO> dailyDetails;
|
||||
|
||||
@Schema(description = "请假记录")
|
||||
private List<HrmAttendanceLeaveRespVO> leaves;
|
||||
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工月度考勤汇总分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class HrmAttendanceMonthRecordPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "员工姓名或工号,模糊匹配", example = "张三")
|
||||
private String search;
|
||||
|
||||
@Schema(description = "员工编号", example = "1024")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "部门编号数组", example = "100,101")
|
||||
private List<Long> deptIds;
|
||||
|
||||
@Schema(description = "年份", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026")
|
||||
@NotNull(message = "年份不能为空")
|
||||
private Integer year;
|
||||
|
||||
@Schema(description = "月份", requiredMode = Schema.RequiredMode.REQUIRED, example = "7")
|
||||
@NotNull(message = "月份不能为空")
|
||||
@Min(value = 1, message = "月份必须在 1 到 12 之间")
|
||||
@Max(value = 12, message = "月份必须在 1 到 12 之间")
|
||||
private Integer month;
|
||||
|
||||
@Schema(description = "是否全勤", example = "true")
|
||||
private Boolean fullAttendance;
|
||||
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工月度考勤汇总 Response VO")
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class HrmAttendanceMonthRecordRespVO {
|
||||
|
||||
@ExcelProperty(value = "员工编号", index = 0)
|
||||
@Schema(description = "员工编号")
|
||||
private Long employeeId;
|
||||
|
||||
@ExcelProperty(value = "员工姓名", index = 1)
|
||||
@Schema(description = "员工姓名")
|
||||
private String employeeName;
|
||||
|
||||
@ExcelProperty(value = "工号", index = 2)
|
||||
@Schema(description = "工号")
|
||||
private String jobNumber;
|
||||
|
||||
@Schema(description = "部门编号")
|
||||
private Long deptId;
|
||||
|
||||
@ExcelProperty(value = "部门", index = 3)
|
||||
@Schema(description = "部门名称")
|
||||
private String deptName;
|
||||
|
||||
@Schema(description = "职位名称")
|
||||
@ExcelProperty(value = "岗位", index = 4)
|
||||
private String postName;
|
||||
|
||||
@Schema(description = "考勤组名称")
|
||||
private String attendanceGroupName;
|
||||
|
||||
@Schema(description = "入职时间")
|
||||
private LocalDateTime entryTime;
|
||||
|
||||
@Schema(description = "员工状态")
|
||||
private Integer employeeStatus;
|
||||
|
||||
@Schema(description = "工作城市")
|
||||
private String workCity;
|
||||
|
||||
@ExcelProperty(value = "年份", index = 5)
|
||||
@Schema(description = "年份")
|
||||
private Integer year;
|
||||
|
||||
@ExcelProperty(value = "月份", index = 6)
|
||||
@Schema(description = "月份")
|
||||
private Integer month;
|
||||
|
||||
@ExcelProperty(value = "应出勤天数", index = 7)
|
||||
@Schema(description = "应出勤天数")
|
||||
private Integer attendDays;
|
||||
|
||||
@ExcelProperty(value = "实际出勤天数", index = 8)
|
||||
@Schema(description = "实际出勤天数")
|
||||
private BigDecimal actualDays;
|
||||
|
||||
@ExcelProperty(value = "迟到时长(分钟)", index = 9)
|
||||
@Schema(description = "迟到时长(分钟)")
|
||||
private Integer lateMinute;
|
||||
|
||||
@ExcelProperty(value = "迟到次数", index = 10)
|
||||
@Schema(description = "迟到次数")
|
||||
private Integer lateCount;
|
||||
|
||||
@ExcelProperty(value = "早退时长(分钟)", index = 11)
|
||||
@Schema(description = "早退时长(分钟)")
|
||||
private Integer earlyMinute;
|
||||
|
||||
@ExcelProperty(value = "早退次数", index = 12)
|
||||
@Schema(description = "早退次数")
|
||||
private Integer earlyCount;
|
||||
|
||||
@ExcelProperty(value = "缺卡次数", index = 13)
|
||||
@Schema(description = "缺卡次数")
|
||||
private Integer misscardCount;
|
||||
|
||||
@ExcelProperty(value = "旷工天数", index = 14)
|
||||
@Schema(description = "旷工天数")
|
||||
private BigDecimal absenteeismDays;
|
||||
|
||||
@ExcelProperty(value = "旷工时长(分钟)", index = 15)
|
||||
@Schema(description = "旷工时长(分钟)")
|
||||
private Integer absenteeismMinutes;
|
||||
|
||||
@ExcelProperty(value = "请假天数", index = 16)
|
||||
@Schema(description = "请假天数")
|
||||
private BigDecimal leaveDays;
|
||||
|
||||
@ExcelProperty(value = "请假时长(分钟)", index = 17)
|
||||
@Schema(description = "请假时长(分钟)")
|
||||
private Integer leaveMinutes;
|
||||
|
||||
@ExcelProperty(value = "迟到扣款", index = 18)
|
||||
@Schema(description = "迟到扣款")
|
||||
private BigDecimal lateDeductAmount;
|
||||
|
||||
@ExcelProperty(value = "早退扣款", index = 19)
|
||||
@Schema(description = "早退扣款")
|
||||
private BigDecimal earlyDeductAmount;
|
||||
|
||||
@ExcelProperty(value = "缺卡扣款", index = 20)
|
||||
@Schema(description = "缺卡扣款")
|
||||
private BigDecimal misscardDeductAmount;
|
||||
|
||||
@ExcelProperty(value = "旷工扣款", index = 21)
|
||||
@Schema(description = "旷工扣款")
|
||||
private BigDecimal absenteeismDeductAmount;
|
||||
|
||||
@ExcelProperty(value = "考勤扣款合计", index = 22)
|
||||
@Schema(description = "考勤扣款合计")
|
||||
private BigDecimal attendanceDeductAmount;
|
||||
|
||||
@Schema(description = "是否全勤")
|
||||
private Boolean fullAttendance;
|
||||
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.certificate.HrmEmployeeCertificateRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.certificate.HrmEmployeeCertificateSaveReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.experience.HrmEmployeeCertificateDO;
|
||||
import cn.iocoder.yudao.module.hrm.service.employee.experience.HrmEmployeeCertificateService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - HRM 员工证书")
|
||||
@RestController
|
||||
@RequestMapping("/hrm/employee/certificate")
|
||||
@Validated
|
||||
public class HrmEmployeeCertificateController {
|
||||
|
||||
@Resource
|
||||
private HrmEmployeeCertificateService certificateService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得员工证书列表")
|
||||
@Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:query')")
|
||||
public CommonResult<List<HrmEmployeeCertificateRespVO>> getCertificateList(
|
||||
@RequestParam("employeeId") Long employeeId) {
|
||||
List<HrmEmployeeCertificateDO> certificates = certificateService.getCertificateListByEmployeeId(employeeId);
|
||||
return success(BeanUtils.toBean(certificates, HrmEmployeeCertificateRespVO.class));
|
||||
}
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建员工证书")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:update')")
|
||||
public CommonResult<Long> createCertificate(
|
||||
@Valid @RequestBody HrmEmployeeCertificateSaveReqVO reqVO) {
|
||||
return success(certificateService.createCertificate(reqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新员工证书")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:update')")
|
||||
public CommonResult<Boolean> updateCertificate(
|
||||
@Valid @RequestBody HrmEmployeeCertificateSaveReqVO reqVO) {
|
||||
certificateService.updateCertificate(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除员工证书")
|
||||
@Parameter(name = "id", description = "证书编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:delete')")
|
||||
public CommonResult<Boolean> deleteCertificate(@RequestParam("id") Long id) {
|
||||
certificateService.deleteCertificate(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.changerecord.HrmEmployeeChangeRecordRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.employment.HrmEmployeeChangeRecordDO;
|
||||
import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO;
|
||||
import cn.iocoder.yudao.module.hrm.service.employee.employment.HrmEmployeeChangeRecordService;
|
||||
import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
|
||||
|
||||
@Tag(name = "管理后台 - HRM 员工异动记录")
|
||||
@RestController
|
||||
@RequestMapping("/hrm/employee/change-record")
|
||||
@Validated
|
||||
public class HrmEmployeeChangeRecordController {
|
||||
|
||||
@Resource
|
||||
private HrmEmployeeChangeRecordService changeRecordService;
|
||||
@Resource
|
||||
private HrmEmployeeService employeeService;
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得员工异动记录列表")
|
||||
@Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:query')")
|
||||
public CommonResult<List<HrmEmployeeChangeRecordRespVO>> getEmployeeChangeRecordList(
|
||||
@RequestParam("employeeId") Long employeeId) {
|
||||
return success(buildEmployeeChangeRecordRespVOList(
|
||||
changeRecordService.getEmployeeChangeRecordListByEmployeeId(employeeId)));
|
||||
}
|
||||
|
||||
// ==================== 拼接 VO ====================
|
||||
|
||||
private List<HrmEmployeeChangeRecordRespVO> buildEmployeeChangeRecordRespVOList(
|
||||
List<HrmEmployeeChangeRecordDO> changeRecords) {
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
|
||||
convertSetByFlatMap(changeRecords, changeRecord -> Stream.of(changeRecord.getOldDeptId(), changeRecord.getNewDeptId())));
|
||||
Map<Long, HrmEmployeeDO> leaderEmployeeMap = employeeService.getEmployeeMap(
|
||||
convertSetByFlatMap(changeRecords, changeRecord -> Stream.of(changeRecord.getOldLeaderEmployeeId(), changeRecord.getNewLeaderEmployeeId())));
|
||||
return BeanUtils.toBean(changeRecords, HrmEmployeeChangeRecordRespVO.class, changeRecord -> {
|
||||
MapUtils.findAndThen(deptMap, changeRecord.getOldDeptId(),
|
||||
dept -> changeRecord.setOldDeptName(dept.getName()));
|
||||
MapUtils.findAndThen(deptMap, changeRecord.getNewDeptId(),
|
||||
dept -> changeRecord.setNewDeptName(dept.getName()));
|
||||
MapUtils.findAndThen(leaderEmployeeMap, changeRecord.getOldLeaderEmployeeId(),
|
||||
leader -> changeRecord.setOldLeaderEmployeeName(leader.getName()));
|
||||
MapUtils.findAndThen(leaderEmployeeMap, changeRecord.getNewLeaderEmployeeId(),
|
||||
leader -> changeRecord.setNewLeaderEmployeeName(leader.getName()));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.contact.HrmEmployeeContactRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.contact.HrmEmployeeContactSaveReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeContactDO;
|
||||
import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeContactService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - HRM 员工联系人")
|
||||
@RestController
|
||||
@RequestMapping("/hrm/employee/contact")
|
||||
@Validated
|
||||
public class HrmEmployeeContactController {
|
||||
|
||||
@Resource
|
||||
private HrmEmployeeContactService contactService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得员工联系人列表")
|
||||
@Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:query')")
|
||||
public CommonResult<List<HrmEmployeeContactRespVO>> getContactList(
|
||||
@RequestParam("employeeId") Long employeeId) {
|
||||
List<HrmEmployeeContactDO> contacts = contactService.getContactListByEmployeeId(employeeId);
|
||||
return success(BeanUtils.toBean(contacts, HrmEmployeeContactRespVO.class));
|
||||
}
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建员工联系人")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:update')")
|
||||
public CommonResult<Long> createContact(@Valid @RequestBody HrmEmployeeContactSaveReqVO reqVO) {
|
||||
return success(contactService.createContact(reqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新员工联系人")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:update')")
|
||||
public CommonResult<Boolean> updateContact(@Valid @RequestBody HrmEmployeeContactSaveReqVO reqVO) {
|
||||
contactService.updateContact(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除员工联系人")
|
||||
@Parameter(name = "id", description = "联系人编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:delete')")
|
||||
public CommonResult<Boolean> deleteContact(@RequestParam("id") Long id) {
|
||||
contactService.deleteContact(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.contract.HrmEmployeeContractRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.contract.HrmEmployeeContractSaveReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.employment.HrmEmployeeContractDO;
|
||||
import cn.iocoder.yudao.module.hrm.service.employee.employment.HrmEmployeeContractService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - HRM 员工合同")
|
||||
@RestController
|
||||
@RequestMapping("/hrm/employee/contract")
|
||||
@Validated
|
||||
public class HrmEmployeeContractController {
|
||||
|
||||
@Resource
|
||||
private HrmEmployeeContractService contractService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得员工合同列表")
|
||||
@Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:query')")
|
||||
public CommonResult<List<HrmEmployeeContractRespVO>> getContractList(
|
||||
@RequestParam("employeeId") Long employeeId) {
|
||||
List<HrmEmployeeContractDO> contracts = contractService.getContractListByEmployeeId(employeeId);
|
||||
return success(BeanUtils.toBean(contracts, HrmEmployeeContractRespVO.class));
|
||||
}
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建员工合同")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:update')")
|
||||
public CommonResult<Long> createContract(@Valid @RequestBody HrmEmployeeContractSaveReqVO reqVO) {
|
||||
return success(contractService.createContract(reqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新员工合同")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:update')")
|
||||
public CommonResult<Boolean> updateContract(@Valid @RequestBody HrmEmployeeContractSaveReqVO reqVO) {
|
||||
contractService.updateContract(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除员工合同")
|
||||
@Parameter(name = "id", description = "合同编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:delete')")
|
||||
public CommonResult<Boolean> deleteContract(@RequestParam("id") Long id) {
|
||||
contractService.deleteContract(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
+477
@@ -0,0 +1,477 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.changerecord.HrmEmployeeConvertToFullTimeReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.changerecord.HrmEmployeeDemoteReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.changerecord.HrmEmployeePromoteReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.changerecord.HrmEmployeeRegularReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.changerecord.HrmEmployeeTransferReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeCancelQuitReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeConfirmEntryReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeCreateFromUserReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeDeptStatisticsRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeImportExcelVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeImportRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeListReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeePageReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeNotifyRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeQuitReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeRehireReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeSaveReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeStatusCountRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO;
|
||||
import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.employment.HrmEmployeeSalaryCardDO;
|
||||
import cn.iocoder.yudao.module.hrm.dal.dataobject.insurance.employee.HrmInsuranceEmployeeInfoDO;
|
||||
import cn.iocoder.yudao.module.hrm.dal.dataobject.recruit.config.HrmRecruitChannelDO;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeEntryStatusEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeStatusEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeStatusTabEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeTypeEnum;
|
||||
import cn.iocoder.yudao.module.hrm.service.employee.employment.HrmEmployeeSalaryCardService;
|
||||
import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService;
|
||||
import cn.iocoder.yudao.module.hrm.service.insurance.employee.HrmInsuranceEmployeeInfoService;
|
||||
import cn.iocoder.yudao.module.hrm.service.recruit.config.HrmRecruitChannelService;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Parameters;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getFirst;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.sum;
|
||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.getYearsBetween;
|
||||
|
||||
@Tag(name = "管理后台 - HRM 员工档案")
|
||||
@RestController
|
||||
@RequestMapping("/hrm/employee")
|
||||
@Validated
|
||||
public class HrmEmployeeController {
|
||||
|
||||
@Resource
|
||||
private HrmEmployeeService employeeService;
|
||||
@Resource
|
||||
private HrmRecruitChannelService recruitChannelService;
|
||||
@Resource
|
||||
private HrmInsuranceEmployeeInfoService insuranceEmployeeInfoService;
|
||||
@Resource
|
||||
private HrmEmployeeSalaryCardService employeeSalaryCardService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建员工档案")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:create')")
|
||||
public CommonResult<Long> createEmployee(@Valid @RequestBody HrmEmployeeSaveReqVO createReqVO) {
|
||||
return success(employeeService.createEmployee(createReqVO));
|
||||
}
|
||||
|
||||
@PostMapping("/create-list")
|
||||
@Operation(summary = "从未建档后台用户批量创建员工档案")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:create')")
|
||||
public CommonResult<List<Long>> createEmployeeList(
|
||||
@RequestBody @NotEmpty(message = "待创建员工列表不能为空")
|
||||
@Size(max = 100, message = "单次最多创建 100 名员工")
|
||||
List<@Valid HrmEmployeeCreateFromUserReqVO> createReqVOList) {
|
||||
return success(employeeService.createEmployeeList(createReqVOList));
|
||||
}
|
||||
|
||||
@GetMapping("/bound-user-id-list")
|
||||
@Operation(summary = "获得已经建立员工档案的后台用户编号列表")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:create')")
|
||||
public CommonResult<List<Long>> getBoundUserIdList() {
|
||||
return success(employeeService.getBoundUserIdList());
|
||||
}
|
||||
|
||||
@PostMapping("/send-profile-fill-message")
|
||||
@Operation(summary = "发送填写员工档案通知")
|
||||
@Parameter(name = "ids", description = "员工编号数组", required = true, example = "1024,1025")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:update')")
|
||||
public CommonResult<HrmEmployeeNotifyRespVO> sendEmployeeProfileFillMessage(
|
||||
@RequestParam("ids") @NotEmpty(message = "员工不能为空")
|
||||
@Size(max = 100, message = "单次最多通知 100 名员工") List<Long> ids) {
|
||||
return success(employeeService.sendEmployeeProfileFillMessage(ids));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新员工档案")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:update')")
|
||||
public CommonResult<Boolean> updateEmployee(@Valid @RequestBody HrmEmployeeSaveReqVO updateReqVO) {
|
||||
employeeService.updateEmployee(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除员工档案")
|
||||
@Parameter(name = "id", description = "员工档案编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:delete')")
|
||||
public CommonResult<Boolean> deleteEmployee(@RequestParam("id") Long id) {
|
||||
employeeService.deleteEmployee(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@Operation(summary = "批量删除员工档案")
|
||||
@Parameter(name = "ids", description = "员工档案编号数组", required = true, example = "1024,1025")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:delete')")
|
||||
public CommonResult<Boolean> deleteEmployeeList(@RequestParam("ids") @NotEmpty List<Long> ids) {
|
||||
employeeService.deleteEmployeeList(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得员工档案")
|
||||
@Parameter(name = "id", description = "员工档案编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:query')")
|
||||
public CommonResult<HrmEmployeeRespVO> getEmployee(@RequestParam("id") Long id) {
|
||||
return success(buildEmployeeRespVO(employeeService.getEmployee(id)));
|
||||
}
|
||||
|
||||
@PutMapping("/confirm-entry")
|
||||
@Operation(summary = "确认员工入职")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:update')")
|
||||
public CommonResult<Boolean> confirmEmployeeEntry(@Valid @RequestBody HrmEmployeeConfirmEntryReqVO reqVO) {
|
||||
employeeService.confirmEmployeeEntry(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/rehire")
|
||||
@Operation(summary = "办理离职员工再入职")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:update')")
|
||||
public CommonResult<Boolean> rehireEmployee(@Valid @RequestBody HrmEmployeeRehireReqVO reqVO) {
|
||||
employeeService.rehireEmployee(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/regular")
|
||||
@Operation(summary = "办理员工转正")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:update')")
|
||||
public CommonResult<Boolean> regularEmployee(@Valid @RequestBody HrmEmployeeRegularReqVO reqVO) {
|
||||
employeeService.regularEmployee(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/transfer")
|
||||
@Operation(summary = "办理员工调岗")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:update')")
|
||||
public CommonResult<Boolean> transferEmployee(@Valid @RequestBody HrmEmployeeTransferReqVO reqVO) {
|
||||
employeeService.transferEmployee(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/promote")
|
||||
@Operation(summary = "办理员工晋升")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:update')")
|
||||
public CommonResult<Boolean> promoteEmployee(@Valid @RequestBody HrmEmployeePromoteReqVO reqVO) {
|
||||
employeeService.promoteEmployee(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/demote")
|
||||
@Operation(summary = "办理员工降级")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:update')")
|
||||
public CommonResult<Boolean> demoteEmployee(@Valid @RequestBody HrmEmployeeDemoteReqVO reqVO) {
|
||||
employeeService.demoteEmployee(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/convert-to-full-time")
|
||||
@Operation(summary = "办理员工转为全职")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:update')")
|
||||
public CommonResult<Boolean> convertEmployeeToFullTime(
|
||||
@Valid @RequestBody HrmEmployeeConvertToFullTimeReqVO reqVO) {
|
||||
employeeService.convertEmployeeToFullTime(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/quit")
|
||||
@Operation(summary = "办理员工离职")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:update')")
|
||||
public CommonResult<Boolean> quitEmployee(@Valid @RequestBody HrmEmployeeQuitReqVO reqVO) {
|
||||
employeeService.quitEmployee(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/cancel-quit")
|
||||
@Operation(summary = "取消员工离职")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:update')")
|
||||
public CommonResult<Boolean> cancelEmployeeQuit(@Valid @RequestBody HrmEmployeeCancelQuitReqVO reqVO) {
|
||||
employeeService.cancelEmployeeQuit(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得员工档案分页")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:query')")
|
||||
public CommonResult<PageResult<HrmEmployeeRespVO>> getEmployeePage(@Validated HrmEmployeePageReqVO pageReqVO) {
|
||||
PageResult<HrmEmployeeDO> pageResult = employeeService.getEmployeePage(pageReqVO);
|
||||
return success(new PageResult<>(buildEmployeeRespVOList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得员工列表", description = "用于表单回显已选员工")
|
||||
@Parameter(name = "ids", description = "员工编号列表", required = true, example = "[1024]")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:query')")
|
||||
public CommonResult<List<HrmEmployeeRespVO>> getEmployeeList(@RequestParam("ids") @NotEmpty List<Long> ids) {
|
||||
return success(buildEmployeeRespVOList(employeeService.getEmployeeListByIds(ids)));
|
||||
}
|
||||
|
||||
@GetMapping("/simple-page")
|
||||
@Operation(summary = "获得员工精简分页", description = "只返回员工选择器需要的非敏感字段")
|
||||
public CommonResult<PageResult<HrmEmployeeRespVO>> getEmployeeSimplePage(
|
||||
@Validated HrmEmployeePageReqVO pageReqVO) {
|
||||
PageResult<HrmEmployeeDO> pageResult = employeeService.getEmployeePage(pageReqVO);
|
||||
return success(new PageResult<>(buildEmployeeSimpleRespVOList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/simple-list")
|
||||
@Operation(summary = "获得员工精简列表", description = "用于员工选择器回显已选员工")
|
||||
@Parameter(name = "ids", description = "员工编号列表", required = true, example = "[1024]")
|
||||
public CommonResult<List<HrmEmployeeRespVO>> getEmployeeSimpleList(
|
||||
@RequestParam("ids") @NotEmpty List<Long> ids) {
|
||||
return success(buildEmployeeSimpleRespVOList(employeeService.getEmployeeListByIds(ids)));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出员工档案")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:export')")
|
||||
@ApiAccessLog(operateType = OperateTypeEnum.EXPORT)
|
||||
public void exportEmployee(@Validated HrmEmployeePageReqVO exportReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
List<HrmEmployeeRespVO> list = buildEmployeeExportRespVOList(employeeService.getEmployeeList(
|
||||
BeanUtils.toBean(exportReqVO, HrmEmployeeListReqVO.class)));
|
||||
ExcelUtils.write(response, "员工档案.xlsx", "数据", HrmEmployeeRespVO.class, list);
|
||||
}
|
||||
|
||||
@GetMapping("/get-import-template")
|
||||
@Operation(summary = "获得员工档案导入模板")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:import')")
|
||||
@ApiAccessLog(operateType = OperateTypeEnum.EXPORT)
|
||||
public void importTemplate(HttpServletResponse response) throws IOException {
|
||||
List<HrmEmployeeImportExcelVO> list = Collections.singletonList(
|
||||
HrmEmployeeImportExcelVO.builder()
|
||||
.name("张三").jobNumber("HRM001").mobile("15601691300")
|
||||
.country("中国").nation("汉族").idType(5).idNumber("OTHER-19900101")
|
||||
.sex(1).email("hrm@example.com").nativePlace("浙江杭州")
|
||||
.birthday(LocalDateTime.of(1990, 1, 1, 0, 0))
|
||||
.address("杭州市西湖区").highestEducation(8)
|
||||
.deptId(1L).leaderJobNumber("HRM000")
|
||||
.postName("Java 工程师").postLevel("P6")
|
||||
.entryStatus(HrmEmployeeEntryStatusEnum.ACTIVE.getStatus())
|
||||
.status(HrmEmployeeStatusEnum.REGULAR.getStatus())
|
||||
.type(HrmEmployeeTypeEnum.FORMAL.getType()).probation(3)
|
||||
.entryTime(LocalDateTime.of(2026, 7, 9, 9, 0))
|
||||
.regularTime(LocalDateTime.of(2026, 10, 9, 9, 0))
|
||||
.workCity("杭州").workAddress("西湖区").workDetailAddress("文三路")
|
||||
.companyAgeStartTime(LocalDateTime.of(2026, 7, 9, 9, 0))
|
||||
.userMobile("15601691301")
|
||||
.bankCardNumber("622202600001").bankAreaId(330100).bankName("招商银行")
|
||||
.bankBranchName("杭州高新支行").firstSocialSecurity(false)
|
||||
.firstAccumulationFund(false).socialSecurityNumber("SB20260001")
|
||||
.accumulationFundNumber("GJJ20260001")
|
||||
.socialSecurityStartMonth(LocalDateTime.of(2026, 7, 1, 0, 0))
|
||||
.schemeName("杭州标准参保方案")
|
||||
.remark("示例员工").build()
|
||||
);
|
||||
ExcelUtils.write(response, "员工档案导入模板.xlsx", "员工列表", HrmEmployeeImportExcelVO.class, list);
|
||||
}
|
||||
|
||||
@PostMapping("/import")
|
||||
@Operation(summary = "导入员工档案")
|
||||
@Parameters({
|
||||
@Parameter(name = "file", description = "Excel 文件", required = true),
|
||||
@Parameter(name = "duplicateStrategy", description = "重复员工处理策略:1 跳过,2 覆盖,3 判失败", example = "3")
|
||||
})
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:import')")
|
||||
@ApiAccessLog(operateType = OperateTypeEnum.IMPORT)
|
||||
public CommonResult<HrmEmployeeImportRespVO> importExcel(
|
||||
@RequestParam("file") MultipartFile file,
|
||||
@RequestParam(value = "duplicateStrategy", required = false, defaultValue = "3") Integer duplicateStrategy)
|
||||
throws Exception {
|
||||
List<HrmEmployeeImportExcelVO> list = ExcelUtils.read(file, HrmEmployeeImportExcelVO.class);
|
||||
return success(employeeService.importEmployeeList(list, duplicateStrategy));
|
||||
}
|
||||
|
||||
@GetMapping("/status-count")
|
||||
@Operation(summary = "获得员工状态统计")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:query')")
|
||||
public CommonResult<List<HrmEmployeeStatusCountRespVO>> getEmployeeStatusCount(
|
||||
@Validated HrmEmployeePageReqVO pageReqVO) {
|
||||
Map<Integer, Long> countMap = employeeService.getEmployeeStatusCount(pageReqVO);
|
||||
return success(convertList(Arrays.asList(HrmEmployeeStatusTabEnum.values()),
|
||||
status -> new HrmEmployeeStatusCountRespVO(status.getStatus(),
|
||||
countMap.getOrDefault(status.getStatus(), 0L))));
|
||||
}
|
||||
|
||||
@GetMapping("/dept-statistics")
|
||||
@Operation(summary = "获得员工部门统计")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:query')")
|
||||
public CommonResult<List<HrmEmployeeDeptStatisticsRespVO>> getEmployeeDeptStatistics() {
|
||||
Map<Long, Map<Integer, Long>> countMap = employeeService.getEmployeeCountMapByDeptAndType();
|
||||
return success(convertList(countMap.entrySet(), entry -> {
|
||||
Map<Integer, Long> typeCountMap = entry.getValue();
|
||||
long activeCount = sum(typeCountMap.values(), Long::longValue);
|
||||
return new HrmEmployeeDeptStatisticsRespVO(entry.getKey(), activeCount,
|
||||
typeCountMap.getOrDefault(HrmEmployeeTypeEnum.FORMAL.getType(), 0L),
|
||||
typeCountMap.getOrDefault(HrmEmployeeTypeEnum.INFORMAL.getType(), 0L));
|
||||
}));
|
||||
}
|
||||
|
||||
// ==================== 拼接 VO ====================
|
||||
|
||||
private HrmEmployeeRespVO buildEmployeeRespVO(HrmEmployeeDO employee) {
|
||||
if (employee == null) {
|
||||
return null;
|
||||
}
|
||||
return getFirst(buildEmployeeRespVOList(Collections.singletonList(employee)));
|
||||
}
|
||||
|
||||
private List<HrmEmployeeRespVO> buildEmployeeRespVOList(List<HrmEmployeeDO> list) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 1.1 后台用户信息
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
|
||||
convertSet(list, HrmEmployeeDO::getUserId));
|
||||
// 1.2 部门信息
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(list, HrmEmployeeDO::getDeptId));
|
||||
// 1.3 直属上级员工信息
|
||||
Map<Long, HrmEmployeeDO> leaderEmployeeMap = employeeService.getEmployeeMap(
|
||||
convertSet(list, HrmEmployeeDO::getLeaderEmployeeId));
|
||||
// 1.4 招聘渠道信息
|
||||
Map<Long, HrmRecruitChannelDO> channelMap = recruitChannelService.getRecruitChannelMap(
|
||||
convertSet(list, HrmEmployeeDO::getChannelId));
|
||||
// 1.5 工资卡和社保资料
|
||||
Set<Long> employeeIds = convertSet(list, HrmEmployeeDO::getId);
|
||||
Map<Long, HrmEmployeeSalaryCardDO> salaryCardMap =
|
||||
employeeSalaryCardService.getSalaryCardMap(employeeIds);
|
||||
Map<Long, HrmInsuranceEmployeeInfoDO> insuranceEmployeeInfoMap =
|
||||
insuranceEmployeeInfoService.getInsuranceEmployeeInfoMap(employeeIds);
|
||||
|
||||
// 2. 拼接响应
|
||||
LocalDate today = LocalDate.now();
|
||||
return BeanUtils.toBean(list, HrmEmployeeRespVO.class, vo -> {
|
||||
MapUtils.findAndThen(userMap, vo.getUserId(), user -> vo.setUserNickname(user.getNickname()));
|
||||
MapUtils.findAndThen(deptMap, vo.getDeptId(), dept -> vo.setDeptName(dept.getName()));
|
||||
MapUtils.findAndThen(leaderEmployeeMap, vo.getLeaderEmployeeId(),
|
||||
leader -> vo.setLeaderEmployeeName(leader.getName()));
|
||||
MapUtils.findAndThen(channelMap, vo.getChannelId(),
|
||||
channel -> vo.setChannelName(channel.getName()));
|
||||
MapUtils.findAndThen(salaryCardMap, vo.getId(), salaryCard -> vo
|
||||
.setSalaryCardNumber(salaryCard.getBankCardNumber())
|
||||
.setSalaryCardAreaId(salaryCard.getBankAreaId())
|
||||
.setSalaryCardAreaName(AreaUtils.format(salaryCard.getBankAreaId()))
|
||||
.setSalaryCardBankName(salaryCard.getBankName())
|
||||
.setSalaryCardBankBranchName(salaryCard.getBankBranchName()));
|
||||
MapUtils.findAndThen(insuranceEmployeeInfoMap, vo.getId(), socialSecurity -> vo
|
||||
.setSocialSecurityNumber(socialSecurity.getSocialSecurityNumber())
|
||||
.setAccumulationFundNumber(socialSecurity.getAccumulationFundNumber()));
|
||||
if (vo.getBirthday() != null) {
|
||||
vo.setAge(getYearsBetween(vo.getBirthday().toLocalDate(), today));
|
||||
}
|
||||
if (vo.getCompanyAgeStartTime() != null) {
|
||||
vo.setCompanyAge(getYearsBetween(vo.getCompanyAgeStartTime().toLocalDate(), today));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private List<HrmEmployeeRespVO> buildEmployeeSimpleRespVOList(List<HrmEmployeeDO> list) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 1. 查询部门和直属上级信息
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(list, HrmEmployeeDO::getDeptId));
|
||||
Map<Long, HrmEmployeeDO> leaderEmployeeMap = employeeService.getEmployeeMap(
|
||||
convertSet(list, HrmEmployeeDO::getLeaderEmployeeId));
|
||||
|
||||
// 2. 拼接精简响应
|
||||
return convertList(list, employee -> {
|
||||
HrmEmployeeRespVO vo = new HrmEmployeeRespVO()
|
||||
.setId(employee.getId()).setName(employee.getName())
|
||||
.setJobNumber(employee.getJobNumber()).setMobile(employee.getMobile())
|
||||
.setDeptId(employee.getDeptId()).setLeaderEmployeeId(employee.getLeaderEmployeeId())
|
||||
.setPostName(employee.getPostName()).setPostLevel(employee.getPostLevel())
|
||||
.setEntryStatus(employee.getEntryStatus()).setStatus(employee.getStatus());
|
||||
MapUtils.findAndThen(deptMap, employee.getDeptId(), dept -> vo.setDeptName(dept.getName()));
|
||||
MapUtils.findAndThen(leaderEmployeeMap, employee.getLeaderEmployeeId(),
|
||||
leader -> vo.setLeaderEmployeeName(leader.getName()));
|
||||
return vo;
|
||||
});
|
||||
}
|
||||
|
||||
private List<HrmEmployeeRespVO> buildEmployeeExportRespVOList(List<HrmEmployeeDO> list) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 1. 查询导出展示信息,不加载工资卡和社保等敏感聚合
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(list, HrmEmployeeDO::getUserId));
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(list, HrmEmployeeDO::getDeptId));
|
||||
Map<Long, HrmEmployeeDO> leaderEmployeeMap = employeeService.getEmployeeMap(
|
||||
convertSet(list, HrmEmployeeDO::getLeaderEmployeeId));
|
||||
Map<Long, HrmRecruitChannelDO> channelMap = recruitChannelService.getRecruitChannelMap(
|
||||
convertSet(list, HrmEmployeeDO::getChannelId));
|
||||
|
||||
// 2. 拼接安全导出字段
|
||||
return convertList(list, employee -> {
|
||||
HrmEmployeeRespVO vo = new HrmEmployeeRespVO()
|
||||
.setId(employee.getId()).setName(employee.getName())
|
||||
.setJobNumber(employee.getJobNumber()).setMobile(employee.getMobile())
|
||||
.setPostName(employee.getPostName()).setPostLevel(employee.getPostLevel())
|
||||
.setEntryStatus(employee.getEntryStatus()).setStatus(employee.getStatus()).setType(employee.getType())
|
||||
.setEntryTime(employee.getEntryTime()).setRegularTime(employee.getRegularTime())
|
||||
.setLeaveTime(employee.getLeaveTime())
|
||||
.setWorkCity(employee.getWorkCity()).setWorkAddress(employee.getWorkAddress());
|
||||
MapUtils.findAndThen(userMap, employee.getUserId(), user -> vo.setUserNickname(user.getNickname()));
|
||||
MapUtils.findAndThen(deptMap, employee.getDeptId(), dept -> vo.setDeptName(dept.getName()));
|
||||
MapUtils.findAndThen(leaderEmployeeMap, employee.getLeaderEmployeeId(),
|
||||
leader -> vo.setLeaderEmployeeName(leader.getName()));
|
||||
MapUtils.findAndThen(channelMap, employee.getChannelId(), channel -> vo.setChannelName(channel.getName()));
|
||||
return vo;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.educationexperience.HrmEmployeeEducationExperienceRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.educationexperience.HrmEmployeeEducationExperienceSaveReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.experience.HrmEmployeeEducationExperienceDO;
|
||||
import cn.iocoder.yudao.module.hrm.service.employee.experience.HrmEmployeeEducationExperienceService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - HRM 员工教育经历")
|
||||
@RestController
|
||||
@RequestMapping("/hrm/employee/education-experience")
|
||||
@Validated
|
||||
public class HrmEmployeeEducationExperienceController {
|
||||
|
||||
@Resource
|
||||
private HrmEmployeeEducationExperienceService educationExperienceService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得员工教育经历列表")
|
||||
@Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:query')")
|
||||
public CommonResult<List<HrmEmployeeEducationExperienceRespVO>> getEducationExperienceList(
|
||||
@RequestParam("employeeId") Long employeeId) {
|
||||
List<HrmEmployeeEducationExperienceDO> educationExperiences =
|
||||
educationExperienceService.getEducationExperienceListByEmployeeId(employeeId);
|
||||
return success(BeanUtils.toBean(educationExperiences, HrmEmployeeEducationExperienceRespVO.class));
|
||||
}
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建员工教育经历")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:update')")
|
||||
public CommonResult<Long> createEducationExperience(
|
||||
@Valid @RequestBody HrmEmployeeEducationExperienceSaveReqVO reqVO) {
|
||||
return success(educationExperienceService.createEducationExperience(reqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新员工教育经历")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:update')")
|
||||
public CommonResult<Boolean> updateEducationExperience(
|
||||
@Valid @RequestBody HrmEmployeeEducationExperienceSaveReqVO reqVO) {
|
||||
educationExperienceService.updateEducationExperience(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除员工教育经历")
|
||||
@Parameter(name = "id", description = "教育经历编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:delete')")
|
||||
public CommonResult<Boolean> deleteEducationExperience(@RequestParam("id") Long id) {
|
||||
educationExperienceService.deleteEducationExperience(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.config.HrmEmployeeArchiveFieldConfigSaveReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.config.HrmEmployeeCreateFieldConfigSaveReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.config.HrmEmployeeFieldConfigRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.service.employee.config.HrmEmployeeFieldConfigService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - HRM 员工字段配置")
|
||||
@RestController
|
||||
@RequestMapping("/hrm/employee/config")
|
||||
@Validated
|
||||
public class HrmEmployeeFieldConfigController {
|
||||
|
||||
@Resource
|
||||
private HrmEmployeeFieldConfigService employeeFieldConfigService;
|
||||
|
||||
@GetMapping("/create-field/list")
|
||||
@Operation(summary = "获得新建员工字段配置")
|
||||
@Parameter(name = "entryStatus", description = "入职状态:1 在职,2 待入职", required = true, example = "1")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:config:query') or @ss.hasPermission('hrm:employee:create')")
|
||||
public CommonResult<List<HrmEmployeeFieldConfigRespVO>> getEmployeeCreateFieldConfigList(
|
||||
@RequestParam("entryStatus") Integer entryStatus) {
|
||||
return success(employeeFieldConfigService.getEmployeeCreateFieldConfigList(entryStatus));
|
||||
}
|
||||
|
||||
@PutMapping("/create-field/save")
|
||||
@Operation(summary = "保存新建员工字段配置")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:config:update')")
|
||||
public CommonResult<Boolean> saveEmployeeCreateFieldConfig(
|
||||
@Valid @RequestBody HrmEmployeeCreateFieldConfigSaveReqVO reqVO) {
|
||||
employeeFieldConfigService.saveEmployeeCreateFieldConfig(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/archive-field/list")
|
||||
@Operation(summary = "获得员工档案字段配置")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:config:query')")
|
||||
public CommonResult<List<HrmEmployeeFieldConfigRespVO>> getEmployeeArchiveFieldConfigList() {
|
||||
return success(employeeFieldConfigService.getEmployeeArchiveFieldConfigList());
|
||||
}
|
||||
|
||||
@PutMapping("/archive-field/save")
|
||||
@Operation(summary = "保存员工档案字段配置")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:config:update')")
|
||||
public CommonResult<Boolean> saveEmployeeArchiveFieldConfig(
|
||||
@Valid @RequestBody HrmEmployeeArchiveFieldConfigSaveReqVO reqVO) {
|
||||
employeeFieldConfigService.saveEmployeeArchiveFieldConfig(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.file.HrmEmployeeFileRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.file.HrmEmployeeFileSaveReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeFileDO;
|
||||
import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeFileService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - HRM 员工材料附件")
|
||||
@RestController
|
||||
@RequestMapping("/hrm/employee/file")
|
||||
@Validated
|
||||
public class HrmEmployeeFileController {
|
||||
|
||||
@Resource
|
||||
private HrmEmployeeFileService employeeFileService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得员工材料附件列表")
|
||||
@Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:query')")
|
||||
public CommonResult<List<HrmEmployeeFileRespVO>> getEmployeeFileList(
|
||||
@RequestParam("employeeId") Long employeeId) {
|
||||
List<HrmEmployeeFileDO> employeeFiles = employeeFileService.getEmployeeFileList(employeeId);
|
||||
return success(BeanUtils.toBean(employeeFiles, HrmEmployeeFileRespVO.class));
|
||||
}
|
||||
|
||||
@PutMapping("/save")
|
||||
@Operation(summary = "保存员工材料附件")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:update')")
|
||||
public CommonResult<Boolean> saveEmployeeFiles(@Valid @RequestBody HrmEmployeeFileSaveReqVO reqVO) {
|
||||
employeeFileService.saveEmployeeFiles(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.personalnote.HrmEmployeePersonalNoteCreateReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeePersonalNoteService;
|
||||
import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - HRM 员工个人备忘")
|
||||
@RestController
|
||||
@RequestMapping("/hrm/employee/personal-note")
|
||||
@Validated
|
||||
public class HrmEmployeePersonalNoteController {
|
||||
|
||||
@Resource
|
||||
private HrmEmployeePersonalNoteService personalNoteService;
|
||||
@Resource
|
||||
private HrmEmployeeService employeeService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建员工个人备忘")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:personal-note:create')")
|
||||
public CommonResult<Long> createPersonalNote(
|
||||
@Valid @RequestBody HrmEmployeePersonalNoteCreateReqVO reqVO) {
|
||||
Long employeeId = employeeService.validateEmployeeBySelf(getLoginUserId()).getId();
|
||||
return success(personalNoteService.createPersonalNote(employeeId, reqVO));
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除员工个人备忘")
|
||||
@Parameter(name = "id", description = "员工个人备忘编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:personal-note:delete')")
|
||||
public CommonResult<Boolean> deletePersonalNote(@RequestParam("id") Long id) {
|
||||
Long employeeId = employeeService.validateEmployeeBySelf(getLoginUserId()).getId();
|
||||
personalNoteService.deletePersonalNote(employeeId, id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.quitinfo.HrmEmployeeQuitInfoRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.employment.HrmEmployeeQuitInfoDO;
|
||||
import cn.iocoder.yudao.module.hrm.service.employee.employment.HrmEmployeeQuitInfoService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - HRM 员工离职信息")
|
||||
@RestController
|
||||
@RequestMapping("/hrm/employee/quit-info")
|
||||
@Validated
|
||||
public class HrmEmployeeQuitInfoController {
|
||||
|
||||
@Resource
|
||||
private HrmEmployeeQuitInfoService quitInfoService;
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得员工离职信息")
|
||||
@Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:query')")
|
||||
public CommonResult<HrmEmployeeQuitInfoRespVO> getQuitInfo(
|
||||
@RequestParam("employeeId") Long employeeId) {
|
||||
HrmEmployeeQuitInfoDO quitInfo = quitInfoService.getQuitInfoByEmployeeId(employeeId);
|
||||
return success(BeanUtils.toBean(quitInfo, HrmEmployeeQuitInfoRespVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.salarycard.HrmEmployeeSalaryCardRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.salarycard.HrmEmployeeSalaryCardSaveReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.employment.HrmEmployeeSalaryCardDO;
|
||||
import cn.iocoder.yudao.module.hrm.service.employee.employment.HrmEmployeeSalaryCardService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - HRM 员工工资卡")
|
||||
@RestController
|
||||
@RequestMapping("/hrm/employee/salary-card")
|
||||
@Validated
|
||||
public class HrmEmployeeSalaryCardController {
|
||||
|
||||
@Resource
|
||||
private HrmEmployeeSalaryCardService salaryCardService;
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得员工工资卡")
|
||||
@Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:query')")
|
||||
public CommonResult<HrmEmployeeSalaryCardRespVO> getEmployeeSalaryCard(
|
||||
@RequestParam("employeeId") Long employeeId) {
|
||||
HrmEmployeeSalaryCardDO salaryCard = salaryCardService.getSalaryCardByEmployeeId(employeeId);
|
||||
if (salaryCard == null) {
|
||||
return success(null);
|
||||
}
|
||||
HrmEmployeeSalaryCardRespVO respVO = BeanUtils.toBean(salaryCard, HrmEmployeeSalaryCardRespVO.class)
|
||||
.setBankAreaName(AreaUtils.format(salaryCard.getBankAreaId()));
|
||||
return success(respVO);
|
||||
}
|
||||
|
||||
@PutMapping("/save")
|
||||
@Operation(summary = "保存员工工资卡")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:update')")
|
||||
public CommonResult<Long> saveEmployeeSalaryCard(
|
||||
@Valid @RequestBody HrmEmployeeSalaryCardSaveReqVO reqVO) {
|
||||
return success(salaryCardService.saveSalaryCard(reqVO));
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除员工工资卡")
|
||||
@Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:update')")
|
||||
public CommonResult<Boolean> deleteEmployeeSalaryCard(@RequestParam("employeeId") Long employeeId) {
|
||||
salaryCardService.deleteSalaryCardByEmployeeId(employeeId);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.trainingexperience.HrmEmployeeTrainingExperienceRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.trainingexperience.HrmEmployeeTrainingExperienceSaveReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.experience.HrmEmployeeTrainingExperienceDO;
|
||||
import cn.iocoder.yudao.module.hrm.service.employee.experience.HrmEmployeeTrainingExperienceService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - HRM 员工培训经历")
|
||||
@RestController
|
||||
@RequestMapping("/hrm/employee/training-experience")
|
||||
@Validated
|
||||
public class HrmEmployeeTrainingExperienceController {
|
||||
|
||||
@Resource
|
||||
private HrmEmployeeTrainingExperienceService trainingExperienceService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得员工培训经历列表")
|
||||
@Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:query')")
|
||||
public CommonResult<List<HrmEmployeeTrainingExperienceRespVO>> getTrainingExperienceList(
|
||||
@RequestParam("employeeId") Long employeeId) {
|
||||
List<HrmEmployeeTrainingExperienceDO> trainingExperiences =
|
||||
trainingExperienceService.getTrainingExperienceListByEmployeeId(employeeId);
|
||||
return success(BeanUtils.toBean(trainingExperiences, HrmEmployeeTrainingExperienceRespVO.class));
|
||||
}
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建员工培训经历")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:update')")
|
||||
public CommonResult<Long> createTrainingExperience(
|
||||
@Valid @RequestBody HrmEmployeeTrainingExperienceSaveReqVO reqVO) {
|
||||
return success(trainingExperienceService.createTrainingExperience(reqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新员工培训经历")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:update')")
|
||||
public CommonResult<Boolean> updateTrainingExperience(
|
||||
@Valid @RequestBody HrmEmployeeTrainingExperienceSaveReqVO reqVO) {
|
||||
trainingExperienceService.updateTrainingExperience(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除员工培训经历")
|
||||
@Parameter(name = "id", description = "培训经历编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:delete')")
|
||||
public CommonResult<Boolean> deleteTrainingExperience(@RequestParam("id") Long id) {
|
||||
trainingExperienceService.deleteTrainingExperience(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.workexperience.HrmEmployeeWorkExperienceRespVO;
|
||||
import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.workexperience.HrmEmployeeWorkExperienceSaveReqVO;
|
||||
import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.experience.HrmEmployeeWorkExperienceDO;
|
||||
import cn.iocoder.yudao.module.hrm.service.employee.experience.HrmEmployeeWorkExperienceService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - HRM 员工工作经历")
|
||||
@RestController
|
||||
@RequestMapping("/hrm/employee/work-experience")
|
||||
@Validated
|
||||
public class HrmEmployeeWorkExperienceController {
|
||||
|
||||
@Resource
|
||||
private HrmEmployeeWorkExperienceService workExperienceService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得员工工作经历列表")
|
||||
@Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:query')")
|
||||
public CommonResult<List<HrmEmployeeWorkExperienceRespVO>> getWorkExperienceList(
|
||||
@RequestParam("employeeId") Long employeeId) {
|
||||
List<HrmEmployeeWorkExperienceDO> workExperiences =
|
||||
workExperienceService.getWorkExperienceListByEmployeeId(employeeId);
|
||||
return success(BeanUtils.toBean(workExperiences, HrmEmployeeWorkExperienceRespVO.class));
|
||||
}
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建员工工作经历")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:update')")
|
||||
public CommonResult<Long> createWorkExperience(
|
||||
@Valid @RequestBody HrmEmployeeWorkExperienceSaveReqVO reqVO) {
|
||||
return success(workExperienceService.createWorkExperience(reqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新员工工作经历")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:update')")
|
||||
public CommonResult<Boolean> updateWorkExperience(
|
||||
@Valid @RequestBody HrmEmployeeWorkExperienceSaveReqVO reqVO) {
|
||||
workExperienceService.updateWorkExperience(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除员工工作经历")
|
||||
@Parameter(name = "id", description = "工作经历编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('hrm:employee:delete')")
|
||||
public CommonResult<Boolean> deleteWorkExperience(@RequestParam("id") Long id) {
|
||||
workExperienceService.deleteWorkExperience(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.certificate;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工证书 Response VO")
|
||||
@Data
|
||||
public class HrmEmployeeCertificateRespVO {
|
||||
|
||||
@Schema(description = "证书编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "证书名称", example = "高级工程师")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "证书级别", example = "高级")
|
||||
private String level;
|
||||
|
||||
@Schema(description = "证书编号", example = "CERT-001")
|
||||
private String no;
|
||||
|
||||
@Schema(description = "有效起始日期")
|
||||
private LocalDateTime startTime;
|
||||
|
||||
@Schema(description = "有效结束日期")
|
||||
private LocalDateTime endTime;
|
||||
|
||||
@Schema(description = "发证机构", example = "人社局")
|
||||
private String issuingAuthority;
|
||||
|
||||
@Schema(description = "发证日期")
|
||||
private LocalDateTime issuingTime;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "排序", example = "1")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.certificate;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.mzt.logapi.starter.annotation.DiffLogField;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.isAfterOrEqual;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工证书保存 Request VO")
|
||||
@Data
|
||||
public class HrmEmployeeCertificateSaveReqVO {
|
||||
|
||||
@Schema(description = "证书编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "员工编号不能为空")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "证书名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "高级工程师")
|
||||
@NotBlank(message = "证书名称不能为空")
|
||||
@Size(max = 255, message = "证书名称长度不能超过 255 个字符")
|
||||
@DiffLogField(name = "证书名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "证书级别", example = "高级")
|
||||
@Size(max = 255, message = "证书级别长度不能超过 255 个字符")
|
||||
@DiffLogField(name = "证书级别")
|
||||
private String level;
|
||||
|
||||
@Schema(description = "证书编号", example = "CERT-001")
|
||||
@Size(max = 255, message = "证书编号长度不能超过 255 个字符")
|
||||
@DiffLogField(name = "证书编号")
|
||||
private String no;
|
||||
|
||||
@Schema(description = "有效起始日期")
|
||||
@DiffLogField(name = "有效起始日期")
|
||||
private LocalDateTime startTime;
|
||||
|
||||
@Schema(description = "有效结束日期")
|
||||
@DiffLogField(name = "有效结束日期")
|
||||
private LocalDateTime endTime;
|
||||
|
||||
@Schema(description = "发证机构", example = "人社局")
|
||||
@Size(max = 255, message = "发证机构长度不能超过 255 个字符")
|
||||
@DiffLogField(name = "发证机构")
|
||||
private String issuingAuthority;
|
||||
|
||||
@Schema(description = "发证日期")
|
||||
@DiffLogField(name = "发证日期")
|
||||
private LocalDateTime issuingTime;
|
||||
|
||||
@Schema(description = "备注", example = "专业技术资格证书")
|
||||
@Size(max = 500, message = "备注长度不能超过 500 个字符")
|
||||
@DiffLogField(name = "备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "排序", example = "1")
|
||||
@DiffLogField(name = "排序")
|
||||
private Integer sort;
|
||||
|
||||
@AssertTrue(message = "证书有效期的结束日期不能早于开始日期")
|
||||
@JsonIgnore
|
||||
public boolean isTimeRangeValid() {
|
||||
return startTime == null || endTime == null || isAfterOrEqual(endTime, startTime);
|
||||
}
|
||||
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.changerecord;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeChangeReasonEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeChangeTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工异动记录创建 Request VO")
|
||||
@Data
|
||||
public class HrmEmployeeChangeRecordCreateReqVO {
|
||||
|
||||
@Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "员工编号不能为空")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "异动类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
|
||||
@NotNull(message = "异动类型不能为空")
|
||||
@InEnum(value = HrmEmployeeChangeTypeEnum.class, message = "异动类型必须是 {value}")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "异动原因", example = "1")
|
||||
@InEnum(value = HrmEmployeeChangeReasonEnum.class, message = "异动原因必须是 {value}")
|
||||
private Integer reason;
|
||||
|
||||
@Schema(description = "原部门编号", example = "100")
|
||||
private Long oldDeptId;
|
||||
|
||||
@Schema(description = "原岗位名称", example = "Java 工程师")
|
||||
@Size(max = 255, message = "原岗位名称不能超过 255 个字符")
|
||||
private String oldPostName;
|
||||
|
||||
@Schema(description = "原职级", example = "P5")
|
||||
@Size(max = 255, message = "原职级不能超过 255 个字符")
|
||||
private String oldPostLevel;
|
||||
|
||||
@Schema(description = "原工作地点", example = "杭州")
|
||||
@Size(max = 255, message = "原工作地点不能超过 255 个字符")
|
||||
private String oldWorkAddress;
|
||||
|
||||
@Schema(description = "原直属上级员工编号", example = "2")
|
||||
private Long oldLeaderEmployeeId;
|
||||
|
||||
@Schema(description = "新部门编号", example = "200")
|
||||
private Long newDeptId;
|
||||
|
||||
@Schema(description = "新岗位名称", example = "高级 Java 工程师")
|
||||
@Size(max = 255, message = "新岗位名称不能超过 255 个字符")
|
||||
private String newPostName;
|
||||
|
||||
@Schema(description = "新职级", example = "P6")
|
||||
@Size(max = 255, message = "新职级不能超过 255 个字符")
|
||||
private String newPostLevel;
|
||||
|
||||
@Schema(description = "新工作地点", example = "上海")
|
||||
@Size(max = 255, message = "新工作地点不能超过 255 个字符")
|
||||
private String newWorkAddress;
|
||||
|
||||
@Schema(description = "新直属上级员工编号", example = "3")
|
||||
private Long newLeaderEmployeeId;
|
||||
|
||||
@Schema(description = "试用期,单位月", example = "3")
|
||||
@Min(value = 0, message = "试用期不能小于 0")
|
||||
@Max(value = 6, message = "试用期不能超过 6 个月")
|
||||
private Integer probation;
|
||||
|
||||
@Schema(description = "生效时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "生效时间不能为空")
|
||||
private LocalDateTime effectTime;
|
||||
|
||||
@Schema(description = "备注", example = "年度晋升")
|
||||
@Size(max = 500, message = "备注不能超过 500 个字符")
|
||||
private String remark;
|
||||
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.changerecord;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工异动记录 Response VO")
|
||||
@Data
|
||||
public class HrmEmployeeChangeRecordRespVO {
|
||||
|
||||
@Schema(description = "异动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "变动类型", example = "5")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "异动原因", example = "1")
|
||||
private Integer reason;
|
||||
|
||||
@Schema(description = "原部门编号", example = "100")
|
||||
private Long oldDeptId;
|
||||
|
||||
@Schema(description = "原部门名称", example = "研发部")
|
||||
private String oldDeptName;
|
||||
|
||||
@Schema(description = "新部门编号", example = "200")
|
||||
private Long newDeptId;
|
||||
|
||||
@Schema(description = "新部门名称", example = "产品部")
|
||||
private String newDeptName;
|
||||
|
||||
@Schema(description = "原岗位名称")
|
||||
private String oldPostName;
|
||||
|
||||
@Schema(description = "新岗位名称")
|
||||
private String newPostName;
|
||||
|
||||
@Schema(description = "原职级")
|
||||
private String oldPostLevel;
|
||||
|
||||
@Schema(description = "新职级")
|
||||
private String newPostLevel;
|
||||
|
||||
@Schema(description = "原工作地点")
|
||||
private String oldWorkAddress;
|
||||
|
||||
@Schema(description = "新工作地点")
|
||||
private String newWorkAddress;
|
||||
|
||||
@Schema(description = "原直属上级员工编号")
|
||||
private Long oldLeaderEmployeeId;
|
||||
|
||||
@Schema(description = "原直属上级员工姓名")
|
||||
private String oldLeaderEmployeeName;
|
||||
|
||||
@Schema(description = "新直属上级员工编号")
|
||||
private Long newLeaderEmployeeId;
|
||||
|
||||
@Schema(description = "新直属上级员工姓名")
|
||||
private String newLeaderEmployeeName;
|
||||
|
||||
@Schema(description = "试用期,单位月")
|
||||
private Integer probation;
|
||||
|
||||
@Schema(description = "生效日期")
|
||||
private LocalDateTime effectTime;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.changerecord;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeChangeReasonEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工转为全职 Request VO")
|
||||
@Data
|
||||
public class HrmEmployeeConvertToFullTimeReqVO {
|
||||
|
||||
@Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "员工编号不能为空")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "异动原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "异动原因不能为空")
|
||||
@InEnum(value = HrmEmployeeChangeReasonEnum.class, message = "异动原因必须是 {value}")
|
||||
private Integer reason;
|
||||
|
||||
@Schema(description = "试用期,单位:月;0 表示无试用期", requiredMode = Schema.RequiredMode.REQUIRED,
|
||||
example = "3")
|
||||
@NotNull(message = "试用期不能为空")
|
||||
@Min(value = 0, message = "试用期不能小于 0")
|
||||
@Max(value = 6, message = "试用期不能超过 6 个月")
|
||||
private Integer probation;
|
||||
|
||||
@Schema(description = "新部门编号;未填写表示不变", example = "200")
|
||||
private Long newDeptId;
|
||||
|
||||
@Schema(description = "新岗位名称;未填写表示不变", example = "Java 工程师")
|
||||
@Size(max = 255, message = "新岗位名称不能超过 255 个字符")
|
||||
private String newPostName;
|
||||
|
||||
@Schema(description = "新职级;未填写表示不变", example = "P6")
|
||||
@Size(max = 255, message = "新职级不能超过 255 个字符")
|
||||
private String newPostLevel;
|
||||
|
||||
@Schema(description = "新工作地点;未填写表示不变", example = "上海")
|
||||
@Size(max = 255, message = "新工作地点不能超过 255 个字符")
|
||||
private String newWorkAddress;
|
||||
|
||||
@Schema(description = "新直属上级员工编号;未填写表示不变", example = "3")
|
||||
private Long newLeaderEmployeeId;
|
||||
|
||||
@Schema(description = "生效日期", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "生效日期不能为空")
|
||||
private LocalDateTime effectTime;
|
||||
|
||||
@Schema(description = "备注", example = "转为全职员工")
|
||||
@Size(max = 500, message = "备注不能超过 500 个字符")
|
||||
private String remark;
|
||||
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.changerecord;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeChangeReasonEnum;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工降级 Request VO")
|
||||
@Data
|
||||
public class HrmEmployeeDemoteReqVO {
|
||||
|
||||
@Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "员工编号不能为空")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "异动原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "异动原因不能为空")
|
||||
@InEnum(value = HrmEmployeeChangeReasonEnum.class, message = "异动原因必须是 {value}")
|
||||
private Integer reason;
|
||||
|
||||
@Schema(description = "新部门编号", example = "200")
|
||||
private Long newDeptId;
|
||||
|
||||
@Schema(description = "新岗位名称", example = "Java 工程师")
|
||||
@Size(max = 255, message = "新岗位名称不能超过 255 个字符")
|
||||
private String newPostName;
|
||||
|
||||
@Schema(description = "新职级", example = "P5")
|
||||
@Size(max = 255, message = "新职级不能超过 255 个字符")
|
||||
private String newPostLevel;
|
||||
|
||||
@Schema(description = "新工作地点", example = "杭州")
|
||||
@Size(max = 255, message = "新工作地点不能超过 255 个字符")
|
||||
private String newWorkAddress;
|
||||
|
||||
@Schema(description = "新直属上级员工编号", example = "3")
|
||||
private Long newLeaderEmployeeId;
|
||||
|
||||
@Schema(description = "生效日期", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "生效日期不能为空")
|
||||
private LocalDateTime effectTime;
|
||||
|
||||
@Schema(description = "备注", example = "岗位职级调整")
|
||||
@Size(max = 500, message = "备注不能超过 500 个字符")
|
||||
private String remark;
|
||||
|
||||
@AssertTrue(message = "降级原因不合法")
|
||||
@JsonIgnore
|
||||
public boolean isReasonValid() {
|
||||
return reason == null || HrmEmployeeChangeReasonEnum.DEMOTION_REASONS.contains(reason);
|
||||
}
|
||||
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.changerecord;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeChangeReasonEnum;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工晋升 Request VO")
|
||||
@Data
|
||||
public class HrmEmployeePromoteReqVO {
|
||||
|
||||
@Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "员工编号不能为空")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "异动原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "异动原因不能为空")
|
||||
@InEnum(value = HrmEmployeeChangeReasonEnum.class, message = "异动原因必须是 {value}")
|
||||
private Integer reason;
|
||||
|
||||
@Schema(description = "新部门编号", example = "200")
|
||||
private Long newDeptId;
|
||||
|
||||
@Schema(description = "新岗位名称", example = "高级 Java 工程师")
|
||||
@Size(max = 255, message = "新岗位名称不能超过 255 个字符")
|
||||
private String newPostName;
|
||||
|
||||
@Schema(description = "新职级", example = "P6")
|
||||
@Size(max = 255, message = "新职级不能超过 255 个字符")
|
||||
private String newPostLevel;
|
||||
|
||||
@Schema(description = "新工作地点", example = "上海")
|
||||
@Size(max = 255, message = "新工作地点不能超过 255 个字符")
|
||||
private String newWorkAddress;
|
||||
|
||||
@Schema(description = "新直属上级员工编号", example = "3")
|
||||
private Long newLeaderEmployeeId;
|
||||
|
||||
@Schema(description = "生效日期", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "生效日期不能为空")
|
||||
private LocalDateTime effectTime;
|
||||
|
||||
@Schema(description = "备注", example = "年度晋升")
|
||||
@Size(max = 500, message = "备注不能超过 500 个字符")
|
||||
private String remark;
|
||||
|
||||
@AssertTrue(message = "晋升原因不合法")
|
||||
@JsonIgnore
|
||||
public boolean isReasonValid() {
|
||||
return reason == null || HrmEmployeeChangeReasonEnum.TRANSFER_AND_PROMOTION_REASONS.contains(reason);
|
||||
}
|
||||
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.changerecord;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeChangeReasonEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工转正 Request VO")
|
||||
@Data
|
||||
public class HrmEmployeeRegularReqVO {
|
||||
|
||||
@Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "员工编号不能为空")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "异动原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "异动原因不能为空")
|
||||
@InEnum(value = HrmEmployeeChangeReasonEnum.class, message = "异动原因必须是 {value}")
|
||||
private Integer reason;
|
||||
|
||||
@Schema(description = "新部门编号;未填写表示不变", example = "200")
|
||||
private Long newDeptId;
|
||||
|
||||
@Schema(description = "新岗位名称;未填写表示不变", example = "Java 工程师")
|
||||
@Size(max = 255, message = "新岗位名称不能超过 255 个字符")
|
||||
private String newPostName;
|
||||
|
||||
@Schema(description = "新职级;未填写表示不变", example = "P6")
|
||||
@Size(max = 255, message = "新职级不能超过 255 个字符")
|
||||
private String newPostLevel;
|
||||
|
||||
@Schema(description = "新工作地点;未填写表示不变", example = "上海")
|
||||
@Size(max = 255, message = "新工作地点不能超过 255 个字符")
|
||||
private String newWorkAddress;
|
||||
|
||||
@Schema(description = "新直属上级员工编号;未填写表示不变", example = "3")
|
||||
private Long newLeaderEmployeeId;
|
||||
|
||||
@Schema(description = "生效日期", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "生效日期不能为空")
|
||||
private LocalDateTime effectTime;
|
||||
|
||||
@Schema(description = "备注", example = "试用期考核通过")
|
||||
@Size(max = 500, message = "备注不能超过 500 个字符")
|
||||
private String remark;
|
||||
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.changerecord;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeChangeReasonEnum;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工调岗 Request VO")
|
||||
@Data
|
||||
public class HrmEmployeeTransferReqVO {
|
||||
|
||||
@Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "员工编号不能为空")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "异动原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "异动原因不能为空")
|
||||
@InEnum(value = HrmEmployeeChangeReasonEnum.class, message = "异动原因必须是 {value}")
|
||||
private Integer reason;
|
||||
|
||||
@Schema(description = "新部门编号", example = "200")
|
||||
private Long newDeptId;
|
||||
|
||||
@Schema(description = "新岗位名称", example = "产品经理")
|
||||
@Size(max = 255, message = "新岗位名称不能超过 255 个字符")
|
||||
private String newPostName;
|
||||
|
||||
@Schema(description = "新职级", example = "P6")
|
||||
@Size(max = 255, message = "新职级不能超过 255 个字符")
|
||||
private String newPostLevel;
|
||||
|
||||
@Schema(description = "新工作地点", example = "上海")
|
||||
@Size(max = 255, message = "新工作地点不能超过 255 个字符")
|
||||
private String newWorkAddress;
|
||||
|
||||
@Schema(description = "新直属上级员工编号", example = "3")
|
||||
private Long newLeaderEmployeeId;
|
||||
|
||||
@Schema(description = "生效日期", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "生效日期不能为空")
|
||||
private LocalDateTime effectTime;
|
||||
|
||||
@Schema(description = "备注", example = "业务调整")
|
||||
@Size(max = 500, message = "备注不能超过 500 个字符")
|
||||
private String remark;
|
||||
|
||||
@AssertTrue(message = "调岗原因不合法")
|
||||
@JsonIgnore
|
||||
public boolean isReasonValid() {
|
||||
return reason == null || HrmEmployeeChangeReasonEnum.TRANSFER_AND_PROMOTION_REASONS.contains(reason);
|
||||
}
|
||||
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.config;
|
||||
|
||||
import com.mzt.logapi.starter.annotation.DiffLogField;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工档案字段配置保存 Request VO")
|
||||
@Data
|
||||
public class HrmEmployeeArchiveFieldConfigSaveReqVO {
|
||||
|
||||
@Schema(description = "字段配置列表", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@Valid
|
||||
@NotEmpty(message = "字段配置列表不能为空")
|
||||
@DiffLogField(name = "字段配置")
|
||||
private List<HrmEmployeeFieldConfigSaveReqVO> fields;
|
||||
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeEntryStatusEnum;
|
||||
import com.mzt.logapi.starter.annotation.DiffLogField;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Range;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 新建员工字段配置保存 Request VO")
|
||||
@Data
|
||||
public class HrmEmployeeCreateFieldConfigSaveReqVO {
|
||||
|
||||
@Schema(description = "入职状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "入职状态不能为空")
|
||||
@InEnum(value = HrmEmployeeEntryStatusEnum.class, message = "入职状态必须是 {value}")
|
||||
@Range(min = 1, max = 2, message = "入职状态必须是 1 或 2")
|
||||
@DiffLogField(name = "入职状态")
|
||||
private Integer entryStatus;
|
||||
|
||||
@Schema(description = "字段配置列表", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@Valid
|
||||
@NotEmpty(message = "字段配置列表不能为空")
|
||||
@DiffLogField(name = "字段配置")
|
||||
private List<HrmEmployeeFieldConfigSaveReqVO> fields;
|
||||
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.config;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工字段配置 Response VO")
|
||||
@Data
|
||||
public class HrmEmployeeFieldConfigRespVO {
|
||||
|
||||
@Schema(description = "字段名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "name")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "字段标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "员工姓名")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "字段分组名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "基本信息")
|
||||
private String groupName;
|
||||
|
||||
@Schema(description = "是否显示", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
private Boolean visible;
|
||||
|
||||
@Schema(description = "是否允许员工编辑", example = "true")
|
||||
private Boolean editable;
|
||||
|
||||
@Schema(description = "是否锁定显示", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
|
||||
private Boolean visibleLocked;
|
||||
|
||||
@Schema(description = "是否锁定编辑", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
|
||||
private Boolean editableLocked;
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.config;
|
||||
|
||||
import com.mzt.logapi.starter.annotation.DiffLogField;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工字段配置保存 Request VO")
|
||||
@Data
|
||||
public class HrmEmployeeFieldConfigSaveReqVO {
|
||||
|
||||
@Schema(description = "字段名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "name")
|
||||
@NotBlank(message = "字段名称不能为空")
|
||||
@Size(max = 40, message = "字段名称不能超过 40 个字符")
|
||||
@DiffLogField(name = "字段名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "是否显示", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
@NotNull(message = "是否显示不能为空")
|
||||
@DiffLogField(name = "是否显示")
|
||||
private Boolean visible;
|
||||
|
||||
@Schema(description = "是否允许员工编辑", example = "true")
|
||||
@DiffLogField(name = "是否允许员工编辑")
|
||||
private Boolean editable;
|
||||
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.config;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工字段配置值 VO")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class HrmEmployeeFieldConfigValueVO {
|
||||
|
||||
@Schema(description = "字段名称", example = "mobile")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "是否显示", example = "true")
|
||||
private Boolean visible;
|
||||
|
||||
@Schema(description = "是否允许员工编辑", example = "true")
|
||||
private Boolean editable;
|
||||
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.contact;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工联系人 Response VO")
|
||||
@Data
|
||||
public class HrmEmployeeContactRespVO {
|
||||
|
||||
@Schema(description = "联系人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "联系人名称", example = "李四")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "关系", example = "配偶")
|
||||
private String relation;
|
||||
|
||||
@Schema(description = "联系人电话", example = "15601691301")
|
||||
private String phone;
|
||||
|
||||
@Schema(description = "联系人工作单位")
|
||||
private String workUnit;
|
||||
|
||||
@Schema(description = "联系人职务")
|
||||
private String postName;
|
||||
|
||||
@Schema(description = "联系人地址")
|
||||
private String address;
|
||||
|
||||
@Schema(description = "排序", example = "1")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.contact;
|
||||
|
||||
import com.mzt.logapi.starter.annotation.DiffLogField;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工联系人保存 Request VO")
|
||||
@Data
|
||||
public class HrmEmployeeContactSaveReqVO {
|
||||
|
||||
@Schema(description = "联系人编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "员工编号不能为空")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "联系人名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
|
||||
@NotBlank(message = "联系人名称不能为空")
|
||||
@Size(max = 64, message = "联系人名称长度不能超过 64 个字符")
|
||||
@DiffLogField(name = "联系人名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "关系", example = "配偶")
|
||||
@Size(max = 64, message = "关系长度不能超过 64 个字符")
|
||||
@DiffLogField(name = "关系")
|
||||
private String relation;
|
||||
|
||||
@Schema(description = "联系人电话", example = "15601691301")
|
||||
@Size(max = 40, message = "联系人电话长度不能超过 40 个字符")
|
||||
@DiffLogField(name = "联系人电话")
|
||||
private String phone;
|
||||
|
||||
@Schema(description = "联系人工作单位", example = "示例科技")
|
||||
@Size(max = 128, message = "联系人工作单位长度不能超过 128 个字符")
|
||||
@DiffLogField(name = "联系人工作单位")
|
||||
private String workUnit;
|
||||
|
||||
@Schema(description = "联系人职务", example = "产品经理")
|
||||
@Size(max = 128, message = "联系人职务长度不能超过 128 个字符")
|
||||
@DiffLogField(name = "联系人职务")
|
||||
private String postName;
|
||||
|
||||
@Schema(description = "联系人地址", example = "杭州市西湖区")
|
||||
@Size(max = 255, message = "联系人地址长度不能超过 255 个字符")
|
||||
@DiffLogField(name = "联系人地址")
|
||||
private String address;
|
||||
|
||||
@Schema(description = "排序", example = "1")
|
||||
@DiffLogField(name = "排序")
|
||||
private Integer sort;
|
||||
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.contract;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工合同 Response VO")
|
||||
@Data
|
||||
public class HrmEmployeeContractRespVO {
|
||||
|
||||
@Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "合同编号", example = "HT-2026-001")
|
||||
private String no;
|
||||
|
||||
@Schema(description = "合同类型", example = "1")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "合同开始日期")
|
||||
private LocalDateTime startTime;
|
||||
|
||||
@Schema(description = "合同结束日期")
|
||||
private LocalDateTime endTime;
|
||||
|
||||
@Schema(description = "期限", example = "36")
|
||||
private Integer term;
|
||||
|
||||
@Schema(description = "合同状态", example = "1")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "签约公司")
|
||||
private String signCompany;
|
||||
|
||||
@Schema(description = "合同签订日期")
|
||||
private LocalDateTime signTime;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "是否到期提醒", example = "true")
|
||||
private Boolean expireRemind;
|
||||
|
||||
@Schema(description = "附件地址数组")
|
||||
private List<String> fileUrls;
|
||||
|
||||
@Schema(description = "排序", example = "1")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.contract;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeContractStatusEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeContractTypeEnum;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.mzt.logapi.starter.annotation.DiffLogField;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.isAfterOrEqual;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工合同保存 Request VO")
|
||||
@Data
|
||||
public class HrmEmployeeContractSaveReqVO {
|
||||
|
||||
@Schema(description = "合同编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "员工编号不能为空")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "合同编号", example = "HT-2026-001")
|
||||
@Size(max = 128, message = "合同编号长度不能超过 128 个字符")
|
||||
@DiffLogField(name = "合同编号")
|
||||
private String no;
|
||||
|
||||
@Schema(description = "合同类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "合同类型不能为空")
|
||||
@InEnum(value = HrmEmployeeContractTypeEnum.class, message = "合同类型必须是 {value}")
|
||||
@DiffLogField(name = "合同类型")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "合同开始日期", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "合同开始日期不能为空")
|
||||
@DiffLogField(name = "合同开始日期")
|
||||
private LocalDateTime startTime;
|
||||
|
||||
@Schema(description = "合同结束日期", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "合同结束日期不能为空")
|
||||
@DiffLogField(name = "合同结束日期")
|
||||
private LocalDateTime endTime;
|
||||
|
||||
@Schema(description = "期限,单位:年", example = "3")
|
||||
@Min(value = 1, message = "合同期限不能小于 1 年")
|
||||
@Max(value = 10, message = "合同期限不能超过 10 年")
|
||||
@DiffLogField(name = "合同期限")
|
||||
private Integer term;
|
||||
|
||||
@Schema(description = "合同状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "合同状态不能为空")
|
||||
@InEnum(value = HrmEmployeeContractStatusEnum.class, message = "合同状态必须是 {value}")
|
||||
@DiffLogField(name = "合同状态")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "签约公司", example = "示例科技有限公司")
|
||||
@Size(max = 255, message = "签约公司长度不能超过 255 个字符")
|
||||
@DiffLogField(name = "签约公司")
|
||||
private String signCompany;
|
||||
|
||||
@Schema(description = "合同签订日期", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "合同签订日期不能为空")
|
||||
@DiffLogField(name = "合同签订日期")
|
||||
private LocalDateTime signTime;
|
||||
|
||||
@Schema(description = "备注", example = "首次签订劳动合同")
|
||||
@Size(max = 500, message = "备注长度不能超过 500 个字符")
|
||||
@DiffLogField(name = "备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "是否到期提醒", example = "true")
|
||||
@DiffLogField(name = "是否到期提醒")
|
||||
private Boolean expireRemind;
|
||||
|
||||
@Schema(description = "附件地址数组", example = "[https://example.com/contract.pdf]")
|
||||
@DiffLogField(name = "附件")
|
||||
private List<String> fileUrls;
|
||||
|
||||
@Schema(description = "排序", example = "1")
|
||||
@DiffLogField(name = "排序")
|
||||
private Integer sort;
|
||||
|
||||
@AssertTrue(message = "合同结束日期不能早于开始日期")
|
||||
@JsonIgnore
|
||||
public boolean isTimeRangeValid() {
|
||||
return startTime == null || endTime == null || isAfterOrEqual(endTime, startTime);
|
||||
}
|
||||
|
||||
@AssertTrue(message = "合同期限不能为空")
|
||||
@JsonIgnore
|
||||
public boolean isTermValid() {
|
||||
return type == null
|
||||
|| HrmEmployeeContractTypeEnum.NON_FIXED_TERM_LABOR_CONTRACT.getType().equals(type)
|
||||
|| term != null;
|
||||
}
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.educationexperience;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工教育经历 Response VO")
|
||||
@Data
|
||||
public class HrmEmployeeEducationExperienceRespVO {
|
||||
|
||||
@Schema(description = "教育经历编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "学历", example = "8")
|
||||
private Integer education;
|
||||
|
||||
@Schema(description = "毕业院校", example = "浙江大学")
|
||||
private String graduateSchool;
|
||||
|
||||
@Schema(description = "专业", example = "软件工程")
|
||||
private String major;
|
||||
|
||||
@Schema(description = "入学日期")
|
||||
private LocalDateTime admissionTime;
|
||||
|
||||
@Schema(description = "毕业日期")
|
||||
private LocalDateTime graduationTime;
|
||||
|
||||
@Schema(description = "教学方式", example = "1")
|
||||
private Integer teachingMethods;
|
||||
|
||||
@Schema(description = "是否第一学历", example = "true")
|
||||
private Boolean firstDegree;
|
||||
|
||||
@Schema(description = "排序", example = "1")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.educationexperience;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.mzt.logapi.starter.annotation.DiffLogField;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.isAfterOrEqual;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工教育经历保存 Request VO")
|
||||
@Data
|
||||
public class HrmEmployeeEducationExperienceSaveReqVO {
|
||||
|
||||
@Schema(description = "教育经历编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "员工编号不能为空")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "学历", requiredMode = Schema.RequiredMode.REQUIRED, example = "8")
|
||||
@NotNull(message = "学历不能为空")
|
||||
@DiffLogField(name = "学历")
|
||||
private Integer education;
|
||||
|
||||
@Schema(description = "毕业院校", requiredMode = Schema.RequiredMode.REQUIRED, example = "浙江大学")
|
||||
@NotBlank(message = "毕业院校不能为空")
|
||||
@Size(max = 255, message = "毕业院校长度不能超过 255 个字符")
|
||||
@DiffLogField(name = "毕业院校")
|
||||
private String graduateSchool;
|
||||
|
||||
@Schema(description = "专业", requiredMode = Schema.RequiredMode.REQUIRED, example = "软件工程")
|
||||
@NotBlank(message = "专业不能为空")
|
||||
@Size(max = 255, message = "专业长度不能超过 255 个字符")
|
||||
@DiffLogField(name = "专业")
|
||||
private String major;
|
||||
|
||||
@Schema(description = "入学日期")
|
||||
@DiffLogField(name = "入学日期")
|
||||
private LocalDateTime admissionTime;
|
||||
|
||||
@Schema(description = "毕业日期")
|
||||
@DiffLogField(name = "毕业日期")
|
||||
private LocalDateTime graduationTime;
|
||||
|
||||
@Schema(description = "教学方式", example = "1")
|
||||
@DiffLogField(name = "教学方式")
|
||||
private Integer teachingMethods;
|
||||
|
||||
@Schema(description = "是否第一学历", example = "true")
|
||||
@DiffLogField(name = "是否第一学历")
|
||||
private Boolean firstDegree;
|
||||
|
||||
@Schema(description = "排序", example = "1")
|
||||
@DiffLogField(name = "排序")
|
||||
private Integer sort;
|
||||
|
||||
@AssertTrue(message = "毕业日期不能早于入学日期")
|
||||
@JsonIgnore
|
||||
public boolean isTimeRangeValid() {
|
||||
return admissionTime == null || graduationTime == null
|
||||
|| isAfterOrEqual(graduationTime, admissionTime);
|
||||
}
|
||||
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工取消离职 Request VO")
|
||||
@Data
|
||||
public class HrmEmployeeCancelQuitReqVO {
|
||||
|
||||
@Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "员工编号不能为空")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "取消原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "员工撤回离职申请")
|
||||
@NotBlank(message = "取消原因不能为空")
|
||||
@Size(max = 500, message = "取消原因不能超过 500 个字符")
|
||||
private String reason;
|
||||
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.beforeOrEqualNow;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工确认入职 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class HrmEmployeeConfirmEntryReqVO extends HrmEmployeeSaveReqVO {
|
||||
|
||||
@AssertTrue(message = "入职时间不能晚于当前时间")
|
||||
@JsonIgnore
|
||||
public boolean isEntryTimeValid() {
|
||||
return getEntryTime() == null || beforeOrEqualNow(getEntryTime());
|
||||
}
|
||||
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee;
|
||||
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.framework.common.validation.Mobile;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeStatusEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeTypeEnum;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 从后台用户创建员工 Request VO")
|
||||
@Data
|
||||
public class HrmEmployeeCreateFromUserReqVO {
|
||||
|
||||
@Schema(description = "后台用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "后台用户编号不能为空")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "工号", requiredMode = Schema.RequiredMode.REQUIRED, example = "HRM001")
|
||||
@NotBlank(message = "工号不能为空")
|
||||
@Size(max = 64, message = "工号不能超过 64 个字符")
|
||||
private String jobNumber;
|
||||
|
||||
@Schema(description = "员工手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300")
|
||||
@NotBlank(message = "员工手机号不能为空")
|
||||
@Mobile
|
||||
private String mobile;
|
||||
|
||||
@Schema(description = "部门编号", example = "100")
|
||||
private Long deptId;
|
||||
|
||||
@Schema(description = "直属上级员工编号", example = "1")
|
||||
private Long leaderEmployeeId;
|
||||
|
||||
@Schema(description = "聘用形式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "聘用形式不能为空")
|
||||
@InEnum(value = HrmEmployeeTypeEnum.class, message = "聘用形式必须是 {value}")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "员工状态;非正式员工必填", example = "3")
|
||||
@InEnum(value = HrmEmployeeStatusEnum.class, message = "员工状态必须是 {value}")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "入职时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "入职时间不能为空")
|
||||
private LocalDateTime entryTime;
|
||||
|
||||
@Schema(description = "试用期,单位:月;正式员工必填", example = "3")
|
||||
@Min(value = 0, message = "试用期不能小于 0")
|
||||
@Max(value = 6, message = "试用期不能超过 6 个月")
|
||||
private Integer probation;
|
||||
|
||||
@Schema(description = "职位名称", example = "Java 工程师")
|
||||
@Size(max = 255, message = "职位名称不能超过 255 个字符")
|
||||
private String postName;
|
||||
|
||||
@Schema(description = "岗位职级", example = "P6")
|
||||
@Size(max = 255, message = "岗位职级不能超过 255 个字符")
|
||||
private String postLevel;
|
||||
|
||||
@Schema(description = "工作城市", example = "杭州")
|
||||
@Size(max = 64, message = "工作城市不能超过 64 个字符")
|
||||
private String workCity;
|
||||
|
||||
@Schema(description = "工作地点", example = "西湖区")
|
||||
@Size(max = 255, message = "工作地点不能超过 255 个字符")
|
||||
private String workAddress;
|
||||
|
||||
@Schema(description = "备注", example = "从后台用户批量建档")
|
||||
@Size(max = 500, message = "备注不能超过 500 个字符")
|
||||
private String remark;
|
||||
|
||||
@AssertTrue(message = "正式员工试用期不能为空")
|
||||
@JsonIgnore
|
||||
public boolean isProbationValid() {
|
||||
return ObjUtil.notEqual(HrmEmployeeTypeEnum.FORMAL.getType(), type) || probation != null;
|
||||
}
|
||||
|
||||
@AssertTrue(message = "非正式员工状态不能为空")
|
||||
@JsonIgnore
|
||||
public boolean isStatusRequired() {
|
||||
return ObjUtil.notEqual(HrmEmployeeTypeEnum.INFORMAL.getType(), type) || status != null;
|
||||
}
|
||||
|
||||
@AssertTrue(message = "员工状态与聘用形式不匹配")
|
||||
@JsonIgnore
|
||||
public boolean isStatusValid() {
|
||||
return ObjUtil.notEqual(HrmEmployeeTypeEnum.INFORMAL.getType(), type)
|
||||
|| status == null || HrmEmployeeStatusEnum.INFORMAL_STATUSES.contains(status);
|
||||
}
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工部门统计 Response VO")
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class HrmEmployeeDeptStatisticsRespVO {
|
||||
|
||||
@Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
|
||||
private Long deptId;
|
||||
|
||||
@Schema(description = "在职员工人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Long activeCount;
|
||||
|
||||
@Schema(description = "全职员工人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "8")
|
||||
private Long fullTimeCount;
|
||||
|
||||
@Schema(description = "非全职员工人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
private Long nonFullTimeCount;
|
||||
|
||||
}
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect;
|
||||
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
|
||||
import cn.iocoder.yudao.framework.excel.core.convert.AreaConvert;
|
||||
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
|
||||
import cn.iocoder.yudao.module.hrm.enums.DictTypeConstants;
|
||||
import cn.iocoder.yudao.module.hrm.framework.excel.core.HrmEmployeeProbationExcelColumnSelectFunction;
|
||||
import cn.iocoder.yudao.module.hrm.framework.excel.core.HrmRecruitChannelExcelColumnSelectFunction;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工档案 Excel 导入 VO")
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@ExcelIgnoreUnannotated
|
||||
public class HrmEmployeeImportExcelVO {
|
||||
|
||||
@ExcelProperty("员工姓名")
|
||||
@Schema(description = "员工姓名")
|
||||
private String name;
|
||||
|
||||
@ExcelProperty("工号")
|
||||
@Schema(description = "工号")
|
||||
private String jobNumber;
|
||||
|
||||
@ExcelProperty("手机号")
|
||||
@Schema(description = "手机号")
|
||||
private String mobile;
|
||||
|
||||
@ExcelProperty("国家或地区")
|
||||
@Schema(description = "国家或地区")
|
||||
private String country;
|
||||
|
||||
@ExcelProperty("民族")
|
||||
@Schema(description = "民族")
|
||||
private String nation;
|
||||
|
||||
@ExcelProperty(value = "证件类型", converter = DictConvert.class)
|
||||
@DictFormat(DictTypeConstants.HRM_EMPLOYEE_ID_TYPE)
|
||||
@ExcelColumnSelect(dictType = DictTypeConstants.HRM_EMPLOYEE_ID_TYPE)
|
||||
@Schema(description = "证件类型")
|
||||
private Integer idType;
|
||||
|
||||
@ExcelProperty("证件号码")
|
||||
@Schema(description = "证件号码")
|
||||
private String idNumber;
|
||||
|
||||
@ExcelProperty(value = "性别", converter = DictConvert.class)
|
||||
@DictFormat(cn.iocoder.yudao.module.system.enums.DictTypeConstants.USER_SEX)
|
||||
@ExcelColumnSelect(dictType = cn.iocoder.yudao.module.system.enums.DictTypeConstants.USER_SEX)
|
||||
@Schema(description = "性别")
|
||||
private Integer sex;
|
||||
|
||||
@ExcelProperty("邮箱")
|
||||
@Schema(description = "邮箱")
|
||||
private String email;
|
||||
|
||||
@ExcelProperty("籍贯")
|
||||
@Schema(description = "籍贯")
|
||||
private String nativePlace;
|
||||
|
||||
@ExcelProperty("出生日期")
|
||||
@Schema(description = "出生日期")
|
||||
private LocalDateTime birthday;
|
||||
|
||||
@ExcelProperty("户籍地址")
|
||||
@Schema(description = "户籍地址")
|
||||
private String address;
|
||||
|
||||
@ExcelProperty(value = "最高学历", converter = DictConvert.class)
|
||||
@DictFormat(DictTypeConstants.HRM_EMPLOYEE_EDUCATION)
|
||||
@ExcelColumnSelect(dictType = DictTypeConstants.HRM_EMPLOYEE_EDUCATION)
|
||||
@Schema(description = "最高学历")
|
||||
private Integer highestEducation;
|
||||
|
||||
@ExcelProperty("部门编号")
|
||||
@Schema(description = "部门编号")
|
||||
private Long deptId;
|
||||
|
||||
@ExcelProperty("直属上级工号")
|
||||
@Schema(description = "直属上级工号")
|
||||
private String leaderJobNumber;
|
||||
|
||||
@ExcelProperty("职位名称")
|
||||
@Schema(description = "职位名称")
|
||||
private String postName;
|
||||
|
||||
@ExcelProperty("岗位职级")
|
||||
@Schema(description = "岗位职级")
|
||||
private String postLevel;
|
||||
|
||||
@ExcelProperty(value = "入职状态", converter = DictConvert.class)
|
||||
@DictFormat(DictTypeConstants.HRM_EMPLOYEE_ENTRY_STATUS)
|
||||
@ExcelColumnSelect(dictType = DictTypeConstants.HRM_EMPLOYEE_ENTRY_STATUS)
|
||||
@Schema(description = "入职状态")
|
||||
private Integer entryStatus;
|
||||
|
||||
@ExcelProperty(value = "员工状态", converter = DictConvert.class)
|
||||
@DictFormat(DictTypeConstants.HRM_EMPLOYEE_STATUS)
|
||||
@ExcelColumnSelect(dictType = DictTypeConstants.HRM_EMPLOYEE_STATUS)
|
||||
@Schema(description = "员工状态")
|
||||
private Integer status;
|
||||
|
||||
@ExcelProperty(value = "聘用形式", converter = DictConvert.class)
|
||||
@DictFormat(DictTypeConstants.HRM_EMPLOYEE_TYPE)
|
||||
@ExcelColumnSelect(dictType = DictTypeConstants.HRM_EMPLOYEE_TYPE)
|
||||
@Schema(description = "聘用形式")
|
||||
private Integer type;
|
||||
|
||||
@ExcelProperty("入职时间")
|
||||
@Schema(description = "入职时间")
|
||||
private LocalDateTime entryTime;
|
||||
|
||||
@ExcelProperty("试用期(月)")
|
||||
@ExcelColumnSelect(functionName = HrmEmployeeProbationExcelColumnSelectFunction.NAME)
|
||||
@Schema(description = "试用期(月)")
|
||||
private Integer probation;
|
||||
|
||||
@ExcelProperty("转正时间")
|
||||
@Schema(description = "转正时间")
|
||||
private LocalDateTime regularTime;
|
||||
|
||||
@ExcelProperty("离职时间")
|
||||
@Schema(description = "离职时间")
|
||||
private LocalDateTime leaveTime;
|
||||
|
||||
@ExcelProperty("工作城市")
|
||||
@Schema(description = "工作城市")
|
||||
private String workCity;
|
||||
|
||||
@ExcelProperty("工作地点")
|
||||
@Schema(description = "工作地点")
|
||||
private String workAddress;
|
||||
|
||||
@ExcelProperty("工作详细地址")
|
||||
@Schema(description = "工作详细地址")
|
||||
private String workDetailAddress;
|
||||
|
||||
@ExcelProperty("招聘渠道")
|
||||
@ExcelColumnSelect(functionName = HrmRecruitChannelExcelColumnSelectFunction.NAME)
|
||||
@Schema(description = "招聘渠道选项,格式为名称(ID)")
|
||||
private String channelName;
|
||||
|
||||
@ExcelProperty("司龄起算时间")
|
||||
@Schema(description = "司龄起算时间")
|
||||
private LocalDateTime companyAgeStartTime;
|
||||
|
||||
@ExcelProperty("绑定用户手机号")
|
||||
@Schema(description = "绑定后台用户的手机号")
|
||||
private String userMobile;
|
||||
|
||||
@ExcelProperty("银行卡号")
|
||||
@Schema(description = "银行卡号")
|
||||
private String bankCardNumber;
|
||||
|
||||
@ExcelProperty(value = "开户地区", converter = AreaConvert.class)
|
||||
@Schema(description = "开户地区")
|
||||
private Integer bankAreaId;
|
||||
|
||||
@ExcelProperty("银行名称")
|
||||
@Schema(description = "银行名称")
|
||||
private String bankName;
|
||||
|
||||
@ExcelProperty("开户支行名称")
|
||||
@Schema(description = "开户支行名称")
|
||||
private String bankBranchName;
|
||||
|
||||
@ExcelProperty(value = "是否首次参保", converter = DictConvert.class)
|
||||
@DictFormat(DictTypeConstants.BOOLEAN_STRING)
|
||||
@ExcelColumnSelect(dictType = DictTypeConstants.BOOLEAN_STRING)
|
||||
@Schema(description = "是否首次参保")
|
||||
private Boolean firstSocialSecurity;
|
||||
|
||||
@ExcelProperty(value = "是否首次缴纳公积金", converter = DictConvert.class)
|
||||
@DictFormat(DictTypeConstants.BOOLEAN_STRING)
|
||||
@ExcelColumnSelect(dictType = DictTypeConstants.BOOLEAN_STRING)
|
||||
@Schema(description = "是否首次缴纳公积金")
|
||||
private Boolean firstAccumulationFund;
|
||||
|
||||
@ExcelProperty("个人社保账号")
|
||||
@Schema(description = "个人社保账号")
|
||||
private String socialSecurityNumber;
|
||||
|
||||
@ExcelProperty("个人公积金账号")
|
||||
@Schema(description = "个人公积金账号")
|
||||
private String accumulationFundNumber;
|
||||
|
||||
@ExcelProperty("社保起缴月份")
|
||||
@Schema(description = "社保起缴月份")
|
||||
private LocalDateTime socialSecurityStartMonth;
|
||||
|
||||
@ExcelProperty("参保方案名称")
|
||||
@Schema(description = "参保方案名称")
|
||||
private String schemeName;
|
||||
|
||||
@ExcelProperty("备注")
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工档案导入 Response VO")
|
||||
@Data
|
||||
@Builder
|
||||
public class HrmEmployeeImportRespVO {
|
||||
|
||||
@Schema(description = "创建成功的工号数组", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<String> createJobNumbers;
|
||||
|
||||
@Schema(description = "更新成功的工号数组", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<String> updateJobNumbers;
|
||||
|
||||
@Schema(description = "因重复而跳过的工号数组", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<String> skipJobNumbers;
|
||||
|
||||
@Schema(description = "导入失败的员工集合,key 为原始行号和员工标识,value 为失败原因",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Map<String, String> failureJobNumbers;
|
||||
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeEntryStatusEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeStatusEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeStatusTabEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeSurveyTypeEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeTypeEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeTodoTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工档案列表 Request VO")
|
||||
@Data
|
||||
public class HrmEmployeeListReqVO {
|
||||
|
||||
@Schema(description = "员工编号列表", example = "1,2")
|
||||
private List<Long> ids;
|
||||
|
||||
@Schema(hidden = true)
|
||||
private List<Long> excludeIds;
|
||||
|
||||
@Schema(description = "员工姓名或工号,模糊匹配", example = "张三")
|
||||
private String search;
|
||||
|
||||
@Schema(description = "员工姓名,模糊匹配", example = "张")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "工号,模糊匹配", example = "HRM")
|
||||
private String jobNumber;
|
||||
|
||||
@Schema(description = "手机号,模糊匹配", example = "156")
|
||||
private String mobile;
|
||||
|
||||
@Schema(description = "部门编号", example = "100")
|
||||
private Long deptId;
|
||||
|
||||
@Schema(description = "部门编号数组", example = "100,101")
|
||||
private List<Long> deptIds;
|
||||
|
||||
@Schema(description = "直属上级员工编号", example = "1")
|
||||
private Long leaderEmployeeId;
|
||||
|
||||
@Schema(description = "性别", example = "1")
|
||||
private Integer sex;
|
||||
|
||||
@Schema(description = "入职时间范围")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] entryTime;
|
||||
|
||||
@Schema(description = "职位名称,模糊匹配", example = "Java")
|
||||
private String postName;
|
||||
|
||||
@Schema(description = "转正时间范围")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] regularTime;
|
||||
|
||||
@Schema(description = "工作地点,模糊匹配", example = "西湖区")
|
||||
private String workAddress;
|
||||
|
||||
@Schema(description = "招聘渠道编号", example = "10")
|
||||
private Long channelId;
|
||||
|
||||
@Schema(description = "聘用形式", example = "1")
|
||||
@InEnum(value = HrmEmployeeTypeEnum.class, message = "聘用形式必须是 {value}")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "入职状态", example = "1")
|
||||
@InEnum(value = HrmEmployeeEntryStatusEnum.class, message = "入职状态必须是 {value}")
|
||||
private Integer entryStatus;
|
||||
|
||||
@Schema(description = "员工状态", example = "1")
|
||||
@InEnum(value = HrmEmployeeStatusEnum.class, message = "员工状态必须是 {value}")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "员工状态分类", example = "12")
|
||||
@InEnum(value = HrmEmployeeStatusTabEnum.class, message = "员工状态分类必须是 {value}")
|
||||
private Integer statusCategory;
|
||||
|
||||
@Schema(description = "首页人事概况筛选类型", example = "1")
|
||||
@InEnum(value = HrmEmployeeSurveyTypeEnum.class, message = "首页人事概况筛选类型必须是 {value}")
|
||||
private Integer surveyType;
|
||||
|
||||
@Schema(description = "首页待办筛选类型", example = "5")
|
||||
@InEnum(value = HrmEmployeeTodoTypeEnum.class, message = "首页待办筛选类型必须是 {value}")
|
||||
private Integer todoType;
|
||||
|
||||
@Schema(hidden = true)
|
||||
private LocalDateTime[] activeTime;
|
||||
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工通知发送结果 Response VO")
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class HrmEmployeeNotifyRespVO {
|
||||
|
||||
@Schema(description = "发送成功数量", example = "10")
|
||||
private Integer successCount;
|
||||
|
||||
@Schema(description = "无后台账号跳过数量", example = "2")
|
||||
private Integer skippedCount;
|
||||
|
||||
@Schema(description = "发送失败数量", example = "1")
|
||||
private Integer failureCount;
|
||||
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.SortablePageParam;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeEntryStatusEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeStatusEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeStatusTabEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeSurveyTypeEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeTypeEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeTodoTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工档案分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class HrmEmployeePageReqVO extends SortablePageParam {
|
||||
|
||||
@Schema(description = "员工编号列表", example = "1,2")
|
||||
private List<Long> ids;
|
||||
|
||||
@Schema(hidden = true)
|
||||
private List<Long> excludeIds;
|
||||
|
||||
@Schema(description = "员工姓名、工号或手机号,模糊匹配", example = "张三")
|
||||
private String search;
|
||||
|
||||
@Schema(description = "员工姓名,模糊匹配", example = "张")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "工号,模糊匹配", example = "HRM")
|
||||
private String jobNumber;
|
||||
|
||||
@Schema(description = "手机号,模糊匹配", example = "156")
|
||||
private String mobile;
|
||||
|
||||
@Schema(description = "部门编号", example = "100")
|
||||
private Long deptId;
|
||||
|
||||
@Schema(description = "部门编号数组", example = "100,101")
|
||||
private List<Long> deptIds;
|
||||
|
||||
@Schema(description = "直属上级员工编号", example = "1")
|
||||
private Long leaderEmployeeId;
|
||||
|
||||
@Schema(description = "性别", example = "1")
|
||||
private Integer sex;
|
||||
|
||||
@Schema(description = "入职时间范围")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] entryTime;
|
||||
|
||||
@Schema(description = "职位名称,模糊匹配", example = "Java")
|
||||
private String postName;
|
||||
|
||||
@Schema(description = "转正时间范围")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] regularTime;
|
||||
|
||||
@Schema(description = "工作地点,模糊匹配", example = "西湖区")
|
||||
private String workAddress;
|
||||
|
||||
@Schema(description = "招聘渠道编号", example = "10")
|
||||
private Long channelId;
|
||||
|
||||
@Schema(description = "聘用形式", example = "1")
|
||||
@InEnum(value = HrmEmployeeTypeEnum.class, message = "聘用形式必须是 {value}")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "入职状态", example = "1")
|
||||
@InEnum(value = HrmEmployeeEntryStatusEnum.class, message = "入职状态必须是 {value}")
|
||||
private Integer entryStatus;
|
||||
|
||||
@Schema(description = "员工状态", example = "1")
|
||||
@InEnum(value = HrmEmployeeStatusEnum.class, message = "员工状态必须是 {value}")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "员工状态分类", example = "12")
|
||||
@InEnum(value = HrmEmployeeStatusTabEnum.class, message = "员工状态分类必须是 {value}")
|
||||
private Integer statusCategory;
|
||||
|
||||
@Schema(description = "首页人事概况筛选类型", example = "1")
|
||||
@InEnum(value = HrmEmployeeSurveyTypeEnum.class, message = "首页人事概况筛选类型必须是 {value}")
|
||||
private Integer surveyType;
|
||||
|
||||
@Schema(description = "首页待办筛选类型", example = "5")
|
||||
@InEnum(value = HrmEmployeeTodoTypeEnum.class, message = "首页待办筛选类型必须是 {value}")
|
||||
private Integer todoType;
|
||||
|
||||
@Schema(hidden = true)
|
||||
private LocalDateTime[] activeTime;
|
||||
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeQuitReasonEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeQuitTypeEnum;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.isAfterOrEqual;
|
||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.isBeforeOrEqual;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工离职 Request VO")
|
||||
@Data
|
||||
public class HrmEmployeeQuitReqVO {
|
||||
|
||||
@Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "员工编号不能为空")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "计划离职时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "计划离职时间不能为空")
|
||||
private LocalDateTime planQuitTime;
|
||||
|
||||
@Schema(description = "申请离职时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "申请离职时间不能为空")
|
||||
private LocalDateTime applyQuitTime;
|
||||
|
||||
@Schema(description = "薪资结算时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "薪资结算时间不能为空")
|
||||
private LocalDateTime salarySettlementTime;
|
||||
|
||||
@Schema(description = "离职类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "离职类型不能为空")
|
||||
@InEnum(value = HrmEmployeeQuitTypeEnum.class, message = "离职类型必须是 {value}")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "离职原因", example = "1")
|
||||
@InEnum(value = HrmEmployeeQuitReasonEnum.class, message = "离职原因必须是 {value}")
|
||||
private Integer reason;
|
||||
|
||||
@Schema(description = "备注", example = "个人原因离职")
|
||||
@Size(max = 500, message = "备注不能超过 500 个字符")
|
||||
private String remark;
|
||||
|
||||
@AssertTrue(message = "申请离职时间不能晚于计划离职时间")
|
||||
@JsonIgnore
|
||||
public boolean isApplyQuitTimeValid() {
|
||||
return applyQuitTime == null || planQuitTime == null
|
||||
|| isBeforeOrEqual(applyQuitTime.toLocalDate(), planQuitTime.toLocalDate());
|
||||
}
|
||||
|
||||
@AssertTrue(message = "薪资结算时间不能早于计划离职时间")
|
||||
@JsonIgnore
|
||||
public boolean isSalarySettlementTimeValid() {
|
||||
return salarySettlementTime == null || planQuitTime == null
|
||||
|| isAfterOrEqual(salarySettlementTime.toLocalDate(), planQuitTime.toLocalDate());
|
||||
}
|
||||
|
||||
@AssertTrue(message = "离职原因与离职类型不匹配")
|
||||
@JsonIgnore
|
||||
public boolean isReasonValid() {
|
||||
if (type == null || HrmEmployeeQuitTypeEnum.RETIREMENT.getType().equals(type)) {
|
||||
return true;
|
||||
}
|
||||
HrmEmployeeQuitReasonEnum quitReason = HrmEmployeeQuitReasonEnum.valueOf(reason);
|
||||
return quitReason != null && quitReason.getQuitType().equals(type);
|
||||
}
|
||||
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.beforeOrEqualNow;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工再入职 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class HrmEmployeeRehireReqVO extends HrmEmployeeSaveReqVO {
|
||||
|
||||
@Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "员工编号不能为空")
|
||||
private Long employeeId;
|
||||
|
||||
@AssertTrue(message = "入职时间不能晚于当前时间")
|
||||
@JsonIgnore
|
||||
public boolean isEntryTimeValid() {
|
||||
return getEntryTime() == null || beforeOrEqualNow(getEntryTime());
|
||||
}
|
||||
|
||||
}
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee;
|
||||
|
||||
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
|
||||
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
|
||||
import cn.iocoder.yudao.module.hrm.enums.DictTypeConstants;
|
||||
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工档案 Response VO")
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@ExcelIgnoreUnannotated
|
||||
public class HrmEmployeeRespVO {
|
||||
|
||||
@ExcelProperty(value = "员工编号", index = 0)
|
||||
@Schema(description = "员工档案编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@ExcelProperty(value = "员工姓名", index = 1)
|
||||
@Schema(description = "员工姓名", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
|
||||
private String name;
|
||||
|
||||
@ExcelProperty(value = "工号", index = 2)
|
||||
@Schema(description = "工号", example = "HRM001")
|
||||
private String jobNumber;
|
||||
|
||||
@Schema(description = "系统用户编号", example = "1024")
|
||||
private Long userId;
|
||||
|
||||
@ExcelProperty(value = "绑定用户", index = 17)
|
||||
@Schema(description = "后台用户昵称", example = "芋道")
|
||||
private String userNickname;
|
||||
|
||||
@ExcelProperty(value = "手机号", index = 3)
|
||||
@Schema(description = "手机号", example = "15601691300")
|
||||
private String mobile;
|
||||
|
||||
@Schema(description = "国家地区", example = "中国")
|
||||
private String country;
|
||||
|
||||
@Schema(description = "民族", example = "汉族")
|
||||
private String nation;
|
||||
|
||||
@Schema(description = "证件类型", example = "1")
|
||||
private Integer idType;
|
||||
|
||||
@Schema(description = "证件号码", example = "310101199001011234")
|
||||
private String idNumber;
|
||||
|
||||
@Schema(description = "性别", example = "1")
|
||||
private Integer sex;
|
||||
|
||||
@Schema(description = "邮箱", example = "hrm@example.com")
|
||||
private String email;
|
||||
|
||||
@Schema(description = "籍贯", example = "浙江杭州")
|
||||
private String nativePlace;
|
||||
|
||||
@Schema(description = "出生时间")
|
||||
private LocalDateTime birthday;
|
||||
|
||||
@Schema(description = "年龄", example = "28")
|
||||
private Integer age;
|
||||
|
||||
@Schema(description = "户籍地址", example = "杭州市西湖区")
|
||||
private String address;
|
||||
|
||||
@Schema(description = "最高学历", example = "8")
|
||||
private Integer highestEducation;
|
||||
|
||||
@Schema(description = "部门编号", example = "100")
|
||||
private Long deptId;
|
||||
|
||||
@ExcelProperty(value = "部门", index = 4)
|
||||
@Schema(description = "部门名称", example = "研发部")
|
||||
private String deptName;
|
||||
|
||||
@Schema(description = "直属上级员工编号", example = "1")
|
||||
private Long leaderEmployeeId;
|
||||
|
||||
@ExcelProperty(value = "直属上级", index = 7)
|
||||
@Schema(description = "直属上级员工姓名", example = "李四")
|
||||
private String leaderEmployeeName;
|
||||
|
||||
@ExcelProperty(value = "入职状态", index = 8, converter = DictConvert.class)
|
||||
@DictFormat(DictTypeConstants.HRM_EMPLOYEE_ENTRY_STATUS)
|
||||
@Schema(description = "入职状态", example = "1")
|
||||
private Integer entryStatus;
|
||||
|
||||
@ExcelProperty(value = "员工状态", index = 9, converter = DictConvert.class)
|
||||
@DictFormat(DictTypeConstants.HRM_EMPLOYEE_STATUS)
|
||||
@Schema(description = "员工状态", example = "1")
|
||||
private Integer status;
|
||||
|
||||
@ExcelProperty(value = "聘用形式", index = 10, converter = DictConvert.class)
|
||||
@DictFormat(DictTypeConstants.HRM_EMPLOYEE_TYPE)
|
||||
@Schema(description = "聘用形式", example = "1")
|
||||
private Integer type;
|
||||
|
||||
@ExcelProperty(value = "入职时间", index = 11)
|
||||
@Schema(description = "入职时间")
|
||||
private LocalDateTime entryTime;
|
||||
|
||||
@Schema(description = "试用期,单位月;0 表示无试用期", example = "3")
|
||||
private Integer probation;
|
||||
|
||||
@ExcelProperty(value = "转正时间", index = 12)
|
||||
@Schema(description = "转正时间")
|
||||
private LocalDateTime regularTime;
|
||||
|
||||
@ExcelProperty(value = "离职时间", index = 13)
|
||||
@Schema(description = "离职时间")
|
||||
private LocalDateTime leaveTime;
|
||||
|
||||
@ExcelProperty(value = "岗位", index = 5)
|
||||
@Schema(description = "职位名称", example = "Java 工程师")
|
||||
private String postName;
|
||||
|
||||
@ExcelProperty(value = "岗位职级", index = 6)
|
||||
@Schema(description = "岗位职级", example = "P6")
|
||||
private String postLevel;
|
||||
|
||||
@ExcelProperty(value = "工作城市", index = 14)
|
||||
@Schema(description = "工作城市", example = "上海")
|
||||
private String workCity;
|
||||
|
||||
@ExcelProperty(value = "工作地点", index = 15)
|
||||
@Schema(description = "工作地址", example = "浦东新区")
|
||||
private String workAddress;
|
||||
|
||||
@Schema(description = "工作详细地址", example = "张江高科 1 号楼")
|
||||
private String workDetailAddress;
|
||||
|
||||
@Schema(description = "招聘渠道编号", example = "10")
|
||||
private Long channelId;
|
||||
|
||||
@ExcelProperty(value = "招聘渠道", index = 16)
|
||||
@Schema(description = "招聘渠道名称", example = "BOSS直聘")
|
||||
private String channelName;
|
||||
|
||||
@Schema(description = "司龄开始时间")
|
||||
private LocalDateTime companyAgeStartTime;
|
||||
|
||||
@Schema(description = "司龄,单位年", example = "2")
|
||||
private Integer companyAge;
|
||||
|
||||
@Schema(description = "关联招聘候选人编号", example = "200")
|
||||
private Long candidateId;
|
||||
|
||||
// ==================== 工资卡信息 ====================
|
||||
|
||||
@Schema(description = "银行卡号", example = "622202600001")
|
||||
private String salaryCardNumber;
|
||||
|
||||
@Schema(description = "开户地区编号", example = "440300")
|
||||
private Integer salaryCardAreaId;
|
||||
|
||||
@Schema(description = "开户地区名称", example = "广东省 深圳市")
|
||||
private String salaryCardAreaName;
|
||||
|
||||
@Schema(description = "银行名称", example = "招商银行")
|
||||
private String salaryCardBankName;
|
||||
|
||||
@Schema(description = "开户支行名称", example = "科技园支行")
|
||||
private String salaryCardBankBranchName;
|
||||
|
||||
// ==================== 社保信息 ====================
|
||||
|
||||
@Schema(description = "个人社保账号", example = "SB20260001")
|
||||
private String socialSecurityNumber;
|
||||
|
||||
@Schema(description = "个人公积金账号", example = "GJJ20260001")
|
||||
private String accumulationFundNumber;
|
||||
|
||||
@Schema(description = "备注", example = "首批迁移员工")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee;
|
||||
|
||||
import cn.hutool.core.util.IdcardUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.framework.common.validation.Mobile;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.experience.HrmEmployeeEducationEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeEntryStatusEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeIdTypeEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeStatusEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeTypeEnum;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.PastOrPresent;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.isBeforeOrEqual;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工档案新增/修改 Request VO")
|
||||
@Data
|
||||
public class HrmEmployeeSaveReqVO {
|
||||
|
||||
@Schema(description = "员工档案编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "员工姓名", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
|
||||
@NotBlank(message = "员工姓名不能为空")
|
||||
@Size(max = 255, message = "员工姓名不能超过 255 个字符")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "工号", example = "HRM001")
|
||||
@Size(max = 64, message = "工号不能超过 64 个字符")
|
||||
private String jobNumber;
|
||||
|
||||
@Schema(description = "系统用户编号", example = "1024")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300")
|
||||
@NotBlank(message = "手机号不能为空")
|
||||
@Mobile
|
||||
private String mobile;
|
||||
|
||||
@Schema(description = "国家地区", example = "中国")
|
||||
@Size(max = 64, message = "国家地区不能超过 64 个字符")
|
||||
private String country;
|
||||
|
||||
@Schema(description = "民族", example = "汉族")
|
||||
@Size(max = 64, message = "民族不能超过 64 个字符")
|
||||
private String nation;
|
||||
|
||||
@Schema(description = "证件类型", example = "1")
|
||||
@InEnum(value = HrmEmployeeIdTypeEnum.class, message = "证件类型必须是 {value}")
|
||||
private Integer idType;
|
||||
|
||||
@Schema(description = "证件号码", example = "310101199001011234")
|
||||
@Size(max = 255, message = "证件号码不能超过 255 个字符")
|
||||
private String idNumber;
|
||||
|
||||
@Schema(description = "性别", example = "1")
|
||||
private Integer sex;
|
||||
|
||||
@Schema(description = "邮箱", example = "hrm@example.com")
|
||||
@Email(message = "邮箱格式不正确")
|
||||
@Size(max = 255, message = "邮箱不能超过 255 个字符")
|
||||
private String email;
|
||||
|
||||
@Schema(description = "籍贯", example = "浙江杭州")
|
||||
@Size(max = 128, message = "籍贯不能超过 128 个字符")
|
||||
private String nativePlace;
|
||||
|
||||
@Schema(description = "出生时间")
|
||||
@PastOrPresent(message = "出生时间不能晚于当前时间")
|
||||
private LocalDateTime birthday;
|
||||
|
||||
@Schema(description = "年龄", example = "28")
|
||||
private Integer age;
|
||||
|
||||
@Schema(description = "户籍地址", example = "杭州市西湖区")
|
||||
@Size(max = 255, message = "户籍地址不能超过 255 个字符")
|
||||
private String address;
|
||||
|
||||
@Schema(description = "最高学历", example = "8")
|
||||
@InEnum(value = HrmEmployeeEducationEnum.class, message = "最高学历必须是 {value}")
|
||||
private Integer highestEducation;
|
||||
|
||||
@Schema(description = "部门编号", example = "100")
|
||||
private Long deptId;
|
||||
|
||||
@Schema(description = "直属上级员工编号", example = "1")
|
||||
private Long leaderEmployeeId;
|
||||
|
||||
@Schema(description = "入职状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "入职状态不能为空")
|
||||
@InEnum(value = HrmEmployeeEntryStatusEnum.class, message = "入职状态必须是 {value}")
|
||||
private Integer entryStatus;
|
||||
|
||||
@Schema(description = "员工状态", example = "1")
|
||||
@InEnum(value = HrmEmployeeStatusEnum.class, message = "员工状态必须是 {value}")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "聘用形式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "聘用形式不能为空")
|
||||
@InEnum(value = HrmEmployeeTypeEnum.class, message = "聘用形式必须是 {value}")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "入职时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "入职时间不能为空")
|
||||
private LocalDateTime entryTime;
|
||||
|
||||
@Schema(description = "试用期,单位月;0 表示无试用期", example = "3")
|
||||
@Min(value = 0, message = "试用期不能小于 0 个月")
|
||||
@Max(value = 6, message = "试用期不能超过 6 个月")
|
||||
private Integer probation;
|
||||
|
||||
@Schema(description = "转正时间")
|
||||
private LocalDateTime regularTime;
|
||||
|
||||
@Schema(description = "离职时间")
|
||||
private LocalDateTime leaveTime;
|
||||
|
||||
@Schema(description = "职位名称", example = "Java 工程师")
|
||||
@Size(max = 255, message = "职位名称不能超过 255 个字符")
|
||||
private String postName;
|
||||
|
||||
@Schema(description = "岗位职级", example = "P6")
|
||||
@Size(max = 255, message = "岗位职级不能超过 255 个字符")
|
||||
private String postLevel;
|
||||
|
||||
@Schema(description = "工作城市", example = "上海")
|
||||
@Size(max = 64, message = "工作城市不能超过 64 个字符")
|
||||
private String workCity;
|
||||
|
||||
@Schema(description = "工作地址", example = "浦东新区")
|
||||
@Size(max = 255, message = "工作地址不能超过 255 个字符")
|
||||
private String workAddress;
|
||||
|
||||
@Schema(description = "工作详细地址", example = "张江高科 1 号楼")
|
||||
@Size(max = 255, message = "工作详细地址不能超过 255 个字符")
|
||||
private String workDetailAddress;
|
||||
|
||||
@Schema(description = "招聘渠道编号", example = "10")
|
||||
private Long channelId;
|
||||
|
||||
@Schema(description = "司龄开始时间")
|
||||
private LocalDateTime companyAgeStartTime;
|
||||
|
||||
@Schema(description = "司龄,单位年", example = "2")
|
||||
private Integer companyAge;
|
||||
|
||||
@Schema(description = "关联招聘候选人编号", example = "200")
|
||||
private Long candidateId;
|
||||
|
||||
@Schema(description = "备注", example = "首批迁移员工")
|
||||
@Size(max = 500, message = "备注不能超过 500 个字符")
|
||||
private String remark;
|
||||
|
||||
@AssertTrue(message = "身份证号码格式不正确")
|
||||
@JsonIgnore
|
||||
public boolean isIdNumberValid() {
|
||||
return ObjUtil.notEqual(HrmEmployeeIdTypeEnum.ID_CARD.getType(), idType)
|
||||
|| StrUtil.isBlank(idNumber) || IdcardUtil.isValidCard(idNumber);
|
||||
}
|
||||
|
||||
@AssertTrue(message = "员工状态与聘用形式不匹配")
|
||||
@JsonIgnore
|
||||
public boolean isStatusValid() {
|
||||
return ObjUtil.notEqual(HrmEmployeeTypeEnum.INFORMAL.getType(), type)
|
||||
|| status == null || HrmEmployeeStatusEnum.INFORMAL_STATUSES.contains(status);
|
||||
}
|
||||
|
||||
@AssertTrue(message = "非正式员工状态不能为空")
|
||||
@JsonIgnore
|
||||
public boolean isStatusRequired() {
|
||||
return ObjUtil.notEqual(HrmEmployeeTypeEnum.INFORMAL.getType(), type) || status != null;
|
||||
}
|
||||
|
||||
@AssertTrue(message = "在职员工工号不能为空")
|
||||
@JsonIgnore
|
||||
public boolean isJobNumberValid() {
|
||||
return ObjUtil.notEqual(HrmEmployeeEntryStatusEnum.ACTIVE.getStatus(), entryStatus)
|
||||
|| StrUtil.isNotBlank(jobNumber);
|
||||
}
|
||||
|
||||
@AssertTrue(message = "正式员工试用期不能为空")
|
||||
@JsonIgnore
|
||||
public boolean isProbationValid() {
|
||||
return ObjUtil.notEqual(HrmEmployeeTypeEnum.FORMAL.getType(), type) || probation != null;
|
||||
}
|
||||
|
||||
@AssertTrue(message = "司龄起算时间不能晚于入职时间")
|
||||
@JsonIgnore
|
||||
public boolean isCompanyAgeStartTimeValid() {
|
||||
return entryTime == null || companyAgeStartTime == null
|
||||
|| isBeforeOrEqual(companyAgeStartTime, entryTime);
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工状态数量 Response VO")
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class HrmEmployeeStatusCountRespVO {
|
||||
|
||||
@Schema(description = "状态页签", requiredMode = Schema.RequiredMode.REQUIRED, example = "12")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Long count;
|
||||
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.file;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工材料附件 Response VO")
|
||||
@Data
|
||||
public class HrmEmployeeFileRespVO {
|
||||
|
||||
@Schema(description = "附件编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "附件类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "11")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "附件地址", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String url;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.file;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeFileTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - HRM 员工材料附件保存 Request VO")
|
||||
@Data
|
||||
public class HrmEmployeeFileSaveReqVO {
|
||||
|
||||
@Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "员工编号不能为空")
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "附件类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "11")
|
||||
@NotNull(message = "附件类型不能为空")
|
||||
@InEnum(value = HrmEmployeeFileTypeEnum.class, message = "附件类型必须是 {value}")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "附件地址数组", requiredMode = Schema.RequiredMode.REQUIRED,
|
||||
example = "[https://example.com/archive.pdf]")
|
||||
@NotNull(message = "附件地址数组不能为空")
|
||||
@Size(max = 20, message = "同类附件不能超过 20 个")
|
||||
private List<@NotBlank(message = "附件地址不能为空")
|
||||
@Size(max = 512, message = "附件地址长度不能超过 512 个字符") String> fileUrls;
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user