diff --git a/pom.xml b/pom.xml index 6e9ac80..cfeb1de 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ yudao-module-system yudao-module-infra - + yudao-module-bpm @@ -26,7 +26,9 @@ - + yudao-module-hrm + yudao-module-fms + @@ -36,7 +38,7 @@ https://github.com/YunaiV/ruoyi-vue-pro - 2026.06-SNAPSHOT + 2026.07-SNAPSHOT 17 ${java.version} diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index a81752a..ee1bf82 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -57,6 +57,7 @@ 5.8.46 6.0.0-M22 1.3.0 + 5.3.3 2.4.1 1.2.83 2.0.62 @@ -488,6 +489,12 @@ + com.googlecode.aviator + aviator + ${aviator.version} + + + org.apache.tika tika-core ${tika-core.version} diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java index b2db78b..c772692 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java @@ -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 long sum(Collection from, ToLongFunction valueFunc) { + if (CollUtil.isEmpty(from)) { + return 0L; + } + return from.stream().mapToLong(valueFunc).sum(); + } + + public static BigDecimal sumBigDecimal(Collection from, Function valueFunc) { + if (CollUtil.isEmpty(from)) { + return BigDecimal.ZERO; + } + return from.stream().map(valueFunc).map(NumberUtils::zeroIfNull).reduce(BigDecimal.ZERO, BigDecimal::add); + } + + public static long count(Collection from, Predicate predicate) { + if (CollUtil.isEmpty(from)) { + return 0L; + } + return from.stream().filter(predicate).count(); + } + + public static long distinctCount(Collection from, Function keyMapper) { + if (CollUtil.isEmpty(from)) { + return 0L; + } + return from.stream().map(keyMapper).filter(Objects::nonNull).distinct().count(); + } + public static List filterList(Collection from, Predicate 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 > S convertSetBySupplier( + Collection from, Function func, Supplier supplier) { + if (CollUtil.isEmpty(from)) { + return supplier.get(); + } + return from.stream().map(func).filter(Objects::nonNull) + .collect(Collectors.toCollection(supplier)); + } + public static Set convertLinkedSet(Collection from, Function func) { if (CollUtil.isEmpty(from)) { return new LinkedHashSet<>(); @@ -398,4 +437,4 @@ public class CollectionUtils { return list; } -} \ No newline at end of file +} diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java index f942d6e..a78eda1 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java @@ -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 subtractTimeRanges(List sourceRanges, TimeRange excludedRange) { + List 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 mergeTimeRanges(List timeRanges) { + if (CollUtil.isEmpty(timeRanges)) { + return Collections.emptyList(); + } + List sortedRanges = timeRanges.stream() + .sorted(Comparator.comparing(TimeRange::getStartTime)) + .collect(Collectors.toList()); + List 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 intersectTimeRanges( + List firstRanges, List secondRanges) { + List 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 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 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 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 timeRanges = buildDailyTimeRanges( + date.minusDays(1), date.plusDays(1), beginTime, endTime); + return CollUtil.findOne(timeRanges, timeRange -> isBetween( + timeRange.getStartTime(), timeRange.getEndTime(), time)); + } + /** * 将 Unix 秒时间戳转换为默认时区的本地时间 * @@ -185,6 +514,62 @@ public class LocalDateTimeUtils { } /** + * 获得指定日期的开始时间 + * + * @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 * @@ -196,6 +581,16 @@ public class LocalDateTimeUtils { } /** + * 获取指定日期的开始时间 + * + * @param date 日期 + * @return 当日开始时间 + */ + public static LocalDateTime beginOfDay(LocalDateTime date) { + return date.with(LocalTime.MIN); + } + + /** * 获取指定日期所在的月份的最后时间 * 例如:2023-09-30 23:59:59,999 * @@ -207,6 +602,82 @@ public class LocalDateTimeUtils { } /** + * 获得指定年月的月初时间 + * + * @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); + } + + /** * 获得指定日期所在季度 * * @param date 日期 @@ -353,7 +824,11 @@ public class LocalDateTimeUtils { * @return 日期列表,包含开始日期 */ public static List getDateList(LocalDate startDate, int days) { - return startDate.datesUntil(startDate.plusDays(days)).toList(); + List 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; + + } + } diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/MoneyUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/MoneyUtils.java index 90888d7..7028151 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/MoneyUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/MoneyUtils.java @@ -97,6 +97,39 @@ public class MoneyUtils { } /** + * 格式化金额,默认进行四舍五入 + * + * 位数:{@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); + } + + /** * 金额相乘,默认进行四舍五入 * * 位数:{@link #PRICE_SCALE} @@ -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)); } /** diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/NumberUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/NumberUtils.java index c2787f4..544a797 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/NumberUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/NumberUtils.java @@ -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 values) { if (CollUtil.isEmpty(values)) { return false; diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/PageUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/PageUtils.java index cf077db..472fff8 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/PageUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/PageUtils.java @@ -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; /** @@ -25,6 +28,27 @@ public class PageUtils { } /** + * 对内存列表进行分页 + * + * @param list 数据列表 + * @param pageParam 分页参数 + * @param 数据类型 + * @return 分页结果 + */ + public static PageResult buildPageResult(List 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); + } + + /** * 构建排序字段(默认倒序) * * @param func 排序字段的 Lambda 表达式 diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/servlet/ServletUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/servlet/ServletUtils.java index 905f5d5..3501bcc 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/servlet/ServletUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/servlet/ServletUtils.java @@ -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); + } + } diff --git a/yudao-framework/yudao-common/src/test/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtilsTest.java b/yudao-framework/yudao-common/src/test/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtilsTest.java index 0e44645..f0aaa63 100644 --- a/yudao-framework/yudao-common/src/test/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtilsTest.java +++ b/yudao-framework/yudao-common/src/test/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtilsTest.java @@ -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; @@ -27,6 +29,38 @@ public class CollectionUtilsTest { } @Test + public void testSum() { + assertEquals(6L, CollectionUtils.sum(Arrays.asList(1L, 2L, 3L), Long::longValue)); + assertEquals(0L, CollectionUtils.sum(Collections.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 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.emptyList(), + Integer::valueOf, TreeSet::new); + assertEquals(TreeSet.class, result.getClass()); + } + + @Test public void testDiffList() { // 准备参数 Collection oldList = Arrays.asList( diff --git a/yudao-framework/yudao-common/src/test/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtilsTest.java b/yudao-framework/yudao-common/src/test/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtilsTest.java new file mode 100644 index 0000000..abb7a66 --- /dev/null +++ b/yudao-framework/yudao-common/src/test/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtilsTest.java @@ -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 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 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 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 firstRanges = Arrays.asList( + new TimeRange(date.atTime(9, 0), date.atTime(12, 0)), + new TimeRange(date.atTime(13, 0), date.atTime(18, 0))); + List secondRanges = Arrays.asList( + new TimeRange(date.atTime(10, 0), date.atTime(14, 0)), + new TimeRange(date.atTime(17, 0), date.atTime(19, 0))); + + // 调用 + List 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 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 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()); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/StringListConvert.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/StringListConvert.java new file mode 100644 index 0000000..edb243e --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/StringListConvert.java @@ -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> { + + @Override + public WriteCellData convertToExcelData(List value, + ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + return new WriteCellData<>(CollUtil.isEmpty(value) ? "" : String.join("/", value)); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java index f4f17ec..d771d18 100644 --- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java @@ -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> head, List> 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 List read(MultipartFile file, Class 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 List read(MultipartFile file, Class 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 rows = new ArrayList<>(Math.min(maxRowCount, 1024)); + try (InputStream inputStream = file.getInputStream()) { + FastExcelFactory.read(inputStream, head, new ReadListener() { + + @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> 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(); + } + } + } diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/PoiExcelUtils.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/PoiExcelUtils.java new file mode 100644 index 0000000..933d348 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/PoiExcelUtils.java @@ -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 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; + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/test/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtilsTest.java b/yudao-framework/yudao-spring-boot-starter-excel/src/test/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtilsTest.java new file mode 100644 index 0000000..c6964d5 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/test/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtilsTest.java @@ -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 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; + } + + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java index 08bdf19..79e95fe 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java @@ -145,6 +145,18 @@ public interface BaseMapperX extends MPJBaseMapper { } /** + * 获得满足条件的第 1 条记录,并使用 FOR UPDATE 锁定。 + * + * 注意:需要在事务中调用,否则锁会立即释放。 + * + * @param queryWrapper 查询条件 + * @return 实体 + */ + default T selectFirstOneForUpdate(LambdaQueryWrapper queryWrapper) { + return selectOne(queryWrapper.last("LIMIT 1 FOR UPDATE")); + } + + /** * 获取满足条件的第 1 条记录 * * 目的:解决并发场景下,插入多条记录后,使用 selectOne 会报错的问题 diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/LambdaQueryWrapperX.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/LambdaQueryWrapperX.java index b766199..b5cbcd3 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/LambdaQueryWrapperX.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/LambdaQueryWrapperX.java @@ -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 extends LambdaQueryWrapper { return betweenIfPresent(column, val1, val2); } + /** + * 拼接两个闭区间的重叠条件 + * + *

记录区间与查询区间重叠的条件为:记录开始值小于等于查询结束值, + * 且记录结束值大于等于查询开始值。不能使用单字段 {@code BETWEEN} 代替, + * 否则会漏掉完整覆盖查询区间的记录。

+ * + * @param beginColumn 记录开始值字段 + * @param endColumn 记录结束值字段 + * @param times 查询闭区间 + * @return 查询条件 + */ + public LambdaQueryWrapperX betweenIfPresent( + SFunction beginColumn, SFunction endColumn, LocalDateTime[] times) { + return leIfPresent(beginColumn, ArrayUtils.get(times, 1)) + .geIfPresent(endColumn, ArrayUtils.get(times, 0)); + } + // ========== 重写父类方法,方便链式调用 ========== @Override diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/QueryWrapperX.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/QueryWrapperX.java index bff0122..525d575 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/QueryWrapperX.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/QueryWrapperX.java @@ -102,16 +102,9 @@ public class QueryWrapperX extends QueryWrapper { } public QueryWrapperX betweenIfPresent(String column, Object[] values) { - if (values != null && values.length != 0 && values[0] != null && values[1] != null) { - return (QueryWrapperX) super.between(column, values[0], values[1]); - } - if (values != null && values.length != 0 && values[0] != null) { - return (QueryWrapperX) ge(column, values[0]); - } - if (values != null && values.length != 0 && values[1] != null) { - return (QueryWrapperX) 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); } // ========== 重写父类方法,方便链式调用 ========== diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java index ab4612c..1cdf7f1 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java @@ -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); diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/test/java/cn/iocoder/yudao/framework/mybatis/core/query/LambdaQueryWrapperXTest.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/test/java/cn/iocoder/yudao/framework/mybatis/core/query/LambdaQueryWrapperXTest.java new file mode 100644 index 0000000..e708e96 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/test/java/cn/iocoder/yudao/framework/mybatis/core/query/LambdaQueryWrapperXTest.java @@ -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() + .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() + .betweenIfPresent(TestDO::getStartTime, TestDO::getEndTime, + new LocalDateTime[]{beginTime, endTime}) + .getSqlSegment(); + assertTrue(sqlSegment.contains("start_time <=")); + assertTrue(sqlSegment.contains("end_time >=")); + + // 调用,并断言:单边范围只追加对应的重叠边界 + sqlSegment = new LambdaQueryWrapperX() + .betweenIfPresent(TestDO::getStartTime, TestDO::getEndTime, + new LocalDateTime[]{beginTime}) + .getSqlSegment(); + assertTrue(sqlSegment.contains("end_time >=")); + sqlSegment = new LambdaQueryWrapperX() + .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; + + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/test/java/cn/iocoder/yudao/framework/mybatis/core/query/QueryWrapperXTest.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/test/java/cn/iocoder/yudao/framework/mybatis/core/query/QueryWrapperXTest.java new file mode 100644 index 0000000..09a2674 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/test/java/cn/iocoder/yudao/framework/mybatis/core/query/QueryWrapperXTest.java @@ -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")); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/test/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtilsTest.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/test/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtilsTest.java index abe8891..b74558c 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/test/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtilsTest.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/test/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtilsTest.java @@ -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; @@ -129,6 +130,14 @@ public class MyBatisUtilsTest { } @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() { // 准备参数 QueryWrapper query = new QueryWrapper<>(); diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApi.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApi.java index b8ccad6..7f1b9cf 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApi.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApi.java @@ -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); } diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java index 5ebb12f..5377314 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java @@ -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)); + } + } diff --git a/yudao-module-hrm/pom.xml b/yudao-module-hrm/pom.xml new file mode 100644 index 0000000..750c2dc --- /dev/null +++ b/yudao-module-hrm/pom.xml @@ -0,0 +1,69 @@ + + + + cn.iocoder.boot + yudao + ${revision} + + 4.0.0 + yudao-module-hrm + jar + + ${project.artifactId} + + hrm 包下,人力资源管理(Human Resource Management) + 例如说:员工、招聘、考勤、薪资、社保、绩效等等 + + + + + cn.iocoder.boot + yudao-module-system + ${revision} + + + cn.iocoder.boot + yudao-module-bpm + ${revision} + + + + + cn.iocoder.boot + yudao-spring-boot-starter-web + + + + cn.iocoder.boot + yudao-spring-boot-starter-security + + + + + cn.iocoder.boot + yudao-spring-boot-starter-mybatis + + + + + cn.iocoder.boot + yudao-spring-boot-starter-excel + + + + + cn.iocoder.boot + yudao-spring-boot-starter-job + + + + + cn.iocoder.boot + yudao-spring-boot-starter-test + test + + + + diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceClockController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceClockController.java new file mode 100644 index 0000000..2721cee --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceClockController.java @@ -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 createAttendanceClock(@Valid @RequestBody HrmAttendanceClockSaveReqVO createReqVO) { + return success(attendanceClockService.createAttendanceClock(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "修改打卡记录") + @PreAuthorize("@ss.hasPermission('hrm:attendance:clock:update')") + public CommonResult 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 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 deleteAttendanceClockList( + @RequestParam("ids") @NotEmpty(message = "打卡记录不能为空") List 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 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 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> getAttendanceClockPage( + @Validated HrmAttendanceClockPageReqVO pageReqVO) { + PageResult 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 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 buildAttendanceClockRespVOList(List clocks) { + if (CollUtil.isEmpty(clocks)) { + return Collections.emptyList(); + } + // 1. 获得员工和部门信息 + Map employeeMap = employeeService.getEmployeeMap( + convertSet(clocks, HrmAttendanceClockDO::getEmployeeId)); + Map 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())); + }); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceGroupController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceGroupController.java new file mode 100644 index 0000000..9a7a4af --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceGroupController.java @@ -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 createAttendanceGroup(@Valid @RequestBody HrmAttendanceGroupSaveReqVO createReqVO) { + return success(attendanceGroupService.createAttendanceGroup(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "修改考勤组") + @PreAuthorize("@ss.hasPermission('hrm:attendance:group:update')") + public CommonResult 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 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 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 getMyAttendanceGroup(@RequestParam("employeeId") Long employeeId) { + return success(buildAttendanceGroupRespVO(attendanceGroupService.getMyAttendanceGroup(employeeId))); + } + + @GetMapping("/page") + @Operation(summary = "获得考勤组分页") + @PreAuthorize("@ss.hasPermission('hrm:attendance:group:query')") + public CommonResult> getAttendanceGroupPage( + @Validated HrmAttendanceGroupPageReqVO pageReqVO) { + PageResult 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> 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 buildAttendanceGroupRespVOList(List list) { + Map deptMap = deptApi.getDeptMap( + convertSetByFlatMap(list, HrmAttendanceGroupDO::getDeptIds, Collection::stream)); + Map 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))); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceHolidayController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceHolidayController.java new file mode 100644 index 0000000..40d3869 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceHolidayController.java @@ -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 createAttendanceHoliday(@Valid @RequestBody HrmAttendanceHolidaySaveReqVO createReqVO) { + return success(attendanceHolidayService.createAttendanceHoliday(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "修改考勤节假日") + @PreAuthorize("@ss.hasPermission('hrm:attendance:holiday:update')") + public CommonResult 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 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 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 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> getAttendanceHolidayPage( + @Validated HrmAttendanceHolidayPageReqVO pageReqVO) { + PageResult pageResult = attendanceHolidayService.getAttendanceHolidayPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, HrmAttendanceHolidayRespVO.class)); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceLeaveController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceLeaveController.java new file mode 100644 index 0000000..a7c81b9 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceLeaveController.java @@ -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 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> getAttendanceLeavePage( + @Validated HrmAttendanceLeavePageReqVO pageReqVO) { + PageResult 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 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 buildAttendanceLeaveRespVOList(List list) { + Map employeeMap = employeeService.getEmployeeMap( + convertSet(list, HrmAttendanceLeaveDO::getEmployeeId)); + Map 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; + }); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceStatisticsController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceStatisticsController.java new file mode 100644 index 0000000..d7bb6d1 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceStatisticsController.java @@ -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 getAttendanceDailyDetail( + @Valid HrmAttendanceDailyDetailReqVO reqVO) { + return success(attendanceStatisticsService.getAttendanceDailyDetail(reqVO)); + } + + @GetMapping("/month-record-page") + @Operation(summary = "查询员工月度考勤汇总") + @PreAuthorize("@ss.hasPermission('hrm:attendance:statistics:query')") + public CommonResult> getAttendanceMonthRecordPage( + @Valid HrmAttendanceMonthRecordPageReqVO reqVO) { + PageResult 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> getAttendanceMonthDailyOverviewPage( + @Valid HrmAttendanceMonthRecordPageReqVO reqVO) { + PageResult 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 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 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 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 list) + throws IOException { + List> 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> buildAttendanceMonthDailyOverviewData( + YearMonth month, List list) { + List> result = new ArrayList<>(list.size()); + for (HrmAttendanceMonthDailyOverviewRespVO row : list) { + List 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 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 list) { + Map 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 list) { + Map deptMap = deptApi.getDeptMap( + convertSet(list, HrmAttendanceMonthRecordRespVO::getDeptId)); + list.forEach(vo -> MapUtils.findAndThen(deptMap, vo.getDeptId(), dept -> vo.setDeptName(dept.getName()))); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/clock/HrmAttendanceClockPageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/clock/HrmAttendanceClockPageReqVO.java new file mode 100644 index 0000000..8d6df1d --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/clock/HrmAttendanceClockPageReqVO.java @@ -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 deptIds; + + @Schema(hidden = true) + private Collection 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 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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/clock/HrmAttendanceClockRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/clock/HrmAttendanceClockRespVO.java new file mode 100644 index 0000000..35c178d --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/clock/HrmAttendanceClockRespVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/clock/HrmAttendanceClockSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/clock/HrmAttendanceClockSaveReqVO.java new file mode 100644 index 0000000..41e50ce --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/clock/HrmAttendanceClockSaveReqVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/clock/HrmAttendanceClockShiftReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/clock/HrmAttendanceClockShiftReqVO.java new file mode 100644 index 0000000..0b03d7e --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/clock/HrmAttendanceClockShiftReqVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/clock/HrmAttendanceClockShiftRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/clock/HrmAttendanceClockShiftRespVO.java new file mode 100644 index 0000000..c1ff2f9 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/clock/HrmAttendanceClockShiftRespVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/group/HrmAttendanceGroupPageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/group/HrmAttendanceGroupPageReqVO.java new file mode 100644 index 0000000..db34028 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/group/HrmAttendanceGroupPageReqVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/group/HrmAttendanceGroupRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/group/HrmAttendanceGroupRespVO.java new file mode 100644 index 0000000..f3e3609 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/group/HrmAttendanceGroupRespVO.java @@ -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 specialDates; + + @Schema(description = "部门范围") + private List deptIds; + + @Schema(description = "适用部门名称") + private List deptNames; + + @Schema(description = "员工范围") + private List employeeIds; + + @Schema(description = "适用员工名称") + private List employeeNames; + + @Schema(description = "班次配置") + private List shifts; + + @Schema(description = "打卡地点") + private List points; + + @Schema(description = "打卡 WiFi") + private List 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 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; + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/group/HrmAttendanceGroupSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/group/HrmAttendanceGroupSaveReqVO.java new file mode 100644 index 0000000..aa476f8 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/group/HrmAttendanceGroupSaveReqVO.java @@ -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 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 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 shifts; + + @Schema(description = "打卡地点", requiredMode = Schema.RequiredMode.REQUIRED) + @Valid + @NotNull(message = "打卡地点不能为空") + @DiffLogField(name = "打卡地点") + private List points; + + @Schema(description = "打卡 WiFi", requiredMode = Schema.RequiredMode.REQUIRED) + @Valid + @NotNull(message = "打卡 WiFi 不能为空") + @DiffLogField(name = "打卡 WiFi") + private List 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 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 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 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())); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/holiday/HrmAttendanceHolidayPageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/holiday/HrmAttendanceHolidayPageReqVO.java new file mode 100644 index 0000000..84b817c --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/holiday/HrmAttendanceHolidayPageReqVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/holiday/HrmAttendanceHolidayRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/holiday/HrmAttendanceHolidayRespVO.java new file mode 100644 index 0000000..3e520e8 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/holiday/HrmAttendanceHolidayRespVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/holiday/HrmAttendanceHolidaySaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/holiday/HrmAttendanceHolidaySaveReqVO.java new file mode 100644 index 0000000..d999235 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/holiday/HrmAttendanceHolidaySaveReqVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/leave/HrmAttendanceLeaveCancelReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/leave/HrmAttendanceLeaveCancelReqVO.java new file mode 100644 index 0000000..1089d4c --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/leave/HrmAttendanceLeaveCancelReqVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/leave/HrmAttendanceLeaveCreateReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/leave/HrmAttendanceLeaveCreateReqVO.java new file mode 100644 index 0000000..c01783c --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/leave/HrmAttendanceLeaveCreateReqVO.java @@ -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); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/leave/HrmAttendanceLeavePageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/leave/HrmAttendanceLeavePageReqVO.java new file mode 100644 index 0000000..dc39afc --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/leave/HrmAttendanceLeavePageReqVO.java @@ -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 deptIds; + + @Schema(description = "员工姓名或工号", example = "张三") + private String employeeKeyword; + + @Schema(description = "请假类型数组", example = "[事假, 病假]") + private List 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 employeeIds; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/leave/HrmAttendanceLeaveRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/leave/HrmAttendanceLeaveRespVO.java new file mode 100644 index 0000000..d21dc36 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/leave/HrmAttendanceLeaveRespVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceDailyDetailReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceDailyDetailReqVO.java new file mode 100644 index 0000000..2b77cc8 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceDailyDetailReqVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceDailyDetailRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceDailyDetailRespVO.java new file mode 100644 index 0000000..4708af0 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceDailyDetailRespVO.java @@ -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 clockList; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceDailyOverviewRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceDailyOverviewRespVO.java new file mode 100644 index 0000000..ed72469 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceDailyOverviewRespVO.java @@ -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 clocks; + + @Schema(description = "考勤结果") + private String attendanceResult; + + @Schema(description = "打卡概况展示项") + private List 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; + + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceMonthDailyOverviewRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceMonthDailyOverviewRespVO.java new file mode 100644 index 0000000..f6cc003 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceMonthDailyOverviewRespVO.java @@ -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 dailyClockMap; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceMonthDetailRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceMonthDetailRespVO.java new file mode 100644 index 0000000..32e83d6 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceMonthDetailRespVO.java @@ -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 dailyDetails; + + @Schema(description = "请假记录") + private List leaves; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceMonthRecordPageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceMonthRecordPageReqVO.java new file mode 100644 index 0000000..b65b106 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceMonthRecordPageReqVO.java @@ -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 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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceMonthRecordRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceMonthRecordRespVO.java new file mode 100644 index 0000000..5aa1eb7 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceMonthRecordRespVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeCertificateController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeCertificateController.java new file mode 100644 index 0000000..770f50e --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeCertificateController.java @@ -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> getCertificateList( + @RequestParam("employeeId") Long employeeId) { + List certificates = certificateService.getCertificateListByEmployeeId(employeeId); + return success(BeanUtils.toBean(certificates, HrmEmployeeCertificateRespVO.class)); + } + + @PostMapping("/create") + @Operation(summary = "创建员工证书") + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") + public CommonResult createCertificate( + @Valid @RequestBody HrmEmployeeCertificateSaveReqVO reqVO) { + return success(certificateService.createCertificate(reqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新员工证书") + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") + public CommonResult 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 deleteCertificate(@RequestParam("id") Long id) { + certificateService.deleteCertificate(id); + return success(true); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeChangeRecordController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeChangeRecordController.java new file mode 100644 index 0000000..a8cba82 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeChangeRecordController.java @@ -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> getEmployeeChangeRecordList( + @RequestParam("employeeId") Long employeeId) { + return success(buildEmployeeChangeRecordRespVOList( + changeRecordService.getEmployeeChangeRecordListByEmployeeId(employeeId))); + } + + // ==================== 拼接 VO ==================== + + private List buildEmployeeChangeRecordRespVOList( + List changeRecords) { + Map deptMap = deptApi.getDeptMap( + convertSetByFlatMap(changeRecords, changeRecord -> Stream.of(changeRecord.getOldDeptId(), changeRecord.getNewDeptId()))); + Map 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())); + }); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeContactController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeContactController.java new file mode 100644 index 0000000..d68cfd7 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeContactController.java @@ -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> getContactList( + @RequestParam("employeeId") Long employeeId) { + List contacts = contactService.getContactListByEmployeeId(employeeId); + return success(BeanUtils.toBean(contacts, HrmEmployeeContactRespVO.class)); + } + + @PostMapping("/create") + @Operation(summary = "创建员工联系人") + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") + public CommonResult createContact(@Valid @RequestBody HrmEmployeeContactSaveReqVO reqVO) { + return success(contactService.createContact(reqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新员工联系人") + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") + public CommonResult 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 deleteContact(@RequestParam("id") Long id) { + contactService.deleteContact(id); + return success(true); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeContractController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeContractController.java new file mode 100644 index 0000000..365b71b --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeContractController.java @@ -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> getContractList( + @RequestParam("employeeId") Long employeeId) { + List contracts = contractService.getContractListByEmployeeId(employeeId); + return success(BeanUtils.toBean(contracts, HrmEmployeeContractRespVO.class)); + } + + @PostMapping("/create") + @Operation(summary = "创建员工合同") + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") + public CommonResult createContract(@Valid @RequestBody HrmEmployeeContractSaveReqVO reqVO) { + return success(contractService.createContract(reqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新员工合同") + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") + public CommonResult 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 deleteContract(@RequestParam("id") Long id) { + contractService.deleteContract(id); + return success(true); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeController.java new file mode 100644 index 0000000..be75c78 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeController.java @@ -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 createEmployee(@Valid @RequestBody HrmEmployeeSaveReqVO createReqVO) { + return success(employeeService.createEmployee(createReqVO)); + } + + @PostMapping("/create-list") + @Operation(summary = "从未建档后台用户批量创建员工档案") + @PreAuthorize("@ss.hasPermission('hrm:employee:create')") + public CommonResult> 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> 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 sendEmployeeProfileFillMessage( + @RequestParam("ids") @NotEmpty(message = "员工不能为空") + @Size(max = 100, message = "单次最多通知 100 名员工") List ids) { + return success(employeeService.sendEmployeeProfileFillMessage(ids)); + } + + @PutMapping("/update") + @Operation(summary = "更新员工档案") + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") + public CommonResult 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 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 deleteEmployeeList(@RequestParam("ids") @NotEmpty List 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 getEmployee(@RequestParam("id") Long id) { + return success(buildEmployeeRespVO(employeeService.getEmployee(id))); + } + + @PutMapping("/confirm-entry") + @Operation(summary = "确认员工入职") + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") + public CommonResult confirmEmployeeEntry(@Valid @RequestBody HrmEmployeeConfirmEntryReqVO reqVO) { + employeeService.confirmEmployeeEntry(reqVO); + return success(true); + } + + @PostMapping("/rehire") + @Operation(summary = "办理离职员工再入职") + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") + public CommonResult rehireEmployee(@Valid @RequestBody HrmEmployeeRehireReqVO reqVO) { + employeeService.rehireEmployee(reqVO); + return success(true); + } + + @PostMapping("/regular") + @Operation(summary = "办理员工转正") + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") + public CommonResult regularEmployee(@Valid @RequestBody HrmEmployeeRegularReqVO reqVO) { + employeeService.regularEmployee(reqVO); + return success(true); + } + + @PostMapping("/transfer") + @Operation(summary = "办理员工调岗") + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") + public CommonResult transferEmployee(@Valid @RequestBody HrmEmployeeTransferReqVO reqVO) { + employeeService.transferEmployee(reqVO); + return success(true); + } + + @PostMapping("/promote") + @Operation(summary = "办理员工晋升") + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") + public CommonResult promoteEmployee(@Valid @RequestBody HrmEmployeePromoteReqVO reqVO) { + employeeService.promoteEmployee(reqVO); + return success(true); + } + + @PostMapping("/demote") + @Operation(summary = "办理员工降级") + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") + public CommonResult 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 convertEmployeeToFullTime( + @Valid @RequestBody HrmEmployeeConvertToFullTimeReqVO reqVO) { + employeeService.convertEmployeeToFullTime(reqVO); + return success(true); + } + + @PostMapping("/quit") + @Operation(summary = "办理员工离职") + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") + public CommonResult quitEmployee(@Valid @RequestBody HrmEmployeeQuitReqVO reqVO) { + employeeService.quitEmployee(reqVO); + return success(true); + } + + @PutMapping("/cancel-quit") + @Operation(summary = "取消员工离职") + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") + public CommonResult cancelEmployeeQuit(@Valid @RequestBody HrmEmployeeCancelQuitReqVO reqVO) { + employeeService.cancelEmployeeQuit(reqVO); + return success(true); + } + + @GetMapping("/page") + @Operation(summary = "获得员工档案分页") + @PreAuthorize("@ss.hasPermission('hrm:employee:query')") + public CommonResult> getEmployeePage(@Validated HrmEmployeePageReqVO pageReqVO) { + PageResult 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> getEmployeeList(@RequestParam("ids") @NotEmpty List ids) { + return success(buildEmployeeRespVOList(employeeService.getEmployeeListByIds(ids))); + } + + @GetMapping("/simple-page") + @Operation(summary = "获得员工精简分页", description = "只返回员工选择器需要的非敏感字段") + public CommonResult> getEmployeeSimplePage( + @Validated HrmEmployeePageReqVO pageReqVO) { + PageResult 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> getEmployeeSimpleList( + @RequestParam("ids") @NotEmpty List 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 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 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 importExcel( + @RequestParam("file") MultipartFile file, + @RequestParam(value = "duplicateStrategy", required = false, defaultValue = "3") Integer duplicateStrategy) + throws Exception { + List 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> getEmployeeStatusCount( + @Validated HrmEmployeePageReqVO pageReqVO) { + Map 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> getEmployeeDeptStatistics() { + Map> countMap = employeeService.getEmployeeCountMapByDeptAndType(); + return success(convertList(countMap.entrySet(), entry -> { + Map 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 buildEmployeeRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 1.1 后台用户信息 + Map userMap = adminUserApi.getUserMap( + convertSet(list, HrmEmployeeDO::getUserId)); + // 1.2 部门信息 + Map deptMap = deptApi.getDeptMap(convertSet(list, HrmEmployeeDO::getDeptId)); + // 1.3 直属上级员工信息 + Map leaderEmployeeMap = employeeService.getEmployeeMap( + convertSet(list, HrmEmployeeDO::getLeaderEmployeeId)); + // 1.4 招聘渠道信息 + Map channelMap = recruitChannelService.getRecruitChannelMap( + convertSet(list, HrmEmployeeDO::getChannelId)); + // 1.5 工资卡和社保资料 + Set employeeIds = convertSet(list, HrmEmployeeDO::getId); + Map salaryCardMap = + employeeSalaryCardService.getSalaryCardMap(employeeIds); + Map 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 buildEmployeeSimpleRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 1. 查询部门和直属上级信息 + Map deptMap = deptApi.getDeptMap(convertSet(list, HrmEmployeeDO::getDeptId)); + Map 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 buildEmployeeExportRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 1. 查询导出展示信息,不加载工资卡和社保等敏感聚合 + Map userMap = adminUserApi.getUserMap(convertSet(list, HrmEmployeeDO::getUserId)); + Map deptMap = deptApi.getDeptMap(convertSet(list, HrmEmployeeDO::getDeptId)); + Map leaderEmployeeMap = employeeService.getEmployeeMap( + convertSet(list, HrmEmployeeDO::getLeaderEmployeeId)); + Map 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; + }); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeEducationExperienceController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeEducationExperienceController.java new file mode 100644 index 0000000..abb6800 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeEducationExperienceController.java @@ -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> getEducationExperienceList( + @RequestParam("employeeId") Long employeeId) { + List educationExperiences = + educationExperienceService.getEducationExperienceListByEmployeeId(employeeId); + return success(BeanUtils.toBean(educationExperiences, HrmEmployeeEducationExperienceRespVO.class)); + } + + @PostMapping("/create") + @Operation(summary = "创建员工教育经历") + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") + public CommonResult createEducationExperience( + @Valid @RequestBody HrmEmployeeEducationExperienceSaveReqVO reqVO) { + return success(educationExperienceService.createEducationExperience(reqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新员工教育经历") + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") + public CommonResult 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 deleteEducationExperience(@RequestParam("id") Long id) { + educationExperienceService.deleteEducationExperience(id); + return success(true); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeFieldConfigController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeFieldConfigController.java new file mode 100644 index 0000000..48c3f2a --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeFieldConfigController.java @@ -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> 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 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> getEmployeeArchiveFieldConfigList() { + return success(employeeFieldConfigService.getEmployeeArchiveFieldConfigList()); + } + + @PutMapping("/archive-field/save") + @Operation(summary = "保存员工档案字段配置") + @PreAuthorize("@ss.hasPermission('hrm:employee:config:update')") + public CommonResult saveEmployeeArchiveFieldConfig( + @Valid @RequestBody HrmEmployeeArchiveFieldConfigSaveReqVO reqVO) { + employeeFieldConfigService.saveEmployeeArchiveFieldConfig(reqVO); + return success(true); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeFileController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeFileController.java new file mode 100644 index 0000000..3e54592 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeFileController.java @@ -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> getEmployeeFileList( + @RequestParam("employeeId") Long employeeId) { + List employeeFiles = employeeFileService.getEmployeeFileList(employeeId); + return success(BeanUtils.toBean(employeeFiles, HrmEmployeeFileRespVO.class)); + } + + @PutMapping("/save") + @Operation(summary = "保存员工材料附件") + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") + public CommonResult saveEmployeeFiles(@Valid @RequestBody HrmEmployeeFileSaveReqVO reqVO) { + employeeFileService.saveEmployeeFiles(reqVO); + return success(true); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeePersonalNoteController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeePersonalNoteController.java new file mode 100644 index 0000000..8dfacb5 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeePersonalNoteController.java @@ -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 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 deletePersonalNote(@RequestParam("id") Long id) { + Long employeeId = employeeService.validateEmployeeBySelf(getLoginUserId()).getId(); + personalNoteService.deletePersonalNote(employeeId, id); + return success(true); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeQuitInfoController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeQuitInfoController.java new file mode 100644 index 0000000..b26172e --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeQuitInfoController.java @@ -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 getQuitInfo( + @RequestParam("employeeId") Long employeeId) { + HrmEmployeeQuitInfoDO quitInfo = quitInfoService.getQuitInfoByEmployeeId(employeeId); + return success(BeanUtils.toBean(quitInfo, HrmEmployeeQuitInfoRespVO.class)); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeSalaryCardController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeSalaryCardController.java new file mode 100644 index 0000000..854d10c --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeSalaryCardController.java @@ -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 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 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 deleteEmployeeSalaryCard(@RequestParam("employeeId") Long employeeId) { + salaryCardService.deleteSalaryCardByEmployeeId(employeeId); + return success(true); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeTrainingExperienceController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeTrainingExperienceController.java new file mode 100644 index 0000000..b90bf12 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeTrainingExperienceController.java @@ -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> getTrainingExperienceList( + @RequestParam("employeeId") Long employeeId) { + List trainingExperiences = + trainingExperienceService.getTrainingExperienceListByEmployeeId(employeeId); + return success(BeanUtils.toBean(trainingExperiences, HrmEmployeeTrainingExperienceRespVO.class)); + } + + @PostMapping("/create") + @Operation(summary = "创建员工培训经历") + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") + public CommonResult createTrainingExperience( + @Valid @RequestBody HrmEmployeeTrainingExperienceSaveReqVO reqVO) { + return success(trainingExperienceService.createTrainingExperience(reqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新员工培训经历") + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") + public CommonResult 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 deleteTrainingExperience(@RequestParam("id") Long id) { + trainingExperienceService.deleteTrainingExperience(id); + return success(true); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeWorkExperienceController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeWorkExperienceController.java new file mode 100644 index 0000000..47d23b7 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeWorkExperienceController.java @@ -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> getWorkExperienceList( + @RequestParam("employeeId") Long employeeId) { + List workExperiences = + workExperienceService.getWorkExperienceListByEmployeeId(employeeId); + return success(BeanUtils.toBean(workExperiences, HrmEmployeeWorkExperienceRespVO.class)); + } + + @PostMapping("/create") + @Operation(summary = "创建员工工作经历") + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") + public CommonResult createWorkExperience( + @Valid @RequestBody HrmEmployeeWorkExperienceSaveReqVO reqVO) { + return success(workExperienceService.createWorkExperience(reqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新员工工作经历") + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") + public CommonResult 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 deleteWorkExperience(@RequestParam("id") Long id) { + workExperienceService.deleteWorkExperience(id); + return success(true); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/certificate/HrmEmployeeCertificateRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/certificate/HrmEmployeeCertificateRespVO.java new file mode 100644 index 0000000..25ba685 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/certificate/HrmEmployeeCertificateRespVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/certificate/HrmEmployeeCertificateSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/certificate/HrmEmployeeCertificateSaveReqVO.java new file mode 100644 index 0000000..79b48ed --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/certificate/HrmEmployeeCertificateSaveReqVO.java @@ -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); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeeChangeRecordCreateReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeeChangeRecordCreateReqVO.java new file mode 100644 index 0000000..0c514c8 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeeChangeRecordCreateReqVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeeChangeRecordRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeeChangeRecordRespVO.java new file mode 100644 index 0000000..5c747a1 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeeChangeRecordRespVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeeConvertToFullTimeReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeeConvertToFullTimeReqVO.java new file mode 100644 index 0000000..1907443 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeeConvertToFullTimeReqVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeeDemoteReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeeDemoteReqVO.java new file mode 100644 index 0000000..0442e0e --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeeDemoteReqVO.java @@ -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); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeePromoteReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeePromoteReqVO.java new file mode 100644 index 0000000..3b2b1c6 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeePromoteReqVO.java @@ -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); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeeRegularReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeeRegularReqVO.java new file mode 100644 index 0000000..86d6c1f --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeeRegularReqVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeeTransferReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeeTransferReqVO.java new file mode 100644 index 0000000..71ef61a --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeeTransferReqVO.java @@ -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); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/config/HrmEmployeeArchiveFieldConfigSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/config/HrmEmployeeArchiveFieldConfigSaveReqVO.java new file mode 100644 index 0000000..d6ffbf1 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/config/HrmEmployeeArchiveFieldConfigSaveReqVO.java @@ -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 fields; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/config/HrmEmployeeCreateFieldConfigSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/config/HrmEmployeeCreateFieldConfigSaveReqVO.java new file mode 100644 index 0000000..f45f17f --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/config/HrmEmployeeCreateFieldConfigSaveReqVO.java @@ -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 fields; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/config/HrmEmployeeFieldConfigRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/config/HrmEmployeeFieldConfigRespVO.java new file mode 100644 index 0000000..cef80ed --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/config/HrmEmployeeFieldConfigRespVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/config/HrmEmployeeFieldConfigSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/config/HrmEmployeeFieldConfigSaveReqVO.java new file mode 100644 index 0000000..d7e773c --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/config/HrmEmployeeFieldConfigSaveReqVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/config/HrmEmployeeFieldConfigValueVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/config/HrmEmployeeFieldConfigValueVO.java new file mode 100644 index 0000000..3b1c88b --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/config/HrmEmployeeFieldConfigValueVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/contact/HrmEmployeeContactRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/contact/HrmEmployeeContactRespVO.java new file mode 100644 index 0000000..6b0c2f0 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/contact/HrmEmployeeContactRespVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/contact/HrmEmployeeContactSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/contact/HrmEmployeeContactSaveReqVO.java new file mode 100644 index 0000000..46e8cd8 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/contact/HrmEmployeeContactSaveReqVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/contract/HrmEmployeeContractRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/contract/HrmEmployeeContractRespVO.java new file mode 100644 index 0000000..cb4265d --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/contract/HrmEmployeeContractRespVO.java @@ -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 fileUrls; + + @Schema(description = "排序", example = "1") + private Integer sort; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/contract/HrmEmployeeContractSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/contract/HrmEmployeeContractSaveReqVO.java new file mode 100644 index 0000000..d2c60ed --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/contract/HrmEmployeeContractSaveReqVO.java @@ -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 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; + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/educationexperience/HrmEmployeeEducationExperienceRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/educationexperience/HrmEmployeeEducationExperienceRespVO.java new file mode 100644 index 0000000..7efd972 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/educationexperience/HrmEmployeeEducationExperienceRespVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/educationexperience/HrmEmployeeEducationExperienceSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/educationexperience/HrmEmployeeEducationExperienceSaveReqVO.java new file mode 100644 index 0000000..aa40592 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/educationexperience/HrmEmployeeEducationExperienceSaveReqVO.java @@ -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); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeCancelQuitReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeCancelQuitReqVO.java new file mode 100644 index 0000000..97e13e3 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeCancelQuitReqVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeConfirmEntryReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeConfirmEntryReqVO.java new file mode 100644 index 0000000..ec9a6b8 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeConfirmEntryReqVO.java @@ -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()); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeCreateFromUserReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeCreateFromUserReqVO.java new file mode 100644 index 0000000..2413090 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeCreateFromUserReqVO.java @@ -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); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeDeptStatisticsRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeDeptStatisticsRespVO.java new file mode 100644 index 0000000..7533fb7 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeDeptStatisticsRespVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeImportExcelVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeImportExcelVO.java new file mode 100644 index 0000000..87b37db --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeImportExcelVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeImportRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeImportRespVO.java new file mode 100644 index 0000000..a555dc0 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeImportRespVO.java @@ -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 createJobNumbers; + + @Schema(description = "更新成功的工号数组", requiredMode = Schema.RequiredMode.REQUIRED) + private List updateJobNumbers; + + @Schema(description = "因重复而跳过的工号数组", requiredMode = Schema.RequiredMode.REQUIRED) + private List skipJobNumbers; + + @Schema(description = "导入失败的员工集合,key 为原始行号和员工标识,value 为失败原因", + requiredMode = Schema.RequiredMode.REQUIRED) + private Map failureJobNumbers; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeListReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeListReqVO.java new file mode 100644 index 0000000..0688961 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeListReqVO.java @@ -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 ids; + + @Schema(hidden = true) + private List 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 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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeNotifyRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeNotifyRespVO.java new file mode 100644 index 0000000..c54ba74 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeNotifyRespVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeePageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeePageReqVO.java new file mode 100644 index 0000000..b69b7e7 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeePageReqVO.java @@ -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 ids; + + @Schema(hidden = true) + private List 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 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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeQuitReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeQuitReqVO.java new file mode 100644 index 0000000..99b0a48 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeQuitReqVO.java @@ -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); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeRehireReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeRehireReqVO.java new file mode 100644 index 0000000..64ace83 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeRehireReqVO.java @@ -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()); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeRespVO.java new file mode 100644 index 0000000..d7e0319 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeRespVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeSaveReqVO.java new file mode 100644 index 0000000..7fcad99 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeSaveReqVO.java @@ -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); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeStatusCountRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeStatusCountRespVO.java new file mode 100644 index 0000000..6cac978 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeStatusCountRespVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/file/HrmEmployeeFileRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/file/HrmEmployeeFileRespVO.java new file mode 100644 index 0000000..a3e61b2 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/file/HrmEmployeeFileRespVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/file/HrmEmployeeFileSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/file/HrmEmployeeFileSaveReqVO.java new file mode 100644 index 0000000..4c793b6 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/file/HrmEmployeeFileSaveReqVO.java @@ -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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/personalnote/HrmEmployeePersonalNoteCreateReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/personalnote/HrmEmployeePersonalNoteCreateReqVO.java new file mode 100644 index 0000000..baf3feb --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/personalnote/HrmEmployeePersonalNoteCreateReqVO.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.personalnote; + +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.time.LocalDateTime; + +@Schema(description = "管理后台 - HRM 员工个人备忘创建 Request VO") +@Data +public class HrmEmployeePersonalNoteCreateReqVO { + + @Schema(description = "备忘内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "跟进转正材料") + @NotBlank(message = "备忘内容不能为空") + @Size(max = 1024, message = "备忘内容不能超过 1024 个字符") + private String content; + + @Schema(description = "提醒时间", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "提醒时间不能为空") + private LocalDateTime reminderTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/quitinfo/HrmEmployeeQuitInfoRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/quitinfo/HrmEmployeeQuitInfoRespVO.java new file mode 100644 index 0000000..a7bbcbf --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/quitinfo/HrmEmployeeQuitInfoRespVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.quitinfo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - HRM 员工离职信息 Response VO") +@Data +public class HrmEmployeeQuitInfoRespVO { + + @Schema(description = "离职信息编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long employeeId; + + @Schema(description = "计划离职时间") + private LocalDateTime planQuitTime; + + @Schema(description = "申请离职时间") + private LocalDateTime applyQuitTime; + + @Schema(description = "薪资结算时间") + private LocalDateTime salarySettlementTime; + + @Schema(description = "离职类型", example = "1") + private Integer type; + + @Schema(description = "离职原因", example = "1") + private Integer reason; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "离职前员工状态") + private Integer oldEmployeeStatus; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/quitinfo/HrmEmployeeQuitInfoSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/quitinfo/HrmEmployeeQuitInfoSaveReqVO.java new file mode 100644 index 0000000..ed97170 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/quitinfo/HrmEmployeeQuitInfoSaveReqVO.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.quitinfo; + +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 cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeStatusEnum; +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 HrmEmployeeQuitInfoSaveReqVO { + + @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; + + @Schema(description = "离职前员工状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "离职前员工状态不能为空") + @InEnum(value = HrmEmployeeStatusEnum.class, message = "离职前员工状态必须是 {value}") + private Integer oldEmployeeStatus; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/salarycard/HrmEmployeeSalaryCardRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/salarycard/HrmEmployeeSalaryCardRespVO.java new file mode 100644 index 0000000..d1da559 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/salarycard/HrmEmployeeSalaryCardRespVO.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.salarycard; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - HRM 员工工资卡 Response VO") +@Data +public class HrmEmployeeSalaryCardRespVO { + + @Schema(description = "工资卡编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long employeeId; + + @Schema(description = "银行卡号", example = "622202600001") + private String bankCardNumber; + + @Schema(description = "开户地区编号", example = "440300") + private Integer bankAreaId; + + @Schema(description = "开户地区名称", example = "广东省 深圳市") + private String bankAreaName; + + @Schema(description = "银行名称", example = "招商银行") + private String bankName; + + @Schema(description = "开户支行名称", example = "科技园支行") + private String bankBranchName; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/salarycard/HrmEmployeeSalaryCardSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/salarycard/HrmEmployeeSalaryCardSaveReqVO.java new file mode 100644 index 0000000..e0125bc --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/salarycard/HrmEmployeeSalaryCardSaveReqVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.salarycard; + +import cn.iocoder.yudao.module.hrm.framework.operatelog.core.HrmBankCardNumberParseFunction; +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 HrmEmployeeSalaryCardSaveReqVO { + + @Schema(description = "工资卡编号", example = "1024") + private Long id; + + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "员工不能为空") + private Long employeeId; + + @Schema(description = "银行卡号", requiredMode = Schema.RequiredMode.REQUIRED, example = "622202600001") + @NotBlank(message = "银行卡号不能为空") + @Size(max = 64, message = "银行卡号不能超过 64 个字符") + @DiffLogField(name = "银行卡号", function = HrmBankCardNumberParseFunction.NAME) + private String bankCardNumber; + + @Schema(description = "开户地区编号", example = "440300") + @DiffLogField(name = "开户地区编号") + private Integer bankAreaId; + + @Schema(description = "银行名称", example = "招商银行") + @Size(max = 64, message = "银行名称不能超过 64 个字符") + @DiffLogField(name = "银行名称") + private String bankName; + + @Schema(description = "开户支行名称", example = "科技园支行") + @Size(max = 128, message = "开户支行名称不能超过 128 个字符") + @DiffLogField(name = "开户支行名称") + private String bankBranchName; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/trainingexperience/HrmEmployeeTrainingExperienceRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/trainingexperience/HrmEmployeeTrainingExperienceRespVO.java new file mode 100644 index 0000000..ad83c06 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/trainingexperience/HrmEmployeeTrainingExperienceRespVO.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.trainingexperience; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - HRM 员工培训经历 Response VO") +@Data +public class HrmEmployeeTrainingExperienceRespVO { + + @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 course; + + @Schema(description = "培训机构名称", example = "内部学院") + private String organizationName; + + @Schema(description = "培训开始日期") + private LocalDateTime startTime; + + @Schema(description = "培训结束日期") + private LocalDateTime endTime; + + @Schema(description = "培训时长", example = "16 小时") + private String duration; + + @Schema(description = "培训成绩", example = "优秀") + private String result; + + @Schema(description = "培训证书名称", example = "PMP") + private String certificateName; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "排序", example = "1") + private Integer sort; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/trainingexperience/HrmEmployeeTrainingExperienceSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/trainingexperience/HrmEmployeeTrainingExperienceSaveReqVO.java new file mode 100644 index 0000000..72c4804 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/trainingexperience/HrmEmployeeTrainingExperienceSaveReqVO.java @@ -0,0 +1,76 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.trainingexperience; + +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 HrmEmployeeTrainingExperienceSaveReqVO { + + @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 = 128, message = "培训课程长度不能超过 128 个字符") + @DiffLogField(name = "培训课程") + private String course; + + @Schema(description = "培训机构名称", example = "内部学院") + @Size(max = 128, message = "培训机构名称长度不能超过 128 个字符") + @DiffLogField(name = "培训机构名称") + private String organizationName; + + @Schema(description = "培训开始日期") + @DiffLogField(name = "培训开始日期") + private LocalDateTime startTime; + + @Schema(description = "培训结束日期") + @DiffLogField(name = "培训结束日期") + private LocalDateTime endTime; + + @Schema(description = "培训时长", example = "16 小时") + @Size(max = 64, message = "培训时长长度不能超过 64 个字符") + @DiffLogField(name = "培训时长") + private String duration; + + @Schema(description = "培训成绩", example = "优秀") + @Size(max = 64, message = "培训成绩长度不能超过 64 个字符") + @DiffLogField(name = "培训成绩") + private String result; + + @Schema(description = "培训证书名称", example = "PMP") + @Size(max = 128, message = "培训证书名称长度不能超过 128 个字符") + @DiffLogField(name = "培训证书名称") + private String certificateName; + + @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); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/workexperience/HrmEmployeeWorkExperienceRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/workexperience/HrmEmployeeWorkExperienceRespVO.java new file mode 100644 index 0000000..4210305 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/workexperience/HrmEmployeeWorkExperienceRespVO.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.workexperience; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - HRM 员工工作经历 Response VO") +@Data +public class HrmEmployeeWorkExperienceRespVO { + + @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 workUnit; + + @Schema(description = "职务", example = "开发工程师") + private String postName; + + @Schema(description = "工作开始日期") + private LocalDateTime startTime; + + @Schema(description = "工作结束日期") + private LocalDateTime endTime; + + @Schema(description = "离职原因") + private String reason; + + @Schema(description = "证明人") + private String witnessName; + + @Schema(description = "证明人手机号") + private String witnessPhone; + + @Schema(description = "工作备注") + private String remark; + + @Schema(description = "排序", example = "1") + private Integer sort; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/workexperience/HrmEmployeeWorkExperienceSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/workexperience/HrmEmployeeWorkExperienceSaveReqVO.java new file mode 100644 index 0000000..6c8c017 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/workexperience/HrmEmployeeWorkExperienceSaveReqVO.java @@ -0,0 +1,77 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.workexperience; + +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 HrmEmployeeWorkExperienceSaveReqVO { + + @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 workUnit; + + @Schema(description = "职务", requiredMode = Schema.RequiredMode.REQUIRED, example = "开发工程师") + @NotBlank(message = "职务不能为空") + @Size(max = 255, message = "职务长度不能超过 255 个字符") + @DiffLogField(name = "职务") + private String postName; + + @Schema(description = "工作开始日期") + @DiffLogField(name = "工作开始日期") + private LocalDateTime startTime; + + @Schema(description = "工作结束日期") + @DiffLogField(name = "工作结束日期") + private LocalDateTime endTime; + + @Schema(description = "离职原因", example = "职业发展") + @Size(max = 1024, message = "离职原因长度不能超过 1024 个字符") + @DiffLogField(name = "离职原因") + private String reason; + + @Schema(description = "证明人", example = "李四") + @Size(max = 255, message = "证明人长度不能超过 255 个字符") + @DiffLogField(name = "证明人") + private String witnessName; + + @Schema(description = "证明人手机号", example = "15601691301") + @Size(max = 32, message = "证明人手机号长度不能超过 32 个字符") + @DiffLogField(name = "证明人手机号") + private String witnessPhone; + + @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); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/home/HrmHomeController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/home/HrmHomeController.java new file mode 100644 index 0000000..4e30d3b --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/home/HrmHomeController.java @@ -0,0 +1,76 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.home; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.hrm.controller.admin.home.vo.HrmHomeCalendarItemRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.home.vo.HrmHrHomeStatisticsRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.home.vo.HrmTeamHomeStatisticsRespVO; +import cn.iocoder.yudao.module.hrm.service.home.HrmHomeService; +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 org.springframework.format.annotation.DateTimeFormat; +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.time.LocalDate; +import java.util.List; + +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/home") +@Validated +public class HrmHomeController { + + @Resource + private HrmHomeService homeService; + + @GetMapping("/hr-statistics-summary") + @Operation(summary = "获得 HR 工作台统计") + @PreAuthorize("@ss.hasPermission('hrm:home:hr-query')") + public CommonResult getHrHomeStatisticsSummary() { + return success(homeService.getHrHomeStatisticsSummary()); + } + + @GetMapping("/hr-calendar") + @Operation(summary = "获得 HR 工作台日历") + @Parameters({ + @Parameter(name = "startDate", description = "开始日期", required = true, example = "2026-08-01"), + @Parameter(name = "endDate", description = "结束日期", required = true, example = "2026-08-31") + }) + @PreAuthorize("@ss.hasPermission('hrm:home:hr-query')") + public CommonResult> getHrHomeCalendar( + @RequestParam("startDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate, + @RequestParam("endDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate) { + return success(homeService.getHrHomeCalendar(getLoginUserId(), startDate, endDate)); + } + + @GetMapping("/team-statistics-summary") + @Operation(summary = "获得团队工作台统计") + @PreAuthorize("@ss.hasPermission('hrm:home:team-query')") + public CommonResult getTeamHomeStatisticsSummary() { + return success(homeService.getTeamHomeStatisticsSummary(getLoginUserId())); + } + + @GetMapping("/team-calendar") + @Operation(summary = "获得团队工作台日历") + @Parameters({ + @Parameter(name = "startDate", description = "开始日期", required = true, example = "2026-08-01"), + @Parameter(name = "endDate", description = "结束日期", required = true, example = "2026-08-31") + }) + @PreAuthorize("@ss.hasPermission('hrm:home:team-query')") + public CommonResult> getTeamHomeCalendar( + @RequestParam("startDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate, + @RequestParam("endDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate) { + return success(homeService.getTeamHomeCalendar(getLoginUserId(), startDate, endDate)); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/home/vo/HrmHomeCalendarItemRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/home/vo/HrmHomeCalendarItemRespVO.java new file mode 100644 index 0000000..30c225e --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/home/vo/HrmHomeCalendarItemRespVO.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.home.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDate; +import java.time.LocalDateTime; +@Schema(description = "管理后台 - HRM 首页日历事项 Response VO") +@Data +public class HrmHomeCalendarItemRespVO { + + @Schema(description = "员工个人备忘编号") + private Long personalNoteId; + + @Schema(description = "事项类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer type; + + @Schema(description = "事项类型名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "备忘") + private String typeName; + + @Schema(description = "事项内容", requiredMode = Schema.RequiredMode.REQUIRED) + private String content; + + @Schema(description = "关联业务编号") + private Long typeId; + + @Schema(description = "事项日期", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDate date; + + @Schema(description = "事项时间") + private LocalDateTime eventTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/home/vo/HrmHrHomeStatisticsRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/home/vo/HrmHrHomeStatisticsRespVO.java new file mode 100644 index 0000000..d98713a --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/home/vo/HrmHrHomeStatisticsRespVO.java @@ -0,0 +1,130 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.home.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +@Schema(description = "管理后台 - HRM HR 工作台统计 Response VO") +@Data +public class HrmHrHomeStatisticsRespVO { + + @Schema(description = "人事概况") + private EmployeeSurvey employeeSurvey; + + @Schema(description = "招聘动态") + private RecruitSurvey recruitSurvey; + + @Schema(description = "上月薪资") + private SalarySurvey salarySurvey; + + @Schema(description = "待办提醒") + private TodoSurvey todoSurvey; + + @Schema(description = "管理后台 - HRM HR 工作台人事概况 Response VO") + @Data + public static class EmployeeSurvey { + + @Schema(description = "在职人数") + private Long activeCount; + + @Schema(description = "本月入职人数") + private Long entryThisMonthCount; + + @Schema(description = "本月待入职人数") + private Long pendingEntryThisMonthCount; + + @Schema(description = "本月离职人数") + private Long leaveThisMonthCount; + + @Schema(description = "本月待离职人数") + private Long pendingLeaveThisMonthCount; + + @Schema(description = "本月转正人数") + private Long regularThisMonthCount; + + @Schema(description = "本月调岗人数") + private Long transferThisMonthCount; + + } + + @Schema(description = "管理后台 - HRM HR 工作台招聘动态 Response VO") + @Data + public static class RecruitSurvey { + + @Schema(description = "招聘中职位数量") + private Long recruitingPostCount; + + @Schema(description = "招聘流程中候选人数") + private Long candidateInProcessCount; + + @Schema(description = "待入职人数") + private Long pendingEntryCount; + + @Schema(description = "已入职人数") + private Long joinedCount; + + } + + @Schema(description = "管理后台 - HRM HR 工作台上月薪资 Response VO") + @Data + public static class SalarySurvey { + + @Schema(description = "工资表编号") + private Long monthRecordId; + + @Schema(description = "员工数量") + private Integer employeeCount; + + @Schema(description = "实发工资") + private BigDecimal realPaySalary; + + @Schema(description = "部门薪资占比列表") + private List deptProportions; + + } + + @Schema(description = "管理后台 - HRM HR 工作台部门薪资占比 Response VO") + @Data + public static class SalaryDept { + + @Schema(description = "部门编号") + private Long deptId; + + @Schema(description = "部门名称") + private String deptName; + + @Schema(description = "薪资占比") + private BigDecimal proportion; + + @Schema(description = "实发工资") + private BigDecimal totalSalary; + + } + + @Schema(description = "管理后台 - HRM HR 工作台待办提醒 Response VO") + @Data + public static class TodoSurvey { + + @Schema(description = "待入职人数") + private Long toEntryCount; + + @Schema(description = "待离职人数") + private Long toLeaveCount; + + @Schema(description = "合同待到期人数") + private Long toExpireContractCount; + + @Schema(description = "待转正人数") + private Long toRegularCount; + + @Schema(description = "待核算工资表数量") + private Long toSalaryComputeCount; + + @Schema(description = "本月生日员工数量") + private Long toBirthdayCount; + + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/home/vo/HrmTeamHomeStatisticsRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/home/vo/HrmTeamHomeStatisticsRespVO.java new file mode 100644 index 0000000..1009c12 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/home/vo/HrmTeamHomeStatisticsRespVO.java @@ -0,0 +1,69 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.home.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - HRM 团队工作台统计 Response VO") +@Data +public class HrmTeamHomeStatisticsRespVO { + + @Schema(description = "当前主管员工编号", example = "1") + private Long leaderEmployeeId; + + @Schema(description = "我的团队") + private TeamOverview teamOverview; + + @Schema(description = "团队概况") + private TeamSurvey teamSurvey; + + @Schema(description = "管理后台 - HRM 团队工作台我的团队 Response VO") + @Data + public static class TeamOverview { + + @Schema(description = "团队人数", example = "12") + private Long employeeCount; + + @Schema(description = "本月入职人数", example = "2") + private Long entryThisMonthCount; + + @Schema(description = "本月离职人数", example = "1") + private Long leaveThisMonthCount; + + @Schema(description = "本月转正人数", example = "3") + private Long regularThisMonthCount; + + } + + @Schema(description = "管理后台 - HRM 团队工作台团队概况 Response VO") + @Data + public static class TeamSurvey { + + @Schema(description = "员工状态占比") + private List statusAnalysis; + + @Schema(description = "男女性别占比") + private List sexAnalysis; + + @Schema(description = "成员年龄占比") + private List ageAnalysis; + + @Schema(description = "成员司龄占比") + private List companyAgeAnalysis; + + } + + @Schema(description = "管理后台 - HRM 团队工作台统计分析项 Response VO") + @Data + public static class AnalysisItem { + + @Schema(description = "分类类型;null 表示未填写", example = "1") + private Integer type; + + @Schema(description = "数量", example = "8") + private Long count; + + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/HrmInsuranceEmployeeInfoController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/HrmInsuranceEmployeeInfoController.java new file mode 100644 index 0000000..1dd3cfd --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/HrmInsuranceEmployeeInfoController.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.insurance; + +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.insurance.vo.employeeinfo.HrmInsuranceEmployeeInfoRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.employeeinfo.HrmInsuranceEmployeeInfoSaveReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.employeeinfo.HrmInsuranceEmployeeInfoUpdateSchemeReqVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.insurance.employee.HrmInsuranceEmployeeInfoDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.insurance.config.HrmInsuranceSchemeDO; +import cn.iocoder.yudao.module.hrm.service.insurance.employee.HrmInsuranceEmployeeInfoService; +import cn.iocoder.yudao.module.hrm.service.insurance.config.HrmInsuranceSchemeService; +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 static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - HRM 员工参保信息") +@RestController +@RequestMapping("/hrm/insurance/employee-info") +@Validated +public class HrmInsuranceEmployeeInfoController { + + @Resource + private HrmInsuranceEmployeeInfoService insuranceEmployeeInfoService; + @Resource + private HrmInsuranceSchemeService insuranceSchemeService; + + @GetMapping("/get") + @Operation(summary = "获得员工参保信息") + @Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:insurance:employee-info:query')") + public CommonResult getInsuranceEmployeeInfo( + @RequestParam("employeeId") Long employeeId) { + HrmInsuranceEmployeeInfoDO employeeInfo = insuranceEmployeeInfoService + .getInsuranceEmployeeInfoByEmployeeId(employeeId); + HrmInsuranceEmployeeInfoRespVO respVO = BeanUtils.toBean( + employeeInfo, HrmInsuranceEmployeeInfoRespVO.class); + if (respVO != null && respVO.getSchemeId() != null) { + HrmInsuranceSchemeDO scheme = insuranceSchemeService.getScheme(respVO.getSchemeId()); + respVO.setSchemeName(scheme == null ? null : scheme.getName()); + } + return success(respVO); + } + + @PutMapping("/save") + @Operation(summary = "保存员工参保信息") + @PreAuthorize("@ss.hasPermission('hrm:insurance:employee-info:update')") + public CommonResult saveInsuranceEmployeeInfo( + @Valid @RequestBody HrmInsuranceEmployeeInfoSaveReqVO reqVO) { + return success(insuranceEmployeeInfoService.saveInsuranceEmployeeInfo(reqVO)); + } + + @PutMapping("/update-scheme") + @Operation(summary = "更新员工参保方案") + @PreAuthorize("@ss.hasPermission('hrm:insurance:employee-info:update')") + public CommonResult updateEmployeeScheme( + @Valid @RequestBody HrmInsuranceEmployeeInfoUpdateSchemeReqVO reqVO) { + insuranceEmployeeInfoService.updateEmployeeScheme(reqVO.getEmployeeId(), reqVO.getSchemeId()); + return success(true); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/HrmInsuranceMonthEmployeeRecordController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/HrmInsuranceMonthEmployeeRecordController.java new file mode 100644 index 0000000..bccb82e --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/HrmInsuranceMonthEmployeeRecordController.java @@ -0,0 +1,188 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.insurance; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +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.ip.core.utils.AreaUtils; +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.monthrecord.employee.HrmInsuranceMonthEmployeeProjectRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.monthrecord.employee.HrmInsuranceMonthEmployeeRecordCreateListReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.monthrecord.employee.HrmInsuranceMonthEmployeeRecordPageReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.monthrecord.employee.HrmInsuranceMonthEmployeeRecordRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.monthrecord.employee.HrmInsuranceMonthEmployeeRecordStopListReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.monthrecord.employee.HrmInsuranceMonthEmployeeRecordUpdateReqVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.insurance.employee.HrmInsuranceEmployeeInfoDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.insurance.monthrecord.HrmInsuranceMonthEmployeeRecordDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.insurance.config.HrmInsuranceSchemeDO; +import cn.iocoder.yudao.module.hrm.enums.insurance.config.HrmInsuranceProjectTypeEnum; +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService; +import cn.iocoder.yudao.module.hrm.service.insurance.config.HrmInsuranceSchemeService; +import cn.iocoder.yudao.module.hrm.service.insurance.employee.HrmInsuranceEmployeeInfoService; +import cn.iocoder.yudao.module.hrm.service.insurance.monthrecord.HrmInsuranceMonthEmployeeRecordService; +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.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.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.convertMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +@Tag(name = "管理后台 - HRM 员工月度社保") +@RestController +@RequestMapping("/hrm/insurance/month-employee-record") +@Validated +public class HrmInsuranceMonthEmployeeRecordController { + + @Resource + private HrmInsuranceMonthEmployeeRecordService monthEmployeeRecordService; + @Resource + private HrmEmployeeService employeeService; + @Resource + private HrmInsuranceEmployeeInfoService insuranceEmployeeInfoService; + @Resource + private HrmInsuranceSchemeService insuranceSchemeService; + + @Resource + private DeptApi deptApi; + + @GetMapping("/page") + @Operation(summary = "获得员工月度社保分页") + @PreAuthorize("@ss.hasPermission('hrm:insurance:month-record:query')") + public CommonResult> getMonthEmployeeRecordPage( + @Valid HrmInsuranceMonthEmployeeRecordPageReqVO reqVO) { + PageResult pageResult = + monthEmployeeRecordService.getMonthEmployeeRecordPage(reqVO); + return success(new PageResult<>(buildMonthEmployeeRecordRespVOList(pageResult.getList()), + pageResult.getTotal())); + } + + @GetMapping("/get") + @Operation(summary = "获得员工月度社保详情") + @Parameter(name = "id", description = "员工月度社保记录编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:insurance:month-record:query')") + public CommonResult getMonthEmployeeRecord(@RequestParam("id") Long id) { + HrmInsuranceMonthEmployeeRecordDO employeeRecord = + monthEmployeeRecordService.getMonthEmployeeRecord(id); + if (employeeRecord == null) { + return success(null); + } + return success(CollUtil.getFirst(buildMonthEmployeeRecordRespVOList( + Collections.singletonList(employeeRecord)))); + } + + @PutMapping("/update") + @Operation(summary = "修改员工月度参保项目") + @PreAuthorize("@ss.hasPermission('hrm:insurance:month-record:update')") + public CommonResult updateMonthEmployeeRecord( + @Valid @RequestBody HrmInsuranceMonthEmployeeRecordUpdateReqVO reqVO) { + monthEmployeeRecordService.updateMonthEmployeeRecord(reqVO); + return success(true); + } + + @PutMapping("/stop-list") + @Operation(summary = "批量停止员工参保") + @PreAuthorize("@ss.hasPermission('hrm:insurance:month-record:update')") + public CommonResult stopMonthEmployeeRecordList( + @Valid @RequestBody HrmInsuranceMonthEmployeeRecordStopListReqVO reqVO) { + monthEmployeeRecordService.stopMonthEmployeeRecordList(reqVO.getIds()); + return success(true); + } + + @PostMapping("/create-list") + @Operation(summary = "添加参保人员") + @PreAuthorize("@ss.hasPermission('hrm:insurance:month-record:update')") + public CommonResult createMonthEmployeeRecordList( + @Valid @RequestBody HrmInsuranceMonthEmployeeRecordCreateListReqVO reqVO) { + monthEmployeeRecordService.createMonthEmployeeRecordList(reqVO); + return success(true); + } + + @GetMapping("/uninsured-employee-list") + @Operation(summary = "获得本月未参保员工") + @Parameter(name = "monthRecordId", description = "社保表编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:insurance:month-record:query')") + public CommonResult> getUninsuredEmployeeList( + @RequestParam("monthRecordId") Long monthRecordId) { + List employees = monthEmployeeRecordService.getUninsuredEmployeeList(monthRecordId); + return success(BeanUtils.toBean(employees, HrmEmployeeRespVO.class)); + } + + // ==================== 拼接 VO ==================== + + private List buildMonthEmployeeRecordRespVOList( + List employeeRecords) { + if (CollUtil.isEmpty(employeeRecords)) { + return Collections.emptyList(); + } + + // 1.1 批量查询员工和部门 + Set employeeIds = convertSet(employeeRecords, HrmInsuranceMonthEmployeeRecordDO::getEmployeeId); + Map employeeMap = employeeService.getEmployeeMap(employeeIds); + Map deptMap = deptApi.getDeptMap(convertSet(employeeMap.values(), HrmEmployeeDO::getDeptId)); + // 1.2 批量查询社保方案和员工社保资料 + Map schemeMap = convertMap(insuranceSchemeService.getSchemeListByIds( + convertSet(employeeRecords, HrmInsuranceMonthEmployeeRecordDO::getSchemeId)), + HrmInsuranceSchemeDO::getId); + Map insuranceEmployeeInfoMap = + insuranceEmployeeInfoService.getInsuranceEmployeeInfoMap(employeeIds); + Map> projectMap = + convertMap(employeeRecords, HrmInsuranceMonthEmployeeRecordDO::getId, + record -> record.getProjects() == null ? Collections.emptyList() : record.getProjects()); + + // 2. 拼接响应 + List respVOs = + BeanUtils.toBean(employeeRecords, HrmInsuranceMonthEmployeeRecordRespVO.class); + for (HrmInsuranceMonthEmployeeRecordRespVO respVO : respVOs) { + MapUtils.findAndThen(employeeMap, respVO.getEmployeeId(), employee -> { + respVO.setEmployeeName(employee.getName()).setJobNumber(employee.getJobNumber()) + .setMobile(employee.getMobile()).setIdNumber(employee.getIdNumber()) + .setSex(employee.getSex()).setAge(employee.getAge()) + .setDeptId(employee.getDeptId()).setPostName(employee.getPostName()) + .setEntryStatus(employee.getEntryStatus()).setEmployeeStatus(employee.getStatus()) + .setEntryTime(employee.getEntryTime()); + MapUtils.findAndThen(deptMap, employee.getDeptId(), + dept -> respVO.setDeptName(dept.getName())); + }); + MapUtils.findAndThen(schemeMap, respVO.getSchemeId(), scheme -> { + respVO.setSchemeName(scheme.getName()).setAreaId(scheme.getAreaId()) + .setAreaName(AreaUtils.format(scheme.getAreaId())) + .setHouseType(scheme.getHouseholdType()).setSchemeType(scheme.getType()); + }); + MapUtils.findAndThen(insuranceEmployeeInfoMap, respVO.getEmployeeId(), insuranceEmployeeInfo -> { + respVO.setSocialSecurityNumber(insuranceEmployeeInfo.getSocialSecurityNumber()) + .setAccumulationFundNumber(insuranceEmployeeInfo.getAccumulationFundNumber()); + }); + List projects = BeanUtils.toBean( + projectMap.getOrDefault(respVO.getId(), Collections.emptyList()), + HrmInsuranceMonthEmployeeProjectRespVO.class); + respVO.setSocialSecurityProjectList(convertList(projects, project -> project, + project -> HrmInsuranceProjectTypeEnum.isSocialSecurity(project.getType()))); + respVO.setProvidentFundProjectList(convertList(projects, project -> project, + project -> HrmInsuranceProjectTypeEnum.isProvidentFund(project.getType()))); + } + return respVOs; + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/HrmInsuranceMonthRecordController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/HrmInsuranceMonthRecordController.java new file mode 100644 index 0000000..4d984a3 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/HrmInsuranceMonthRecordController.java @@ -0,0 +1,88 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.insurance; + +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.insurance.vo.monthrecord.HrmInsuranceMonthRecordCreateReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.monthrecord.HrmInsuranceMonthRecordRespVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.insurance.monthrecord.HrmInsuranceMonthRecordDO; +import cn.iocoder.yudao.module.hrm.service.insurance.monthrecord.HrmInsuranceMonthRecordService; +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.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/insurance/month-record") +@Validated +public class HrmInsuranceMonthRecordController { + + @Resource + private HrmInsuranceMonthRecordService insuranceMonthRecordService; + + @PostMapping("/create-first") + @Operation(summary = "创建首月社保表") + @PreAuthorize("@ss.hasPermission('hrm:insurance:month-record:create')") + public CommonResult createFirstMonthRecord( + @Valid @RequestBody HrmInsuranceMonthRecordCreateReqVO reqVO) { + return success(insuranceMonthRecordService.createFirstMonthRecord(reqVO)); + } + + @PostMapping("/create-next") + @Operation(summary = "创建下月社保表") + @PreAuthorize("@ss.hasPermission('hrm:insurance:month-record:create')") + public CommonResult createNextMonthRecord() { + return success(insuranceMonthRecordService.createNextMonthRecord()); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除月度社保表") + @Parameter(name = "id", description = "社保表编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:insurance:month-record:delete')") + public CommonResult deleteMonthRecord(@RequestParam("id") Long id) { + insuranceMonthRecordService.deleteMonthRecord(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得月度社保表") + @Parameter(name = "id", description = "月度社保表编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:insurance:month-record:query')") + public CommonResult getMonthRecord(@RequestParam("id") Long id) { + HrmInsuranceMonthRecordDO monthRecord = insuranceMonthRecordService.getMonthRecord(id); + return success(BeanUtils.toBean(monthRecord, HrmInsuranceMonthRecordRespVO.class)); + } + + @GetMapping("/last") + @Operation(summary = "获得最近月度社保表") + @PreAuthorize("@ss.hasPermission('hrm:insurance:month-record:query')") + public CommonResult getLastMonthRecord() { + HrmInsuranceMonthRecordDO monthRecord = insuranceMonthRecordService.getLastMonthRecord(); + return success(BeanUtils.toBean(monthRecord, HrmInsuranceMonthRecordRespVO.class)); + } + + @GetMapping("/list") + @Operation(summary = "获得月度社保表列表") + @Parameter(name = "year", description = "年份", example = "2026") + @PreAuthorize("@ss.hasPermission('hrm:insurance:month-record:query')") + public CommonResult> getMonthRecordList( + @RequestParam(value = "year", required = false) Integer year) { + List monthRecords = insuranceMonthRecordService.getMonthRecordList(year); + return success(BeanUtils.toBean(monthRecords, HrmInsuranceMonthRecordRespVO.class)); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/HrmInsuranceSchemeController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/HrmInsuranceSchemeController.java new file mode 100644 index 0000000..67ada40 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/HrmInsuranceSchemeController.java @@ -0,0 +1,157 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.insurance; + +import cn.hutool.core.collection.CollUtil; +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.insurance.vo.scheme.HrmInsuranceSchemeProjectRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.scheme.HrmInsuranceSchemeRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.scheme.HrmInsuranceSchemeSaveReqVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.insurance.config.HrmInsuranceSchemeDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.insurance.config.HrmInsuranceSchemeProjectDO; +import cn.iocoder.yudao.module.hrm.enums.insurance.config.HrmInsuranceProjectTypeEnum; +import cn.iocoder.yudao.module.hrm.service.insurance.config.HrmInsuranceSchemeService; +import cn.iocoder.yudao.module.hrm.service.insurance.employee.HrmInsuranceEmployeeInfoService; +import cn.iocoder.yudao.module.hrm.service.insurance.monthrecord.HrmInsuranceMonthEmployeeRecordService; +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.math.BigDecimal; +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.getSumValue; + +@Tag(name = "管理后台 - HRM 社保方案") +@RestController +@RequestMapping("/hrm/insurance/scheme") +@Validated +public class HrmInsuranceSchemeController { + + @Resource + private HrmInsuranceSchemeService insuranceSchemeService; + @Resource + private HrmInsuranceEmployeeInfoService insuranceEmployeeInfoService; + @Resource + private HrmInsuranceMonthEmployeeRecordService insuranceMonthEmployeeRecordService; + + @PostMapping("/create") + @Operation(summary = "创建社保方案") + @PreAuthorize("@ss.hasPermission('hrm:insurance:scheme:create')") + public CommonResult createScheme(@Valid @RequestBody HrmInsuranceSchemeSaveReqVO reqVO) { + return success(insuranceSchemeService.createScheme(reqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新社保方案") + @PreAuthorize("@ss.hasPermission('hrm:insurance:scheme:update')") + public CommonResult updateScheme(@Valid @RequestBody HrmInsuranceSchemeSaveReqVO reqVO) { + insuranceSchemeService.updateScheme(reqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除社保方案") + @Parameter(name = "id", description = "方案编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:insurance:scheme:delete')") + public CommonResult deleteScheme(@RequestParam("id") Long id) { + insuranceSchemeService.deleteScheme(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得社保方案") + @Parameter(name = "id", description = "社保方案编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:insurance:scheme:query')") + public CommonResult getScheme(@RequestParam("id") Long id) { + HrmInsuranceSchemeDO scheme = insuranceSchemeService.getScheme(id); + return success(buildSchemeRespVO(scheme)); + } + + @GetMapping("/list") + @Operation(summary = "获得社保方案列表") + @PreAuthorize("@ss.hasPermission('hrm:insurance:scheme:query')") + public CommonResult> getSchemeList() { + List schemes = insuranceSchemeService.getSchemeList(); + return success(buildSchemeRespVOList(schemes)); + } + + @GetMapping("/simple-list") + @Operation(summary = "获得社保方案精简列表") + @PreAuthorize("@ss.hasPermission('hrm:insurance:scheme:query')") + public CommonResult> getSchemeSimpleList() { + List list = insuranceSchemeService.getSchemeList(); + return success(BeanUtils.toBean(list, HrmInsuranceSchemeRespVO.class, + scheme -> scheme.setAreaName(AreaUtils.format(scheme.getAreaId())))); + } + + // ==================== 拼接 VO ==================== + + private HrmInsuranceSchemeRespVO buildSchemeRespVO(HrmInsuranceSchemeDO scheme) { + if (scheme == null) { + return null; + } + return CollUtil.getFirst(buildSchemeRespVOList(Collections.singletonList(scheme))); + } + + private List buildSchemeRespVOList(List schemes) { + if (CollUtil.isEmpty(schemes)) { + return Collections.emptyList(); + } + // 1. 批量查询社保项目和使用人数 + Set schemeIds = convertSet(schemes, HrmInsuranceSchemeDO::getId); + Map> projectMap = + insuranceSchemeService.getSchemeProjectListMap(schemeIds); + Map useCountMap = + insuranceEmployeeInfoService.getInsuranceEmployeeInfoCountMapBySchemeIds(schemeIds); + Map monthRecordCountMap = insuranceMonthEmployeeRecordService + .getMonthEmployeeRecordCountMapBySchemeIds(schemeIds); + + // 2. 拼接响应 + return convertList(schemes, scheme -> { + HrmInsuranceSchemeRespVO respVO = BeanUtils.toBean(scheme, HrmInsuranceSchemeRespVO.class); + respVO.setAreaName(AreaUtils.format(respVO.getAreaId())); + List projects = BeanUtils.toBean( + projectMap.getOrDefault(scheme.getId(), Collections.emptyList()), HrmInsuranceSchemeProjectRespVO.class); + respVO.setProjectList(projects); + List socialSecurityProjects = convertList( + projects, project -> project, + project -> HrmInsuranceProjectTypeEnum.isSocialSecurity(project.getType())); + List providentFundProjects = convertList( + projects, project -> project, + project -> HrmInsuranceProjectTypeEnum.isProvidentFund(project.getType())); + respVO.setSocialSecurityProjectList(socialSecurityProjects) + .setProvidentFundProjectList(providentFundProjects); + respVO.setPersonalInsuranceAmount(getSumValue(socialSecurityProjects, + HrmInsuranceSchemeProjectRespVO::getPersonalAmount, BigDecimal::add, BigDecimal.ZERO)) + .setCorporateInsuranceAmount(getSumValue(socialSecurityProjects, + HrmInsuranceSchemeProjectRespVO::getCorporateAmount, BigDecimal::add, BigDecimal.ZERO)) + .setPersonalProvidentFundAmount(getSumValue(providentFundProjects, + HrmInsuranceSchemeProjectRespVO::getPersonalAmount, BigDecimal::add, BigDecimal.ZERO)) + .setCorporateProvidentFundAmount(getSumValue(providentFundProjects, + HrmInsuranceSchemeProjectRespVO::getCorporateAmount, BigDecimal::add, BigDecimal.ZERO)) + .setUseCount(useCountMap.getOrDefault(scheme.getId(), 0L)) + .setMonthRecordCount(monthRecordCountMap.getOrDefault(scheme.getId(), 0L)); + return respVO; + }); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/HrmInsuranceStandardController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/HrmInsuranceStandardController.java new file mode 100644 index 0000000..07680d5 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/HrmInsuranceStandardController.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.insurance; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.standard.HrmInsuranceStandardProjectListReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.standard.HrmInsuranceStandardProjectRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.standard.HrmInsuranceStandardTypeListReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.standard.HrmInsuranceStandardTypeRespVO; +import cn.iocoder.yudao.module.hrm.service.insurance.config.HrmInsuranceStandardService; +import io.swagger.v3.oas.annotations.Operation; +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.RequestMapping; +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/insurance/standard") +@Validated +public class HrmInsuranceStandardController { + + @Resource + private HrmInsuranceStandardService insuranceStandardService; + + @GetMapping("/type-list") + @Operation(summary = "获得标准参保类型列表") + @PreAuthorize("@ss.hasPermission('hrm:insurance:scheme:query')") + public CommonResult> getStandardTypeList( + @Valid HrmInsuranceStandardTypeListReqVO reqVO) { + return success(insuranceStandardService.getStandardTypeList(reqVO.getAreaId())); + } + + @GetMapping("/project-list") + @Operation(summary = "获得标准参保项目列表") + @PreAuthorize("@ss.hasPermission('hrm:insurance:scheme:query')") + public CommonResult> getStandardProjectList( + @Valid HrmInsuranceStandardProjectListReqVO reqVO) { + return success(insuranceStandardService.getStandardProjectList(reqVO.getAreaId(), reqVO.getTypeCode())); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/employeeinfo/HrmInsuranceEmployeeInfoRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/employeeinfo/HrmInsuranceEmployeeInfoRespVO.java new file mode 100644 index 0000000..4c5d094 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/employeeinfo/HrmInsuranceEmployeeInfoRespVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.employeeinfo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - HRM 员工参保信息 Response VO") +@Data +public class HrmInsuranceEmployeeInfoRespVO { + + @Schema(description = "员工参保信息编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long employeeId; + + @Schema(description = "是否本地首次缴纳社保", example = "true") + private Boolean firstSocialSecurity; + + @Schema(description = "是否本地首次缴纳公积金", example = "false") + private Boolean firstAccumulationFund; + + @Schema(description = "社保账号", example = "SB20260001") + private String socialSecurityNumber; + + @Schema(description = "公积金账号", example = "GJJ20260001") + private String accumulationFundNumber; + + @Schema(description = "社保起缴月份") + private LocalDateTime socialSecurityStartMonth; + + @Schema(description = "社保方案编号", example = "1024") + private Long schemeId; + + @Schema(description = "社保方案名称", example = "深圳标准社保方案") + private String schemeName; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/employeeinfo/HrmInsuranceEmployeeInfoSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/employeeinfo/HrmInsuranceEmployeeInfoSaveReqVO.java new file mode 100644 index 0000000..fea4f46 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/employeeinfo/HrmInsuranceEmployeeInfoSaveReqVO.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.employeeinfo; + +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 HrmInsuranceEmployeeInfoSaveReqVO { + + @Schema(description = "员工参保信息编号", example = "1024") + private Long id; + + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "员工不能为空") + private Long employeeId; + + @Schema(description = "是否本地首次缴纳社保", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是否本地首次缴纳社保不能为空") + private Boolean firstSocialSecurity; + + @Schema(description = "是否本地首次缴纳公积金", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + @NotNull(message = "是否本地首次缴纳公积金不能为空") + private Boolean firstAccumulationFund; + + @Schema(description = "社保账号", example = "SB20260001") + @Size(max = 64, message = "社保账号不能超过 64 个字符") + private String socialSecurityNumber; + + @Schema(description = "公积金账号", example = "GJJ20260001") + @Size(max = 64, message = "公积金账号不能超过 64 个字符") + private String accumulationFundNumber; + + @Schema(description = "社保起缴月份") + private LocalDateTime socialSecurityStartMonth; + + @Schema(description = "社保方案编号", example = "1024") + private Long schemeId; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/employeeinfo/HrmInsuranceEmployeeInfoUpdateSchemeReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/employeeinfo/HrmInsuranceEmployeeInfoUpdateSchemeReqVO.java new file mode 100644 index 0000000..28a87cd --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/employeeinfo/HrmInsuranceEmployeeInfoUpdateSchemeReqVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.employeeinfo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - HRM 员工参保方案更新 Request VO") +@Data +public class HrmInsuranceEmployeeInfoUpdateSchemeReqVO { + + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "员工不能为空") + private Long employeeId; + + @Schema(description = "社保方案编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "社保方案不能为空") + private Long schemeId; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/HrmInsuranceMonthRecordCreateReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/HrmInsuranceMonthRecordCreateReqVO.java new file mode 100644 index 0000000..77ff671 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/HrmInsuranceMonthRecordCreateReqVO.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.monthrecord; + +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; + +@Schema(description = "管理后台 - HRM 月度社保表创建 Request VO") +@Data +public class HrmInsuranceMonthRecordCreateReqVO { + + @Schema(description = "年份", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026") + @NotNull(message = "年份不能为空") + @Min(value = 2000, message = "年份不能小于 2000") + @Max(value = 2100, message = "年份不能大于 2100") + 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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/HrmInsuranceMonthRecordRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/HrmInsuranceMonthRecordRespVO.java new file mode 100644 index 0000000..3f3a452 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/HrmInsuranceMonthRecordRespVO.java @@ -0,0 +1,49 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.monthrecord; + +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 +public class HrmInsuranceMonthRecordRespVO { + + @Schema(description = "月度社保表编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "年份", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026") + private Integer year; + + @Schema(description = "月份", requiredMode = Schema.RequiredMode.REQUIRED, example = "7") + private Integer month; + + @Schema(description = "标题", example = "2026 年 7 月社保表") + private String title; + + @Schema(description = "参保人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer insuredEmployeeCount; + + @Schema(description = "停保人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer stoppedEmployeeCount; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "个人社保金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "800.00") + private BigDecimal personalInsuranceAmount; + + @Schema(description = "个人公积金金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "700.00") + private BigDecimal personalProvidentFundAmount; + + @Schema(description = "企业社保金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1600.00") + private BigDecimal corporateInsuranceAmount; + + @Schema(description = "企业公积金金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "700.00") + private BigDecimal corporateProvidentFundAmount; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/employee/HrmInsuranceMonthEmployeeProjectRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/employee/HrmInsuranceMonthEmployeeProjectRespVO.java new file mode 100644 index 0000000..bd63e12 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/employee/HrmInsuranceMonthEmployeeProjectRespVO.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.monthrecord.employee; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - HRM 员工月度社保项目 Response VO") +@Data +public class HrmInsuranceMonthEmployeeProjectRespVO { + + @Schema(description = "来源社保方案项目编号", example = "1024") + private Long schemeProjectId; + + @Schema(description = "项目类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer type; + + @Schema(description = "项目名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "养老保险") + private String name; + + @Schema(description = "缴纳基数", example = "10000.00") + private BigDecimal baseAmount; + + @Schema(description = "公司缴纳比例", example = "16.00") + private BigDecimal corporateRate; + + @Schema(description = "个人缴纳比例", example = "8.00") + private BigDecimal personalRate; + + @Schema(description = "公司缴纳金额", example = "1600.00") + private BigDecimal corporateAmount; + + @Schema(description = "个人缴纳金额", example = "800.00") + private BigDecimal personalAmount; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/employee/HrmInsuranceMonthEmployeeProjectUpdateReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/employee/HrmInsuranceMonthEmployeeProjectUpdateReqVO.java new file mode 100644 index 0000000..140a611 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/employee/HrmInsuranceMonthEmployeeProjectUpdateReqVO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.monthrecord.employee; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.Digits; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - HRM 员工月度社保项目调整 Request VO") +@Data +public class HrmInsuranceMonthEmployeeProjectUpdateReqVO { + + @Schema(description = "社保方案项目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "社保方案项目编号不能为空") + private Long schemeProjectId; + + @Schema(description = "缴纳基数", example = "10000.00") + @DecimalMin(value = "0", message = "缴纳基数不能小于 0") + @Digits(integer = 10, fraction = 2, message = "缴纳基数最多 10 位整数和 2 位小数") + private BigDecimal baseAmount; + + @Schema(description = "公司缴纳金额", example = "1600.00") + @DecimalMin(value = "0", message = "公司缴纳金额不能小于 0") + @Digits(integer = 10, fraction = 2, message = "公司缴纳金额最多 10 位整数和 2 位小数") + private BigDecimal corporateAmount; + + @Schema(description = "个人缴纳金额", example = "800.00") + @DecimalMin(value = "0", message = "个人缴纳金额不能小于 0") + @Digits(integer = 10, fraction = 2, message = "个人缴纳金额最多 10 位整数和 2 位小数") + private BigDecimal personalAmount; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/employee/HrmInsuranceMonthEmployeeRecordCreateListReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/employee/HrmInsuranceMonthEmployeeRecordCreateListReqVO.java new file mode 100644 index 0000000..9fad291 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/employee/HrmInsuranceMonthEmployeeRecordCreateListReqVO.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.monthrecord.employee; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - HRM 添加参保人员 Request VO") +@Data +public class HrmInsuranceMonthEmployeeRecordCreateListReqVO { + + @Schema(description = "月度社保表编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "社保表编号不能为空") + private Long monthRecordId; + + @Schema(description = "员工编号列表", requiredMode = Schema.RequiredMode.REQUIRED, + example = "[1024, 1025]") + @NotEmpty(message = "员工不能为空") + private List<@NotNull(message = "员工编号不能为空") Long> employeeIds; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/employee/HrmInsuranceMonthEmployeeRecordPageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/employee/HrmInsuranceMonthEmployeeRecordPageReqVO.java new file mode 100644 index 0000000..89acdf1 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/employee/HrmInsuranceMonthEmployeeRecordPageReqVO.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.monthrecord.employee; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.hrm.enums.insurance.employee.HrmInsuranceEmployeeStatusEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Schema(description = "管理后台 - HRM 员工月度社保分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class HrmInsuranceMonthEmployeeRecordPageReqVO extends PageParam { + + @Schema(description = "月度社保表编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "社保表编号不能为空") + private Long monthRecordId; + + @Schema(description = "员工编号", example = "1024") + private Long employeeId; + + @Schema(description = "员工姓名", example = "张三") + private String employeeName; + + @Schema(description = "社保方案编号", example = "1024") + private Long schemeId; + + @Schema(description = "参保地区编号", example = "440300") + private Integer areaId; + + @Schema(description = "参保状态", example = "1") + @InEnum(value = HrmInsuranceEmployeeStatusEnum.class, message = "参保状态必须是 {value}") + private Integer status; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/employee/HrmInsuranceMonthEmployeeRecordRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/employee/HrmInsuranceMonthEmployeeRecordRespVO.java new file mode 100644 index 0000000..fe05804 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/employee/HrmInsuranceMonthEmployeeRecordRespVO.java @@ -0,0 +1,113 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.monthrecord.employee; + +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 +public class HrmInsuranceMonthEmployeeRecordRespVO { + + @Schema(description = "记录编号") + private Long id; + + @Schema(description = "社保表编号") + private Long monthRecordId; + + @Schema(description = "员工编号") + private Long employeeId; + + @Schema(description = "员工姓名") + private String employeeName; + + @Schema(description = "工号") + private String jobNumber; + + @Schema(description = "手机号") + private String mobile; + + @Schema(description = "身份证号") + private String idNumber; + + @Schema(description = "性别") + private Integer sex; + + @Schema(description = "年龄") + private Integer age; + + @Schema(description = "部门编号") + private Long deptId; + + @Schema(description = "部门") + private String deptName; + + @Schema(description = "职位名称") + private String postName; + + @Schema(description = "入职状态") + private Integer entryStatus; + + @Schema(description = "员工状态") + private Integer employeeStatus; + + @Schema(description = "入职时间") + private LocalDateTime entryTime; + + @Schema(description = "社保方案编号") + private Long schemeId; + + @Schema(description = "社保方案") + private String schemeName; + + @Schema(description = "参保地区") + private String areaName; + + @Schema(description = "参保地区编号") + private Integer areaId; + + @Schema(description = "户籍类型") + private String houseType; + + @Schema(description = "社保方案类型") + private Integer schemeType; + + @Schema(description = "个人社保号") + private String socialSecurityNumber; + + @Schema(description = "个人公积金号") + private String accumulationFundNumber; + + @Schema(description = "年份") + private Integer year; + + @Schema(description = "月份") + private Integer month; + + @Schema(description = "个人社保") + private BigDecimal personalInsuranceAmount; + + @Schema(description = "个人公积金") + private BigDecimal personalProvidentFundAmount; + + @Schema(description = "公司社保") + private BigDecimal corporateInsuranceAmount; + + @Schema(description = "公司公积金") + private BigDecimal corporateProvidentFundAmount; + + @Schema(description = "参保状态") + private Integer status; + + @Schema(description = "社保项目列表") + private List socialSecurityProjectList; + + @Schema(description = "公积金项目列表") + private List providentFundProjectList; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/employee/HrmInsuranceMonthEmployeeRecordStopListReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/employee/HrmInsuranceMonthEmployeeRecordStopListReqVO.java new file mode 100644 index 0000000..78d6b59 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/employee/HrmInsuranceMonthEmployeeRecordStopListReqVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.monthrecord.employee; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - HRM 员工停止参保 Request VO") +@Data +public class HrmInsuranceMonthEmployeeRecordStopListReqVO { + + @Schema(description = "员工月度社保记录编号列表", requiredMode = Schema.RequiredMode.REQUIRED, + example = "[1024, 1025]") + @NotEmpty(message = "员工社保记录不能为空") + private List<@NotNull(message = "员工社保记录编号不能为空") Long> ids; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/employee/HrmInsuranceMonthEmployeeRecordUpdateReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/employee/HrmInsuranceMonthEmployeeRecordUpdateReqVO.java new file mode 100644 index 0000000..266e002 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/monthrecord/employee/HrmInsuranceMonthEmployeeRecordUpdateReqVO.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.monthrecord.employee; + +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 java.util.List; + +@Schema(description = "管理后台 - HRM 员工月度社保项目修改 Request VO") +@Data +public class HrmInsuranceMonthEmployeeRecordUpdateReqVO { + + @Schema(description = "员工月度社保记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "员工社保记录编号不能为空") + private Long id; + + @Schema(description = "社保方案编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "社保方案不能为空") + private Long schemeId; + + @Schema(description = "社保项目列表", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "社保项目不能为空") + @Valid + private List projects; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/scheme/HrmInsuranceSchemeProjectRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/scheme/HrmInsuranceSchemeProjectRespVO.java new file mode 100644 index 0000000..ac10171 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/scheme/HrmInsuranceSchemeProjectRespVO.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.scheme; + +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 +public class HrmInsuranceSchemeProjectRespVO { + + @Schema(description = "社保方案项目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "社保方案编号", example = "1024") + private Long schemeId; + + @Schema(description = "项目类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer type; + + @Schema(description = "项目名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "养老保险") + private String name; + + @Schema(description = "缴纳基数", example = "10000.00") + private BigDecimal baseAmount; + + @Schema(description = "公司缴纳比例", example = "16.00") + private BigDecimal corporateRate; + + @Schema(description = "个人缴纳比例", example = "8.00") + private BigDecimal personalRate; + + @Schema(description = "公司缴纳金额", example = "1600.00") + private BigDecimal corporateAmount; + + @Schema(description = "个人缴纳金额", example = "800.00") + private BigDecimal personalAmount; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/scheme/HrmInsuranceSchemeProjectSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/scheme/HrmInsuranceSchemeProjectSaveReqVO.java new file mode 100644 index 0000000..9dbc9f7 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/scheme/HrmInsuranceSchemeProjectSaveReqVO.java @@ -0,0 +1,60 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.scheme; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.hrm.enums.insurance.config.HrmInsuranceProjectTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.DecimalMax; +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; + +@Schema(description = "管理后台 - HRM 社保方案项目保存 Request VO") +@Data +public class HrmInsuranceSchemeProjectSaveReqVO { + + @Schema(description = "社保方案项目编号", example = "1024") + private Long id; + + @Schema(description = "项目类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "项目类型不能为空") + @InEnum(value = HrmInsuranceProjectTypeEnum.class, message = "项目类型必须是 {value}") + private Integer type; + + @Schema(description = "项目名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "养老保险") + @NotBlank(message = "项目名称不能为空") + @Size(max = 64, message = "项目名称不能超过 64 个字符") + private String name; + + @Schema(description = "缴纳基数", example = "10000.00") + @DecimalMin(value = "0.00", message = "缴纳基数不能小于 0") + @Digits(integer = 10, fraction = 2, message = "缴纳基数最多 10 位整数和 2 位小数") + private BigDecimal baseAmount; + + @Schema(description = "公司缴纳比例", example = "16.00") + @DecimalMin(value = "0.00", message = "公司缴纳比例不能小于 0") + @DecimalMax(value = "100.00", message = "公司缴纳比例不能大于 100") + @Digits(integer = 3, fraction = 2, message = "公司缴纳比例最多 2 位小数") + private BigDecimal corporateRate; + + @Schema(description = "个人缴纳比例", example = "8.00") + @DecimalMin(value = "0.00", message = "个人缴纳比例不能小于 0") + @DecimalMax(value = "100.00", message = "个人缴纳比例不能大于 100") + @Digits(integer = 3, fraction = 2, message = "个人缴纳比例最多 2 位小数") + private BigDecimal personalRate; + + @Schema(description = "公司缴纳金额", example = "1600.00") + @DecimalMin(value = "0.00", message = "公司缴纳金额不能小于 0") + @Digits(integer = 10, fraction = 2, message = "公司缴纳金额最多 10 位整数和 2 位小数") + private BigDecimal corporateAmount; + + @Schema(description = "个人缴纳金额", example = "800.00") + @DecimalMin(value = "0.00", message = "个人缴纳金额不能小于 0") + @Digits(integer = 10, fraction = 2, message = "个人缴纳金额最多 10 位整数和 2 位小数") + private BigDecimal personalAmount; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/scheme/HrmInsuranceSchemeRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/scheme/HrmInsuranceSchemeRespVO.java new file mode 100644 index 0000000..3c8e71b --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/scheme/HrmInsuranceSchemeRespVO.java @@ -0,0 +1,62 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.scheme; + +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 +public class HrmInsuranceSchemeRespVO { + + @Schema(description = "社保方案编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "方案名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "深圳标准社保方案") + private String name; + + @Schema(description = "参保地区编号", example = "440300") + private Integer areaId; + + @Schema(description = "参保地区", example = "广东省 深圳市") + private String areaName; + + @Schema(description = "户籍类型", example = "深户") + private String householdType; + + @Schema(description = "方案类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer type; + + @Schema(description = "全部社保项目") + private List projectList; + + @Schema(description = "社保项目") + private List socialSecurityProjectList; + + @Schema(description = "公积金项目") + private List providentFundProjectList; + + @Schema(description = "个人社保金额", example = "800.00") + private BigDecimal personalInsuranceAmount; + + @Schema(description = "公司社保金额", example = "1600.00") + private BigDecimal corporateInsuranceAmount; + + @Schema(description = "个人公积金金额", example = "700.00") + private BigDecimal personalProvidentFundAmount; + + @Schema(description = "公司公积金金额", example = "700.00") + private BigDecimal corporateProvidentFundAmount; + + @Schema(description = "使用人数", example = "10") + private Long useCount; + + @Schema(description = "历史月记录数", example = "120") + private Long monthRecordCount; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/scheme/HrmInsuranceSchemeSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/scheme/HrmInsuranceSchemeSaveReqVO.java new file mode 100644 index 0000000..d2f4661 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/scheme/HrmInsuranceSchemeSaveReqVO.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.scheme; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.hrm.enums.insurance.config.HrmInsuranceSchemeTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +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 HrmInsuranceSchemeSaveReqVO { + + @Schema(description = "社保方案编号", example = "1024") + private Long id; + + @Schema(description = "方案名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "深圳标准社保方案") + @NotBlank(message = "方案名称不能为空") + @Size(max = 64, message = "方案名称不能超过 64 个字符") + private String name; + + @Schema(description = "参保地区编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "440300") + @NotNull(message = "参保城市不能为空") + private Integer areaId; + + @Schema(description = "户籍类型", example = "深户") + @Size(max = 64, message = "户籍类型不能超过 64 个字符") + private String householdType; + + @Schema(description = "方案类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "方案类型不能为空") + @InEnum(value = HrmInsuranceSchemeTypeEnum.class, message = "方案类型必须是 {value}") + private Integer type; + + @Schema(description = "社保项目列表", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "社保项目不能为空") + private List<@NotNull(message = "社保项目不能为空") @Valid HrmInsuranceSchemeProjectSaveReqVO> projectList; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/standard/HrmInsuranceStandardProjectListReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/standard/HrmInsuranceStandardProjectListReqVO.java new file mode 100644 index 0000000..64a7909 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/standard/HrmInsuranceStandardProjectListReqVO.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.standard; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - HRM 标准参保项目列表 Request VO") +@Data +public class HrmInsuranceStandardProjectListReqVO { + + @Schema(description = "参保地区编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "440300") + @NotNull(message = "参保城市不能为空") + private Integer areaId; + + @Schema(description = "参保方案编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "SS100173") + @NotBlank(message = "参保方案编码不能为空") + private String typeCode; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/standard/HrmInsuranceStandardProjectRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/standard/HrmInsuranceStandardProjectRespVO.java new file mode 100644 index 0000000..a75887a --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/standard/HrmInsuranceStandardProjectRespVO.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.standard; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - HRM 标准参保项目 Response VO") +@Data +public class HrmInsuranceStandardProjectRespVO { + + @Schema(description = "项目类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer type; + + @Schema(description = "项目名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "养老保险") + private String name; + + @Schema(description = "缴纳基数", example = "10000.00") + private BigDecimal baseAmount; + + @Schema(description = "公司缴纳比例", example = "16.00") + private BigDecimal corporateRate; + + @Schema(description = "个人缴纳比例", example = "8.00") + private BigDecimal personalRate; + + @Schema(description = "公司缴纳金额", example = "1600.00") + private BigDecimal corporateAmount; + + @Schema(description = "个人缴纳金额", example = "800.00") + private BigDecimal personalAmount; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/standard/HrmInsuranceStandardTypeListReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/standard/HrmInsuranceStandardTypeListReqVO.java new file mode 100644 index 0000000..1a907fd --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/standard/HrmInsuranceStandardTypeListReqVO.java @@ -0,0 +1,15 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.standard; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - HRM 标准参保类型列表 Request VO") +@Data +public class HrmInsuranceStandardTypeListReqVO { + + @Schema(description = "参保地区编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "440300") + @NotNull(message = "参保城市不能为空") + private Integer areaId; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/standard/HrmInsuranceStandardTypeRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/standard/HrmInsuranceStandardTypeRespVO.java new file mode 100644 index 0000000..aaf9d3c --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/insurance/vo/standard/HrmInsuranceStandardTypeRespVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.insurance.vo.standard; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - HRM 标准参保类型 Response VO") +@Data +public class HrmInsuranceStandardTypeRespVO { + + @Schema(description = "参保方案编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "SS100173") + private String code; + + @Schema(description = "参保方案名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "深户一档") + private String name; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/operatelog/HrmOperateLogController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/operatelog/HrmOperateLogController.java new file mode 100644 index 0000000..b353cdd --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/operatelog/HrmOperateLogController.java @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.operatelog; + +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.operatelog.vo.HrmOperateLogPageReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.operatelog.vo.HrmOperateLogRespVO; +import cn.iocoder.yudao.module.hrm.enums.LogRecordConstants; +import cn.iocoder.yudao.module.hrm.enums.common.HrmBizTypeEnum; +import cn.iocoder.yudao.module.system.api.logger.OperateLogApi; +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogPageReqDTO; +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogRespDTO; +import io.swagger.v3.oas.annotations.Operation; +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.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; +import static cn.iocoder.yudao.module.hrm.enums.LogRecordConstants.HRM_EMPLOYEE_TYPE; +import static cn.iocoder.yudao.module.hrm.enums.LogRecordConstants.HRM_PERFORMANCE_PLAN_TYPE; +import static cn.iocoder.yudao.module.hrm.enums.LogRecordConstants.HRM_PERFORMANCE_TYPE; +import static cn.iocoder.yudao.module.hrm.enums.LogRecordConstants.HRM_RECRUIT_CANDIDATE_TYPE; +import static cn.iocoder.yudao.module.hrm.enums.LogRecordConstants.HRM_RECRUIT_POST_TYPE; + +@Tag(name = "管理后台 - HRM 操作日志") +@RestController +@RequestMapping("/hrm/operate-log") +@Validated +public class HrmOperateLogController { + + private static final Map BIZ_TYPE_MAP = new HashMap<>(); + + static { + BIZ_TYPE_MAP.put(HrmBizTypeEnum.RECRUIT_POST.getType(), HRM_RECRUIT_POST_TYPE); + BIZ_TYPE_MAP.put(HrmBizTypeEnum.RECRUIT_CANDIDATE.getType(), HRM_RECRUIT_CANDIDATE_TYPE); + BIZ_TYPE_MAP.put(HrmBizTypeEnum.EMPLOYEE.getType(), HRM_EMPLOYEE_TYPE); + BIZ_TYPE_MAP.put(HrmBizTypeEnum.PERFORMANCE_ASSESSMENT.getType(), HRM_PERFORMANCE_TYPE); + BIZ_TYPE_MAP.put(HrmBizTypeEnum.PERFORMANCE_PLAN.getType(), HRM_PERFORMANCE_PLAN_TYPE); + } + + @Resource + private OperateLogApi operateLogApi; + + @GetMapping("/page") + @Operation(summary = "获得操作日志") + @PreAuthorize("@ss.hasAnyPermissions('hrm:recruit:post:query', 'hrm:recruit:candidate:query', " + + "'hrm:employee:query', 'hrm:performance:plan:query')") + public CommonResult> getOperateLogPage( + @Valid HrmOperateLogPageReqVO pageReqVO) { + OperateLogPageReqDTO reqDTO = new OperateLogPageReqDTO(); + reqDTO.setPageSize(PAGE_SIZE_NONE); + reqDTO.setType(BIZ_TYPE_MAP.get(pageReqVO.getBizType())); + reqDTO.setBizId(pageReqVO.getBizId()); + PageResult pageResult = operateLogApi.getOperateLogPage(reqDTO); + return success(BeanUtils.toBean(pageResult, HrmOperateLogRespVO.class)); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/operatelog/vo/HrmOperateLogPageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/operatelog/vo/HrmOperateLogPageReqVO.java new file mode 100644 index 0000000..ce6ad8a --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/operatelog/vo/HrmOperateLogPageReqVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.operatelog.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.hrm.enums.common.HrmBizTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管理后台 - HRM 操作日志分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class HrmOperateLogPageReqVO extends PageParam { + + @Schema(description = "业务类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @InEnum(HrmBizTypeEnum.class) + @NotNull(message = "业务类型不能为空") + private Integer bizType; + + @Schema(description = "业务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "业务编号不能为空") + private Long bizId; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/operatelog/vo/HrmOperateLogRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/operatelog/vo/HrmOperateLogRespVO.java new file mode 100644 index 0000000..1119910 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/operatelog/vo/HrmOperateLogRespVO.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.operatelog.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - HRM 操作日志 Response VO") +@Data +public class HrmOperateLogRespVO { + + @Schema(description = "日志编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "操作用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long userId; + + @Schema(description = "操作用户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") + private String userName; + + @Schema(description = "操作用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer userType; + + @Schema(description = "日志类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "HRM 招聘职位") + private String type; + + @Schema(description = "日志子类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "更新招聘职位") + private String subType; + + @Schema(description = "业务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long bizId; + + @Schema(description = "操作内容", requiredMode = Schema.RequiredMode.REQUIRED, + example = "更新了招聘职位【Java 开发工程师】") + private String action; + + @Schema(description = "拓展字段", example = "{}") + private String extra; + + @Schema(description = "操作时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/HrmPerformanceAssessmentController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/HrmPerformanceAssessmentController.java new file mode 100644 index 0000000..1cd6934 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/HrmPerformanceAssessmentController.java @@ -0,0 +1,202 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.performance; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeListReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessment.HrmPerformanceArchiveEmployeePageReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessment.HrmPerformanceArchiveEmployeeRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessment.HrmPerformanceArchivePageReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessment.HrmPerformanceAssessmentBatchReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessment.HrmPerformanceAssessmentPageReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessment.HrmPerformanceAssessmentRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessment.HrmPerformanceProcessRecordRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.plan.HrmPerformancePlanRespVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.performance.assessment.HrmPerformanceAssessmentDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.performance.plan.HrmPerformancePlanDO; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformancePlanStatusEnum; +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService; +import cn.iocoder.yudao.module.hrm.service.performance.assessment.HrmPerformanceAssessmentProcessService; +import cn.iocoder.yudao.module.hrm.service.performance.assessment.HrmPerformanceAssessmentQueryService; +import cn.iocoder.yudao.module.hrm.service.performance.assessment.HrmPerformanceAssessmentService; +import cn.iocoder.yudao.module.hrm.service.performance.plan.HrmPerformancePlanService; +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 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.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 java.util.Set; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertLinkedSet; +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.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "管理后台 - HRM 员工绩效考核") +@RestController +@RequestMapping("/hrm/performance/assessment") +@Validated +public class HrmPerformanceAssessmentController { + + @Resource + private HrmPerformanceAssessmentService performanceAssessmentService; + @Resource + private HrmPerformanceAssessmentQueryService performanceAssessmentQueryService; + @Resource + private HrmPerformancePlanService performancePlanService; + @Resource + private HrmEmployeeService employeeService; + + @PostMapping("/create-list") + @Operation(summary = "添加员工绩效考核") + @PreAuthorize("@ss.hasPermission('hrm:performance:plan:update')") + public CommonResult addPerformancePlanEmployees( + @Valid @RequestBody HrmPerformanceAssessmentBatchReqVO reqVO) { + performancePlanService.addPerformancePlanEmployees(reqVO); + return success(true); + } + + @DeleteMapping("/delete-list") + @Operation(summary = "移除员工绩效考核") + @PreAuthorize("@ss.hasPermission('hrm:performance:plan:update')") + public CommonResult removePerformancePlanEmployees( + @Valid @RequestBody HrmPerformanceAssessmentBatchReqVO reqVO) { + performancePlanService.removePerformancePlanEmployees(reqVO); + return success(true); + } + + @GetMapping("/page") + @Operation(summary = "获得员工绩效考核分页") + @PreAuthorize("@ss.hasPermission('hrm:performance:plan:query')") + public CommonResult> getPerformanceAssessmentPage( + @Validated HrmPerformanceAssessmentPageReqVO reqVO) { + PageResult pageResult = + performanceAssessmentService.getPerformanceAssessmentPage(reqVO); + return success(performanceAssessmentQueryService.getPerformanceAssessmentRespVOPage(pageResult)); + } + + @GetMapping("/unassigned-employee-id-list") + @Operation(summary = "获得未加入指定绩效计划的员工编号列表") + @Parameter(name = "planId", description = "绩效计划编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:performance:plan:query')") + public CommonResult> getPerformancePlanUnassignedEmployeeIdList( + @RequestParam("planId") Long planId) { + performancePlanService.validatePerformancePlanExists(planId); + // 1. 查询已经参与计划的员工 + List assessments = + performanceAssessmentService.getPerformanceAssessmentListByPlanId(planId); + Set assignedEmployeeIds = convertSet(assessments, HrmPerformanceAssessmentDO::getEmployeeId); + // 2. 筛选尚未参与计划的员工 + List employees = employeeService.getEmployeeList(new HrmEmployeeListReqVO()); + return success(convertList(employees, HrmEmployeeDO::getId, + employee -> !assignedEmployeeIds.contains(employee.getId()))); + } + + @GetMapping("/get") + @Operation(summary = "获得员工绩效考核") + @Parameter(name = "id", description = "员工绩效考核编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:performance:plan:query')") + public CommonResult getPerformanceAssessment(@RequestParam("id") Long id) { + HrmPerformanceAssessmentDO assessment = performanceAssessmentService.getPerformanceAssessment(id); + return success(performanceAssessmentQueryService.getPerformanceAssessmentProcessRespVO( + assessment, getLoginUserId())); + } + + @GetMapping("/process-record-list") + @Operation(summary = "获得绩效流程记录列表") + @Parameter(name = "id", description = "员工绩效考核编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:performance:plan:query')") + public CommonResult> getPerformanceAssessmentProcessRecordList( + @RequestParam("id") Long id) { + HrmPerformanceAssessmentDO assessment = performanceAssessmentService.getPerformanceAssessment(id); + return success(performanceAssessmentQueryService.getPerformanceAssessmentProcessRecordList(assessment)); + } + + @GetMapping("/archive-page") + @Operation(summary = "获得绩效归档分页") + @PreAuthorize("@ss.hasPermission('hrm:performance:archive:query')") + public CommonResult> getPerformanceAssessmentArchivePage( + @Validated HrmPerformanceArchivePageReqVO reqVO) { + PageResult pageResult = + performanceAssessmentService.getPerformanceAssessmentArchivePage(reqVO); + return success(performanceAssessmentQueryService.getPerformanceAssessmentRespVOPage(pageResult)); + } + + @GetMapping("/archive-employee-page") + @Operation(summary = "获得员工绩效档案分页") + @PreAuthorize("@ss.hasPermission('hrm:performance:archive:query')") + public CommonResult> getPerformanceArchiveEmployeePage( + @Validated HrmPerformanceArchiveEmployeePageReqVO reqVO) { + PageResult pageResult = + performanceAssessmentService.getPerformanceArchiveEmployeePage(reqVO); + return success(performanceAssessmentQueryService.getPerformanceArchiveEmployeeRespVOPage(pageResult)); + } + + @GetMapping("/archive-get") + @Operation(summary = "获得绩效归档详情") + @Parameter(name = "id", description = "员工绩效考核编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:performance:archive:query')") + public CommonResult getPerformanceAssessmentArchive(@RequestParam("id") Long id) { + HrmPerformanceAssessmentDO assessment = performanceAssessmentService.getPerformanceAssessmentArchive(id); + return success(performanceAssessmentQueryService.getPerformanceAssessmentRespVO(assessment)); + } + + @GetMapping("/archive-process-record-list") + @Operation(summary = "获得绩效归档流程记录列表") + @Parameter(name = "id", description = "员工绩效考核编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:performance:archive:query')") + public CommonResult> getPerformanceAssessmentArchiveProcessRecordList( + @RequestParam("id") Long id) { + HrmPerformanceAssessmentDO assessment = performanceAssessmentService.getPerformanceAssessmentArchive(id); + return success(performanceAssessmentQueryService.getPerformanceAssessmentProcessRecordList(assessment)); + } + + @GetMapping("/archive-plan-simple-list") + @Operation(summary = "获得绩效归档计划精简列表") + @PreAuthorize("@ss.hasPermission('hrm:performance:archive:query')") + public CommonResult> getPerformanceArchivePlanSimpleList() { + // 1. 查询存在归档考核的绩效计划 + List assessments = + performanceAssessmentService.getPerformanceAssessmentListByStatus( + HrmPerformancePlanStatusEnum.ARCHIVED.getStatus()); + Set planIds = convertLinkedSet(assessments, HrmPerformanceAssessmentDO::getPlanId); + List plans = performancePlanService.getPerformancePlanList(planIds); + // 2. 转换响应 + return success(convertList(plans, plan -> new HrmPerformancePlanRespVO() + .setId(plan.getId()).setName(plan.getName()))); + } + + @DeleteMapping("/archive-delete") + @Operation(summary = "删除绩效归档记录") + @Parameter(name = "ids", description = "绩效归档记录编号列表", required = true, example = "1024,1025") + @PreAuthorize("@ss.hasPermission('hrm:performance:archive:delete')") + public CommonResult deletePerformanceArchiveRecords( + @RequestParam("ids") @NotEmpty(message = "归档记录不能为空") List ids) { + performanceAssessmentService.deletePerformanceArchiveRecords(ids); + return success(true); + } + + @DeleteMapping("/archive-employee-delete") + @Operation(summary = "删除员工的全部绩效档案") + @Parameter(name = "employeeIds", description = "员工编号列表", required = true, example = "1024,1025") + @PreAuthorize("@ss.hasPermission('hrm:performance:archive:delete')") + public CommonResult deletePerformanceArchiveEmployeeRecords( + @RequestParam("employeeIds") @NotEmpty(message = "员工不能为空") List employeeIds) { + performanceAssessmentService.deletePerformanceArchiveRecordsByEmployeeIds(employeeIds); + return success(true); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/HrmPerformanceAssessmentTemplateController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/HrmPerformanceAssessmentTemplateController.java new file mode 100644 index 0000000..24b4a5b --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/HrmPerformanceAssessmentTemplateController.java @@ -0,0 +1,148 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.performance; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; +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.performance.vo.assessmenttemplate.HrmPerformanceAssessmentTemplatePageReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessmenttemplate.HrmPerformanceAssessmentTemplateRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessmenttemplate.HrmPerformanceAssessmentTemplateSaveReqVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.performance.config.HrmPerformanceAssessmentTemplateDO; +import cn.iocoder.yudao.module.hrm.service.performance.config.HrmPerformanceAssessmentTemplateService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +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 java.util.Map; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; + +@Tag(name = "管理后台 - HRM 绩效考核模板") +@RestController +@RequestMapping("/hrm/performance/assessment-template") +@Validated +public class HrmPerformanceAssessmentTemplateController { + + @Resource + private HrmPerformanceAssessmentTemplateService assessmentTemplateService; + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建绩效考核模板") + @PreAuthorize("@ss.hasPermission('hrm:performance:assessment-template:create')") + public CommonResult createPerformanceAssessmentTemplate( + @Valid @RequestBody HrmPerformanceAssessmentTemplateSaveReqVO createReqVO) { + return success(assessmentTemplateService.createPerformanceAssessmentTemplate(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新绩效考核模板") + @PreAuthorize("@ss.hasPermission('hrm:performance:assessment-template:update')") + public CommonResult updatePerformanceAssessmentTemplate( + @Valid @RequestBody HrmPerformanceAssessmentTemplateSaveReqVO updateReqVO) { + assessmentTemplateService.updatePerformanceAssessmentTemplate(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除绩效考核模板") + @Parameter(name = "id", description = "模板编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:performance:assessment-template:delete')") + public CommonResult deletePerformanceAssessmentTemplate(@RequestParam("id") Long id) { + assessmentTemplateService.deletePerformanceAssessmentTemplate(id); + return success(true); + } + + @DeleteMapping("/delete-list") + @Operation(summary = "批量删除绩效考核模板") + @Parameter(name = "ids", description = "模板编号列表", required = true, example = "1024,1025") + @PreAuthorize("@ss.hasPermission('hrm:performance:assessment-template:delete')") + public CommonResult deletePerformanceAssessmentTemplateList(@RequestParam("ids") List ids) { + assessmentTemplateService.deletePerformanceAssessmentTemplateList(ids); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得绩效考核模板") + @Parameter(name = "id", description = "模板编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:performance:assessment-template:query')") + public CommonResult getPerformanceAssessmentTemplate( + @RequestParam("id") Long id) { + // 1. 获得考核模板 + HrmPerformanceAssessmentTemplateDO template = + assessmentTemplateService.getPerformanceAssessmentTemplate(id); + // 2. 拼接数据 + return success(buildAssessmentTemplateRespVO(template)); + } + + @GetMapping("/page") + @Operation(summary = "获得绩效考核模板分页") + @PreAuthorize("@ss.hasPermission('hrm:performance:assessment-template:query')") + public CommonResult> getPerformanceAssessmentTemplatePage( + @Validated HrmPerformanceAssessmentTemplatePageReqVO pageReqVO) { + PageResult pageResult = + assessmentTemplateService.getPerformanceAssessmentTemplatePage(pageReqVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty(pageResult.getTotal())); + } + // 2. 拼接数据 + return success(new PageResult<>(buildAssessmentTemplateRespVOList(pageResult.getList()), + pageResult.getTotal())); + } + + @GetMapping("/simple-list") + @Operation(summary = "获得绩效考核模板精简列表") + @PreAuthorize("@ss.hasPermission('hrm:performance:assessment-template:query')") + public CommonResult> getPerformanceAssessmentTemplateSimpleList() { + List templates = assessmentTemplateService + .getPerformanceAssessmentTemplateListByStatus(CommonStatusEnum.ENABLE.getStatus()); + return success(BeanUtils.toBean(templates, HrmPerformanceAssessmentTemplateRespVO.class)); + } + + // ==================== 拼接 VO ==================== + + private HrmPerformanceAssessmentTemplateRespVO buildAssessmentTemplateRespVO( + HrmPerformanceAssessmentTemplateDO template) { + if (template == null) { + return null; + } + return CollUtil.getFirst(buildAssessmentTemplateRespVOList(singletonList(template))); + } + + private List buildAssessmentTemplateRespVOList( + List templates) { + if (CollUtil.isEmpty(templates)) { + return emptyList(); + } + // 1. 获得创建人信息 + Map userMap = adminUserApi.getUserMap( + convertSet(templates, template -> NumberUtils.parseLong(template.getCreator()))); + // 2. 拼接响应 + return BeanUtils.toBean(templates, HrmPerformanceAssessmentTemplateRespVO.class, template -> + MapUtils.findAndThen(userMap, NumberUtils.parseLong(template.getCreator()), + user -> template.setCreatorName(user.getNickname()))); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/HrmPerformancePlanController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/HrmPerformancePlanController.java new file mode 100644 index 0000000..e2398ea --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/HrmPerformancePlanController.java @@ -0,0 +1,258 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.performance; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +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.performance.vo.assessment.HrmPerformancePlanResultLevelCountRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessment.HrmPerformancePlanStageCountRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.plan.HrmPerformancePlanPageReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.plan.HrmPerformancePlanRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.plan.HrmPerformancePlanSaveReqVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.performance.assessment.HrmPerformanceAssessmentDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.performance.assessment.HrmPerformanceAssessmentStageDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.performance.plan.HrmPerformancePlanDO; +import cn.iocoder.yudao.module.hrm.enums.performance.assessment.HrmPerformanceAssessmentProcessStatusEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformancePlanScopeTypeEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformanceStageTypeEnum; +import cn.iocoder.yudao.module.hrm.service.performance.assessment.HrmPerformanceAssessmentService; +import cn.iocoder.yudao.module.hrm.service.performance.plan.HrmPerformancePlanService; +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.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +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.convertListByFlatMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "管理后台 - HRM 绩效计划") +@RestController +@RequestMapping("/hrm/performance/plan") +@Validated +public class HrmPerformancePlanController { + + @Resource + private HrmPerformancePlanService performancePlanService; + @Resource + private HrmPerformanceAssessmentService performanceAssessmentService; + + @PostMapping("/create") + @Operation(summary = "创建绩效计划") + @PreAuthorize("@ss.hasPermission('hrm:performance:plan:create')") + public CommonResult createPerformancePlan( + @Valid @RequestBody HrmPerformancePlanSaveReqVO reqVO) { + return success(performancePlanService.createPerformancePlan(reqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新绩效计划") + @PreAuthorize("@ss.hasPermission('hrm:performance:plan:update')") + public CommonResult updatePerformancePlan(@Valid @RequestBody HrmPerformancePlanSaveReqVO reqVO) { + performancePlanService.updatePerformancePlan(reqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除绩效计划") + @Parameter(name = "id", description = "计划编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:performance:plan:delete')") + public CommonResult deletePerformancePlan(@RequestParam("id") Long id) { + performancePlanService.deletePerformancePlan(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得绩效计划") + @Parameter(name = "id", description = "绩效计划编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:performance:plan:query')") + public CommonResult getPerformancePlan(@RequestParam("id") Long id) { + return success(buildPlanRespVO(performancePlanService.getPerformancePlan(id))); + } + + @GetMapping("/page") + @Operation(summary = "获得绩效计划分页") + @PreAuthorize("@ss.hasPermission('hrm:performance:plan:query')") + public CommonResult> getPerformancePlanPage( + @Validated HrmPerformancePlanPageReqVO reqVO) { + PageResult pageResult = performancePlanService.getPerformancePlanPage(reqVO); + return success(new PageResult<>(buildPlanRespVOList(pageResult.getList()), pageResult.getTotal())); + } + + @PostMapping("/start") + @Operation(summary = "启动绩效计划") + @Parameter(name = "id", description = "绩效计划编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:performance:plan:update')") + public CommonResult startPerformancePlan(@RequestParam("id") Long id) { + performancePlanService.startPerformancePlan(id); + return success(true); + } + + @PostMapping("/open-scoring") + @Operation(summary = "开启绩效评分") + @Parameter(name = "id", description = "绩效计划编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:performance:plan:update')") + public CommonResult openPerformancePlanScoring(@RequestParam("id") Long id) { + performancePlanService.openPerformancePlanScoring(id); + return success(true); + } + + @PostMapping("/start-interview") + @Operation(summary = "发起绩效面谈") + @Parameter(name = "id", description = "绩效计划编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:performance:plan:update')") + public CommonResult startPerformancePlanInterview(@RequestParam("id") Long id) { + performancePlanService.startPerformancePlanInterview(id); + return success(true); + } + + @PostMapping("/archive") + @Operation(summary = "归档绩效计划") + @Parameter(name = "id", description = "绩效计划编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:performance:plan:update')") + public CommonResult archivePerformancePlan(@RequestParam("id") Long id) { + performancePlanService.archivePerformancePlan(id); + return success(true); + } + + @PostMapping("/terminate") + @Operation(summary = "终止绩效计划") + @Parameter(name = "id", description = "绩效计划编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:performance:plan:update')") + public CommonResult terminatePerformancePlan(@RequestParam("id") Long id) { + performancePlanService.terminatePerformancePlan(getLoginUserId(), id); + return success(true); + } + + @GetMapping("/status-count") + @Operation(summary = "获得绩效计划状态统计") + @PreAuthorize("@ss.hasPermission('hrm:performance:plan:query')") + public CommonResult> getPerformancePlanStatusCount( + @Validated HrmPerformancePlanPageReqVO reqVO) { + return success(performancePlanService.getPerformancePlanStatusCount(reqVO)); + } + + @GetMapping("/stage-count") + @Operation(summary = "获得绩效计划阶段统计") + @Parameter(name = "planId", description = "绩效计划编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:performance:plan:query')") + public CommonResult> getPerformancePlanStageCount( + @RequestParam("planId") Long planId) { + performancePlanService.validatePerformancePlanExists(planId); + // 1.1 查询绩效计划的员工考核 + List assessments = + performanceAssessmentService.getPerformanceAssessmentListByPlanId(planId); + // 1.2 按考核阶段统计 + Map countMap = convertMap( + filterList(assessments, assessment -> assessment.getStageType() != null), + HrmPerformanceAssessmentDO::getStageType, assessment -> 1L, Long::sum, LinkedHashMap::new); + // 2. 转换响应 + return success(convertList(Arrays.asList(HrmPerformanceStageTypeEnum.ARRAYS), + stageType -> new HrmPerformancePlanStageCountRespVO() + .setStageType(stageType).setCount(countMap.get(stageType)), + stageType -> countMap.getOrDefault(stageType, 0L) > 0)); + } + + @GetMapping("/level-count") + @Operation(summary = "获得绩效结果等级统计") + @Parameter(name = "planId", description = "绩效计划编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:performance:plan:query')") + public CommonResult> getPerformancePlanLevelCount( + @RequestParam("planId") Long planId) { + HrmPerformancePlanDO plan = performancePlanService.getPerformancePlan(planId); + if (plan == null) { + return success(Collections.emptyList()); + } + // 1.1 查询绩效计划的员工考核 + List assessments = + performanceAssessmentService.getPerformanceAssessmentListByPlanId(planId); + // 1.2 按结果模板顺序初始化全部等级,再累计实际人数 + Map levelCountMap = new LinkedHashMap<>(); + plan.getResultConfig().getLevels().forEach(level -> levelCountMap.put(level.getName(), 0L)); + filterList(assessments, assessment -> StrUtil.isNotBlank(assessment.getResultLevel())) + .forEach(assessment -> levelCountMap.merge(assessment.getResultLevel(), 1L, Long::sum)); + // 2. 转换响应 + return success(convertList(levelCountMap.entrySet(), entry -> new HrmPerformancePlanResultLevelCountRespVO() + .setLevelName(entry.getKey()).setCount(entry.getValue()))); + } + + // ==================== 拼接 VO ==================== + + private HrmPerformancePlanRespVO buildPlanRespVO(HrmPerformancePlanDO plan) { + return plan == null ? null : CollUtil.getFirst(buildPlanRespVOList(Collections.singletonList(plan))); + } + + private List buildPlanRespVOList(List plans) { + // 1. 获得员工考核和阶段信息 + List assessments = + performanceAssessmentService.getPerformanceAssessmentListByPlanIds( + convertList(plans, HrmPerformancePlanDO::getId)); + Map> assessmentMap = + convertMultiMap(assessments, HrmPerformanceAssessmentDO::getPlanId); + List stages = performanceAssessmentService.getPerformanceAssessmentStageList( + convertSet(assessments, HrmPerformanceAssessmentDO::getId)); + Map> stageMap = + convertMultiMap(stages, HrmPerformanceAssessmentStageDO::getAssessmentId); + // 2. 拼接响应 + Map planMap = convertMap(plans, HrmPerformancePlanDO::getId); + return BeanUtils.toBean(plans, HrmPerformancePlanRespVO.class, respVO -> { + HrmPerformancePlanDO plan = planMap.get(respVO.getId()); + // 排除员工范围只用于计划内部维护,不返回给前端编辑 + respVO.setScopes(filterList(respVO.getScopes(), scope -> ObjUtil.notEqual(scope.getType(), + HrmPerformancePlanScopeTypeEnum.EXCLUDED_EMPLOYEE.getType()))); + if (plan.getAssessmentConfig() != null) { + respVO.setAssessmentTemplateName(plan.getAssessmentConfig().getName()); + } + if (plan.getResultConfig() != null) { + respVO.setResultTemplateName(plan.getResultConfig().getName()); + } + // 获得当前计划的员工考核和处理阶段,用于统计执行进度并判断后续可操作节点 + List planAssessments = + assessmentMap.getOrDefault(plan.getId(), Collections.emptyList()); + List planStages = convertListByFlatMap( + planAssessments, assessment -> stageMap.getOrDefault( + assessment.getId(), Collections.emptyList()).stream()); + int finishedCount = getSumValue(planAssessments, assessment -> Objects.equals( + assessment.getProcessStatus(), + HrmPerformanceAssessmentProcessStatusEnum.FINISHED.getStatus()) ? 1 : 0, Integer::sum, 0); + Map stageCountMap = convertMap( + filterList(planAssessments, assessment -> assessment.getStageType() != null), + HrmPerformanceAssessmentDO::getStageType, assessment -> 1L, Long::sum, LinkedHashMap::new); + respVO.setEmployeeCount(planAssessments.size()).setFinishedCount(finishedCount) + .setStageCountMap(stageCountMap) + .setScoringReady(performanceAssessmentService + .isPerformanceAssessmentScoringReady(plan, planAssessments)) + .setInterviewReady(performanceAssessmentService + .isPerformanceAssessmentInterviewReady(plan, planAssessments, planStages)) + .setArchiveReady(performanceAssessmentService + .isPerformanceAssessmentArchiveReady(plan, planAssessments)); + }); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/HrmPerformanceResultTemplateController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/HrmPerformanceResultTemplateController.java new file mode 100644 index 0000000..0f87939 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/HrmPerformanceResultTemplateController.java @@ -0,0 +1,149 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.performance; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.resulttemplate.HrmPerformanceResultTemplatePageReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.resulttemplate.HrmPerformanceResultTemplateRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.resulttemplate.HrmPerformanceResultTemplateSaveReqVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.performance.config.HrmPerformanceResultTemplateDO; +import cn.iocoder.yudao.module.hrm.service.performance.config.HrmPerformanceResultTemplateService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +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 java.util.Map; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; + +@Tag(name = "管理后台 - HRM 绩效结果模板") +@RestController +@RequestMapping("/hrm/performance/result-template") +@Validated +public class HrmPerformanceResultTemplateController { + + @Resource + private HrmPerformanceResultTemplateService resultTemplateService; + + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建绩效结果模板") + @PreAuthorize("@ss.hasPermission('hrm:performance:result-template:create')") + public CommonResult createPerformanceResultTemplate( + @Valid @RequestBody HrmPerformanceResultTemplateSaveReqVO createReqVO) { + return success(resultTemplateService.createPerformanceResultTemplate(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新绩效结果模板") + @PreAuthorize("@ss.hasPermission('hrm:performance:result-template:update')") + public CommonResult updatePerformanceResultTemplate( + @Valid @RequestBody HrmPerformanceResultTemplateSaveReqVO updateReqVO) { + resultTemplateService.updatePerformanceResultTemplate(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除绩效结果模板") + @Parameter(name = "id", description = "模板编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:performance:result-template:delete')") + public CommonResult deletePerformanceResultTemplate(@RequestParam("id") Long id) { + resultTemplateService.deletePerformanceResultTemplate(id); + return success(true); + } + + @DeleteMapping("/delete-list") + @Operation(summary = "批量删除绩效结果模板") + @Parameter(name = "ids", description = "模板编号列表", required = true, example = "1024,1025") + @PreAuthorize("@ss.hasPermission('hrm:performance:result-template:delete')") + public CommonResult deletePerformanceResultTemplateList(@RequestParam("ids") List ids) { + resultTemplateService.deletePerformanceResultTemplateList(ids); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得绩效结果模板") + @Parameter(name = "id", description = "模板编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:performance:result-template:query')") + public CommonResult getPerformanceResultTemplate( + @RequestParam("id") Long id) { + // 1. 获得结果模板 + HrmPerformanceResultTemplateDO template = resultTemplateService.getPerformanceResultTemplate(id); + // 2. 拼接数据 + return success(buildResultTemplateRespVO(template)); + } + + @GetMapping("/page") + @Operation(summary = "获得绩效结果模板分页") + @PreAuthorize("@ss.hasPermission('hrm:performance:result-template:query')") + public CommonResult> getPerformanceResultTemplatePage( + @Validated HrmPerformanceResultTemplatePageReqVO pageReqVO) { + PageResult pageResult = + resultTemplateService.getPerformanceResultTemplatePage(pageReqVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty(pageResult.getTotal())); + } + // 2. 拼接数据 + return success(new PageResult<>(buildResultTemplateRespVOList(pageResult.getList()), + pageResult.getTotal())); + } + + @GetMapping("/simple-list") + @Operation(summary = "获得绩效结果模板精简列表") + @Parameter(name = "status", description = "状态", example = "0") + @PreAuthorize("@ss.hasPermission('hrm:performance:result-template:query')") + public CommonResult> getPerformanceResultTemplateSimpleList( + @RequestParam(value = "status", required = false) Integer status) { + List templates = + resultTemplateService.getPerformanceResultTemplateList(status); + return success(BeanUtils.toBean(templates, HrmPerformanceResultTemplateRespVO.class)); + } + + // ==================== 拼接 VO ==================== + + private HrmPerformanceResultTemplateRespVO buildResultTemplateRespVO( + HrmPerformanceResultTemplateDO template) { + if (template == null) { + return null; + } + return CollUtil.getFirst(buildResultTemplateRespVOList(singletonList(template))); + } + + private List buildResultTemplateRespVOList( + List templates) { + if (CollUtil.isEmpty(templates)) { + return emptyList(); + } + // 1. 获得创建人信息 + Map userMap = adminUserApi.getUserMap( + convertSet(templates, template -> NumberUtils.parseLong(template.getCreator()))); + // 2. 拼接响应 + return BeanUtils.toBean(templates, HrmPerformanceResultTemplateRespVO.class, template -> + MapUtils.findAndThen(userMap, NumberUtils.parseLong(template.getCreator()), + user -> template.setCreatorName(user.getNickname()))); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceArchiveEmployeePageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceArchiveEmployeePageReqVO.java new file mode 100644 index 0000000..517d787 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceArchiveEmployeePageReqVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessment; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Schema(description = "管理后台 - HRM 员工绩效档案分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class HrmPerformanceArchiveEmployeePageReqVO extends PageParam { + + @Schema(description = "员工姓名或工号,模糊匹配", example = "张三") + private String search; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceArchiveEmployeeRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceArchiveEmployeeRespVO.java new file mode 100644 index 0000000..d18e4e1 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceArchiveEmployeeRespVO.java @@ -0,0 +1,54 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessment; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - HRM 员工绩效档案 Response VO") +@Data +public class HrmPerformanceArchiveEmployeeRespVO { + + @Schema(description = "员工编号", example = "1024") + private Long employeeId; + + @Schema(description = "员工姓名", example = "张三") + private String employeeName; + + @Schema(description = "工号", example = "HRM001") + private String jobNumber; + + @Schema(description = "部门编号", example = "100") + private Long deptId; + + @Schema(description = "部门名称", example = "研发部") + private String deptName; + + @Schema(description = "职位名称", example = "Java 工程师") + private String postName; + + @Schema(description = "手机号码", example = "15601691300") + private String mobile; + + @Schema(description = "员工状态", example = "1") + private Integer employeeStatus; + + @Schema(description = "聘用形式", example = "1") + private Integer employeeType; + + @Schema(description = "最近考核编号", example = "2048") + private Long latestAssessmentId; + + @Schema(description = "最近考核计划", example = "2026 年度绩效考核") + private String latestPlanName; + + @Schema(description = "最近绩效评分", example = "95.50") + private BigDecimal latestScore; + + @Schema(description = "最近绩效等级", example = "A") + private String latestResultLevel; + + @Schema(description = "考核次数", example = "3") + private Long assessmentCount; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceArchivePageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceArchivePageReqVO.java new file mode 100644 index 0000000..8d33b2a --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceArchivePageReqVO.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessment; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +@Schema(description = "管理后台 - HRM 绩效归档分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class HrmPerformanceArchivePageReqVO extends PageParam { + + @Schema(description = "绩效计划编号", example = "1024") + private Long planId; + + @Schema(description = "员工编号", example = "2048") + private Long employeeId; + + @Schema(description = "员工编号列表", example = "[2048, 2049]") + private List employeeIds; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceAssessmentBatchReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceAssessmentBatchReqVO.java new file mode 100644 index 0000000..8a8fc67 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceAssessmentBatchReqVO.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessment; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - HRM 绩效计划增删员工 Request VO") +@Data +public class HrmPerformanceAssessmentBatchReqVO { + + @Schema(description = "绩效计划编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "绩效计划不能为空") + private Long planId; + + @Schema(description = "员工编号列表", requiredMode = Schema.RequiredMode.REQUIRED, + example = "[1024, 2048]") + @NotEmpty(message = "员工不能为空") + private List employeeIds; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceAssessmentDimensionRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceAssessmentDimensionRespVO.java new file mode 100644 index 0000000..94dfba8 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceAssessmentDimensionRespVO.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessment; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - HRM 员工绩效维度 Response VO") +@Data +public class HrmPerformanceAssessmentDimensionRespVO { + + @Schema(description = "维度编号") + private Long id; + + @Schema(description = "员工绩效考核编号") + private Long assessmentId; + + @Schema(description = "维度名称") + private String name; + + @Schema(description = "指标类型") + private Integer quotaType; + + @Schema(description = "维度权重") + private BigDecimal weight; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "是否允许编辑") + private Boolean allowEdit; + + @Schema(description = "排序") + private Integer sort; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceAssessmentPageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceAssessmentPageReqVO.java new file mode 100644 index 0000000..bb62fc3 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceAssessmentPageReqVO.java @@ -0,0 +1,58 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessment; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.assessment.HrmPerformanceAppealStatusEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformancePlanStatusEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformanceStageTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +@Schema(description = "管理后台 - HRM 绩效计划员工分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class HrmPerformanceAssessmentPageReqVO extends PageParam { + + @Schema(description = "绩效计划编号", example = "1024") + private Long planId; + + @Schema(description = "员工编号", example = "2048") + private Long employeeId; + + @Schema(description = "员工编号列表", example = "[2048, 2049]") + private List employeeIds; + + @Schema(description = "员工姓名或工号", example = "张三") + private String search; + + @Schema(description = "部门编号", example = "100") + private Long deptId; + + @Schema(description = "聘用形式", example = "1") + private Integer employeeType; + + @Schema(description = "员工状态", example = "1") + private Integer employeeStatus; + + @Schema(description = "考核状态", example = "1") + @InEnum(value = HrmPerformancePlanStatusEnum.class, message = "考核状态必须是 {value}") + private Integer status; + + @Schema(description = "阶段状态", example = "2") + @InEnum(value = HrmPerformanceStageTypeEnum.class, message = "阶段状态必须是 {value}") + private Integer stageType; + + @Schema(description = "申诉状态", example = "1") + @InEnum(value = HrmPerformanceAppealStatusEnum.class, message = "申诉状态必须是 {value}") + private Integer appealStatus; + + @Schema(description = "结果等级", example = "A") + private String resultLevel; + + @Schema(description = "结果等级是否为空", example = "false") + private Boolean resultLevelEmpty; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceAssessmentQuotaRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceAssessmentQuotaRespVO.java new file mode 100644 index 0000000..51abaf6 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceAssessmentQuotaRespVO.java @@ -0,0 +1,69 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessment; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - HRM 员工绩效指标 Response VO") +@Data +public class HrmPerformanceAssessmentQuotaRespVO { + + @Schema(description = "编号") + private Long id; + + @Schema(description = "员工绩效考核编号") + private Long assessmentId; + + @Schema(description = "员工绩效维度编号") + private Long dimensionId; + + @Schema(description = "是否允许编辑") + private Boolean allowEdit; + + @Schema(description = "预设值") + private Boolean preset; + + @Schema(description = "维度名称") + private String dimensionName; + + @Schema(description = "指标名称") + private String name; + + @Schema(description = "指标说明") + private String description; + + @Schema(description = "标准值") + private String standard; + + @Schema(description = "维度权重") + private BigDecimal dimensionWeight; + + @Schema(description = "指标权重") + private BigDecimal weight; + + @Schema(description = "分数类型") + private Integer scoreType; + + @Schema(description = "目标值") + private String targetValue; + + @Schema(description = "实际值") + private String actualValue; + + @Schema(description = "自评分数") + private BigDecimal selfScore; + + @Schema(description = "评分人得分") + private BigDecimal reviewerScore; + + @Schema(description = "最终得分") + private BigDecimal finalScore; + + @Schema(description = "说明") + private String comment; + + @Schema(description = "排序") + private Integer sort; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceAssessmentQuotaScoreRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceAssessmentQuotaScoreRespVO.java new file mode 100644 index 0000000..e1e66cb --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceAssessmentQuotaScoreRespVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessment; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - HRM 员工绩效指标评分明细 Response VO") +@Data +public class HrmPerformanceAssessmentQuotaScoreRespVO { + + @Schema(description = "编号") + private Long id; + + @Schema(description = "员工评分阶段编号") + private Long assessmentStageId; + + @Schema(description = "员工绩效指标编号") + private Long assessmentQuotaId; + + @Schema(description = "得分") + private BigDecimal score; + + @Schema(description = "说明") + private String comment; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceAssessmentRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceAssessmentRespVO.java new file mode 100644 index 0000000..1fc3f02 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceAssessmentRespVO.java @@ -0,0 +1,170 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessment; + +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 +public class HrmPerformanceAssessmentRespVO { + + @Schema(description = "编号") + private Long id; + + @Schema(description = "绩效计划编号") + private Long planId; + + @Schema(description = "绩效计划名称") + private String name; + + @Schema(description = "考核周期类型") + private Integer cycleType; + + @Schema(description = "考核周期") + private String cycle; + + @Schema(description = "开始日期") + private LocalDateTime startTime; + + @Schema(description = "结束日期") + private LocalDateTime endTime; + + @Schema(description = "单项评分上限") + private BigDecimal upperLimitScore; + + @Schema(description = "员工编号") + private Long employeeId; + + @Schema(description = "员工姓名") + private String employeeName; + + @Schema(description = "工号") + private String jobNumber; + + @Schema(description = "手机号") + private String mobile; + + @Schema(description = "部门编号") + private Long deptId; + + @Schema(description = "部门名称") + private String deptName; + + @Schema(description = "职位名称") + private String postName; + + @Schema(description = "聘用形式") + private Integer employeeType; + + @Schema(description = "员工状态") + private Integer employeeStatus; + + @Schema(description = "当前处理人") + private String currentHandlerName; + + @Schema(description = "考核状态") + private Integer status; + + @Schema(description = "激活状态") + private Integer processStatus; + + @Schema(description = "阶段状态") + private Integer stageType; + + @Schema(description = "阶段排序") + private Integer stageSort; + + @Schema(description = "得分") + private BigDecimal score; + + @Schema(description = "结果等级") + private String resultLevel; + + @Schema(description = "绩效系数") + private BigDecimal coefficient; + + @Schema(description = "目标确认员工姓名") + private String targetConfirmationEmployeeName; + + @Schema(description = "目标确认结果") + private Integer targetConfirmationResult; + + @Schema(description = "目标确认意见") + private String targetConfirmationComment; + + @Schema(description = "目标确认时间") + private LocalDateTime targetConfirmationTime; + + @Schema(description = "是否可确认目标") + private Boolean canConfirmTarget; + + @Schema(description = "自评说明") + private String selfComment; + + @Schema(description = "评分人说明") + private String reviewerComment; + + @Schema(description = "结果说明") + private String resultComment; + + @Schema(description = "结果确认时间") + private LocalDateTime resultConfirmationTime; + + @Schema(description = "结果审核状态") + private Integer resultAuditStatus; + + @Schema(description = "结果审核时间") + private LocalDateTime resultAuditTime; + + @Schema(description = "结果审核意见") + private String resultAuditReason; + + @Schema(description = "申诉原因") + private String appealReason; + + @Schema(description = "申诉附件地址列表") + private List appealFileUrls; + + @Schema(description = "申诉退回评分阶段编号列表") + private List appealReviewStageIds; + + @Schema(description = "申诉提交时间") + private LocalDateTime appealSubmitTime; + + @Schema(description = "申诉状态") + private Integer appealStatus; + + @Schema(description = "申诉处理时间") + private LocalDateTime appealTime; + + @Schema(description = "申诉处理意见") + private String appealComment; + + @Schema(description = "归档时间") + private LocalDateTime archiveTime; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + + @Schema(description = "指标维度列表") + private List dimensions; + + @Schema(description = "指标列表") + private List quotas; + + @Schema(description = "评分阶段列表") + private List reviewStages; + + @Schema(description = "当前评分阶段") + private HrmPerformanceAssessmentStageRespVO currentReviewStage; + + @Schema(description = "运行阶段列表") + private List stages; + + @Schema(description = "当前待处理阶段") + private HrmPerformanceAssessmentStageRespVO currentStage; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceAssessmentStageRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceAssessmentStageRespVO.java new file mode 100644 index 0000000..b98bbf1 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceAssessmentStageRespVO.java @@ -0,0 +1,83 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessment; + +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 +public class HrmPerformanceAssessmentStageRespVO { + + @Schema(description = "编号") + private Long id; + + @Schema(description = "员工绩效考核编号") + private Long assessmentId; + + @Schema(description = "阶段类型") + private Integer type; + + @Schema(description = "处理员工编号") + private Long handlerEmployeeId; + + @Schema(description = "处理人姓名") + private String handlerName; + + @Schema(description = "阶段名称") + private String name; + + @Schema(description = "评分人类型") + private Integer raterType; + + @Schema(description = "权重") + private BigDecimal weight; + + @Schema(description = "评分类型") + private Integer scoringType; + + @Schema(description = "可见内容") + private Integer visibleContent; + + @Schema(description = "是否必填") + private Boolean requiredSetting; + + @Schema(description = "驳回权限") + private Boolean rejectAuthority; + + @Schema(description = "排序") + private Integer sort; + + @Schema(description = "绩效员工评审阶段状态") + private Integer status; + + @Schema(description = "得分") + private BigDecimal score; + + @Schema(description = "结果等级") + private String resultLevel; + + @Schema(description = "说明") + private String comment; + + @Schema(description = "驳回原因") + private String rejectReason; + + @Schema(description = "提交时间") + private LocalDateTime submitTime; + + @Schema(description = "截止时间") + private LocalDateTime deadlineTime; + + @Schema(description = "是否可处理") + private Boolean canHandle; + + @Schema(description = "是否可评分") + private Boolean canScore; + + @Schema(description = "指标评分列表") + private List quotaScoreList; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformancePlanResultLevelCountRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformancePlanResultLevelCountRespVO.java new file mode 100644 index 0000000..409e03c --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformancePlanResultLevelCountRespVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessment; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - HRM 绩效计划结果等级统计 Response VO") +@Data +public class HrmPerformancePlanResultLevelCountRespVO { + + @Schema(description = "等级名称") + private String levelName; + + @Schema(description = "数量") + private Long count; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformancePlanStageCountRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformancePlanStageCountRespVO.java new file mode 100644 index 0000000..82602e9 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformancePlanStageCountRespVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessment; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - HRM 绩效计划阶段统计 Response VO") +@Data +public class HrmPerformancePlanStageCountRespVO { + + @Schema(description = "阶段状态") + private Integer stageType; + + @Schema(description = "数量") + private Long count; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceProcessRecordRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceProcessRecordRespVO.java new file mode 100644 index 0000000..6a43e38 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessment/HrmPerformanceProcessRecordRespVO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessment; + +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 HrmPerformanceProcessRecordRespVO { + + @Schema(description = "标题") + private String title; + + @Schema(description = "内容") + private String content; + + @Schema(description = "来源") + private String source; + + @Schema(description = "绩效流程记录状态") + private Integer status; + + @Schema(description = "操作人姓名") + private String operatorName; + + @Schema(description = "操作时间") + private LocalDateTime operateTime; + + @Schema(description = "附件地址列表") + private List fileUrls; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessmenttemplate/HrmPerformanceAssessmentTemplatePageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessmenttemplate/HrmPerformanceAssessmentTemplatePageReqVO.java new file mode 100644 index 0000000..288b3b0 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessmenttemplate/HrmPerformanceAssessmentTemplatePageReqVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessmenttemplate; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Schema(description = "管理后台 - HRM 绩效考核模板分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class HrmPerformanceAssessmentTemplatePageReqVO extends PageParam { + + @Schema(description = "模板名称,模糊匹配", example = "季度") + private String name; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessmenttemplate/HrmPerformanceAssessmentTemplateRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessmenttemplate/HrmPerformanceAssessmentTemplateRespVO.java new file mode 100644 index 0000000..5930ed0 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessmenttemplate/HrmPerformanceAssessmentTemplateRespVO.java @@ -0,0 +1,99 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessmenttemplate; + +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 +public class HrmPerformanceAssessmentTemplateRespVO { + + @Schema(description = "模板编号", example = "1024") + private Long id; + + @Schema(description = "模板名称", example = "季度绩效模板") + private String name; + + @Schema(description = "模板说明", example = "适用于季度绩效考核") + private String illustrate; + + @Schema(description = "计分方式", example = "1") + private Integer scoreCalculation; + + @Schema(description = "分数上限类型", example = "1") + private Integer upperLimitType; + + @Schema(description = "分数上限", example = "100") + private BigDecimal upperLimitScore; + + @Schema(description = "维度数量", example = "3") + private Integer dimensionCount; + + @Schema(description = "指标数量", example = "12") + private Integer quotaCount; + + @Schema(description = "状态", example = "0") + private Integer status; + + @Schema(description = "考核维度列表") + private List dimensions; + + @Schema(description = "创建人") + private String creator; + + @Schema(description = "创建人名称") + private String creatorName; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + + @Schema(description = "更新时间") + private LocalDateTime updateTime; + + @Schema(description = "管理后台 - HRM 绩效考核模板维度") + @Data + public static class Dimension { + + @Schema(description = "维度名称", example = "工作业绩") + private String name; + + @Schema(description = "指标类型", example = "1") + private Integer quotaType; + + @Schema(description = "维度权重", example = "60") + private BigDecimal weight; + + @Schema(description = "备注", example = "核心业绩指标") + private String remark; + + @Schema(description = "是否允许员工编辑", example = "true") + private Boolean allowEdit; + + @Schema(description = "考核指标列表") + private List quotas; + } + + @Schema(description = "管理后台 - HRM 绩效考核模板指标") + @Data + public static class Quota { + + @Schema(description = "指标名称", example = "销售目标达成率") + private String name; + + @Schema(description = "指标说明", example = "按季度销售目标计算") + private String illustrate; + + @Schema(description = "评分标准", example = "完成率达到 100% 得满分") + private String standard; + + @Schema(description = "指标权重", example = "50") + private BigDecimal weight; + + @Schema(description = "评分类型", example = "1") + private Integer scoreType; + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessmenttemplate/HrmPerformanceAssessmentTemplateSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessmenttemplate/HrmPerformanceAssessmentTemplateSaveReqVO.java new file mode 100644 index 0000000..4a84b91 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/assessmenttemplate/HrmPerformanceAssessmentTemplateSaveReqVO.java @@ -0,0 +1,234 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessmenttemplate; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformanceQuotaScoreTypeEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.config.HrmPerformanceQuotaTypeEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.config.HrmPerformanceScoreCalculationEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.config.HrmPerformanceUpperLimitTypeEnum; +import com.fasterxml.jackson.annotation.JsonIgnore; +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.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.util.number.MoneyUtils.PERCENT_100; + +@Schema(description = "管理后台 - HRM 绩效考核模板保存 Request VO") +@Data +public class HrmPerformanceAssessmentTemplateSaveReqVO { + + @Schema(description = "模板编号", example = "1024") + private Long id; + + @Schema(description = "模板名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "季度绩效模板") + @NotBlank(message = "模板名称不能为空") + @Size(max = 50, message = "模板名称不能超过 50 个字符") + private String name; + + @Schema(description = "模板说明", example = "适用于季度绩效考核") + @Size(max = 200, message = "模板说明不能超过 200 个字符") + private String illustrate; + + @Schema(description = "计分方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "计分方式不能为空") + @InEnum(value = HrmPerformanceScoreCalculationEnum.class, message = "计分方式必须是 {value}") + private Integer scoreCalculation; + + @Schema(description = "分数上限类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "分数上限类型不能为空") + @InEnum(value = HrmPerformanceUpperLimitTypeEnum.class, message = "分数上限类型必须是 {value}") + private Integer upperLimitType; + + @Schema(description = "分数上限", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + @NotNull(message = "分数上限不能为空") + @DecimalMin(value = "0", message = "分数上限不能小于 0") + @DecimalMax(value = "100", message = "分数上限不能大于 100") + private BigDecimal upperLimitScore; + + @Schema(description = "考核维度列表", requiredMode = Schema.RequiredMode.REQUIRED) + @Valid + @NotEmpty(message = "至少需要一个考核维度") + private List<@NotNull(message = "考核维度不能为空") Dimension> dimensions; + + @AssertTrue(message = "考核维度名称不能重复") + @JsonIgnore + public boolean isDimensionNamesUnique() { + if (CollUtil.isEmpty(dimensions)) { + return true; + } + Set names = new HashSet<>(); + for (Dimension dimension : dimensions) { + if (dimension != null && StrUtil.isNotBlank(dimension.getName()) + && !names.add(dimension.getName().trim())) { + return false; + } + } + return true; + } + + @AssertTrue(message = "考核指标名称不能重复") + @JsonIgnore + public boolean isQuotaNamesUnique() { + if (CollUtil.isEmpty(dimensions)) { + return true; + } + Set names = new HashSet<>(); + for (Dimension dimension : dimensions) { + if (dimension == null || CollUtil.isEmpty(dimension.getQuotas())) { + continue; + } + for (Quota quota : dimension.getQuotas()) { + if (quota != null && StrUtil.isNotBlank(quota.getName()) + && !names.add(quota.getName().trim())) { + return false; + } + } + } + return true; + } + + @AssertTrue(message = "维度权重总和必须等于 100%") + @JsonIgnore + public boolean isDimensionWeightTotalValid() { + if (CollUtil.isEmpty(dimensions)) { + return true; + } + BigDecimal totalWeight = BigDecimal.ZERO; + for (Dimension dimension : dimensions) { + if (dimension == null || dimension.getWeight() == null) { + return true; + } + totalWeight = totalWeight.add(dimension.getWeight()); + } + return totalWeight.compareTo(PERCENT_100) == 0; + } + + @AssertTrue(message = "不可编辑维度的指标权重总和必须等于 100%") + @JsonIgnore + public boolean isFixedQuotaWeightTotalValid() { + if (CollUtil.isEmpty(dimensions)) { + return true; + } + for (Dimension dimension : dimensions) { + if (dimension == null || ObjUtil.notEqual(Boolean.FALSE, dimension.getAllowEdit()) + || CollUtil.isEmpty(dimension.getQuotas())) { + continue; + } + BigDecimal totalWeight = BigDecimal.ZERO; + for (Quota quota : dimension.getQuotas()) { + if (quota == null || quota.getWeight() == null) { + return true; + } + totalWeight = totalWeight.add(quota.getWeight()); + } + if (totalWeight.compareTo(PERCENT_100) != 0) { + return false; + } + } + return true; + } + + @AssertTrue(message = "可编辑维度的指标权重总和不能大于 100%") + @JsonIgnore + public boolean isEditableQuotaWeightTotalValid() { + if (CollUtil.isEmpty(dimensions)) { + return true; + } + for (Dimension dimension : dimensions) { + if (dimension == null || ObjUtil.notEqual(Boolean.TRUE, dimension.getAllowEdit()) + || CollUtil.isEmpty(dimension.getQuotas())) { + continue; + } + BigDecimal totalWeight = BigDecimal.ZERO; + for (Quota quota : dimension.getQuotas()) { + if (quota == null || quota.getWeight() == null) { + return true; + } + totalWeight = totalWeight.add(quota.getWeight()); + } + if (totalWeight.compareTo(PERCENT_100) > 0) { + return false; + } + } + return true; + } + + @Schema(description = "管理后台 - HRM 绩效考核模板维度") + @Data + public static class Dimension { + + @Schema(description = "维度名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "工作业绩") + @NotBlank(message = "维度名称不能为空") + @Size(max = 50, message = "维度名称不能超过 50 个字符") + private String name; + + @Schema(description = "指标类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "指标类型不能为空") + @InEnum(value = HrmPerformanceQuotaTypeEnum.class, message = "指标类型必须是 {value}") + private Integer quotaType; + + @Schema(description = "维度权重", requiredMode = Schema.RequiredMode.REQUIRED, example = "60") + @NotNull(message = "维度权重不能为空") + @DecimalMin(value = "0", message = "维度权重不能小于 0") + @DecimalMax(value = "100", message = "维度权重不能大于 100") + private BigDecimal weight; + + @Schema(description = "备注", example = "核心业绩指标") + @Size(max = 200, message = "维度备注不能超过 200 个字符") + private String remark; + + @Schema(description = "是否允许员工编辑", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是否允许员工编辑不能为空") + private Boolean allowEdit; + + @Schema(description = "考核指标列表", requiredMode = Schema.RequiredMode.REQUIRED) + @Valid + @NotEmpty(message = "每个考核维度至少需要一个指标") + private List<@NotNull(message = "考核指标不能为空") Quota> quotas; + } + + @Schema(description = "管理后台 - HRM 绩效考核模板指标") + @Data + public static class Quota { + + @Schema(description = "指标名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "销售目标达成率") + @NotBlank(message = "指标名称不能为空") + @Size(max = 50, message = "指标名称不能超过 50 个字符") + private String name; + + @Schema(description = "指标说明", example = "按季度销售目标计算") + @Size(max = 200, message = "指标说明不能超过 200 个字符") + private String illustrate; + + @Schema(description = "评分标准", requiredMode = Schema.RequiredMode.REQUIRED, example = "完成率达到 100% 得满分") + @NotBlank(message = "评分标准不能为空") + @Size(max = 200, message = "评分标准不能超过 200 个字符") + private String standard; + + @Schema(description = "指标权重", requiredMode = Schema.RequiredMode.REQUIRED, example = "50") + @NotNull(message = "指标权重不能为空") + @DecimalMin(value = "0", message = "指标权重不能小于 0") + @DecimalMax(value = "100", message = "指标权重不能大于 100") + private BigDecimal weight; + + @Schema(description = "评分类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "评分类型不能为空") + @InEnum(value = HrmPerformanceQuotaScoreTypeEnum.class, message = "评分类型必须是 {value}") + private Integer scoreType; + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/plan/HrmPerformancePlanPageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/plan/HrmPerformancePlanPageReqVO.java new file mode 100644 index 0000000..917dc1c --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/plan/HrmPerformancePlanPageReqVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.plan; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformancePlanStatusEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformanceStageTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Schema(description = "管理后台 - HRM 绩效计划分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class HrmPerformancePlanPageReqVO extends PageParam { + + @Schema(description = "绩效计划名称", example = "2026 年第三季度绩效计划") + private String name; + + @Schema(description = "绩效计划状态", example = "1") + @InEnum(value = HrmPerformancePlanStatusEnum.class, message = "计划状态必须是 {value}") + private Integer status; + + @Schema(description = "阶段状态", example = "2") + @InEnum(value = HrmPerformanceStageTypeEnum.class, message = "阶段状态必须是 {value}") + private Integer stageType; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/plan/HrmPerformancePlanRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/plan/HrmPerformancePlanRespVO.java new file mode 100644 index 0000000..eed7f5d --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/plan/HrmPerformancePlanRespVO.java @@ -0,0 +1,280 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.plan; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +@Schema(description = "管理后台 - HRM 绩效计划 Response VO") +@Data +public class HrmPerformancePlanRespVO { + + @Schema(description = "编号") + private Long id; + + @Schema(description = "绩效计划名称") + private String name; + + @Schema(description = "考核周期类型") + private Integer cycleType; + + @Schema(description = "考核周期") + private String cycle; + + @Schema(description = "季度") + private Integer quarter; + + @Schema(description = "开始日期") + private LocalDateTime startTime; + + @Schema(description = "结束日期") + private LocalDateTime endTime; + + @Schema(description = "考核说明") + private String description; + + @Schema(description = "考评范围列表") + private List scopes; + + @Schema(description = "考核模板编号") + private Long assessmentTemplateId; + + @Schema(description = "考核配置快照") + private AssessmentConfig assessmentConfig; + + @Schema(description = "结果模板编号") + private Long resultTemplateId; + + @Schema(description = "结果配置快照") + private ResultConfig resultConfig; + + @Schema(description = "指标设置类型") + private Integer quotaSettingType; + + @Schema(description = "是否需要目标确认") + private Boolean targetConfirmation; + + @Schema(description = "目标确认节点") + private HandlerStage targetConfirmationStage; + + @Schema(description = "评分阶段列表") + private List reviewStages; + + @Schema(description = "是否需要结果审核") + private Boolean resultAudit; + + @Schema(description = "结果审核节点列表") + private List resultAuditStages; + + @Schema(description = "是否需要结果确认") + private Boolean resultConfirmation; + + @Schema(description = "申诉处理节点列表") + private List appealStages; + + @Schema(description = "申诉超期天数") + private Integer appealTimeoutDays; + + @Schema(description = "申诉超期处理方式") + private Integer appealTimeoutAction; + + @Schema(description = "是否同步薪资") + private Boolean syncToSalary; + + @Schema(description = "计薪月份") + private String paidForMonth; + + @Schema(description = "各阶段员工数量") + private Map stageCountMap; + + @Schema(description = "阶段状态") + private Integer stageType; + + @Schema(description = "绩效计划状态") + private Integer status; + + @Schema(description = "当前操作阶段") + private Integer operationType; + + @Schema(description = "终止时间") + private LocalDateTime terminateTime; + + @Schema(description = "考核模板名称") + private String assessmentTemplateName; + + @Schema(description = "结果模板名称") + private String resultTemplateName; + + @Schema(description = "员工数量") + private Integer employeeCount; + + @Schema(description = "已完成数量") + private Integer finishedCount; + + @Schema(description = "是否可开始评分") + private Boolean scoringReady; + + @Schema(description = "是否可开始面谈") + private Boolean interviewReady; + + @Schema(description = "是否可归档") + private Boolean archiveReady; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + + @Schema(description = "管理后台 - HRM 绩效计划考评范围 Response VO") + @Data + public static class Scope { + + @Schema(description = "范围类型", example = "1") + private Integer type; + + @Schema(description = "员工编号列表", example = "[1024, 1025]") + private List employeeIds; + + @Schema(description = "部门编号列表", example = "[100, 101]") + private List deptIds; + + @Schema(description = "聘用形式", example = "1") + private Integer employeeType; + + @Schema(description = "员工状态列表", example = "[1, 2]") + private List employeeStatuses; + } + + @Schema(description = "管理后台 - HRM 绩效计划处理节点 Response VO") + @Data + public static class HandlerStage { + + @Schema(description = "处理人类型", example = "1") + private Integer type; + + @Schema(description = "上级或部门层级", example = "1") + private Integer level; + + @Schema(description = "指定处理员工编号", example = "1024") + private Long employeeId; + } + + @Schema(description = "管理后台 - HRM 绩效计划评分阶段 Response VO") + @Data + public static class ReviewStage { + + @Schema(description = "评分阶段名称", example = "直属上级评分") + private String name; + + @Schema(description = "评分人") + private HandlerStage rater; + + @Schema(description = "评分权重", example = "60") + private BigDecimal weight; + + @Schema(description = "评分方式", example = "1") + private Integer scoringType; + + @Schema(description = "评分内容可见范围", example = "1") + private Integer visibleContent; + + @Schema(description = "评语是否必填", example = "true") + private Boolean requiredSetting; + + @Schema(description = "是否允许驳回", example = "true") + private Boolean rejectAuthority; + } + + @Schema(description = "管理后台 - HRM 绩效考核配置快照 Response VO") + @Data + public static class AssessmentConfig { + + @Schema(description = "模板名称", example = "季度绩效模板") + private String name; + + @Schema(description = "计分方式", example = "1") + private Integer scoreCalculation; + + @Schema(description = "分数上限类型", example = "1") + private Integer upperLimitType; + + @Schema(description = "分数上限", example = "100") + private BigDecimal upperLimitScore; + + @Schema(description = "考核维度配置") + private List dimensions; + + @Schema(description = "管理后台 - HRM 绩效考核维度 Response VO") + @Data + public static class Dimension { + + @Schema(description = "维度名称", example = "工作业绩") + private String name; + + @Schema(description = "指标类型", example = "1") + private Integer quotaType; + + @Schema(description = "维度权重", example = "60") + private BigDecimal weight; + + @Schema(description = "备注", example = "核心业绩指标") + private String remark; + + @Schema(description = "是否允许员工编辑", example = "true") + private Boolean allowEdit; + + @Schema(description = "考核指标列表") + private List quotas; + } + + @Schema(description = "管理后台 - HRM 绩效考核指标 Response VO") + @Data + public static class Quota { + + @Schema(description = "指标名称", example = "销售目标达成率") + private String name; + + @Schema(description = "指标说明", example = "按季度销售目标计算") + private String illustrate; + + @Schema(description = "评分标准", example = "完成率达到 100% 得满分") + private String standard; + + @Schema(description = "指标权重", example = "50") + private BigDecimal weight; + + @Schema(description = "评分类型", example = "1") + private Integer scoreType; + } + } + + @Schema(description = "管理后台 - HRM 绩效结果配置快照 Response VO") + @Data + public static class ResultConfig { + + @Schema(description = "模板名称", example = "绩效结果模板") + private String name; + + @Schema(description = "结果等级配置") + private List levels; + + @Schema(description = "管理后台 - HRM 绩效结果等级 Response VO") + @Data + public static class Level { + + @Schema(description = "等级名称", example = "优秀") + private String name; + + @Schema(description = "最低分数", example = "90") + private BigDecimal minScore; + + @Schema(description = "最高分数", example = "100") + private BigDecimal maxScore; + + @Schema(description = "绩效系数", example = "1.2") + private BigDecimal coefficient; + } + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/plan/HrmPerformancePlanSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/plan/HrmPerformancePlanSaveReqVO.java new file mode 100644 index 0000000..a3a0b5a --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/plan/HrmPerformancePlanSaveReqVO.java @@ -0,0 +1,665 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.plan; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessmenttemplate.HrmPerformanceAssessmentTemplateSaveReqVO.Dimension; +import cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessmenttemplate.HrmPerformanceAssessmentTemplateSaveReqVO.Quota; +import cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.resulttemplate.HrmPerformanceResultTemplateSaveReqVO.Level; +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeStatusEnum; +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeTypeEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.assessment.HrmPerformanceAppealTimeoutActionEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformanceCycleTypeEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformancePlanScopeTypeEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformanceQuarterEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformanceQuotaSettingTypeEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformanceRaterTypeEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformanceReviewScoringTypeEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformanceReviewVisibleContentEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.config.HrmPerformanceScoreCalculationEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.config.HrmPerformanceUpperLimitTypeEnum; +import com.fasterxml.jackson.annotation.JsonIgnore; +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.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.Size; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.time.Year; +import java.time.YearMonth; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.util.number.MoneyUtils.PERCENT_100; + +@Schema(description = "管理后台 - HRM 绩效计划保存 Request VO") +@Data +public class HrmPerformancePlanSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "绩效计划名称", requiredMode = Schema.RequiredMode.REQUIRED, + example = "2026 年第三季度绩效计划") + @NotBlank(message = "计划名称不能为空") + @Size(max = 50, message = "计划名称不能超过 50 个字符") + private String name; + + @Schema(description = "考核周期类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "考核周期类型不能为空") + @InEnum(value = HrmPerformanceCycleTypeEnum.class, message = "考核周期类型必须是 {value}") + private Integer cycleType; + + @Schema(description = "考核周期", example = "2026-08") + @Size(max = 255, message = "考核周期不能超过 255 个字符") + private String cycle; + + @Schema(description = "季度", example = "3") + @InEnum(value = HrmPerformanceQuarterEnum.class, message = "季度必须是 {value}") + private Integer quarter; + + @Schema(description = "开始日期") + private LocalDateTime startTime; + + @Schema(description = "结束日期") + private LocalDateTime endTime; + + @Schema(description = "考核说明", example = "2026 年第三季度绩效考核") + @Size(max = 200, message = "考核说明不能超过 200 个字符") + private String description; + + @Schema(description = "考评范围列表", requiredMode = Schema.RequiredMode.REQUIRED) + @Valid + @NotEmpty(message = "考评范围不能为空") + @Size(max = 3, message = "考评范围不能超过 3 个") + private List<@NotNull(message = "考评范围不能为空") Scope> scopes; + + @Schema(description = "考核模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "考核模板不能为空") + private Long assessmentTemplateId; + + @Schema(description = "考核配置快照", requiredMode = Schema.RequiredMode.REQUIRED) + @Valid + @NotNull(message = "考核配置不能为空") + private AssessmentConfig assessmentConfig; + + @Schema(description = "指标设置类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "指标设置类型不能为空") + @InEnum(value = HrmPerformanceQuotaSettingTypeEnum.class, message = "指标设置类型必须是 {value}") + private Integer quotaSettingType; + + @Schema(description = "是否需要目标确认", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是否需要目标确认不能为空") + private Boolean targetConfirmation; + + @Schema(description = "目标确认节点") + @Valid + private HandlerStage targetConfirmationStage; + + @Schema(description = "评分阶段列表", requiredMode = Schema.RequiredMode.REQUIRED) + @Valid + @NotEmpty(message = "评分阶段不能为空") + private List<@NotNull(message = "评分阶段不能为空") ReviewStage> reviewStages; + + @Schema(description = "是否需要结果审核", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是否需要结果审核不能为空") + private Boolean resultAudit; + + @Schema(description = "结果审核节点列表") + @Valid + private List<@NotNull(message = "结果审核节点不能为空") HandlerStage> resultAuditStages; + + @Schema(description = "是否需要结果确认", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是否需要结果确认不能为空") + private Boolean resultConfirmation; + + @Schema(description = "申诉处理节点列表") + @Valid + private List<@NotNull(message = "申诉处理节点不能为空") HandlerStage> appealStages; + + @Schema(description = "申诉超期天数", requiredMode = Schema.RequiredMode.REQUIRED, example = "3") + @Min(value = 1, message = "申诉超期天数不能小于 1 天") + @Max(value = 100, message = "申诉超期天数不能超过 100 天") + @NotNull(message = "申诉超期天数不能为空") + private Integer appealTimeoutDays; + + @Schema(description = "申诉超期处理方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @InEnum(HrmPerformanceAppealTimeoutActionEnum.class) + @NotNull(message = "申诉超期处理方式不能为空") + private Integer appealTimeoutAction; + + @Schema(description = "结果模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "结果模板不能为空") + private Long resultTemplateId; + + @Schema(description = "结果配置快照", requiredMode = Schema.RequiredMode.REQUIRED) + @Valid + @NotNull(message = "结果配置不能为空") + private ResultConfig resultConfig; + + @Schema(description = "是否同步薪资", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + @NotNull(message = "是否同步薪资不能为空") + private Boolean syncToSalary; + + @Schema(description = "计薪月份", example = "2026-09") + @Size(max = 20, message = "计薪月份不能超过 20 个字符") + private String paidForMonth; + + @Schema(description = "管理后台 - HRM 绩效计划考评范围") + @Data + public static class Scope { + + @Schema(description = "范围类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "考评范围类型不能为空") + @InEnum(value = HrmPerformancePlanScopeTypeEnum.class, message = "考评范围类型必须是 {value}") + private Integer type; + + @Schema(description = "员工编号列表", example = "[1024, 1025]") + private List<@NotNull(message = "员工编号不能为空") Long> employeeIds; + + @Schema(description = "部门编号列表", example = "[100, 101]") + private List<@NotNull(message = "部门编号不能为空") Long> deptIds; + + @Schema(description = "聘用形式", example = "1") + @InEnum(value = HrmEmployeeTypeEnum.class, message = "聘用形式必须是 {value}") + private Integer employeeType; + + @Schema(description = "员工状态列表", example = "[1, 2]") + @InEnum(value = HrmEmployeeStatusEnum.class, message = "员工状态必须是 {value}") + private List employeeStatuses; + } + + @Schema(description = "管理后台 - HRM 绩效计划处理节点") + @Data + public static class HandlerStage { + + @Schema(description = "处理人类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "处理人类型不能为空") + @InEnum(value = HrmPerformanceRaterTypeEnum.class, message = "处理人类型必须是 {value}") + private Integer type; + + @Schema(description = "上级或部门层级", example = "1") + @Min(value = 1, message = "处理人层级不能小于 1") + @Max(value = 10, message = "处理人层级不能大于 10") + private Integer level; + + @Schema(description = "指定处理员工编号", example = "1024") + private Long employeeId; + } + + @Schema(description = "管理后台 - HRM 绩效计划评分阶段") + @Data + public static class ReviewStage { + + @Schema(description = "评分阶段名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "直属上级评分") + @NotBlank(message = "评分阶段名称不能为空") + @Size(max = 50, message = "评分阶段名称不能超过 50 个字符") + private String name; + + @Schema(description = "评分人", requiredMode = Schema.RequiredMode.REQUIRED) + @Valid + @NotNull(message = "评分人不能为空") + private HandlerStage rater; + + @Schema(description = "评分权重", requiredMode = Schema.RequiredMode.REQUIRED, example = "60") + @NotNull(message = "评分权重不能为空") + @DecimalMin(value = "0.01", message = "评分权重必须大于 0") + @DecimalMax(value = "100", message = "评分权重不能大于 100") + private BigDecimal weight; + + @Schema(description = "评分方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "评分方式不能为空") + @InEnum(value = HrmPerformanceReviewScoringTypeEnum.class, message = "评分方式必须是 {value}") + private Integer scoringType; + + @Schema(description = "评分内容可见范围", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "评分内容可见范围不能为空") + @InEnum(value = HrmPerformanceReviewVisibleContentEnum.class, message = "评分内容可见范围必须是 {value}") + private Integer visibleContent; + + @Schema(description = "评语是否必填", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "评语是否必填不能为空") + private Boolean requiredSetting; + + @Schema(description = "是否允许驳回", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是否允许驳回不能为空") + private Boolean rejectAuthority; + } + + @Schema(description = "管理后台 - HRM 绩效考核配置快照") + @Data + public static class AssessmentConfig { + + @Schema(description = "模板名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "季度绩效模板") + @NotBlank(message = "模板名称不能为空") + @Size(max = 50, message = "模板名称不能超过 50 个字符") + private String name; + + @Schema(description = "计分方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "计分方式不能为空") + @InEnum(value = HrmPerformanceScoreCalculationEnum.class, message = "计分方式必须是 {value}") + private Integer scoreCalculation; + + @Schema(description = "分数上限类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "分数上限类型不能为空") + @InEnum(value = HrmPerformanceUpperLimitTypeEnum.class, message = "分数上限类型必须是 {value}") + private Integer upperLimitType; + + @Schema(description = "分数上限", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + @NotNull(message = "分数上限不能为空") + @DecimalMin(value = "0", message = "分数上限不能小于 0") + @DecimalMax(value = "100", message = "分数上限不能大于 100") + private BigDecimal upperLimitScore; + + @Schema(description = "考核维度配置", requiredMode = Schema.RequiredMode.REQUIRED) + @Valid + @NotEmpty(message = "至少需要一个考核维度") + private List<@NotNull(message = "考核维度不能为空") Dimension> dimensions; + + @AssertTrue(message = "考核维度名称不能重复") + @JsonIgnore + public boolean isDimensionNamesUnique() { + if (CollUtil.isEmpty(dimensions)) { + return true; + } + Set names = new HashSet<>(); + for (Dimension dimension : dimensions) { + if (dimension != null && StrUtil.isNotBlank(dimension.getName()) + && !names.add(dimension.getName().trim())) { + return false; + } + } + return true; + } + + @AssertTrue(message = "考核指标名称不能重复") + @JsonIgnore + public boolean isQuotaNamesUnique() { + if (CollUtil.isEmpty(dimensions)) { + return true; + } + Set names = new HashSet<>(); + for (Dimension dimension : dimensions) { + if (dimension == null || CollUtil.isEmpty(dimension.getQuotas())) { + continue; + } + for (Quota quota : dimension.getQuotas()) { + if (quota != null && StrUtil.isNotBlank(quota.getName()) + && !names.add(quota.getName().trim())) { + return false; + } + } + } + return true; + } + + @AssertTrue(message = "维度权重总和必须等于 100%") + @JsonIgnore + public boolean isDimensionWeightTotalValid() { + if (CollUtil.isEmpty(dimensions)) { + return true; + } + BigDecimal totalWeight = BigDecimal.ZERO; + for (Dimension dimension : dimensions) { + if (dimension == null || dimension.getWeight() == null) { + return true; + } + totalWeight = totalWeight.add(dimension.getWeight()); + } + return totalWeight.compareTo(PERCENT_100) == 0; + } + + @AssertTrue(message = "不可编辑维度的指标权重总和必须等于 100%") + @JsonIgnore + public boolean isFixedQuotaWeightTotalValid() { + if (CollUtil.isEmpty(dimensions)) { + return true; + } + for (Dimension dimension : dimensions) { + if (dimension == null || ObjUtil.notEqual(Boolean.FALSE, dimension.getAllowEdit()) + || CollUtil.isEmpty(dimension.getQuotas())) { + continue; + } + BigDecimal totalWeight = BigDecimal.ZERO; + for (Quota quota : dimension.getQuotas()) { + if (quota == null || quota.getWeight() == null) { + return true; + } + totalWeight = totalWeight.add(quota.getWeight()); + } + if (totalWeight.compareTo(PERCENT_100) != 0) { + return false; + } + } + return true; + } + + @AssertTrue(message = "可编辑维度的指标权重总和不能大于 100%") + @JsonIgnore + public boolean isEditableQuotaWeightTotalValid() { + if (CollUtil.isEmpty(dimensions)) { + return true; + } + for (Dimension dimension : dimensions) { + if (dimension == null || ObjUtil.notEqual(Boolean.TRUE, dimension.getAllowEdit()) + || CollUtil.isEmpty(dimension.getQuotas())) { + continue; + } + BigDecimal totalWeight = BigDecimal.ZERO; + for (Quota quota : dimension.getQuotas()) { + if (quota == null || quota.getWeight() == null) { + return true; + } + totalWeight = totalWeight.add(quota.getWeight()); + } + if (totalWeight.compareTo(PERCENT_100) > 0) { + return false; + } + } + return true; + } + } + + @Schema(description = "管理后台 - HRM 绩效结果配置快照") + @Data + public static class ResultConfig { + + private static final BigDecimal MIN_SCORE = BigDecimal.ZERO; + private static final BigDecimal MAX_SCORE = BigDecimal.valueOf(100); + private static final BigDecimal SCORE_STEP = new BigDecimal("0.01"); + + @Schema(description = "模板名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "绩效结果模板") + @NotBlank(message = "结果模板名称不能为空") + @Size(max = 255, message = "结果模板名称不能超过 255 个字符") + private String name; + + @Schema(description = "结果等级配置", requiredMode = Schema.RequiredMode.REQUIRED) + @Valid + @NotEmpty(message = "至少需要一个结果等级") + private List<@NotNull(message = "结果等级不能为空") Level> levels; + + @AssertTrue(message = "结果等级名称不能重复") + @JsonIgnore + public boolean isLevelNamesUnique() { + if (CollUtil.isEmpty(levels)) { + return true; + } + Set names = new HashSet<>(); + for (Level level : levels) { + if (level != null && StrUtil.isNotBlank(level.getName()) + && !names.add(level.getName().trim())) { + return false; + } + } + return true; + } + + @AssertTrue(message = "结果等级分数区间必须连续覆盖 0 到 100 分") + @JsonIgnore + public boolean isScoreRangeContinuous() { + if (CollUtil.isEmpty(levels)) { + return true; + } + List sortedLevels = new ArrayList<>(levels); + if (sortedLevels.stream().anyMatch(level -> level == null + || level.getMinScore() == null || level.getMaxScore() == null)) { + return true; + } + sortedLevels.sort(Comparator.comparing(Level::getMinScore)); + if (CollUtil.getFirst(sortedLevels).getMinScore().compareTo(MIN_SCORE) != 0) { + return false; + } + for (int i = 1; i < sortedLevels.size(); i++) { + Level previous = sortedLevels.get(i - 1); + Level current = sortedLevels.get(i); + if (current.getMinScore().compareTo(previous.getMaxScore().add(SCORE_STEP)) != 0) { + return false; + } + } + return CollUtil.getLast(sortedLevels).getMaxScore().compareTo(MAX_SCORE) == 0; + } + } + + // Service 只校验模板、员工和部门等数据引用。 + @AssertTrue(message = "考评范围配置不正确") + @JsonIgnore + public boolean isScopeConfigValid() { + if (CollUtil.isEmpty(scopes)) { + return true; + } + for (Scope scope : scopes) { + if (scope == null || scope.getType() == null) { + continue; + } + if (Objects.equals(scope.getType(), HrmPerformancePlanScopeTypeEnum.EMPLOYEE_DEPT.getType())) { + if (CollUtil.isEmpty(scope.getEmployeeIds()) && CollUtil.isEmpty(scope.getDeptIds())) { + return false; + } + } else if (Objects.equals(scope.getType(), HrmPerformancePlanScopeTypeEnum.EMPLOYMENT.getType())) { + if (scope.getEmployeeType() == null || CollUtil.isEmpty(scope.getEmployeeStatuses())) { + return false; + } + } else if (Objects.equals(scope.getType(), + HrmPerformancePlanScopeTypeEnum.EXCLUDED_EMPLOYEE.getType())) { + return false; + } + } + return true; + } + + @AssertTrue(message = "评分人配置不正确") + @JsonIgnore + public boolean isReviewStageConfigValid() { + if (CollUtil.isEmpty(reviewStages)) { + return true; + } + for (ReviewStage reviewStage : reviewStages) { + if (reviewStage == null || reviewStage.getRater() == null) { + continue; + } + if (!isHandlerStageValid(reviewStage.getRater(), true) + || Objects.equals(reviewStage.getRater().getType(), HrmPerformanceRaterTypeEnum.SELF.getType()) + && Boolean.TRUE.equals(reviewStage.getRejectAuthority())) { + return false; + } + } + return true; + } + + @AssertTrue(message = "评分权重总和必须等于 100%") + @JsonIgnore + public boolean isReviewStageWeightTotalValid() { + if (CollUtil.isEmpty(reviewStages)) { + return true; + } + BigDecimal totalWeight = BigDecimal.ZERO; + for (ReviewStage reviewStage : reviewStages) { + if (reviewStage == null || reviewStage.getWeight() == null) { + return true; + } + totalWeight = totalWeight.add(reviewStage.getWeight()); + } + return totalWeight.compareTo(PERCENT_100) == 0; + } + + @AssertTrue(message = "评分人不能重复") + @JsonIgnore + public boolean isReviewStageRaterUnique() { + if (CollUtil.isEmpty(reviewStages)) { + return true; + } + List raters = new ArrayList<>(); + for (ReviewStage reviewStage : reviewStages) { + if (reviewStage != null) { + raters.add(reviewStage.getRater()); + } + } + return isHandlerStageUnique(raters); + } + + @AssertTrue(message = "目标确认配置不正确") + @JsonIgnore + public boolean isTargetConfirmationConfigValid() { + if (quotaSettingType == null || targetConfirmation == null) { + return true; + } + if (Objects.equals(quotaSettingType, HrmPerformanceQuotaSettingTypeEnum.SYSTEM.getType())) { + return !targetConfirmation; + } + return !targetConfirmation || targetConfirmationStage != null + && isHandlerStageValid(targetConfirmationStage, true); + } + + @AssertTrue(message = "结果审核节点配置不正确") + @JsonIgnore + public boolean isResultAuditConfigValid() { + return ObjUtil.notEqual(Boolean.TRUE, resultAudit) || isHandlerStagesValid(resultAuditStages); + } + + @AssertTrue(message = "申诉节点配置不正确") + @JsonIgnore + public boolean isResultConfirmationConfigValid() { + return ObjUtil.notEqual(Boolean.TRUE, resultConfirmation) || isHandlerStagesValid(appealStages); + } + + @AssertTrue(message = "计薪月份配置不正确") + @JsonIgnore + public boolean isSalaryConfigValid() { + if (!Boolean.TRUE.equals(syncToSalary)) { + return true; + } + if (StrUtil.isBlank(paidForMonth)) { + return false; + } + try { + YearMonth.parse(paidForMonth); + return true; + } catch (DateTimeParseException ignored) { + return false; + } + } + + @AssertTrue(message = "考核周期、季度和起止时间配置不正确") + @JsonIgnore + public boolean isCycleConfigValid() { + if (cycleType == null) { + return true; + } + if (startTime == null || endTime == null || endTime.isBefore(startTime)) { + return false; + } + if (Objects.equals(cycleType, HrmPerformanceCycleTypeEnum.OTHER.getType())) { + return StrUtil.isNotBlank(cycle) && quarter == null; + } + try { + if (Objects.equals(cycleType, HrmPerformanceCycleTypeEnum.MONTH.getType())) { + YearMonth month = YearMonth.parse(cycle); + return quarter == null && startTime.toLocalDate().equals(month.atDay(1)) + && endTime.toLocalDate().equals(month.atEndOfMonth()); + } + Year year = Year.parse(cycle); + int beginMonth = 1; + int endMonth = 12; + if (Objects.equals(cycleType, HrmPerformanceCycleTypeEnum.QUARTER.getType())) { + if (HrmPerformanceQuarterEnum.valueOf(quarter) == null) { + return false; + } + beginMonth = (quarter - 1) * 3 + 1; + endMonth = beginMonth + 2; + } else if (Objects.equals(cycleType, + HrmPerformanceCycleTypeEnum.FIRST_HALF_YEAR.getType())) { + endMonth = 6; + } else if (Objects.equals(cycleType, + HrmPerformanceCycleTypeEnum.SECOND_HALF_YEAR.getType())) { + beginMonth = 7; + } + if (!Objects.equals(cycleType, HrmPerformanceCycleTypeEnum.QUARTER.getType()) + && quarter != null) { + return false; + } + YearMonth beginYearMonth = year.atMonth(beginMonth); + YearMonth endYearMonth = year.atMonth(endMonth); + return startTime.toLocalDate().equals(beginYearMonth.atDay(1)) + && endTime.toLocalDate().equals(endYearMonth.atEndOfMonth()); + } catch (DateTimeParseException | NullPointerException ignored) { + return false; + } + } + + private boolean isHandlerStagesValid(List stages) { + if (CollUtil.isEmpty(stages) || stages.size() > 3) { + return false; + } + for (HandlerStage stage : stages) { + if (stage != null && !isHandlerStageValid(stage, false)) { + return false; + } + } + return isHandlerStageUnique(stages); + } + + private boolean isHandlerStageValid(HandlerStage stage, boolean allowSelf) { + if (stage == null || stage.getType() == null) { + return true; + } + if (Objects.equals(stage.getType(), HrmPerformanceRaterTypeEnum.SELF.getType())) { + return allowSelf; + } + if (ObjectUtils.equalsAny(stage.getType(), HrmPerformanceRaterTypeEnum.SUPERIOR.getType(), + HrmPerformanceRaterTypeEnum.DEPT_LEADER.getType())) { + return stage.getLevel() != null && stage.getLevel() >= 1 && stage.getLevel() <= 10; + } + if (Objects.equals(stage.getType(), HrmPerformanceRaterTypeEnum.SPECIFIED.getType())) { + return stage.getEmployeeId() != null; + } + return true; + } + + private boolean isHandlerStageUnique(List stages) { + if (CollUtil.isEmpty(stages)) { + return true; + } + Set handlerKeys = new HashSet<>(); + for (HandlerStage stage : stages) { + String handlerKey = buildHandlerKey(stage); + if (handlerKey != null && !handlerKeys.add(handlerKey)) { + return false; + } + } + return true; + } + + private String buildHandlerKey(HandlerStage stage) { + if (stage == null || stage.getType() == null) { + return null; + } + if (Objects.equals(stage.getType(), HrmPerformanceRaterTypeEnum.SELF.getType())) { + return String.valueOf(stage.getType()); + } + if (Objects.equals(stage.getType(), HrmPerformanceRaterTypeEnum.SPECIFIED.getType())) { + return stage.getEmployeeId() == null ? null : stage.getType() + ":" + stage.getEmployeeId(); + } + if (ObjectUtils.equalsAny(stage.getType(), HrmPerformanceRaterTypeEnum.SUPERIOR.getType(), + HrmPerformanceRaterTypeEnum.DEPT_LEADER.getType())) { + return stage.getLevel() == null ? null : stage.getType() + ":" + stage.getLevel(); + } + return null; + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/resulttemplate/HrmPerformanceResultTemplatePageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/resulttemplate/HrmPerformanceResultTemplatePageReqVO.java new file mode 100644 index 0000000..3b3b4e9 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/resulttemplate/HrmPerformanceResultTemplatePageReqVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.resulttemplate; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Schema(description = "管理后台 - HRM 绩效结果模板分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class HrmPerformanceResultTemplatePageReqVO extends PageParam { + + @Schema(description = "结果模板名称,模糊匹配", example = "季度") + private String name; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/resulttemplate/HrmPerformanceResultTemplateRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/resulttemplate/HrmPerformanceResultTemplateRespVO.java new file mode 100644 index 0000000..109c9e9 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/resulttemplate/HrmPerformanceResultTemplateRespVO.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.resulttemplate; + +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 +public class HrmPerformanceResultTemplateRespVO { + + @Schema(description = "结果模板编号", example = "1024") + private Long id; + + @Schema(description = "结果模板名称", example = "季度绩效结果") + private String name; + + @Schema(description = "状态", example = "0") + private Integer status; + + @Schema(description = "结果等级列表") + private List levels; + + @Schema(description = "创建人") + private String creator; + + @Schema(description = "创建人名称") + private String creatorName; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + + @Schema(description = "更新时间") + private LocalDateTime updateTime; + + @Schema(description = "管理后台 - HRM 绩效结果模板等级") + @Data + public static class Level { + + @Schema(description = "等级名称", example = "A") + private String name; + + @Schema(description = "最低分数", example = "90") + private BigDecimal minScore; + + @Schema(description = "最高分数", example = "100") + private BigDecimal maxScore; + + @Schema(description = "绩效系数", example = "1.2") + private BigDecimal coefficient; + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/resulttemplate/HrmPerformanceResultTemplateSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/resulttemplate/HrmPerformanceResultTemplateSaveReqVO.java new file mode 100644 index 0000000..968d823 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/performance/vo/resulttemplate/HrmPerformanceResultTemplateSaveReqVO.java @@ -0,0 +1,129 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.resulttemplate; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.annotation.JsonIgnore; +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.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@Schema(description = "管理后台 - HRM 绩效结果模板保存 Request VO") +@Data +public class HrmPerformanceResultTemplateSaveReqVO { + + private static final BigDecimal MIN_SCORE = BigDecimal.ZERO; + private static final BigDecimal MAX_SCORE = BigDecimal.valueOf(100); + private static final BigDecimal SCORE_STEP = new BigDecimal("0.01"); + + @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 = "结果等级列表", requiredMode = Schema.RequiredMode.REQUIRED) + @Valid + @NotEmpty(message = "至少需要一个结果等级") + private List<@NotNull(message = "结果等级不能为空") Level> levels; + + /** + * 校验结果等级名称唯一 + */ + @AssertTrue(message = "结果等级名称不能重复") + @JsonIgnore + public boolean isLevelNamesUnique() { + if (CollUtil.isEmpty(levels)) { + return true; + } + Set names = new HashSet<>(); + for (Level level : levels) { + if (level != null && StrUtil.isNotBlank(level.getName()) + && !names.add(level.getName().trim())) { + return false; + } + } + return true; + } + + /** + * 校验结果等级分数区间连续覆盖 0 到 100 分 + */ + @AssertTrue(message = "结果等级分数区间必须连续覆盖 0 到 100 分") + @JsonIgnore + public boolean isScoreRangeContinuous() { + if (CollUtil.isEmpty(levels)) { + return true; + } + List sortedLevels = new ArrayList<>(levels); + if (sortedLevels.stream().anyMatch(level -> level == null + || level.getMinScore() == null || level.getMaxScore() == null)) { + return true; + } + sortedLevels.sort(Comparator.comparing(Level::getMinScore)); + if (CollUtil.getFirst(sortedLevels).getMinScore().compareTo(MIN_SCORE) != 0) { + return false; + } + for (int i = 1; i < sortedLevels.size(); i++) { + Level previous = sortedLevels.get(i - 1); + Level current = sortedLevels.get(i); + if (current.getMinScore().compareTo(previous.getMaxScore().add(SCORE_STEP)) != 0) { + return false; + } + } + return CollUtil.getLast(sortedLevels).getMaxScore().compareTo(MAX_SCORE) == 0; + } + + @Schema(description = "管理后台 - HRM 绩效结果模板等级") + @Data + public static class Level { + + @Schema(description = "等级名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "A") + @NotBlank(message = "等级名称不能为空") + @Size(max = 255, message = "等级名称不能超过 255 个字符") + private String name; + + @Schema(description = "最低分数", requiredMode = Schema.RequiredMode.REQUIRED, example = "90") + @NotNull(message = "最低分数不能为空") + @DecimalMin(value = "0", message = "最低分数不能小于 0") + @DecimalMax(value = "100", message = "最低分数不能大于 100") + @Digits(integer = 3, fraction = 2, message = "最低分数最多保留两位小数") + private BigDecimal minScore; + + @Schema(description = "最高分数", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + @NotNull(message = "最高分数不能为空") + @DecimalMin(value = "0", message = "最高分数不能小于 0") + @DecimalMax(value = "100", message = "最高分数不能大于 100") + @Digits(integer = 3, fraction = 2, message = "最高分数最多保留两位小数") + private BigDecimal maxScore; + + @Schema(description = "绩效系数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.2") + @NotNull(message = "绩效系数不能为空") + @DecimalMin(value = "0", message = "绩效系数不能小于 0") + @Digits(integer = 8, fraction = 2, message = "绩效系数最多保留两位小数") + private BigDecimal coefficient; + + @AssertTrue(message = "最低分数不能大于最高分数") + @JsonIgnore + public boolean isScoreRangeValid() { + return minScore == null || maxScore == null || minScore.compareTo(maxScore) <= 0; + } + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/attendance/HrmPortalAttendanceClockController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/attendance/HrmPortalAttendanceClockController.java new file mode 100644 index 0000000..fd8e47a --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/attendance/HrmPortalAttendanceClockController.java @@ -0,0 +1,128 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.attendance; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.hrm.controller.admin.portal.attendance.vo.clock.HrmPortalAttendanceClockCreateReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.portal.attendance.vo.clock.HrmPortalAttendanceClockDetailRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.portal.attendance.vo.clock.HrmPortalAttendanceRecordRespVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.config.HrmAttendanceGroupDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.record.HrmAttendanceClockDO; +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.employee.info.HrmEmployeeService; +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.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.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 java.time.LocalDateTime; +import java.time.YearMonth; +import java.time.format.DateTimeFormatter; +import java.util.Collections; +import java.util.List; + +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/portal/attendance/clock") +@Validated +public class HrmPortalAttendanceClockController { + + private static final DateTimeFormatter SHIFT_TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm"); + + @Resource + private HrmEmployeeService employeeService; + @Resource + private HrmAttendanceClockService attendanceClockService; + + @GetMapping("/get-detail") + @Operation(summary = "获得我的打卡详情") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult getAttendanceClockDetail() { + HrmPortalAttendanceClockDetailRespVO detail = + attendanceClockService.getMyAttendanceClockDetail(getLoginUserId()); + return success(buildAttendanceClockDetailRespVO(detail)); + } + + @PostMapping("/create") + @Operation(summary = "手机端打卡") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult createAttendanceClock( + @Valid @RequestBody HrmPortalAttendanceClockCreateReqVO createReqVO) { + return success(attendanceClockService.createMyAttendanceClock(getLoginUserId(), createReqVO)); + } + + @GetMapping("/list") + @Operation(summary = "获得我的考勤记录") + @Parameters({ + @Parameter(name = "year", description = "年份", example = "2026"), + @Parameter(name = "month", description = "月份", example = "8") + }) + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult> getAttendanceRecordList( + @RequestParam(value = "year", required = false) Integer year, + @RequestParam(value = "month", required = false) Integer month) { + HrmEmployeeDO employee = employeeService.validateEmployeeBySelf(getLoginUserId()); + // 1. 计算所选月份的闭区间 + YearMonth queryMonth = year == null || month == null + ? YearMonth.now() : YearMonth.of(year, month); + LocalDateTime[] monthTimes = LocalDateTimeUtils.getMonthDateTimeRange( + queryMonth.getYear(), queryMonth.getMonthValue()); + // 2. 查询当前员工在所选月份的打卡记录 + List list = attendanceClockService.getAttendanceClockListByEmployeeIdAndClockTime( + employee.getId(), monthTimes); + return success(BeanUtils.toBean(list, HrmPortalAttendanceRecordRespVO.class)); + } + + // ==================== 拼接 VO ==================== + + private HrmPortalAttendanceClockDetailRespVO buildAttendanceClockDetailRespVO( + HrmPortalAttendanceClockDetailRespVO detail) { + HrmAttendanceGroupDO group = detail.getGroup(); + detail.setGroupName(group.getName()) + .setOpenPointCard(Boolean.TRUE.equals(group.getOpenPointCard())) + .setOpenWifiCard(Boolean.TRUE.equals(group.getOpenWifiCard())) + .setShiftTitle(buildShiftTitle(detail.getShift())) + .setPoints(Boolean.TRUE.equals(group.getOpenPointCard()) + ? BeanUtils.toBean(CollUtil.emptyIfNull(group.getPoints()), + HrmPortalAttendanceClockDetailRespVO.Point.class) : Collections.emptyList()) + .setWifis(Boolean.TRUE.equals(group.getOpenWifiCard()) + ? BeanUtils.toBean(CollUtil.emptyIfNull(group.getWifis()), + HrmPortalAttendanceClockDetailRespVO.Wifi.class) : Collections.emptyList()); + return detail; + } + + /** + * 构建班次展示标题 + * + * @param shift 班次配置 + * @return 班次标题 + */ + private String buildShiftTitle(HrmAttendanceGroupDO.Shift shift) { + if (shift == null || shift.getStartTime() == null || shift.getEndTime() == null) { + return "休息"; + } + String title = shift.getStartTime().format(SHIFT_TIME_FORMATTER) + + "-" + shift.getEndTime().format(SHIFT_TIME_FORMATTER); + if (shift.getRestStartTime() != null && shift.getRestEndTime() != null) { + title = title + " 休息:" + shift.getRestStartTime().format(SHIFT_TIME_FORMATTER) + + "-" + shift.getRestEndTime().format(SHIFT_TIME_FORMATTER); + } + return title; + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/attendance/HrmPortalAttendanceLeaveController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/attendance/HrmPortalAttendanceLeaveController.java new file mode 100644 index 0000000..f122401 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/attendance/HrmPortalAttendanceLeaveController.java @@ -0,0 +1,62 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.attendance; + +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.attendance.vo.leave.HrmAttendanceLeaveCancelReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.leave.HrmAttendanceLeaveCreateReqVO; +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.service.attendance.record.HrmAttendanceLeaveService; +import io.swagger.v3.oas.annotations.Operation; +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.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.RestController; + +import java.util.List; + +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/portal/attendance/leave") +@Validated +public class HrmPortalAttendanceLeaveController { + + @Resource + private HrmAttendanceLeaveService attendanceLeaveService; + + @GetMapping("/list") + @Operation(summary = "获得我的请假申请列表") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult> getAttendanceLeaveList() { + List list = attendanceLeaveService.getMyAttendanceLeaveList(getLoginUserId()); + return success(BeanUtils.toBean(list, HrmAttendanceLeaveRespVO.class)); + } + + @PostMapping("/create") + @Operation(summary = "创建我的请假申请") + @PreAuthorize("@ss.hasPermission('hrm:portal:attendance:leave')") + public CommonResult createAttendanceLeave( + @Valid @RequestBody HrmAttendanceLeaveCreateReqVO reqVO) { + return success(attendanceLeaveService.createLeave(getLoginUserId(), reqVO)); + } + + @PutMapping("/cancel") + @Operation(summary = "取消我的请假申请") + @PreAuthorize("@ss.hasPermission('hrm:portal:attendance:leave')") + public CommonResult cancelAttendanceLeave( + @Valid @RequestBody HrmAttendanceLeaveCancelReqVO reqVO) { + attendanceLeaveService.cancelLeave(getLoginUserId(), reqVO); + return success(true); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/attendance/HrmPortalAttendanceStatisticsController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/attendance/HrmPortalAttendanceStatisticsController.java new file mode 100644 index 0000000..51d4958 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/attendance/HrmPortalAttendanceStatisticsController.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.attendance; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics.HrmAttendanceDailyDetailRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics.HrmAttendanceMonthDetailRespVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.module.hrm.service.attendance.statistics.HrmAttendanceStatisticsService; +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.Parameters; +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.time.YearMonth; + +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/portal/attendance/statistics") +@Validated +public class HrmPortalAttendanceStatisticsController { + + @Resource + private HrmEmployeeService employeeService; + @Resource + private HrmAttendanceStatisticsService attendanceStatisticsService; + + @GetMapping("/month-detail") + @Operation(summary = "获得我的月度考勤详情") + @Parameters({ + @Parameter(name = "year", description = "年份", example = "2026"), + @Parameter(name = "month", description = "月份", example = "8") + }) + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult getAttendanceMonthDetail( + @RequestParam(value = "year", required = false) Integer year, + @RequestParam(value = "month", required = false) Integer month) { + HrmEmployeeDO employee = employeeService.validateEmployeeBySelf(getLoginUserId()); + YearMonth queryMonth = year == null || month == null + ? YearMonth.now() : YearMonth.of(year, month); + return success(attendanceStatisticsService.getAttendanceMonthDetail( + employee.getId(), queryMonth.getYear(), queryMonth.getMonthValue())); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出我的月度考勤日报") + @Parameters({ + @Parameter(name = "year", description = "年份", required = true, example = "2026"), + @Parameter(name = "month", description = "月份", required = true, example = "8") + }) + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public void exportAttendanceMonthDetail( + @RequestParam("year") Integer year, @RequestParam("month") Integer month, + HttpServletResponse response) throws Exception { + HrmEmployeeDO employee = employeeService.validateEmployeeBySelf(getLoginUserId()); + HrmAttendanceMonthDetailRespVO detail = attendanceStatisticsService.getAttendanceMonthDetail( + employee.getId(), year, month); + ExcelUtils.write(response, year + "年" + month + "月个人考勤日报.xls", "考勤日报", + HrmAttendanceDailyDetailRespVO.class, detail.getDailyDetails()); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/attendance/vo/clock/HrmPortalAttendanceClockCreateReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/attendance/vo/clock/HrmPortalAttendanceClockCreateReqVO.java new file mode 100644 index 0000000..8a7b6d2 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/attendance/vo/clock/HrmPortalAttendanceClockCreateReqVO.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.attendance.vo.clock; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.DecimalMax; +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - HRM 员工端手机打卡 Request VO") +@Data +public class HrmPortalAttendanceClockCreateReqVO { + + @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; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/attendance/vo/clock/HrmPortalAttendanceClockDetailRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/attendance/vo/clock/HrmPortalAttendanceClockDetailRespVO.java new file mode 100644 index 0000000..6dca4b0 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/attendance/vo/clock/HrmPortalAttendanceClockDetailRespVO.java @@ -0,0 +1,147 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.attendance.vo.clock; + +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.TimeRange; +import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.config.HrmAttendanceGroupDO; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - HRM 员工端打卡详情 Response VO") +@Data +public class HrmPortalAttendanceClockDetailRespVO { + + @Schema(description = "考勤组名称") + private String groupName; + + @Schema(description = "是否启用定位打卡") + private Boolean openPointCard; + + @Schema(description = "是否启用 WiFi 打卡") + private Boolean openWifiCard; + + @Schema(description = "班次所属考勤日") + private LocalDate attendanceDate; + + @Schema(description = "班次标题,例如 09:00-18:00") + private String shiftTitle; + + @Schema(description = "是否休息日") + private Boolean restDay; + + @Schema(description = "上班应打卡时间") + private LocalDateTime onDutyAttendanceTime; + + @Schema(description = "下班应打卡时间") + private LocalDateTime offDutyAttendanceTime; + + @Schema(description = "打卡地点列表") + private List points; + + @Schema(description = "WiFi 列表") + private List wifis; + + @Schema(description = "下一次可打卡动作") + private NextClock nextClock; + + @Schema(description = "当日打卡时间线") + private List timeline; + + // ==================== 内部计算字段 ==================== + + @JsonIgnore + @Schema(hidden = true) + private HrmAttendanceGroupDO group; + + @JsonIgnore + @Schema(hidden = true) + private HrmAttendanceGroupDO.Shift shift; + + @JsonIgnore + @Schema(hidden = true) + private TimeRange clockInRange; + + @JsonIgnore + @Schema(hidden = true) + private TimeRange clockOutRange; + + @Schema(description = "打卡地点") + @Data + public static class Point { + + @Schema(description = "地点名称") + private String name; + + @Schema(description = "定位地址") + private String address; + + @Schema(description = "纬度") + private BigDecimal latitude; + + @Schema(description = "经度") + private BigDecimal longitude; + + @Schema(description = "有效半径(米)") + private Integer radius; + + } + + @Schema(description = "WiFi 配置") + @Data + public static class Wifi { + + @Schema(description = "WiFi 名称") + private String ssid; + + @Schema(description = "MAC 地址") + private String mac; + + } + + @Schema(description = "下一次打卡动作") + @Data + public static class NextClock { + + @Schema(description = "打卡类型") + private Integer type; + + @Schema(description = "打卡阶段") + private Integer stage; + + @Schema(description = "应打卡时间") + private LocalDateTime attendanceTime; + + @Schema(description = "按钮状态:0 未到时间 1 正常 2 更新 3 迟到 4 早退") + private Integer buttonStatus; + + } + + @Schema(description = "当日打卡时间线条目") + @Data + public static class TimelineItem { + + @Schema(description = "打卡类型") + private Integer type; + + @Schema(description = "应打卡时间") + private LocalDateTime attendanceTime; + + @Schema(description = "实际打卡时间;缺卡时为空") + private LocalDateTime clockTime; + + @Schema(description = "打卡状态") + private Integer status; + + @Schema(description = "是否缺卡") + private Boolean missCard; + + @Schema(description = "打卡地址") + private String address; + + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/attendance/vo/clock/HrmPortalAttendanceRecordRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/attendance/vo/clock/HrmPortalAttendanceRecordRespVO.java new file mode 100644 index 0000000..e63f4bc --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/attendance/vo/clock/HrmPortalAttendanceRecordRespVO.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.attendance.vo.clock; + +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 +public class HrmPortalAttendanceRecordRespVO { + + @Schema(description = "编号") + private Long id; + + @Schema(description = "员工编号") + private Long employeeId; + + @Schema(description = "打卡时间") + private LocalDateTime clockTime; + + @Schema(description = "员工端考勤记录类型") + private Integer type; + + @Schema(description = "应打卡时间") + private LocalDateTime attendanceTime; + + @Schema(description = "来源类型") + private Integer sourceType; + + @Schema(description = "员工端考勤记录状态") + private Integer status; + + @Schema(description = "阶段") + private Integer stage; + + @Schema(description = "地址") + private String address; + + @Schema(description = "经度") + private BigDecimal longitude; + + @Schema(description = "纬度") + private BigDecimal latitude; + + @Schema(description = "WiFi 名称") + private String ssid; + + @Schema(description = "WiFi MAC 地址") + private String mac; + + @Schema(description = "备注") + private String remark; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/HrmPortalEmployeeCertificateController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/HrmPortalEmployeeCertificateController.java new file mode 100644 index 0000000..9f0702c --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/HrmPortalEmployeeCertificateController.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.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.dal.dataobject.employee.experience.HrmEmployeeCertificateDO; +import cn.iocoder.yudao.module.hrm.service.employee.experience.HrmEmployeeCertificateService; +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService; +import io.swagger.v3.oas.annotations.Operation; +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.RestController; + +import java.util.List; + +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/portal/employee/certificate") +@Validated +public class HrmPortalEmployeeCertificateController { + + @Resource + private HrmEmployeeCertificateService certificateService; + @Resource + private HrmEmployeeService employeeService; + + @GetMapping("/list") + @Operation(summary = "获得我的证书列表") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult> getCertificateList() { + Long employeeId = employeeService.validateEmployeeBySelf(getLoginUserId()).getId(); + List certificates = + certificateService.getCertificateListByEmployeeId(employeeId); + return success(BeanUtils.toBean(certificates, HrmEmployeeCertificateRespVO.class)); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/HrmPortalEmployeeContactController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/HrmPortalEmployeeContactController.java new file mode 100644 index 0000000..ec941b3 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/HrmPortalEmployeeContactController.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.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.dal.dataobject.employee.info.HrmEmployeeContactDO; +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeContactService; +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService; +import io.swagger.v3.oas.annotations.Operation; +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.RestController; + +import java.util.List; + +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/portal/employee/contact") +@Validated +public class HrmPortalEmployeeContactController { + + @Resource + private HrmEmployeeContactService contactService; + @Resource + private HrmEmployeeService employeeService; + + @GetMapping("/list") + @Operation(summary = "获得我的联系人列表") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult> getContactList() { + Long employeeId = employeeService.validateEmployeeBySelf(getLoginUserId()).getId(); + List contacts = contactService.getContactListByEmployeeId(employeeId); + return success(BeanUtils.toBean(contacts, HrmEmployeeContactRespVO.class)); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/HrmPortalEmployeeController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/HrmPortalEmployeeController.java new file mode 100644 index 0000000..d4e770f --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/HrmPortalEmployeeController.java @@ -0,0 +1,142 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.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.portal.employee.vo.employee.HrmPortalEmployeeRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.portal.employee.vo.employee.HrmPortalEmployeeUpdateReqVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.module.hrm.enums.employee.config.HrmEmployeeArchiveFieldEnum; +import cn.iocoder.yudao.module.hrm.service.employee.config.HrmEmployeeFieldConfigService; +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.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import io.swagger.v3.oas.annotations.Operation; +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.RestController; + +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.getYearsBetween; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "管理后台 - HRM 员工端档案") +@RestController +@RequestMapping("/hrm/portal/employee") +@Validated +public class HrmPortalEmployeeController { + + @Resource + private HrmEmployeeService employeeService; + @Resource + private HrmEmployeeFieldConfigService employeeFieldConfigService; + @Resource + private DeptApi deptApi; + @Resource + private AdminUserApi adminUserApi; + + @GetMapping("/get-bind-status") + @Operation(summary = "获得当前账号的员工绑定状态") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult getEmployeeBindStatus() { + return success(employeeService.getEmployeeByUserId(getLoginUserId()) != null); + } + + @GetMapping("/get") + @Operation(summary = "获得我的员工档案") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult getEmployee() { + HrmEmployeeDO employee = employeeService.validateEmployeeBySelf(getLoginUserId()); + // 拼接 VO + HrmPortalEmployeeRespVO respVO = BeanUtils.toBean(employee, HrmPortalEmployeeRespVO.class); + LocalDate today = LocalDate.now(); + if (employee.getBirthday() != null) { + respVO.setAge(getYearsBetween(employee.getBirthday().toLocalDate(), today)); + } + if (employee.getCompanyAgeStartTime() != null) { + respVO.setCompanyAge(getYearsBetween(employee.getCompanyAgeStartTime().toLocalDate(), today)); + } + respVO.setEntryDay(employee.getEntryTime() == null ? 0L + : Math.max(0L, ChronoUnit.DAYS.between(employee.getEntryTime().toLocalDate(), today) + 1)); + if (employee.getUserId() != null) { + AdminUserRespDTO user = adminUserApi.getUser(employee.getUserId()); + respVO.setAvatar(user == null ? null : user.getAvatar()); + } + if (employee.getDeptId() != null) { + DeptRespDTO dept = deptApi.getDept(employee.getDeptId()); + respVO.setDeptName(dept == null ? null : dept.getName()); + } + if (employee.getLeaderEmployeeId() != null) { + HrmEmployeeDO leader = employeeService.getEmployee(employee.getLeaderEmployeeId()); + respVO.setLeaderEmployeeName(leader == null ? null : leader.getName()); + } + hideInvisibleArchiveFields(respVO, employeeFieldConfigService.getVisibleArchiveFieldNames()); + return success(respVO); + } + + @PutMapping("/update") + @Operation(summary = "更新我的员工档案") + @PreAuthorize("@ss.hasPermission('hrm:portal:employee:update')") + public CommonResult updateEmployee( + @Valid @RequestBody HrmPortalEmployeeUpdateReqVO reqVO) { + employeeService.updateEmployeeBySelf(getLoginUserId(), reqVO); + return success(true); + } + + // ==================== 拼接 VO ==================== + + private void hideInvisibleArchiveFields(HrmPortalEmployeeRespVO respVO, Set visibleFields) { + if (!visibleFields.contains(HrmEmployeeArchiveFieldEnum.NAME.getName())) { + respVO.setName(null); + } + if (!visibleFields.contains(HrmEmployeeArchiveFieldEnum.COUNTRY.getName())) { + respVO.setCountry(null); + } + if (!visibleFields.contains(HrmEmployeeArchiveFieldEnum.NATION.getName())) { + respVO.setNation(null); + } + if (!visibleFields.contains(HrmEmployeeArchiveFieldEnum.ID_TYPE.getName())) { + respVO.setIdType(null); + } + if (!visibleFields.contains(HrmEmployeeArchiveFieldEnum.ID_NUMBER.getName())) { + respVO.setIdNumber(null); + } + if (!visibleFields.contains(HrmEmployeeArchiveFieldEnum.SEX.getName())) { + respVO.setSex(null); + } + if (!visibleFields.contains(HrmEmployeeArchiveFieldEnum.NATIVE_PLACE.getName())) { + respVO.setNativePlace(null); + } + if (!visibleFields.contains(HrmEmployeeArchiveFieldEnum.BIRTHDAY.getName())) { + respVO.setBirthday(null); + } + if (!visibleFields.contains(HrmEmployeeArchiveFieldEnum.AGE.getName())) { + respVO.setAge(null); + } + if (!visibleFields.contains(HrmEmployeeArchiveFieldEnum.HIGHEST_EDUCATION.getName())) { + respVO.setHighestEducation(null); + } + if (!visibleFields.contains(HrmEmployeeArchiveFieldEnum.MOBILE.getName())) { + respVO.setMobile(null); + } + if (!visibleFields.contains(HrmEmployeeArchiveFieldEnum.EMAIL.getName())) { + respVO.setEmail(null); + } + if (!visibleFields.contains(HrmEmployeeArchiveFieldEnum.ADDRESS.getName())) { + respVO.setAddress(null); + } + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/HrmPortalEmployeeEducationExperienceController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/HrmPortalEmployeeEducationExperienceController.java new file mode 100644 index 0000000..fc8cec3 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/HrmPortalEmployeeEducationExperienceController.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.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.dal.dataobject.employee.experience.HrmEmployeeEducationExperienceDO; +import cn.iocoder.yudao.module.hrm.service.employee.experience.HrmEmployeeEducationExperienceService; +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService; +import io.swagger.v3.oas.annotations.Operation; +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.RestController; + +import java.util.List; + +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/portal/employee/education-experience") +@Validated +public class HrmPortalEmployeeEducationExperienceController { + + @Resource + private HrmEmployeeEducationExperienceService educationExperienceService; + @Resource + private HrmEmployeeService employeeService; + + @GetMapping("/list") + @Operation(summary = "获得我的教育经历列表") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult> getEducationExperienceList() { + Long employeeId = employeeService.validateEmployeeBySelf(getLoginUserId()).getId(); + List educationExperiences = + educationExperienceService.getEducationExperienceListByEmployeeId(employeeId); + return success(BeanUtils.toBean( + educationExperiences, HrmEmployeeEducationExperienceRespVO.class)); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/HrmPortalEmployeeFieldConfigController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/HrmPortalEmployeeFieldConfigController.java new file mode 100644 index 0000000..eb00fb9 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/HrmPortalEmployeeFieldConfigController.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.employee; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +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.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.RestController; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - HRM 员工端档案字段配置") +@RestController +@RequestMapping("/hrm/portal/employee/field-config") +@Validated +public class HrmPortalEmployeeFieldConfigController { + + @Resource + private HrmEmployeeFieldConfigService employeeFieldConfigService; + + @GetMapping("/list") + @Operation(summary = "获得我的员工档案字段配置") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult> getEmployeeFieldConfigList() { + return success(employeeFieldConfigService.getEmployeeArchiveFieldConfigList()); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/HrmPortalEmployeeQuitInfoController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/HrmPortalEmployeeQuitInfoController.java new file mode 100644 index 0000000..1fae541 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/HrmPortalEmployeeQuitInfoController.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.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 cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService; +import io.swagger.v3.oas.annotations.Operation; +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.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/portal/employee/quit-info") +@Validated +public class HrmPortalEmployeeQuitInfoController { + + @Resource + private HrmEmployeeQuitInfoService quitInfoService; + @Resource + private HrmEmployeeService employeeService; + + @GetMapping("/get") + @Operation(summary = "获得我的离职信息") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult getQuitInfo() { + Long employeeId = employeeService.validateEmployeeBySelf(getLoginUserId()).getId(); + HrmEmployeeQuitInfoDO quitInfo = quitInfoService.getQuitInfoByEmployeeId(employeeId); + return success(BeanUtils.toBean(quitInfo, HrmEmployeeQuitInfoRespVO.class)); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/HrmPortalEmployeeTrainingExperienceController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/HrmPortalEmployeeTrainingExperienceController.java new file mode 100644 index 0000000..e67400f --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/HrmPortalEmployeeTrainingExperienceController.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.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.dal.dataobject.employee.experience.HrmEmployeeTrainingExperienceDO; +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService; +import cn.iocoder.yudao.module.hrm.service.employee.experience.HrmEmployeeTrainingExperienceService; +import io.swagger.v3.oas.annotations.Operation; +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.RestController; + +import java.util.List; + +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/portal/employee/training-experience") +@Validated +public class HrmPortalEmployeeTrainingExperienceController { + + @Resource + private HrmEmployeeTrainingExperienceService trainingExperienceService; + @Resource + private HrmEmployeeService employeeService; + + @GetMapping("/list") + @Operation(summary = "获得我的培训经历列表") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult> getTrainingExperienceList() { + Long employeeId = employeeService.validateEmployeeBySelf(getLoginUserId()).getId(); + List trainingExperiences = + trainingExperienceService.getTrainingExperienceListByEmployeeId(employeeId); + return success(BeanUtils.toBean( + trainingExperiences, HrmEmployeeTrainingExperienceRespVO.class)); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/HrmPortalEmployeeWorkExperienceController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/HrmPortalEmployeeWorkExperienceController.java new file mode 100644 index 0000000..aa8c4b9 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/HrmPortalEmployeeWorkExperienceController.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.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.dal.dataobject.employee.experience.HrmEmployeeWorkExperienceDO; +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService; +import cn.iocoder.yudao.module.hrm.service.employee.experience.HrmEmployeeWorkExperienceService; +import io.swagger.v3.oas.annotations.Operation; +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.RestController; + +import java.util.List; + +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/portal/employee/work-experience") +@Validated +public class HrmPortalEmployeeWorkExperienceController { + + @Resource + private HrmEmployeeWorkExperienceService workExperienceService; + @Resource + private HrmEmployeeService employeeService; + + @GetMapping("/list") + @Operation(summary = "获得我的工作经历列表") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult> getWorkExperienceList() { + Long employeeId = employeeService.validateEmployeeBySelf(getLoginUserId()).getId(); + List workExperiences = + workExperienceService.getWorkExperienceListByEmployeeId(employeeId); + return success(BeanUtils.toBean( + workExperiences, HrmEmployeeWorkExperienceRespVO.class)); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/vo/employee/HrmPortalEmployeeRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/vo/employee/HrmPortalEmployeeRespVO.java new file mode 100644 index 0000000..c803184 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/vo/employee/HrmPortalEmployeeRespVO.java @@ -0,0 +1,117 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.employee.vo.employee; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - HRM 员工端员工档案 Response VO") +@Data +public class HrmPortalEmployeeRespVO { + + @Schema(description = "员工档案编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "员工姓名", example = "张三") + private String name; + + @Schema(description = "员工头像") + private String avatar; + + @Schema(description = "工号", example = "HRM001") + private String jobNumber; + + @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; + + @Schema(description = "部门名称", example = "研发部") + private String deptName; + + @Schema(description = "直属上级员工编号", example = "1") + private Long leaderEmployeeId; + + @Schema(description = "直属上级员工姓名", example = "李四") + private String leaderEmployeeName; + + @Schema(description = "入职状态", example = "1") + private Integer entryStatus; + + @Schema(description = "员工状态", example = "1") + private Integer status; + + @Schema(description = "聘用形式", example = "1") + private Integer type; + + @Schema(description = "入职时间") + private LocalDateTime entryTime; + + @Schema(description = "入职天数", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Long entryDay; + + @Schema(description = "试用期,单位月;0 表示无试用期", example = "3") + private Integer probation; + + @Schema(description = "转正时间") + private LocalDateTime regularTime; + + @Schema(description = "离职时间") + private LocalDateTime leaveTime; + + @Schema(description = "职位名称", example = "Java 工程师") + private String postName; + + @Schema(description = "岗位职级", example = "P6") + private String postLevel; + + @Schema(description = "工作城市", example = "上海") + private String workCity; + + @Schema(description = "工作地点", example = "浦东新区") + private String workAddress; + + @Schema(description = "工作详细地址", example = "张江高科 1 号楼") + private String workDetailAddress; + + @Schema(description = "司龄开始时间") + private LocalDateTime companyAgeStartTime; + + @Schema(description = "司龄,单位年", example = "2") + private Integer companyAge; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/vo/employee/HrmPortalEmployeeUpdateReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/vo/employee/HrmPortalEmployeeUpdateReqVO.java new file mode 100644 index 0000000..3c541ba --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/employee/vo/employee/HrmPortalEmployeeUpdateReqVO.java @@ -0,0 +1,79 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.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.HrmEmployeeIdTypeEnum; +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.PastOrPresent; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - HRM 员工端档案更新 Request VO") +@Data +public class HrmPortalEmployeeUpdateReqVO { + + @Schema(description = "员工姓名", example = "张三") + @Size(max = 255, message = "员工姓名不能超过 255 个字符") + private String name; + + @Schema(description = "手机号", example = "15601691300") + @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 = "杭州市西湖区") + @Size(max = 255, message = "户籍地址不能超过 255 个字符") + private String address; + + @Schema(description = "最高学历", example = "8") + @InEnum(value = HrmEmployeeEducationEnum.class, message = "最高学历必须是 {value}") + private Integer highestEducation; + + @AssertTrue(message = "身份证号码格式不正确") + @JsonIgnore + public boolean isIdNumberValid() { + return ObjUtil.notEqual(HrmEmployeeIdTypeEnum.ID_CARD.getType(), idType) + || StrUtil.isBlank(idNumber) || IdcardUtil.isValidCard(idNumber); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/home/HrmPortalHomeController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/home/HrmPortalHomeController.java new file mode 100644 index 0000000..e6ebe27 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/home/HrmPortalHomeController.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.home; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.hrm.controller.admin.home.vo.HrmHomeCalendarItemRespVO; +import cn.iocoder.yudao.module.hrm.service.home.HrmHomeService; +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 org.springframework.format.annotation.DateTimeFormat; +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.time.LocalDate; +import java.util.List; +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/portal/home") +@Validated +public class HrmPortalHomeController { + + @Resource + private HrmHomeService homeService; + + @GetMapping("/calendar") + @Operation(summary = "获得员工端首页日历") + @Parameters({ + @Parameter(name = "startDate", description = "开始日期", required = true, example = "2026-08-01"), + @Parameter(name = "endDate", description = "结束日期", required = true, example = "2026-08-31") + }) + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult> getEmployeeHomeCalendar( + @RequestParam("startDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate, + @RequestParam("endDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate) { + return success(homeService.getEmployeeCalendar(getLoginUserId(), startDate, endDate)); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/insurance/HrmPortalInsuranceController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/insurance/HrmPortalInsuranceController.java new file mode 100644 index 0000000..c76b3ef --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/insurance/HrmPortalInsuranceController.java @@ -0,0 +1,117 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.insurance; + +import cn.hutool.core.util.BooleanUtil; +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.portal.insurance.vo.HrmPortalInsuranceRecordRespVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.insurance.monthrecord.HrmInsuranceMonthEmployeeRecordDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.insurance.config.HrmInsuranceSchemeDO; +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService; +import cn.iocoder.yudao.module.hrm.service.insurance.monthrecord.HrmInsuranceMonthEmployeeRecordService; +import cn.iocoder.yudao.module.hrm.service.insurance.config.HrmInsuranceSchemeService; +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 static cn.hutool.core.util.ObjectUtil.notEqual; +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.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; +import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.INSURANCE_MONTH_EMP_RECORD_NOT_EXISTS; + +@Tag(name = "管理后台 - HRM 员工端社保") +@RestController +@RequestMapping("/hrm/portal/insurance/record") +@Validated +public class HrmPortalInsuranceController { + + @Resource + private HrmEmployeeService employeeService; + @Resource + private HrmInsuranceMonthEmployeeRecordService insuranceMonthEmployeeRecordService; + @Resource + private HrmInsuranceSchemeService insuranceSchemeService; + + @GetMapping("/list") + @Operation(summary = "获得我的社保记录") + @Parameter(name = "year", description = "年份", example = "2026") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult> getInsuranceRecordList( + @RequestParam(value = "year", required = false) Integer year) { + HrmEmployeeDO employee = employeeService.validateEmployeeBySelf(getLoginUserId()); + List records = + insuranceMonthEmployeeRecordService.getMonthEmployeeRecordListByEmployeeIdAndYear( + employee.getId(), year); + // 拼接 VO + return success(buildInsuranceRecordRespVOList(records)); + } + + @GetMapping("/get") + @Operation(summary = "获得我的社保记录详情") + @Parameter(name = "id", description = "社保记录编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult getInsuranceRecord(@RequestParam("id") Long id) { + HrmEmployeeDO employee = employeeService.validateEmployeeBySelf(getLoginUserId()); + HrmInsuranceMonthEmployeeRecordDO record = insuranceMonthEmployeeRecordService.getMonthEmployeeRecord(id); + if (record == null || notEqual(record.getEmployeeId(), employee.getId())) { + throw exception(INSURANCE_MONTH_EMP_RECORD_NOT_EXISTS); + } + // 拼接 VO + return success(buildInsuranceRecordRespVO( + record, insuranceSchemeService.getScheme(record.getSchemeId()), true)); + } + + // ==================== 拼接 VO ==================== + + /** + * 构建社保记录响应列表 + * + * @param records 社保记录列表 + * @return 社保记录响应列表 + */ + private List buildInsuranceRecordRespVOList( + List records) { + Map schemeMap = insuranceSchemeService.getSchemeMap( + convertSet(records, HrmInsuranceMonthEmployeeRecordDO::getSchemeId)); + return convertList(records, record -> + buildInsuranceRecordRespVO(record, schemeMap.get(record.getSchemeId()), false)); + } + + /** + * 构建社保记录响应 + * + * @param record 社保记录 + * @param scheme 社保方案 + * @param includeProjects 是否返回社保项目 + * @return 社保记录响应 + */ + private HrmPortalInsuranceRecordRespVO buildInsuranceRecordRespVO( + HrmInsuranceMonthEmployeeRecordDO record, HrmInsuranceSchemeDO scheme, + boolean includeProjects) { + HrmPortalInsuranceRecordRespVO respVO = BeanUtils.toBean(record, HrmPortalInsuranceRecordRespVO.class); + if (scheme != null) { + respVO.setSchemeName(scheme.getName()).setSchemeType(scheme.getType()) + .setSchemeCity(AreaUtils.format(scheme.getAreaId())); + } + if (BooleanUtil.isFalse(includeProjects)) { + respVO.setProjects(null); + } + return respVO; + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/insurance/vo/HrmPortalInsuranceProjectRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/insurance/vo/HrmPortalInsuranceProjectRespVO.java new file mode 100644 index 0000000..45b10c5 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/insurance/vo/HrmPortalInsuranceProjectRespVO.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.insurance.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - HRM 员工端社保项目 Response VO") +@Data +public class HrmPortalInsuranceProjectRespVO { + + @Schema(description = "社保项目编号") + private Long schemeProjectId; + + @Schema(description = "员工端社保项目类型") + private Integer type; + + @Schema(description = "社保项目名称") + private String name; + + @Schema(description = "默认缴纳金额") + private BigDecimal baseAmount; + + @Schema(description = "公司缴纳比例") + private BigDecimal corporateRate; + + @Schema(description = "个人缴纳比例") + private BigDecimal personalRate; + + @Schema(description = "公司缴纳金额") + private BigDecimal corporateAmount; + + @Schema(description = "个人缴纳金额") + private BigDecimal personalAmount; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/insurance/vo/HrmPortalInsuranceRecordRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/insurance/vo/HrmPortalInsuranceRecordRespVO.java new file mode 100644 index 0000000..c5ce6a8 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/insurance/vo/HrmPortalInsuranceRecordRespVO.java @@ -0,0 +1,62 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.insurance.vo; + +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 +public class HrmPortalInsuranceRecordRespVO { + + @Schema(description = "编号") + private Long id; + + @Schema(description = "月度社保记录编号") + private Long monthRecordId; + + @Schema(description = "员工编号") + private Long employeeId; + + @Schema(description = "社保方案编号") + private Long schemeId; + + @Schema(description = "社保方案名称") + private String schemeName; + + @Schema(description = "社保方案类型") + private Integer schemeType; + + @Schema(description = "参保城市") + private String schemeCity; + + @Schema(description = "年份") + private Integer year; + + @Schema(description = "月份") + private Integer month; + + @Schema(description = "个人社保金额") + private BigDecimal personalInsuranceAmount; + + @Schema(description = "个人公积金金额") + private BigDecimal personalProvidentFundAmount; + + @Schema(description = "公司社保金额") + private BigDecimal corporateInsuranceAmount; + + @Schema(description = "公司公积金金额") + private BigDecimal corporateProvidentFundAmount; + + @Schema(description = "员工端社保记录状态") + private Integer status; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + + @Schema(description = "社保项目列表") + private List projects; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/HrmPortalPerformanceAssessmentController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/HrmPortalPerformanceAssessmentController.java new file mode 100644 index 0000000..0306757 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/HrmPortalPerformanceAssessmentController.java @@ -0,0 +1,354 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.performance; + +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.performance.vo.assessment.HrmPerformanceAssessmentRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.performance.vo.assessment.HrmPerformanceProcessRecordRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.portal.performance.vo.assessment.HrmPortalPerformanceAssessmentPageReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.portal.performance.vo.assessment.HrmPortalPerformanceAssessmentRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.portal.performance.vo.assessment.HrmPortalPerformanceTaskCountRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.portal.performance.vo.process.HrmPortalPerformanceAppealReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.portal.performance.vo.process.HrmPortalPerformanceConfirmReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.portal.performance.vo.process.HrmPortalPerformanceHandleStageReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.portal.performance.vo.process.HrmPortalPerformanceProcessRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.portal.performance.vo.review.HrmPortalPerformanceFillQuotaReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.portal.performance.vo.review.HrmPortalPerformanceReviewRejectReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.portal.performance.vo.review.HrmPortalPerformanceScorePreviewRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.portal.performance.vo.review.HrmPortalPerformanceScoreReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.portal.performance.vo.task.HrmPortalPerformanceTaskPageReqVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.performance.assessment.HrmPerformanceAssessmentDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.performance.assessment.HrmPerformanceAssessmentStageDO; +import cn.iocoder.yudao.module.hrm.enums.performance.assessment.HrmPerformanceAssessmentStageStatusEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformanceStageTypeEnum; +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService; +import cn.iocoder.yudao.module.hrm.service.performance.assessment.HrmPerformanceAssessmentProcessService; +import cn.iocoder.yudao.module.hrm.service.performance.assessment.HrmPerformanceAssessmentQueryService; +import cn.iocoder.yudao.module.hrm.service.performance.assessment.HrmPerformanceAssessmentReviewService; +import cn.iocoder.yudao.module.hrm.service.performance.assessment.HrmPerformanceAssessmentService; +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.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 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/portal/performance/assessment") +@Validated +public class HrmPortalPerformanceAssessmentController { + + @Resource + private HrmEmployeeService employeeService; + @Resource + private HrmPerformanceAssessmentService performanceAssessmentService; + @Resource + private HrmPerformanceAssessmentProcessService performanceAssessmentProcessService; + @Resource + private HrmPerformanceAssessmentReviewService performanceAssessmentReviewService; + @Resource + private HrmPerformanceAssessmentQueryService performanceAssessmentQueryService; + + @GetMapping("/page") + @Operation(summary = "获得我的绩效分页") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult> getPerformanceAssessmentPage( + @Valid HrmPortalPerformanceAssessmentPageReqVO reqVO) { + HrmEmployeeDO employee = employeeService.validateEmployeeBySelf(getLoginUserId()); + PageResult pageResult = + performanceAssessmentService.getPortalPerformanceAssessmentPage(reqVO, employee.getId()); + // 拼接 VO + return success(buildPortalAssessmentRespVOPage(pageResult)); + } + + @GetMapping("/task-count") + @Operation(summary = "获得我的绩效任务数量") + @Parameter(name = "search", description = "员工姓名或工号", example = "张三") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult getPerformanceAssessmentTaskCount( + @RequestParam(value = "search", required = false) String search) { + Long loginUserId = getLoginUserId(); + employeeService.validateEmployeeBySelf(loginUserId); + Map> taskCountMap = + performanceAssessmentProcessService.getMyTaskCountMap(loginUserId, search); + // 拼接 VO + return success(buildTaskCountRespVO(taskCountMap)); + } + + @GetMapping("/fill-quota-task-page") + @Operation(summary = "获得我的绩效指标填写任务分页") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult> getPerformanceAssessmentFillQuotaTaskPage( + @Valid HrmPortalPerformanceTaskPageReqVO reqVO) { + Long loginUserId = getLoginUserId(); + PageResult pageResult = + performanceAssessmentReviewService.getMyFillQuotaTaskPage(loginUserId, reqVO); + return success(performanceAssessmentQueryService + .getPerformanceAssessmentTaskRespVOPage(pageResult, loginUserId)); + } + + @GetMapping("/target-confirmation-task-page") + @Operation(summary = "获得我的绩效目标确认任务分页") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult> getPerformanceAssessmentTargetConfirmationTaskPage( + @Valid HrmPortalPerformanceTaskPageReqVO reqVO) { + Long loginUserId = getLoginUserId(); + PageResult pageResult = + performanceAssessmentProcessService.getMyTargetConfirmationTaskPage(loginUserId, reqVO); + return success(performanceAssessmentQueryService + .getPerformanceAssessmentTaskRespVOPage(pageResult, loginUserId)); + } + + @GetMapping("/review-task-page") + @Operation(summary = "获得我的绩效评分任务分页") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult> getPerformanceAssessmentReviewTaskPage( + @Valid HrmPortalPerformanceTaskPageReqVO reqVO) { + Long loginUserId = getLoginUserId(); + PageResult pageResult = + performanceAssessmentReviewService.getMyReviewTaskPage(loginUserId, reqVO); + return success(performanceAssessmentQueryService + .getPerformanceAssessmentTaskRespVOPage(pageResult, loginUserId)); + } + + @GetMapping("/result-audit-task-page") + @Operation(summary = "获得我的绩效结果审核任务分页") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult> getPerformanceAssessmentResultAuditTaskPage( + @Valid HrmPortalPerformanceTaskPageReqVO reqVO) { + Long loginUserId = getLoginUserId(); + PageResult pageResult = + performanceAssessmentProcessService.getMyResultAuditTaskPage(loginUserId, reqVO); + return success(performanceAssessmentQueryService + .getPerformanceAssessmentTaskRespVOPage(pageResult, loginUserId)); + } + + @GetMapping("/result-confirmation-task-page") + @Operation(summary = "获得我的绩效结果确认任务分页") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult> getPerformanceAssessmentResultConfirmationTaskPage( + @Valid HrmPortalPerformanceTaskPageReqVO reqVO) { + Long loginUserId = getLoginUserId(); + PageResult pageResult = + performanceAssessmentProcessService.getMyResultConfirmationTaskPage(loginUserId, reqVO); + return success(performanceAssessmentQueryService + .getPerformanceAssessmentTaskRespVOPage(pageResult, loginUserId)); + } + + @GetMapping("/appeal-task-page") + @Operation(summary = "获得我的绩效申诉处理任务分页") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult> getPerformanceAssessmentAppealTaskPage( + @Valid HrmPortalPerformanceTaskPageReqVO reqVO) { + Long loginUserId = getLoginUserId(); + PageResult pageResult = + performanceAssessmentProcessService.getMyAppealTaskPage(loginUserId, reqVO); + return success(performanceAssessmentQueryService + .getPerformanceAssessmentTaskRespVOPage(pageResult, loginUserId)); + } + + @GetMapping("/get") + @Operation(summary = "获得我的员工绩效考核明细") + @Parameter(name = "id", description = "员工绩效考核编号", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult getPerformanceAssessment( + @RequestParam("id") Long id, + @RequestParam(value = "stageId", required = false) Long stageId) { + Long loginUserId = getLoginUserId(); + HrmPerformanceAssessmentDO assessment = validateReadablePerformanceAssessment(loginUserId, id, stageId); + // 拼接 VO + if (stageId != null) { + return success(performanceAssessmentQueryService + .getPerformanceAssessmentTaskRespVO(assessment, stageId, loginUserId)); + } + return success(performanceAssessmentQueryService + .getPerformanceAssessmentProcessRespVO(assessment, loginUserId)); + } + + @GetMapping("/process-record-list") + @Operation(summary = "获得我的绩效流程记录") + @Parameter(name = "id", description = "员工绩效考核编号", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult> getPerformanceAssessmentProcessRecordList( + @RequestParam("id") Long id, + @RequestParam(value = "stageId", required = false) Long stageId) { + Long loginUserId = getLoginUserId(); + HrmPerformanceAssessmentDO assessment = validateReadablePerformanceAssessment(loginUserId, id, stageId); + return success(performanceAssessmentQueryService.getPerformanceAssessmentProcessRecordList(assessment, loginUserId)); + } + + @PutMapping("/fill-quota") + @Operation(summary = "填写我的绩效指标") + @PreAuthorize("@ss.hasPermission('hrm:portal:performance:action')") + public CommonResult fillPerformanceAssessmentQuota( + @Valid @RequestBody HrmPortalPerformanceFillQuotaReqVO reqVO) { + Long loginUserId = getLoginUserId(); + validateOwnPerformanceAssessment(loginUserId, reqVO.getAssessmentId()); + performanceAssessmentReviewService.fillQuota(loginUserId, reqVO); + return success(true); + } + + @PutMapping("/confirm-target") + @Operation(summary = "确认我的绩效目标") + @PreAuthorize("@ss.hasPermission('hrm:portal:performance:action')") + public CommonResult confirmPerformanceAssessmentTarget( + @Valid @RequestBody HrmPortalPerformanceConfirmReqVO reqVO) { + performanceAssessmentProcessService.confirmTarget(getLoginUserId(), reqVO); + return success(true); + } + + @PostMapping("/score-preview") + @Operation(summary = "试算绩效评分") + @PreAuthorize("@ss.hasPermission('hrm:portal:performance:action')") + public CommonResult previewPerformanceAssessmentScore( + @Valid @RequestBody HrmPortalPerformanceScoreReqVO reqVO) { + return success(performanceAssessmentReviewService.previewScore(getLoginUserId(), reqVO)); + } + + @PutMapping("/score") + @Operation(summary = "提交绩效自评或他评") + @PreAuthorize("@ss.hasPermission('hrm:portal:performance:action')") + public CommonResult scorePerformanceAssessment( + @Valid @RequestBody HrmPortalPerformanceScoreReqVO reqVO) { + return success(performanceAssessmentReviewService.scoreAssessment(getLoginUserId(), reqVO)); + } + + @PutMapping("/reject-review-stage") + @Operation(summary = "驳回上一绩效评分阶段") + @PreAuthorize("@ss.hasPermission('hrm:portal:performance:action')") + public CommonResult rejectPerformanceAssessmentReviewStage( + @Valid @RequestBody HrmPortalPerformanceReviewRejectReqVO reqVO) { + performanceAssessmentReviewService.rejectReviewStage(getLoginUserId(), reqVO); + return success(true); + } + + @PutMapping("/handle-result-audit") + @Operation(summary = "处理绩效结果审核") + @PreAuthorize("@ss.hasPermission('hrm:portal:performance:action')") + public CommonResult handlePerformanceAssessmentResultAudit( + @Valid @RequestBody HrmPortalPerformanceHandleStageReqVO reqVO) { + performanceAssessmentProcessService.handleResultAudit(getLoginUserId(), reqVO); + return success(true); + } + + @PutMapping("/confirm-result") + @Operation(summary = "确认我的绩效结果") + @PreAuthorize("@ss.hasPermission('hrm:portal:performance:action')") + public CommonResult confirmPerformanceAssessmentResult( + @Valid @RequestBody HrmPortalPerformanceConfirmReqVO reqVO) { + Long loginUserId = getLoginUserId(); + validateOwnPerformanceAssessment(loginUserId, reqVO.getAssessmentId()); + performanceAssessmentProcessService.confirmResult(loginUserId, reqVO); + return success(true); + } + + @PutMapping("/submit-appeal") + @Operation(summary = "提交我的绩效申诉") + @PreAuthorize("@ss.hasPermission('hrm:portal:performance:action')") + public CommonResult submitPerformanceAssessmentAppeal( + @Valid @RequestBody HrmPortalPerformanceAppealReqVO reqVO) { + Long loginUserId = getLoginUserId(); + validateOwnPerformanceAssessment(loginUserId, reqVO.getAssessmentId()); + return success(performanceAssessmentProcessService.submitAppeal(loginUserId, reqVO)); + } + + @PutMapping("/handle-appeal") + @Operation(summary = "处理绩效申诉") + @PreAuthorize("@ss.hasPermission('hrm:portal:performance:action')") + public CommonResult handlePerformanceAssessmentAppeal( + @Valid @RequestBody HrmPortalPerformanceHandleStageReqVO reqVO) { + performanceAssessmentProcessService.handleAppeal(getLoginUserId(), reqVO); + return success(true); + } + + private HrmPerformanceAssessmentDO validateOwnPerformanceAssessment(Long loginUserId, Long assessmentId) { + HrmEmployeeDO employee = employeeService.validateEmployeeBySelf(loginUserId); + return performanceAssessmentService.validatePerformanceAssessmentByEmployeeId(assessmentId, employee.getId()); + } + + private HrmPerformanceAssessmentDO validateReadablePerformanceAssessment( + Long loginUserId, Long assessmentId, Long stageId) { + if (stageId == null) { + return validateOwnPerformanceAssessment(loginUserId, assessmentId); + } + performanceAssessmentProcessService.validateTaskStage(loginUserId, assessmentId, stageId); + return performanceAssessmentService.getPerformanceAssessment(assessmentId); + } + + // ==================== 拼接 VO ==================== + + private PageResult buildPortalAssessmentRespVOPage( + PageResult pageResult) { + PageResult respVOPage = + performanceAssessmentQueryService.getPerformanceAssessmentRespVOPage(pageResult); + return new PageResult<>( + BeanUtils.toBean(respVOPage.getList(), HrmPortalPerformanceAssessmentRespVO.class), + respVOPage.getTotal()); + } + + private HrmPortalPerformanceTaskCountRespVO buildTaskCountRespVO( + Map> taskCountMap) { + Integer pendingStatus = HrmPerformanceAssessmentStageStatusEnum.PENDING.getStatus(); + Integer completedStatus = HrmPerformanceAssessmentStageStatusEnum.PROCESSED.getStatus(); + // 按流程类型和处理状态组装员工端各页签的待办、已办数量 + return new HrmPortalPerformanceTaskCountRespVO() + .setFillPendingCount(getTaskCount( + taskCountMap, HrmPerformanceStageTypeEnum.FILL_QUOTA.getType(), pendingStatus)) + .setFillCompletedCount(getTaskCount( + taskCountMap, HrmPerformanceStageTypeEnum.FILL_QUOTA.getType(), completedStatus)) + .setTargetPendingCount(getTaskCount( + taskCountMap, HrmPerformanceStageTypeEnum.TARGET_CONFIRM.getType(), pendingStatus)) + .setTargetCompletedCount(getTaskCount( + taskCountMap, HrmPerformanceStageTypeEnum.TARGET_CONFIRM.getType(), completedStatus)) + .setReviewPendingCount(getTaskCount(taskCountMap, HrmPerformanceStageTypeEnum.REVIEW_TYPES, pendingStatus)) + .setReviewCompletedCount(getTaskCount(taskCountMap, HrmPerformanceStageTypeEnum.REVIEW_TYPES, completedStatus)) + .setResultAuditPendingCount(getTaskCount( + taskCountMap, HrmPerformanceStageTypeEnum.RESULT_AUDIT.getType(), pendingStatus)) + .setResultAuditCompletedCount(getTaskCount( + taskCountMap, HrmPerformanceStageTypeEnum.RESULT_AUDIT.getType(), completedStatus)) + .setResultConfirmationPendingCount(getTaskCount( + taskCountMap, HrmPerformanceStageTypeEnum.RESULT_CONFIRM.getType(), pendingStatus)) + .setResultConfirmationCompletedCount(getTaskCount( + taskCountMap, HrmPerformanceStageTypeEnum.RESULT_CONFIRM.getType(), completedStatus)) + .setResultConfirmationAppealedCount(getTaskCount( + taskCountMap, HrmPerformanceStageTypeEnum.RESULT_CONFIRM.getType(), + HrmPerformanceAssessmentStageStatusEnum.APPEALED.getStatus())) + .setAppealPendingCount(getTaskCount( + taskCountMap, HrmPerformanceStageTypeEnum.APPEAL_CONFIRM.getType(), pendingStatus)) + .setAppealCompletedCount(getTaskCount( + taskCountMap, HrmPerformanceStageTypeEnum.APPEAL_CONFIRM.getType(), completedStatus)); + } + + private long getTaskCount(Map> countMap, Integer type, Integer status) { + return countMap.getOrDefault(type, Collections.emptyMap()).getOrDefault(status, 0L); + } + + @SuppressWarnings("SameParameterValue") + private long getTaskCount(Map> countMap, + Collection types, Integer status) { + long count = 0L; + for (Integer type : types) { + count += getTaskCount(countMap, type, status); + } + return count; + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/assessment/HrmPortalPerformanceAssessmentPageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/assessment/HrmPortalPerformanceAssessmentPageReqVO.java new file mode 100644 index 0000000..75e1dae --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/assessment/HrmPortalPerformanceAssessmentPageReqVO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.performance.vo.assessment; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Schema(description = "管理后台 - HRM 员工端我的绩效分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class HrmPortalPerformanceAssessmentPageReqVO extends PageParam { + + @Schema(description = "绩效计划名称", example = "2026 年第三季度绩效计划") + private String search; + + @Schema(description = "是否已归档", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + @NotNull(message = "是否已归档不能为空") + private Boolean archived; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/assessment/HrmPortalPerformanceAssessmentRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/assessment/HrmPortalPerformanceAssessmentRespVO.java new file mode 100644 index 0000000..8d43217 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/assessment/HrmPortalPerformanceAssessmentRespVO.java @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.performance.vo.assessment; + +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 +public class HrmPortalPerformanceAssessmentRespVO { + + @Schema(description = "编号") + private Long id; + + @Schema(description = "绩效计划编号") + private Long planId; + + @Schema(description = "绩效计划名称") + private String name; + + @Schema(description = "考核状态") + private Integer status; + + @Schema(description = "阶段状态") + private Integer stageType; + + @Schema(description = "得分") + private BigDecimal score; + + @Schema(description = "结果等级") + private String resultLevel; + + @Schema(description = "绩效系数") + private BigDecimal coefficient; + + @Schema(description = "结果审核状态") + private Integer resultAuditStatus; + + @Schema(description = "结果审核时间") + private LocalDateTime resultAuditTime; + + @Schema(description = "结果审核意见") + private String resultAuditReason; + + @Schema(description = "申诉原因") + private String appealReason; + + @Schema(description = "申诉状态") + private Integer appealStatus; + + @Schema(description = "申诉处理时间") + private LocalDateTime appealTime; + + @Schema(description = "申诉处理意见") + private String appealComment; + + @Schema(description = "开始日期") + private LocalDateTime startTime; + + @Schema(description = "结束日期") + private LocalDateTime endTime; + + @Schema(description = "归档时间") + private LocalDateTime archiveTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/assessment/HrmPortalPerformanceTaskCountRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/assessment/HrmPortalPerformanceTaskCountRespVO.java new file mode 100644 index 0000000..5ffd548 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/assessment/HrmPortalPerformanceTaskCountRespVO.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.performance.vo.assessment; + +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 HrmPortalPerformanceTaskCountRespVO { + + @Schema(description = "待填写指标数量") + private Long fillPendingCount; + + @Schema(description = "已填写指标数量") + private Long fillCompletedCount; + + @Schema(description = "待确认目标数量") + private Long targetPendingCount; + + @Schema(description = "已确认目标数量") + private Long targetCompletedCount; + + @Schema(description = "待评分数量") + private Long reviewPendingCount; + + @Schema(description = "已评分数量") + private Long reviewCompletedCount; + + @Schema(description = "待审核结果数量") + private Long resultAuditPendingCount; + + @Schema(description = "已审核结果数量") + private Long resultAuditCompletedCount; + + @Schema(description = "待确认结果数量") + private Long resultConfirmationPendingCount; + + @Schema(description = "已确认结果数量") + private Long resultConfirmationCompletedCount; + + @Schema(description = "已申诉结果数量") + private Long resultConfirmationAppealedCount; + + @Schema(description = "待处理申诉数量") + private Long appealPendingCount; + + @Schema(description = "已处理申诉数量") + private Long appealCompletedCount; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/process/HrmPortalPerformanceAppealReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/process/HrmPortalPerformanceAppealReqVO.java new file mode 100644 index 0000000..1f7e1c2 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/process/HrmPortalPerformanceAppealReqVO.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.performance.vo.process; + +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 HrmPortalPerformanceAppealReqVO { + + @Schema(description = "员工绩效考核编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "员工绩效考核不能为空") + private Long assessmentId; + + @Schema(description = "申诉原因", requiredMode = Schema.RequiredMode.REQUIRED, + example = "实际完成结果与评分依据不一致") + @NotBlank(message = "申诉原因不能为空") + @Size(max = 500, message = "申诉原因不能超过 500 个字符") + private String appealReason; + + @Schema(description = "申诉附件地址列表", example = "[https://example.com/appeal.pdf]") + private List appealFileUrls; + + @Schema(description = "退回评分节点编号列表", requiredMode = Schema.RequiredMode.REQUIRED, + example = "[1024, 2048]") + @NotNull(message = "退回评分节点不能为空") + private List reviewStageIds; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/process/HrmPortalPerformanceConfirmReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/process/HrmPortalPerformanceConfirmReqVO.java new file mode 100644 index 0000000..d1da2f7 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/process/HrmPortalPerformanceConfirmReqVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.performance.vo.process; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.assessment.HrmPerformanceConfirmationResultEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +@Schema(description = "管理后台 - HRM 员工端绩效确认 Request VO") +@Data +public class HrmPortalPerformanceConfirmReqVO { + + @Schema(description = "员工绩效考核编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "员工绩效考核不能为空") + private Long assessmentId; + + @Schema(description = "是否通过", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "确认结果不能为空") + @InEnum(value = HrmPerformanceConfirmationResultEnum.class, message = "确认结果必须是 {value}") + private Integer pass; + + @Schema(description = "说明", example = "确认考核目标") + @Size(max = 1000, message = "说明不能超过 1000 个字符") + private String comment; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/process/HrmPortalPerformanceHandleStageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/process/HrmPortalPerformanceHandleStageReqVO.java new file mode 100644 index 0000000..7018643 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/process/HrmPortalPerformanceHandleStageReqVO.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.performance.vo.process; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.assessment.HrmPerformanceConfirmationResultEnum; +import io.swagger.v3.oas.annotations.media.Schema; +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 HrmPortalPerformanceHandleStageReqVO { + + @Schema(description = "员工绩效考核编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "员工绩效考核不能为空") + private Long assessmentId; + + @Schema(description = "运行阶段编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + @NotNull(message = "运行阶段不能为空") + private Long stageId; + + @Schema(description = "是否通过", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "处理结果不能为空") + @InEnum(value = HrmPerformanceConfirmationResultEnum.class, message = "处理结果必须是 {value}") + private Integer pass; + + @Schema(description = "处理意见", example = "审核通过") + @Size(max = 500, message = "处理意见不能超过 500 个字符") + private String comment; + + @Schema(description = "退回评分阶段编号列表", example = "[1024, 1025]") + private List reviewStageIds; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/process/HrmPortalPerformanceProcessRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/process/HrmPortalPerformanceProcessRespVO.java new file mode 100644 index 0000000..d8813ab --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/process/HrmPortalPerformanceProcessRespVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.performance.vo.process; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - HRM 员工端绩效处理 Response VO") +@Data +public class HrmPortalPerformanceProcessRespVO { + + @Schema(description = "编号") + private Long id; + + @Schema(description = "下一运行阶段编号") + private Long nextStageId; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/review/HrmPortalPerformanceFillQuotaReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/review/HrmPortalPerformanceFillQuotaReqVO.java new file mode 100644 index 0000000..896f5f9 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/review/HrmPortalPerformanceFillQuotaReqVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.performance.vo.review; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - HRM 员工端绩效指标填写 Request VO") +@Data +public class HrmPortalPerformanceFillQuotaReqVO { + + @Schema(description = "员工绩效考核编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "员工绩效考核不能为空") + private Long assessmentId; + + @Schema(description = "指标列表") + @Valid + private List quotas; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/review/HrmPortalPerformanceQuotaSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/review/HrmPortalPerformanceQuotaSaveReqVO.java new file mode 100644 index 0000000..3840070 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/review/HrmPortalPerformanceQuotaSaveReqVO.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.performance.vo.review; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - HRM 员工绩效指标保存 Request VO") +@Data +public class HrmPortalPerformanceQuotaSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "员工绩效维度编号", example = "2048") + private Long dimensionId; + + @Schema(description = "指标名称", example = "销售目标达成率") + @Size(max = 255, message = "指标名称不能超过 255 个字符") + private String name; + + @Schema(description = "指标说明", example = "按季度销售目标计算") + @Size(max = 1000, message = "指标说明不能超过 1000 个字符") + private String description; + + @Schema(description = "标准值", example = "完成率达到 100% 得满分") + @Size(max = 1000, message = "标准值不能超过 1000 个字符") + private String standard; + + @Schema(description = "指标权重", example = "50") + private BigDecimal weight; + + @Schema(description = "分数类型", example = "1") + private Integer scoreType; + + @Schema(description = "目标值", example = "100%") + @Size(max = 1000, message = "目标值不能超过 1000 个字符") + private String targetValue; + + @Schema(description = "实际值", example = "95%") + @Size(max = 1000, message = "实际值不能超过 1000 个字符") + private String actualValue; + + @Schema(description = "自评分数", example = "90") + private BigDecimal selfScore; + + @Schema(description = "评分人得分", example = "88") + private BigDecimal reviewerScore; + + @Schema(description = "最终得分", example = "89") + private BigDecimal finalScore; + + @Schema(description = "说明", example = "目标基本达成") + @Size(max = 1000, message = "说明不能超过 1000 个字符") + private String comment; + + @Schema(description = "排序", example = "1") + private Integer sort; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/review/HrmPortalPerformanceReviewRejectReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/review/HrmPortalPerformanceReviewRejectReqVO.java new file mode 100644 index 0000000..72c1321 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/review/HrmPortalPerformanceReviewRejectReqVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.performance.vo.review; + +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 HrmPortalPerformanceReviewRejectReqVO { + + @Schema(description = "员工绩效考核编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "员工绩效考核不能为空") + private Long assessmentId; + + @Schema(description = "评分阶段编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + @NotNull(message = "当前评分阶段不能为空") + private Long reviewStageId; + + @Schema(description = "绩效评审驳回原因", requiredMode = Schema.RequiredMode.REQUIRED, + example = "评分依据不完整,请补充说明") + @NotBlank(message = "驳回原因不能为空") + @Size(max = 1000, message = "驳回原因不能超过 1000 个字符") + private String reason; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/review/HrmPortalPerformanceScorePreviewRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/review/HrmPortalPerformanceScorePreviewRespVO.java new file mode 100644 index 0000000..9c07933 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/review/HrmPortalPerformanceScorePreviewRespVO.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.performance.vo.review; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - HRM 员工端绩效评分试算 Response VO") +@Data +public class HrmPortalPerformanceScorePreviewRespVO { + + @Schema(description = "阶段得分") + private BigDecimal stageScore; + + @Schema(description = "阶段结果等级") + private String stageResultLevel; + + @Schema(description = "当前累计得分") + private BigDecimal cumulativeScore; + + @Schema(description = "当前累计结果等级;全部评分阶段完成时返回") + private String cumulativeResultLevel; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/review/HrmPortalPerformanceScoreReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/review/HrmPortalPerformanceScoreReqVO.java new file mode 100644 index 0000000..22d19a6 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/review/HrmPortalPerformanceScoreReqVO.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.performance.vo.review; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +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 HrmPortalPerformanceScoreReqVO { + + @Schema(description = "员工绩效考核编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "员工绩效考核不能为空") + private Long assessmentId; + + @Schema(description = "评分阶段编号", example = "2048") + private Long reviewStageId; + + @Schema(description = "说明", example = "本阶段评分完成") + @Size(max = 2000, message = "说明不能超过 2000 个字符") + private String comment; + + @Schema(description = "自评说明", example = "已完成本季度目标") + @Size(max = 2000, message = "自评说明不能超过 2000 个字符") + private String selfComment; + + @Schema(description = "评分人说明", example = "目标完成情况良好") + @Size(max = 2000, message = "评分人说明不能超过 2000 个字符") + private String reviewerComment; + + @Schema(description = "指标列表") + @Valid + private List quotas; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/task/HrmPortalPerformanceTaskPageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/task/HrmPortalPerformanceTaskPageReqVO.java new file mode 100644 index 0000000..abcce5c --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/performance/vo/task/HrmPortalPerformanceTaskPageReqVO.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.performance.vo.task; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.assessment.HrmPerformanceAssessmentStageStatusEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Schema(description = "管理后台 - HRM 员工端绩效任务分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class HrmPortalPerformanceTaskPageReqVO extends PageParam { + + @Schema(description = "绩效计划名称、员工姓名或工号", example = "张三") + private String search; + + @Schema(description = "阶段处理状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "阶段处理状态不能为空") + @InEnum(value = HrmPerformanceAssessmentStageStatusEnum.class, + message = "阶段处理状态必须是 {value}") + private Integer stageStatus; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/salary/HrmPortalSalarySlipController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/salary/HrmPortalSalarySlipController.java new file mode 100644 index 0000000..d433267 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/salary/HrmPortalSalarySlipController.java @@ -0,0 +1,130 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.salary; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +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.portal.salary.vo.HrmPortalSalarySlipListReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.portal.salary.vo.HrmPortalSalarySlipRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.portal.salary.vo.HrmPortalSalarySlipUnreadSummaryRespVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.slip.HrmSalarySlipDO; +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService; +import cn.iocoder.yudao.module.hrm.service.salary.slip.HrmSalarySlipService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import cn.iocoder.yudao.module.hrm.enums.salary.slip.HrmSalarySlipReadStatusEnum; +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 jakarta.validation.constraints.NotEmpty; +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.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.time.YearMonth; +import java.util.List; + +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.filterList; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "管理后台 - HRM 员工端工资条") +@RestController +@RequestMapping("/hrm/portal/salary/slip") +@Validated +public class HrmPortalSalarySlipController { + + @Resource + private HrmEmployeeService employeeService; + @Resource + private HrmSalarySlipService salarySlipService; + @Resource + private AdminUserApi adminUserApi; + + @GetMapping("/unread-summary") + @Operation(summary = "获得我的未读工资条概况") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult getUnreadSalarySlipSummary() { + // 1. 获得当前员工的未读工资条 + HrmEmployeeDO employee = employeeService.validateEmployeeBySelf(getLoginUserId()); + List unreadSalarySlips = filterList( + salarySlipService.getSalarySlipListByEmployeeId( + employee.getId(), null, null, null, null), + salarySlip -> HrmSalarySlipReadStatusEnum.UNREAD.getStatus().equals(salarySlip.getReadStatus())); + + // 2. 构建未读工资条概况 + return success(new HrmPortalSalarySlipUnreadSummaryRespVO( + (long) unreadSalarySlips.size(), + buildSalarySlipReminder(CollUtil.getFirst(unreadSalarySlips)))); + } + + @GetMapping("/list") + @Operation(summary = "获得我的工资条列表") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult> getSalarySlipList( + @Valid HrmPortalSalarySlipListReqVO reqVO) { + // 1.1 获得当前员工和月份范围 + HrmEmployeeDO employee = employeeService.validateEmployeeBySelf(getLoginUserId()); + YearMonth startMonth = reqVO.getStartMonth() == null ? null : YearMonth.parse(reqVO.getStartMonth()); + YearMonth endMonth = reqVO.getEndMonth() == null ? null : YearMonth.parse(reqVO.getEndMonth()); + // 1.2 查询工资条列表 + List salarySlips = salarySlipService.getSalarySlipListByEmployeeId( + employee.getId(), startMonth, endMonth, reqVO.getOrderType(), reqVO.getOrder()); + + // 2. 构建工资条响应 + return success(BeanUtils.toBean(salarySlips, HrmPortalSalarySlipRespVO.class)); + } + + @GetMapping("/get") + @Operation(summary = "获得我的工资条详情") + @Parameter(name = "id", description = "工资条编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult getSalarySlip(@RequestParam("id") Long id) { + // 1. 获得当前员工,并校验工资条归属本人 + HrmEmployeeDO employee = employeeService.validateEmployeeBySelf(getLoginUserId()); + HrmSalarySlipDO salarySlip = salarySlipService.getSalarySlipByIdAndEmployeeId(id, employee.getId()); + // 2. 构建工资条响应 + return success(BeanUtils.toBean(salarySlip, HrmPortalSalarySlipRespVO.class)); + } + + @PutMapping("/read") + @Operation(summary = "标记我的工资条为已读") + @Parameter(name = "ids", description = "工资条编号列表", required = true, example = "1024,1025") + @PreAuthorize("@ss.hasPermission('hrm:portal:query')") + public CommonResult markSalarySlipRead( + @RequestParam("ids") @NotEmpty(message = "工资条编号列表不能为空") List ids) { + HrmEmployeeDO employee = employeeService.validateEmployeeBySelf(getLoginUserId()); + salarySlipService.markSalarySlipListRead(employee.getId(), ids); + return success(true); + } + + // ==================== 拼接 VO ==================== + + /** + * 构建最新未读工资条提醒 + * + * @param salarySlip 工资条 + * @return 工资条提醒 + */ + private String buildSalarySlipReminder(HrmSalarySlipDO salarySlip) { + if (salarySlip == null) { + return null; + } + String creator = salarySlip.getCreator(); + String publisherName = StrUtil.blankToDefault(creator, "HR"); + if (StrUtil.isNotBlank(creator) && StrUtil.isNumeric(creator)) { + AdminUserRespDTO user = adminUserApi.getUser(Long.valueOf(creator)); + publisherName = user == null ? "HR" : user.getNickname(); + } + return publisherName + "更新了您" + salarySlip.getMonth() + "月的工资条,前往查看"; + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/salary/vo/HrmPortalSalarySlipListReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/salary/vo/HrmPortalSalarySlipListReqVO.java new file mode 100644 index 0000000..9c3ea7e --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/salary/vo/HrmPortalSalarySlipListReqVO.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.salary.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Pattern; +import lombok.Data; +import org.hibernate.validator.constraints.Range; + +@Schema(description = "管理后台 - HRM 员工端工资条列表 Request VO") +@Data +public class HrmPortalSalarySlipListReqVO { + + @Schema(description = "开始月份", example = "2026-01") + @Pattern(regexp = "\\d{4}-(0[1-9]|1[0-2])", message = "开始月份格式必须为 yyyy-MM") + private String startMonth; + + @Schema(description = "结束月份", example = "2026-12") + @Pattern(regexp = "\\d{4}-(0[1-9]|1[0-2])", message = "结束月份格式必须为 yyyy-MM") + private String endMonth; + + @Schema(description = "排序类型", example = "1") + @Range(min = 1, max = 2, message = "排序类型必须在 1 到 2 之间") + private Integer orderType; + + @Schema(description = "排序方向", example = "1") + @Range(min = 1, max = 2, message = "排序方向必须在 1 到 2 之间") + private Integer order; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/salary/vo/HrmPortalSalarySlipOptionRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/salary/vo/HrmPortalSalarySlipOptionRespVO.java new file mode 100644 index 0000000..7e88b8a --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/salary/vo/HrmPortalSalarySlipOptionRespVO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.salary.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +@Schema(description = "管理后台 - HRM 员工端工资条项 Response VO") +@Data +public class HrmPortalSalarySlipOptionRespVO { + + @Schema(description = "员工端薪资工资条选项名称") + private String name; + + @Schema(description = "员工端薪资工资条选项类型") + private Integer type; + + @Schema(description = "编码") + private Integer code; + + @Schema(description = "值") + private BigDecimal value; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "排序") + private Integer sort; + + @Schema(description = "子工资条项") + private List children; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/salary/vo/HrmPortalSalarySlipRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/salary/vo/HrmPortalSalarySlipRespVO.java new file mode 100644 index 0000000..ad85104 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/salary/vo/HrmPortalSalarySlipRespVO.java @@ -0,0 +1,47 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.salary.vo; + +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 +public class HrmPortalSalarySlipRespVO { + + @Schema(description = "编号") + private Long id; + + @Schema(description = "工资条记录编号") + private Long sendRecordId; + + @Schema(description = "员工工资记录编号") + private Long monthEmployeeRecordId; + + @Schema(description = "员工编号") + private Long employeeId; + + @Schema(description = "年份") + private Integer year; + + @Schema(description = "月份") + private Integer month; + + @Schema(description = "已读状态") + private Integer readStatus; + + @Schema(description = "实际工资") + private BigDecimal realPaySalary; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + + @Schema(description = "选项列表") + private List options; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/salary/vo/HrmPortalSalarySlipUnreadSummaryRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/salary/vo/HrmPortalSalarySlipUnreadSummaryRespVO.java new file mode 100644 index 0000000..a3281c9 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/portal/salary/vo/HrmPortalSalarySlipUnreadSummaryRespVO.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.portal.salary.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Schema(description = "管理后台 - HRM 员工端未读工资条概况 Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class HrmPortalSalarySlipUnreadSummaryRespVO { + + @Schema(description = "未读工资条数量", requiredMode = Schema.RequiredMode.REQUIRED) + private Long unreadCount; + + @Schema(description = "最新未读工资条提醒") + private String reminder; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/HrmRecruitCandidateController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/HrmRecruitCandidateController.java new file mode 100644 index 0000000..e5794db --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/HrmRecruitCandidateController.java @@ -0,0 +1,259 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.NumberUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +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.module.hrm.controller.admin.recruit.vo.HrmRecruitStatusCountRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.candidate.HrmRecruitCandidateUpdateChannelReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.candidate.HrmRecruitCandidateCleanReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.candidate.HrmRecruitCandidateUpdateEliminateReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.candidate.HrmRecruitCandidateEntryReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.candidate.HrmRecruitCandidatePageReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.candidate.HrmRecruitCandidateUpdatePostReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.candidate.HrmRecruitCandidateRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.candidate.HrmRecruitCandidateSaveReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.candidate.HrmRecruitCandidateUpdateStatusReqVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.recruit.candidate.HrmRecruitCandidateDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.recruit.config.HrmRecruitChannelDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.recruit.candidate.HrmRecruitInterviewDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.recruit.post.HrmRecruitPostDO; +import cn.iocoder.yudao.module.hrm.enums.recruit.candidate.HrmRecruitCandidateStatusEnum; +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService; +import cn.iocoder.yudao.module.hrm.service.recruit.candidate.HrmRecruitCandidateService; +import cn.iocoder.yudao.module.hrm.service.recruit.config.HrmRecruitChannelService; +import cn.iocoder.yudao.module.hrm.service.recruit.candidate.HrmRecruitInterviewService; +import cn.iocoder.yudao.module.hrm.service.recruit.post.HrmRecruitPostService; +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 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.time.LocalDateTime; +import java.util.Arrays; +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.convertSet; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getFirst; + +@Tag(name = "管理后台 - HRM 招聘候选人") +@RestController +@RequestMapping("/hrm/recruit/candidate") +@Validated +public class HrmRecruitCandidateController { + + @Resource + private HrmRecruitCandidateService recruitCandidateService; + @Resource + private HrmRecruitPostService recruitPostService; + @Resource + private HrmRecruitChannelService recruitChannelService; + @Resource + private HrmRecruitInterviewService recruitInterviewService; + @Resource + private HrmEmployeeService employeeService; + + @Resource + private DeptApi deptApi; + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建招聘候选人") + @PreAuthorize("@ss.hasPermission('hrm:recruit:candidate:create')") + public CommonResult createRecruitCandidate(@Valid @RequestBody HrmRecruitCandidateSaveReqVO createReqVO) { + return success(recruitCandidateService.createRecruitCandidate(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新招聘候选人") + @PreAuthorize("@ss.hasPermission('hrm:recruit:candidate:update')") + public CommonResult updateRecruitCandidate(@Valid @RequestBody HrmRecruitCandidateSaveReqVO updateReqVO) { + recruitCandidateService.updateRecruitCandidate(updateReqVO); + return success(true); + } + + @PutMapping("/update-status") + @Operation(summary = "更新招聘候选人状态") + @PreAuthorize("@ss.hasPermission('hrm:recruit:candidate:update')") + public CommonResult updateRecruitCandidateStatus(@Valid @RequestBody HrmRecruitCandidateUpdateStatusReqVO reqVO) { + recruitCandidateService.updateRecruitCandidateStatus(reqVO); + return success(true); + } + + @PutMapping("/update-post") + @Operation(summary = "更新招聘候选人应聘职位") + @PreAuthorize("@ss.hasPermission('hrm:recruit:candidate:update')") + public CommonResult updateRecruitCandidatePost(@Valid @RequestBody HrmRecruitCandidateUpdatePostReqVO reqVO) { + recruitCandidateService.updateRecruitCandidatePost(reqVO); + return success(true); + } + + @PutMapping("/update-channel") + @Operation(summary = "更新招聘候选人渠道") + @PreAuthorize("@ss.hasPermission('hrm:recruit:candidate:update')") + public CommonResult updateRecruitCandidateChannel( + @Valid @RequestBody HrmRecruitCandidateUpdateChannelReqVO reqVO) { + recruitCandidateService.updateRecruitCandidateChannel(reqVO); + return success(true); + } + + @PutMapping("/eliminate") + @Operation(summary = "淘汰招聘候选人") + @PreAuthorize("@ss.hasPermission('hrm:recruit:candidate:update')") + public CommonResult eliminateRecruitCandidate(@Valid @RequestBody HrmRecruitCandidateUpdateEliminateReqVO reqVO) { + recruitCandidateService.eliminateRecruitCandidate(reqVO); + return success(true); + } + + @PostMapping("/convert-employee") + @Operation(summary = "候选人转员工档案") + @PreAuthorize("@ss.hasPermission('hrm:recruit:candidate:update')") + public CommonResult convertRecruitCandidateToEmployee( + @Valid @RequestBody HrmRecruitCandidateEntryReqVO reqVO) { + return success(recruitCandidateService.convertRecruitCandidateToEmployee(reqVO)); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除招聘候选人") + @Parameter(name = "id", description = "候选人编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:recruit:candidate:delete')") + public CommonResult deleteRecruitCandidate(@RequestParam("id") Long id) { + recruitCandidateService.deleteRecruitCandidate(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得招聘候选人") + @Parameter(name = "id", description = "候选人编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:recruit:candidate:query')") + public CommonResult getRecruitCandidate(@RequestParam("id") Long id) { + return success(buildRecruitCandidateRespVO(recruitCandidateService.getRecruitCandidate(id))); + } + + @GetMapping("/page") + @Operation(summary = "获得招聘候选人分页") + @PreAuthorize("@ss.hasPermission('hrm:recruit:candidate:query')") + public CommonResult> getRecruitCandidatePage( + @Validated HrmRecruitCandidatePageReqVO pageReqVO) { + PageResult pageResult = recruitCandidateService.getRecruitCandidatePage(pageReqVO); + return success(new PageResult<>(buildRecruitCandidateRespVOList(pageResult.getList()), pageResult.getTotal())); + } + + @GetMapping("/clean-ids") + @Operation(summary = "获得待清理候选人编号") + @PreAuthorize("@ss.hasPermission('hrm:recruit:candidate:delete')") + public CommonResult> getCleanRecruitCandidateIdList(@Valid HrmRecruitCandidateCleanReqVO reqVO) { + List list = recruitCandidateService.getRecruitCandidateList( + reqVO.getStatuses(), LocalDateTime.now().minusDays(reqVO.getDays())); + return success(convertList(list, HrmRecruitCandidateDO::getId)); + } + + @GetMapping("/status-count") + @Operation(summary = "获得候选人状态数量") + @PreAuthorize("@ss.hasPermission('hrm:recruit:candidate:query')") + public CommonResult> getRecruitCandidateStatusCount( + @Validated HrmRecruitCandidatePageReqVO pageReqVO) { + Map countMap = recruitCandidateService.getRecruitCandidateStatusCount(pageReqVO); + return success(convertList(Arrays.asList(HrmRecruitCandidateStatusEnum.values()), + status -> new HrmRecruitStatusCountRespVO(status.getStatus(), + countMap.getOrDefault(status.getStatus(), 0L)))); + } + + // ==================== 拼接 VO ==================== + + private HrmRecruitCandidateRespVO buildRecruitCandidateRespVO(HrmRecruitCandidateDO recruitCandidate) { + if (recruitCandidate == null) { + return null; + } + return getFirst(buildRecruitCandidateRespVOList(Collections.singletonList(recruitCandidate))); + } + + private List buildRecruitCandidateRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 1.1 招聘职位、部门和招聘渠道信息 + Map recruitPostMap = recruitPostService.getRecruitPostMap( + convertSet(list, HrmRecruitCandidateDO::getPostId)); + Map deptMap = deptApi.getDeptMap( + convertSet(recruitPostMap.values(), HrmRecruitPostDO::getDeptId)); + Map recruitChannelMap = recruitChannelService.getRecruitChannelMap( + convertSet(list, HrmRecruitCandidateDO::getChannelId)); + // 1.2 当前面试和员工信息 + Set candidateIds = convertSet(list, HrmRecruitCandidateDO::getId); + Map recruitInterviewMap = recruitInterviewService + .getLatestRecruitInterviewMapByCandidateIds(candidateIds); + Set employeeIds = convertSet(recruitPostMap.values(), HrmRecruitPostDO::getOwnerEmployeeId); + employeeIds.addAll(convertSet(recruitInterviewMap.values(), HrmRecruitInterviewDO::getInterviewEmployeeId)); + employeeIds.addAll(convertSetByFlatMap(recruitInterviewMap.values(), + HrmRecruitInterviewDO::getOtherInterviewEmployeeIds, + Collection::stream)); + Map employeeMap = employeeService.getEmployeeMap(employeeIds); + Map candidateEmployeeMap = employeeService.getEmployeeMapByCandidateIds(candidateIds); + // 1.3 创建人信息 + Map userMap = adminUserApi.getUserMap( + convertSet(list, candidate -> NumberUtil.parseLong(candidate.getCreator()))); + + // 2. 拼接响应 + return BeanUtils.toBean(list, HrmRecruitCandidateRespVO.class, vo -> { + // 2.1 拼接招聘职位、部门和招聘渠道信息 + MapUtils.findAndThen(recruitPostMap, vo.getPostId(), post -> { + vo.setPostName(post.getPostName()).setPostStatus(post.getStatus()) + .setDeptId(post.getDeptId()).setOwnerEmployeeId(post.getOwnerEmployeeId()); + MapUtils.findAndThen(deptMap, post.getDeptId(), dept -> vo.setDeptName(dept.getName())); + MapUtils.findAndThen(employeeMap, post.getOwnerEmployeeId(), + employee -> vo.setOwnerEmployeeName(employee.getName())); + }); + MapUtils.findAndThen(recruitChannelMap, vo.getChannelId(), + channel -> vo.setChannelName(channel.getName())); + // 2.2 拼接面试信息 + if (HrmRecruitCandidateStatusEnum.INTERVIEW_RELATED_STATUSES.contains(vo.getStatus())) { + MapUtils.findAndThen(recruitInterviewMap, vo.getId(), interview -> { + vo.setInterviewId(interview.getId()).setInterviewType(interview.getType()) + .setInterviewEmployeeId(interview.getInterviewEmployeeId()) + .setOtherInterviewEmployeeIds(interview.getOtherInterviewEmployeeIds()) + .setInterviewTime(interview.getInterviewTime()).setInterviewAddress(interview.getAddress()) + .setInterviewResult(interview.getResult()) + .setOtherInterviewEmployeeNames(convertList( + convertList(interview.getOtherInterviewEmployeeIds(), employeeMap::get), + HrmEmployeeDO::getName)); + MapUtils.findAndThen(employeeMap, interview.getInterviewEmployeeId(), + employee -> vo.setInterviewEmployeeName(employee.getName())); + }); + } else { + vo.setStageNumber(0); + } + // 2.3 拼接员工和创建人信息 + MapUtils.findAndThen(candidateEmployeeMap, vo.getId(), employee -> vo.setEmployeeId(employee.getId())); + MapUtils.findAndThen(userMap, NumberUtil.parseLong(vo.getCreator()), + creator -> vo.setCreatorName(creator.getNickname())); + }); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/HrmRecruitChannelController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/HrmRecruitChannelController.java new file mode 100644 index 0000000..d45f827 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/HrmRecruitChannelController.java @@ -0,0 +1,99 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit; + +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.recruit.vo.channel.HrmRecruitChannelDeleteReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.channel.HrmRecruitChannelPageReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.channel.HrmRecruitChannelRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.channel.HrmRecruitChannelSaveReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.channel.HrmRecruitChannelStatusReqVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.recruit.config.HrmRecruitChannelDO; +import cn.iocoder.yudao.module.hrm.service.recruit.config.HrmRecruitChannelService; +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; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; + +@Tag(name = "管理后台 - HRM 招聘渠道") +@RestController +@RequestMapping("/hrm/recruit/channel") +@Validated +public class HrmRecruitChannelController { + + @Resource + private HrmRecruitChannelService recruitChannelService; + + @PostMapping("/create") + @Operation(summary = "创建招聘渠道") + @PreAuthorize("@ss.hasPermission('hrm:recruit:channel:create')") + public CommonResult createRecruitChannel(@Valid @RequestBody HrmRecruitChannelSaveReqVO createReqVO) { + return success(recruitChannelService.createRecruitChannel(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新招聘渠道") + @PreAuthorize("@ss.hasPermission('hrm:recruit:channel:update')") + public CommonResult updateRecruitChannel(@Valid @RequestBody HrmRecruitChannelSaveReqVO updateReqVO) { + recruitChannelService.updateRecruitChannel(updateReqVO); + return success(true); + } + + @PutMapping("/update-status") + @Operation(summary = "更新招聘渠道状态") + @PreAuthorize("@ss.hasPermission('hrm:recruit:channel:update')") + public CommonResult updateRecruitChannelStatus( + @Valid @RequestBody HrmRecruitChannelStatusReqVO statusReqVO) { + recruitChannelService.updateRecruitChannelStatus(statusReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除招聘渠道") + @PreAuthorize("@ss.hasPermission('hrm:recruit:channel:delete')") + public CommonResult deleteRecruitChannel(@Valid @RequestBody HrmRecruitChannelDeleteReqVO deleteReqVO) { + recruitChannelService.deleteRecruitChannel(deleteReqVO); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得招聘渠道") + @Parameter(name = "id", description = "招聘渠道编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:recruit:channel:query')") + public CommonResult getRecruitChannel(@RequestParam("id") Long id) { + HrmRecruitChannelDO recruitChannel = recruitChannelService.getRecruitChannel(id); + return success(BeanUtils.toBean(recruitChannel, HrmRecruitChannelRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得招聘渠道分页") + @PreAuthorize("@ss.hasPermission('hrm:recruit:channel:query')") + public CommonResult> getRecruitChannelPage(@Validated HrmRecruitChannelPageReqVO pageReqVO) { + PageResult pageResult = recruitChannelService.getRecruitChannelPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, HrmRecruitChannelRespVO.class)); + } + + @GetMapping("/simple-list") + @Operation(summary = "获得招聘渠道精简列表", description = "用于招聘职位、候选人等表单的下拉选项") + public CommonResult> getRecruitChannelSimpleList() { + return success(convertList(recruitChannelService.getRecruitChannelSimpleList(), channel -> + new HrmRecruitChannelRespVO().setId(channel.getId()).setName(channel.getName()).setStatus(channel.getStatus()))); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/HrmRecruitConfigController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/HrmRecruitConfigController.java new file mode 100644 index 0000000..f50147d --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/HrmRecruitConfigController.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.config.HrmRecruitEliminateReasonSaveReqVO; +import cn.iocoder.yudao.module.hrm.service.recruit.config.HrmRecruitConfigService; +import io.swagger.v3.oas.annotations.Operation; +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.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +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/recruit/config") +@Validated +public class HrmRecruitConfigController { + + @Resource + private HrmRecruitConfigService recruitConfigService; + + @PostMapping("/eliminate-reason/save") + @Operation(summary = "保存招聘淘汰原因") + @PreAuthorize("@ss.hasPermission('hrm:recruit:config:update')") + public CommonResult saveRecruitEliminateReason( + @Valid @RequestBody HrmRecruitEliminateReasonSaveReqVO saveReqVO) { + recruitConfigService.saveRecruitEliminateReason(saveReqVO); + return success(true); + } + + @GetMapping("/eliminate-reason/list") + @Operation(summary = "获得招聘淘汰原因列表") + @PreAuthorize("@ss.hasPermission('hrm:recruit:config:query') " + + "or @ss.hasPermission('hrm:recruit:candidate:update')") + public CommonResult> getRecruitEliminateReasonList() { + return success(recruitConfigService.getRecruitEliminateReasonList()); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/HrmRecruitInterviewController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/HrmRecruitInterviewController.java new file mode 100644 index 0000000..17257ac --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/HrmRecruitInterviewController.java @@ -0,0 +1,132 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit; + +import cn.hutool.core.collection.CollUtil; +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.recruit.vo.interview.HrmRecruitInterviewRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.interview.HrmRecruitInterviewResultReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.interview.HrmRecruitInterviewSaveReqVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.recruit.candidate.HrmRecruitInterviewDO; +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService; +import cn.iocoder.yudao.module.hrm.service.recruit.candidate.HrmRecruitInterviewService; +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.convertSet; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getFirst; + +@Tag(name = "管理后台 - HRM 招聘面试") +@RestController +@RequestMapping("/hrm/recruit/interview") +@Validated +public class HrmRecruitInterviewController { + + @Resource + private HrmRecruitInterviewService recruitInterviewService; + @Resource + private HrmEmployeeService employeeService; + + @PostMapping("/create") + @Operation(summary = "创建招聘面试") + @PreAuthorize("@ss.hasPermission('hrm:recruit:interview:create')") + public CommonResult createRecruitInterview(@Valid @RequestBody HrmRecruitInterviewSaveReqVO createReqVO) { + return success(recruitInterviewService.createRecruitInterview(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新招聘面试") + @PreAuthorize("@ss.hasPermission('hrm:recruit:interview:update')") + public CommonResult updateRecruitInterview(@Valid @RequestBody HrmRecruitInterviewSaveReqVO updateReqVO) { + recruitInterviewService.updateRecruitInterview(updateReqVO); + return success(true); + } + + @PutMapping("/update-result") + @Operation(summary = "更新招聘面试结果") + @PreAuthorize("@ss.hasPermission('hrm:recruit:interview:update')") + public CommonResult updateRecruitInterviewResult(@Valid @RequestBody HrmRecruitInterviewResultReqVO reqVO) { + recruitInterviewService.updateRecruitInterviewResult(reqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除招聘面试") + @Parameter(name = "id", description = "面试编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:recruit:interview:delete')") + public CommonResult deleteRecruitInterview(@RequestParam("id") Long id) { + recruitInterviewService.deleteRecruitInterview(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得招聘面试") + @Parameter(name = "id", description = "面试编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:recruit:interview:query')") + public CommonResult getRecruitInterview(@RequestParam("id") Long id) { + return success(buildRecruitInterviewRespVO(recruitInterviewService.getRecruitInterview(id))); + } + + @GetMapping("/list-by-candidate") + @Operation(summary = "获得候选人的招聘面试列表") + @Parameter(name = "candidateId", description = "候选人编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:recruit:interview:query')") + public CommonResult> getRecruitInterviewListByCandidate( + @RequestParam("candidateId") Long candidateId) { + return success(buildRecruitInterviewRespVOList( + recruitInterviewService.getRecruitInterviewListByCandidateId(candidateId))); + } + + // ==================== 拼接 VO ==================== + + private HrmRecruitInterviewRespVO buildRecruitInterviewRespVO(HrmRecruitInterviewDO recruitInterview) { + if (recruitInterview == null) { + return null; + } + return getFirst(buildRecruitInterviewRespVOList(Collections.singletonList(recruitInterview))); + } + + private List buildRecruitInterviewRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 1. 获得面试官信息 + Set employeeIds = convertSet(list, HrmRecruitInterviewDO::getInterviewEmployeeId); + employeeIds.addAll(convertSetByFlatMap(list, HrmRecruitInterviewDO::getOtherInterviewEmployeeIds, + Collection::stream)); + Map employeeMap = employeeService.getEmployeeMap(employeeIds); + + // 2. 拼接响应 + return BeanUtils.toBean(list, HrmRecruitInterviewRespVO.class, vo -> { + MapUtils.findAndThen(employeeMap, vo.getInterviewEmployeeId(), + employee -> vo.setInterviewEmployeeName(employee.getName())); + vo.setOtherInterviewEmployeeNames(convertList( + convertList(vo.getOtherInterviewEmployeeIds(), employeeMap::get), + HrmEmployeeDO::getName)); + }); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/HrmRecruitPostController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/HrmRecruitPostController.java new file mode 100644 index 0000000..2251e2a --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/HrmRecruitPostController.java @@ -0,0 +1,181 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +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.ip.core.utils.AreaUtils; +import cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.HrmRecruitStatusCountRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.post.HrmRecruitPostPageReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.post.HrmRecruitPostRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.post.HrmRecruitPostSaveReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.post.HrmRecruitPostStatusReqVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.recruit.post.HrmRecruitPostDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.recruit.post.HrmRecruitPostTypeDO; +import cn.iocoder.yudao.module.hrm.enums.recruit.post.HrmRecruitPostStatusEnum; +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService; +import cn.iocoder.yudao.module.hrm.service.recruit.candidate.HrmRecruitCandidateService; +import cn.iocoder.yudao.module.hrm.service.recruit.post.HrmRecruitPostService; +import cn.iocoder.yudao.module.hrm.service.recruit.post.HrmRecruitPostTypeService; +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.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.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Arrays; +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.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.convertSetByFlatMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getFirst; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - HRM 招聘职位") +@RestController +@RequestMapping("/hrm/recruit/post") +@Validated +public class HrmRecruitPostController { + + @Resource + private HrmRecruitPostService recruitPostService; + @Resource + private HrmRecruitCandidateService recruitCandidateService; + @Resource + private HrmEmployeeService employeeService; + @Resource + private HrmRecruitPostTypeService recruitPostTypeService; + + @Resource + private DeptApi deptApi; + + @PostMapping("/create") + @Operation(summary = "创建招聘职位") + @PreAuthorize("@ss.hasPermission('hrm:recruit:post:create')") + public CommonResult createRecruitPost(@Valid @RequestBody HrmRecruitPostSaveReqVO createReqVO) { + return success(recruitPostService.createRecruitPost(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新招聘职位") + @PreAuthorize("@ss.hasPermission('hrm:recruit:post:update')") + public CommonResult updateRecruitPost(@Valid @RequestBody HrmRecruitPostSaveReqVO updateReqVO) { + recruitPostService.updateRecruitPost(updateReqVO); + return success(true); + } + + @PutMapping("/update-status") + @Operation(summary = "更新招聘职位状态") + @PreAuthorize("@ss.hasPermission('hrm:recruit:post:update')") + public CommonResult updateRecruitPostStatus(@Valid @RequestBody HrmRecruitPostStatusReqVO reqVO) { + recruitPostService.updateRecruitPostStatus(reqVO); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得招聘职位") + @Parameter(name = "id", description = "招聘职位编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:recruit:post:query')") + public CommonResult getRecruitPost(@RequestParam("id") Long id) { + return success(buildRecruitPostRespVO(recruitPostService.getRecruitPost(id))); + } + + @GetMapping("/page") + @Operation(summary = "获得招聘职位分页") + @PreAuthorize("@ss.hasPermission('hrm:recruit:post:query')") + public CommonResult> getRecruitPostPage( + @Validated HrmRecruitPostPageReqVO pageReqVO) { + PageResult pageResult = recruitPostService.getRecruitPostPage(pageReqVO); + return success(new PageResult<>(buildRecruitPostRespVOList(pageResult.getList()), pageResult.getTotal())); + } + + @GetMapping("/simple-list") + @Operation(summary = "获得招聘职位精简列表") + @PreAuthorize("@ss.hasPermission('hrm:recruit:post:query')") + public CommonResult> getRecruitPostSimpleList() { + return success(convertList(recruitPostService.getRecruitPostSimpleList(), post -> + new HrmRecruitPostRespVO().setId(post.getId()).setPostName(post.getPostName()) + .setStatus(post.getStatus()))); + } + + @GetMapping("/status-count") + @Operation(summary = "获得招聘职位状态数量") + @PreAuthorize("@ss.hasPermission('hrm:recruit:post:query')") + public CommonResult> getRecruitPostStatusCount( + @Validated HrmRecruitPostPageReqVO pageReqVO) { + Map countMap = recruitPostService.getRecruitPostStatusCount(pageReqVO); + return success(convertList(Arrays.asList(HrmRecruitPostStatusEnum.values()), + status -> new HrmRecruitStatusCountRespVO(status.getStatus(), + countMap.getOrDefault(status.getStatus(), 0L)))); + } + + // ==================== 拼接 VO ==================== + + private HrmRecruitPostRespVO buildRecruitPostRespVO(HrmRecruitPostDO recruitPost) { + if (recruitPost == null) { + return null; + } + return getFirst(buildRecruitPostRespVOList(Collections.singletonList(recruitPost))); + } + + private List buildRecruitPostRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 1.1 员工信息 + Set employeeIds = convertSet(list, HrmRecruitPostDO::getOwnerEmployeeId); + employeeIds.addAll(convertSetByFlatMap(list, HrmRecruitPostDO::getInterviewEmployeeIds, + Collection::stream)); + Map employeeMap = employeeService.getEmployeeMap(employeeIds); + // 1.2 部门信息 + Map deptMap = deptApi.getDeptMap(convertSet(list, HrmRecruitPostDO::getDeptId)); + // 1.3 职位类型信息 + Map postTypeMap = recruitPostTypeService.getRecruitPostTypeMap( + convertSet(list, HrmRecruitPostDO::getPostTypeId)); + // 1.4 已入职人数 + Map joinedCountMap = recruitCandidateService.getJoinedCandidateCountMap( + convertSet(list, HrmRecruitPostDO::getId)); + // 2. 拼接响应 + return BeanUtils.toBean(list, HrmRecruitPostRespVO.class, vo -> { + vo.setAreaName(AreaUtils.format(vo.getAreaId())); + MapUtils.findAndThen(deptMap, vo.getDeptId(), dept -> vo.setDeptName(dept.getName())); + MapUtils.findAndThen(employeeMap, vo.getOwnerEmployeeId(), + employee -> vo.setOwnerEmployeeName(employee.getName())); + vo.setInterviewEmployeeNames(convertList(vo.getInterviewEmployeeIds(), + employeeId -> employeeMap.containsKey(employeeId) + ? employeeMap.get(employeeId).getName() : null)); + MapUtils.findAndThen(postTypeMap, vo.getPostTypeId(), + postType -> vo.setPostTypeName(postType.getName())); + fillRecruitPostProgress(vo, joinedCountMap.getOrDefault(vo.getId(), 0L)); + }); + } + + private void fillRecruitPostProgress(HrmRecruitPostRespVO recruitPost, Long hasEntryNum) { + recruitPost.setHasEntryNum(hasEntryNum); + if (recruitPost.getRecruitNum() != null && recruitPost.getRecruitNum() != 0) { + recruitPost.setRecruitSchedule(BigDecimal.valueOf(hasEntryNum).multiply(BigDecimal.valueOf(100)) + .divide(BigDecimal.valueOf(recruitPost.getRecruitNum()), 2, RoundingMode.HALF_UP)); + } + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/HrmRecruitPostTypeController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/HrmRecruitPostTypeController.java new file mode 100644 index 0000000..bd1ad14 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/HrmRecruitPostTypeController.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit; + +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.recruit.vo.post.type.HrmRecruitPostTypeRespVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.recruit.post.HrmRecruitPostTypeDO; +import cn.iocoder.yudao.module.hrm.service.recruit.post.HrmRecruitPostTypeService; +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 static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - HRM 招聘职位类型") +@RestController +@RequestMapping("/hrm/recruit/post-type") +@Validated +public class HrmRecruitPostTypeController { + + @Resource + private HrmRecruitPostTypeService recruitPostTypeService; + + @GetMapping("/list") + @Operation(summary = "获得招聘职位类型列表") + @Parameter(name = "status", description = "状态", example = "0") + @PreAuthorize("@ss.hasPermission('hrm:recruit:post:query')") + public CommonResult> getRecruitPostTypeList(@RequestParam(value = "status", required = false) Integer status) { + List postTypes = recruitPostTypeService.getRecruitPostTypeList(status); + return success(BeanUtils.toBean(postTypes, HrmRecruitPostTypeRespVO.class)); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/HrmRecruitStatusCountRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/HrmRecruitStatusCountRespVO.java new file mode 100644 index 0000000..52f714d --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/HrmRecruitStatusCountRespVO.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Schema(description = "管理后台 - HRM 招聘状态数量 Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class HrmRecruitStatusCountRespVO { + + @Schema(description = "状态", example = "1") + private Integer status; + + @Schema(description = "数量", example = "12") + private Long count; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidateCleanReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidateCleanReqVO.java new file mode 100644 index 0000000..4ef3fcd --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidateCleanReqVO.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.candidate; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.hrm.enums.recruit.candidate.HrmRecruitCandidateStatusEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - HRM 招聘候选人清理 Request VO") +@Data +public class HrmRecruitCandidateCleanReqVO { + + @Schema(description = "候选人状态数组", requiredMode = Schema.RequiredMode.REQUIRED, + example = "[1, 2, 3, 4]") + @NotEmpty(message = "候选人状态不能为空") + private List<@NotNull(message = "候选人状态不能为空") + @InEnum(value = HrmRecruitCandidateStatusEnum.class, message = "候选人状态必须是 {value}") Integer> statuses; + + @Schema(description = "状态持续天数", requiredMode = Schema.RequiredMode.REQUIRED, example = "30") + @NotNull(message = "状态持续天数不能为空") + @Positive(message = "状态持续天数必须大于 0") + private Integer days; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidateEntryReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidateEntryReqVO.java new file mode 100644 index 0000000..dc2a7cc --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidateEntryReqVO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.candidate; + +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeSaveReqVO; +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; + +@Schema(description = "管理后台 - HRM 招聘候选人转员工 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class HrmRecruitCandidateEntryReqVO extends HrmEmployeeSaveReqVO { + + @AssertTrue(message = "候选人编号不能为空") + @JsonIgnore + public boolean isCandidateIdValid() { + return getCandidateId() != null; + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidatePageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidatePageReqVO.java new file mode 100644 index 0000000..f84f00b --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidatePageReqVO.java @@ -0,0 +1,74 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.candidate; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.hrm.enums.recruit.candidate.HrmRecruitCandidateStatusEnum; +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 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 HrmRecruitCandidatePageReqVO extends PageParam { + + @Schema(description = "姓名、手机号码或邮箱,模糊匹配", example = "张三") + private String search; + + @Schema(description = "职位编号", example = "1024") + private Long postId; + + @Schema(description = "招聘负责人员工编号", example = "1") + private Long ownerEmployeeId; + + @Schema(description = "性别", example = "1") + private Integer sex; + + @Schema(description = "最小年龄", example = "20") + private Integer minAge; + + @Schema(description = "最大年龄", example = "35") + private Integer maxAge; + + @Schema(description = "最小工作年限", example = "1") + private Integer minWorkTime; + + @Schema(description = "最大工作年限", example = "5") + private Integer maxWorkTime; + + @Schema(description = "学历", example = "5") + private Integer education; + + @Schema(description = "毕业院校,模糊匹配", example = "浙江大学") + private String graduateSchool; + + @Schema(description = "最近工作单位,模糊匹配", example = "某科技公司") + private String latestWorkPlace; + + @Schema(description = "渠道编号", example = "1") + private Long channelId; + + @Schema(description = "面试官员工编号", example = "1") + private Long interviewEmployeeId; + + @Schema(description = "面试时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] interviewTime; + + @Schema(description = "创建人用户编号", example = "1") + private Long creator; + + @Schema(description = "候选人状态", example = "1") + @InEnum(value = HrmRecruitCandidateStatusEnum.class, message = "候选人状态必须是 {value}") + private Integer status; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidateRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidateRespVO.java new file mode 100644 index 0000000..16f22fb --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidateRespVO.java @@ -0,0 +1,135 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.candidate; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - HRM 招聘候选人 Response VO") +@Data +@Accessors(chain = true) +public class HrmRecruitCandidateRespVO { + + @Schema(description = "候选人编号", example = "1024") + private Long id; + + @Schema(description = "候选人姓名", example = "张三") + private String name; + + @Schema(description = "手机号码", example = "15601691399") + private String mobile; + + @Schema(description = "性别", example = "1") + private Integer sex; + + @Schema(description = "年龄", example = "28") + private Integer age; + + @Schema(description = "邮箱", example = "candidate@example.com") + private String email; + + @Schema(description = "应聘职位编号", example = "1024") + private Long postId; + + @Schema(description = "应聘职位名称", example = "Java 开发工程师") + private String postName; + + @Schema(description = "应聘职位状态", example = "1") + private Integer postStatus; + + @Schema(description = "用人部门编号", example = "100") + private Long deptId; + + @Schema(description = "用人部门名称", example = "研发部") + private String deptName; + + @Schema(description = "招聘负责人员工编号", example = "1") + private Long ownerEmployeeId; + + @Schema(description = "招聘负责人姓名", example = "李四") + private String ownerEmployeeName; + + @Schema(description = "面试轮次", example = "1") + private Integer stageNumber; + + @Schema(description = "工作年限", example = "5") + private Integer workTime; + + @Schema(description = "学历", example = "5") + private Integer education; + + @Schema(description = "毕业院校", example = "浙江大学") + private String graduateSchool; + + @Schema(description = "最近工作单位", example = "某科技公司") + private String latestWorkPlace; + + @Schema(description = "招聘渠道编号", example = "1") + private Long channelId; + + @Schema(description = "招聘渠道名称", example = "BOSS 直聘") + private String channelName; + + @Schema(description = "备注", example = "沟通意向强") + private String remark; + + @Schema(description = "候选人状态", example = "1") + private Integer status; + + @Schema(description = "淘汰原因", example = "技能不匹配") + private String eliminate; + + @Schema(description = "状态更新时间") + private LocalDateTime statusUpdateTime; + + @Schema(description = "入职时间") + private LocalDateTime entryTime; + + @Schema(description = "简历附件地址数组", example = "[\"https://example.com/resume.pdf\"]") + private List resumeUrls; + + @Schema(description = "当前面试编号", example = "1024") + private Long interviewId; + + @Schema(description = "面试方式", example = "3") + private Integer interviewType; + + @Schema(description = "主面试官员工编号", example = "1") + private Long interviewEmployeeId; + + @Schema(description = "主面试官姓名", example = "王五") + private String interviewEmployeeName; + + @Schema(description = "其他面试官员工编号数组", example = "[2, 3]") + private List otherInterviewEmployeeIds; + + @Schema(description = "其他面试官姓名数组", example = "[\"赵六\", \"钱七\"]") + private List otherInterviewEmployeeNames; + + @Schema(description = "面试时间") + private LocalDateTime interviewTime; + + @Schema(description = "面试地址", example = "上海市浦东新区") + private String interviewAddress; + + @Schema(description = "面试结果", example = "1") + private Integer interviewResult; + + @Schema(description = "转入的员工编号", example = "100") + private Long employeeId; + + @Schema(description = "创建人", example = "1") + private String creator; + + @Schema(description = "创建人名称", example = "管理员") + private String creatorName; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + + @Schema(description = "更新时间") + private LocalDateTime updateTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidateSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidateSaveReqVO.java new file mode 100644 index 0000000..796f19c --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidateSaveReqVO.java @@ -0,0 +1,93 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.candidate; + +import com.mzt.logapi.starter.annotation.DiffLogField; +import io.swagger.v3.oas.annotations.media.Schema; +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.Pattern; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - HRM 招聘候选人新增/修改 Request VO") +@Data +public class HrmRecruitCandidateSaveReqVO { + + @Schema(description = "候选人编号", example = "1024") + private Long id; + + @Schema(description = "候选人姓名", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三") + @NotBlank(message = "候选人姓名不能为空") + @Size(max = 255, message = "候选人姓名长度不能超过 255 个字符") + @DiffLogField(name = "候选人姓名") + private String name; + + @Schema(description = "手机号码", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691399") + @NotBlank(message = "手机号码不能为空") + @Size(max = 18, message = "手机号码长度不能超过 18 个字符") + @Pattern(regexp = "^(\\+?0?\\d{2,4}-?)?\\d{6,11}$", message = "手机号码格式不正确") + @DiffLogField(name = "手机号码") + private String mobile; + + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "性别不能为空") + @DiffLogField(name = "性别") + private Integer sex; + + @Schema(description = "年龄", example = "28") + @Min(value = 0, message = "年龄不能小于 0") + @Max(value = 99, message = "年龄不能大于 99") + @DiffLogField(name = "年龄") + private Integer age; + + @Schema(description = "邮箱", example = "candidate@example.com") + @Email(message = "邮箱格式不正确") + @Size(max = 255, message = "邮箱长度不能超过 255 个字符") + @DiffLogField(name = "邮箱") + private String email; + + @Schema(description = "应聘职位编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "应聘职位不能为空") + @DiffLogField(name = "应聘职位") + private Long postId; + + @Schema(description = "工作年限", example = "5") + @Min(value = 0, message = "工作年限不能小于 0") + @Max(value = 60, message = "工作年限不能大于 60") + @DiffLogField(name = "工作年限") + private Integer workTime; + + @Schema(description = "学历", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") + @NotNull(message = "学历不能为空") + @DiffLogField(name = "学历") + private Integer education; + + @Schema(description = "毕业院校", example = "浙江大学") + @Size(max = 255, message = "毕业院校长度不能超过 255 个字符") + @DiffLogField(name = "毕业院校") + private String graduateSchool; + + @Schema(description = "最近工作单位", example = "某科技公司") + @Size(max = 255, message = "最近工作单位长度不能超过 255 个字符") + @DiffLogField(name = "最近工作单位") + private String latestWorkPlace; + + @Schema(description = "招聘渠道编号", example = "1") + @DiffLogField(name = "招聘渠道") + private Long channelId; + + @Schema(description = "备注", example = "沟通意向强") + @Size(max = 255, message = "备注长度不能超过 255 个字符") + @DiffLogField(name = "备注") + private String remark; + + @Schema(description = "简历附件地址数组", example = "[\"https://example.com/resume.pdf\"]") + @Size(max = 5, message = "简历附件不能超过 5 个") + @DiffLogField(name = "简历附件") + private List<@Size(max = 512, message = "简历附件地址长度不能超过 512 个字符") String> resumeUrls; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidateUpdateChannelReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidateUpdateChannelReqVO.java new file mode 100644 index 0000000..9b1443c --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidateUpdateChannelReqVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.candidate; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - HRM 招聘候选人渠道修改 Request VO") +@Data +public class HrmRecruitCandidateUpdateChannelReqVO { + + @Schema(description = "候选人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "候选人编号不能为空") + private Long id; + + @Schema(description = "招聘渠道编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "招聘渠道编号不能为空") + private Long channelId; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidateUpdateEliminateReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidateUpdateEliminateReqVO.java new file mode 100644 index 0000000..e1da481 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidateUpdateEliminateReqVO.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.candidate; + +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 HrmRecruitCandidateUpdateEliminateReqVO { + + @Schema(description = "候选人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "候选人编号不能为空") + private Long id; + + @Schema(description = "淘汰原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "技能不匹配") + @NotBlank(message = "淘汰原因不能为空") + @Size(max = 255, message = "淘汰原因长度不能超过 255 个字符") + private String eliminate; + + @Schema(description = "备注", example = "二面未通过") + @Size(max = 255, message = "备注长度不能超过 255 个字符") + private String remark; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidateUpdatePostReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidateUpdatePostReqVO.java new file mode 100644 index 0000000..d756236 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidateUpdatePostReqVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.candidate; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - HRM 招聘候选人职位修改 Request VO") +@Data +public class HrmRecruitCandidateUpdatePostReqVO { + + @Schema(description = "候选人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "候选人编号不能为空") + private Long id; + + @Schema(description = "职位编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + @NotNull(message = "职位编号不能为空") + private Long postId; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidateUpdateStatusReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidateUpdateStatusReqVO.java new file mode 100644 index 0000000..850b84e --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/candidate/HrmRecruitCandidateUpdateStatusReqVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.candidate; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.hrm.enums.recruit.candidate.HrmRecruitCandidateStatusEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - HRM 招聘候选人状态修改 Request VO") +@Data +public class HrmRecruitCandidateUpdateStatusReqVO { + + @Schema(description = "候选人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "候选人编号不能为空") + private Long id; + + @Schema(description = "候选人状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @NotNull(message = "候选人状态不能为空") + @InEnum(value = HrmRecruitCandidateStatusEnum.class, message = "候选人状态必须是 {value}") + private Integer status; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/channel/HrmRecruitChannelDeleteReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/channel/HrmRecruitChannelDeleteReqVO.java new file mode 100644 index 0000000..888380a --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/channel/HrmRecruitChannelDeleteReqVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.channel; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - HRM 招聘渠道删除 Request VO") +@Data +public class HrmRecruitChannelDeleteReqVO { + + @Schema(description = "待删除招聘渠道编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "待删除招聘渠道编号不能为空") + private Long id; + + @Schema(description = "承接招聘渠道编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1025") + @NotNull(message = "承接招聘渠道编号不能为空") + private Long transferChannelId; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/channel/HrmRecruitChannelPageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/channel/HrmRecruitChannelPageReqVO.java new file mode 100644 index 0000000..a6e1f8d --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/channel/HrmRecruitChannelPageReqVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.channel; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Schema(description = "管理后台 - HRM 招聘渠道分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class HrmRecruitChannelPageReqVO extends PageParam { + + @Schema(description = "渠道名称,模糊匹配", example = "BOSS") + private String name; + + @Schema(description = "状态", example = "0") + private Integer status; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/channel/HrmRecruitChannelRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/channel/HrmRecruitChannelRespVO.java new file mode 100644 index 0000000..ccbd668 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/channel/HrmRecruitChannelRespVO.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.channel; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - HRM 招聘渠道 Response VO") +@Data +public class HrmRecruitChannelRespVO { + + @Schema(description = "招聘渠道编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "是否系统内置", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + private Boolean systemFlag; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + private Integer status; + + @Schema(description = "渠道名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "BOSS 直聘") + private String name; + + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer sort; + + @Schema(description = "备注", example = "常用渠道") + private String remark; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/channel/HrmRecruitChannelSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/channel/HrmRecruitChannelSaveReqVO.java new file mode 100644 index 0000000..baadd03 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/channel/HrmRecruitChannelSaveReqVO.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.channel; + +import com.mzt.logapi.starter.annotation.DiffLogField; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Min; +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 HrmRecruitChannelSaveReqVO { + + @Schema(description = "招聘渠道编号", example = "1024") + private Long id; + + @Schema(description = "渠道名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "BOSS 直聘") + @NotBlank(message = "渠道名称不能为空") + @Size(max = 255, message = "渠道名称不能超过 255 个字符") + @DiffLogField(name = "渠道名称") + private String name; + + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @NotNull(message = "显示顺序不能为空") + @Min(value = 0, message = "显示顺序不能小于 0") + @DiffLogField(name = "显示顺序") + private Integer sort; + + @Schema(description = "备注", example = "常用渠道") + @Size(max = 500, message = "备注不能超过 500 个字符") + @DiffLogField(name = "备注") + private String remark; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/channel/HrmRecruitChannelStatusReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/channel/HrmRecruitChannelStatusReqVO.java new file mode 100644 index 0000000..e09122c --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/channel/HrmRecruitChannelStatusReqVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.channel; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - HRM 招聘渠道状态修改 Request VO") +@Data +public class HrmRecruitChannelStatusReqVO { + + @Schema(description = "招聘渠道编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "招聘渠道编号不能为空") + private Long id; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + @NotNull(message = "状态不能为空") + @InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}") + private Integer status; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/config/HrmRecruitEliminateReasonSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/config/HrmRecruitEliminateReasonSaveReqVO.java new file mode 100644 index 0000000..dfc0dc5 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/config/HrmRecruitEliminateReasonSaveReqVO.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit.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 lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - HRM 招聘淘汰原因保存 Request VO") +@Data +public class HrmRecruitEliminateReasonSaveReqVO { + + @Schema(description = "淘汰原因列表", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "淘汰原因不能为空") + @DiffLogField(name = "淘汰原因") + private List<@NotBlank(message = "淘汰原因不能为空") String> reasons; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/interview/HrmRecruitInterviewRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/interview/HrmRecruitInterviewRespVO.java new file mode 100644 index 0000000..7cbb6c3 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/interview/HrmRecruitInterviewRespVO.java @@ -0,0 +1,58 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.interview; + +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 HrmRecruitInterviewRespVO { + + @Schema(description = "面试编号", example = "1024") + private Long id; + + @Schema(description = "候选人编号", example = "1024") + private Long candidateId; + + @Schema(description = "面试方式", example = "1") + private Integer type; + + @Schema(description = "面试轮次", example = "1") + private Integer stageNumber; + + @Schema(description = "主面试官员工编号", example = "1") + private Long interviewEmployeeId; + + @Schema(description = "主面试官姓名", example = "张三") + private String interviewEmployeeName; + + @Schema(description = "其他面试官员工编号数组", example = "[2, 3]") + private List otherInterviewEmployeeIds; + + @Schema(description = "其他面试官姓名数组", example = "[\"李四\", \"王五\"]") + private List otherInterviewEmployeeNames; + + @Schema(description = "面试时间") + private LocalDateTime interviewTime; + + @Schema(description = "面试地址", example = "上海会议室 A") + private String address; + + @Schema(description = "备注", example = "请带作品集") + private String remark; + + @Schema(description = "面试结果", example = "1") + private Integer result; + + @Schema(description = "评价") + private String evaluate; + + @Schema(description = "取消原因") + private String cancelReason; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/interview/HrmRecruitInterviewResultReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/interview/HrmRecruitInterviewResultReqVO.java new file mode 100644 index 0000000..90d4b10 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/interview/HrmRecruitInterviewResultReqVO.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.interview; + +import cn.iocoder.yudao.module.hrm.enums.recruit.candidate.HrmRecruitInterviewResultEnum; +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 static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.equalsAny; + +@Schema(description = "管理后台 - HRM 招聘面试结果 Request VO") +@Data +public class HrmRecruitInterviewResultReqVO { + + @Schema(description = "面试编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "面试编号不能为空") + private Long id; + + @Schema(description = "面试结果", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @NotNull(message = "面试结果不能为空") + private Integer result; + + @Schema(description = "评价", example = "基础扎实") + @Size(max = 255, message = "评价长度不能超过 255 个字符") + private String evaluate; + + @Schema(description = "取消原因", example = "候选人临时改期") + @Size(max = 255, message = "取消原因长度不能超过 255 个字符") + private String cancelReason; + + @AssertTrue(message = "面试结果必须是通过、未通过或取消") + @JsonIgnore + public boolean isResultValid() { + return equalsAny(result, null, HrmRecruitInterviewResultEnum.PASS.getResult(), + HrmRecruitInterviewResultEnum.NOT_PASS.getResult(), HrmRecruitInterviewResultEnum.CANCEL.getResult()); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/interview/HrmRecruitInterviewSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/interview/HrmRecruitInterviewSaveReqVO.java new file mode 100644 index 0000000..947c639 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/interview/HrmRecruitInterviewSaveReqVO.java @@ -0,0 +1,59 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.interview; + +import com.mzt.logapi.starter.annotation.DiffLogField; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +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 HrmRecruitInterviewSaveReqVO { + + @Schema(description = "面试编号", example = "1024") + private Long id; + + @Schema(description = "候选人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "候选人编号不能为空") + private Long candidateId; + + @Schema(description = "面试方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "面试方式不能为空") + @DiffLogField(name = "面试方式") + private Integer type; + + @Schema(description = "面试轮次", example = "1") + private Integer stageNumber; + + @Schema(description = "主面试官员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "主面试官不能为空") + @DiffLogField(name = "主面试官") + private Long interviewEmployeeId; + + @Schema(description = "其他面试官员工编号数组", example = "[2, 3]") + @DiffLogField(name = "其他面试官") + private List otherInterviewEmployeeIds; + + @Schema(description = "面试时间", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "面试时间不能为空") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @DiffLogField(name = "面试时间") + private LocalDateTime interviewTime; + + @Schema(description = "面试地址", example = "上海会议室 A") + @Size(max = 255, message = "面试地址长度不能超过 255 个字符") + @DiffLogField(name = "面试地址") + private String address; + + @Schema(description = "备注", example = "请带作品集") + @Size(max = 255, message = "备注长度不能超过 255 个字符") + @DiffLogField(name = "备注") + private String remark; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/post/HrmRecruitPostPageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/post/HrmRecruitPostPageReqVO.java new file mode 100644 index 0000000..70839dc --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/post/HrmRecruitPostPageReqVO.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.post; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +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 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 HrmRecruitPostPageReqVO extends PageParam { + + @Schema(description = "职位名称,模糊匹配", example = "Java") + private String postName; + + @Schema(description = "工作性质", example = "1") + private Integer jobNature; + + @Schema(description = "工作城市地区编号", example = "310115") + private Integer areaId; + + @Schema(description = "部门编号", example = "100") + private Long deptId; + + @Schema(description = "招聘负责人员工编号", example = "200") + private Long ownerEmployeeId; + + @Schema(description = "职位类型编号", example = "100") + private Long postTypeId; + + @Schema(description = "职位状态", example = "1") + private Integer status; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/post/HrmRecruitPostRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/post/HrmRecruitPostRespVO.java new file mode 100644 index 0000000..fbe016b --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/post/HrmRecruitPostRespVO.java @@ -0,0 +1,104 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.post; + +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 +public class HrmRecruitPostRespVO { + + @Schema(description = "招聘职位编号", example = "1024") + private Long id; + + @Schema(description = "职位名称", example = "Java 开发工程师") + private String postName; + + @Schema(description = "部门编号", example = "100") + private Long deptId; + + @Schema(description = "用人部门名称", example = "研发部") + private String deptName; + + @Schema(description = "工作性质", example = "1") + private Integer jobNature; + + @Schema(description = "工作城市地区编号", example = "310115") + private Integer areaId; + + @Schema(description = "工作城市地区名称", example = "上海 上海市 浦东新区") + private String areaName; + + @Schema(description = "招聘人数", example = "3") + private Integer recruitNum; + + @Schema(description = "招聘原因", example = "团队扩编") + private String reason; + + @Schema(description = "工作经验", example = "3") + private Integer workTime; + + @Schema(description = "学历要求", example = "4") + private Integer educationRequire; + + @Schema(description = "最低薪资", example = "20000") + private BigDecimal minSalary; + + @Schema(description = "最高薪资", example = "30000") + private BigDecimal maxSalary; + + @Schema(description = "薪资单位", example = "2") + private Integer salaryUnit; + + @Schema(description = "最小年龄", example = "22") + private Integer minAge; + + @Schema(description = "最大年龄", example = "35") + private Integer maxAge; + + @Schema(description = "最迟到岗时间") + private LocalDateTime latestEntryTime; + + @Schema(description = "招聘负责人员工编号", example = "1") + private Long ownerEmployeeId; + + @Schema(description = "招聘负责人姓名", example = "张三") + private String ownerEmployeeName; + + @Schema(description = "面试官员工编号数组", example = "[1, 2]") + private List interviewEmployeeIds; + + @Schema(description = "面试官姓名数组", example = "[\"张三\", \"李四\"]") + private List interviewEmployeeNames; + + @Schema(description = "职位描述") + private String description; + + @Schema(description = "紧急程度", example = "1") + private Integer emergencyLevel; + + @Schema(description = "职位类型编号", example = "100") + private Long postTypeId; + + @Schema(description = "职位类型名称", example = "研发") + private String postTypeName; + + @Schema(description = "职位状态", example = "1") + private Integer status; + + @Schema(description = "停止原因", example = "岗位暂停") + private String stopReason; + + @Schema(description = "已入职人数", example = "2") + private Long hasEntryNum; + + @Schema(description = "招聘进度百分比", example = "40.00") + private BigDecimal recruitSchedule; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/post/HrmRecruitPostSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/post/HrmRecruitPostSaveReqVO.java new file mode 100644 index 0000000..910e5ff --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/post/HrmRecruitPostSaveReqVO.java @@ -0,0 +1,144 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.post; + +import cn.iocoder.yudao.module.system.framework.operatelog.core.AreaParseFunction; +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.Digits; +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.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.module.hrm.dal.dataobject.recruit.post.HrmRecruitPostDO.AGE_UNLIMITED_VALUE; +import static cn.iocoder.yudao.module.hrm.dal.dataobject.recruit.post.HrmRecruitPostDO.SALARY_NEGOTIABLE_UNIT_VALUE; +import static cn.iocoder.yudao.module.hrm.dal.dataobject.recruit.post.HrmRecruitPostDO.SALARY_NEGOTIABLE_VALUE; + +@Schema(description = "管理后台 - HRM 招聘职位新增/修改 Request VO") +@Data +public class HrmRecruitPostSaveReqVO { + + @Schema(description = "招聘职位编号", example = "1024") + private Long id; + + @Schema(description = "职位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发工程师") + @NotBlank(message = "职位名称不能为空") + @Size(max = 255, message = "职位名称不能超过 255 个字符") + @DiffLogField(name = "职位名称") + private String postName; + + @Schema(description = "部门编号", example = "100") + @DiffLogField(name = "部门编号") + private Long deptId; + + @Schema(description = "工作性质", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "工作性质不能为空") + @DiffLogField(name = "工作性质") + private Integer jobNature; + + @Schema(description = "工作城市地区编号", example = "310115") + @DiffLogField(name = "工作城市", function = AreaParseFunction.NAME) + private Integer areaId; + + @Schema(description = "招聘人数", example = "3") + @Min(value = 0, message = "招聘人数不能小于 0") + @DiffLogField(name = "招聘人数") + private Integer recruitNum; + + @Schema(description = "招聘原因", example = "团队扩编") + @Size(max = 255, message = "招聘原因不能超过 255 个字符") + @DiffLogField(name = "招聘原因") + private String reason; + + @Schema(description = "工作经验", example = "3") + @DiffLogField(name = "工作经验") + private Integer workTime; + + @Schema(description = "学历要求", example = "4") + @DiffLogField(name = "学历要求") + private Integer educationRequire; + + @Schema(description = "最低薪资", example = "20000") + @Digits(integer = 8, fraction = 2, message = "最低薪资最多 8 位整数和 2 位小数") + @DiffLogField(name = "最低薪资") + private BigDecimal minSalary; + + @Schema(description = "最高薪资", example = "30000") + @Digits(integer = 8, fraction = 2, message = "最高薪资最多 8 位整数和 2 位小数") + @DiffLogField(name = "最高薪资") + private BigDecimal maxSalary; + + @Schema(description = "薪资单位", example = "2") + @DiffLogField(name = "薪资单位") + private Integer salaryUnit; + + @Schema(description = "最小年龄", example = "22") + @DiffLogField(name = "最小年龄") + private Integer minAge; + + @Schema(description = "最大年龄", example = "35") + @DiffLogField(name = "最大年龄") + private Integer maxAge; + + @Schema(description = "最迟到岗时间") + @DiffLogField(name = "最迟到岗时间") + private LocalDateTime latestEntryTime; + + @Schema(description = "负责人员工编号", example = "1") + @DiffLogField(name = "负责人") + private Long ownerEmployeeId; + + @Schema(description = "面试官员工编号数组", example = "[1, 2]") + @DiffLogField(name = "面试官") + private List interviewEmployeeIds; + + @Schema(description = "职位描述", example = "负责核心业务系统设计与开发") + @Size(max = 4000, message = "职位描述不能超过 4000 个字符") + @DiffLogField(name = "职位描述") + private String description; + + @Schema(description = "紧急程度", example = "1") + @DiffLogField(name = "紧急程度") + private Integer emergencyLevel; + + @Schema(description = "职位类型编号", example = "100") + @DiffLogField(name = "职位类型编号") + private Long postTypeId; + + @AssertTrue(message = "薪资范围不正确") + @JsonIgnore + public boolean isSalaryRangeValid() { + boolean hasNegotiableValue = SALARY_NEGOTIABLE_VALUE.equals(getMinSalary()) + || SALARY_NEGOTIABLE_VALUE.equals(getMaxSalary()) || SALARY_NEGOTIABLE_UNIT_VALUE.equals(getSalaryUnit()); + if (hasNegotiableValue) { + return SALARY_NEGOTIABLE_VALUE.equals(getMinSalary()) + && SALARY_NEGOTIABLE_VALUE.equals(getMaxSalary()) + && SALARY_NEGOTIABLE_UNIT_VALUE.equals(getSalaryUnit()); + } + if ((getMinSalary() != null && getMinSalary().signum() < 0) + || (getMaxSalary() != null && getMaxSalary().signum() < 0)) { + return false; + } + return getMinSalary() == null || getMaxSalary() == null || getMinSalary().compareTo(getMaxSalary()) <= 0; + } + + @AssertTrue(message = "年龄范围不正确") + @JsonIgnore + public boolean isAgeRangeValid() { + boolean hasUnlimitedValue = AGE_UNLIMITED_VALUE.equals(getMinAge()) || AGE_UNLIMITED_VALUE.equals(getMaxAge()); + if (hasUnlimitedValue) { + return AGE_UNLIMITED_VALUE.equals(getMinAge()) && AGE_UNLIMITED_VALUE.equals(getMaxAge()); + } + if ((getMinAge() != null && getMinAge() < 0) || (getMaxAge() != null && getMaxAge() < 0)) { + return false; + } + return getMinAge() == null || getMaxAge() == null || getMinAge() <= getMaxAge(); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/post/HrmRecruitPostStatusReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/post/HrmRecruitPostStatusReqVO.java new file mode 100644 index 0000000..2eab16b --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/post/HrmRecruitPostStatusReqVO.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.post; + +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.hrm.enums.recruit.post.HrmRecruitPostStatusEnum; +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; + +@Schema(description = "管理后台 - HRM 招聘职位状态修改 Request VO") +@Data +public class HrmRecruitPostStatusReqVO { + + @Schema(description = "招聘职位编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "招聘职位编号不能为空") + private Long id; + + @Schema(description = "职位状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + @NotNull(message = "职位状态不能为空") + @InEnum(value = HrmRecruitPostStatusEnum.class, message = "职位状态必须是 {value}") + private Integer status; + + @Schema(description = "停止原因", example = "岗位暂停") + @Size(max = 255, message = "停止原因不能超过 255 个字符") + private String stopReason; + + @AssertTrue(message = "停止招聘时,停止原因不能为空") + @JsonIgnore + public boolean isStopReasonValid() { + return ObjUtil.notEqual(HrmRecruitPostStatusEnum.STOPPED.getStatus(), status) + || StrUtil.isNotBlank(stopReason); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/post/type/HrmRecruitPostTypeRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/post/type/HrmRecruitPostTypeRespVO.java new file mode 100644 index 0000000..f2482f2 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/recruit/vo/post/type/HrmRecruitPostTypeRespVO.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.recruit.vo.post.type; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - HRM 招聘职位类型 Response VO") +@Data +public class HrmRecruitPostTypeRespVO { + + @Schema(description = "职位类型编号", example = "1024") + private Long id; + + @Schema(description = "类型名称", example = "后端开发") + private String name; + + @Schema(description = "父类型编号", example = "0") + private Long parentId; + + @Schema(description = "排序", example = "10") + private Integer sort; + + @Schema(description = "状态,参见 common_status", example = "0") + private Integer status; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryChangeRecordController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryChangeRecordController.java new file mode 100644 index 0000000..f8f7fcc --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryChangeRecordController.java @@ -0,0 +1,89 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary; + +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.salary.vo.changerecord.HrmSalaryChangeRecordRespVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.employee.HrmSalaryChangeRecordDO; +import cn.iocoder.yudao.module.hrm.enums.salary.employee.HrmSalaryChangeRecordTypeEnum; +import cn.iocoder.yudao.module.hrm.service.salary.employee.HrmSalaryChangeRecordService; +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.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PutMapping; +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; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; + +@Tag(name = "管理后台 - HRM 定薪/调薪记录") +@RestController +@RequestMapping("/hrm/salary/change-record") +@Validated +public class HrmSalaryChangeRecordController { + + @Resource + private HrmSalaryChangeRecordService salaryChangeRecordService; + + @GetMapping("/get") + @Operation(summary = "获得定薪/调薪记录") + @Parameter(name = "id", description = "记录编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:salary:employee-info:query')") + public CommonResult getSalaryChangeRecord(@RequestParam("id") Long id) { + return success(buildSalaryChangeRecordRespVO( + salaryChangeRecordService.getSalaryChangeRecord(id))); + } + + @GetMapping("/list") + @Operation(summary = "获得定薪/调薪记录列表") + @Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:salary:employee-info:query')") + public CommonResult> getSalaryChangeRecordList( + @RequestParam("employeeId") Long employeeId) { + return success(convertList(salaryChangeRecordService.getSalaryChangeRecordList(employeeId), + this::buildSalaryChangeRecordRespVO)); + } + + @PutMapping("/cancel") + @Operation(summary = "取消定薪/调薪记录") + @Parameter(name = "id", description = "记录编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:salary:employee-info:update')") + public CommonResult cancelSalaryChangeRecord(@RequestParam("id") Long id) { + salaryChangeRecordService.cancelSalaryChangeRecord(id); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除定薪/调薪记录") + @Parameter(name = "id", description = "记录编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:salary:change-record:delete')") + public CommonResult deleteSalaryChangeRecord(@RequestParam("id") Long id) { + salaryChangeRecordService.deleteSalaryChangeRecord(id); + return success(true); + } + + // ==================== 拼接 VO ==================== + + private HrmSalaryChangeRecordRespVO buildSalaryChangeRecordRespVO( + HrmSalaryChangeRecordDO changeRecord) { + if (changeRecord == null) { + return null; + } + HrmSalaryChangeRecordRespVO respVO = BeanUtils.toBean(changeRecord, HrmSalaryChangeRecordRespVO.class); + respVO.setRecordType(changeRecord.getType()); + respVO.setChangeReason(changeRecord.getReason()); + HrmSalaryChangeRecordTypeEnum recordType = + HrmSalaryChangeRecordTypeEnum.valueOf(changeRecord.getType()); + respVO.setRecordTypeName(recordType == null ? null : recordType.getName()); + return respVO; + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryChangeTemplateController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryChangeTemplateController.java new file mode 100644 index 0000000..eaf16f7 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryChangeTemplateController.java @@ -0,0 +1,82 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary; + +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.salary.vo.changetemplate.HrmSalaryChangeTemplateRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.changetemplate.HrmSalaryChangeTemplateSaveReqVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.config.HrmSalaryChangeTemplateDO; +import cn.iocoder.yudao.module.hrm.service.salary.config.HrmSalaryChangeTemplateService; +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/salary/change-template") +@Validated +public class HrmSalaryChangeTemplateController { + + @Resource + private HrmSalaryChangeTemplateService salaryChangeTemplateService; + + @PostMapping("/create") + @Operation(summary = "创建调薪模板") + @PreAuthorize("@ss.hasPermission('hrm:salary:change-template:create')") + public CommonResult createSalaryChangeTemplate( + @Valid @RequestBody HrmSalaryChangeTemplateSaveReqVO createReqVO) { + return success(salaryChangeTemplateService.createSalaryChangeTemplate(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新调薪模板") + @PreAuthorize("@ss.hasPermission('hrm:salary:change-template:update')") + public CommonResult updateSalaryChangeTemplate( + @Valid @RequestBody HrmSalaryChangeTemplateSaveReqVO updateReqVO) { + salaryChangeTemplateService.updateSalaryChangeTemplate(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除调薪模板") + @Parameter(name = "id", description = "调薪模板编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:salary:change-template:delete')") + public CommonResult deleteSalaryChangeTemplate(@RequestParam("id") Long id) { + salaryChangeTemplateService.deleteSalaryChangeTemplate(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得调薪模板") + @Parameter(name = "id", description = "调薪模板编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:salary:change-template:query')") + public CommonResult getSalaryChangeTemplate(@RequestParam("id") Long id) { + HrmSalaryChangeTemplateDO template = salaryChangeTemplateService.getSalaryChangeTemplate(id); + return success(BeanUtils.toBean(template, HrmSalaryChangeTemplateRespVO.class)); + } + + @GetMapping("/list") + @Operation(summary = "获得调薪模板列表") + @PreAuthorize("@ss.hasPermission('hrm:salary:change-template:query') " + + "or @ss.hasPermission('hrm:salary:employee-info:query')") + public CommonResult> getSalaryChangeTemplateList() { + List list = salaryChangeTemplateService.getSalaryChangeTemplateList(); + return success(BeanUtils.toBean(list, HrmSalaryChangeTemplateRespVO.class)); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryConfigController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryConfigController.java new file mode 100644 index 0000000..5e5d28c --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryConfigController.java @@ -0,0 +1,57 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary; + +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.salary.vo.config.HrmSalaryConfigCreateReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.config.HrmSalaryConfigRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.config.HrmSalaryConfigUpdateReqVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.config.HrmSalaryConfigDO; +import cn.iocoder.yudao.module.hrm.service.salary.config.HrmSalaryConfigService; +import io.swagger.v3.oas.annotations.Operation; +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.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.RestController; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - HRM 薪资配置") +@RestController +@RequestMapping("/hrm/salary/config") +@Validated +public class HrmSalaryConfigController { + + @Resource + private HrmSalaryConfigService salaryConfigService; + + @PostMapping("/create") + @Operation(summary = "创建薪资配置") + @PreAuthorize("@ss.hasPermission('hrm:salary:config:update')") + public CommonResult createSalaryConfig(@Valid @RequestBody HrmSalaryConfigCreateReqVO reqVO) { + return success(salaryConfigService.createSalaryConfig(reqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新薪资配置") + @PreAuthorize("@ss.hasPermission('hrm:salary:config:update')") + public CommonResult updateSalaryConfig(@Valid @RequestBody HrmSalaryConfigUpdateReqVO reqVO) { + salaryConfigService.updateSalaryConfig(reqVO); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得薪资配置") + @PreAuthorize("@ss.hasPermission('hrm:salary:config:query')") + public CommonResult getSalaryConfig() { + HrmSalaryConfigDO config = salaryConfigService.getSalaryConfig(); + return success(BeanUtils.toBean(config, HrmSalaryConfigRespVO.class)); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryEmployeeInfoController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryEmployeeInfoController.java new file mode 100644 index 0000000..287d94d --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryEmployeeInfoController.java @@ -0,0 +1,343 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary; + +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.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.employee.vo.employee.HrmEmployeeListReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeStatusCountRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.employeeinfo.HrmSalaryEmployeeInfoUpdateListReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.employeeinfo.HrmSalaryEmployeeInfoUpdateListRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.employeeinfo.HrmSalaryEmployeeInfoPageReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.employeeinfo.HrmSalaryEmployeeInfoUpdateReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.employeeinfo.HrmSalaryEmployeeInfoRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.employeeinfo.HrmSalaryEmployeeInfoImportRespVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.employee.HrmSalaryEmployeeInfoDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.monthrecord.HrmSalaryMonthRecordDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.config.HrmSalaryOptionDO; +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeStatusTabEnum; +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService; +import cn.iocoder.yudao.module.hrm.service.salary.employee.HrmSalaryEmployeeInfoService; +import cn.iocoder.yudao.module.hrm.service.salary.monthrecord.HrmSalaryMonthRecordService; +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 cn.iocoder.yudao.module.hrm.enums.salary.employee.HrmSalaryEmployeeInfoChangeTypeEnum; +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 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.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.math.BigDecimal; +import java.time.LocalDate; +import java.time.YearMonth; +import java.util.ArrayList; +import java.util.Arrays; +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.convertMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +@Tag(name = "管理后台 - HRM 员工薪资信息") +@RestController +@RequestMapping("/hrm/salary/employee-info") +@Validated +public class HrmSalaryEmployeeInfoController { + + @Resource + private HrmSalaryEmployeeInfoService salaryEmployeeInfoService; + @Resource + private HrmEmployeeService employeeService; + @Resource + private HrmSalaryMonthRecordService salaryMonthRecordService; + + @Resource + private DeptApi deptApi; + + @GetMapping("/page") + @Operation(summary = "获得员工薪资信息分页") + @PreAuthorize("@ss.hasPermission('hrm:salary:employee-info:query')") + public CommonResult> getSalaryEmployeeInfoPage( + @Validated HrmSalaryEmployeeInfoPageReqVO reqVO) { + PageResult pageResult = salaryEmployeeInfoService.getSalaryEmployeeInfoPage(reqVO); + return success(buildSalaryEmployeeInfoRespVOPageResult(pageResult)); + } + + @GetMapping("/status-count") + @Operation(summary = "获得员工薪资信息状态数量") + @PreAuthorize("@ss.hasPermission('hrm:salary:employee-info:query')") + public CommonResult> getSalaryEmployeeInfoStatusCount( + @Validated HrmSalaryEmployeeInfoPageReqVO reqVO) { + Map countMap = salaryEmployeeInfoService.getSalaryEmployeeInfoStatusCount(reqVO); + return success(convertList(Arrays.asList(HrmEmployeeStatusTabEnum.values()), + status -> new HrmEmployeeStatusCountRespVO(status.getStatus(), + countMap.getOrDefault(status.getStatus(), 0L)))); + } + + @GetMapping("/get") + @Operation(summary = "获得员工薪资信息") + @Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:salary:employee-info:query')") + public CommonResult getSalaryEmployeeInfo( + @RequestParam("employeeId") Long employeeId) { + HrmEmployeeDO employee = employeeService.getEmployee(employeeId); + if (employee == null) { + return success(null); + } + HrmSalaryEmployeeInfoDO salaryEmployeeInfo = + salaryEmployeeInfoService.getSalaryEmployeeInfoByEmployeeId(employeeId); + Map deptMap = deptApi.getDeptMap(Collections.singleton(employee.getDeptId())); + return success(buildSalaryEmployeeInfoRespVO(employee, salaryEmployeeInfo, deptMap)); + } + + @GetMapping("/get-adjustment-min-effect-date") + @Operation(summary = "获得最早调薪生效日期") + @PreAuthorize("@ss.hasPermission('hrm:salary:employee-info:update')") + public CommonResult getSalaryAdjustmentMinEffectDate() { + return success(getSalaryAdjustmentMinEffectDateValue()); + } + + private LocalDate getSalaryAdjustmentMinEffectDateValue() { + HrmSalaryMonthRecordDO monthRecord = salaryMonthRecordService.getLastMonthRecord(); + if (monthRecord == null || monthRecord.getYear() == null || monthRecord.getMonth() == null) { + return null; + } + return YearMonth.of(monthRecord.getYear(), monthRecord.getMonth()).atDay(1); + } + + @PutMapping("/update") + @Operation(summary = "修改员工薪资信息") + @PreAuthorize("@ss.hasPermission('hrm:salary:employee-info:update')") + public CommonResult updateSalaryEmployeeInfo( + @Valid @RequestBody HrmSalaryEmployeeInfoUpdateReqVO reqVO) { + return success(salaryEmployeeInfoService.updateSalaryEmployeeInfo(reqVO)); + } + + @PutMapping("/update-list") + @Operation(summary = "批量调薪") + @PreAuthorize("@ss.hasPermission('hrm:salary:employee-info:update')") + public CommonResult updateSalaryEmployeeInfoList( + @Valid @RequestBody HrmSalaryEmployeeInfoUpdateListReqVO reqVO) { + return success(salaryEmployeeInfoService.updateSalaryEmployeeInfoList(reqVO)); + } + + @GetMapping("/get-fix-import-template") + @Operation(summary = "获得薪资档案定薪导入模板") + @PreAuthorize("@ss.hasPermission('hrm:salary:employee-info:import')") + @ApiAccessLog(operateType = OperateTypeEnum.EXPORT) + public void getFixImportTemplate(HttpServletResponse response) throws IOException { + List options = salaryEmployeeInfoService.getSalaryImportOptionList(); + List> head = buildFixSalaryImportHead(options); + List> data = buildFixSalaryImportTemplateData(options); + ExcelUtils.write(response, "薪资档案定薪导入模板.xls", "薪资档案", head, data); + } + + @PostMapping("/import-fix") + @Operation(summary = "导入薪资档案定薪") + @Parameter(name = "file", description = "Excel 文件", required = true) + @PreAuthorize("@ss.hasPermission('hrm:salary:employee-info:import')") + @ApiAccessLog(operateType = OperateTypeEnum.IMPORT) + public CommonResult importFixExcel(@RequestParam("file") MultipartFile file) + throws IOException { + return success(salaryEmployeeInfoService.importFixSalaryList(ExcelUtils.read(file))); + } + + @GetMapping("/get-change-import-template") + @Operation(summary = "获得薪资档案调薪导入模板") + @PreAuthorize("@ss.hasPermission('hrm:salary:employee-info:import')") + @ApiAccessLog(operateType = OperateTypeEnum.EXPORT) + public void getChangeImportTemplate(HttpServletResponse response) throws IOException { + List options = salaryEmployeeInfoService.getSalaryImportOptionList(); + List> head = buildChangeSalaryImportHead(options); + List> data = buildChangeSalaryImportTemplateData(options); + ExcelUtils.write(response, "薪资档案调薪导入模板.xls", "薪资档案", head, data); + } + + @PostMapping("/import-change") + @Operation(summary = "导入薪资档案调薪") + @Parameter(name = "file", description = "Excel 文件", required = true) + @PreAuthorize("@ss.hasPermission('hrm:salary:employee-info:import')") + @ApiAccessLog(operateType = OperateTypeEnum.IMPORT) + public CommonResult importChangeExcel(@RequestParam("file") MultipartFile file) + throws IOException { + return success(salaryEmployeeInfoService.importChangeSalaryList(ExcelUtils.read(file))); + } + + // ==================== 拼接 VO ==================== + + private PageResult buildSalaryEmployeeInfoRespVOPageResult( + PageResult pageResult) { + if (CollUtil.isEmpty(pageResult.getList())) { + return PageResult.empty(pageResult.getTotal()); + } + // 1. 获取关联数据 + Map salaryEmployeeInfoMap = + salaryEmployeeInfoService.getSalaryEmployeeInfoMap( + convertSet(pageResult.getList(), HrmEmployeeDO::getId)); + Map deptMap = deptApi.getDeptMap( + convertSet(pageResult.getList(), HrmEmployeeDO::getDeptId)); + + // 2. 拼接响应 + return new PageResult<>(convertList(pageResult.getList(), employee -> buildSalaryEmployeeInfoRespVO( + employee, salaryEmployeeInfoMap.get(employee.getId()), deptMap)), pageResult.getTotal()); + } + + private HrmSalaryEmployeeInfoRespVO buildSalaryEmployeeInfoRespVO( + HrmEmployeeDO employee, HrmSalaryEmployeeInfoDO salaryEmployeeInfo, Map deptMap) { + // 1. 拼接员工基础信息 + HrmSalaryEmployeeInfoRespVO respVO = salaryEmployeeInfo == null + ? new HrmSalaryEmployeeInfoRespVO() + : BeanUtils.toBean(salaryEmployeeInfo, HrmSalaryEmployeeInfoRespVO.class); + respVO.setEmployeeId(employee.getId()).setEmployeeName(employee.getName()) + .setJobNumber(employee.getJobNumber()).setMobile(employee.getMobile()) + .setDeptId(employee.getDeptId()).setPostName(employee.getPostName()) + .setEntryStatus(employee.getEntryStatus()).setStatus(employee.getStatus()) + .setEntryTime(employee.getEntryTime()).setRegularTime(employee.getRegularTime()); + MapUtils.findAndThen(deptMap, employee.getDeptId(), + dept -> respVO.setDeptName(dept.getName())); + + // 2. 拼接当前薪资信息 + if (salaryEmployeeInfo == null) { + respVO.setChangeType(HrmSalaryEmployeeInfoChangeTypeEnum.UNSET.getType()) + .setSalaryOptions(Collections.emptyList()).setProbationSalaryOptions(Collections.emptyList()); + return respVO; + } + return respVO; + } + + // ==================== Excel 导入导出 ==================== + + private List> buildFixSalaryImportHead(List options) { + List> head = buildSalaryImportEmployeeHead(); + options.forEach(option -> head.add(Collections.singletonList("试用期-" + option.getName()))); + options.forEach(option -> head.add(Collections.singletonList("正式-" + option.getName()))); + head.add(Collections.singletonList("备注")); + return head; + } + + private List> buildFixSalaryImportTemplateData(List options) { + List employees = getActiveEmployeeList(); + List> rows = convertList(employees, employee -> { + List row = buildSalaryImportEmployeeRow(employee); + options.forEach(option -> row.add(BigDecimal.ZERO)); + options.forEach(option -> row.add(BigDecimal.ZERO)); + row.add(""); + return row; + }); + if (CollUtil.isEmpty(rows)) { + List row = new ArrayList<>(Arrays.asList( + "张三", "HRM001", 100L, "Java 工程师")); + options.forEach(option -> row.add(BigDecimal.ZERO)); + options.forEach(option -> row.add(BigDecimal.ZERO)); + row.add("示例定薪"); + rows.add(row); + } + return rows; + } + + private List> buildChangeSalaryImportHead(List options) { + List> head = buildSalaryImportBaseHead("调薪原因"); + options.forEach(option -> { + head.add(Collections.singletonList("试用期-" + option.getName() + "-调整前")); + head.add(Collections.singletonList("试用期-" + option.getName() + "-调整后")); + }); + options.forEach(option -> { + head.add(Collections.singletonList("正式-" + option.getName() + "-调整前")); + head.add(Collections.singletonList("正式-" + option.getName() + "-调整后")); + }); + head.add(Collections.singletonList("备注")); + return head; + } + + private List> buildChangeSalaryImportTemplateData(List options) { + List employees = getActiveEmployeeList(); + Map salaryEmployeeInfoMap = + salaryEmployeeInfoService.getSalaryEmployeeInfoMap(convertSet(employees, HrmEmployeeDO::getId)); + LocalDate effectDate = getSalaryAdjustmentMinEffectDateValue(); + if (effectDate == null || effectDate.isBefore(LocalDate.now())) { + effectDate = LocalDate.now(); + } + LocalDate finalEffectDate = effectDate; + return convertList(employees, employee -> { + HrmSalaryEmployeeInfoDO salaryEmployeeInfo = salaryEmployeeInfoMap.get(employee.getId()); + List row = buildSalaryImportBaseRow(employee, finalEffectDate, "转正"); + appendChangeOptionTemplateValues(row, options, + buildSalaryOptionValueMap(salaryEmployeeInfo == null + ? Collections.emptyList() : salaryEmployeeInfo.getProbationSalaryOptions())); + appendChangeOptionTemplateValues(row, options, + buildSalaryOptionValueMap(salaryEmployeeInfo == null + ? Collections.emptyList() : salaryEmployeeInfo.getSalaryOptions())); + row.add(""); + return row; + }); + } + + private List getActiveEmployeeList() { + return employeeService.getEmployeeList(new HrmEmployeeListReqVO() + .setStatusCategory(HrmEmployeeStatusTabEnum.ACTIVE.getStatus())); + } + + private List> buildSalaryImportBaseHead(String reasonName) { + List> head = buildSalaryImportEmployeeHead(); + head.add(Collections.singletonList("生效日期")); + head.add(Collections.singletonList(reasonName)); + return head; + } + + private List> buildSalaryImportEmployeeHead() { + return new ArrayList<>(Arrays.asList( + Collections.singletonList("员工姓名"), + Collections.singletonList("工号"), + Collections.singletonList("部门编号"), + Collections.singletonList("岗位"))); + } + + private List buildSalaryImportEmployeeRow(HrmEmployeeDO employee) { + return new ArrayList<>(Arrays.asList( + employee.getName(), employee.getJobNumber(), employee.getDeptId(), employee.getPostName())); + } + + private List buildSalaryImportBaseRow(HrmEmployeeDO employee, LocalDate effectDate, String changeReason) { + List row = buildSalaryImportEmployeeRow(employee); + row.add(effectDate.toString()); + row.add(changeReason); + return row; + } + + private Map buildSalaryOptionValueMap( + List options) { + return convertMap(options, HrmSalaryEmployeeInfoDO.SalaryOption::getCode, + option -> option.getValue() == null ? BigDecimal.ZERO : option.getValue()); + } + + private void appendChangeOptionTemplateValues(List row, List options, + Map valueMap) { + for (HrmSalaryOptionDO option : options) { + BigDecimal value = valueMap.getOrDefault(option.getCode(), BigDecimal.ZERO); + row.add(value); + row.add(value); + } + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryGroupController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryGroupController.java new file mode 100644 index 0000000..0dae418 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryGroupController.java @@ -0,0 +1,141 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +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.module.hrm.controller.admin.salary.vo.group.HrmSalaryGroupPageReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.group.HrmSalaryGroupRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.group.HrmSalaryGroupSaveReqVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.config.HrmSalaryGroupDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.config.HrmSalaryTaxRuleDO; +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService; +import cn.iocoder.yudao.module.hrm.service.salary.config.HrmSalaryGroupService; +import cn.iocoder.yudao.module.hrm.service.salary.config.HrmSalaryTaxRuleService; +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 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.convertSetByFlatMap; + +@Tag(name = "管理后台 - HRM 薪资组") +@RestController +@RequestMapping("/hrm/salary/group") +@Validated +public class HrmSalaryGroupController { + + @Resource + private HrmSalaryGroupService salaryGroupService; + @Resource + private HrmSalaryTaxRuleService salaryTaxRuleService; + @Resource + private HrmEmployeeService employeeService; + @Resource + private DeptApi deptApi; + + @PostMapping("/create") + @Operation(summary = "创建薪资组") + @PreAuthorize("@ss.hasPermission('hrm:salary:group:create')") + public CommonResult createSalaryGroup(@Valid @RequestBody HrmSalaryGroupSaveReqVO reqVO) { + return success(salaryGroupService.createSalaryGroup(reqVO)); + } + + @PutMapping("/update") + @Operation(summary = "修改薪资组") + @PreAuthorize("@ss.hasPermission('hrm:salary:group:update')") + public CommonResult updateSalaryGroup(@Valid @RequestBody HrmSalaryGroupSaveReqVO reqVO) { + salaryGroupService.updateSalaryGroup(reqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除薪资组") + @Parameter(name = "id", description = "薪资组编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:salary:group:delete')") + public CommonResult deleteSalaryGroup(@RequestParam("id") Long id) { + salaryGroupService.deleteSalaryGroup(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得薪资组") + @Parameter(name = "id", description = "薪资组编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:salary:group:query')") + public CommonResult getSalaryGroup(@RequestParam("id") Long id) { + return success(buildSalaryGroupRespVO(salaryGroupService.getSalaryGroup(id))); + } + + @GetMapping("/page") + @Operation(summary = "获得薪资组分页") + @PreAuthorize("@ss.hasPermission('hrm:salary:group:query')") + public CommonResult> getSalaryGroupPage( + @Validated HrmSalaryGroupPageReqVO reqVO) { + PageResult pageResult = salaryGroupService.getSalaryGroupPage(reqVO); + return success(new PageResult<>(buildSalaryGroupRespVOList(pageResult.getList()), pageResult.getTotal())); + } + + @GetMapping("/simple-list") + @Operation(summary = "获得薪资组精简列表") + @PreAuthorize("@ss.hasPermission('hrm:salary:group:query')") + public CommonResult> getSalaryGroupSimpleList() { + return success(convertList(salaryGroupService.getSalaryGroupList(), + salaryGroup -> new HrmSalaryGroupRespVO().setId(salaryGroup.getId()) + .setName(salaryGroup.getName()).setTaxRuleId(salaryGroup.getTaxRuleId()))); + } + + // ==================== 拼接 VO ==================== + + private HrmSalaryGroupRespVO buildSalaryGroupRespVO(HrmSalaryGroupDO group) { + if (group == null) { + return null; + } + return CollUtil.getFirst(buildSalaryGroupRespVOList(Collections.singletonList(group))); + } + + private List buildSalaryGroupRespVOList(List salaryGroups) { + if (CollUtil.isEmpty(salaryGroups)) { + return Collections.emptyList(); + } + // 1.1 批量查询计税规则 + Map taxRuleMap = salaryTaxRuleService.getSalaryTaxRuleMap( + convertSet(salaryGroups, HrmSalaryGroupDO::getTaxRuleId)); + // 1.2 批量查询部门和员工 + Map deptMap = deptApi.getDeptMap( + convertSetByFlatMap(salaryGroups, HrmSalaryGroupDO::getDeptIds, Collection::stream)); + Map employeeMap = employeeService.getEmployeeMap( + convertSetByFlatMap(salaryGroups, HrmSalaryGroupDO::getEmployeeIds, Collection::stream)); + + // 2. 拼接响应 + return BeanUtils.toBean(salaryGroups, HrmSalaryGroupRespVO.class, vo -> { + MapUtils.findAndThen(taxRuleMap, vo.getTaxRuleId(), taxRule -> vo.setTaxRuleName(taxRule.getName())); + vo.setDeptNames(convertList(convertList(vo.getDeptIds(), deptMap::get), DeptRespDTO::getName)); + vo.setEmployeeNames(convertList(convertList(vo.getEmployeeIds(), employeeMap::get), + HrmEmployeeDO::getName)); + }); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryMonthEmployeeRecordController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryMonthEmployeeRecordController.java new file mode 100644 index 0000000..03bd06b --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryMonthEmployeeRecordController.java @@ -0,0 +1,153 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +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.module.hrm.controller.admin.salary.vo.monthrecord.employee.HrmSalaryEmployeeMonthRecordPageReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.monthrecord.employee.HrmSalaryMonthEmployeeRecordListReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.monthrecord.employee.HrmSalaryMonthEmployeeRecordPageReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.monthrecord.employee.HrmSalaryMonthEmployeeRecordRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.monthrecord.employee.HrmSalaryMonthEmployeeRecordUpdateReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.monthrecord.employee.HrmSalaryPerformanceCoefficientReqVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.monthrecord.HrmSalaryMonthEmployeeRecordDO; +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService; +import cn.iocoder.yudao.module.hrm.service.salary.monthrecord.HrmSalaryMonthEmployeeRecordService; +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.tags.Tag; +import jakarta.annotation.Resource; +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.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.RestController; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +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/salary/month-employee-record") +@Validated +public class HrmSalaryMonthEmployeeRecordController { + + @Resource + private HrmSalaryMonthEmployeeRecordService monthEmployeeRecordService; + @Resource + private HrmEmployeeService employeeService; + @Resource + private DeptApi deptApi; + + @PutMapping("/update-list") + @Operation(summary = "批量修改员工工资项") + @PreAuthorize("@ss.hasPermission('hrm:salary:month-record:update')") + public CommonResult updateMonthEmployeeRecordList( + @NotEmpty(message = "员工工资记录不能为空") + @RequestBody List<@Valid HrmSalaryMonthEmployeeRecordUpdateReqVO> reqVOs) { + monthEmployeeRecordService.updateMonthEmployeeRecordList(reqVOs); + return success(true); + } + + @GetMapping("/page") + @Operation(summary = "获得员工月度工资分页") + @PreAuthorize("@ss.hasPermission('hrm:salary:month-record:query')") + public CommonResult> getMonthEmployeeRecordPage( + @Valid HrmSalaryMonthEmployeeRecordPageReqVO reqVO) { + PageResult pageResult = + monthEmployeeRecordService.getMonthEmployeeRecordPage(reqVO); + return success(new PageResult<>(buildMonthEmployeeRecordRespVOList(pageResult.getList()), + pageResult.getTotal())); + } + + @GetMapping("/employee-page") + @Operation(summary = "获得指定员工的月度工资分页") + @PreAuthorize("@ss.hasPermission('hrm:salary:month-record:query')") + public CommonResult> getEmployeeMonthRecordPage( + @Valid HrmSalaryEmployeeMonthRecordPageReqVO reqVO) { + PageResult pageResult = + monthEmployeeRecordService.getEmployeeMonthRecordPage(reqVO); + return success(new PageResult<>(buildMonthEmployeeRecordRespVOList(pageResult.getList()), + pageResult.getTotal())); + } + + @GetMapping("/list") + @Operation(summary = "获得员工月度工资列表") + @PreAuthorize("@ss.hasPermission('hrm:salary:month-record:query')") + public CommonResult> getMonthEmployeeRecordList( + @Valid HrmSalaryMonthEmployeeRecordListReqVO reqVO) { + return success(buildMonthEmployeeRecordRespVOList( + monthEmployeeRecordService.getMonthEmployeeRecordList(reqVO))); + } + + @GetMapping("/change-count") + @Operation(summary = "获得工资表员工异动分类数量") + @PreAuthorize("@ss.hasPermission('hrm:salary:month-record:query')") + public CommonResult> getMonthEmployeeChangeCount( + @Valid HrmSalaryMonthEmployeeRecordPageReqVO reqVO) { + return success(monthEmployeeRecordService.getMonthEmployeeChangeCount(reqVO)); + } + + @PostMapping("/performance-coefficients") + @Operation(summary = "获得员工绩效系数") + @PreAuthorize("@ss.hasPermission('hrm:salary:month-record:query')") + public CommonResult> getPerformanceCoefficientMap( + @Valid @RequestBody HrmSalaryPerformanceCoefficientReqVO reqVO) { + return success(monthEmployeeRecordService.getPerformanceCoefficientMap(reqVO)); + } + + // ==================== 拼接 VO ==================== + + private List buildMonthEmployeeRecordRespVOList( + List employeeRecords) { + if (CollUtil.isEmpty(employeeRecords)) { + return Collections.emptyList(); + } + + // 1.1 批量查询员工和部门 + Set employeeIds = convertSet(employeeRecords, HrmSalaryMonthEmployeeRecordDO::getEmployeeId); + Map employeeMap = employeeService.getEmployeeMap(employeeIds); + Map deptMap = deptApi.getDeptMap( + convertSet(employeeMap.values(), HrmEmployeeDO::getDeptId)); + // 1.2 批量查询绩效系数和薪资项 + HrmSalaryMonthEmployeeRecordDO firstRecord = CollUtil.getFirst(employeeRecords); + boolean sameMonth = employeeRecords.stream().allMatch(record -> + Objects.equals(record.getYear(), firstRecord.getYear()) + && Objects.equals(record.getMonth(), firstRecord.getMonth())); + Map coefficientMap = sameMonth + ? monthEmployeeRecordService.getPerformanceCoefficientMap( + new HrmSalaryPerformanceCoefficientReqVO().setYear(firstRecord.getYear()) + .setMonth(firstRecord.getMonth()).setEmployeeIds(new ArrayList<>(employeeIds))) + : Collections.emptyMap(); + // 2. 拼接响应 + List respVOs = BeanUtils.toBean( + employeeRecords, HrmSalaryMonthEmployeeRecordRespVO.class); + for (HrmSalaryMonthEmployeeRecordRespVO respVO : respVOs) { + MapUtils.findAndThen(employeeMap, respVO.getEmployeeId(), employee -> { + respVO.setEmployeeName(employee.getName()).setJobNumber(employee.getJobNumber()) + .setDeptId(employee.getDeptId()).setPostName(employee.getPostName()); + MapUtils.findAndThen(deptMap, employee.getDeptId(), + dept -> respVO.setDeptName(dept.getName())); + }); + respVO.setPerformanceCoefficient(coefficientMap.get(respVO.getEmployeeId())); + } + return respVOs; + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryMonthRecordController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryMonthRecordController.java new file mode 100644 index 0000000..ece5bb9 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryMonthRecordController.java @@ -0,0 +1,352 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary; + +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.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics.HrmAttendanceMonthRecordRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.option.HrmSalaryOptionValueVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.monthrecord.employee.HrmSalaryMonthEmployeeRecordListReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.monthrecord.HrmSalaryMonthRecordPageReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.monthrecord.HrmSalaryMonthRecordRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.monthrecord.HrmSalaryPayrollReadinessRespVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.config.HrmSalaryGroupDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.monthrecord.HrmSalaryMonthEmployeeRecordDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.monthrecord.HrmSalaryMonthRecordDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.config.HrmSalaryTaxRuleDO; +import cn.iocoder.yudao.module.hrm.service.attendance.statistics.HrmAttendanceStatisticsService; +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService; +import cn.iocoder.yudao.module.hrm.service.salary.config.HrmSalaryGroupService; +import cn.iocoder.yudao.module.hrm.service.salary.monthrecord.HrmSalaryMonthEmployeeRecordService; +import cn.iocoder.yudao.module.hrm.service.salary.monthrecord.HrmSalaryMonthRecordService; +import cn.iocoder.yudao.module.hrm.service.salary.config.HrmSalaryTaxRuleService; +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 cn.iocoder.yudao.module.hrm.enums.salary.config.HrmSalaryTaxTypeEnum; +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.http.MediaType; +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.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.math.BigDecimal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +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.convertMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +@Tag(name = "管理后台 - HRM 月度工资表") +@RestController +@RequestMapping("/hrm/salary/month-record") +@Validated +public class HrmSalaryMonthRecordController { + + @Resource + private HrmSalaryMonthRecordService salaryMonthRecordService; + @Resource + private HrmSalaryMonthEmployeeRecordService monthEmployeeRecordService; + @Resource + private HrmSalaryGroupService salaryGroupService; + @Resource + private HrmSalaryTaxRuleService salaryTaxRuleService; + @Resource + private HrmEmployeeService employeeService; + @Resource + private HrmAttendanceStatisticsService attendanceStatisticsService; + + @Resource + private DeptApi deptApi; + + @PostMapping("/create-next") + @Operation(summary = "创建下月工资表") + @PreAuthorize("@ss.hasPermission('hrm:salary:month-record:create')") + public CommonResult createNextMonthRecord() { + return success(salaryMonthRecordService.createNextMonthRecord()); + } + + @PostMapping("/compute") + @Operation(summary = "核算工资表") + @Parameter(name = "id", description = "工资表编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:salary:month-record:compute')") + public CommonResult computeMonthRecord(@RequestParam("id") Long id) { + salaryMonthRecordService.computeMonthRecord(id); + return success(true); + } + + @PostMapping(value = "/compute-import", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @Operation(summary = "带导入文件核算工资表") + @Parameters({ + @Parameter(name = "id", description = "工资表编号", required = true, example = "1024"), + @Parameter(name = "syncInsuranceData", description = "是否同步社保数据", example = "true"), + @Parameter(name = "syncAttendanceData", description = "是否同步考勤数据", example = "false"), + @Parameter(name = "attendanceFile", description = "考勤数据文件"), + @Parameter(name = "additionalDeductionFile", description = "专项附加扣除数据文件"), + @Parameter(name = "cumulativeTaxFile", description = "上月个税累计数据文件") + }) + @PreAuthorize("@ss.hasPermission('hrm:salary:month-record:compute')") + @ApiAccessLog(operateType = OperateTypeEnum.IMPORT) + public CommonResult computeMonthRecordWithImport( + @RequestParam("id") Long id, + @RequestParam(value = "syncInsuranceData", defaultValue = "true") boolean syncInsuranceData, + @RequestParam(value = "syncAttendanceData", defaultValue = "false") boolean syncAttendanceData, + @RequestParam(value = "attendanceFile", required = false) MultipartFile attendanceFile, + @RequestParam(value = "additionalDeductionFile", required = false) MultipartFile additionalDeductionFile, + @RequestParam(value = "cumulativeTaxFile", required = false) MultipartFile cumulativeTaxFile) + throws IOException { + salaryMonthRecordService.computeMonthRecord(id, syncInsuranceData, syncAttendanceData, + ExcelUtils.read(attendanceFile), + ExcelUtils.read(additionalDeductionFile), + ExcelUtils.read(cumulativeTaxFile)); + return success(true); + } + + @GetMapping("/get-attendance-import-template") + @Operation(summary = "获得工资表考勤导入模板") + @Parameter(name = "monthRecordId", description = "月度工资表编号", example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:salary:month-record:compute')") + @ApiAccessLog(operateType = OperateTypeEnum.EXPORT) + public void getAttendanceImportTemplate( + @RequestParam(value = "monthRecordId", required = false) Long monthRecordId, + HttpServletResponse response) throws IOException { + List employees = getPayrollImportEmployeeList(monthRecordId, false); + List> data; + if (monthRecordId == null) { + data = buildImportTemplateData(employees, 8); + } else { + HrmSalaryMonthRecordDO monthRecord = salaryMonthRecordService.getMonthRecord(monthRecordId); + if (monthRecord == null) { + data = buildImportTemplateData(employees, 8); + } else { + Map attendanceMap = convertMap( + attendanceStatisticsService.getAttendanceMonthRecordList(monthRecord.getYear(), + monthRecord.getMonth(), convertList(employees, HrmEmployeeDO::getId)), + HrmAttendanceMonthRecordRespVO::getEmployeeId); + data = convertList(employees, employee -> { + List row = buildImportEmployeeRow(employee); + HrmAttendanceMonthRecordRespVO attendance = attendanceMap.get(employee.getId()); + row.add(BigDecimal.ZERO); + row.add(attendance == null ? BigDecimal.ZERO : attendance.getLateDeductAmount()); + row.add(attendance == null ? BigDecimal.ZERO : attendance.getEarlyDeductAmount()); + row.add(attendance == null ? BigDecimal.ZERO : attendance.getAbsenteeismDeductAmount()); + row.add(BigDecimal.ZERO); + row.add(attendance == null ? BigDecimal.ZERO : attendance.getMisscardDeductAmount()); + row.add(BigDecimal.ZERO); + row.add(attendance == null ? BigDecimal.ZERO : attendance.getActualDays()); + return row; + }); + } + } + List> head = buildImportHead(Arrays.asList( + "加班工资", "迟到扣款", "早退扣款", "旷工扣款", "假期扣款", + "缺卡扣款", "综合扣款", "实际计薪天数")); + ExcelUtils.write(response, "月度工资考勤导入模板.xls", "月度工资", head, data); + } + + @GetMapping("/get-cumulative-tax-import-template") + @Operation(summary = "获得工资表上月个税累计导入模板") + @Parameter(name = "monthRecordId", description = "月度工资表编号", example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:salary:month-record:compute')") + @ApiAccessLog(operateType = OperateTypeEnum.EXPORT) + public void getCumulativeTaxImportTemplate( + @RequestParam(value = "monthRecordId", required = false) Long monthRecordId, + HttpServletResponse response) throws IOException { + List> head = buildImportHead(Arrays.asList( + "累计收入额(截至上月)", "累计减除费用(截至上月)", + "累计专项扣除(截至上月)", "累计已预缴税额")); + List employees = getPayrollImportEmployeeList(monthRecordId, true); + List> data = buildImportTemplateData(employees, 4); + ExcelUtils.write(response, "月度工资上月个税累计导入模板.xls", "月度工资", head, data); + } + + @GetMapping("/get-additional-deduction-import-template") + @Operation(summary = "获得工资表专项附加扣除导入模板") + @Parameter(name = "monthRecordId", description = "月度工资表编号", example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:salary:month-record:compute')") + @ApiAccessLog(operateType = OperateTypeEnum.EXPORT) + public void getAdditionalDeductionImportTemplate( + @RequestParam(value = "monthRecordId", required = false) Long monthRecordId, + HttpServletResponse response) throws IOException { + List> head = buildImportHead(Arrays.asList( + "累计子女教育", "累计住房租金", "累计住房贷款利息", "累计赡养老人", "累计继续教育")); + List employees = getPayrollImportEmployeeList(monthRecordId, true); + List> data = buildImportTemplateData(employees, 5); + ExcelUtils.write(response, "月度工资专项附加扣除导入模板.xls", "月度工资", head, data); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除月度工资表") + @Parameter(name = "id", description = "工资表编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:salary:month-record:delete')") + public CommonResult deleteMonthRecord(@RequestParam("id") Long id) { + salaryMonthRecordService.deleteMonthRecord(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得月度工资表") + @Parameter(name = "id", description = "月度工资表编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:salary:month-record:query')") + public CommonResult getMonthRecord(@RequestParam("id") Long id) { + HrmSalaryMonthRecordDO monthRecord = salaryMonthRecordService.getMonthRecord(id); + return success(buildMonthRecordRespVO(monthRecord)); + } + + @GetMapping("/page") + @Operation(summary = "获得月度工资表分页") + @PreAuthorize("@ss.hasPermission('hrm:salary:month-record:query')") + public CommonResult> getMonthRecordPage( + @Valid HrmSalaryMonthRecordPageReqVO reqVO) { + PageResult pageResult = salaryMonthRecordService.getMonthRecordPage(reqVO); + return success(BeanUtils.toBean(pageResult, HrmSalaryMonthRecordRespVO.class)); + } + + @GetMapping("/last") + @Operation(summary = "获得最近月度工资表") + @PreAuthorize("@ss.hasPermission('hrm:salary:month-record:query')") + public CommonResult getLastMonthRecord() { + HrmSalaryMonthRecordDO monthRecord = salaryMonthRecordService.getLastMonthRecord(); + return success(buildMonthRecordRespVO(monthRecord)); + } + + @GetMapping("/payroll-readiness") + @Operation(summary = "获得工资核算准备情况") + @Parameter(name = "monthRecordId", description = "月度工资表编号", example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:salary:month-record:query')") + public CommonResult getPayrollReadiness( + @RequestParam(value = "monthRecordId", required = false) Long monthRecordId) { + HrmSalaryPayrollReadinessRespVO readiness = + salaryMonthRecordService.getPayrollReadiness(monthRecordId); + if (readiness == null) { + return success(null); + } + fillPayrollReadinessEmployeeDeptName(readiness.getNoSalaryEmployees()); + fillPayrollReadinessEmployeeDeptName(readiness.getNoSalaryGroupEmployees()); + return success(readiness); + } + + @GetMapping("/option-summary") + @Operation(summary = "获得工资项汇总") + @PreAuthorize("@ss.hasPermission('hrm:salary:month-record:query')") + public CommonResult> getMonthOptionSummary( + @Valid HrmSalaryMonthEmployeeRecordListReqVO reqVO) { + List employeeRecords = + monthEmployeeRecordService.getMonthEmployeeRecordList(reqVO); + List optionSummary = + monthEmployeeRecordService.getMonthOptionSummary(employeeRecords); + return success(BeanUtils.toBean(optionSummary, HrmSalaryOptionValueVO.class)); + } + + // ==================== 拼接 VO ==================== + + private HrmSalaryMonthRecordRespVO buildMonthRecordRespVO(HrmSalaryMonthRecordDO monthRecord) { + if (monthRecord == null) { + return null; + } + return BeanUtils.toBean(monthRecord, HrmSalaryMonthRecordRespVO.class); + } + + private void fillPayrollReadinessEmployeeDeptName( + List employees) { + if (CollUtil.isEmpty(employees)) { + return; + } + Map deptMap = deptApi.getDeptMap( + convertSet(employees, HrmSalaryPayrollReadinessRespVO.Employee::getDeptId)); + employees.forEach(employee -> MapUtils.findAndThen(deptMap, employee.getDeptId(), + dept -> employee.setDeptName(dept.getName()))); + } + + // ==================== Excel 导入导出 ==================== + + private List getPayrollImportEmployeeList(Long monthRecordId, boolean salaryTaxOnly) { + // 1. 获得指定或最近月度工资表 + HrmSalaryMonthRecordDO monthRecord = monthRecordId == null + ? salaryMonthRecordService.getLastMonthRecord() + : salaryMonthRecordService.getMonthRecord(monthRecordId); + if (monthRecord == null) { + return Collections.emptyList(); + } + + // 2. 获得指定工资表已有员工,或尚未核算时的计薪员工 + List employees; + if (monthRecordId != null) { + List employeeRecords = + monthEmployeeRecordService.getMonthEmployeeRecordListByMonthRecordId(monthRecordId); + if (CollUtil.isNotEmpty(employeeRecords)) { + Map employeeMap = employeeService.getEmployeeMap( + convertSet(employeeRecords, HrmSalaryMonthEmployeeRecordDO::getEmployeeId)); + employees = convertList(employeeRecords, record -> employeeMap.get(record.getEmployeeId()), + Objects::nonNull); + } else { + employees = salaryMonthRecordService.getPayrollEmployeeList(monthRecord); + } + } else { + employees = salaryMonthRecordService.getPayrollEmployeeList(monthRecord); + } + + // 3. 筛选已配置薪资组的员工 + Map salaryGroupMap = salaryGroupService.getEmployeeSalaryGroupMap(employees); + if (!salaryTaxOnly) { + return convertList(employees, employee -> employee, + employee -> salaryGroupMap.containsKey(employee.getId())); + } + + // 4. 个税累计模板只包含使用工资薪金计税规则的员工 + Set taxRuleIds = convertSet(salaryGroupMap.values(), HrmSalaryGroupDO::getTaxRuleId, + salaryGroup -> salaryGroup.getTaxRuleId() != null); + Map taxRuleMap = salaryTaxRuleService.getSalaryTaxRuleMap(taxRuleIds); + return convertList(employees, employee -> employee, employee -> { + HrmSalaryGroupDO salaryGroup = salaryGroupMap.get(employee.getId()); + HrmSalaryTaxRuleDO taxRule = salaryGroup == null ? null + : taxRuleMap.get(salaryGroup.getTaxRuleId()); + return taxRule != null && Objects.equals(taxRule.getType(), HrmSalaryTaxTypeEnum.SALARY.getType()); + }); + } + + private List> buildImportHead(List businessHeaders) { + List headers = Arrays.asList("员工名称", "岗位", "工号", "部门编号"); + return convertList(CollUtil.union(headers, businessHeaders), Collections::singletonList); + } + + private List> buildImportTemplateData(List employees, int blankColumnSize) { + return convertList(employees, employee -> { + List row = buildImportEmployeeRow(employee); + for (int i = 0; i < blankColumnSize; i++) { + row.add(""); + } + return row; + }); + } + + private List buildImportEmployeeRow(HrmEmployeeDO employee) { + return new ArrayList<>(Arrays.asList( + employee.getName(), employee.getPostName(), employee.getJobNumber(), employee.getDeptId())); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryOptionController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryOptionController.java new file mode 100644 index 0000000..ffd3ec8 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryOptionController.java @@ -0,0 +1,103 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary; + +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.salary.vo.option.HrmSalaryOptionRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.option.HrmSalaryOptionSaveReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.option.HrmSalaryOptionUpdateEnabledReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.option.HrmSalaryOptionUpdateVisibleReqVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.config.HrmSalaryOptionDO; +import cn.iocoder.yudao.module.hrm.service.salary.config.HrmSalaryOptionService; +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; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; + +@Tag(name = "管理后台 - HRM 薪资项") +@RestController +@RequestMapping("/hrm/salary/option") +@Validated +public class HrmSalaryOptionController { + + @Resource + private HrmSalaryOptionService salaryOptionService; + + @PostMapping("/create") + @Operation(summary = "创建薪资项") + @PreAuthorize("@ss.hasPermission('hrm:salary:option:create')") + public CommonResult createSalaryOption(@Valid @RequestBody HrmSalaryOptionSaveReqVO reqVO) { + return success(salaryOptionService.createSalaryOption(reqVO)); + } + + @PutMapping("/update-enabled") + @Operation(summary = "更新薪资项启用状态") + @PreAuthorize("@ss.hasPermission('hrm:salary:option:update')") + public CommonResult updateSalaryOptionEnabled( + @Valid @RequestBody HrmSalaryOptionUpdateEnabledReqVO reqVO) { + salaryOptionService.updateSalaryOptionEnabled(reqVO.getId(), reqVO.getEnabled()); + return success(true); + } + + @PutMapping("/update-visible") + @Operation(summary = "更新薪资项显示状态") + @PreAuthorize("@ss.hasPermission('hrm:salary:option:update')") + public CommonResult updateSalaryOptionVisible( + @Valid @RequestBody HrmSalaryOptionUpdateVisibleReqVO reqVO) { + salaryOptionService.updateSalaryOptionVisible(reqVO.getId(), reqVO.getVisible()); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除薪资项") + @Parameter(name = "id", description = "薪资项编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:salary:option:delete')") + public CommonResult deleteSalaryOption(@RequestParam("id") Long id) { + salaryOptionService.deleteSalaryOption(id); + return success(true); + } + + @PutMapping("/sync") + @Operation(summary = "同步标准薪资项") + @PreAuthorize("@ss.hasPermission('hrm:salary:option:update')") + public CommonResult syncSalaryOption() { + salaryOptionService.syncSalaryOption(); + return success(true); + } + + @GetMapping("/list") + @Operation(summary = "获得薪资项列表") + @PreAuthorize("@ss.hasPermission('hrm:salary:option:query')") + public CommonResult> getSalaryOptionList() { + List list = salaryOptionService.getSalaryOptionList(); + return success(BeanUtils.toBean(list, HrmSalaryOptionRespVO.class)); + } + + @GetMapping("/simple-list") + @Operation(summary = "获得薪资项精简列表", description = "只包含已启用的薪资项") + @Parameter(name = "adjustable", description = "是否可调薪", example = "true") + @PreAuthorize("@ss.hasPermission('hrm:salary:option:query')") + public CommonResult> getSalaryOptionSimpleList( + @RequestParam(value = "adjustable", required = false) Boolean adjustable) { + List list = salaryOptionService.getSalaryOptionList(adjustable); + return success(convertList(list, option -> new HrmSalaryOptionRespVO() + .setCode(option.getCode()).setParentCode(option.getParentCode()) + .setName(option.getName()).setCalculateEnabled(option.getCalculateEnabled()))); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalarySlipController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalarySlipController.java new file mode 100644 index 0000000..7d9b03d --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalarySlipController.java @@ -0,0 +1,135 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +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.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeListReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.slip.HrmSalarySlipOptionRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.slip.HrmSalarySlipPageReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.slip.HrmSalarySlipRemarkReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.slip.HrmSalarySlipRespVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.slip.HrmSalarySlipDO; +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService; +import cn.iocoder.yudao.module.hrm.service.salary.slip.HrmSalarySlipService; +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.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.ArrayList; +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; + +@Tag(name = "管理后台 - HRM 工资条") +@RestController +@RequestMapping("/hrm/salary/slip") +@Validated +public class HrmSalarySlipController { + + @Resource + private HrmSalarySlipService salarySlipService; + @Resource + private HrmEmployeeService employeeService; + + @Resource + private DeptApi deptApi; + + @GetMapping("/page") + @Operation(summary = "获得工资条分页") + @PreAuthorize("@ss.hasPermission('hrm:salary:slip:query')") + public CommonResult> getSalarySlipPage( + @Validated HrmSalarySlipPageReqVO reqVO) { + // 1. 根据员工展示条件获得员工编号 + if (StrUtil.isNotBlank(reqVO.getSearch()) || reqVO.getDeptId() != null) { + HrmEmployeeListReqVO employeeReqVO = new HrmEmployeeListReqVO(); + employeeReqVO.setSearch(reqVO.getSearch()); + employeeReqVO.setDeptId(reqVO.getDeptId()); + Set employeeIds = convertSet( + employeeService.getEmployeeList(employeeReqVO), HrmEmployeeDO::getId); + if (CollUtil.isNotEmpty(reqVO.getEmployeeIds())) { + employeeIds.retainAll(reqVO.getEmployeeIds()); + } + if (CollUtil.isEmpty(employeeIds)) { + return success(PageResult.empty()); + } + reqVO.setEmployeeIds(new ArrayList<>(employeeIds)); + } + + // 2. 查询工资条并拼接响应 + PageResult pageResult = salarySlipService.getSalarySlipPage(reqVO); + return success(new PageResult<>(buildSalarySlipRespVOList(pageResult.getList()), pageResult.getTotal())); + } + + @GetMapping("/get") + @Operation(summary = "获得工资条") + @Parameter(name = "id", description = "工资条编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:salary:slip:query')") + public CommonResult getSalarySlip(@RequestParam("id") Long id) { + return success(buildSalarySlipRespVO(salarySlipService.getSalarySlip(id))); + } + + @PutMapping("/remark") + @Operation(summary = "修改工资条备注") + @PreAuthorize("@ss.hasPermission('hrm:salary:slip:update')") + public CommonResult updateSalarySlipRemark( + @Valid @RequestBody HrmSalarySlipRemarkReqVO reqVO) { + salarySlipService.updateSalarySlipRemark(reqVO); + return success(true); + } + + // ==================== 拼接 VO ==================== + + private HrmSalarySlipRespVO buildSalarySlipRespVO(HrmSalarySlipDO salarySlip) { + if (salarySlip == null) { + return null; + } + return CollUtil.getFirst(buildSalarySlipRespVOList(Collections.singletonList(salarySlip))); + } + + private List buildSalarySlipRespVOList(List salarySlips) { + if (CollUtil.isEmpty(salarySlips)) { + return Collections.emptyList(); + } + // 1. 获取员工、部门信息 + Map employeeMap = employeeService.getEmployeeMap( + convertSet(salarySlips, HrmSalarySlipDO::getEmployeeId)); + Map deptMap = deptApi.getDeptMap( + convertSet(employeeMap.values(), HrmEmployeeDO::getDeptId)); + + // 2. 拼接响应 + return convertList(salarySlips, salarySlip -> { + HrmSalarySlipRespVO respVO = BeanUtils.toBean(salarySlip, HrmSalarySlipRespVO.class); + HrmEmployeeDO employee = employeeMap.get(salarySlip.getEmployeeId()); + if (employee != null) { + respVO.setEmployeeName(employee.getName()).setJobNumber(employee.getJobNumber()) + .setMobile(employee.getMobile()).setDeptId(employee.getDeptId()) + .setPostName(employee.getPostName()); + MapUtils.findAndThen(deptMap, employee.getDeptId(), dept -> respVO.setDeptName(dept.getName())); + } + respVO.setOptions(BeanUtils.toBean(salarySlip.getOptions(), HrmSalarySlipOptionRespVO.class)); + return respVO; + }); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalarySlipSendRecordController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalarySlipSendRecordController.java new file mode 100644 index 0000000..40a57c9 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalarySlipSendRecordController.java @@ -0,0 +1,182 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.NumberUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.slip.sendrecord.HrmSalarySlipSendEmployeeReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.slip.sendrecord.HrmSalarySlipSendEmployeeRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.slip.sendrecord.HrmSalarySlipSendRecordPageReqVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.slip.sendrecord.HrmSalarySlipSendRecordRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.slip.sendrecord.HrmSalarySlipSendReqVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.monthrecord.HrmSalaryMonthEmployeeRecordDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.slip.HrmSalarySlipSendRecordDO; +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService; +import cn.iocoder.yudao.module.hrm.service.salary.slip.HrmSalarySlipService; +import cn.iocoder.yudao.module.hrm.service.salary.slip.HrmSalarySlipSendRecordService; +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 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.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.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; + +@Tag(name = "管理后台 - HRM 工资条发放记录") +@RestController +@RequestMapping("/hrm/salary/slip-send-record") +@Validated +public class HrmSalarySlipSendRecordController { + + @Resource + private HrmSalarySlipSendRecordService salarySlipSendRecordService; + @Resource + private HrmSalarySlipService salarySlipService; + @Resource + private HrmEmployeeService employeeService; + + @Resource + private AdminUserApi adminUserApi; + @Resource + private DeptApi deptApi; + + @PostMapping("/create") + @Operation(summary = "发放工资条") + @PreAuthorize("@ss.hasPermission('hrm:salary:slip:create')") + public CommonResult sendSalarySlip(@Valid @RequestBody HrmSalarySlipSendReqVO reqVO) { + return success(salarySlipSendRecordService.sendSalarySlip(reqVO)); + } + + @GetMapping("/employee-page") + @Operation(summary = "获得工资条待发员工分页") + @PreAuthorize("@ss.hasPermission('hrm:salary:slip:query')") + public CommonResult> getSalarySlipSendEmployeePage( + @Valid HrmSalarySlipSendEmployeeReqVO reqVO) { + // 1. 获得员工月度工资分页 + PageResult pageResult = + salarySlipSendRecordService.getSalarySlipSendEmployeePage(reqVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty(pageResult.getTotal())); + } + + // 2.1 获取员工和发放状态 + Map employeeMap = employeeService.getEmployeeMap( + convertSet(pageResult.getList(), HrmSalaryMonthEmployeeRecordDO::getEmployeeId)); + Set sentMonthEmployeeRecordIds = salarySlipService.getSentMonthEmployeeRecordIdSet( + convertSet(pageResult.getList(), HrmSalaryMonthEmployeeRecordDO::getId)); + // 2.2 拼接响应 + return success(new PageResult<>(buildSalarySlipSendEmployeeRespVOList( + pageResult.getList(), employeeMap, sentMonthEmployeeRecordIds), pageResult.getTotal())); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除工资条发放记录") + @Parameter(name = "id", description = "发放记录编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:salary:slip:delete')") + public CommonResult deleteSalarySlipSendRecord(@RequestParam("id") Long id) { + salarySlipSendRecordService.deleteSalarySlipSendRecord(id); + return success(true); + } + + @GetMapping("/page") + @Operation(summary = "获得工资条发放记录分页") + @PreAuthorize("@ss.hasPermission('hrm:salary:slip:query')") + public CommonResult> getSalarySlipSendRecordPage( + @Validated HrmSalarySlipSendRecordPageReqVO reqVO) { + PageResult pageResult = + salarySlipSendRecordService.getSalarySlipSendRecordPage(reqVO); + return success(new PageResult<>(buildSalarySlipSendRecordRespVOList(pageResult.getList()), + pageResult.getTotal())); + } + + @GetMapping("/get") + @Operation(summary = "获得工资条发放记录") + @Parameter(name = "id", description = "发放记录编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:salary:slip:query')") + public CommonResult getSalarySlipSendRecord(@RequestParam("id") Long id) { + HrmSalarySlipSendRecordDO slipSendRecord = + salarySlipSendRecordService.getSalarySlipSendRecord(id); + if (slipSendRecord == null) { + return success(null); + } + return success(CollUtil.getFirst(buildSalarySlipSendRecordRespVOList( + Collections.singletonList(slipSendRecord)))); + } + + // ==================== 拼接 VO ==================== + + private List buildSalarySlipSendRecordRespVOList( + List slipSendRecords) { + if (CollUtil.isEmpty(slipSendRecords)) { + return Collections.emptyList(); + } + // 1. 获取关联数据 + Map readCountMap = salarySlipService.getSalarySlipReadCountMap( + convertSet(slipSendRecords, HrmSalarySlipSendRecordDO::getId)); + Map creatorMap = adminUserApi.getUserMap( + convertSet(slipSendRecords, record -> NumberUtil.isNumber(record.getCreator()) + ? NumberUtils.parseLong(record.getCreator()) : null)); + + // 2. 拼接响应 + return BeanUtils.toBean(slipSendRecords, HrmSalarySlipSendRecordRespVO.class, record -> { + record.setReadCount(readCountMap.getOrDefault(record.getId(), 0L).intValue()) + .setCreatorName(record.getCreator()); + MapUtils.findAndThen(creatorMap, NumberUtil.isNumber(record.getCreator()) + ? NumberUtils.parseLong(record.getCreator()) : null, + creator -> record.setCreatorName(creator.getNickname())); + }); + } + + private List buildSalarySlipSendEmployeeRespVOList( + List employeeRecords, + Map employeeMap, Set sentMonthEmployeeRecordIds) { + if (CollUtil.isEmpty(employeeRecords)) { + return Collections.emptyList(); + } + // 1. 获取部门信息 + Set deptIds = convertSet(employeeMap.values(), HrmEmployeeDO::getDeptId); + Map deptMap = CollUtil.isEmpty(deptIds) + ? Collections.emptyMap() : deptApi.getDeptMap(deptIds); + + // 2. 拼接响应 + return convertList(employeeRecords, employeeRecord -> { + HrmSalarySlipSendEmployeeRespVO respVO = + BeanUtils.toBean(employeeRecord, HrmSalarySlipSendEmployeeRespVO.class); + respVO.setMonthEmployeeRecordId(employeeRecord.getId()); + respVO.setSent(sentMonthEmployeeRecordIds.contains(employeeRecord.getId())); + MapUtils.findAndThen(employeeMap, employeeRecord.getEmployeeId(), employee -> { + respVO.setEmployeeName(employee.getName()).setJobNumber(employee.getJobNumber()) + .setMobile(employee.getMobile()).setDeptId(employee.getDeptId()) + .setPostName(employee.getPostName()); + MapUtils.findAndThen(deptMap, employee.getDeptId(), + dept -> respVO.setDeptName(dept.getName())); + }); + return respVO; + }); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalarySlipTemplateController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalarySlipTemplateController.java new file mode 100644 index 0000000..e7dba6a --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalarySlipTemplateController.java @@ -0,0 +1,109 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary; + +import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.config.HrmSalaryOptionDO; +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.salary.vo.slip.template.HrmSalarySlipTemplateOptionVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.slip.template.HrmSalarySlipTemplateRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.slip.template.HrmSalarySlipTemplateSaveReqVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.slip.HrmSalarySlipTemplateDO; +import cn.iocoder.yudao.module.hrm.service.salary.slip.HrmSalarySlipTemplateService; +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.ArrayList; +import java.util.List; + +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.module.hrm.dal.dataobject.salary.config.HrmSalaryOptionDO.ROOT_PARENT_CODE; + +@Tag(name = "管理后台 - HRM 工资条模板") +@RestController +@RequestMapping("/hrm/salary/slip-template") +@Validated +public class HrmSalarySlipTemplateController { + + @Resource + private HrmSalarySlipTemplateService salarySlipTemplateService; + + @PostMapping("/create") + @Operation(summary = "创建工资条模板") + @PreAuthorize("@ss.hasPermission('hrm:salary:slip:update')") + public CommonResult createSalarySlipTemplate( + @Valid @RequestBody HrmSalarySlipTemplateSaveReqVO createReqVO) { + return success(salarySlipTemplateService.createSalarySlipTemplate(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新工资条模板") + @PreAuthorize("@ss.hasPermission('hrm:salary:slip:update')") + public CommonResult updateSalarySlipTemplate( + @Valid @RequestBody HrmSalarySlipTemplateSaveReqVO updateReqVO) { + salarySlipTemplateService.updateSalarySlipTemplate(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除工资条模板") + @Parameter(name = "id", description = "模板编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:salary:slip:delete')") + public CommonResult deleteSalarySlipTemplate(@RequestParam("id") Long id) { + salarySlipTemplateService.deleteSalarySlipTemplate(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得工资条模板") + @Parameter(name = "id", description = "工资条模板编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:salary:slip:query')") + public CommonResult getSalarySlipTemplate(@RequestParam("id") Long id) { + return success(buildSalarySlipTemplateRespVO( + salarySlipTemplateService.getSalarySlipTemplate(id))); + } + + @GetMapping("/list") + @Operation(summary = "获得工资条模板列表") + @PreAuthorize("@ss.hasPermission('hrm:salary:slip:query')") + public CommonResult> getSalarySlipTemplateList() { + return success(convertList(salarySlipTemplateService.getSalarySlipTemplateList(), + this::buildSalarySlipTemplateRespVO)); + } + + // ==================== 拼接 VO ==================== + + private HrmSalarySlipTemplateRespVO buildSalarySlipTemplateRespVO(HrmSalarySlipTemplateDO template) { + if (template == null) { + return null; + } + return BeanUtils.toBean(template, HrmSalarySlipTemplateRespVO.class) + .setOptions(buildSalarySlipTemplateOptionRespVOList(template.getOptions())); + } + + private List buildSalarySlipTemplateOptionRespVOList( + List options) { + List result = new ArrayList<>(convertList(options, + option -> BeanUtils.toBean(option, HrmSalarySlipTemplateOptionVO.class) + .setParentCode(ROOT_PARENT_CODE))); + for (HrmSalarySlipTemplateDO.Option option : options) { + result.addAll(convertList(option.getChildren(), child -> + BeanUtils.toBean(child, HrmSalarySlipTemplateOptionVO.class) + .setParentCode(option.getCode()))); + } + return result; + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryTaxRuleController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryTaxRuleController.java new file mode 100644 index 0000000..cb2ff66 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryTaxRuleController.java @@ -0,0 +1,90 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary; + +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.salary.vo.taxrule.HrmSalaryTaxRuleRespVO; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.taxrule.HrmSalaryTaxRuleSaveReqVO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.config.HrmSalaryTaxRuleDO; +import cn.iocoder.yudao.module.hrm.service.salary.config.HrmSalaryGroupService; +import cn.iocoder.yudao.module.hrm.service.salary.config.HrmSalaryTaxRuleService; +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 java.util.Map; + +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/salary/tax-rule") +@Validated +public class HrmSalaryTaxRuleController { + + @Resource + private HrmSalaryTaxRuleService salaryTaxRuleService; + @Resource + private HrmSalaryGroupService salaryGroupService; + + @PostMapping("/create") + @Operation(summary = "创建计税规则") + @PreAuthorize("@ss.hasPermission('hrm:salary:tax-rule:create')") + public CommonResult createSalaryTaxRule(@Valid @RequestBody HrmSalaryTaxRuleSaveReqVO reqVO) { + return success(salaryTaxRuleService.createSalaryTaxRule(reqVO)); + } + + @PutMapping("/update") + @Operation(summary = "修改计税规则") + @PreAuthorize("@ss.hasPermission('hrm:salary:tax-rule:update')") + public CommonResult updateSalaryTaxRule(@Valid @RequestBody HrmSalaryTaxRuleSaveReqVO reqVO) { + salaryTaxRuleService.updateSalaryTaxRule(reqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除计税规则") + @Parameter(name = "id", description = "计税规则编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:salary:tax-rule:delete')") + public CommonResult deleteSalaryTaxRule(@RequestParam("id") Long id) { + salaryTaxRuleService.deleteSalaryTaxRule(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得计税规则") + @Parameter(name = "id", description = "计税规则编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('hrm:salary:tax-rule:query')") + public CommonResult getSalaryTaxRule(@RequestParam("id") Long id) { + HrmSalaryTaxRuleDO taxRule = salaryTaxRuleService.getSalaryTaxRule(id); + return success(BeanUtils.toBean(taxRule, HrmSalaryTaxRuleRespVO.class)); + } + + @GetMapping("/list") + @Operation(summary = "获得计税规则列表") + @PreAuthorize("@ss.hasPermission('hrm:salary:tax-rule:query')") + public CommonResult> getSalaryTaxRuleList() { + // 1. 获得计税规则及其使用数量 + List taxRules = salaryTaxRuleService.getSalaryTaxRuleList(); + Map usedGroupCountMap = salaryGroupService.getSalaryGroupCountMapByTaxRuleIds( + convertSet(taxRules, HrmSalaryTaxRuleDO::getId)); + + // 2. 拼接响应 + return success(BeanUtils.toBean(taxRules, HrmSalaryTaxRuleRespVO.class, + rule -> rule.setUsedGroupCount(usedGroupCountMap.getOrDefault(rule.getId(), 0L)))); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/changerecord/HrmSalaryChangeRecordRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/changerecord/HrmSalaryChangeRecordRespVO.java new file mode 100644 index 0000000..c02b1f5 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/changerecord/HrmSalaryChangeRecordRespVO.java @@ -0,0 +1,80 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.changerecord; + +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.controller.admin.salary.vo.option.HrmSalaryOptionValueVO; +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; +import java.util.List; + +@Schema(description = "管理后台 - HRM 定薪/调薪记录 Response VO") +@Data +@ExcelIgnoreUnannotated +public class HrmSalaryChangeRecordRespVO { + + @ExcelProperty(value = "记录编号", index = 0) + @Schema(description = "记录编号") + private Long id; + + @ExcelProperty(value = "员工编号", index = 1) + @Schema(description = "员工编号") + private Long employeeId; + + @Schema(description = "记录类型") + private Integer recordType; + + @ExcelProperty(value = "记录类型", index = 2) + @Schema(description = "记录类型") + private String recordTypeName; + + @ExcelProperty(value = "调整原因", index = 3, converter = DictConvert.class) + @DictFormat(DictTypeConstants.HRM_SALARY_CHANGE_REASON) + @Schema(description = "调整原因") + private Integer changeReason; + + @ExcelProperty(value = "生效日期", index = 4) + @Schema(description = "生效日期") + private LocalDateTime effectTime; + + @ExcelProperty(value = "调整前正式工资", index = 5) + @Schema(description = "调整前正式工资") + private BigDecimal beforeTotal; + + @ExcelProperty(value = "调整后正式工资", index = 6) + @Schema(description = "调整后正式工资") + private BigDecimal afterTotal; + + @ExcelProperty(value = "调整前试用期工资", index = 7) + @Schema(description = "调整前试用期工资") + private BigDecimal probationBeforeTotal; + + @ExcelProperty(value = "调整后试用期工资", index = 8) + @Schema(description = "调整后试用期工资") + private BigDecimal probationAfterTotal; + + @ExcelProperty(value = "状态", index = 9, converter = DictConvert.class) + @DictFormat(DictTypeConstants.HRM_SALARY_CHANGE_RECORD_STATUS) + @Schema(description = "状态") + private Integer status; + + @ExcelProperty(value = "备注", index = 10) + @Schema(description = "备注") + private String remark; + + @Schema(description = "薪资项列表") + private List salaryOptions; + + @Schema(description = "试用期薪资项列表") + private List probationSalaryOptions; + + @ExcelProperty(value = "创建时间", index = 11) + @Schema(description = "创建时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/changetemplate/HrmSalaryChangeOptionVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/changetemplate/HrmSalaryChangeOptionVO.java new file mode 100644 index 0000000..175d310 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/changetemplate/HrmSalaryChangeOptionVO.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.changetemplate; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - HRM 调薪项 VO") +@Data +public class HrmSalaryChangeOptionVO { + + @Schema(description = "薪资项名称", example = "基本工资") + private String name; + + @Schema(description = "薪资项编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "10101") + @NotNull(message = "薪资项编码不能为空") + private Integer code; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/changetemplate/HrmSalaryChangeTemplateRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/changetemplate/HrmSalaryChangeTemplateRespVO.java new file mode 100644 index 0000000..7be2224 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/changetemplate/HrmSalaryChangeTemplateRespVO.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.changetemplate; + +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 HrmSalaryChangeTemplateRespVO { + + @Schema(description = "调薪模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "模板名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "研发调薪模板") + private String name; + + @Schema(description = "是否默认模板", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean defaultStatus; + + @Schema(description = "调薪项配置") + private List options; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/changetemplate/HrmSalaryChangeTemplateSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/changetemplate/HrmSalaryChangeTemplateSaveReqVO.java new file mode 100644 index 0000000..696c1b7 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/changetemplate/HrmSalaryChangeTemplateSaveReqVO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.changetemplate; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +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 HrmSalaryChangeTemplateSaveReqVO { + + @Schema(description = "调薪模板编号", example = "1024") + private Long id; + + @Schema(description = "模板名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "研发调薪模板") + @NotBlank(message = "模板名称不能为空") + @Size(max = 64, message = "模板名称不能超过 64 个字符") + private String name; + + @Schema(description = "是否默认模板", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是否默认模板不能为空") + private Boolean defaultStatus; + + @Schema(description = "调薪项配置", requiredMode = Schema.RequiredMode.REQUIRED) + @Valid + @NotEmpty(message = "调薪项配置不能为空") + private List options; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/config/HrmSalaryConfigCreateReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/config/HrmSalaryConfigCreateReqVO.java new file mode 100644 index 0000000..c532123 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/config/HrmSalaryConfigCreateReqVO.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.config; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.hrm.enums.salary.config.HrmSalarySocialSecurityMonthTypeEnum; +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; + +@Schema(description = "管理后台 - HRM 计薪配置创建 Request VO") +@Data +public class HrmSalaryConfigCreateReqVO { + + @Schema(description = "计薪周期开始日", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "计薪周期开始日不能为空") + @Min(value = 1, message = "计薪周期开始日必须在 1 到 31 之间") + @Max(value = 31, message = "计薪周期开始日必须在 1 到 31 之间") + private Integer cycleStartDay; + + @Schema(description = "社保对应月份类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "社保对应月份类型不能为空") + @InEnum(value = HrmSalarySocialSecurityMonthTypeEnum.class, message = "社保对应月份类型必须是 {value}") + private Integer socialSecurityMonthType; + + @Schema(description = "工资开始年份", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026") + @NotNull(message = "工资开始年份不能为空") + @Min(value = 2000, message = "工资开始年份不能小于 2000") + @Max(value = 2100, message = "工资开始年份不能大于 2100") + private Integer startYear; + + @Schema(description = "工资开始月份", requiredMode = Schema.RequiredMode.REQUIRED, example = "7") + @NotNull(message = "工资开始月份不能为空") + @Min(value = 1, message = "工资开始月份必须在 1 到 12 之间") + @Max(value = 12, message = "工资开始月份必须在 1 到 12 之间") + private Integer startMonth; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/config/HrmSalaryConfigRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/config/HrmSalaryConfigRespVO.java new file mode 100644 index 0000000..33a329a --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/config/HrmSalaryConfigRespVO.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.config; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - HRM 计薪配置 Response VO") +@Data +public class HrmSalaryConfigRespVO { + + @Schema(description = "配置编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "计薪周期开始日", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer cycleStartDay; + + @Schema(description = "计薪周期结束日", requiredMode = Schema.RequiredMode.REQUIRED, example = "31") + private Integer cycleEndDay; + + @Schema(description = "社保对应月份类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer socialSecurityMonthType; + + @Schema(description = "工资开始年份", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026") + private Integer startYear; + + @Schema(description = "工资开始月份", requiredMode = Schema.RequiredMode.REQUIRED, example = "7") + private Integer startMonth; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/config/HrmSalaryConfigUpdateReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/config/HrmSalaryConfigUpdateReqVO.java new file mode 100644 index 0000000..b6c181b --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/config/HrmSalaryConfigUpdateReqVO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.config; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.hrm.enums.salary.config.HrmSalarySocialSecurityMonthTypeEnum; +import cn.iocoder.yudao.module.hrm.framework.operatelog.core.HrmSalarySocialSecurityMonthTypeParseFunction; +import com.mzt.logapi.starter.annotation.DiffLogField; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - HRM 计薪配置更新 Request VO") +@Data +public class HrmSalaryConfigUpdateReqVO { + + @Schema(description = "社保对应月份类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "社保对应月份类型不能为空") + @InEnum(value = HrmSalarySocialSecurityMonthTypeEnum.class, message = "社保对应月份类型必须是 {value}") + @DiffLogField(name = "社保对应月份", function = HrmSalarySocialSecurityMonthTypeParseFunction.NAME) + private Integer socialSecurityMonthType; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/employeeinfo/HrmSalaryEmployeeInfoImportRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/employeeinfo/HrmSalaryEmployeeInfoImportRespVO.java new file mode 100644 index 0000000..60b9c7b --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/employeeinfo/HrmSalaryEmployeeInfoImportRespVO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.employeeinfo; + +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 HrmSalaryEmployeeInfoImportRespVO { + + @Schema(description = "导入成功的工号数组", requiredMode = Schema.RequiredMode.REQUIRED) + private List successJobNumbers; + + @Schema(description = "导入失败的员工集合,key 为工号或行号,value 为失败原因", requiredMode = Schema.RequiredMode.REQUIRED) + private Map failureJobNumbers; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/employeeinfo/HrmSalaryEmployeeInfoPageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/employeeinfo/HrmSalaryEmployeeInfoPageReqVO.java new file mode 100644 index 0000000..2601ef5 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/employeeinfo/HrmSalaryEmployeeInfoPageReqVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.employeeinfo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +@Schema(description = "管理后台 - HRM 员工薪资信息分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class HrmSalaryEmployeeInfoPageReqVO extends PageParam { + + @Schema(description = "员工编号", example = "1024") + private Long employeeId; + + @Schema(description = "员工编号列表", example = "[1024, 1025]") + private List employeeIds; + + @Schema(description = "调整类型", example = "1") + private Integer changeType; + + @Schema(description = "员工姓名或工号", example = "张三") + private String search; + + @Schema(description = "部门编号", example = "100") + private Long deptId; + + @Schema(description = "岗位名称", example = "Java 工程师") + private String postName; + + @Schema(description = "员工状态", example = "1") + private Integer status; + + @Schema(description = "入职状态", example = "1") + private Integer entryStatus; + + @Schema(description = "员工状态页签", example = "1") + private Integer statusCategory; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/employeeinfo/HrmSalaryEmployeeInfoRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/employeeinfo/HrmSalaryEmployeeInfoRespVO.java new file mode 100644 index 0000000..c1969c0 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/employeeinfo/HrmSalaryEmployeeInfoRespVO.java @@ -0,0 +1,97 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.employeeinfo; + +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.controller.admin.salary.vo.option.HrmSalaryOptionValueVO; +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; +import java.util.List; + +@Schema(description = "管理后台 - HRM 员工薪资信息 Response VO") +@Data +@ExcelIgnoreUnannotated +public class HrmSalaryEmployeeInfoRespVO { + + @ExcelProperty(value = "员工薪资编号", index = 0) + @Schema(description = "员工薪资编号") + private Long id; + + @ExcelProperty(value = "员工编号", index = 1) + @Schema(description = "员工编号") + private Long employeeId; + + @ExcelProperty(value = "员工姓名", index = 2) + @Schema(description = "员工姓名") + private String employeeName; + + @ExcelProperty(value = "工号", index = 3) + @Schema(description = "工号") + private String jobNumber; + + @Schema(description = "手机号") + private String mobile; + + @ExcelProperty(value = "部门编号", index = 4) + @Schema(description = "部门编号") + private Long deptId; + + @Schema(description = "部门名称") + private String deptName; + + @Schema(description = "岗位名称") + private String postName; + + @Schema(description = "入职状态") + private Integer entryStatus; + + @Schema(description = "员工状态") + private Integer status; + + @Schema(description = "入职时间") + private LocalDateTime entryTime; + + @Schema(description = "转正时间") + private LocalDateTime regularTime; + + @ExcelProperty(value = "调整原因", index = 6, converter = DictConvert.class) + @DictFormat(DictTypeConstants.HRM_SALARY_CHANGE_REASON) + @Schema(description = "调整原因") + private Integer changeReason; + + @ExcelProperty(value = "生效日期", index = 7) + @Schema(description = "生效日期") + private LocalDateTime effectTime; + + @ExcelProperty(value = "薪资状态", index = 5, converter = DictConvert.class) + @DictFormat(DictTypeConstants.HRM_SALARY_CHANGE_TYPE) + @Schema(description = "薪资状态") + private Integer changeType; + + @ExcelProperty(value = "试用期工资", index = 9) + @Schema(description = "试用期工资") + private BigDecimal probationSalary; + + @ExcelProperty(value = "正式工资", index = 8) + @Schema(description = "正式工资") + private BigDecimal regularSalary; + + @ExcelProperty(value = "备注", index = 10) + @Schema(description = "备注") + private String remark; + + @Schema(description = "薪资项列表") + private List salaryOptions; + + @Schema(description = "试用期薪资项列表") + private List probationSalaryOptions; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/employeeinfo/HrmSalaryEmployeeInfoUpdateListReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/employeeinfo/HrmSalaryEmployeeInfoUpdateListReqVO.java new file mode 100644 index 0000000..a114d72 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/employeeinfo/HrmSalaryEmployeeInfoUpdateListReqVO.java @@ -0,0 +1,59 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.employeeinfo; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.option.HrmSalaryOptionValueVO; +import cn.iocoder.yudao.module.hrm.enums.salary.employee.HrmSalaryBatchAdjustTypeEnum; +import cn.iocoder.yudao.module.hrm.enums.salary.employee.HrmSalaryChangeReasonEnum; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - HRM 批量调薪 Request VO") +@Data +public class HrmSalaryEmployeeInfoUpdateListReqVO { + + @Schema(description = "员工编号列表", example = "[1024, 1025]") + private List<@NotNull(message = "员工不能为空") Long> employeeIds; + + @Schema(description = "部门编号列表", example = "[100, 101]") + private List<@NotNull(message = "部门不能为空") Long> deptIds; + + @Schema(description = "调薪方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "调薪方式不能为空") + @InEnum(value = HrmSalaryBatchAdjustTypeEnum.class, message = "调薪方式必须是 {value}") + private Integer type; + + @Schema(description = "调整原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "调薪原因不能为空") + @InEnum(value = HrmSalaryChangeReasonEnum.class, message = "调薪原因必须是 {value}") + private Integer changeReason; + + @Schema(description = "生效日期", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "调薪生效日期不能为空") + private LocalDateTime effectTime; + + @Schema(description = "备注", example = "年度统一调薪") + @Size(max = 500, message = "备注不能超过 500 个字符") + private String remark; + + @Schema(description = "薪资项列表", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "调薪项不能为空") + @Valid + private List salaryOptions; + + @AssertTrue(message = "至少需要选择一个部门或员工") + @JsonIgnore + public boolean isScopeValid() { + return CollUtil.isNotEmpty(deptIds) || CollUtil.isNotEmpty(employeeIds); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/employeeinfo/HrmSalaryEmployeeInfoUpdateListRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/employeeinfo/HrmSalaryEmployeeInfoUpdateListRespVO.java new file mode 100644 index 0000000..4f8e576 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/employeeinfo/HrmSalaryEmployeeInfoUpdateListRespVO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.employeeinfo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +@Schema(description = "管理后台 - HRM 批量调薪 Response VO") +@Data +public class HrmSalaryEmployeeInfoUpdateListRespVO { + + @Schema(description = "成功员工编号列表") + private List successEmployeeIds = new ArrayList<>(); + + @Schema(description = "失败员工及原因") + private Map failureEmployeeReasons = new LinkedHashMap<>(); + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/employeeinfo/HrmSalaryEmployeeInfoUpdateReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/employeeinfo/HrmSalaryEmployeeInfoUpdateReqVO.java new file mode 100644 index 0000000..20bd1a0 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/employeeinfo/HrmSalaryEmployeeInfoUpdateReqVO.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.employeeinfo; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.option.HrmSalaryOptionValueVO; +import cn.iocoder.yudao.module.hrm.enums.salary.employee.HrmSalaryChangeReasonEnum; +import cn.iocoder.yudao.module.hrm.enums.salary.employee.HrmSalaryChangeRecordTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - HRM 员工薪资信息修改 Request VO") +@Data +public class HrmSalaryEmployeeInfoUpdateReqVO { + + @Schema(description = "定薪/调薪记录编号;编辑待生效或已取消记录时传入", example = "1024") + private Long id; + + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "员工不能为空") + private Long employeeId; + + @Schema(description = "记录类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "记录类型不能为空") + @InEnum(value = HrmSalaryChangeRecordTypeEnum.class, message = "记录类型必须是 {value}") + private Integer recordType; + + @Schema(description = "调整原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "调整原因不能为空") + @InEnum(value = HrmSalaryChangeReasonEnum.class, message = "调整原因必须是 {value}") + private Integer changeReason; + + @Schema(description = "生效日期") + private LocalDateTime effectTime; + + @Schema(description = "备注", example = "年度调薪") + @Size(max = 500, message = "备注不能超过 500 个字符") + private String remark; + + @Schema(description = "薪资项列表") + @Valid + private List salaryOptions; + + @Schema(description = "试用期薪资项列表") + @Valid + private List probationSalaryOptions; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/group/HrmSalaryGroupPageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/group/HrmSalaryGroupPageReqVO.java new file mode 100644 index 0000000..92cba98 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/group/HrmSalaryGroupPageReqVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.group; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Schema(description = "管理后台 - HRM 薪资组分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class HrmSalaryGroupPageReqVO extends PageParam { + + @Schema(description = "薪资组名称", example = "总部薪资组") + private String name; + + @Schema(description = "计税规则编号", example = "1024") + private Long taxRuleId; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/group/HrmSalaryGroupRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/group/HrmSalaryGroupRespVO.java new file mode 100644 index 0000000..f01bd56 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/group/HrmSalaryGroupRespVO.java @@ -0,0 +1,47 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.group; + +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 +public class HrmSalaryGroupRespVO { + + @Schema(description = "薪资组编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "薪资组名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "总部薪资组") + private String name; + + @Schema(description = "月计薪标准", example = "21.75") + private BigDecimal salaryStandard; + + @Schema(description = "转正、调薪月规则") + private String changeRule; + + @Schema(description = "计税规则编号", example = "1024") + private Long taxRuleId; + + @Schema(description = "计税规则名称", example = "工资薪金所得税") + private String taxRuleName; + + @Schema(description = "适用部门编号列表") + private List deptIds; + + @Schema(description = "适用部门名称列表") + private List deptNames; + + @Schema(description = "适用员工编号列表") + private List employeeIds; + + @Schema(description = "适用员工名称列表") + private List employeeNames; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/group/HrmSalaryGroupSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/group/HrmSalaryGroupSaveReqVO.java new file mode 100644 index 0000000..b8711ae --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/group/HrmSalaryGroupSaveReqVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.group; + +import cn.hutool.core.collection.CollUtil; +import com.fasterxml.jackson.annotation.JsonIgnore; +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.util.List; + +@Schema(description = "管理后台 - HRM 薪资组保存 Request VO") +@Data +public class HrmSalaryGroupSaveReqVO { + + @Schema(description = "薪资组编号", example = "1024") + private Long id; + + @Schema(description = "薪资组名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "总部薪资组") + @NotBlank(message = "薪资组名称不能为空") + @Size(max = 64, message = "薪资组名称不能超过 64 个字符") + private String name; + + @Schema(description = "计税规则编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "计税规则不能为空") + private Long taxRuleId; + + @Schema(description = "适用部门编号列表", example = "[100, 101]") + private List deptIds; + + @Schema(description = "适用员工编号列表", example = "[1024, 1025]") + private List employeeIds; + + @AssertTrue(message = "适用部门和适用员工不能同时为空") + @JsonIgnore + public boolean isScopeValid() { + return CollUtil.isNotEmpty(deptIds) || CollUtil.isNotEmpty(employeeIds); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/HrmSalaryMonthRecordCreateReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/HrmSalaryMonthRecordCreateReqVO.java new file mode 100644 index 0000000..d15e4c3 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/HrmSalaryMonthRecordCreateReqVO.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.monthrecord; + +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 HrmSalaryMonthRecordCreateReqVO { + + @Schema(description = "年份", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026") + @NotNull(message = "年份不能为空") + @Min(value = 2000, message = "年份不能小于 2000") + @Max(value = 2100, message = "年份不能大于 2100") + 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 = "2026 年 7 月工资表") + @Size(max = 128, message = "标题不能超过 128 个字符") + private String title; + + @Schema(description = "开始日期") + private LocalDateTime startTime; + + @Schema(description = "结束日期") + private LocalDateTime endTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/HrmSalaryMonthRecordPageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/HrmSalaryMonthRecordPageReqVO.java new file mode 100644 index 0000000..1e6eb7e --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/HrmSalaryMonthRecordPageReqVO.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.monthrecord; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.hrm.enums.salary.monthrecord.HrmSalaryMonthRecordStatusEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Schema(description = "管理后台 - HRM 月度工资表分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class HrmSalaryMonthRecordPageReqVO extends PageParam { + + @Schema(description = "年份", example = "2026") + private Integer year; + + @Schema(description = "月份", example = "7") + private Integer month; + + @Schema(description = "核算状态", example = "1") + @InEnum(value = HrmSalaryMonthRecordStatusEnum.class, message = "月度工资表状态必须是 {value}") + private Integer status; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/HrmSalaryMonthRecordRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/HrmSalaryMonthRecordRespVO.java new file mode 100644 index 0000000..5a8a6f5 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/HrmSalaryMonthRecordRespVO.java @@ -0,0 +1,66 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.monthrecord; + +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.option.HrmSalaryOptionNodeRespVO; +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 +public class HrmSalaryMonthRecordRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "年份", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026") + private Integer year; + + @Schema(description = "月份", requiredMode = Schema.RequiredMode.REQUIRED, example = "7") + private Integer month; + + @Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026 年 7 月工资表") + private String title; + + @Schema(description = "开始日期") + private LocalDateTime startTime; + + @Schema(description = "结束日期") + private LocalDateTime endTime; + + @Schema(description = "员工数量") + private Integer employeeCount; + + @Schema(description = "应发工资") + private BigDecimal expectedPaySalary; + + @Schema(description = "个人社保金额") + private BigDecimal personalInsuranceAmount; + + @Schema(description = "个人公积金金额") + private BigDecimal personalProvidentFundAmount; + + @Schema(description = "个人所得税") + private BigDecimal personalTax; + + @Schema(description = "实发工资") + private BigDecimal realPaySalary; + + @Schema(description = "公司社保金额") + private BigDecimal corporateInsuranceAmount; + + @Schema(description = "公司公积金金额") + private BigDecimal corporateProvidentFundAmount; + + @Schema(description = "核算状态") + private Integer status; + + @Schema(description = "薪资项表头") + private List optionHeaders; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/HrmSalaryPayrollReadinessRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/HrmSalaryPayrollReadinessRespVO.java new file mode 100644 index 0000000..6a844df --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/HrmSalaryPayrollReadinessRespVO.java @@ -0,0 +1,91 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.monthrecord; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +@Schema(description = "管理后台 - HRM 薪资核算准备 Response VO") +@Data +public class HrmSalaryPayrollReadinessRespVO { + + @Schema(description = "工资表编号") + private Long monthRecordId; + + @Schema(description = "标题") + private String title; + + @Schema(description = "年份") + private Integer year; + + @Schema(description = "月份") + private Integer month; + + @Schema(description = "开始日期") + private LocalDateTime startTime; + + @Schema(description = "结束日期") + private LocalDateTime endTime; + + @Schema(description = "社保年月") + private String socialSecurityYearMonth; + + @Schema(description = "计薪员工人数") + private Long payrollEmployeeCount; + + @Schema(description = "已定薪员工人数") + private Long salaryEmployeeCount; + + @Schema(description = "未定薪员工人数") + private Long noSalaryEmployeeCount; + + @Schema(description = "未分配薪资组员工人数") + private Long noSalaryGroupEmployeeCount; + + @Schema(description = "异动员工人数") + private Long changeEmployeeCount; + + @Schema(description = "变更类型数量映射") + private Map changeTypeCountMap; + + @Schema(description = "未定薪员工列表") + private List noSalaryEmployees; + @Schema(description = "未分配薪资组员工列表") + private List noSalaryGroupEmployees; + + @Schema(description = "管理后台 - HRM 薪资核算准备员工 Response VO") + @Data + public static class Employee { + + @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 entryStatus; + + @Schema(description = "员工状态") + private Integer status; + + @Schema(description = "入职时间") + private LocalDateTime entryTime; + + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/employee/HrmSalaryEmployeeMonthRecordPageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/employee/HrmSalaryEmployeeMonthRecordPageReqVO.java new file mode 100644 index 0000000..b7b9773 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/employee/HrmSalaryEmployeeMonthRecordPageReqVO.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.monthrecord.employee; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.hrm.enums.salary.monthrecord.HrmSalaryMonthRecordStatusEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Schema(description = "管理后台 - HRM 员工月度工资分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class HrmSalaryEmployeeMonthRecordPageReqVO extends PageParam { + + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "员工编号不能为空") + private Long employeeId; + + @Schema(description = "月度工资表状态", example = "10") + @InEnum(value = HrmSalaryMonthRecordStatusEnum.class, message = "月度工资表状态必须是 {value}") + private Integer monthRecordStatus; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/employee/HrmSalaryMonthEmployeeRecordListReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/employee/HrmSalaryMonthEmployeeRecordListReqVO.java new file mode 100644 index 0000000..6be8ad2 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/employee/HrmSalaryMonthEmployeeRecordListReqVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.monthrecord.employee; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.hrm.enums.salary.employee.HrmSalaryEmployeeChangeTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.Collection; +import java.util.List; + +@Schema(description = "管理后台 - HRM 员工工资列表 Request VO") +@Data +public class HrmSalaryMonthEmployeeRecordListReqVO { + + @Schema(description = "工资表编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "工资表编号不能为空") + private Long monthRecordId; + + @Schema(description = "员工编号", example = "1024") + private Long employeeId; + + @Schema(description = "员工姓名", example = "张三") + private String employeeName; + + @Schema(description = "工号", example = "HRM001") + private String jobNumber; + + @Schema(description = "部门编号", example = "100") + private Long deptId; + + @Schema(description = "员工异动类型", example = "1") + @InEnum(value = HrmSalaryEmployeeChangeTypeEnum.class, message = "员工异动分类必须是 {value}") + private Integer employeeChangeType; + + @Schema(hidden = true) + private Collection employeeIds; + + @Schema(hidden = true) + private Boolean salarySlipSent; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/employee/HrmSalaryMonthEmployeeRecordPageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/employee/HrmSalaryMonthEmployeeRecordPageReqVO.java new file mode 100644 index 0000000..65bf102 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/employee/HrmSalaryMonthEmployeeRecordPageReqVO.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.monthrecord.employee; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.hrm.enums.salary.employee.HrmSalaryEmployeeChangeTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +@Schema(description = "管理后台 - HRM 员工工资表分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class HrmSalaryMonthEmployeeRecordPageReqVO extends PageParam { + + @Schema(description = "工资表编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "工资表编号不能为空") + private Long monthRecordId; + + @Schema(description = "员工编号", example = "1024") + private Long employeeId; + + @Schema(description = "员工姓名", example = "张三") + private String employeeName; + + @Schema(description = "工号", example = "HRM001") + private String jobNumber; + + @Schema(description = "部门编号", example = "100") + private Long deptId; + + @Schema(description = "员工异动类型", example = "1") + @InEnum(value = HrmSalaryEmployeeChangeTypeEnum.class, message = "员工异动分类必须是 {value}") + private Integer employeeChangeType; + + @Schema(description = "员工编号列表", example = "[1024, 1025]") + private List employeeIds; + + @Schema(hidden = true) + private Boolean salarySlipSent; +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/employee/HrmSalaryMonthEmployeeRecordRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/employee/HrmSalaryMonthEmployeeRecordRespVO.java new file mode 100644 index 0000000..578ac76 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/employee/HrmSalaryMonthEmployeeRecordRespVO.java @@ -0,0 +1,68 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.monthrecord.employee; + +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.option.HrmSalaryOptionValueVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +@Schema(description = "管理后台 - HRM 员工月度工资 Response VO") +@Data +public class HrmSalaryMonthEmployeeRecordRespVO { + + @Schema(description = "编号") + private Long id; + + @Schema(description = "工资表编号") + private Long monthRecordId; + + @Schema(description = "员工编号") + private Long employeeId; + + @Schema(description = "年份") + private Integer year; + + @Schema(description = "月份") + private Integer month; + + @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 BigDecimal actualWorkDay; + + @Schema(description = "应出勤天数") + private BigDecimal needWorkDay; + + @Schema(description = "应发工资") + private BigDecimal expectedPaySalary; + + @Schema(description = "应税工资") + private BigDecimal taxableSalary; + + @Schema(description = "个人所得税") + private BigDecimal personalTax; + + @Schema(description = "实发工资") + private BigDecimal realPaySalary; + + @Schema(description = "绩效系数") + private BigDecimal performanceCoefficient; + + @Schema(description = "薪资项值列表") + private List optionValues; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/employee/HrmSalaryMonthEmployeeRecordUpdateReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/employee/HrmSalaryMonthEmployeeRecordUpdateReqVO.java new file mode 100644 index 0000000..8c4a103 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/employee/HrmSalaryMonthEmployeeRecordUpdateReqVO.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.monthrecord.employee; + +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.option.HrmSalaryOptionValueVO; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - HRM 员工工资项修改 Request VO") +@Data +public class HrmSalaryMonthEmployeeRecordUpdateReqVO { + + @Schema(description = "员工月度工资记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "员工工资记录编号不能为空") + private Long id; + + @Schema(description = "薪资项值列表") + @Valid + private List optionValues; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/employee/HrmSalaryPerformanceCoefficientReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/employee/HrmSalaryPerformanceCoefficientReqVO.java new file mode 100644 index 0000000..a49f75b --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/monthrecord/employee/HrmSalaryPerformanceCoefficientReqVO.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.monthrecord.employee; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - HRM 绩效系数查询 Request VO") +@Data +public class HrmSalaryPerformanceCoefficientReqVO { + + @Schema(description = "年份", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026") + @NotNull(message = "年份不能为空") + private Integer year; + + @Schema(description = "月份", requiredMode = Schema.RequiredMode.REQUIRED, example = "7") + @NotNull(message = "月份不能为空") + private Integer month; + + @Schema(description = "员工编号列表", example = "[1024, 1025]") + private List employeeIds; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/option/HrmSalaryOptionNodeRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/option/HrmSalaryOptionNodeRespVO.java new file mode 100644 index 0000000..26bf3b4 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/option/HrmSalaryOptionNodeRespVO.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.option; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +@Schema(description = "管理后台 - HRM 工资表薪资项节点 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class HrmSalaryOptionNodeRespVO extends HrmSalaryOptionRespVO { + + @Schema(description = "子薪资项") + private List children; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/option/HrmSalaryOptionRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/option/HrmSalaryOptionRespVO.java new file mode 100644 index 0000000..4e821a2 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/option/HrmSalaryOptionRespVO.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.option; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - HRM 工资表薪资项 Response VO") +@Data +public class HrmSalaryOptionRespVO { + + @Schema(description = "薪资项编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "薪资项编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "10101") + private Integer code; + + @Schema(description = "父薪资项编码", example = "10") + private Integer parentCode; + + @Schema(description = "薪资项名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "基本工资") + private String name; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "是否系统默认项", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean systemFlag; + + @Schema(description = "薪资项类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer type; + + @Schema(description = "是否计税", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean taxEnabled; + + @Schema(description = "是否显示", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean visible; + + @Schema(description = "是否参与计算", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean calculateEnabled; + + @Schema(description = "是否启用", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean enabled; + + @Schema(description = "标准薪资项目录编号", example = "1024") + private Long templateId; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/option/HrmSalaryOptionSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/option/HrmSalaryOptionSaveReqVO.java new file mode 100644 index 0000000..a393e57 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/option/HrmSalaryOptionSaveReqVO.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.option; + +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 HrmSalaryOptionSaveReqVO { + + @Schema(description = "父薪资项编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @NotNull(message = "薪资项分类不能为空") + private Integer parentCode; + + @Schema(description = "薪资项名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "基本工资") + @NotBlank(message = "薪资项名称不能为空") + @Size(max = 64, message = "薪资项名称长度不能超过 64 个字符") + private String name; + + @Schema(description = "备注", example = "固定薪资项") + @Size(max = 255, message = "备注长度不能超过 255 个字符") + private String remark; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/option/HrmSalaryOptionUpdateEnabledReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/option/HrmSalaryOptionUpdateEnabledReqVO.java new file mode 100644 index 0000000..ecca90e --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/option/HrmSalaryOptionUpdateEnabledReqVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.option; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - HRM 薪资项更新启用状态 Request VO") +@Data +public class HrmSalaryOptionUpdateEnabledReqVO { + + @Schema(description = "薪资项编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "薪资项编号不能为空") + private Long id; + + @Schema(description = "是否启用", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "启用状态不能为空") + private Boolean enabled; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/option/HrmSalaryOptionUpdateVisibleReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/option/HrmSalaryOptionUpdateVisibleReqVO.java new file mode 100644 index 0000000..e38b9e0 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/option/HrmSalaryOptionUpdateVisibleReqVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.option; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - HRM 薪资项更新显示状态 Request VO") +@Data +public class HrmSalaryOptionUpdateVisibleReqVO { + + @Schema(description = "薪资项编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "薪资项编号不能为空") + private Long id; + + @Schema(description = "是否显示", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "显示状态不能为空") + private Boolean visible; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/option/HrmSalaryOptionValueVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/option/HrmSalaryOptionValueVO.java new file mode 100644 index 0000000..d816ced --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/option/HrmSalaryOptionValueVO.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.option; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Digits; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - HRM 薪资项值 VO") +@Data +public class HrmSalaryOptionValueVO { + + @Schema(description = "编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "10101") + @NotNull(message = "薪资项编码不能为空") + private Integer code; + + @Schema(description = "薪资选项值名称") + private String name; + + @Schema(description = "值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000.00") + @NotNull(message = "薪资项值不能为空") + @Digits(integer = 10, fraction = 2, message = "薪资项值最多 10 位整数和 2 位小数") + private BigDecimal value; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/HrmSalarySlipOptionRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/HrmSalarySlipOptionRespVO.java new file mode 100644 index 0000000..04b19f6 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/HrmSalarySlipOptionRespVO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.slip; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +@Schema(description = "管理后台 - HRM 工资条项 Response VO") +@Data +public class HrmSalarySlipOptionRespVO { + + @Schema(description = "薪资工资条选项名称") + private String name; + + @Schema(description = "薪资工资条选项类型") + private Integer type; + + @Schema(description = "编码") + private Integer code; + + @Schema(description = "值") + private BigDecimal value; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "排序") + private Integer sort; + + @Schema(description = "子工资条项") + private List children; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/HrmSalarySlipPageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/HrmSalarySlipPageReqVO.java new file mode 100644 index 0000000..b48d36c --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/HrmSalarySlipPageReqVO.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.slip; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +@Schema(description = "管理后台 - HRM 工资条分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class HrmSalarySlipPageReqVO extends PageParam { + @Schema(description = "工资条记录编号", example = "1024") + private Long sendRecordId; + @Schema(description = "员工编号", example = "2048") + private Long employeeId; + @Schema(description = "员工编号列表", example = "[2048, 2049]") + private List employeeIds; + @Schema(description = "搜索关键字", example = "张三") + private String search; + @Schema(description = "部门编号", example = "100") + private Long deptId; + @Schema(description = "已读状态", example = "1") + private Integer readStatus; + @Schema(description = "备注", example = "7 月工资条") + private String remark; +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/HrmSalarySlipRemarkReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/HrmSalarySlipRemarkReqVO.java new file mode 100644 index 0000000..2a4616e --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/HrmSalarySlipRemarkReqVO.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.slip; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +@Schema(description = "管理后台 - HRM 工资条备注 Request VO") +@Data +public class HrmSalarySlipRemarkReqVO { + + @Schema(description = "工资条编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "工资条编号不能为空") + private Long id; + + @Schema(description = "备注", example = "本月绩效奖金已计入") + @Size(max = 500, message = "备注不能超过 500 个字符") + private String remark; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/HrmSalarySlipRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/HrmSalarySlipRespVO.java new file mode 100644 index 0000000..86b98fb --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/HrmSalarySlipRespVO.java @@ -0,0 +1,65 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.slip; + +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 +public class HrmSalarySlipRespVO { + + @Schema(description = "编号") + private Long id; + + @Schema(description = "工资条记录编号") + private Long sendRecordId; + + @Schema(description = "员工工资记录编号") + private Long monthEmployeeRecordId; + + @Schema(description = "员工编号") + private Long employeeId; + + @Schema(description = "员工姓名") + private String employeeName; + + @Schema(description = "工号") + private String jobNumber; + + @Schema(description = "手机号码") + private String mobile; + + @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 = "已读状态") + private Integer readStatus; + + @Schema(description = "实际工资") + private BigDecimal realPaySalary; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "选项列表") + private List options; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/sendrecord/HrmSalarySlipSendEmployeeReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/sendrecord/HrmSalarySlipSendEmployeeReqVO.java new file mode 100644 index 0000000..199d48e --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/sendrecord/HrmSalarySlipSendEmployeeReqVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.slip.sendrecord; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Schema(description = "管理后台 - HRM 工资条待发员工 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class HrmSalarySlipSendEmployeeReqVO extends PageParam { + + @Schema(description = "月度工资表编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "月度工资表编号不能为空") + private Long monthRecordId; + + @Schema(description = "员工姓名、工号或手机号", example = "张三") + private String search; + + @Schema(description = "部门编号", example = "100") + private Long deptId; + + @Schema(description = "是否已发送", example = "false") + private Boolean sent; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/sendrecord/HrmSalarySlipSendEmployeeRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/sendrecord/HrmSalarySlipSendEmployeeRespVO.java new file mode 100644 index 0000000..27359dd --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/sendrecord/HrmSalarySlipSendEmployeeRespVO.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.slip.sendrecord; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - HRM 工资条待发员工 Response VO") +@Data +public class HrmSalarySlipSendEmployeeRespVO { + + @Schema(description = "员工月度工资记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long monthEmployeeRecordId; + + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long employeeId; + + @Schema(description = "员工姓名", example = "张三") + private String employeeName; + + @Schema(description = "工号", example = "HRM001") + private String jobNumber; + + @Schema(description = "手机号码", example = "13800138000") + private String mobile; + + @Schema(description = "部门编号", example = "100") + private Long deptId; + + @Schema(description = "部门名称", example = "研发部") + private String deptName; + + @Schema(description = "岗位名称", example = "Java 工程师") + private String postName; + + @Schema(description = "应发工资", example = "10000") + private BigDecimal expectedPaySalary; + + @Schema(description = "实发工资", example = "8500") + private BigDecimal realPaySalary; + + @Schema(description = "是否已发送", example = "false") + private Boolean sent; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/sendrecord/HrmSalarySlipSendRecordPageReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/sendrecord/HrmSalarySlipSendRecordPageReqVO.java new file mode 100644 index 0000000..c7ca7a2 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/sendrecord/HrmSalarySlipSendRecordPageReqVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.slip.sendrecord; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Schema(description = "管理后台 - HRM 工资条发放记录分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class HrmSalarySlipSendRecordPageReqVO extends PageParam { + + @Schema(description = "年份", example = "2026") + private Integer year; + + @Schema(description = "月份", example = "7") + private Integer month; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/sendrecord/HrmSalarySlipSendRecordRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/sendrecord/HrmSalarySlipSendRecordRespVO.java new file mode 100644 index 0000000..5741c81 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/sendrecord/HrmSalarySlipSendRecordRespVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.slip.sendrecord; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - HRM 工资条发放记录 Response VO") +@Data +public class HrmSalarySlipSendRecordRespVO { + + @Schema(description = "编号") + private Long id; + + @Schema(description = "工资表编号") + private Long monthRecordId; + + @Schema(description = "计薪人数") + private Integer employeeCount; + + @Schema(description = "发薪人数") + private Integer sendEmployeeCount; + + @Schema(description = "已读人数") + private Integer readCount; + + @Schema(description = "年份") + private Integer year; + + @Schema(description = "月份") + private Integer month; + + @Schema(description = "创建人编号") + private String creator; + + @Schema(description = "创建人姓名") + private String creatorName; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/sendrecord/HrmSalarySlipSendReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/sendrecord/HrmSalarySlipSendReqVO.java new file mode 100644 index 0000000..e4557f8 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/sendrecord/HrmSalarySlipSendReqVO.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.slip.sendrecord; + +import cn.hutool.core.collection.CollUtil; +import com.fasterxml.jackson.annotation.JsonIgnore; +import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.slip.template.HrmSalarySlipTemplateOptionVO; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - HRM 工资条发放 Request VO") +@Data +public class HrmSalarySlipSendReqVO { + + @Schema(description = "工资表编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "工资表编号不能为空") + private Long monthRecordId; + + @Schema(description = "是否隐藏空值项", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + @NotNull(message = "是否隐藏空值项不能为空") + private Boolean hideEmpty; + + @Schema(description = "工资条模板项列表") + @Valid + private List options; + + @Schema(description = "是否发放全部筛选结果", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + @NotNull(message = "是否发放全部筛选结果不能为空") + private Boolean all; + + @Schema(description = "员工编号列表", example = "[1024, 1025]") + private List employeeIds; + + @Schema(description = "员工姓名、工号或手机号", example = "张三") + private String search; + + @Schema(description = "部门编号", example = "100") + private Long deptId; + + @Schema(description = "是否已发送", example = "false") + private Boolean sent; + + @AssertTrue(message = "发放已选员工时,员工不能为空") + @JsonIgnore + public boolean isEmployeeIdsValid() { + return Boolean.TRUE.equals(all) || CollUtil.isNotEmpty(employeeIds); + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/template/HrmSalarySlipTemplateOptionVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/template/HrmSalarySlipTemplateOptionVO.java new file mode 100644 index 0000000..035b941 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/template/HrmSalarySlipTemplateOptionVO.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.slip.template; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Data; + +@Schema(description = "管理后台 - HRM 工资条模板项 VO") +@Data +public class HrmSalarySlipTemplateOptionVO { + + @Schema(description = "薪资工资条模板选项名称") + @NotBlank(message = "工资条模板项名称不能为空") + @Size(max = 64, message = "工资条模板项名称不能超过 64 个字符") + private String name; + + @Schema(description = "薪资工资条模板选项类型") + private Integer type; + + @Schema(description = "编码") + private Integer code; + + @Schema(description = "备注") + @Size(max = 255, message = "备注不能超过 255 个字符") + private String remark; + + @Schema(description = "父级编码") + private Integer parentCode; + + @Schema(description = "是否隐藏") + private Boolean hidden; + + @Schema(description = "排序") + private Integer sort; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/template/HrmSalarySlipTemplateRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/template/HrmSalarySlipTemplateRespVO.java new file mode 100644 index 0000000..deae0e7 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/template/HrmSalarySlipTemplateRespVO.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.slip.template; + +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 HrmSalarySlipTemplateRespVO { + + @Schema(description = "编号") + private Long id; + + @Schema(description = "模板名称") + private String name; + + @Schema(description = "是否隐藏空值项") + private Boolean hideEmpty; + + @Schema(description = "是否默认选项") + private Boolean defaultStatus; + + @Schema(description = "选项列表") + private List options; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/template/HrmSalarySlipTemplateSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/template/HrmSalarySlipTemplateSaveReqVO.java new file mode 100644 index 0000000..e0885a8 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/slip/template/HrmSalarySlipTemplateSaveReqVO.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.slip.template; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - HRM 工资条模板新增/修改 Request VO") +@Data +public class HrmSalarySlipTemplateSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "模板名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "标准工资条模板") + @NotBlank(message = "模板名称不能为空") + @Size(max = 64, message = "模板名称不能超过 64 个字符") + private String name; + + @Schema(description = "是否隐藏空值项", example = "true") + private Boolean hideEmpty; + + @Schema(description = "选项列表") + @Valid + private List options; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/taxrule/HrmSalaryTaxRuleRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/taxrule/HrmSalaryTaxRuleRespVO.java new file mode 100644 index 0000000..c93b6fe --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/taxrule/HrmSalaryTaxRuleRespVO.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.taxrule; + +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 +public class HrmSalaryTaxRuleRespVO { + + @Schema(description = "计税规则编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "计税规则名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "工资薪金所得税") + private String name; + + @Schema(description = "计税类型", example = "1") + private Integer type; + + @Schema(description = "是否计税", example = "true") + private Boolean taxEnabled; + + @Schema(description = "起征阈值", example = "5000") + private BigDecimal threshold; + + @Schema(description = "小数位数", example = "2") + private Integer decimalScale; + + @Schema(description = "计税周期类型", example = "1") + private Integer cycleType; + + @Schema(description = "使用该规则的薪资组数量", example = "2") + private Long usedGroupCount; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/taxrule/HrmSalaryTaxRuleSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/taxrule/HrmSalaryTaxRuleSaveReqVO.java new file mode 100644 index 0000000..63a6b6f --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/taxrule/HrmSalaryTaxRuleSaveReqVO.java @@ -0,0 +1,69 @@ +package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.taxrule; + +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.hrm.enums.salary.config.HrmSalaryTaxCycleTypeEnum; +import cn.iocoder.yudao.module.hrm.enums.salary.config.HrmSalaryTaxTypeEnum; +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.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.math.BigDecimal; + + +@Schema(description = "管理后台 - HRM 计税规则保存 Request VO") +@Data +public class HrmSalaryTaxRuleSaveReqVO { + + @Schema(description = "计税规则编号", example = "1024") + private Long id; + + @Schema(description = "计税规则名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "工资薪金所得税") + @NotBlank(message = "计税规则名称不能为空") + @Size(max = 64, message = "计税规则名称长度不能超过 64 个字符") + private String name; + + @Schema(description = "计税类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "计税类型不能为空") + @InEnum(value = HrmSalaryTaxTypeEnum.class, message = "计税类型必须是 {value}") + private Integer type; + + @Schema(description = "是否计税", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是否计税不能为空") + private Boolean taxEnabled; + + @Schema(description = "起征阈值", example = "5000") + @DecimalMin(value = "0.00", message = "起征阈值不能小于 0") + @Digits(integer = 10, fraction = 2, message = "起征阈值最多 10 位整数和 2 位小数") + private BigDecimal threshold; + + @Schema(description = "小数位数", example = "2") + @Min(value = 0, message = "小数位数不能小于 0") + @Max(value = 4, message = "小数位数不能大于 4") + private Integer decimalScale; + + @Schema(description = "计税周期类型", example = "1") + @InEnum(value = HrmSalaryTaxCycleTypeEnum.class, message = "计税周期类型必须是 {value}") + private Integer cycleType; + + @AssertTrue(message = "计税规则的起征阈值和小数位数不能为空") + @JsonIgnore + public boolean isTaxConfigValid() { + return HrmSalaryTaxTypeEnum.NONE.getType().equals(type) || threshold != null && decimalScale != null; + } + + @AssertTrue(message = "工资薪金所得税的计税周期不能为空") + @JsonIgnore + public boolean isCycleTypeValid() { + return ObjUtil.notEqual(HrmSalaryTaxTypeEnum.SALARY.getType(), type) || cycleType != null; + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/package-info.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/package-info.java new file mode 100644 index 0000000..320cae4 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/package-info.java @@ -0,0 +1,7 @@ +/** + * 提供 RESTful API 给前端 + * + * 1. admin 包:提供给管理后台 yudao-ui-admin 前端项目 + * 2. admin.portal 包:提供给 HRM 员工自助端 + */ +package cn.iocoder.yudao.module.hrm.controller; diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/attendance/config/HrmAttendanceGroupDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/attendance/config/HrmAttendanceGroupDO.java new file mode 100644 index 0000000..1f4273d --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/attendance/config/HrmAttendanceGroupDO.java @@ -0,0 +1,290 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.config; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.module.hrm.enums.attendance.config.HrmAttendanceAbsenteeismDeductMethodEnum; +import cn.iocoder.yudao.module.hrm.enums.attendance.config.HrmAttendanceLateEarlyDeductMethodEnum; +import cn.iocoder.yudao.module.hrm.enums.attendance.config.HrmAttendanceHolidayTypeEnum; +import cn.iocoder.yudao.module.hrm.enums.attendance.config.HrmAttendanceMisscardDeductMethodEnum; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.List; + +/** + * HRM 考勤组 DO + * + * @author 芋道源码 + */ +@TableName(value = "hrm_attendance_group", autoResultMap = true) +@KeySequence("hrm_attendance_group_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmAttendanceGroupDO extends BaseDO { + + /** + * 考勤组编号 + */ + @TableId + private Long id; + /** + * 考勤组名称 + */ + private String name; + /** + * 适用部门编号 + * + * 关联 {@link DeptDO#getId()} + */ + @TableField(typeHandler = LongListTypeHandler.class) + private List deptIds; + /** + * 适用员工编号 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + @TableField(typeHandler = LongListTypeHandler.class) + private List employeeIds; + /** + * 是否启用 WiFi 打卡 + */ + private Boolean openWifiCard; + /** + * 是否启用定位打卡 + */ + private Boolean openPointCard; + /** + * 班次配置 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List shifts; + /** + * 是否法定节假日休息 + */ + private Boolean rest; + /** + * 特殊日期设置数组 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List specialDates; + /** + * 打卡地点配置 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List points; + /** + * WiFi 打卡配置 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List wifis; + /** + * 扣款规则 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private DeductRule deductRule; + /** + * 是否默认考勤组 + */ + private Boolean defaultStatus; + + /** + * 特殊日期设置 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class SpecialDate { + + /** + * 日期类型 + * + * 枚举 {@link HrmAttendanceHolidayTypeEnum} + */ + private Integer type; + /** + * 特殊日期 + */ + private LocalDateTime date; + } + + /** + * 考勤班次配置 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class Shift { + + /** + * 工作日数组 + */ + private List weeks; + /** + * 上班时间 + */ + @JsonFormat(pattern = "HH:mm") + private LocalTime startTime; + /** + * 下班时间 + */ + @JsonFormat(pattern = "HH:mm") + private LocalTime endTime; + /** + * 上班打卡开始时间 + */ + @JsonFormat(pattern = "HH:mm") + private LocalTime clockInStartTime; + /** + * 上班打卡结束时间 + */ + @JsonFormat(pattern = "HH:mm") + private LocalTime clockInEndTime; + /** + * 下班打卡开始时间 + */ + @JsonFormat(pattern = "HH:mm") + private LocalTime clockOutStartTime; + /** + * 下班打卡结束时间 + */ + @JsonFormat(pattern = "HH:mm") + private LocalTime clockOutEndTime; + /** + * 休息开始时间 + */ + @JsonFormat(pattern = "HH:mm") + private LocalTime restStartTime; + /** + * 休息结束时间 + */ + @JsonFormat(pattern = "HH:mm") + private LocalTime restEndTime; + /** + * 休息时间是否不计入工作时长 + */ + private Boolean excludeRestTime; + } + + /** + * 打卡地点配置 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class Point { + + /** + * 地点名称 + */ + private String name; + /** + * 定位地址 + */ + private String address; + /** + * 纬度 + */ + private BigDecimal latitude; + /** + * 经度 + */ + private BigDecimal longitude; + /** + * 有效打卡半径,单位:米 + */ + private Integer radius; + } + + /** + * WiFi 打卡配置 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class Wifi { + + /** + * WiFi 名称 + */ + private String ssid; + /** + * MAC 地址 + */ + private String mac; + } + + /** + * 考勤扣款规则 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class DeductRule { + + /** + * 迟到扣款方式 + * + * 枚举 {@link HrmAttendanceLateEarlyDeductMethodEnum} + */ + private Integer lateMethod; + /** + * 迟到扣款金额 + */ + private BigDecimal lateDeductMoney; + /** + * 早退扣款方式 + * + * 枚举 {@link HrmAttendanceLateEarlyDeductMethodEnum} + */ + private Integer earlyMethod; + /** + * 早退扣款金额 + */ + private BigDecimal earlyDeductMoney; + /** + * 旷工扣款方式 + * + * 枚举 {@link HrmAttendanceAbsenteeismDeductMethodEnum} + */ + private Integer absenteeismMethod; + /** + * 旷工扣款金额 + */ + private BigDecimal absenteeismDeductMoney; + /** + * 缺卡扣款方式 + * + * 枚举 {@link HrmAttendanceMisscardDeductMethodEnum} + */ + private Integer misscardMethod; + /** + * 缺卡扣款金额 + */ + private BigDecimal misscardDeductMoney; + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/attendance/config/HrmAttendanceHolidayDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/attendance/config/HrmAttendanceHolidayDO.java new file mode 100644 index 0000000..662f0a3 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/attendance/config/HrmAttendanceHolidayDO.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.config; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.hrm.enums.DictTypeConstants; +import cn.iocoder.yudao.module.hrm.enums.attendance.config.HrmAttendanceHolidayTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.time.LocalDateTime; + +/** + * HRM 考勤节假日 DO + * + * @author 芋道源码 + */ +@TableName("hrm_attendance_holiday") +@KeySequence("hrm_attendance_holiday_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmAttendanceHolidayDO extends BaseDO { + + /** + * 节假日编号 + */ + @TableId + private Long id; + /** + * 日期 + */ + private LocalDateTime date; + /** + * 日期类型 + * + * 枚举 {@link HrmAttendanceHolidayTypeEnum} + * 字典 {@link DictTypeConstants#HRM_ATTENDANCE_HOLIDAY_TYPE} + */ + private Integer type; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/attendance/record/HrmAttendanceClockDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/attendance/record/HrmAttendanceClockDO.java new file mode 100644 index 0000000..aee355d --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/attendance/record/HrmAttendanceClockDO.java @@ -0,0 +1,109 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.record; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.module.hrm.enums.DictTypeConstants; +import cn.iocoder.yudao.module.hrm.enums.attendance.record.HrmAttendanceClockSourceEnum; +import cn.iocoder.yudao.module.hrm.enums.attendance.record.HrmAttendanceClockStageEnum; +import cn.iocoder.yudao.module.hrm.enums.attendance.record.HrmAttendanceClockStatusEnum; +import cn.iocoder.yudao.module.hrm.enums.attendance.record.HrmAttendanceClockTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * HRM 考勤打卡记录 DO + * + * @author 芋道源码 + */ +@TableName("hrm_attendance_clock") +@KeySequence("hrm_attendance_clock_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmAttendanceClockDO extends BaseDO { + + /** + * 打卡记录编号 + */ + @TableId + private Long id; + /** + * 员工编号 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + private Long employeeId; + /** + * 打卡时间 + */ + private LocalDateTime clockTime; + /** + * 打卡类型 + * + * 枚举 {@link HrmAttendanceClockTypeEnum} + * 字典 {@link DictTypeConstants#HRM_ATTENDANCE_CLOCK_TYPE} + */ + private Integer type; + /** + * 应打卡时间 + */ + private LocalDateTime attendanceTime; + /** + * 打卡来源 + * + * 枚举 {@link HrmAttendanceClockSourceEnum} + * 字典 {@link DictTypeConstants#HRM_ATTENDANCE_CLOCK_SOURCE} + */ + private Integer sourceType; + /** + * 打卡状态 + * + * 枚举 {@link HrmAttendanceClockStatusEnum} + * 字典 {@link DictTypeConstants#HRM_ATTENDANCE_CLOCK_STATUS} + */ + private Integer status; + /** + * 打卡阶段 + * + * 枚举 {@link HrmAttendanceClockStageEnum} + */ + private Integer stage; + /** + * 打卡地址 + */ + private String address; + /** + * 经度 + */ + private BigDecimal longitude; + /** + * 纬度 + */ + private BigDecimal latitude; + /** + * WiFi 名称 + */ + private String ssid; + /** + * WiFi MAC 地址 + */ + private String mac; + /** + * 备注 + */ + private String remark; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/attendance/record/HrmAttendanceLeaveDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/attendance/record/HrmAttendanceLeaveDO.java new file mode 100644 index 0000000..a1b0be8 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/attendance/record/HrmAttendanceLeaveDO.java @@ -0,0 +1,93 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.record; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.module.hrm.enums.DictTypeConstants; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * HRM 考勤请假 DO + * + * @author 芋道源码 + */ +@TableName("hrm_attendance_leave") +@KeySequence("hrm_attendance_leave_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmAttendanceLeaveDO extends BaseDO { + + /** + * 请假记录编号 + */ + @TableId + private Long id; + /** + * 员工编号 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + private Long employeeId; + /** + * 请假类型 + * + * 字典 {@link DictTypeConstants#HRM_ATTENDANCE_LEAVE_TYPE} + */ + private String type; + /** + * 请假开始时间 + */ + private LocalDateTime startTime; + /** + * 请假结束时间 + */ + private LocalDateTime endTime; + /** + * 请假时长 + */ + private BigDecimal day; + /** + * 请假理由 + */ + private String reason; + /** + * 备注 + */ + private String remark; + /** + * 审批状态 + * + * 枚举 {@link BpmProcessInstanceStatusEnum} + */ + private Integer approvalStatus; + /** + * BPM 流程实例编号 + * + * 关联 ProcessInstance 的 id 属性 + */ + private String processInstanceId; + /** + * 审批完成时间 + */ + private LocalDateTime approvalTime; + /** + * 审批或取消原因 + */ + private String approvalReason; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/config/HrmConfigDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/config/HrmConfigDO.java new file mode 100644 index 0000000..48977a8 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/config/HrmConfigDO.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.config; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.hrm.enums.config.HrmConfigTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +/** + * HRM 通用业务配置 DO + * + * @author 芋道源码 + */ +@TableName("hrm_config") +@KeySequence("hrm_config_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmConfigDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 配置类型 + * + * 枚举 {@link HrmConfigTypeEnum} + */ + private Integer type; + /** + * 配置值 + */ + private String value; + /** + * 显示顺序 + */ + private Integer sort; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/employment/HrmEmployeeChangeRecordDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/employment/HrmEmployeeChangeRecordDO.java new file mode 100644 index 0000000..69283eb --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/employment/HrmEmployeeChangeRecordDO.java @@ -0,0 +1,123 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.employee.employment; + +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeChangeReasonEnum; +import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeChangeTypeEnum; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.time.LocalDateTime; + +/** + * HRM 员工异动记录 DO + * + * @author 芋道源码 + */ +@TableName("hrm_employee_change_record") +@KeySequence("hrm_employee_change_record_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmEmployeeChangeRecordDO extends BaseDO { + + /** + * 异动记录编号 + */ + @TableId + private Long id; + /** + * 员工编号 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + private Long employeeId; + /** + * 异动类型 + * + * 枚举 {@link HrmEmployeeChangeTypeEnum} + */ + private Integer type; + /** + * 异动原因 + * + * 枚举 {@link HrmEmployeeChangeReasonEnum} + */ + private Integer reason; + /** + * 原部门编号 + * + * 关联 {@link DeptDO#getId()} + */ + private Long oldDeptId; + /** + * 新部门编号 + * + * 关联 {@link DeptDO#getId()} + */ + private Long newDeptId; + /** + * 原岗位 + */ + private String oldPostName; + /** + * 新岗位 + */ + private String newPostName; + /** + * 原职级 + */ + private String oldPostLevel; + /** + * 新职级 + */ + private String newPostLevel; + /** + * 原工作地点 + */ + private String oldWorkAddress; + /** + * 新工作地点 + */ + private String newWorkAddress; + /** + * 原直属上级员工编号 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + private Long oldLeaderEmployeeId; + /** + * 新直属上级员工编号 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + private Long newLeaderEmployeeId; + /** + * 试用期,单位:月 + */ + private Integer probation; + /** + * 生效日期 + */ + private LocalDateTime effectTime; + /** + * 实际生效时间 + */ + private LocalDateTime appliedTime; + /** + * 备注 + */ + private String remark; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/employment/HrmEmployeeContractDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/employment/HrmEmployeeContractDO.java new file mode 100644 index 0000000..0fdf476 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/employment/HrmEmployeeContractDO.java @@ -0,0 +1,103 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.employee.employment; + +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler; +import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeContractStatusEnum; +import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeContractTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * HRM 员工合同 DO + * + * @author 芋道源码 + */ +@TableName(value = "hrm_employee_contract", autoResultMap = true) +@KeySequence("hrm_employee_contract_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmEmployeeContractDO extends BaseDO { + + /** + * 合同编号 + */ + @TableId + private Long id; + /** + * 员工编号 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + private Long employeeId; + /** + * 合同编号 + */ + private String no; + /** + * 合同类型 + * + * 枚举 {@link HrmEmployeeContractTypeEnum} + */ + private Integer type; + /** + * 合同开始日期 + */ + private LocalDateTime startTime; + /** + * 合同结束日期 + */ + private LocalDateTime endTime; + /** + * 合同期限,单位:年 + */ + private Integer term; + /** + * 合同状态 + * + * 枚举 {@link HrmEmployeeContractStatusEnum} + */ + private Integer status; + /** + * 签约公司 + */ + private String signCompany; + /** + * 合同签订日期 + */ + private LocalDateTime signTime; + /** + * 备注 + */ + private String remark; + /** + * 是否到期提醒 + * + */ + private Boolean expireRemind; + /** + * 显示顺序 + */ + private Integer sort; + /** + * 附件地址数组 + */ + @TableField(typeHandler = StringListTypeHandler.class) + private List fileUrls; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/employment/HrmEmployeeQuitInfoDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/employment/HrmEmployeeQuitInfoDO.java new file mode 100644 index 0000000..24f40d9 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/employment/HrmEmployeeQuitInfoDO.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.employee.employment; + +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeQuitReasonEnum; +import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeQuitTypeEnum; +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeStatusEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.time.LocalDateTime; + +/** + * HRM 员工离职信息 DO + * + * @author 芋道源码 + */ +@TableName("hrm_employee_quit_info") +@KeySequence("hrm_employee_quit_info_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmEmployeeQuitInfoDO extends BaseDO { + + /** + * 离职信息编号 + */ + @TableId + private Long id; + /** + * 员工编号 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + private Long employeeId; + /** + * 计划离职时间 + */ + private LocalDateTime planQuitTime; + /** + * 申请离职时间 + */ + private LocalDateTime applyQuitTime; + /** + * 薪资结算时间 + */ + private LocalDateTime salarySettlementTime; + /** + * 离职类型 + * + * 枚举 {@link HrmEmployeeQuitTypeEnum} + */ + private Integer type; + /** + * 离职原因 + * + * 枚举 {@link HrmEmployeeQuitReasonEnum} + */ + private Integer reason; + /** + * 备注 + */ + private String remark; + /** + * 离职前员工状态 + * + * 枚举 {@link HrmEmployeeStatusEnum} + */ + private Integer oldEmployeeStatus; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/employment/HrmEmployeeSalaryCardDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/employment/HrmEmployeeSalaryCardDO.java new file mode 100644 index 0000000..de9d2e5 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/employment/HrmEmployeeSalaryCardDO.java @@ -0,0 +1,58 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.employee.employment; + +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.framework.ip.core.Area; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +/** + * HRM 员工工资卡 DO + * + * @author 芋道源码 + */ +@TableName("hrm_employee_salary_card") +@KeySequence("hrm_employee_salary_card_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmEmployeeSalaryCardDO extends BaseDO { + + @TableId + private Long id; + /** + * 员工编号 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + private Long employeeId; + /** + * 银行卡号 + */ + private String bankCardNumber; + /** + * 开户地区编号 + * + * 关联 {@link Area#getId()} + */ + private Integer bankAreaId; + /** + * 银行名称 + */ + private String bankName; + /** + * 开户支行名称 + */ + private String bankBranchName; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/experience/HrmEmployeeCertificateDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/experience/HrmEmployeeCertificateDO.java new file mode 100644 index 0000000..f8334c5 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/experience/HrmEmployeeCertificateDO.java @@ -0,0 +1,80 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.employee.experience; + +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.time.LocalDateTime; + +/** + * HRM 员工证书 DO + * + * @author 芋道源码 + */ +@TableName("hrm_employee_certificate") +@KeySequence("hrm_employee_certificate_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmEmployeeCertificateDO extends BaseDO { + + /** + * 证书编号 + */ + @TableId + private Long id; + /** + * 员工编号 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + private Long employeeId; + /** + * 证书名称 + */ + private String name; + /** + * 证书级别 + */ + private String level; + /** + * 证书编号 + */ + private String no; + /** + * 有效开始日期 + */ + private LocalDateTime startTime; + /** + * 有效结束日期 + */ + private LocalDateTime endTime; + /** + * 发证机构 + */ + private String issuingAuthority; + /** + * 发证日期 + */ + private LocalDateTime issuingTime; + /** + * 备注 + */ + private String remark; + /** + * 显示顺序 + */ + private Integer sort; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/experience/HrmEmployeeEducationExperienceDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/experience/HrmEmployeeEducationExperienceDO.java new file mode 100644 index 0000000..c0d2de9 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/experience/HrmEmployeeEducationExperienceDO.java @@ -0,0 +1,85 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.employee.experience; + +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.hrm.enums.DictTypeConstants; +import cn.iocoder.yudao.module.hrm.enums.employee.experience.HrmEmployeeEducationEnum; +import cn.iocoder.yudao.module.hrm.enums.employee.experience.HrmEmployeeTeachingMethodEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.time.LocalDateTime; + +/** + * HRM 员工教育经历 DO + * + * @author 芋道源码 + */ +@TableName("hrm_employee_education_experience") +@KeySequence("hrm_employee_education_experience_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmEmployeeEducationExperienceDO extends BaseDO { + + /** + * 教育经历编号 + */ + @TableId + private Long id; + /** + * 员工编号 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + private Long employeeId; + /** + * 学历 + * + * 枚举 {@link HrmEmployeeEducationEnum} + * 字典 {@link DictTypeConstants#HRM_EMPLOYEE_EDUCATION} + */ + private Integer education; + /** + * 毕业院校 + */ + private String graduateSchool; + /** + * 专业 + */ + private String major; + /** + * 入学日期 + */ + private LocalDateTime admissionTime; + /** + * 毕业日期 + */ + private LocalDateTime graduationTime; + /** + * 教学方式 + * + * 枚举 {@link HrmEmployeeTeachingMethodEnum} + */ + private Integer teachingMethods; + /** + * 是否第一学历 + * + */ + private Boolean firstDegree; + /** + * 显示顺序 + */ + private Integer sort; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/experience/HrmEmployeeTrainingExperienceDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/experience/HrmEmployeeTrainingExperienceDO.java new file mode 100644 index 0000000..afb4be1 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/experience/HrmEmployeeTrainingExperienceDO.java @@ -0,0 +1,80 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.employee.experience; + +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.time.LocalDateTime; + +/** + * HRM 员工培训经历 DO + * + * @author 芋道源码 + */ +@TableName("hrm_employee_training_experience") +@KeySequence("hrm_employee_training_experience_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmEmployeeTrainingExperienceDO extends BaseDO { + + /** + * 培训经历编号 + */ + @TableId + private Long id; + /** + * 员工编号 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + private Long employeeId; + /** + * 培训课程 + */ + private String course; + /** + * 培训机构名称 + */ + private String organizationName; + /** + * 培训开始日期 + */ + private LocalDateTime startTime; + /** + * 培训结束日期 + */ + private LocalDateTime endTime; + /** + * 培训时长 + */ + private String duration; + /** + * 培训成绩 + */ + private String result; + /** + * 培训证书名称 + */ + private String certificateName; + /** + * 备注 + */ + private String remark; + /** + * 显示顺序 + */ + private Integer sort; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/experience/HrmEmployeeWorkExperienceDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/experience/HrmEmployeeWorkExperienceDO.java new file mode 100644 index 0000000..736ab55 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/experience/HrmEmployeeWorkExperienceDO.java @@ -0,0 +1,80 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.employee.experience; + +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.time.LocalDateTime; + +/** + * HRM 员工工作经历 DO + * + * @author 芋道源码 + */ +@TableName("hrm_employee_work_experience") +@KeySequence("hrm_employee_work_experience_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmEmployeeWorkExperienceDO extends BaseDO { + + /** + * 工作经历编号 + */ + @TableId + private Long id; + /** + * 员工编号 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + private Long employeeId; + /** + * 工作单位 + */ + private String workUnit; + /** + * 职务 + */ + private String postName; + /** + * 工作开始日期 + */ + private LocalDateTime startTime; + /** + * 工作结束日期 + */ + private LocalDateTime endTime; + /** + * 离职原因 + */ + private String reason; + /** + * 证明人 + */ + private String witnessName; + /** + * 证明人手机号 + */ + private String witnessPhone; + /** + * 工作备注 + */ + private String remark; + /** + * 显示顺序 + */ + private Integer sort; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/info/HrmEmployeeContactDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/info/HrmEmployeeContactDO.java new file mode 100644 index 0000000..8f4a00e --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/info/HrmEmployeeContactDO.java @@ -0,0 +1,69 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +/** + * HRM 员工联系人 DO + * + * @author 芋道源码 + */ +@TableName("hrm_employee_contact") +@KeySequence("hrm_employee_contact_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmEmployeeContactDO extends BaseDO { + + /** + * 联系人编号 + */ + @TableId + private Long id; + /** + * 员工编号 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + private Long employeeId; + /** + * 联系人名称 + */ + private String name; + /** + * 与员工的关系 + */ + private String relation; + /** + * 联系人电话 + */ + private String phone; + /** + * 联系人工作单位 + */ + private String workUnit; + /** + * 联系人职务 + */ + private String postName; + /** + * 联系人地址 + */ + private String address; + /** + * 显示顺序 + */ + private Integer sort; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/info/HrmEmployeeDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/info/HrmEmployeeDO.java new file mode 100644 index 0000000..ef63346 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/info/HrmEmployeeDO.java @@ -0,0 +1,213 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.recruit.candidate.HrmRecruitCandidateDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.recruit.config.HrmRecruitChannelDO; +import cn.iocoder.yudao.module.hrm.enums.DictTypeConstants; +import cn.iocoder.yudao.module.hrm.enums.employee.experience.HrmEmployeeEducationEnum; +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeTypeEnum; +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.system.dal.dataobject.dept.DeptDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.module.system.enums.common.SexEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.time.LocalDateTime; + +/** + * HRM 员工档案 DO + * + * @author 芋道源码 + */ +@TableName("hrm_employee") +@KeySequence("hrm_employee_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmEmployeeDO extends BaseDO { + + /** + * 员工编号 + */ + @TableId + private Long id; + /** + * 员工姓名 + */ + private String name; + /** + * 工号 + */ + private String jobNumber; + /** + * 后台用户编号 + * + * 关联 {@link AdminUserDO#getId()} + * 允许为空,用于待入职、离职历史等没有登录账号的员工档案 + */ + private Long userId; + /** + * 手机号码 + */ + private String mobile; + /** + * 国家或地区 + */ + private String country; + /** + * 民族 + */ + private String nation; + /** + * 证件类型 + * + * 枚举 {@link HrmEmployeeIdTypeEnum} + */ + private Integer idType; + /** + * 证件号码 + */ + private String idNumber; + /** + * 性别 + * + * 枚举 {@link SexEnum} + */ + private Integer sex; + /** + * 邮箱 + */ + private String email; + /** + * 籍贯 + */ + private String nativePlace; + /** + * 出生时间 + */ + private LocalDateTime birthday; + /** + * 年龄 + */ + private Integer age; + /** + * 户籍地址 + */ + private String address; + /** + * 最高学历 + * + * 枚举 {@link HrmEmployeeEducationEnum} + * 字典 {@link DictTypeConstants#HRM_EMPLOYEE_EDUCATION} + */ + private Integer highestEducation; + /** + * 部门编号 + * + * 关联 {@link DeptDO#getId()} + */ + private Long deptId; + /** + * 直属上级员工编号 + * + * 关联 {@link #id} + */ + private Long leaderEmployeeId; + /** + * 入职状态 + * + * 枚举 {@link HrmEmployeeEntryStatusEnum} + * 字典 {@link DictTypeConstants#HRM_EMPLOYEE_ENTRY_STATUS} + */ + private Integer entryStatus; + /** + * 员工状态 + * + * 枚举 {@link HrmEmployeeStatusEnum} + * 字典 {@link DictTypeConstants#HRM_EMPLOYEE_STATUS} + */ + private Integer status; + /** + * 聘用形式 + * + * 枚举 {@link HrmEmployeeTypeEnum} + * 字典 {@link DictTypeConstants#HRM_EMPLOYEE_TYPE} + */ + private Integer type; + /** + * 入职时间 + */ + private LocalDateTime entryTime; + /** + * 试用期,单位:月 + * + * 0 表示无试用期 + */ + private Integer probation; + /** + * 转正时间 + */ + private LocalDateTime regularTime; + /** + * 离职时间 + */ + private LocalDateTime leaveTime; + /** + * 职位名称 + */ + private String postName; + /** + * 岗位职级 + */ + private String postLevel; + /** + * 工作城市 + */ + private String workCity; + /** + * 工作地址 + */ + private String workAddress; + /** + * 工作详细地址 + */ + private String workDetailAddress; + /** + * 招聘渠道编号 + * + * 关联 {@link HrmRecruitChannelDO#getId()} + */ + private Long channelId; + /** + * 司龄开始时间 + */ + private LocalDateTime companyAgeStartTime; + /** + * 司龄,单位:年 + */ + private Integer companyAge; + /** + * 招聘候选人编号 + * + * 关联 {@link HrmRecruitCandidateDO#getId()} + */ + private Long candidateId; + /** + * 备注 + */ + private String remark; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/info/HrmEmployeeFileDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/info/HrmEmployeeFileDO.java new file mode 100644 index 0000000..637726f --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/info/HrmEmployeeFileDO.java @@ -0,0 +1,53 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info; + +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeFileTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +/** + * HRM 员工材料附件 DO + * + * @author 芋道源码 + */ +@TableName("hrm_employee_file") +@KeySequence("hrm_employee_file_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmEmployeeFileDO extends BaseDO { + + /** + * 附件编号 + */ + @TableId + private Long id; + /** + * 员工编号 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + private Long employeeId; + /** + * 附件类型 + * + * 枚举 {@link HrmEmployeeFileTypeEnum} + */ + private Integer type; + /** + * 附件地址 + */ + private String url; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/info/HrmEmployeePersonalNoteDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/info/HrmEmployeePersonalNoteDO.java new file mode 100644 index 0000000..32b4696 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/employee/info/HrmEmployeePersonalNoteDO.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.time.LocalDateTime; + +/** + * HRM 员工个人备忘 DO + * + * @author 芋道源码 + */ +@TableName("hrm_employee_personal_note") +@KeySequence("hrm_employee_personal_note_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmEmployeePersonalNoteDO extends BaseDO { + + @TableId + private Long id; + + /** + * 所属员工编号 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + private Long employeeId; + + /** + * 备忘内容 + */ + private String content; + + /** + * 提醒时间 + */ + private LocalDateTime reminderTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/insurance/config/HrmInsuranceSchemeDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/insurance/config/HrmInsuranceSchemeDO.java new file mode 100644 index 0000000..b3b3ecd --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/insurance/config/HrmInsuranceSchemeDO.java @@ -0,0 +1,54 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.insurance.config; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.hrm.enums.insurance.config.HrmInsuranceSchemeTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +/** + * HRM 社保方案 DO + * + * @author 芋道源码 + */ +@TableName("hrm_insurance_scheme") +@KeySequence("hrm_insurance_scheme_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmInsuranceSchemeDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 方案名称 + */ + private String name; + /** + * 参保地区编号 + */ + private Integer areaId; + /** + * 户籍类型 + */ + private String householdType; + /** + * 方案类型 + * + * 枚举 {@link HrmInsuranceSchemeTypeEnum} + */ + private Integer type; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/insurance/config/HrmInsuranceSchemeProjectDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/insurance/config/HrmInsuranceSchemeProjectDO.java new file mode 100644 index 0000000..3250653 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/insurance/config/HrmInsuranceSchemeProjectDO.java @@ -0,0 +1,74 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.insurance.config; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.hrm.enums.insurance.config.HrmInsuranceProjectTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.math.BigDecimal; + +/** + * HRM 社保方案项目 DO + * + * @author 芋道源码 + */ +@TableName("hrm_insurance_scheme_project") +@KeySequence("hrm_insurance_scheme_project_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmInsuranceSchemeProjectDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 社保方案编号 + * + * 关联 {@link HrmInsuranceSchemeDO#getId()} + */ + private Long schemeId; + /** + * 项目类型 + * + * 枚举 {@link HrmInsuranceProjectTypeEnum} + */ + private Integer type; + /** + * 项目名称 + */ + private String name; + /** + * 缴纳基数 + */ + private BigDecimal baseAmount; + /** + * 公司缴纳比例 + */ + private BigDecimal corporateRate; + /** + * 个人缴纳比例 + */ + private BigDecimal personalRate; + /** + * 公司缴纳金额 + */ + private BigDecimal corporateAmount; + /** + * 个人缴纳金额 + */ + private BigDecimal personalAmount; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/insurance/employee/HrmInsuranceEmployeeInfoDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/insurance/employee/HrmInsuranceEmployeeInfoDO.java new file mode 100644 index 0000000..d1af7fa --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/insurance/employee/HrmInsuranceEmployeeInfoDO.java @@ -0,0 +1,68 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.insurance.employee; + +import cn.iocoder.yudao.module.hrm.dal.dataobject.insurance.config.HrmInsuranceSchemeDO; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.time.LocalDateTime; + +/** + * HRM 员工参保信息 DO + * + * @author 芋道源码 + */ +@TableName("hrm_insurance_employee_info") +@KeySequence("hrm_insurance_employee_info_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmInsuranceEmployeeInfoDO extends BaseDO { + + @TableId + private Long id; + /** + * 员工编号 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + private Long employeeId; + /** + * 是否首次参保 + */ + private Boolean firstSocialSecurity; + /** + * 是否首次缴纳公积金 + */ + private Boolean firstAccumulationFund; + /** + * 社保账号 + */ + private String socialSecurityNumber; + /** + * 公积金账号 + */ + private String accumulationFundNumber; + /** + * 社保起缴月份 + */ + private LocalDateTime socialSecurityStartMonth; + /** + * 社保方案编号 + * + * 关联 {@link HrmInsuranceSchemeDO#getId()} + */ + private Long schemeId; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/insurance/monthrecord/HrmInsuranceMonthEmployeeRecordDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/insurance/monthrecord/HrmInsuranceMonthEmployeeRecordDO.java new file mode 100644 index 0000000..9cd25f7 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/insurance/monthrecord/HrmInsuranceMonthEmployeeRecordDO.java @@ -0,0 +1,146 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.insurance.monthrecord; + +import cn.iocoder.yudao.module.hrm.dal.dataobject.insurance.config.HrmInsuranceSchemeDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.insurance.config.HrmInsuranceSchemeProjectDO; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.module.hrm.enums.insurance.employee.HrmInsuranceEmployeeStatusEnum; +import cn.iocoder.yudao.module.hrm.enums.insurance.config.HrmInsuranceProjectTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.math.BigDecimal; +import java.util.List; + +/** + * HRM 员工月度社保记录 DO + * + * @author 芋道源码 + */ +@TableName(value = "hrm_insurance_month_employee_record", autoResultMap = true) +@KeySequence("hrm_insurance_month_employee_record_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmInsuranceMonthEmployeeRecordDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 月度社保表编号 + * + * 关联 {@link HrmInsuranceMonthRecordDO#getId()} + */ + private Long monthRecordId; + /** + * 员工编号 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + private Long employeeId; + /** + * 社保方案编号 + * + * 关联 {@link HrmInsuranceSchemeDO#getId()} + */ + private Long schemeId; + /** + * 年份 + */ + private Integer year; + /** + * 月份 + */ + private Integer month; + /** + * 个人社保金额 + */ + private BigDecimal personalInsuranceAmount; + /** + * 个人公积金金额 + */ + private BigDecimal personalProvidentFundAmount; + /** + * 公司社保金额 + */ + private BigDecimal corporateInsuranceAmount; + /** + * 公司公积金金额 + */ + private BigDecimal corporateProvidentFundAmount; + /** + * 参保状态 + * + * 枚举 {@link HrmInsuranceEmployeeStatusEnum} + */ + private Integer status; + /** + * 参保项目快照 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List projects; + + /** + * 参保项目快照 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class Project { + + /** + * 来源社保方案项目编号 + * + * 关联 {@link HrmInsuranceSchemeProjectDO#getId()} + */ + private Long schemeProjectId; + /** + * 项目类型 + * + * 枚举 {@link HrmInsuranceProjectTypeEnum} + */ + private Integer type; + /** + * 项目名称 + */ + private String name; + /** + * 缴纳基数 + */ + private BigDecimal baseAmount; + /** + * 公司缴纳比例 + */ + private BigDecimal corporateRate; + /** + * 个人缴纳比例 + */ + private BigDecimal personalRate; + /** + * 公司缴纳金额 + */ + private BigDecimal corporateAmount; + /** + * 个人缴纳金额 + */ + private BigDecimal personalAmount; + + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/insurance/monthrecord/HrmInsuranceMonthRecordDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/insurance/monthrecord/HrmInsuranceMonthRecordDO.java new file mode 100644 index 0000000..3b57cc7 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/insurance/monthrecord/HrmInsuranceMonthRecordDO.java @@ -0,0 +1,80 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.insurance.monthrecord; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.hrm.enums.insurance.monthrecord.HrmInsuranceMonthStatusEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.math.BigDecimal; + +/** + * HRM 月度社保表 DO + * + * @author 芋道源码 + */ +@TableName("hrm_insurance_month_record") +@KeySequence("hrm_insurance_month_record_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmInsuranceMonthRecordDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 标题 + */ + private String title; + /** + * 年份 + */ + private Integer year; + /** + * 月份 + */ + private Integer month; + /** + * 参保人数 + */ + private Integer insuredEmployeeCount; + /** + * 停保人数 + */ + private Integer stoppedEmployeeCount; + /** + * 状态 + * + * 枚举 {@link HrmInsuranceMonthStatusEnum} + */ + private Integer status; + /** + * 个人社保金额 + */ + private BigDecimal personalInsuranceAmount; + /** + * 个人公积金金额 + */ + private BigDecimal personalProvidentFundAmount; + /** + * 公司社保金额 + */ + private BigDecimal corporateInsuranceAmount; + /** + * 公司公积金金额 + */ + private BigDecimal corporateProvidentFundAmount; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/assessment/HrmPerformanceAssessmentActionRecordDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/assessment/HrmPerformanceAssessmentActionRecordDO.java new file mode 100644 index 0000000..5652b1f --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/assessment/HrmPerformanceAssessmentActionRecordDO.java @@ -0,0 +1,79 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.performance.assessment; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.module.hrm.enums.performance.assessment.HrmPerformanceAssessmentActionTypeEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.assessment.HrmPerformanceAssessmentStageStatusEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.List; + +/** + * HRM 员工绩效考核动作记录 DO + * + * @author 芋道源码 + */ +@TableName(value = "hrm_performance_assessment_action_record", autoResultMap = true) +@KeySequence("hrm_performance_assessment_action_record_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class HrmPerformanceAssessmentActionRecordDO extends BaseDO { + + /** + * 动作记录编号 + */ + @TableId + private Long id; + /** + * 员工绩效考核编号 + * + * 关联 {@link HrmPerformanceAssessmentDO#getId()} + */ + private Long assessmentId; + /** + * 员工绩效考核阶段编号 + * + * 关联 {@link HrmPerformanceAssessmentStageDO#getId()} + */ + private Long stageId; + /** + * 操作员工编号 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + private Long employeeId; + /** + * 动作类型 + * + * 枚举 {@link HrmPerformanceAssessmentActionTypeEnum} + */ + private Integer type; + /** + * 动作标题 + */ + private String title; + /** + * 动作内容 + */ + private String content; + /** + * 附件地址列表 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List fileUrls; + /** + * 动作完成后的阶段状态 + * + * 枚举 {@link HrmPerformanceAssessmentStageStatusEnum} + */ + private Integer status; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/assessment/HrmPerformanceAssessmentAppealRecordDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/assessment/HrmPerformanceAssessmentAppealRecordDO.java new file mode 100644 index 0000000..8e8b466 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/assessment/HrmPerformanceAssessmentAppealRecordDO.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.performance.assessment; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.hrm.enums.performance.assessment.HrmPerformanceAppealRecordStatusEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +/** + * HRM 员工绩效考核申诉记录 DO + * + * @author 芋道源码 + */ +@TableName("hrm_performance_assessment_appeal_record") +@KeySequence("hrm_performance_assessment_appeal_record_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class HrmPerformanceAssessmentAppealRecordDO extends BaseDO { + + /** + * 申诉记录编号 + */ + @TableId + private Long id; + /** + * 员工绩效考核编号 + * + * 关联 {@link HrmPerformanceAssessmentDO#getId()} + */ + private Long assessmentId; + /** + * 员工绩效考核阶段编号 + * + * 关联 {@link HrmPerformanceAssessmentStageDO#getId()} + */ + private Long stageId; + /** + * 处理状态 + * + * 枚举 {@link HrmPerformanceAppealRecordStatusEnum} + */ + private Integer status; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/assessment/HrmPerformanceAssessmentDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/assessment/HrmPerformanceAssessmentDO.java new file mode 100644 index 0000000..1d856b2 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/assessment/HrmPerformanceAssessmentDO.java @@ -0,0 +1,171 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.performance.assessment; + +import cn.iocoder.yudao.module.hrm.dal.dataobject.performance.plan.HrmPerformancePlanDO; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.module.hrm.enums.performance.assessment.HrmPerformanceAppealStatusEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.assessment.HrmPerformanceConfirmationResultEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.assessment.HrmPerformanceAssessmentProcessStatusEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformancePlanStatusEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.assessment.HrmPerformanceResultAuditStatusEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformanceStageTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +/** + * HRM 员工绩效考核 DO + * + * @author 芋道源码 + */ +@TableName(value = "hrm_performance_assessment", autoResultMap = true) +@KeySequence("hrm_performance_assessment_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmPerformanceAssessmentDO extends BaseDO { + + /** + * 员工绩效考核编号 + */ + @TableId + private Long id; + /** + * 绩效计划编号 + * + * 关联 {@link HrmPerformancePlanDO#getId()} + */ + private Long planId; + /** + * 员工编号 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + private Long employeeId; + /** + * 考核状态 + * + * 枚举 {@link HrmPerformancePlanStatusEnum} + */ + private Integer status; + /** + * 流程处理状态 + * + * 枚举 {@link HrmPerformanceAssessmentProcessStatusEnum} + */ + private Integer processStatus; + /** + * 当前业务阶段类型 + * + * 枚举 {@link HrmPerformanceStageTypeEnum} + */ + private Integer stageType; + /** + * 当前阶段排序 + */ + private Integer stageSort; + /** + * 综合得分 + */ + private BigDecimal score; + /** + * 结果等级 + */ + private String resultLevel; + /** + * 绩效系数 + */ + private BigDecimal coefficient; + /** + * 目标确认结果 + * + * 枚举 {@link HrmPerformanceConfirmationResultEnum} + */ + private Integer targetConfirmationResult; + /** + * 目标确认意见 + */ + private String targetConfirmationComment; + /** + * 目标确认时间 + */ + private LocalDateTime targetConfirmationTime; + /** + * 自评说明 + */ + private String selfComment; + /** + * 他评说明 + */ + private String reviewerComment; + /** + * 结果说明 + */ + private String resultComment; + /** + * 结果确认时间 + */ + private LocalDateTime resultConfirmationTime; + /** + * 结果审核状态 + * + * 枚举 {@link HrmPerformanceResultAuditStatusEnum} + */ + private Integer resultAuditStatus; + /** + * 结果审核时间 + */ + private LocalDateTime resultAuditTime; + /** + * 结果审核意见 + */ + private String resultAuditReason; + + /** + * 申诉原因 + */ + private String appealReason; + /** + * 申诉附件地址列表 + */ + @TableField(typeHandler = StringListTypeHandler.class) + private List appealFileUrls; + /** + * 申诉提交时间 + */ + private LocalDateTime appealSubmitTime; + /** + * 申诉状态 + * + * 枚举 {@link HrmPerformanceAppealStatusEnum} + */ + private Integer appealStatus; + /** + * 申诉完成时间 + */ + private LocalDateTime appealTime; + /** + * 申诉处理意见 + */ + private String appealComment; + /** + * 归档时间 + */ + private LocalDateTime archiveTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/assessment/HrmPerformanceAssessmentDimensionDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/assessment/HrmPerformanceAssessmentDimensionDO.java new file mode 100644 index 0000000..1ce4457 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/assessment/HrmPerformanceAssessmentDimensionDO.java @@ -0,0 +1,64 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.performance.assessment; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.hrm.enums.performance.config.HrmPerformanceQuotaTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.math.BigDecimal; + +/** + * HRM 员工绩效考核维度 DO + * + * @author 芋道源码 + */ +@TableName("hrm_performance_assessment_dimension") +@KeySequence("hrm_performance_assessment_dimension_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class HrmPerformanceAssessmentDimensionDO extends BaseDO { + + /** + * 考核维度编号 + */ + @TableId + private Long id; + /** + * 员工绩效考核编号 + * + * 关联 {@link HrmPerformanceAssessmentDO#getId()} + */ + private Long assessmentId; + /** + * 维度名称 + */ + private String name; + /** + * 指标类型 + * + * 枚举 {@link HrmPerformanceQuotaTypeEnum} + */ + private Integer quotaType; + /** + * 维度权重 + */ + private BigDecimal weight; + /** + * 备注 + */ + private String remark; + /** + * 是否允许员工编辑指标 + */ + private Boolean allowEdit; + /** + * 排序 + */ + private Integer sort; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/assessment/HrmPerformanceAssessmentQuotaDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/assessment/HrmPerformanceAssessmentQuotaDO.java new file mode 100644 index 0000000..d958e6f --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/assessment/HrmPerformanceAssessmentQuotaDO.java @@ -0,0 +1,100 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.performance.assessment; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformanceQuotaScoreTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.math.BigDecimal; + +/** + * HRM 员工绩效考核指标 DO + * + * @author 芋道源码 + */ +@TableName("hrm_performance_assessment_quota") +@KeySequence("hrm_performance_assessment_quota_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmPerformanceAssessmentQuotaDO extends BaseDO { + + /** + * 考核指标编号 + */ + @TableId + private Long id; + /** + * 员工绩效考核编号 + * + * 关联 {@link HrmPerformanceAssessmentDO#getId()} + */ + private Long assessmentId; + /** + * 考核维度编号 + * + * 关联 {@link HrmPerformanceAssessmentDimensionDO#getId()} + */ + private Long dimensionId; + /** + * 是否预置指标 + */ + private Boolean preset; + /** + * 指标名称 + */ + private String name; + /** + * 指标说明 + */ + private String description; + /** + * 评分标准 + */ + private String standard; + /** + * 指标权重 + */ + private BigDecimal weight; + /** + * 评分类型 + * + * 枚举 {@link HrmPerformanceQuotaScoreTypeEnum} + */ + private Integer scoreType; + /** + * 目标值 + */ + private String targetValue; + /** + * 实际值 + */ + private String actualValue; + /** + * 自评汇总分 + */ + private BigDecimal selfScore; + /** + * 他评汇总分 + */ + private BigDecimal reviewerScore; + /** + * 最终指标得分 + */ + private BigDecimal finalScore; + /** + * 排序 + */ + private Integer sort; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/assessment/HrmPerformanceAssessmentQuotaScoreDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/assessment/HrmPerformanceAssessmentQuotaScoreDO.java new file mode 100644 index 0000000..de6c847 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/assessment/HrmPerformanceAssessmentQuotaScoreDO.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.performance.assessment; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.math.BigDecimal; + +/** + * HRM 员工绩效考核指标评分 DO + * + * @author 芋道源码 + */ +@TableName("hrm_performance_assessment_quota_score") +@KeySequence("hrm_performance_assessment_quota_score_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class HrmPerformanceAssessmentQuotaScoreDO extends BaseDO { + + /** + * 考核指标评分编号 + */ + @TableId + private Long id; + /** + * 员工绩效考核阶段编号 + * + * 关联 {@link HrmPerformanceAssessmentStageDO#getId()} + */ + private Long assessmentStageId; + /** + * 员工绩效考核指标编号 + * + * 关联 {@link HrmPerformanceAssessmentQuotaDO#getId()} + */ + private Long assessmentQuotaId; + /** + * 指标得分 + */ + private BigDecimal score; + /** + * 指标评语 + */ + private String comment; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/assessment/HrmPerformanceAssessmentStageDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/assessment/HrmPerformanceAssessmentStageDO.java new file mode 100644 index 0000000..54cf5f4 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/assessment/HrmPerformanceAssessmentStageDO.java @@ -0,0 +1,124 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.performance.assessment; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.module.hrm.enums.performance.assessment.HrmPerformanceAssessmentStageStatusEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformanceRaterTypeEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformanceReviewScoringTypeEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformanceReviewVisibleContentEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformanceStageTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * HRM 员工绩效考核阶段 DO + * + * @author 芋道源码 + */ +@TableName("hrm_performance_assessment_stage") +@KeySequence("hrm_performance_assessment_stage_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class HrmPerformanceAssessmentStageDO extends BaseDO { + + /** + * 考核阶段编号 + */ + @TableId + private Long id; + /** + * 员工绩效考核编号 + * + * 关联 {@link HrmPerformanceAssessmentDO#getId()} + */ + private Long assessmentId; + /** + * 业务阶段类型 + * + * 枚举 {@link HrmPerformanceStageTypeEnum} + */ + private Integer type; + /** + * 阶段名称 + */ + private String name; + /** + * 实际处理员工编号 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + private Long handlerEmployeeId; + /** + * 评分人类型 + * + * 枚举 {@link HrmPerformanceRaterTypeEnum} + */ + private Integer raterType; + /** + * 评分权重快照 + */ + private BigDecimal weight; + /** + * 评分方式快照 + * + * 枚举 {@link HrmPerformanceReviewScoringTypeEnum} + */ + private Integer scoringType; + /** + * 评分内容可见范围快照 + * + * 枚举 {@link HrmPerformanceReviewVisibleContentEnum} + */ + private Integer visibleContent; + /** + * 评语是否必填 + */ + private Boolean requiredSetting; + /** + * 是否允许驳回 + */ + private Boolean rejectAuthority; + /** + * 排序 + */ + private Integer sort; + /** + * 阶段处理状态 + * + * 枚举 {@link HrmPerformanceAssessmentStageStatusEnum} + */ + private Integer status; + /** + * 阶段汇总得分 + */ + private BigDecimal score; + /** + * 阶段结果等级 + */ + private String resultLevel; + /** + * 阶段意见 + */ + private String comment; + /** + * 最近一次驳回原因 + */ + private String rejectReason; + /** + * 提交时间 + */ + private LocalDateTime submitTime; + /** + * 截止时间 + */ + private LocalDateTime deadlineTime; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/config/HrmPerformanceAssessmentTemplateDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/config/HrmPerformanceAssessmentTemplateDO.java new file mode 100644 index 0000000..94a735c --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/config/HrmPerformanceAssessmentTemplateDO.java @@ -0,0 +1,194 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.performance.config; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformanceQuotaScoreTypeEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.config.HrmPerformanceQuotaTypeEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.config.HrmPerformanceScoreCalculationEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.config.HrmPerformanceUpperLimitTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.math.BigDecimal; +import java.util.List; + +/** + * HRM 绩效考核模板 DO + * + * @author 芋道源码 + */ +@TableName(value = "hrm_performance_assessment_template", autoResultMap = true) +@KeySequence("hrm_performance_assessment_template_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmPerformanceAssessmentTemplateDO extends BaseDO { + + /** + * 绩效考核模板编号 + */ + @TableId + private Long id; + + /** + * 模板名称 + */ + private String name; + /** + * 模板说明 + */ + private String illustrate; + /** + * 计分方式 + * + * 枚举 {@link HrmPerformanceScoreCalculationEnum} + */ + private Integer scoreCalculation; + /** + * 分数上限类型 + * + * 枚举 {@link HrmPerformanceUpperLimitTypeEnum} + */ + private Integer upperLimitType; + /** + * 分数上限 + */ + private BigDecimal upperLimitScore; + /** + * 维度数量 + */ + private Integer dimensionCount; + /** + * 指标数量 + */ + private Integer quotaCount; + /** + * 考核维度配置 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List dimensions; + /** + * 状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + + /** + * 绩效考核配置快照 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class AssessmentConfig { + + /** + * 模板名称 + */ + private String name; + /** + * 计分方式 + * + * 枚举 {@link HrmPerformanceScoreCalculationEnum} + */ + private Integer scoreCalculation; + /** + * 分数上限类型 + * + * 枚举 {@link HrmPerformanceUpperLimitTypeEnum} + */ + private Integer upperLimitType; + /** + * 分数上限 + */ + private BigDecimal upperLimitScore; + /** + * 考核维度配置 + */ + private List dimensions; + } + + /** + * 绩效考核维度 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class Dimension { + + /** + * 维度名称 + */ + private String name; + /** + * 指标类型 + * + * 枚举 {@link HrmPerformanceQuotaTypeEnum} + */ + private Integer quotaType; + /** + * 维度权重 + */ + private BigDecimal weight; + /** + * 备注 + */ + private String remark; + /** + * 是否允许员工编辑 + */ + private Boolean allowEdit; + /** + * 考核指标配置 + */ + private List quotas; + } + + /** + * 绩效考核指标 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class Quota { + + /** + * 指标名称 + */ + private String name; + /** + * 指标说明 + */ + private String illustrate; + /** + * 评分标准 + */ + private String standard; + /** + * 指标权重 + */ + private BigDecimal weight; + /** + * 评分类型 + * + * 枚举 {@link HrmPerformanceQuotaScoreTypeEnum} + */ + private Integer scoreType; + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/config/HrmPerformanceResultTemplateDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/config/HrmPerformanceResultTemplateDO.java new file mode 100644 index 0000000..f120c92 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/config/HrmPerformanceResultTemplateDO.java @@ -0,0 +1,83 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.performance.config; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.math.BigDecimal; +import java.util.List; + +/** + * HRM 绩效结果模板 DO + * + * @author 芋道源码 + */ +@TableName(value = "hrm_performance_result_template", autoResultMap = true) +@KeySequence("hrm_performance_result_template_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmPerformanceResultTemplateDO extends BaseDO { + + /** + * 绩效结果模板编号 + */ + @TableId + private Long id; + /** + * 模板名称 + */ + private String name; + /** + * 状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 结果等级配置 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List levels; + + /** + * 绩效结果等级 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class Level { + + /** + * 等级名称 + */ + private String name; + /** + * 最低分数 + */ + private BigDecimal minScore; + /** + * 最高分数 + */ + private BigDecimal maxScore; + /** + * 绩效系数 + */ + private BigDecimal coefficient; + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/plan/HrmPerformancePlanDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/plan/HrmPerformancePlanDO.java new file mode 100644 index 0000000..971f930 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/performance/plan/HrmPerformancePlanDO.java @@ -0,0 +1,338 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.performance.plan; + +import cn.iocoder.yudao.module.hrm.dal.dataobject.performance.config.HrmPerformanceAssessmentTemplateDO; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.performance.config.HrmPerformanceResultTemplateDO.Level; +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeStatusEnum; +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeTypeEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.assessment.HrmPerformanceAppealTimeoutActionEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformanceCycleTypeEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformancePlanOperationTypeEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformancePlanScopeTypeEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformancePlanStatusEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformanceQuarterEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformanceQuotaSettingTypeEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformanceRaterTypeEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformanceReviewScoringTypeEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformanceReviewVisibleContentEnum; +import cn.iocoder.yudao.module.hrm.enums.performance.plan.HrmPerformanceStageTypeEnum; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +/** + * HRM 绩效考核计划 DO + * + * @author 芋道源码 + */ +@TableName(value = "hrm_performance_plan", autoResultMap = true) +@KeySequence("hrm_performance_plan_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmPerformancePlanDO extends BaseDO { + + /** + * 计划编号 + */ + @TableId + private Long id; + /** + * 计划状态 + * + * 枚举 {@link HrmPerformancePlanStatusEnum} + */ + private Integer status; + /** + * 当前业务阶段类型 + * + * 枚举 {@link HrmPerformanceStageTypeEnum} + */ + private Integer stageType; + /** + * 当前可执行操作类型 + * + * 枚举 {@link HrmPerformancePlanOperationTypeEnum} + */ + private Integer operationType; + /** + * 终止时间 + */ + private LocalDateTime terminateTime; + + // ==================== 1. 基础设置 ==================== + + /** + * 计划名称 + */ + private String name; + /** + * 考核周期类型 + * + * 枚举 {@link HrmPerformanceCycleTypeEnum} + */ + private Integer cycleType; + /** + * 考核周期 + */ + private String cycle; + /** + * 季度 + * + * 枚举 {@link HrmPerformanceQuarterEnum} + */ + private Integer quarter; + /** + * 开始时间 + */ + private LocalDateTime startTime; + /** + * 结束时间 + */ + private LocalDateTime endTime; + /** + * 考核说明 + */ + private String description; + /** + * 考评范围列表 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List scopes; + + // ==================== 2. 指标设置 ==================== + + /** + * 考核模板编号 + * + * 关联 {@link HrmPerformanceAssessmentTemplateDO#getId()} + */ + private Long assessmentTemplateId; + /** + * 考核配置快照 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private HrmPerformanceAssessmentTemplateDO.AssessmentConfig assessmentConfig; + + // ==================== 3. 流程设置 ==================== + + /** + * 指标制定类型 + * + * 枚举 {@link HrmPerformanceQuotaSettingTypeEnum} + */ + private Integer quotaSettingType; + /** + * 是否开启目标确认 + */ + private Boolean targetConfirmation; + /** + * 目标确认节点 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private HandlerStage targetConfirmationStage; + /** + * 评分阶段列表 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List reviewStages; + /** + * 是否开启结果审核 + */ + private Boolean resultAudit; + /** + * 结果审核节点列表 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List resultAuditStages; + /** + * 是否开启结果确认 + */ + private Boolean resultConfirmation; + /** + * 申诉处理节点列表 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List appealStages; + /** + * 申诉超期天数 + */ + private Integer appealTimeoutDays; + /** + * 申诉超期处理动作 + * + * 枚举 {@link HrmPerformanceAppealTimeoutActionEnum} + */ + private Integer appealTimeoutAction; + + // ==================== 4. 结果设置 ==================== + + /** + * 结果模板编号 + * + * 关联 {@link HrmPerformanceResultTemplateDO#getId()} + */ + private Long resultTemplateId; + /** + * 结果配置快照 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private ResultConfig resultConfig; + /** + * 是否同步到薪资 + */ + private Boolean syncToSalary; + /** + * 计薪月份 + */ + private String paidForMonth; + + /** + * 绩效计划考评范围 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class Scope { + + /** + * 范围类型 + * + * 枚举 {@link HrmPerformancePlanScopeTypeEnum} + */ + private Integer type; + + /** + * 员工编号列表 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + private List employeeIds; + /** + * 部门编号列表 + * + * 关联 {@link DeptDO#getId()} + */ + private List deptIds; + /** + * 聘用形式 + * + * 枚举 {@link HrmEmployeeTypeEnum} + */ + private Integer employeeType; + /** + * 员工状态列表 + * + * 枚举 {@link HrmEmployeeStatusEnum} + */ + private List employeeStatuses; + } + + /** + * 绩效计划评分阶段 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class ReviewStage { + + /** + * 评分阶段名称 + */ + private String name; + /** + * 评分人 + */ + private HandlerStage rater; + /** + * 评分权重 + */ + private BigDecimal weight; + /** + * 评分方式 + * + * 枚举 {@link HrmPerformanceReviewScoringTypeEnum} + */ + private Integer scoringType; + /** + * 评分内容可见范围 + * + * 枚举 {@link HrmPerformanceReviewVisibleContentEnum} + */ + private Integer visibleContent; + /** + * 评语是否必填 + */ + private Boolean requiredSetting; + /** + * 是否允许驳回 + */ + private Boolean rejectAuthority; + } + + /** + * 绩效计划处理节点 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class HandlerStage { + + /** + * 处理人类型 + * + * 枚举 {@link HrmPerformanceRaterTypeEnum} + */ + private Integer type; + /** + * 上级或部门层级 + */ + private Integer level; + /** + * 指定处理员工编号 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + private Long employeeId; + } + + /** + * 绩效结果配置快照 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class ResultConfig { + + /** + * 模板名称 + */ + private String name; + /** + * 结果等级配置 + */ + private List levels; + } + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/recruit/candidate/HrmRecruitCandidateDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/recruit/candidate/HrmRecruitCandidateDO.java new file mode 100644 index 0000000..151b61f --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/recruit/candidate/HrmRecruitCandidateDO.java @@ -0,0 +1,129 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.recruit.candidate; + +import cn.iocoder.yudao.module.hrm.dal.dataobject.recruit.config.HrmRecruitChannelDO; +import cn.iocoder.yudao.module.hrm.dal.dataobject.recruit.post.HrmRecruitPostDO; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler; +import cn.iocoder.yudao.module.hrm.enums.DictTypeConstants; +import cn.iocoder.yudao.module.hrm.enums.recruit.candidate.HrmRecruitCandidateStatusEnum; +import cn.iocoder.yudao.module.system.enums.common.SexEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * HRM 招聘候选人 DO + * + * @author 芋道源码 + */ +@TableName(value = "hrm_recruit_candidate", autoResultMap = true) +@KeySequence("hrm_recruit_candidate_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmRecruitCandidateDO extends BaseDO { + + /** + * 候选人编号 + */ + @TableId + private Long id; + /** + * 候选人姓名 + */ + private String name; + /** + * 手机号码 + */ + private String mobile; + /** + * 性别 + * + * 枚举 {@link SexEnum} + */ + private Integer sex; + /** + * 年龄 + */ + private Integer age; + /** + * 邮箱 + */ + private String email; + /** + * 应聘职位编号 + * + * 关联 {@link HrmRecruitPostDO#getId()} + */ + private Long postId; + /** + * 面试轮次 + */ + private Integer stageNumber; + /** + * 工作年限,单位:年 + */ + private Integer workTime; + /** + * 学历 + * + * 字典 {@link DictTypeConstants#HRM_RECRUIT_CANDIDATE_EDUCATION} + */ + private Integer education; + /** + * 毕业院校 + */ + private String graduateSchool; + /** + * 最近工作单位 + */ + private String latestWorkPlace; + /** + * 招聘渠道编号 + * + * 关联 {@link HrmRecruitChannelDO#getId()} + */ + private Long channelId; + /** + * 备注 + */ + private String remark; + /** + * 候选人状态 + * + * 枚举 {@link HrmRecruitCandidateStatusEnum} + * 字典 {@link DictTypeConstants#HRM_RECRUIT_CANDIDATE_STATUS} + */ + private Integer status; + /** + * 淘汰原因 + */ + private String eliminate; + /** + * 状态更新时间 + */ + private LocalDateTime statusUpdateTime; + /** + * 入职时间 + */ + private LocalDateTime entryTime; + /** + * 简历附件地址数组 + */ + @TableField(typeHandler = StringListTypeHandler.class) + private List resumeUrls; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/recruit/candidate/HrmRecruitInterviewDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/recruit/candidate/HrmRecruitInterviewDO.java new file mode 100644 index 0000000..40beac0 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/recruit/candidate/HrmRecruitInterviewDO.java @@ -0,0 +1,99 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.recruit.candidate; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.module.hrm.enums.DictTypeConstants; +import cn.iocoder.yudao.module.hrm.enums.recruit.candidate.HrmRecruitInterviewResultEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * HRM 招聘面试记录 DO + * + * @author 芋道源码 + */ +@TableName(value = "hrm_recruit_interview", autoResultMap = true) +@KeySequence("hrm_recruit_interview_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmRecruitInterviewDO extends BaseDO { + + /** + * 面试编号 + */ + @TableId + private Long id; + /** + * 候选人编号 + * + * 关联 {@link HrmRecruitCandidateDO#getId()} + */ + private Long candidateId; + /** + * 面试方式 + * + * 字典 {@link DictTypeConstants#HRM_RECRUIT_INTERVIEW_TYPE} + */ + private Integer type; + /** + * 面试轮次 + */ + private Integer stageNumber; + /** + * 主面试官员工编号 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + private Long interviewEmployeeId; + /** + * 其他面试官员工编号数组 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + @TableField(typeHandler = LongListTypeHandler.class) + private List otherInterviewEmployeeIds; + /** + * 面试时间 + */ + private LocalDateTime interviewTime; + /** + * 面试地址 + */ + private String address; + /** + * 备注 + */ + private String remark; + /** + * 面试结果 + * + * 枚举 {@link HrmRecruitInterviewResultEnum} + * 字典 {@link DictTypeConstants#HRM_RECRUIT_INTERVIEW_RESULT} + */ + private Integer result; + /** + * 面试评价 + */ + private String evaluate; + /** + * 取消原因 + */ + private String cancelReason; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/recruit/config/HrmRecruitChannelDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/recruit/config/HrmRecruitChannelDO.java new file mode 100644 index 0000000..bbcc17c --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/recruit/config/HrmRecruitChannelDO.java @@ -0,0 +1,58 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.recruit.config; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +/** + * HRM 招聘渠道 DO + * + * @author 芋道源码 + */ +@TableName("hrm_recruit_channel") +@KeySequence("hrm_recruit_channel_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmRecruitChannelDO extends BaseDO { + + /** + * 招聘渠道编号 + */ + @TableId + private Long id; + /** + * 是否系统内置 + */ + private Boolean systemFlag; + /** + * 状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 渠道名称 + */ + private String name; + /** + * 显示顺序 + */ + private Integer sort; + /** + * 备注 + */ + private String remark; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/recruit/post/HrmRecruitPostDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/recruit/post/HrmRecruitPostDO.java new file mode 100644 index 0000000..afee29f --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/recruit/post/HrmRecruitPostDO.java @@ -0,0 +1,175 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.recruit.post; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; +import cn.iocoder.yudao.module.hrm.enums.DictTypeConstants; +import cn.iocoder.yudao.module.hrm.enums.recruit.post.HrmRecruitPostStatusEnum; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +/** + * HRM 招聘职位 DO + * + * @author 芋道源码 + */ +@TableName(value = "hrm_recruit_post", autoResultMap = true) +@KeySequence("hrm_recruit_post_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmRecruitPostDO extends BaseDO { + + /** + * 薪资 - 面议值 + */ + public static final BigDecimal SALARY_NEGOTIABLE_VALUE = BigDecimal.valueOf(-1); + /** + * 薪资 - 面议单位值 + */ + public static final Integer SALARY_NEGOTIABLE_UNIT_VALUE = -1; + + /** + * 年龄 - 不限值 + */ + public static final Integer AGE_UNLIMITED_VALUE = -1; + + /** + * 招聘职位编号 + */ + @TableId + private Long id; + /** + * 职位名称 + */ + private String postName; + /** + * 部门编号 + * + * 关联 {@link DeptDO#getId()} + */ + private Long deptId; + /** + * 工作性质 + * + * 字典 {@link DictTypeConstants#HRM_RECRUIT_JOB_NATURE} + */ + private Integer jobNature; + /** + * 工作城市地区编号 + * + * 关联 {@link cn.iocoder.yudao.framework.ip.core.Area#getId()} + */ + private Integer areaId; + /** + * 招聘人数 + */ + private Integer recruitNum; + /** + * 招聘原因 + */ + private String reason; + /** + * 工作经验要求 + * + * 字典 {@link DictTypeConstants#HRM_RECRUIT_WORK_TIME} + */ + private Integer workTime; + /** + * 学历要求 + * + * 字典 {@link DictTypeConstants#HRM_RECRUIT_POST_EDUCATION} + */ + private Integer educationRequire; + /** + * 最低薪资 + * + * -1 表示面议 + */ + private BigDecimal minSalary; + /** + * 最高薪资 + * + * -1 表示面议 + */ + private BigDecimal maxSalary; + /** + * 薪资单位 + * + * 字典 {@link DictTypeConstants#HRM_RECRUIT_SALARY_UNIT} + */ + private Integer salaryUnit; + /** + * 最小年龄 + * + * -1 表示不限 + */ + private Integer minAge; + /** + * 最大年龄 + * + * -1 表示不限 + */ + private Integer maxAge; + /** + * 最迟到岗时间 + */ + private LocalDateTime latestEntryTime; + /** + * 招聘负责人员工编号 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + private Long ownerEmployeeId; + /** + * 面试官员工编号数组 + * + * 关联 {@link HrmEmployeeDO#getId()} + */ + @TableField(typeHandler = LongListTypeHandler.class) + private List interviewEmployeeIds; + /** + * 职位描述 + */ + private String description; + /** + * 紧急程度 + * + * 字典 {@link DictTypeConstants#HRM_RECRUIT_EMERGENCY_LEVEL} + */ + private Integer emergencyLevel; + /** + * 职位类型编号 + * + * 关联 {@link HrmRecruitPostTypeDO#getId()} + */ + private Long postTypeId; + /** + * 职位状态 + * + * 枚举 {@link HrmRecruitPostStatusEnum} + * 字典 {@link DictTypeConstants#HRM_RECRUIT_POST_STATUS} + */ + private Integer status; + /** + * 停止原因 + */ + private String stopReason; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/recruit/post/HrmRecruitPostTypeDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/recruit/post/HrmRecruitPostTypeDO.java new file mode 100644 index 0000000..13944fc --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/recruit/post/HrmRecruitPostTypeDO.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.recruit.post; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +/** + * HRM 招聘职位类型 DO + * + * @author 芋道源码 + */ +@TableName("hrm_recruit_post_type") +@KeySequence("hrm_recruit_post_type_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmRecruitPostTypeDO extends BaseDO { + + /** + * 职位类型编号 + */ + @TableId + private Long id; + /** + * 类型名称 + */ + private String name; + /** + * 父类型编号 + * + * 关联 {@link #id} + */ + private Long parentId; + /** + * 显示顺序 + */ + private Integer sort; + /** + * 状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + +} diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/salary/config/HrmSalaryChangeTemplateDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/salary/config/HrmSalaryChangeTemplateDO.java new file mode 100644 index 0000000..58647c5 --- /dev/null +++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/salary/config/HrmSalaryChangeTemplateDO.java @@ -0,0 +1,76 @@ +package cn.iocoder.yudao.module.hrm.dal.dataobject.salary.config; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.util.List; + +/** + * HRM 调薪模板 DO + * + * @author 芋道源码 + */ +@TableName(value = "hrm_salary_change_template", autoResultMap = true) +@KeySequence("hrm_salary_change_template_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HrmSalaryChangeTemplateDO extends BaseDO { + + /** + * 调薪模板编号 + */ + @TableId + private Long id; + /** + * 模板名称 + */ + private String name; + /** + * 是否默认模板 + */ + private Boolean defaultStatus; + /** + * 调薪项配置 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List