Commit e2fa61b7e59152e89532c0c461a926e8c1368833
1 parent
caade90f
feat(hrm): consolidate human resources management
Showing
663 changed files
with
74608 additions
and
30 deletions
Too many changes.
To preserve performance only 100 of 663 files are displayed.
pom.xml
| ... | ... | @@ -16,7 +16,7 @@ |
| 16 | 16 | <module>yudao-module-system</module> |
| 17 | 17 | <module>yudao-module-infra</module> |
| 18 | 18 | <!-- <module>yudao-module-member</module>--> |
| 19 | -<!-- <module>yudao-module-bpm</module>--> | |
| 19 | + <module>yudao-module-bpm</module> | |
| 20 | 20 | <!-- <module>yudao-module-report</module>--> |
| 21 | 21 | <!-- <module>yudao-module-mp</module>--> |
| 22 | 22 | <!-- <module>yudao-module-pay</module>--> |
| ... | ... | @@ -26,7 +26,9 @@ |
| 26 | 26 | <!-- <module>yudao-module-iot</module>--> |
| 27 | 27 | <!-- <module>yudao-module-mes</module>--> |
| 28 | 28 | <!-- <module>yudao-module-wms</module>--> |
| 29 | -<!-- <module>yudao-module-im</module>--> | |
| 29 | + <module>yudao-module-hrm</module> | |
| 30 | + <module>yudao-module-fms</module> | |
| 31 | + <!-- <module>yudao-module-im</module>--> | |
| 30 | 32 | <!-- 请参考 https://doc.iocoder.cn/ai/build/ 文档,完成 AI 模块的启动!!! --> |
| 31 | 33 | <!-- <module>yudao-module-ai</module>--> |
| 32 | 34 | </modules> |
| ... | ... | @@ -36,7 +38,7 @@ |
| 36 | 38 | <url>https://github.com/YunaiV/ruoyi-vue-pro</url> |
| 37 | 39 | |
| 38 | 40 | <properties> |
| 39 | - <revision>2026.06-SNAPSHOT</revision> | |
| 41 | + <revision>2026.07-SNAPSHOT</revision> | |
| 40 | 42 | <!-- Maven 相关 --> |
| 41 | 43 | <java.version>17</java.version> |
| 42 | 44 | <maven.compiler.source>${java.version}</maven.compiler.source> | ... | ... |
yudao-dependencies/pom.xml
| ... | ... | @@ -57,6 +57,7 @@ |
| 57 | 57 | <hutool-5.version>5.8.46</hutool-5.version> |
| 58 | 58 | <hutool-6.version>6.0.0-M22</hutool-6.version> |
| 59 | 59 | <fastexcel.version>1.3.0</fastexcel.version> |
| 60 | + <aviator.version>5.3.3</aviator.version> | |
| 60 | 61 | <velocity.version>2.4.1</velocity.version> |
| 61 | 62 | <fastjson.version>1.2.83</fastjson.version> |
| 62 | 63 | <fastjson2.version>2.0.62</fastjson2.version> |
| ... | ... | @@ -488,6 +489,12 @@ |
| 488 | 489 | </dependency> |
| 489 | 490 | |
| 490 | 491 | <dependency> |
| 492 | + <groupId>com.googlecode.aviator</groupId> | |
| 493 | + <artifactId>aviator</artifactId> | |
| 494 | + <version>${aviator.version}</version> | |
| 495 | + </dependency> | |
| 496 | + | |
| 497 | + <dependency> | |
| 491 | 498 | <groupId>org.apache.tika</groupId> |
| 492 | 499 | <artifactId>tika-core</artifactId> <!-- 文件类型的识别 --> |
| 493 | 500 | <version>${tika-core.version}</version> | ... | ... |
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; |
| 4 | 4 | import cn.hutool.core.collection.CollectionUtil; |
| 5 | 5 | import cn.hutool.core.util.ArrayUtil; |
| 6 | 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 7 | +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | |
| 7 | 8 | import com.google.common.collect.ImmutableMap; |
| 8 | 9 | |
| 10 | +import java.math.BigDecimal; | |
| 9 | 11 | import java.util.*; |
| 10 | 12 | import java.util.function.*; |
| 11 | 13 | import java.util.stream.Collectors; |
| ... | ... | @@ -33,6 +35,34 @@ public class CollectionUtils { |
| 33 | 35 | return from.stream().anyMatch(predicate); |
| 34 | 36 | } |
| 35 | 37 | |
| 38 | + public static <T> long sum(Collection<T> from, ToLongFunction<T> valueFunc) { | |
| 39 | + if (CollUtil.isEmpty(from)) { | |
| 40 | + return 0L; | |
| 41 | + } | |
| 42 | + return from.stream().mapToLong(valueFunc).sum(); | |
| 43 | + } | |
| 44 | + | |
| 45 | + public static <T> BigDecimal sumBigDecimal(Collection<T> from, Function<T, BigDecimal> valueFunc) { | |
| 46 | + if (CollUtil.isEmpty(from)) { | |
| 47 | + return BigDecimal.ZERO; | |
| 48 | + } | |
| 49 | + return from.stream().map(valueFunc).map(NumberUtils::zeroIfNull).reduce(BigDecimal.ZERO, BigDecimal::add); | |
| 50 | + } | |
| 51 | + | |
| 52 | + public static <T> long count(Collection<T> from, Predicate<T> predicate) { | |
| 53 | + if (CollUtil.isEmpty(from)) { | |
| 54 | + return 0L; | |
| 55 | + } | |
| 56 | + return from.stream().filter(predicate).count(); | |
| 57 | + } | |
| 58 | + | |
| 59 | + public static <T, R> long distinctCount(Collection<T> from, Function<T, R> keyMapper) { | |
| 60 | + if (CollUtil.isEmpty(from)) { | |
| 61 | + return 0L; | |
| 62 | + } | |
| 63 | + return from.stream().map(keyMapper).filter(Objects::nonNull).distinct().count(); | |
| 64 | + } | |
| 65 | + | |
| 36 | 66 | public static <T> List<T> filterList(Collection<T> from, Predicate<T> predicate) { |
| 37 | 67 | if (CollUtil.isEmpty(from)) { |
| 38 | 68 | return new ArrayList<>(); |
| ... | ... | @@ -124,6 +154,15 @@ public class CollectionUtils { |
| 124 | 154 | return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toSet()); |
| 125 | 155 | } |
| 126 | 156 | |
| 157 | + public static <T, U, S extends Set<U>> S convertSetBySupplier( | |
| 158 | + Collection<T> from, Function<T, U> func, Supplier<S> supplier) { | |
| 159 | + if (CollUtil.isEmpty(from)) { | |
| 160 | + return supplier.get(); | |
| 161 | + } | |
| 162 | + return from.stream().map(func).filter(Objects::nonNull) | |
| 163 | + .collect(Collectors.toCollection(supplier)); | |
| 164 | + } | |
| 165 | + | |
| 127 | 166 | public static <T, U> Set<U> convertLinkedSet(Collection<T> from, Function<T, U> func) { |
| 128 | 167 | if (CollUtil.isEmpty(from)) { |
| 129 | 168 | return new LinkedHashSet<>(); |
| ... | ... | @@ -398,4 +437,4 @@ public class CollectionUtils { |
| 398 | 437 | return list; |
| 399 | 438 | } |
| 400 | 439 | |
| 401 | -} | |
| 402 | 440 | \ No newline at end of file |
| 441 | +} | ... | ... |
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; |
| 6 | 6 | import cn.hutool.core.lang.Assert; |
| 7 | 7 | import cn.hutool.core.util.StrUtil; |
| 8 | 8 | import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum; |
| 9 | +import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; | |
| 10 | +import lombok.AllArgsConstructor; | |
| 11 | +import lombok.Getter; | |
| 9 | 12 | |
| 10 | 13 | import java.sql.Timestamp; |
| 11 | 14 | import java.time.*; |
| ... | ... | @@ -14,13 +17,16 @@ import java.time.format.DateTimeParseException; |
| 14 | 17 | import java.time.temporal.ChronoUnit; |
| 15 | 18 | import java.time.temporal.TemporalAdjusters; |
| 16 | 19 | import java.util.ArrayList; |
| 20 | +import java.util.Collections; | |
| 21 | +import java.util.Comparator; | |
| 17 | 22 | import java.util.List; |
| 18 | 23 | import java.util.TimeZone; |
| 24 | +import java.util.stream.Collectors; | |
| 19 | 25 | |
| 20 | 26 | import static cn.hutool.core.date.DatePattern.*; |
| 21 | 27 | |
| 22 | 28 | /** |
| 23 | - * 时间工具类,用于 {@link LocalDateTime} | |
| 29 | + * 时间工具类,用于 {@link LocalDate}、{@link LocalDateTime} | |
| 24 | 30 | * |
| 25 | 31 | * @author 芋道源码 |
| 26 | 32 | */ |
| ... | ... | @@ -54,6 +60,16 @@ public class LocalDateTimeUtils { |
| 54 | 60 | } |
| 55 | 61 | } |
| 56 | 62 | |
| 63 | + /** | |
| 64 | + * 解析年月字符串为 {@link YearMonth},格式为 yyyy-MM | |
| 65 | + * | |
| 66 | + * @param month 年月字符串 | |
| 67 | + * @return 年月 | |
| 68 | + */ | |
| 69 | + public static YearMonth parseYearMonth(String month) { | |
| 70 | + return YearMonth.parse(month, NORM_MONTH_FORMATTER); | |
| 71 | + } | |
| 72 | + | |
| 57 | 73 | public static LocalDateTime addTime(Duration duration) { |
| 58 | 74 | return LocalDateTime.now().plus(duration); |
| 59 | 75 | } |
| ... | ... | @@ -70,6 +86,319 @@ public class LocalDateTimeUtils { |
| 70 | 86 | return date.isAfter(LocalDateTime.now()); |
| 71 | 87 | } |
| 72 | 88 | |
| 89 | + public static boolean beforeOrEqualNow(LocalDateTime time) { | |
| 90 | + LocalDateTime now = LocalDateTime.now(); | |
| 91 | + return time.isBefore(now) || time.isEqual(now); | |
| 92 | + } | |
| 93 | + | |
| 94 | + public static boolean beforeNow(LocalDate date) { | |
| 95 | + return date.isBefore(LocalDate.now()); | |
| 96 | + } | |
| 97 | + | |
| 98 | + public static boolean beforeOrEqualNow(LocalDate date) { | |
| 99 | + LocalDate today = LocalDate.now(); | |
| 100 | + return date.isBefore(today) || date.isEqual(today); | |
| 101 | + } | |
| 102 | + | |
| 103 | + public static boolean afterNow(LocalDate date) { | |
| 104 | + return date.isAfter(LocalDate.now()); | |
| 105 | + } | |
| 106 | + | |
| 107 | + /** | |
| 108 | + * 判断时间是否处于一月 | |
| 109 | + * | |
| 110 | + * @param time 时间 | |
| 111 | + * @return 是否处于一月 | |
| 112 | + */ | |
| 113 | + public static boolean isJanuary(LocalDateTime time) { | |
| 114 | + return time.getMonthValue() == 1; | |
| 115 | + } | |
| 116 | + | |
| 117 | + /** | |
| 118 | + * 判断第一个日期是否早于或等于第二个日期 | |
| 119 | + * | |
| 120 | + * @param firstDate 第一个日期 | |
| 121 | + * @param secondDate 第二个日期 | |
| 122 | + * @return 是否早于或等于 | |
| 123 | + */ | |
| 124 | + public static boolean isBeforeOrEqual(LocalDate firstDate, LocalDate secondDate) { | |
| 125 | + return firstDate.isBefore(secondDate) || firstDate.isEqual(secondDate); | |
| 126 | + } | |
| 127 | + | |
| 128 | + /** | |
| 129 | + * 判断第一个日期是否晚于或等于第二个日期 | |
| 130 | + * | |
| 131 | + * @param firstDate 第一个日期 | |
| 132 | + * @param secondDate 第二个日期 | |
| 133 | + * @return 是否晚于或等于 | |
| 134 | + */ | |
| 135 | + public static boolean isAfterOrEqual(LocalDate firstDate, LocalDate secondDate) { | |
| 136 | + return firstDate.isAfter(secondDate) || firstDate.isEqual(secondDate); | |
| 137 | + } | |
| 138 | + | |
| 139 | + /** | |
| 140 | + * 判断第一个时间是否早于或等于第二个时间 | |
| 141 | + * | |
| 142 | + * @param firstTime 第一个时间 | |
| 143 | + * @param secondTime 第二个时间 | |
| 144 | + * @return 是否早于或等于 | |
| 145 | + */ | |
| 146 | + public static boolean isBeforeOrEqual(LocalDateTime firstTime, LocalDateTime secondTime) { | |
| 147 | + return firstTime.isBefore(secondTime) || firstTime.isEqual(secondTime); | |
| 148 | + } | |
| 149 | + | |
| 150 | + /** | |
| 151 | + * 判断第一个时间是否晚于或等于第二个时间 | |
| 152 | + * | |
| 153 | + * @param firstTime 第一个时间 | |
| 154 | + * @param secondTime 第二个时间 | |
| 155 | + * @return 是否晚于或等于 | |
| 156 | + */ | |
| 157 | + public static boolean isAfterOrEqual(LocalDateTime firstTime, LocalDateTime secondTime) { | |
| 158 | + return firstTime.isAfter(secondTime) || firstTime.isEqual(secondTime); | |
| 159 | + } | |
| 160 | + | |
| 161 | + /** | |
| 162 | + * 判断时间范围是否有效 | |
| 163 | + * | |
| 164 | + * @param beginTime 开始时间 | |
| 165 | + * @param endTime 结束时间 | |
| 166 | + * @return 开始、结束时间非空,且结束时间晚于开始时间时返回 {@code true} | |
| 167 | + */ | |
| 168 | + public static boolean isTimeRangeValid(LocalDateTime beginTime, LocalDateTime endTime) { | |
| 169 | + return beginTime != null && endTime != null && endTime.isAfter(beginTime); | |
| 170 | + } | |
| 171 | + | |
| 172 | + /** | |
| 173 | + * 判断时间范围是否存在有效边界 | |
| 174 | + * | |
| 175 | + * @param times 时间范围 | |
| 176 | + * @return 任一边界非空时返回 {@code true} | |
| 177 | + */ | |
| 178 | + public static boolean isTimeRangePresent(LocalDateTime[] times) { | |
| 179 | + return ArrayUtils.get(times, 0) != null || ArrayUtils.get(times, 1) != null; | |
| 180 | + } | |
| 181 | + | |
| 182 | + /** | |
| 183 | + * 判断指定日期是否位于闭区间内 | |
| 184 | + * | |
| 185 | + * @param beginDate 开始日期 | |
| 186 | + * @param endDate 结束日期 | |
| 187 | + * @param date 指定日期 | |
| 188 | + * @return 是否位于闭区间内 | |
| 189 | + */ | |
| 190 | + public static boolean isBetween(LocalDate beginDate, LocalDate endDate, LocalDate date) { | |
| 191 | + return beginDate != null && endDate != null && date != null | |
| 192 | + && isAfterOrEqual(date, beginDate) && isBeforeOrEqual(date, endDate); | |
| 193 | + } | |
| 194 | + | |
| 195 | + /** | |
| 196 | + * 判断指定日期是否不在闭区间内 | |
| 197 | + * | |
| 198 | + * @param beginDate 开始日期 | |
| 199 | + * @param endDate 结束日期 | |
| 200 | + * @param date 指定日期 | |
| 201 | + * @return 是否不在闭区间内;任一参数为空时返回 {@code true} | |
| 202 | + */ | |
| 203 | + public static boolean isNotBetween(LocalDate beginDate, LocalDate endDate, LocalDate date) { | |
| 204 | + return !isBetween(beginDate, endDate, date); | |
| 205 | + } | |
| 206 | + | |
| 207 | + /** | |
| 208 | + * 判断指定时间是否位于闭区间内 | |
| 209 | + * | |
| 210 | + * @param beginTime 开始时间 | |
| 211 | + * @param endTime 结束时间 | |
| 212 | + * @param time 指定时间 | |
| 213 | + * @return 是否位于闭区间内 | |
| 214 | + */ | |
| 215 | + public static boolean isBetween(LocalDateTime beginTime, LocalDateTime endTime, LocalDateTime time) { | |
| 216 | + return beginTime != null && endTime != null && time != null | |
| 217 | + && isAfterOrEqual(time, beginTime) && isBeforeOrEqual(time, endTime); | |
| 218 | + } | |
| 219 | + | |
| 220 | + /** | |
| 221 | + * 判断指定时间是否不在闭区间内 | |
| 222 | + * | |
| 223 | + * @param beginTime 开始时间 | |
| 224 | + * @param endTime 结束时间 | |
| 225 | + * @param time 指定时间 | |
| 226 | + * @return 是否不在闭区间内;任一参数为空时返回 {@code true} | |
| 227 | + */ | |
| 228 | + public static boolean isNotBetween(LocalDateTime beginTime, LocalDateTime endTime, LocalDateTime time) { | |
| 229 | + return !isBetween(beginTime, endTime, time); | |
| 230 | + } | |
| 231 | + | |
| 232 | + /** | |
| 233 | + * 计算两个日期之间经过的完整年数 | |
| 234 | + * | |
| 235 | + * @param beginDate 开始日期 | |
| 236 | + * @param endDate 结束日期 | |
| 237 | + * @return 完整年数;日期为空或开始日期晚于结束日期时,返回 0 | |
| 238 | + */ | |
| 239 | + public static int getYearsBetween(LocalDate beginDate, LocalDate endDate) { | |
| 240 | + if (beginDate == null || endDate == null || beginDate.isAfter(endDate)) { | |
| 241 | + return 0; | |
| 242 | + } | |
| 243 | + return Period.between(beginDate, endDate).getYears(); | |
| 244 | + } | |
| 245 | + | |
| 246 | + /** | |
| 247 | + * 从时间范围列表中扣除指定时间范围 | |
| 248 | + * | |
| 249 | + * @param sourceRanges 原时间范围列表 | |
| 250 | + * @param excludedRange 需要扣除的时间范围 | |
| 251 | + * @return 扣除后的时间范围列表 | |
| 252 | + */ | |
| 253 | + public static List<TimeRange> subtractTimeRanges(List<TimeRange> sourceRanges, TimeRange excludedRange) { | |
| 254 | + List<TimeRange> result = new ArrayList<>(); | |
| 255 | + for (TimeRange sourceRange : sourceRanges) { | |
| 256 | + TimeRange overlap = intersectTimeRange(sourceRange, excludedRange); | |
| 257 | + if (overlap == null) { | |
| 258 | + result.add(sourceRange); | |
| 259 | + continue; | |
| 260 | + } | |
| 261 | + if (sourceRange.getStartTime().isBefore(overlap.getStartTime())) { | |
| 262 | + result.add(new TimeRange(sourceRange.getStartTime(), overlap.getStartTime())); | |
| 263 | + } | |
| 264 | + if (overlap.getEndTime().isBefore(sourceRange.getEndTime())) { | |
| 265 | + result.add(new TimeRange(overlap.getEndTime(), sourceRange.getEndTime())); | |
| 266 | + } | |
| 267 | + } | |
| 268 | + return result; | |
| 269 | + } | |
| 270 | + | |
| 271 | + /** | |
| 272 | + * 合并重叠或首尾相接的时间范围 | |
| 273 | + * | |
| 274 | + * @param timeRanges 时间范围列表 | |
| 275 | + * @return 合并后的时间范围列表 | |
| 276 | + */ | |
| 277 | + public static List<TimeRange> mergeTimeRanges(List<TimeRange> timeRanges) { | |
| 278 | + if (CollUtil.isEmpty(timeRanges)) { | |
| 279 | + return Collections.emptyList(); | |
| 280 | + } | |
| 281 | + List<TimeRange> sortedRanges = timeRanges.stream() | |
| 282 | + .sorted(Comparator.comparing(TimeRange::getStartTime)) | |
| 283 | + .collect(Collectors.toList()); | |
| 284 | + List<TimeRange> result = new ArrayList<>(); | |
| 285 | + TimeRange currentRange = CollUtil.getFirst(sortedRanges); | |
| 286 | + for (int i = 1; i < sortedRanges.size(); i++) { | |
| 287 | + TimeRange nextRange = sortedRanges.get(i); | |
| 288 | + if (nextRange.getStartTime().isAfter(currentRange.getEndTime())) { | |
| 289 | + result.add(currentRange); | |
| 290 | + currentRange = nextRange; | |
| 291 | + continue; | |
| 292 | + } | |
| 293 | + LocalDateTime endTime = currentRange.getEndTime().isAfter(nextRange.getEndTime()) | |
| 294 | + ? currentRange.getEndTime() : nextRange.getEndTime(); | |
| 295 | + currentRange = new TimeRange(currentRange.getStartTime(), endTime); | |
| 296 | + } | |
| 297 | + result.add(currentRange); | |
| 298 | + return result; | |
| 299 | + } | |
| 300 | + | |
| 301 | + /** | |
| 302 | + * 计算两个时间范围列表的交集 | |
| 303 | + * | |
| 304 | + * @param firstRanges 第一组时间范围 | |
| 305 | + * @param secondRanges 第二组时间范围 | |
| 306 | + * @return 合并后的交集时间范围列表 | |
| 307 | + */ | |
| 308 | + public static List<TimeRange> intersectTimeRanges( | |
| 309 | + List<TimeRange> firstRanges, List<TimeRange> secondRanges) { | |
| 310 | + List<TimeRange> result = new ArrayList<>(); | |
| 311 | + for (TimeRange firstRange : firstRanges) { | |
| 312 | + for (TimeRange secondRange : secondRanges) { | |
| 313 | + TimeRange overlap = intersectTimeRange(firstRange, secondRange); | |
| 314 | + if (overlap != null) { | |
| 315 | + result.add(overlap); | |
| 316 | + } | |
| 317 | + } | |
| 318 | + } | |
| 319 | + return mergeTimeRanges(result); | |
| 320 | + } | |
| 321 | + | |
| 322 | + /** | |
| 323 | + * 计算两个时间范围的交集 | |
| 324 | + * | |
| 325 | + * @param firstRange 第一个时间范围 | |
| 326 | + * @param secondRange 第二个时间范围 | |
| 327 | + * @return 交集时间范围;没有交集时返回 {@code null} | |
| 328 | + */ | |
| 329 | + public static TimeRange intersectTimeRange(TimeRange firstRange, TimeRange secondRange) { | |
| 330 | + LocalDateTime startTime = firstRange.getStartTime().isAfter(secondRange.getStartTime()) | |
| 331 | + ? firstRange.getStartTime() : secondRange.getStartTime(); | |
| 332 | + LocalDateTime endTime = firstRange.getEndTime().isBefore(secondRange.getEndTime()) | |
| 333 | + ? firstRange.getEndTime() : secondRange.getEndTime(); | |
| 334 | + return endTime.isAfter(startTime) ? new TimeRange(startTime, endTime) : null; | |
| 335 | + } | |
| 336 | + | |
| 337 | + /** | |
| 338 | + * 计算时间范围的总分钟数 | |
| 339 | + * | |
| 340 | + * @param timeRanges 时间范围列表 | |
| 341 | + * @return 总分钟数 | |
| 342 | + */ | |
| 343 | + public static int calculateDurationMinutes(List<TimeRange> timeRanges) { | |
| 344 | + return timeRanges.stream() | |
| 345 | + .mapToInt(range -> Math.toIntExact(Duration.between( | |
| 346 | + range.getStartTime(), range.getEndTime()).toMinutes())) | |
| 347 | + .sum(); | |
| 348 | + } | |
| 349 | + | |
| 350 | + /** | |
| 351 | + * 构建日期范围内每天对应的时间范围 | |
| 352 | + * | |
| 353 | + * 当结束时刻早于或等于开始时刻时,按跨天时间范围处理。 | |
| 354 | + * | |
| 355 | + * @param beginDate 开始日期 | |
| 356 | + * @param endDate 结束日期 | |
| 357 | + * @param beginTime 每日开始时刻 | |
| 358 | + * @param endTime 每日结束时刻 | |
| 359 | + * @return 每日时间范围列表 | |
| 360 | + */ | |
| 361 | + public static List<TimeRange> buildDailyTimeRanges( | |
| 362 | + LocalDate beginDate, LocalDate endDate, LocalTime beginTime, LocalTime endTime) { | |
| 363 | + if (beginDate == null || endDate == null || beginTime == null || endTime == null | |
| 364 | + || beginDate.isAfter(endDate)) { | |
| 365 | + return Collections.emptyList(); | |
| 366 | + } | |
| 367 | + List<TimeRange> result = new ArrayList<>(); | |
| 368 | + for (LocalDate date = beginDate; isBeforeOrEqual(date, endDate); date = date.plusDays(1)) { | |
| 369 | + LocalDateTime rangeBeginTime = date.atTime(beginTime); | |
| 370 | + LocalDateTime rangeEndTime = date.atTime(endTime); | |
| 371 | + if (rangeEndTime.isBefore(rangeBeginTime) || rangeEndTime.isEqual(rangeBeginTime)) { | |
| 372 | + rangeEndTime = rangeEndTime.plusDays(1); | |
| 373 | + } | |
| 374 | + result.add(new TimeRange(rangeBeginTime, rangeEndTime)); | |
| 375 | + } | |
| 376 | + return result; | |
| 377 | + } | |
| 378 | + | |
| 379 | + /** | |
| 380 | + * 查找包含指定时刻的每日时间范围 | |
| 381 | + * | |
| 382 | + * 每日时间范围允许跨天,因此会同时检查基准日期的前一天、当天和后一天。 | |
| 383 | + * 范围匹配使用左右闭区间。 | |
| 384 | + * | |
| 385 | + * @param date 基准日期 | |
| 386 | + * @param beginTime 每日开始时刻 | |
| 387 | + * @param endTime 每日结束时刻 | |
| 388 | + * @param time 指定时刻 | |
| 389 | + * @return 包含指定时刻的时间范围;不存在时返回 {@code null} | |
| 390 | + */ | |
| 391 | + public static TimeRange findDailyTimeRange( | |
| 392 | + LocalDate date, LocalTime beginTime, LocalTime endTime, LocalDateTime time) { | |
| 393 | + if (date == null || time == null) { | |
| 394 | + return null; | |
| 395 | + } | |
| 396 | + List<TimeRange> timeRanges = buildDailyTimeRanges( | |
| 397 | + date.minusDays(1), date.plusDays(1), beginTime, endTime); | |
| 398 | + return CollUtil.findOne(timeRanges, timeRange -> isBetween( | |
| 399 | + timeRange.getStartTime(), timeRange.getEndTime(), time)); | |
| 400 | + } | |
| 401 | + | |
| 73 | 402 | /** |
| 74 | 403 | * 将 Unix 秒时间戳转换为默认时区的本地时间 |
| 75 | 404 | * |
| ... | ... | @@ -185,6 +514,62 @@ public class LocalDateTimeUtils { |
| 185 | 514 | } |
| 186 | 515 | |
| 187 | 516 | /** |
| 517 | + * 获得指定日期的开始时间 | |
| 518 | + * | |
| 519 | + * @param date 日期 | |
| 520 | + * @return 当日开始时间;日期为空时返回 {@code null} | |
| 521 | + */ | |
| 522 | + public static LocalDateTime getDayBeginTime(LocalDate date) { | |
| 523 | + return date == null ? null : date.atStartOfDay(); | |
| 524 | + } | |
| 525 | + | |
| 526 | + /** | |
| 527 | + * 获得指定时间所在日期的开始时间 | |
| 528 | + * | |
| 529 | + * @param time 时间 | |
| 530 | + * @return 当日开始时间;时间为空时返回 {@code null} | |
| 531 | + */ | |
| 532 | + public static LocalDateTime getDayBeginTime(LocalDateTime time) { | |
| 533 | + return time == null ? null : getDayBeginTime(time.toLocalDate()); | |
| 534 | + } | |
| 535 | + | |
| 536 | + /** | |
| 537 | + * 获得指定日期的结束时间 | |
| 538 | + * | |
| 539 | + * @param date 日期 | |
| 540 | + * @return 当日结束时间;日期为空时返回 {@code null} | |
| 541 | + */ | |
| 542 | + public static LocalDateTime getDayEndTime(LocalDate date) { | |
| 543 | + return date == null ? null : date.atTime(LocalTime.MAX); | |
| 544 | + } | |
| 545 | + | |
| 546 | + /** | |
| 547 | + * 获得自然日期范围对应的日期时间闭区间 | |
| 548 | + * | |
| 549 | + * @param beginDate 开始日期 | |
| 550 | + * @param endDate 结束日期 | |
| 551 | + * @return 从开始日期零点到结束日期最后时刻的闭区间;两个日期都为空时返回 {@code null} | |
| 552 | + */ | |
| 553 | + public static LocalDateTime[] getDateTimeRange(LocalDate beginDate, LocalDate endDate) { | |
| 554 | + if (beginDate == null && endDate == null) { | |
| 555 | + return null; | |
| 556 | + } | |
| 557 | + return new LocalDateTime[]{getDayBeginTime(beginDate), getDayEndTime(endDate)}; | |
| 558 | + } | |
| 559 | + | |
| 560 | + /** | |
| 561 | + * 获得指定月份的日期时间闭区间 | |
| 562 | + * | |
| 563 | + * @param year 年份 | |
| 564 | + * @param month 月份 | |
| 565 | + * @return 从当月月初零点到月末最后时刻的闭区间 | |
| 566 | + */ | |
| 567 | + public static LocalDateTime[] getMonthDateTimeRange(int year, int month) { | |
| 568 | + LocalDate monthBeginDate = LocalDate.of(year, month, 1); | |
| 569 | + return getDateTimeRange(monthBeginDate, monthBeginDate.with(TemporalAdjusters.lastDayOfMonth())); | |
| 570 | + } | |
| 571 | + | |
| 572 | + /** | |
| 188 | 573 | * 获取指定日期所在的月份的开始时间 |
| 189 | 574 | * 例如:2023-09-30 00:00:00,000 |
| 190 | 575 | * |
| ... | ... | @@ -196,6 +581,16 @@ public class LocalDateTimeUtils { |
| 196 | 581 | } |
| 197 | 582 | |
| 198 | 583 | /** |
| 584 | + * 获取指定日期的开始时间 | |
| 585 | + * | |
| 586 | + * @param date 日期 | |
| 587 | + * @return 当日开始时间 | |
| 588 | + */ | |
| 589 | + public static LocalDateTime beginOfDay(LocalDateTime date) { | |
| 590 | + return date.with(LocalTime.MIN); | |
| 591 | + } | |
| 592 | + | |
| 593 | + /** | |
| 199 | 594 | * 获取指定日期所在的月份的最后时间 |
| 200 | 595 | * 例如:2023-09-30 23:59:59,999 |
| 201 | 596 | * |
| ... | ... | @@ -207,6 +602,82 @@ public class LocalDateTimeUtils { |
| 207 | 602 | } |
| 208 | 603 | |
| 209 | 604 | /** |
| 605 | + * 获得指定年月的月初时间 | |
| 606 | + * | |
| 607 | + * @param year 年份 | |
| 608 | + * @param month 月份 | |
| 609 | + * @return 月初时间 | |
| 610 | + */ | |
| 611 | + public static LocalDateTime getMonthBeginTime(int year, int month) { | |
| 612 | + return LocalDateTime.of(year, month, 1, 0, 0); | |
| 613 | + } | |
| 614 | + | |
| 615 | + /** | |
| 616 | + * 获得指定年月的月初时间 | |
| 617 | + * | |
| 618 | + * @param month 年月 | |
| 619 | + * @return 月初时间 | |
| 620 | + */ | |
| 621 | + public static LocalDateTime getMonthBeginTime(YearMonth month) { | |
| 622 | + return getMonthBeginTime(month.getYear(), month.getMonthValue()); | |
| 623 | + } | |
| 624 | + | |
| 625 | + /** | |
| 626 | + * 获得指定年月的下月月初时间 | |
| 627 | + * | |
| 628 | + * @param year 年份 | |
| 629 | + * @param month 月份 | |
| 630 | + * @return 下月月初时间 | |
| 631 | + */ | |
| 632 | + public static LocalDateTime getNextMonthBeginTime(int year, int month) { | |
| 633 | + return getMonthBeginTime(year, month).plusMonths(1); | |
| 634 | + } | |
| 635 | + | |
| 636 | + /** | |
| 637 | + * 获得指定年月的下月月初时间 | |
| 638 | + * | |
| 639 | + * @param month 年月 | |
| 640 | + * @return 下月月初时间 | |
| 641 | + */ | |
| 642 | + public static LocalDateTime getNextMonthBeginTime(YearMonth month) { | |
| 643 | + return getMonthBeginTime(month).plusMonths(1); | |
| 644 | + } | |
| 645 | + | |
| 646 | + /** | |
| 647 | + * 判断两个时间段是否存在正时长的重叠 | |
| 648 | + * | |
| 649 | + * @param beginTime1 第一个范围的开始时间 | |
| 650 | + * @param endTime1 第一个范围的结束时间 | |
| 651 | + * @param beginTime2 第二个范围的开始时间 | |
| 652 | + * @param endTime2 第二个范围的结束时间 | |
| 653 | + * @return 是否存在正时长的重叠 | |
| 654 | + */ | |
| 655 | + public static boolean isOverlap(LocalDateTime beginTime1, LocalDateTime endTime1, | |
| 656 | + LocalDateTime beginTime2, LocalDateTime endTime2) { | |
| 657 | + if (beginTime1 == null || endTime1 == null || beginTime2 == null || endTime2 == null) { | |
| 658 | + return false; | |
| 659 | + } | |
| 660 | + return beginTime1.isBefore(endTime2) && endTime1.isAfter(beginTime2); | |
| 661 | + } | |
| 662 | + | |
| 663 | + /** | |
| 664 | + * 判断两个左右闭合的时间范围是否重叠 | |
| 665 | + * | |
| 666 | + * @param beginTime1 第一个范围的开始时间 | |
| 667 | + * @param endTime1 第一个范围的结束时间 | |
| 668 | + * @param beginTime2 第二个范围的开始时间 | |
| 669 | + * @param endTime2 第二个范围的结束时间 | |
| 670 | + * @return 是否重叠 | |
| 671 | + */ | |
| 672 | + public static boolean isClosedRangeOverlap(LocalDateTime beginTime1, LocalDateTime endTime1, | |
| 673 | + LocalDateTime beginTime2, LocalDateTime endTime2) { | |
| 674 | + if (beginTime1 == null || endTime1 == null || beginTime2 == null || endTime2 == null) { | |
| 675 | + return false; | |
| 676 | + } | |
| 677 | + return isBeforeOrEqual(beginTime1, endTime2) && isAfterOrEqual(endTime1, beginTime2); | |
| 678 | + } | |
| 679 | + | |
| 680 | + /** | |
| 210 | 681 | * 获得指定日期所在季度 |
| 211 | 682 | * |
| 212 | 683 | * @param date 日期 |
| ... | ... | @@ -353,7 +824,11 @@ public class LocalDateTimeUtils { |
| 353 | 824 | * @return 日期列表,包含开始日期 |
| 354 | 825 | */ |
| 355 | 826 | public static List<LocalDate> getDateList(LocalDate startDate, int days) { |
| 356 | - return startDate.datesUntil(startDate.plusDays(days)).toList(); | |
| 827 | + List<LocalDate> dateList = new ArrayList<>(days); | |
| 828 | + for (int i = 0; i < days; i++) { | |
| 829 | + dateList.add(startDate.plusDays(i)); | |
| 830 | + } | |
| 831 | + return dateList; | |
| 357 | 832 | } |
| 358 | 833 | |
| 359 | 834 | /** |
| ... | ... | @@ -433,4 +908,23 @@ public class LocalDateTimeUtils { |
| 433 | 908 | return sourceDateTime.atZone(zoneId).toEpochSecond(); |
| 434 | 909 | } |
| 435 | 910 | |
| 911 | + /** | |
| 912 | + * 时间范围 | |
| 913 | + */ | |
| 914 | + @Getter | |
| 915 | + @AllArgsConstructor | |
| 916 | + @SuppressWarnings("ClassCanBeRecord") | |
| 917 | + public static class TimeRange { | |
| 918 | + | |
| 919 | + /** | |
| 920 | + * 开始时间 | |
| 921 | + */ | |
| 922 | + private final LocalDateTime startTime; | |
| 923 | + /** | |
| 924 | + * 结束时间 | |
| 925 | + */ | |
| 926 | + private final LocalDateTime endTime; | |
| 927 | + | |
| 928 | + } | |
| 929 | + | |
| 436 | 930 | } | ... | ... |
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/MoneyUtils.java
| ... | ... | @@ -97,6 +97,39 @@ public class MoneyUtils { |
| 97 | 97 | } |
| 98 | 98 | |
| 99 | 99 | /** |
| 100 | + * 格式化金额,默认进行四舍五入 | |
| 101 | + * | |
| 102 | + * 位数:{@link #PRICE_SCALE}。金额为空时,按 0 处理。 | |
| 103 | + * | |
| 104 | + * @param price 金额 | |
| 105 | + * @return 格式化后的金额 | |
| 106 | + */ | |
| 107 | + public static BigDecimal priceScale(BigDecimal price) { | |
| 108 | + return (price == null ? BigDecimal.ZERO : price).setScale(PRICE_SCALE, RoundingMode.HALF_UP); | |
| 109 | + } | |
| 110 | + | |
| 111 | + /** | |
| 112 | + * 金额相加,默认进行四舍五入 | |
| 113 | + * | |
| 114 | + * 位数:{@link #PRICE_SCALE}。忽略空金额,没有有效金额时返回 0。 | |
| 115 | + * | |
| 116 | + * @param prices 金额数组 | |
| 117 | + * @return 金额相加结果 | |
| 118 | + */ | |
| 119 | + public static BigDecimal priceAdd(BigDecimal... prices) { | |
| 120 | + BigDecimal result = BigDecimal.ZERO; | |
| 121 | + if (prices == null) { | |
| 122 | + return priceScale(result); | |
| 123 | + } | |
| 124 | + for (BigDecimal price : prices) { | |
| 125 | + if (price != null) { | |
| 126 | + result = result.add(price); | |
| 127 | + } | |
| 128 | + } | |
| 129 | + return priceScale(result); | |
| 130 | + } | |
| 131 | + | |
| 132 | + /** | |
| 100 | 133 | * 金额相乘,默认进行四舍五入 |
| 101 | 134 | * |
| 102 | 135 | * 位数:{@link #PRICE_SCALE} |
| ... | ... | @@ -109,7 +142,7 @@ public class MoneyUtils { |
| 109 | 142 | if (price == null || count == null) { |
| 110 | 143 | return null; |
| 111 | 144 | } |
| 112 | - return price.multiply(count).setScale(PRICE_SCALE, RoundingMode.HALF_UP); | |
| 145 | + return priceScale(price.multiply(count)); | |
| 113 | 146 | } |
| 114 | 147 | |
| 115 | 148 | /** | ... | ... |
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; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.util.NumberUtil; |
| 5 | +import cn.hutool.core.util.ReUtil; | |
| 5 | 6 | import cn.hutool.core.util.StrUtil; |
| 6 | 7 | |
| 7 | 8 | import java.math.BigDecimal; |
| 8 | 9 | import java.util.List; |
| 10 | +import java.util.regex.Pattern; | |
| 9 | 11 | |
| 10 | 12 | /** |
| 11 | 13 | * 数字的工具类,补全 {@link cn.hutool.core.util.NumberUtil} 的功能 |
| ... | ... | @@ -14,6 +16,13 @@ import java.util.List; |
| 14 | 16 | */ |
| 15 | 17 | public class NumberUtils { |
| 16 | 18 | |
| 19 | + private static final Pattern FIRST_BIG_DECIMAL_PATTERN = Pattern.compile("-?\\d+(?:\\.\\d+)?"); | |
| 20 | + | |
| 21 | + /** | |
| 22 | + * 一百,用于百分数和比例计算 | |
| 23 | + */ | |
| 24 | + public static final BigDecimal ONE_HUNDRED = new BigDecimal("100"); | |
| 25 | + | |
| 17 | 26 | public static Long parseLong(String str) { |
| 18 | 27 | return StrUtil.isNotEmpty(str) ? Long.valueOf(str) : null; |
| 19 | 28 | } |
| ... | ... | @@ -22,6 +31,30 @@ public class NumberUtils { |
| 22 | 31 | return StrUtil.isNotEmpty(str) ? Integer.valueOf(str) : null; |
| 23 | 32 | } |
| 24 | 33 | |
| 34 | + /** | |
| 35 | + * 获得数字,为 null 时返回 {@link BigDecimal#ZERO} | |
| 36 | + * | |
| 37 | + * @param value 数字 | |
| 38 | + * @return 数字 | |
| 39 | + */ | |
| 40 | + public static BigDecimal zeroIfNull(BigDecimal value) { | |
| 41 | + return value == null ? BigDecimal.ZERO : value; | |
| 42 | + } | |
| 43 | + | |
| 44 | + /** | |
| 45 | + * 从文本中解析首个数字 | |
| 46 | + * | |
| 47 | + * @param text 文本 | |
| 48 | + * @return 数字;不存在时返回 {@link BigDecimal#ZERO} | |
| 49 | + */ | |
| 50 | + public static BigDecimal parseFirstBigDecimal(String text) { | |
| 51 | + if (StrUtil.isEmpty(text)) { | |
| 52 | + return BigDecimal.ZERO; | |
| 53 | + } | |
| 54 | + String number = ReUtil.getGroup0(FIRST_BIG_DECIMAL_PATTERN, text.replace(",", "")); | |
| 55 | + return NumberUtil.toBigDecimal(number); | |
| 56 | + } | |
| 57 | + | |
| 25 | 58 | public static boolean isAllNumber(List<String> values) { |
| 26 | 59 | if (CollUtil.isEmpty(values)) { |
| 27 | 60 | return false; | ... | ... |
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; |
| 5 | 5 | import cn.hutool.core.lang.func.LambdaUtil; |
| 6 | 6 | import cn.hutool.core.util.ArrayUtil; |
| 7 | 7 | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| 8 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 8 | 9 | import cn.iocoder.yudao.framework.common.pojo.SortablePageParam; |
| 9 | 10 | import cn.iocoder.yudao.framework.common.pojo.SortingField; |
| 10 | 11 | import org.springframework.util.Assert; |
| 11 | 12 | |
| 13 | +import java.util.List; | |
| 14 | + | |
| 12 | 15 | import static java.util.Collections.singletonList; |
| 13 | 16 | |
| 14 | 17 | /** |
| ... | ... | @@ -25,6 +28,27 @@ public class PageUtils { |
| 25 | 28 | } |
| 26 | 29 | |
| 27 | 30 | /** |
| 31 | + * 对内存列表进行分页 | |
| 32 | + * | |
| 33 | + * @param list 数据列表 | |
| 34 | + * @param pageParam 分页参数 | |
| 35 | + * @param <T> 数据类型 | |
| 36 | + * @return 分页结果 | |
| 37 | + */ | |
| 38 | + public static <T> PageResult<T> buildPageResult(List<T> list, PageParam pageParam) { | |
| 39 | + if (CollUtil.isEmpty(list)) { | |
| 40 | + return PageResult.empty(); | |
| 41 | + } | |
| 42 | + long total = list.size(); | |
| 43 | + if (PageParam.PAGE_SIZE_NONE.equals(pageParam.getPageSize())) { | |
| 44 | + return new PageResult<>(list, total); | |
| 45 | + } | |
| 46 | + int fromIndex = Math.toIntExact(Math.min(getStart(pageParam), total)); | |
| 47 | + int toIndex = Math.toIntExact(Math.min((long) fromIndex + pageParam.getPageSize(), total)); | |
| 48 | + return new PageResult<>(list.subList(fromIndex, toIndex), total); | |
| 49 | + } | |
| 50 | + | |
| 51 | + /** | |
| 28 | 52 | * 构建排序字段(默认倒序) |
| 29 | 53 | * |
| 30 | 54 | * @param func 排序字段的 Lambda 表达式 | ... | ... |
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; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.util.StrUtil; |
| 4 | 4 | import cn.hutool.extra.servlet.JakartaServletUtil; |
| 5 | +import cn.iocoder.yudao.framework.common.util.http.HttpUtils; | |
| 5 | 6 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 6 | 7 | import jakarta.servlet.ServletRequest; |
| 7 | 8 | import jakarta.servlet.http.HttpServletRequest; |
| ... | ... | @@ -11,6 +12,7 @@ import org.springframework.web.context.request.RequestAttributes; |
| 11 | 12 | import org.springframework.web.context.request.RequestContextHolder; |
| 12 | 13 | import org.springframework.web.context.request.ServletRequestAttributes; |
| 13 | 14 | |
| 15 | +import java.io.IOException; | |
| 14 | 16 | import java.util.Map; |
| 15 | 17 | |
| 16 | 18 | /** |
| ... | ... | @@ -102,4 +104,11 @@ public class ServletUtils { |
| 102 | 104 | return JakartaServletUtil.getHeaderMap(request); |
| 103 | 105 | } |
| 104 | 106 | |
| 107 | + public static void writeAttachment(HttpServletResponse response, String filename, byte[] content) | |
| 108 | + throws IOException { | |
| 109 | + response.getOutputStream().write(content); | |
| 110 | + response.addHeader("Content-Disposition", "attachment;filename=" + HttpUtils.encodeUtf8(filename)); | |
| 111 | + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); | |
| 112 | + } | |
| 113 | + | |
| 105 | 114 | } | ... | ... |
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; |
| 6 | 6 | |
| 7 | 7 | import java.util.Arrays; |
| 8 | 8 | import java.util.Collection; |
| 9 | +import java.util.Collections; | |
| 9 | 10 | import java.util.List; |
| 11 | +import java.util.TreeSet; | |
| 10 | 12 | import java.util.function.BiFunction; |
| 11 | 13 | |
| 12 | 14 | import static org.junit.jupiter.api.Assertions.assertEquals; |
| ... | ... | @@ -27,6 +29,38 @@ public class CollectionUtilsTest { |
| 27 | 29 | } |
| 28 | 30 | |
| 29 | 31 | @Test |
| 32 | + public void testSum() { | |
| 33 | + assertEquals(6L, CollectionUtils.sum(Arrays.asList(1L, 2L, 3L), Long::longValue)); | |
| 34 | + assertEquals(0L, CollectionUtils.sum(Collections.<Long>emptyList(), Long::longValue)); | |
| 35 | + } | |
| 36 | + | |
| 37 | + @Test | |
| 38 | + public void testCount() { | |
| 39 | + assertEquals(2L, CollectionUtils.count(Arrays.asList(1, 2, 3, 4), value -> value % 2 == 0)); | |
| 40 | + assertEquals(0L, CollectionUtils.count(Collections.emptyList(), value -> true)); | |
| 41 | + } | |
| 42 | + | |
| 43 | + @Test | |
| 44 | + public void testDistinctCount() { | |
| 45 | + assertEquals(3L, CollectionUtils.distinctCount(Arrays.asList("a", "b", "a", "c"), value -> value)); | |
| 46 | + assertEquals(0L, CollectionUtils.distinctCount(Collections.emptyList(), value -> value)); | |
| 47 | + } | |
| 48 | + | |
| 49 | + @Test | |
| 50 | + public void testConvertSet_withSupplier() { | |
| 51 | + // 调用,并断言:按指定 Set 类型完成转换、去重和排序 | |
| 52 | + TreeSet<Integer> result = CollectionUtils.convertSetBySupplier( | |
| 53 | + Arrays.asList("3", "1", null, "3", "2"), | |
| 54 | + value -> value == null ? null : Integer.valueOf(value), TreeSet::new); | |
| 55 | + assertEquals(Arrays.asList(1, 2, 3), Arrays.asList(result.toArray(new Integer[0]))); | |
| 56 | + | |
| 57 | + // 调用,并断言:空集合仍返回指定 Set 类型 | |
| 58 | + result = CollectionUtils.convertSetBySupplier(Collections.<String>emptyList(), | |
| 59 | + Integer::valueOf, TreeSet::new); | |
| 60 | + assertEquals(TreeSet.class, result.getClass()); | |
| 61 | + } | |
| 62 | + | |
| 63 | + @Test | |
| 30 | 64 | public void testDiffList() { |
| 31 | 65 | // 准备参数 |
| 32 | 66 | Collection<Dog> oldList = Arrays.asList( | ... | ... |
yudao-framework/yudao-common/src/test/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtilsTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.common.util.date; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.TimeRange; | |
| 5 | +import org.junit.jupiter.api.Test; | |
| 6 | + | |
| 7 | +import java.time.LocalDate; | |
| 8 | +import java.time.LocalDateTime; | |
| 9 | +import java.time.LocalTime; | |
| 10 | +import java.time.YearMonth; | |
| 11 | +import java.util.Arrays; | |
| 12 | +import java.util.Collections; | |
| 13 | +import java.util.List; | |
| 14 | + | |
| 15 | +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| 16 | +import static org.junit.jupiter.api.Assertions.assertFalse; | |
| 17 | +import static org.junit.jupiter.api.Assertions.assertNotNull; | |
| 18 | +import static org.junit.jupiter.api.Assertions.assertNull; | |
| 19 | +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| 20 | + | |
| 21 | +/** | |
| 22 | + * {@link LocalDateTimeUtils} 的单元测试 | |
| 23 | + * | |
| 24 | + * @author 芋道源码 | |
| 25 | + */ | |
| 26 | +public class LocalDateTimeUtilsTest { | |
| 27 | + | |
| 28 | + @Test | |
| 29 | + public void testDateCompare() { | |
| 30 | + // 准备参数 | |
| 31 | + LocalDate firstDate = LocalDate.of(2026, 7, 23); | |
| 32 | + LocalDate secondDate = LocalDate.of(2026, 7, 24); | |
| 33 | + | |
| 34 | + // 调用,并断言 | |
| 35 | + assertTrue(LocalDateTimeUtils.isBeforeOrEqual(firstDate, secondDate)); | |
| 36 | + assertTrue(LocalDateTimeUtils.isBeforeOrEqual(secondDate, secondDate)); | |
| 37 | + assertFalse(LocalDateTimeUtils.isBeforeOrEqual(secondDate, firstDate)); | |
| 38 | + assertTrue(LocalDateTimeUtils.isAfterOrEqual(secondDate, firstDate)); | |
| 39 | + assertTrue(LocalDateTimeUtils.isAfterOrEqual(secondDate, secondDate)); | |
| 40 | + assertFalse(LocalDateTimeUtils.isAfterOrEqual(firstDate, secondDate)); | |
| 41 | + } | |
| 42 | + | |
| 43 | + @Test | |
| 44 | + public void testDateTimeCompare() { | |
| 45 | + // 准备参数 | |
| 46 | + LocalDateTime firstTime = LocalDateTime.of(2026, 7, 24, 9, 0); | |
| 47 | + LocalDateTime secondTime = LocalDateTime.of(2026, 7, 24, 18, 0); | |
| 48 | + | |
| 49 | + // 调用,并断言 | |
| 50 | + assertTrue(LocalDateTimeUtils.isBeforeOrEqual(firstTime, secondTime)); | |
| 51 | + assertTrue(LocalDateTimeUtils.isBeforeOrEqual(secondTime, secondTime)); | |
| 52 | + assertFalse(LocalDateTimeUtils.isBeforeOrEqual(secondTime, firstTime)); | |
| 53 | + assertTrue(LocalDateTimeUtils.isAfterOrEqual(secondTime, firstTime)); | |
| 54 | + assertTrue(LocalDateTimeUtils.isAfterOrEqual(secondTime, secondTime)); | |
| 55 | + assertFalse(LocalDateTimeUtils.isAfterOrEqual(firstTime, secondTime)); | |
| 56 | + } | |
| 57 | + | |
| 58 | + @Test | |
| 59 | + public void testIsTimeRangeValid() { | |
| 60 | + // 准备参数 | |
| 61 | + LocalDateTime beginTime = LocalDateTime.of(2026, 7, 24, 9, 0); | |
| 62 | + LocalDateTime endTime = LocalDateTime.of(2026, 7, 24, 18, 0); | |
| 63 | + | |
| 64 | + // 调用,并断言 | |
| 65 | + assertTrue(LocalDateTimeUtils.isTimeRangeValid(beginTime, endTime)); | |
| 66 | + assertFalse(LocalDateTimeUtils.isTimeRangeValid(beginTime, beginTime)); | |
| 67 | + assertFalse(LocalDateTimeUtils.isTimeRangeValid(endTime, beginTime)); | |
| 68 | + assertFalse(LocalDateTimeUtils.isTimeRangeValid(null, endTime)); | |
| 69 | + assertFalse(LocalDateTimeUtils.isTimeRangeValid(beginTime, null)); | |
| 70 | + } | |
| 71 | + | |
| 72 | + @Test | |
| 73 | + public void testIsTimeRangePresent() { | |
| 74 | + // 准备参数 | |
| 75 | + LocalDateTime time = LocalDateTime.of(2026, 7, 24, 9, 0); | |
| 76 | + | |
| 77 | + // 调用,并断言:空数组和双空边界都不是有效范围 | |
| 78 | + assertFalse(LocalDateTimeUtils.isTimeRangePresent(null)); | |
| 79 | + assertFalse(LocalDateTimeUtils.isTimeRangePresent(new LocalDateTime[0])); | |
| 80 | + assertFalse(LocalDateTimeUtils.isTimeRangePresent(new LocalDateTime[]{null, null})); | |
| 81 | + | |
| 82 | + // 调用,并断言:单边或双边存在即为有效范围 | |
| 83 | + assertTrue(LocalDateTimeUtils.isTimeRangePresent(new LocalDateTime[]{time})); | |
| 84 | + assertTrue(LocalDateTimeUtils.isTimeRangePresent(new LocalDateTime[]{null, time})); | |
| 85 | + assertTrue(LocalDateTimeUtils.isTimeRangePresent(new LocalDateTime[]{time, time.plusHours(1)})); | |
| 86 | + } | |
| 87 | + | |
| 88 | + @Test | |
| 89 | + public void testGetDayTime() { | |
| 90 | + // 准备参数 | |
| 91 | + LocalDate date = LocalDate.of(2026, 7, 24); | |
| 92 | + LocalDateTime time = date.atTime(15, 30); | |
| 93 | + | |
| 94 | + // 调用,并断言 | |
| 95 | + assertEquals(LocalDateTime.of(2026, 7, 24, 0, 0), | |
| 96 | + LocalDateTimeUtils.getDayBeginTime(date)); | |
| 97 | + assertEquals(LocalDateTime.of(2026, 7, 24, 0, 0), | |
| 98 | + LocalDateTimeUtils.getDayBeginTime(time)); | |
| 99 | + assertNull(LocalDateTimeUtils.getDayBeginTime((LocalDateTime) null)); | |
| 100 | + assertEquals(LocalDateTime.of(date, LocalTime.MAX), | |
| 101 | + LocalDateTimeUtils.getDayEndTime(date)); | |
| 102 | + } | |
| 103 | + | |
| 104 | + @Test | |
| 105 | + public void testGetDateTimeRange() { | |
| 106 | + // 准备参数 | |
| 107 | + LocalDate beginDate = LocalDate.of(2026, 7, 1); | |
| 108 | + LocalDate endDate = LocalDate.of(2026, 7, 31); | |
| 109 | + | |
| 110 | + // 调用,并断言:左右两端均为闭区间边界 | |
| 111 | + LocalDateTime[] result = LocalDateTimeUtils.getDateTimeRange(beginDate, endDate); | |
| 112 | + assertEquals(beginDate.atStartOfDay(), result[0]); | |
| 113 | + assertEquals(endDate.atTime(LocalTime.MAX), result[1]); | |
| 114 | + | |
| 115 | + // 调用,并断言:支持单边范围 | |
| 116 | + result = LocalDateTimeUtils.getDateTimeRange(beginDate, null); | |
| 117 | + assertEquals(beginDate.atStartOfDay(), result[0]); | |
| 118 | + assertNull(result[1]); | |
| 119 | + result = LocalDateTimeUtils.getDateTimeRange(null, endDate); | |
| 120 | + assertNull(result[0]); | |
| 121 | + assertEquals(endDate.atTime(LocalTime.MAX), result[1]); | |
| 122 | + | |
| 123 | + // 调用,并断言:两个边界都为空时不生成范围 | |
| 124 | + assertNull(LocalDateTimeUtils.getDateTimeRange(null, null)); | |
| 125 | + } | |
| 126 | + | |
| 127 | + @Test | |
| 128 | + public void testGetMonthDateTimeRange() { | |
| 129 | + // 调用:闰年二月 | |
| 130 | + LocalDateTime[] result = LocalDateTimeUtils.getMonthDateTimeRange(2024, 2); | |
| 131 | + | |
| 132 | + // 断言:完整包含闰年二月的首尾时刻 | |
| 133 | + assertEquals(LocalDateTime.of(2024, 2, 1, 0, 0), result[0]); | |
| 134 | + assertEquals(LocalDate.of(2024, 2, 29).atTime(LocalTime.MAX), result[1]); | |
| 135 | + } | |
| 136 | + | |
| 137 | + @Test | |
| 138 | + public void testGetMonthBeginTime() { | |
| 139 | + // 准备参数 | |
| 140 | + YearMonth month = YearMonth.of(2026, 12); | |
| 141 | + | |
| 142 | + // 调用,并断言 | |
| 143 | + assertEquals(LocalDateTime.of(2026, 12, 1, 0, 0), | |
| 144 | + LocalDateTimeUtils.getMonthBeginTime(month)); | |
| 145 | + assertEquals(LocalDateTime.of(2027, 1, 1, 0, 0), | |
| 146 | + LocalDateTimeUtils.getNextMonthBeginTime(month)); | |
| 147 | + } | |
| 148 | + | |
| 149 | + @Test | |
| 150 | + public void testIsBetween() { | |
| 151 | + // 准备参数 | |
| 152 | + LocalDate beginDate = LocalDate.of(2026, 7, 23); | |
| 153 | + LocalDate endDate = LocalDate.of(2026, 7, 25); | |
| 154 | + LocalDateTime beginTime = beginDate.atTime(9, 0); | |
| 155 | + LocalDateTime endTime = beginDate.atTime(18, 0); | |
| 156 | + | |
| 157 | + // 调用,并断言:日期闭区间 | |
| 158 | + assertTrue(LocalDateTimeUtils.isBetween(beginDate, endDate, beginDate)); | |
| 159 | + assertTrue(LocalDateTimeUtils.isBetween(beginDate, endDate, LocalDate.of(2026, 7, 24))); | |
| 160 | + assertTrue(LocalDateTimeUtils.isBetween(beginDate, endDate, endDate)); | |
| 161 | + assertFalse(LocalDateTimeUtils.isBetween(beginDate, endDate, LocalDate.of(2026, 7, 26))); | |
| 162 | + assertFalse(LocalDateTimeUtils.isBetween(beginDate, endDate, null)); | |
| 163 | + assertFalse(LocalDateTimeUtils.isNotBetween(beginDate, endDate, beginDate)); | |
| 164 | + assertTrue(LocalDateTimeUtils.isNotBetween(beginDate, endDate, LocalDate.of(2026, 7, 26))); | |
| 165 | + assertTrue(LocalDateTimeUtils.isNotBetween(beginDate, endDate, null)); | |
| 166 | + | |
| 167 | + // 调用,并断言:时间闭区间 | |
| 168 | + assertTrue(LocalDateTimeUtils.isBetween(beginTime, endTime, beginTime)); | |
| 169 | + assertTrue(LocalDateTimeUtils.isBetween(beginTime, endTime, beginDate.atTime(12, 0))); | |
| 170 | + assertTrue(LocalDateTimeUtils.isBetween(beginTime, endTime, endTime)); | |
| 171 | + assertFalse(LocalDateTimeUtils.isBetween(beginTime, endTime, beginDate.atTime(18, 1))); | |
| 172 | + assertFalse(LocalDateTimeUtils.isBetween(beginTime, endTime, (LocalDateTime) null)); | |
| 173 | + assertFalse(LocalDateTimeUtils.isNotBetween(beginTime, endTime, endTime)); | |
| 174 | + assertTrue(LocalDateTimeUtils.isNotBetween(beginTime, endTime, beginDate.atTime(18, 1))); | |
| 175 | + assertTrue(LocalDateTimeUtils.isNotBetween(beginTime, endTime, null)); | |
| 176 | + } | |
| 177 | + | |
| 178 | + @Test | |
| 179 | + public void testIsClosedRangeOverlap() { | |
| 180 | + // 准备参数 | |
| 181 | + LocalDateTime firstBeginTime = LocalDateTime.of(2026, 7, 24, 9, 0); | |
| 182 | + LocalDateTime firstEndTime = LocalDateTime.of(2026, 7, 24, 12, 0); | |
| 183 | + | |
| 184 | + // 调用,并断言:首尾相接属于闭区间重叠 | |
| 185 | + assertTrue(LocalDateTimeUtils.isClosedRangeOverlap( | |
| 186 | + firstBeginTime, firstEndTime, firstEndTime, firstEndTime.plusHours(1))); | |
| 187 | + assertTrue(LocalDateTimeUtils.isClosedRangeOverlap( | |
| 188 | + firstBeginTime, firstEndTime, firstBeginTime.minusHours(1), firstBeginTime)); | |
| 189 | + assertFalse(LocalDateTimeUtils.isClosedRangeOverlap( | |
| 190 | + firstBeginTime, firstEndTime, firstEndTime.plusNanos(1), firstEndTime.plusHours(1))); | |
| 191 | + assertFalse(LocalDateTimeUtils.isClosedRangeOverlap( | |
| 192 | + firstBeginTime, firstEndTime, null, firstEndTime)); | |
| 193 | + } | |
| 194 | + | |
| 195 | + @Test | |
| 196 | + public void testSubtractTimeRanges() { | |
| 197 | + // 准备参数 | |
| 198 | + LocalDate date = LocalDate.of(2026, 7, 24); | |
| 199 | + TimeRange sourceRange = new TimeRange(date.atTime(9, 0), date.atTime(18, 0)); | |
| 200 | + | |
| 201 | + // 调用:扣除原时段中间的一段 | |
| 202 | + List<TimeRange> result = LocalDateTimeUtils.subtractTimeRanges( | |
| 203 | + Collections.singletonList(sourceRange), | |
| 204 | + new TimeRange(date.atTime(12, 0), date.atTime(13, 0))); | |
| 205 | + | |
| 206 | + // 断言:原时段被拆分为两段 | |
| 207 | + assertEquals(2, result.size()); | |
| 208 | + assertTimeRange(CollUtil.getFirst(result), date.atTime(9, 0), date.atTime(12, 0)); | |
| 209 | + assertTimeRange(result.get(1), date.atTime(13, 0), date.atTime(18, 0)); | |
| 210 | + | |
| 211 | + // 调用,并断言:扣除时段完整覆盖原时段 | |
| 212 | + result = LocalDateTimeUtils.subtractTimeRanges( | |
| 213 | + Collections.singletonList(sourceRange), | |
| 214 | + new TimeRange(date.atTime(8, 0), date.atTime(19, 0))); | |
| 215 | + assertTrue(result.isEmpty()); | |
| 216 | + | |
| 217 | + // 调用:扣除时段与原时段首尾相接 | |
| 218 | + result = LocalDateTimeUtils.subtractTimeRanges( | |
| 219 | + Collections.singletonList(sourceRange), | |
| 220 | + new TimeRange(date.atTime(18, 0), date.atTime(19, 0))); | |
| 221 | + // 断言:两个时段仅首尾相接,没有正时长交集,原时段保持不变 | |
| 222 | + assertEquals(1, result.size()); | |
| 223 | + assertTimeRange(CollUtil.getFirst(result), date.atTime(9, 0), date.atTime(18, 0)); | |
| 224 | + } | |
| 225 | + | |
| 226 | + @Test | |
| 227 | + public void testMergeTimeRanges() { | |
| 228 | + // 准备参数 | |
| 229 | + LocalDate date = LocalDate.of(2026, 7, 24); | |
| 230 | + List<TimeRange> timeRanges = Arrays.asList( | |
| 231 | + new TimeRange(date.atTime(13, 0), date.atTime(14, 0)), | |
| 232 | + new TimeRange(date.atTime(9, 0), date.atTime(11, 0)), | |
| 233 | + new TimeRange(date.atTime(10, 0), date.atTime(13, 0)), | |
| 234 | + new TimeRange(date.atTime(16, 0), date.atTime(17, 0))); | |
| 235 | + | |
| 236 | + // 调用 | |
| 237 | + List<TimeRange> result = LocalDateTimeUtils.mergeTimeRanges(timeRanges); | |
| 238 | + | |
| 239 | + // 断言:重叠或首尾相接的时段被合并,独立时段保持不变 | |
| 240 | + assertEquals(2, result.size()); | |
| 241 | + assertTimeRange(CollUtil.getFirst(result), date.atTime(9, 0), date.atTime(14, 0)); | |
| 242 | + assertTimeRange(result.get(1), date.atTime(16, 0), date.atTime(17, 0)); | |
| 243 | + // 调用,并断言:空列表 | |
| 244 | + assertTrue(LocalDateTimeUtils.mergeTimeRanges(Collections.emptyList()).isEmpty()); | |
| 245 | + } | |
| 246 | + | |
| 247 | + @Test | |
| 248 | + public void testIntersectTimeRanges() { | |
| 249 | + // 准备参数 | |
| 250 | + LocalDate date = LocalDate.of(2026, 7, 24); | |
| 251 | + List<TimeRange> firstRanges = Arrays.asList( | |
| 252 | + new TimeRange(date.atTime(9, 0), date.atTime(12, 0)), | |
| 253 | + new TimeRange(date.atTime(13, 0), date.atTime(18, 0))); | |
| 254 | + List<TimeRange> secondRanges = Arrays.asList( | |
| 255 | + new TimeRange(date.atTime(10, 0), date.atTime(14, 0)), | |
| 256 | + new TimeRange(date.atTime(17, 0), date.atTime(19, 0))); | |
| 257 | + | |
| 258 | + // 调用 | |
| 259 | + List<TimeRange> result = LocalDateTimeUtils.intersectTimeRanges(firstRanges, secondRanges); | |
| 260 | + | |
| 261 | + // 断言 | |
| 262 | + assertEquals(3, result.size()); | |
| 263 | + assertTimeRange(CollUtil.getFirst(result), date.atTime(10, 0), date.atTime(12, 0)); | |
| 264 | + assertTimeRange(result.get(1), date.atTime(13, 0), date.atTime(14, 0)); | |
| 265 | + assertTimeRange(result.get(2), date.atTime(17, 0), date.atTime(18, 0)); | |
| 266 | + // 调用,并断言:两个时段仅首尾相接时不生成零时长交集 | |
| 267 | + assertNull(LocalDateTimeUtils.intersectTimeRange( | |
| 268 | + new TimeRange(date.atTime(9, 0), date.atTime(10, 0)), | |
| 269 | + new TimeRange(date.atTime(10, 0), date.atTime(11, 0)))); | |
| 270 | + } | |
| 271 | + | |
| 272 | + @Test | |
| 273 | + public void testCalculateDurationMinutes() { | |
| 274 | + // 准备参数 | |
| 275 | + LocalDate date = LocalDate.of(2026, 7, 24); | |
| 276 | + List<TimeRange> timeRanges = Arrays.asList( | |
| 277 | + new TimeRange(date.atTime(9, 0), date.atTime(10, 0)), | |
| 278 | + new TimeRange(date.atTime(13, 0), date.atTime(14, 30))); | |
| 279 | + | |
| 280 | + // 调用,并断言 | |
| 281 | + assertEquals(150, LocalDateTimeUtils.calculateDurationMinutes(timeRanges)); | |
| 282 | + // 调用,并断言:空列表 | |
| 283 | + assertEquals(0, LocalDateTimeUtils.calculateDurationMinutes(Collections.emptyList())); | |
| 284 | + } | |
| 285 | + | |
| 286 | + @Test | |
| 287 | + public void testBuildDailyTimeRanges() { | |
| 288 | + // 准备参数 | |
| 289 | + LocalDate beginDate = LocalDate.of(2026, 7, 24); | |
| 290 | + LocalDate endDate = LocalDate.of(2026, 7, 25); | |
| 291 | + | |
| 292 | + // 调用:构建跨天时间范围 | |
| 293 | + List<TimeRange> result = LocalDateTimeUtils.buildDailyTimeRanges( | |
| 294 | + beginDate, endDate, LocalTime.of(22, 0), LocalTime.of(6, 0)); | |
| 295 | + | |
| 296 | + // 断言 | |
| 297 | + assertEquals(2, result.size()); | |
| 298 | + assertTimeRange(CollUtil.getFirst(result), beginDate.atTime(22, 0), beginDate.plusDays(1).atTime(6, 0)); | |
| 299 | + assertTimeRange(result.get(1), endDate.atTime(22, 0), endDate.plusDays(1).atTime(6, 0)); | |
| 300 | + | |
| 301 | + // 调用:每日开始和结束时刻相同 | |
| 302 | + result = LocalDateTimeUtils.buildDailyTimeRanges( | |
| 303 | + beginDate, beginDate, LocalTime.of(9, 0), LocalTime.of(9, 0)); | |
| 304 | + // 断言:按完整一天处理 | |
| 305 | + assertEquals(1, result.size()); | |
| 306 | + assertTimeRange(CollUtil.getFirst(result), beginDate.atTime(9, 0), beginDate.plusDays(1).atTime(9, 0)); | |
| 307 | + | |
| 308 | + // 调用,并断言:开始日期晚于结束日期 | |
| 309 | + assertTrue(LocalDateTimeUtils.buildDailyTimeRanges( | |
| 310 | + endDate, beginDate, LocalTime.of(9, 0), LocalTime.of(18, 0)).isEmpty()); | |
| 311 | + } | |
| 312 | + | |
| 313 | + @Test | |
| 314 | + public void testFindDailyTimeRange() { | |
| 315 | + // 准备参数 | |
| 316 | + LocalDate date = LocalDate.of(2026, 7, 8); | |
| 317 | + | |
| 318 | + // 调用,并断言普通时间范围的左右边界 | |
| 319 | + TimeRange result = LocalDateTimeUtils.findDailyTimeRange( | |
| 320 | + date, LocalTime.of(8, 0), LocalTime.of(10, 0), date.atTime(8, 0)); | |
| 321 | + assertEquals(date.atTime(8, 0), result.getStartTime()); | |
| 322 | + assertEquals(date.atTime(10, 0), result.getEndTime()); | |
| 323 | + assertNotNull(LocalDateTimeUtils.findDailyTimeRange( | |
| 324 | + date, LocalTime.of(8, 0), LocalTime.of(10, 0), date.atTime(10, 0))); | |
| 325 | + | |
| 326 | + // 调用,并断言跨日时间范围 | |
| 327 | + result = LocalDateTimeUtils.findDailyTimeRange( | |
| 328 | + date, LocalTime.of(20, 0), LocalTime.of(6, 0), date.plusDays(1).atTime(5, 0)); | |
| 329 | + assertEquals(date.atTime(20, 0), result.getStartTime()); | |
| 330 | + assertEquals(date.plusDays(1).atTime(6, 0), result.getEndTime()); | |
| 331 | + assertNull(LocalDateTimeUtils.findDailyTimeRange( | |
| 332 | + date, LocalTime.of(8, 0), LocalTime.of(10, 0), date.atTime(12, 0))); | |
| 333 | + } | |
| 334 | + | |
| 335 | + private static void assertTimeRange(TimeRange timeRange, | |
| 336 | + LocalDateTime expectedStartTime, | |
| 337 | + LocalDateTime expectedEndTime) { | |
| 338 | + assertEquals(expectedStartTime, timeRange.getStartTime()); | |
| 339 | + assertEquals(expectedEndTime, timeRange.getEndTime()); | |
| 340 | + } | |
| 341 | + | |
| 342 | +} | ... | ... |
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/StringListConvert.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.excel.core.convert; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.idev.excel.converters.Converter; | |
| 5 | +import cn.idev.excel.metadata.GlobalConfiguration; | |
| 6 | +import cn.idev.excel.metadata.data.WriteCellData; | |
| 7 | +import cn.idev.excel.metadata.property.ExcelContentProperty; | |
| 8 | + | |
| 9 | +import java.util.List; | |
| 10 | + | |
| 11 | +/** | |
| 12 | + * Excel 字符串列表转换器 | |
| 13 | + * | |
| 14 | + * @author 芋道源码 | |
| 15 | + */ | |
| 16 | +public class StringListConvert implements Converter<List<String>> { | |
| 17 | + | |
| 18 | + @Override | |
| 19 | + public WriteCellData<String> convertToExcelData(List<String> value, | |
| 20 | + ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { | |
| 21 | + return new WriteCellData<>(CollUtil.isEmpty(value) ? "" : String.join("/", value)); | |
| 22 | + } | |
| 23 | + | |
| 24 | +} | ... | ... |
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java
| 1 | 1 | package cn.iocoder.yudao.framework.excel.core.util; |
| 2 | 2 | |
| 3 | 3 | import cn.idev.excel.FastExcelFactory; |
| 4 | +import cn.idev.excel.context.AnalysisContext; | |
| 4 | 5 | import cn.idev.excel.converters.longconverter.LongStringConverter; |
| 6 | +import cn.idev.excel.read.listener.ReadListener; | |
| 5 | 7 | import cn.iocoder.yudao.framework.common.util.http.HttpUtils; |
| 6 | 8 | import cn.iocoder.yudao.framework.excel.core.handler.ColumnWidthMatchStyleStrategy; |
| 7 | 9 | import cn.iocoder.yudao.framework.excel.core.handler.SelectSheetWriteHandler; |
| ... | ... | @@ -10,7 +12,10 @@ import org.springframework.web.multipart.MultipartFile; |
| 10 | 12 | |
| 11 | 13 | import java.io.IOException; |
| 12 | 14 | import java.io.InputStream; |
| 15 | +import java.util.ArrayList; | |
| 16 | +import java.util.Collections; | |
| 13 | 17 | import java.util.List; |
| 18 | +import java.util.Map; | |
| 14 | 19 | |
| 15 | 20 | /** |
| 16 | 21 | * Excel 工具类 |
| ... | ... | @@ -44,7 +49,33 @@ public class ExcelUtils { |
| 44 | 49 | response.setContentType("application/vnd.ms-excel;charset=UTF-8"); |
| 45 | 50 | } |
| 46 | 51 | |
| 52 | + /** | |
| 53 | + * 将动态表头和数据以 Excel 响应给前端 | |
| 54 | + * | |
| 55 | + * @param response 响应 | |
| 56 | + * @param filename 文件名 | |
| 57 | + * @param sheetName Excel sheet 名 | |
| 58 | + * @param head Excel head 头 | |
| 59 | + * @param data 数据列表 | |
| 60 | + * @throws IOException 写入失败的情况 | |
| 61 | + */ | |
| 62 | + public static void write(HttpServletResponse response, String filename, String sheetName, | |
| 63 | + List<List<String>> head, List<List<Object>> data) throws IOException { | |
| 64 | + // 输出 Excel | |
| 65 | + FastExcelFactory.write(response.getOutputStream()) | |
| 66 | + .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 | |
| 67 | + .registerWriteHandler(new ColumnWidthMatchStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度 | |
| 68 | + .registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度 | |
| 69 | + .head(head).sheet(sheetName).doWrite(data); | |
| 70 | + // 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了 | |
| 71 | + response.addHeader("Content-Disposition", "attachment;filename=" + HttpUtils.encodeUtf8(filename)); | |
| 72 | + response.setContentType("application/vnd.ms-excel;charset=UTF-8"); | |
| 73 | + } | |
| 74 | + | |
| 47 | 75 | public static <T> List<T> read(MultipartFile file, Class<T> head) throws IOException { |
| 76 | + if (file == null || file.isEmpty()) { | |
| 77 | + return Collections.emptyList(); | |
| 78 | + } | |
| 48 | 79 | // 参考 https://t.zsxq.com/zM77F 帖子,增加 try 处理,兼容 windows 场景 |
| 49 | 80 | try (InputStream inputStream = file.getInputStream()) { |
| 50 | 81 | return FastExcelFactory.read(inputStream, head, null) |
| ... | ... | @@ -53,4 +84,64 @@ public class ExcelUtils { |
| 53 | 84 | } |
| 54 | 85 | } |
| 55 | 86 | |
| 87 | + /** | |
| 88 | + * 读取指定数量以内的 Excel 数据,达到上限后停止继续解析。 | |
| 89 | + * | |
| 90 | + * @param file Excel 文件,可为空 | |
| 91 | + * @param head Excel 表头类型 | |
| 92 | + * @param maxRowCount 最大读取行数 | |
| 93 | + * @return Excel 数据 | |
| 94 | + */ | |
| 95 | + public static <T> List<T> read(MultipartFile file, Class<T> head, int maxRowCount) throws IOException { | |
| 96 | + if (file == null || file.isEmpty()) { | |
| 97 | + return Collections.emptyList(); | |
| 98 | + } | |
| 99 | + if (maxRowCount <= 0) { | |
| 100 | + throw new IllegalArgumentException("maxRowCount must be greater than 0"); | |
| 101 | + } | |
| 102 | + List<T> rows = new ArrayList<>(Math.min(maxRowCount, 1024)); | |
| 103 | + try (InputStream inputStream = file.getInputStream()) { | |
| 104 | + FastExcelFactory.read(inputStream, head, new ReadListener<T>() { | |
| 105 | + | |
| 106 | + @Override | |
| 107 | + public void invoke(T data, AnalysisContext context) { | |
| 108 | + rows.add(data); | |
| 109 | + } | |
| 110 | + | |
| 111 | + @Override | |
| 112 | + public void doAfterAllAnalysed(AnalysisContext context) { | |
| 113 | + // 无需处理 | |
| 114 | + } | |
| 115 | + | |
| 116 | + @Override | |
| 117 | + public boolean hasNext(AnalysisContext context) { | |
| 118 | + return rows.size() < maxRowCount; | |
| 119 | + } | |
| 120 | + | |
| 121 | + }).autoCloseStream(false).doReadAll(); | |
| 122 | + } | |
| 123 | + return rows; | |
| 124 | + } | |
| 125 | + | |
| 126 | + /** | |
| 127 | + * 读取动态表头 Excel 数据 | |
| 128 | + * | |
| 129 | + * @param file Excel 文件,可为空 | |
| 130 | + * @return 按列下标存储的行数据 | |
| 131 | + * @throws IOException 读取失败 | |
| 132 | + */ | |
| 133 | + public static List<Map<Integer, String>> read(MultipartFile file) throws IOException { | |
| 134 | + if (file == null || file.isEmpty()) { | |
| 135 | + return Collections.emptyList(); | |
| 136 | + } | |
| 137 | + // 参考 https://t.zsxq.com/zM77F 帖子,增加 try 处理,兼容 windows 场景 | |
| 138 | + try (InputStream inputStream = file.getInputStream()) { | |
| 139 | + return FastExcelFactory.read(inputStream) | |
| 140 | + .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 | |
| 141 | + .headRowNumber(1) | |
| 142 | + .sheet() | |
| 143 | + .doReadSync(); | |
| 144 | + } | |
| 145 | + } | |
| 146 | + | |
| 56 | 147 | } | ... | ... |
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/PoiExcelUtils.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.excel.core.util; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.NumberUtil; | |
| 4 | +import cn.hutool.core.util.StrUtil; | |
| 5 | +import lombok.experimental.UtilityClass; | |
| 6 | +import org.apache.poi.ss.usermodel.BorderStyle; | |
| 7 | +import org.apache.poi.ss.usermodel.Cell; | |
| 8 | +import org.apache.poi.ss.usermodel.CellStyle; | |
| 9 | +import org.apache.poi.ss.usermodel.ClientAnchor; | |
| 10 | +import org.apache.poi.ss.usermodel.Comment; | |
| 11 | +import org.apache.poi.ss.usermodel.CreationHelper; | |
| 12 | +import org.apache.poi.ss.usermodel.DataFormatter; | |
| 13 | +import org.apache.poi.ss.usermodel.FillPatternType; | |
| 14 | +import org.apache.poi.ss.usermodel.Font; | |
| 15 | +import org.apache.poi.ss.usermodel.FormulaEvaluator; | |
| 16 | +import org.apache.poi.ss.usermodel.HorizontalAlignment; | |
| 17 | +import org.apache.poi.ss.usermodel.IndexedColors; | |
| 18 | +import org.apache.poi.ss.usermodel.Row; | |
| 19 | +import org.apache.poi.ss.usermodel.Sheet; | |
| 20 | +import org.apache.poi.ss.usermodel.VerticalAlignment; | |
| 21 | +import org.apache.poi.ss.usermodel.Workbook; | |
| 22 | +import org.apache.poi.ss.util.CellRangeAddress; | |
| 23 | + | |
| 24 | +import java.math.BigDecimal; | |
| 25 | +import java.util.Collection; | |
| 26 | + | |
| 27 | +/** | |
| 28 | + * 原生 POI 的 Excel 工具类 | |
| 29 | + * | |
| 30 | + * 全局 {@link ExcelUtils}(FastExcel)无法表达单元格锁定、公式、动态列等复杂模板时,各模块基于本类实现自定义 Excel 读写 | |
| 31 | + * | |
| 32 | + * @author 芋道源码 | |
| 33 | + */ | |
| 34 | +@UtilityClass | |
| 35 | +public class PoiExcelUtils { | |
| 36 | + | |
| 37 | + /** | |
| 38 | + * 读取公式计算值必须 DataFormatter + FormulaEvaluator,hutool 没有更简的等价写法 | |
| 39 | + */ | |
| 40 | + private static final DataFormatter DATA_FORMATTER = new DataFormatter(); | |
| 41 | + | |
| 42 | + // ==================== 读取 ==================== | |
| 43 | + | |
| 44 | + /** | |
| 45 | + * 读取单元格文本,公式单元格取公式计算值 | |
| 46 | + * | |
| 47 | + * @param row 行 | |
| 48 | + * @param column 列 | |
| 49 | + * @param evaluator 公式计算器,不需要求值时传 null | |
| 50 | + * @return 文本,空单元格返回空字符串 | |
| 51 | + */ | |
| 52 | + public static String getCellText(Row row, Integer column, FormulaEvaluator evaluator) { | |
| 53 | + if (row == null || column == null || row.getCell(column) == null) { | |
| 54 | + return ""; | |
| 55 | + } | |
| 56 | + return evaluator == null | |
| 57 | + ? DATA_FORMATTER.formatCellValue(row.getCell(column)) | |
| 58 | + : DATA_FORMATTER.formatCellValue(row.getCell(column), evaluator); | |
| 59 | + } | |
| 60 | + | |
| 61 | + /** | |
| 62 | + * 读取单元格数字,兼容千分位文本 | |
| 63 | + * | |
| 64 | + * @param row 行 | |
| 65 | + * @param column 列 | |
| 66 | + * @param evaluator 公式计算器,不需要求值时传 null | |
| 67 | + * @return 数字,空单元格返回 null;格式不正确时抛出 {@link NumberFormatException} | |
| 68 | + */ | |
| 69 | + public static BigDecimal getDecimal(Row row, Integer column, FormulaEvaluator evaluator) { | |
| 70 | + String value = getCellText(row, column, evaluator).replace(",", ""); | |
| 71 | + return StrUtil.isBlank(value) ? null : NumberUtil.toBigDecimal(value); | |
| 72 | + } | |
| 73 | + | |
| 74 | + /** | |
| 75 | + * 判断指定列是否全部为空 | |
| 76 | + * | |
| 77 | + * @param row 行 | |
| 78 | + * @param evaluator 公式计算器,不需要求值时传 null | |
| 79 | + * @param columns 列数组 | |
| 80 | + * @return 是否全部为空 | |
| 81 | + */ | |
| 82 | + public static boolean isEmptyRow(Row row, FormulaEvaluator evaluator, Collection<Integer> columns) { | |
| 83 | + if (row == null) { | |
| 84 | + return true; | |
| 85 | + } | |
| 86 | + for (Integer column : columns) { | |
| 87 | + if (StrUtil.isNotBlank(getCellText(row, column, evaluator))) { | |
| 88 | + return false; | |
| 89 | + } | |
| 90 | + } | |
| 91 | + return true; | |
| 92 | + } | |
| 93 | + | |
| 94 | + // ==================== 写入 ==================== | |
| 95 | + | |
| 96 | + /** | |
| 97 | + * 写入文本单元格,null 按空字符串处理 | |
| 98 | + */ | |
| 99 | + public static void writeText(Row row, int column, String value, CellStyle style) { | |
| 100 | + Cell cell = row.createCell(column); | |
| 101 | + cell.setCellValue(StrUtil.nullToEmpty(value)); | |
| 102 | + cell.setCellStyle(style); | |
| 103 | + } | |
| 104 | + | |
| 105 | + /** | |
| 106 | + * 写入数字单元格,null 时留空 | |
| 107 | + */ | |
| 108 | + public static void writeNumber(Row row, int column, BigDecimal value, CellStyle style) { | |
| 109 | + Cell cell = row.createCell(column); | |
| 110 | + if (value != null) { | |
| 111 | + cell.setCellValue(value.doubleValue()); | |
| 112 | + } | |
| 113 | + cell.setCellStyle(style); | |
| 114 | + } | |
| 115 | + | |
| 116 | + /** | |
| 117 | + * 写入公式单元格 | |
| 118 | + */ | |
| 119 | + public static void writeFormula(Row row, int column, String formula, CellStyle style) { | |
| 120 | + Cell cell = row.createCell(column); | |
| 121 | + cell.setCellFormula(formula); | |
| 122 | + cell.setCellStyle(style); | |
| 123 | + } | |
| 124 | + | |
| 125 | + /** | |
| 126 | + * 写入跨两行的合并表头 | |
| 127 | + */ | |
| 128 | + public static void mergeHeader(Sheet sheet, Row firstRow, Row secondRow, | |
| 129 | + int beginColumn, int endColumn, String title, CellStyle style) { | |
| 130 | + sheet.addMergedRegion(new CellRangeAddress(firstRow.getRowNum(), secondRow.getRowNum(), | |
| 131 | + beginColumn, endColumn)); | |
| 132 | + Cell cell = firstRow.createCell(beginColumn); | |
| 133 | + cell.setCellValue(title); | |
| 134 | + cell.setCellStyle(style); | |
| 135 | + secondRow.createCell(beginColumn).setCellStyle(style); | |
| 136 | + } | |
| 137 | + | |
| 138 | + /** | |
| 139 | + * 添加单元格批注 | |
| 140 | + * | |
| 141 | + * @param cell 单元格 | |
| 142 | + * @param text 批注内容 | |
| 143 | + */ | |
| 144 | + public static void addComment(Cell cell, String text) { | |
| 145 | + CreationHelper helper = cell.getSheet().getWorkbook().getCreationHelper(); | |
| 146 | + ClientAnchor anchor = helper.createClientAnchor(); | |
| 147 | + anchor.setCol1(cell.getColumnIndex()); | |
| 148 | + anchor.setCol2(cell.getColumnIndex() + 3); | |
| 149 | + anchor.setRow1(cell.getRowIndex()); | |
| 150 | + anchor.setRow2(cell.getRowIndex() + 3); | |
| 151 | + Comment comment = cell.getSheet().createDrawingPatriarch().createCellComment(anchor); | |
| 152 | + comment.setString(helper.createRichTextString(text)); | |
| 153 | + cell.setCellComment(comment); | |
| 154 | + } | |
| 155 | + | |
| 156 | + // ==================== 样式 ==================== | |
| 157 | + | |
| 158 | + /** | |
| 159 | + * 创建细边框样式 | |
| 160 | + */ | |
| 161 | + public static CellStyle createBorderStyle(Workbook workbook) { | |
| 162 | + CellStyle style = workbook.createCellStyle(); | |
| 163 | + style.setBorderTop(BorderStyle.THIN); | |
| 164 | + style.setBorderRight(BorderStyle.THIN); | |
| 165 | + style.setBorderBottom(BorderStyle.THIN); | |
| 166 | + style.setBorderLeft(BorderStyle.THIN); | |
| 167 | + return style; | |
| 168 | + } | |
| 169 | + | |
| 170 | + /** | |
| 171 | + * 创建数据单元格样式:细边框、垂直居中,可指定锁定和填充色 | |
| 172 | + * | |
| 173 | + * @param workbook 工作簿 | |
| 174 | + * @param locked 是否锁定,配合 protectSheet 使用 | |
| 175 | + * @param fillColor 填充色,传 null 时不填充 | |
| 176 | + */ | |
| 177 | + public static CellStyle createDataStyle(Workbook workbook, boolean locked, IndexedColors fillColor) { | |
| 178 | + CellStyle style = createBorderStyle(workbook); | |
| 179 | + style.setLocked(locked); | |
| 180 | + if (fillColor != null) { | |
| 181 | + style.setFillForegroundColor(fillColor.getIndex()); | |
| 182 | + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); | |
| 183 | + } | |
| 184 | + style.setVerticalAlignment(VerticalAlignment.CENTER); | |
| 185 | + return style; | |
| 186 | + } | |
| 187 | + | |
| 188 | + /** | |
| 189 | + * 基于已有样式创建数字格式样式 | |
| 190 | + */ | |
| 191 | + public static CellStyle createNumberStyle(Workbook workbook, CellStyle base, String format) { | |
| 192 | + CellStyle style = workbook.createCellStyle(); | |
| 193 | + style.cloneStyleFrom(base); | |
| 194 | + style.setDataFormat(workbook.createDataFormat().getFormat(format)); | |
| 195 | + return style; | |
| 196 | + } | |
| 197 | + | |
| 198 | + /** | |
| 199 | + * 创建表头样式:细边框、指定填充色、加粗居中 | |
| 200 | + */ | |
| 201 | + public static CellStyle createHeaderStyle(Workbook workbook, IndexedColors fillColor) { | |
| 202 | + CellStyle style = createBorderStyle(workbook); | |
| 203 | + style.setFillForegroundColor(fillColor.getIndex()); | |
| 204 | + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); | |
| 205 | + style.setAlignment(HorizontalAlignment.CENTER); | |
| 206 | + style.setVerticalAlignment(VerticalAlignment.CENTER); | |
| 207 | + Font font = workbook.createFont(); | |
| 208 | + font.setBold(true); | |
| 209 | + style.setFont(font); | |
| 210 | + return style; | |
| 211 | + } | |
| 212 | + | |
| 213 | + /** | |
| 214 | + * 创建标题样式:大号加粗居中 | |
| 215 | + */ | |
| 216 | + public static CellStyle createTitleStyle(Workbook workbook) { | |
| 217 | + CellStyle style = workbook.createCellStyle(); | |
| 218 | + style.setAlignment(HorizontalAlignment.CENTER); | |
| 219 | + Font font = workbook.createFont(); | |
| 220 | + font.setBold(true); | |
| 221 | + font.setFontHeightInPoints((short) 20); | |
| 222 | + style.setFont(font); | |
| 223 | + return style; | |
| 224 | + } | |
| 225 | + | |
| 226 | +} | ... | ... |
yudao-framework/yudao-spring-boot-starter-excel/src/test/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtilsTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.excel.core.util; | |
| 2 | + | |
| 3 | +import cn.idev.excel.FastExcelFactory; | |
| 4 | +import cn.idev.excel.annotation.ExcelProperty; | |
| 5 | +import org.junit.jupiter.api.Test; | |
| 6 | +import org.springframework.mock.web.MockMultipartFile; | |
| 7 | + | |
| 8 | +import java.io.ByteArrayOutputStream; | |
| 9 | +import java.util.Arrays; | |
| 10 | +import java.util.List; | |
| 11 | + | |
| 12 | +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| 13 | + | |
| 14 | +/** | |
| 15 | + * {@link ExcelUtils} 的单元测试 | |
| 16 | + * | |
| 17 | + * @author 芋道源码 | |
| 18 | + */ | |
| 19 | +public class ExcelUtilsTest { | |
| 20 | + | |
| 21 | + @Test | |
| 22 | + public void testRead_maxRowCount() throws Exception { | |
| 23 | + // mock 数据 | |
| 24 | + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); | |
| 25 | + FastExcelFactory.write(outputStream, TestRow.class).sheet().doWrite(Arrays.asList( | |
| 26 | + new TestRow("员工一"), new TestRow("员工二"), new TestRow("员工三"))); | |
| 27 | + MockMultipartFile file = new MockMultipartFile( | |
| 28 | + "file", "employees.xlsx", null, outputStream.toByteArray()); | |
| 29 | + | |
| 30 | + // 调用 | |
| 31 | + List<TestRow> rows = ExcelUtils.read(file, TestRow.class, 2); | |
| 32 | + | |
| 33 | + // 断言 | |
| 34 | + assertEquals(2, rows.size()); | |
| 35 | + assertEquals("员工一", rows.get(0).getName()); | |
| 36 | + assertEquals("员工二", rows.get(1).getName()); | |
| 37 | + } | |
| 38 | + | |
| 39 | + public static class TestRow { | |
| 40 | + | |
| 41 | + @ExcelProperty("姓名") | |
| 42 | + private String name; | |
| 43 | + | |
| 44 | + public TestRow() { | |
| 45 | + } | |
| 46 | + | |
| 47 | + public TestRow(String name) { | |
| 48 | + this.name = name; | |
| 49 | + } | |
| 50 | + | |
| 51 | + public String getName() { | |
| 52 | + return name; | |
| 53 | + } | |
| 54 | + | |
| 55 | + public void setName(String name) { | |
| 56 | + this.name = name; | |
| 57 | + } | |
| 58 | + | |
| 59 | + } | |
| 60 | + | |
| 61 | +} | ... | ... |
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<T> extends MPJBaseMapper<T> { |
| 145 | 145 | } |
| 146 | 146 | |
| 147 | 147 | /** |
| 148 | + * 获得满足条件的第 1 条记录,并使用 FOR UPDATE 锁定。 | |
| 149 | + * | |
| 150 | + * 注意:需要在事务中调用,否则锁会立即释放。 | |
| 151 | + * | |
| 152 | + * @param queryWrapper 查询条件 | |
| 153 | + * @return 实体 | |
| 154 | + */ | |
| 155 | + default T selectFirstOneForUpdate(LambdaQueryWrapper<T> queryWrapper) { | |
| 156 | + return selectOne(queryWrapper.last("LIMIT 1 FOR UPDATE")); | |
| 157 | + } | |
| 158 | + | |
| 159 | + /** | |
| 148 | 160 | * 获取满足条件的第 1 条记录 |
| 149 | 161 | * |
| 150 | 162 | * 目的:解决并发场景下,插入多条记录后,使用 selectOne 会报错的问题 | ... | ... |
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; |
| 7 | 7 | import com.baomidou.mybatisplus.core.toolkit.support.SFunction; |
| 8 | 8 | import org.springframework.util.StringUtils; |
| 9 | 9 | |
| 10 | +import java.time.LocalDateTime; | |
| 10 | 11 | import java.util.Collection; |
| 11 | 12 | |
| 12 | 13 | /** |
| ... | ... | @@ -107,6 +108,24 @@ public class LambdaQueryWrapperX<T> extends LambdaQueryWrapper<T> { |
| 107 | 108 | return betweenIfPresent(column, val1, val2); |
| 108 | 109 | } |
| 109 | 110 | |
| 111 | + /** | |
| 112 | + * 拼接两个闭区间的重叠条件 | |
| 113 | + * | |
| 114 | + * <p>记录区间与查询区间重叠的条件为:记录开始值小于等于查询结束值, | |
| 115 | + * 且记录结束值大于等于查询开始值。不能使用单字段 {@code BETWEEN} 代替, | |
| 116 | + * 否则会漏掉完整覆盖查询区间的记录。</p> | |
| 117 | + * | |
| 118 | + * @param beginColumn 记录开始值字段 | |
| 119 | + * @param endColumn 记录结束值字段 | |
| 120 | + * @param times 查询闭区间 | |
| 121 | + * @return 查询条件 | |
| 122 | + */ | |
| 123 | + public LambdaQueryWrapperX<T> betweenIfPresent( | |
| 124 | + SFunction<T, ?> beginColumn, SFunction<T, ?> endColumn, LocalDateTime[] times) { | |
| 125 | + return leIfPresent(beginColumn, ArrayUtils.get(times, 1)) | |
| 126 | + .geIfPresent(endColumn, ArrayUtils.get(times, 0)); | |
| 127 | + } | |
| 128 | + | |
| 110 | 129 | // ========== 重写父类方法,方便链式调用 ========== |
| 111 | 130 | |
| 112 | 131 | @Override | ... | ... |
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<T> extends QueryWrapper<T> { |
| 102 | 102 | } |
| 103 | 103 | |
| 104 | 104 | public QueryWrapperX<T> betweenIfPresent(String column, Object[] values) { |
| 105 | - if (values != null && values.length != 0 && values[0] != null && values[1] != null) { | |
| 106 | - return (QueryWrapperX<T>) super.between(column, values[0], values[1]); | |
| 107 | - } | |
| 108 | - if (values != null && values.length != 0 && values[0] != null) { | |
| 109 | - return (QueryWrapperX<T>) ge(column, values[0]); | |
| 110 | - } | |
| 111 | - if (values != null && values.length != 0 && values[1] != null) { | |
| 112 | - return (QueryWrapperX<T>) le(column, values[1]); | |
| 113 | - } | |
| 114 | - return this; | |
| 105 | + Object val1 = values != null && values.length > 0 ? values[0] : null; | |
| 106 | + Object val2 = values != null && values.length > 1 ? values[1] : null; | |
| 107 | + return betweenIfPresent(column, val1, val2); | |
| 115 | 108 | } |
| 116 | 109 | |
| 117 | 110 | // ========== 重写父类方法,方便链式调用 ========== | ... | ... |
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; |
| 24 | 24 | import java.util.Collection; |
| 25 | 25 | import java.util.List; |
| 26 | 26 | import java.util.regex.Pattern; |
| 27 | +import java.util.stream.Collectors; | |
| 28 | +import java.util.stream.IntStream; | |
| 27 | 29 | |
| 28 | 30 | /** |
| 29 | 31 | * MyBatis 工具类 |
| ... | ... | @@ -179,6 +181,28 @@ public class MyBatisUtils { |
| 179 | 181 | return findInSet(columnName, paramIndex); |
| 180 | 182 | } |
| 181 | 183 | |
| 184 | + /** | |
| 185 | + * 跨数据库的 find_in_set 实现,适用于同一字段匹配多个参数的场景 | |
| 186 | + * | |
| 187 | + * 每个参数生成一个 find_in_set 条件,并使用 OR 连接。 | |
| 188 | + * | |
| 189 | + * @param columnName 字段名称 | |
| 190 | + * @param values 参数集合 | |
| 191 | + * @return sql | |
| 192 | + */ | |
| 193 | + public static String findInSet(String columnName, Collection<?> values) { | |
| 194 | + return findInSet(JdbcUtils.getDbType(), columnName, values); | |
| 195 | + } | |
| 196 | + | |
| 197 | + static String findInSet(DbType dbType, String columnName, Collection<?> values) { | |
| 198 | + if (CollUtil.isEmpty(values)) { | |
| 199 | + throw new IllegalArgumentException("Values cannot be empty"); | |
| 200 | + } | |
| 201 | + return IntStream.range(0, values.size()) | |
| 202 | + .mapToObj(index -> findInSet(dbType, columnName, index)) | |
| 203 | + .collect(Collectors.joining(" OR ")); | |
| 204 | + } | |
| 205 | + | |
| 182 | 206 | private static String findInSet(String columnName, int paramIndex) { |
| 183 | 207 | DbType dbType = JdbcUtils.getDbType(); |
| 184 | 208 | return findInSet(dbType, columnName, paramIndex); | ... | ... |
yudao-framework/yudao-spring-boot-starter-mybatis/src/test/java/cn/iocoder/yudao/framework/mybatis/core/query/LambdaQueryWrapperXTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.mybatis.core.query; | |
| 2 | + | |
| 3 | +import com.baomidou.mybatisplus.annotation.TableName; | |
| 4 | +import com.baomidou.mybatisplus.core.MybatisConfiguration; | |
| 5 | +import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; | |
| 6 | +import lombok.Data; | |
| 7 | +import org.apache.ibatis.builder.MapperBuilderAssistant; | |
| 8 | +import org.junit.jupiter.api.BeforeAll; | |
| 9 | +import org.junit.jupiter.api.Test; | |
| 10 | + | |
| 11 | +import java.time.LocalDateTime; | |
| 12 | + | |
| 13 | +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| 14 | +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| 15 | + | |
| 16 | +/** | |
| 17 | + * {@link LambdaQueryWrapperX} 的单元测试 | |
| 18 | + * | |
| 19 | + * @author 芋道源码 | |
| 20 | + */ | |
| 21 | +public class LambdaQueryWrapperXTest { | |
| 22 | + | |
| 23 | + @BeforeAll | |
| 24 | + public static void initTableInfo() { | |
| 25 | + TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""), TestDO.class); | |
| 26 | + } | |
| 27 | + | |
| 28 | + @Test | |
| 29 | + public void testBetweenIfPresent_overlap() { | |
| 30 | + // 调用,并断言:空范围不追加条件 | |
| 31 | + assertEquals("", new LambdaQueryWrapperX<TestDO>() | |
| 32 | + .betweenIfPresent(TestDO::getStartTime, TestDO::getEndTime, null) | |
| 33 | + .getSqlSegment()); | |
| 34 | + | |
| 35 | + // 调用,并断言:双边范围使用闭区间重叠条件 | |
| 36 | + LocalDateTime beginTime = LocalDateTime.of(2026, 8, 1, 0, 0); | |
| 37 | + LocalDateTime endTime = LocalDateTime.of(2026, 8, 31, 23, 59); | |
| 38 | + String sqlSegment = new LambdaQueryWrapperX<TestDO>() | |
| 39 | + .betweenIfPresent(TestDO::getStartTime, TestDO::getEndTime, | |
| 40 | + new LocalDateTime[]{beginTime, endTime}) | |
| 41 | + .getSqlSegment(); | |
| 42 | + assertTrue(sqlSegment.contains("start_time <=")); | |
| 43 | + assertTrue(sqlSegment.contains("end_time >=")); | |
| 44 | + | |
| 45 | + // 调用,并断言:单边范围只追加对应的重叠边界 | |
| 46 | + sqlSegment = new LambdaQueryWrapperX<TestDO>() | |
| 47 | + .betweenIfPresent(TestDO::getStartTime, TestDO::getEndTime, | |
| 48 | + new LocalDateTime[]{beginTime}) | |
| 49 | + .getSqlSegment(); | |
| 50 | + assertTrue(sqlSegment.contains("end_time >=")); | |
| 51 | + sqlSegment = new LambdaQueryWrapperX<TestDO>() | |
| 52 | + .betweenIfPresent(TestDO::getStartTime, TestDO::getEndTime, | |
| 53 | + new LocalDateTime[]{null, endTime}) | |
| 54 | + .getSqlSegment(); | |
| 55 | + assertTrue(sqlSegment.contains("start_time <=")); | |
| 56 | + } | |
| 57 | + | |
| 58 | + @Data | |
| 59 | + @TableName("test_data") | |
| 60 | + private static class TestDO { | |
| 61 | + | |
| 62 | + private LocalDateTime startTime; | |
| 63 | + private LocalDateTime endTime; | |
| 64 | + | |
| 65 | + } | |
| 66 | + | |
| 67 | +} | ... | ... |
yudao-framework/yudao-spring-boot-starter-mybatis/src/test/java/cn/iocoder/yudao/framework/mybatis/core/query/QueryWrapperXTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.mybatis.core.query; | |
| 2 | + | |
| 3 | +import org.junit.jupiter.api.Test; | |
| 4 | + | |
| 5 | +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| 6 | +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * {@link QueryWrapperX} 的单元测试 | |
| 10 | + * | |
| 11 | + * @author 芋道源码 | |
| 12 | + */ | |
| 13 | +public class QueryWrapperXTest { | |
| 14 | + | |
| 15 | + @Test | |
| 16 | + public void testBetweenIfPresent_array() { | |
| 17 | + // 调用,并断言:空范围不追加条件 | |
| 18 | + assertEquals("", new QueryWrapperX<>().betweenIfPresent("create_time", (Object[]) null).getSqlSegment()); | |
| 19 | + assertEquals("", new QueryWrapperX<>().betweenIfPresent("create_time", new Object[0]).getSqlSegment()); | |
| 20 | + | |
| 21 | + // 调用,并断言:单边范围安全降级为大于等于或小于等于 | |
| 22 | + assertTrue(new QueryWrapperX<>().betweenIfPresent("create_time", new Object[]{1}) | |
| 23 | + .getSqlSegment().contains("create_time >=")); | |
| 24 | + assertTrue(new QueryWrapperX<>().betweenIfPresent("create_time", new Object[]{null, 2}) | |
| 25 | + .getSqlSegment().contains("create_time <=")); | |
| 26 | + | |
| 27 | + // 调用,并断言:双边范围使用左右闭合的 BETWEEN | |
| 28 | + assertTrue(new QueryWrapperX<>().betweenIfPresent("create_time", new Object[]{1, 2}) | |
| 29 | + .getSqlSegment().contains("create_time BETWEEN")); | |
| 30 | + } | |
| 31 | + | |
| 32 | +} | ... | ... |
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; |
| 10 | 10 | import org.junit.jupiter.api.Test; |
| 11 | 11 | |
| 12 | 12 | import java.util.Arrays; |
| 13 | +import java.util.Collections; | |
| 13 | 14 | import java.util.List; |
| 14 | 15 | |
| 15 | 16 | import static org.junit.jupiter.api.Assertions.assertEquals; |
| ... | ... | @@ -129,6 +130,14 @@ public class MyBatisUtilsTest { |
| 129 | 130 | } |
| 130 | 131 | |
| 131 | 132 | @Test |
| 133 | + public void testFindInSet_multipleValues() { | |
| 134 | + assertEquals("FIND_IN_SET({0}, tag_ids) <> 0 OR FIND_IN_SET({1}, tag_ids) <> 0", | |
| 135 | + MyBatisUtils.findInSet(DbType.MYSQL, "tag_ids", Arrays.asList(1L, 2L))); | |
| 136 | + assertThrows(IllegalArgumentException.class, | |
| 137 | + () -> MyBatisUtils.findInSet(DbType.MYSQL, "tag_ids", Collections.emptyList())); | |
| 138 | + } | |
| 139 | + | |
| 140 | + @Test | |
| 132 | 141 | public void testFindInSet_applyBindsValue() { |
| 133 | 142 | // 准备参数 |
| 134 | 143 | QueryWrapper<Object> query = new QueryWrapper<>(); | ... | ... |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApi.java
| ... | ... | @@ -19,6 +19,13 @@ public interface BpmProcessInstanceApi { |
| 19 | 19 | */ |
| 20 | 20 | String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO reqDTO); |
| 21 | 21 | |
| 22 | - | |
| 22 | + /** | |
| 23 | + * 发起人取消运行中的流程实例(提供给内部) | |
| 24 | + * | |
| 25 | + * @param userId 发起人用户编号 | |
| 26 | + * @param processInstanceId 流程实例编号 | |
| 27 | + * @param reason 取消原因 | |
| 28 | + */ | |
| 29 | + void cancelProcessInstanceByStartUser(Long userId, String processInstanceId, String reason); | |
| 23 | 30 | |
| 24 | 31 | } | ... | ... |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.bpm.api.task; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; |
| 4 | +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCancelReqVO; | |
| 4 | 5 | import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; |
| 5 | 6 | import jakarta.annotation.Resource; |
| 6 | 7 | import jakarta.validation.Valid; |
| ... | ... | @@ -25,4 +26,10 @@ public class BpmProcessInstanceApiImpl implements BpmProcessInstanceApi { |
| 25 | 26 | return processInstanceService.createProcessInstance(userId, reqDTO); |
| 26 | 27 | } |
| 27 | 28 | |
| 29 | + @Override | |
| 30 | + public void cancelProcessInstanceByStartUser(Long userId, String processInstanceId, String reason) { | |
| 31 | + processInstanceService.cancelProcessInstanceByStartUser(userId, | |
| 32 | + new BpmProcessInstanceCancelReqVO().setId(processInstanceId).setReason(reason)); | |
| 33 | + } | |
| 34 | + | |
| 28 | 35 | } | ... | ... |
yudao-module-hrm/pom.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | +<project xmlns="http://maven.apache.org/POM/4.0.0" | |
| 3 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
| 4 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
| 5 | + <parent> | |
| 6 | + <groupId>cn.iocoder.boot</groupId> | |
| 7 | + <artifactId>yudao</artifactId> | |
| 8 | + <version>${revision}</version> | |
| 9 | + </parent> | |
| 10 | + <modelVersion>4.0.0</modelVersion> | |
| 11 | + <artifactId>yudao-module-hrm</artifactId> | |
| 12 | + <packaging>jar</packaging> | |
| 13 | + | |
| 14 | + <name>${project.artifactId}</name> | |
| 15 | + <description> | |
| 16 | + hrm 包下,人力资源管理(Human Resource Management) | |
| 17 | + 例如说:员工、招聘、考勤、薪资、社保、绩效等等 | |
| 18 | + </description> | |
| 19 | + | |
| 20 | + <dependencies> | |
| 21 | + <dependency> | |
| 22 | + <groupId>cn.iocoder.boot</groupId> | |
| 23 | + <artifactId>yudao-module-system</artifactId> | |
| 24 | + <version>${revision}</version> | |
| 25 | + </dependency> | |
| 26 | + <dependency> | |
| 27 | + <groupId>cn.iocoder.boot</groupId> | |
| 28 | + <artifactId>yudao-module-bpm</artifactId> | |
| 29 | + <version>${revision}</version> | |
| 30 | + </dependency> | |
| 31 | + | |
| 32 | + <!-- Web 相关 --> | |
| 33 | + <dependency> | |
| 34 | + <groupId>cn.iocoder.boot</groupId> | |
| 35 | + <artifactId>yudao-spring-boot-starter-web</artifactId> | |
| 36 | + </dependency> | |
| 37 | + | |
| 38 | + <dependency> | |
| 39 | + <groupId>cn.iocoder.boot</groupId> | |
| 40 | + <artifactId>yudao-spring-boot-starter-security</artifactId> | |
| 41 | + </dependency> | |
| 42 | + | |
| 43 | + <!-- DB 相关 --> | |
| 44 | + <dependency> | |
| 45 | + <groupId>cn.iocoder.boot</groupId> | |
| 46 | + <artifactId>yudao-spring-boot-starter-mybatis</artifactId> | |
| 47 | + </dependency> | |
| 48 | + | |
| 49 | + <!-- 工具类相关 --> | |
| 50 | + <dependency> | |
| 51 | + <groupId>cn.iocoder.boot</groupId> | |
| 52 | + <artifactId>yudao-spring-boot-starter-excel</artifactId> | |
| 53 | + </dependency> | |
| 54 | + | |
| 55 | + <!-- 定时任务相关 --> | |
| 56 | + <dependency> | |
| 57 | + <groupId>cn.iocoder.boot</groupId> | |
| 58 | + <artifactId>yudao-spring-boot-starter-job</artifactId> | |
| 59 | + </dependency> | |
| 60 | + | |
| 61 | + <!-- Test 测试相关 --> | |
| 62 | + <dependency> | |
| 63 | + <groupId>cn.iocoder.boot</groupId> | |
| 64 | + <artifactId>yudao-spring-boot-starter-test</artifactId> | |
| 65 | + <scope>test</scope> | |
| 66 | + </dependency> | |
| 67 | + </dependencies> | |
| 68 | + | |
| 69 | +</project> | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceClockController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; | |
| 5 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 6 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 7 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 8 | +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; | |
| 9 | +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; | |
| 10 | +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.TimeRange; | |
| 11 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 12 | +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; | |
| 13 | +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.clock.HrmAttendanceClockPageReqVO; | |
| 14 | +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.clock.HrmAttendanceClockRespVO; | |
| 15 | +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.clock.HrmAttendanceClockSaveReqVO; | |
| 16 | +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.clock.HrmAttendanceClockShiftReqVO; | |
| 17 | +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.clock.HrmAttendanceClockShiftRespVO; | |
| 18 | +import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.record.HrmAttendanceClockDO; | |
| 19 | +import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.config.HrmAttendanceGroupDO; | |
| 20 | +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; | |
| 21 | +import cn.iocoder.yudao.module.hrm.service.attendance.record.HrmAttendanceClockService; | |
| 22 | +import cn.iocoder.yudao.module.hrm.service.attendance.config.HrmAttendanceGroupService; | |
| 23 | +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService; | |
| 24 | +import cn.iocoder.yudao.module.system.api.dept.DeptApi; | |
| 25 | +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; | |
| 26 | +import cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum; | |
| 27 | +import io.swagger.v3.oas.annotations.Operation; | |
| 28 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 29 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 30 | +import jakarta.annotation.Resource; | |
| 31 | +import jakarta.servlet.http.HttpServletResponse; | |
| 32 | +import jakarta.validation.Valid; | |
| 33 | +import jakarta.validation.constraints.NotEmpty; | |
| 34 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 35 | +import org.springframework.validation.annotation.Validated; | |
| 36 | +import org.springframework.web.bind.annotation.DeleteMapping; | |
| 37 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 38 | +import org.springframework.web.bind.annotation.PostMapping; | |
| 39 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 40 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 41 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 42 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 43 | +import org.springframework.web.bind.annotation.RestController; | |
| 44 | + | |
| 45 | +import java.io.IOException; | |
| 46 | +import java.time.LocalDate; | |
| 47 | +import java.util.Collections; | |
| 48 | +import java.util.List; | |
| 49 | +import java.util.Map; | |
| 50 | +import java.util.Set; | |
| 51 | + | |
| 52 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 53 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | |
| 54 | + | |
| 55 | +@Tag(name = "管理后台 - HRM 打卡记录") | |
| 56 | +@RestController | |
| 57 | +@RequestMapping("/hrm/attendance/clock") | |
| 58 | +@Validated | |
| 59 | +public class HrmAttendanceClockController { | |
| 60 | + | |
| 61 | + @Resource | |
| 62 | + private HrmAttendanceClockService attendanceClockService; | |
| 63 | + @Resource | |
| 64 | + private HrmAttendanceGroupService attendanceGroupService; | |
| 65 | + @Resource | |
| 66 | + private HrmEmployeeService employeeService; | |
| 67 | + @Resource | |
| 68 | + private DeptApi deptApi; | |
| 69 | + | |
| 70 | + @PostMapping("/create") | |
| 71 | + @Operation(summary = "创建打卡记录") | |
| 72 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:clock:create')") | |
| 73 | + public CommonResult<Long> createAttendanceClock(@Valid @RequestBody HrmAttendanceClockSaveReqVO createReqVO) { | |
| 74 | + return success(attendanceClockService.createAttendanceClock(createReqVO)); | |
| 75 | + } | |
| 76 | + | |
| 77 | + @PutMapping("/update") | |
| 78 | + @Operation(summary = "修改打卡记录") | |
| 79 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:clock:update')") | |
| 80 | + public CommonResult<Boolean> updateAttendanceClock(@Valid @RequestBody HrmAttendanceClockSaveReqVO updateReqVO) { | |
| 81 | + attendanceClockService.updateAttendanceClock(updateReqVO); | |
| 82 | + return success(true); | |
| 83 | + } | |
| 84 | + | |
| 85 | + @DeleteMapping("/delete") | |
| 86 | + @Operation(summary = "删除打卡记录") | |
| 87 | + @Parameter(name = "id", description = "打卡记录编号", required = true, example = "1024") | |
| 88 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:clock:delete')") | |
| 89 | + public CommonResult<Boolean> deleteAttendanceClock(@RequestParam("id") Long id) { | |
| 90 | + attendanceClockService.deleteAttendanceClock(id); | |
| 91 | + return success(true); | |
| 92 | + } | |
| 93 | + | |
| 94 | + @DeleteMapping("/delete-list") | |
| 95 | + @Operation(summary = "批量删除打卡记录") | |
| 96 | + @Parameter(name = "ids", description = "打卡记录编号列表", required = true, example = "1024,1025") | |
| 97 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:clock:delete')") | |
| 98 | + public CommonResult<Boolean> deleteAttendanceClockList( | |
| 99 | + @RequestParam("ids") @NotEmpty(message = "打卡记录不能为空") List<Long> ids) { | |
| 100 | + attendanceClockService.deleteAttendanceClockList(ids); | |
| 101 | + return success(true); | |
| 102 | + } | |
| 103 | + | |
| 104 | + @GetMapping("/get") | |
| 105 | + @Operation(summary = "获得打卡记录") | |
| 106 | + @Parameter(name = "id", description = "打卡记录编号", required = true, example = "1024") | |
| 107 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:clock:query')") | |
| 108 | + public CommonResult<HrmAttendanceClockRespVO> getAttendanceClock(@RequestParam("id") Long id) { | |
| 109 | + HrmAttendanceClockDO attendanceClock = attendanceClockService.getAttendanceClock(id); | |
| 110 | + return success(attendanceClock == null ? null | |
| 111 | + : CollUtil.getFirst(buildAttendanceClockRespVOList(Collections.singletonList(attendanceClock)))); | |
| 112 | + } | |
| 113 | + | |
| 114 | + @GetMapping("/get-shift") | |
| 115 | + @Operation(summary = "获得员工实际班次和允许打卡时间") | |
| 116 | + @PreAuthorize("@ss.hasAnyPermissions('hrm:attendance:clock:create', 'hrm:attendance:clock:update')") | |
| 117 | + public CommonResult<HrmAttendanceClockShiftRespVO> getAttendanceClockShift( | |
| 118 | + @Valid HrmAttendanceClockShiftReqVO reqVO) { | |
| 119 | + HrmAttendanceGroupDO.Shift shift = attendanceGroupService.getEmployeeAttendanceShift( | |
| 120 | + reqVO.getEmployeeId(), reqVO.getAttendanceTime()); | |
| 121 | + return success(shift == null ? null : buildAttendanceClockShiftRespVO(reqVO, shift)); | |
| 122 | + } | |
| 123 | + | |
| 124 | + @GetMapping("/page") | |
| 125 | + @Operation(summary = "获得打卡记录分页") | |
| 126 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:clock:query')") | |
| 127 | + public CommonResult<PageResult<HrmAttendanceClockRespVO>> getAttendanceClockPage( | |
| 128 | + @Validated HrmAttendanceClockPageReqVO pageReqVO) { | |
| 129 | + PageResult<HrmAttendanceClockDO> pageResult = attendanceClockService.getAttendanceClockPage(pageReqVO); | |
| 130 | + return success(new PageResult<>(buildAttendanceClockRespVOList(pageResult.getList()), pageResult.getTotal())); | |
| 131 | + } | |
| 132 | + | |
| 133 | + @GetMapping("/export-excel") | |
| 134 | + @Operation(summary = "导出打卡记录") | |
| 135 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:clock:export')") | |
| 136 | + @ApiAccessLog(operateType = OperateTypeEnum.EXPORT) | |
| 137 | + public void exportAttendanceClock(@Validated HrmAttendanceClockPageReqVO exportReqVO, | |
| 138 | + HttpServletResponse response) throws IOException { | |
| 139 | + exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); | |
| 140 | + List<HrmAttendanceClockRespVO> list = buildAttendanceClockRespVOList( | |
| 141 | + attendanceClockService.getAttendanceClockList(exportReqVO)); | |
| 142 | + ExcelUtils.write(response, "打卡记录.xls", "数据", HrmAttendanceClockRespVO.class, list); | |
| 143 | + } | |
| 144 | + | |
| 145 | + // ==================== 拼接 VO ==================== | |
| 146 | + | |
| 147 | + private HrmAttendanceClockShiftRespVO buildAttendanceClockShiftRespVO( | |
| 148 | + HrmAttendanceClockShiftReqVO reqVO, HrmAttendanceGroupDO.Shift shift) { | |
| 149 | + if (shift.getStartTime() == null || shift.getEndTime() == null | |
| 150 | + || shift.getClockInStartTime() == null || shift.getClockInEndTime() == null | |
| 151 | + || shift.getClockOutStartTime() == null || shift.getClockOutEndTime() == null) { | |
| 152 | + return null; | |
| 153 | + } | |
| 154 | + LocalDate attendanceDate = reqVO.getAttendanceTime().toLocalDate(); | |
| 155 | + TimeRange shiftTimeRange = CollUtil.getFirst(LocalDateTimeUtils.buildDailyTimeRanges( | |
| 156 | + attendanceDate, attendanceDate, shift.getStartTime(), shift.getEndTime())); | |
| 157 | + TimeRange clockInTimeRange = LocalDateTimeUtils.findDailyTimeRange( | |
| 158 | + attendanceDate, shift.getClockInStartTime(), | |
| 159 | + shift.getClockInEndTime(), shiftTimeRange.getStartTime()); | |
| 160 | + TimeRange clockOutTimeRange = LocalDateTimeUtils.findDailyTimeRange( | |
| 161 | + attendanceDate, shift.getClockOutStartTime(), | |
| 162 | + shift.getClockOutEndTime(), shiftTimeRange.getEndTime()); | |
| 163 | + if (clockInTimeRange == null || clockOutTimeRange == null) { | |
| 164 | + return null; | |
| 165 | + } | |
| 166 | + return new HrmAttendanceClockShiftRespVO() | |
| 167 | + .setStartTime(shiftTimeRange.getStartTime()).setEndTime(shiftTimeRange.getEndTime()) | |
| 168 | + .setClockInStartTime(clockInTimeRange.getStartTime()).setClockInEndTime(clockInTimeRange.getEndTime()) | |
| 169 | + .setClockOutStartTime(clockOutTimeRange.getStartTime()).setClockOutEndTime(clockOutTimeRange.getEndTime()); | |
| 170 | + } | |
| 171 | + | |
| 172 | + private List<HrmAttendanceClockRespVO> buildAttendanceClockRespVOList(List<HrmAttendanceClockDO> clocks) { | |
| 173 | + if (CollUtil.isEmpty(clocks)) { | |
| 174 | + return Collections.emptyList(); | |
| 175 | + } | |
| 176 | + // 1. 获得员工和部门信息 | |
| 177 | + Map<Long, HrmEmployeeDO> employeeMap = employeeService.getEmployeeMap( | |
| 178 | + convertSet(clocks, HrmAttendanceClockDO::getEmployeeId)); | |
| 179 | + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(employeeMap.values(), HrmEmployeeDO::getDeptId)); | |
| 180 | + | |
| 181 | + // 2. 拼接响应 | |
| 182 | + return BeanUtils.toBean(clocks, HrmAttendanceClockRespVO.class, respVO -> { | |
| 183 | + MapUtils.findAndThen(employeeMap, respVO.getEmployeeId(), employee -> { | |
| 184 | + respVO.setEmployeeName(employee.getName()).setJobNumber(employee.getJobNumber()) | |
| 185 | + .setDeptId(employee.getDeptId()).setPostName(employee.getPostName()); | |
| 186 | + }); | |
| 187 | + MapUtils.findAndThen(deptMap, respVO.getDeptId(), dept -> respVO.setDeptName(dept.getName())); | |
| 188 | + }); | |
| 189 | + } | |
| 190 | + | |
| 191 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceGroupController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 5 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 6 | +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.group.HrmAttendanceGroupPageReqVO; | |
| 7 | +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.group.HrmAttendanceGroupRespVO; | |
| 8 | +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.group.HrmAttendanceGroupSaveReqVO; | |
| 9 | +import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.config.HrmAttendanceGroupDO; | |
| 10 | +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; | |
| 11 | +import cn.iocoder.yudao.module.hrm.service.attendance.config.HrmAttendanceGroupService; | |
| 12 | +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService; | |
| 13 | +import cn.iocoder.yudao.module.system.api.dept.DeptApi; | |
| 14 | +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; | |
| 15 | +import io.swagger.v3.oas.annotations.Operation; | |
| 16 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 17 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 18 | +import jakarta.annotation.Resource; | |
| 19 | +import jakarta.validation.Valid; | |
| 20 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 21 | +import org.springframework.validation.annotation.Validated; | |
| 22 | +import org.springframework.web.bind.annotation.DeleteMapping; | |
| 23 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 24 | +import org.springframework.web.bind.annotation.PostMapping; | |
| 25 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 26 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 27 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 28 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 29 | +import org.springframework.web.bind.annotation.RestController; | |
| 30 | + | |
| 31 | +import java.util.Collection; | |
| 32 | +import java.util.Collections; | |
| 33 | +import java.util.List; | |
| 34 | +import java.util.Map; | |
| 35 | +import java.util.Set; | |
| 36 | + | |
| 37 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 38 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 39 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; | |
| 40 | + | |
| 41 | +@Tag(name = "管理后台 - HRM 考勤组") | |
| 42 | +@RestController | |
| 43 | +@RequestMapping("/hrm/attendance/group") | |
| 44 | +@Validated | |
| 45 | +public class HrmAttendanceGroupController { | |
| 46 | + | |
| 47 | + @Resource | |
| 48 | + private HrmAttendanceGroupService attendanceGroupService; | |
| 49 | + @Resource | |
| 50 | + private HrmEmployeeService employeeService; | |
| 51 | + @Resource | |
| 52 | + private DeptApi deptApi; | |
| 53 | + | |
| 54 | + @PostMapping("/create") | |
| 55 | + @Operation(summary = "创建考勤组") | |
| 56 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:group:create')") | |
| 57 | + public CommonResult<Long> createAttendanceGroup(@Valid @RequestBody HrmAttendanceGroupSaveReqVO createReqVO) { | |
| 58 | + return success(attendanceGroupService.createAttendanceGroup(createReqVO)); | |
| 59 | + } | |
| 60 | + | |
| 61 | + @PutMapping("/update") | |
| 62 | + @Operation(summary = "修改考勤组") | |
| 63 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:group:update')") | |
| 64 | + public CommonResult<Boolean> updateAttendanceGroup(@Valid @RequestBody HrmAttendanceGroupSaveReqVO updateReqVO) { | |
| 65 | + attendanceGroupService.updateAttendanceGroup(updateReqVO); | |
| 66 | + return success(true); | |
| 67 | + } | |
| 68 | + | |
| 69 | + @DeleteMapping("/delete") | |
| 70 | + @Operation(summary = "删除考勤组") | |
| 71 | + @Parameter(name = "id", description = "考勤组编号", required = true, example = "1024") | |
| 72 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:group:delete')") | |
| 73 | + public CommonResult<Boolean> deleteAttendanceGroup(@RequestParam("id") Long id) { | |
| 74 | + attendanceGroupService.deleteAttendanceGroup(id); | |
| 75 | + return success(true); | |
| 76 | + } | |
| 77 | + | |
| 78 | + @GetMapping("/get") | |
| 79 | + @Operation(summary = "获得考勤组") | |
| 80 | + @Parameter(name = "id", description = "考勤组编号", required = true, example = "1024") | |
| 81 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:group:query')") | |
| 82 | + public CommonResult<HrmAttendanceGroupRespVO> getAttendanceGroup(@RequestParam("id") Long id) { | |
| 83 | + return success(buildAttendanceGroupRespVO(attendanceGroupService.getAttendanceGroup(id))); | |
| 84 | + } | |
| 85 | + | |
| 86 | + @GetMapping("/my") | |
| 87 | + @Operation(summary = "获得员工所在考勤组") | |
| 88 | + @Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024") | |
| 89 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:group:query')") | |
| 90 | + public CommonResult<HrmAttendanceGroupRespVO> getMyAttendanceGroup(@RequestParam("employeeId") Long employeeId) { | |
| 91 | + return success(buildAttendanceGroupRespVO(attendanceGroupService.getMyAttendanceGroup(employeeId))); | |
| 92 | + } | |
| 93 | + | |
| 94 | + @GetMapping("/page") | |
| 95 | + @Operation(summary = "获得考勤组分页") | |
| 96 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:group:query')") | |
| 97 | + public CommonResult<PageResult<HrmAttendanceGroupRespVO>> getAttendanceGroupPage( | |
| 98 | + @Validated HrmAttendanceGroupPageReqVO pageReqVO) { | |
| 99 | + PageResult<HrmAttendanceGroupDO> pageResult = attendanceGroupService.getAttendanceGroupPage(pageReqVO); | |
| 100 | + return success(new PageResult<>( | |
| 101 | + buildAttendanceGroupRespVOList(pageResult.getList()), pageResult.getTotal())); | |
| 102 | + } | |
| 103 | + | |
| 104 | + @GetMapping("/simple-list") | |
| 105 | + @Operation(summary = "获得考勤组精简列表") | |
| 106 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:group:query')") | |
| 107 | + public CommonResult<List<HrmAttendanceGroupRespVO>> getAttendanceGroupSimpleList() { | |
| 108 | + return success(convertList(attendanceGroupService.getAttendanceGroupList(), | |
| 109 | + group -> new HrmAttendanceGroupRespVO().setId(group.getId()).setName(group.getName()))); | |
| 110 | + } | |
| 111 | + | |
| 112 | + // ==================== 拼接 VO ==================== | |
| 113 | + | |
| 114 | + private HrmAttendanceGroupRespVO buildAttendanceGroupRespVO(HrmAttendanceGroupDO group) { | |
| 115 | + if (group == null) { | |
| 116 | + return null; | |
| 117 | + } | |
| 118 | + return buildAttendanceGroupRespVOList(Collections.singletonList(group)).get(0); | |
| 119 | + } | |
| 120 | + | |
| 121 | + private List<HrmAttendanceGroupRespVO> buildAttendanceGroupRespVOList(List<HrmAttendanceGroupDO> list) { | |
| 122 | + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap( | |
| 123 | + convertSetByFlatMap(list, HrmAttendanceGroupDO::getDeptIds, Collection::stream)); | |
| 124 | + Map<Long, HrmEmployeeDO> employeeMap = employeeService.getEmployeeMap( | |
| 125 | + convertSetByFlatMap(list, HrmAttendanceGroupDO::getEmployeeIds, Collection::stream)); | |
| 126 | + return convertList(list, group -> BeanUtils.toBean(group, HrmAttendanceGroupRespVO.class) | |
| 127 | + .setDeptNames(convertList(group.getDeptIds(), | |
| 128 | + deptId -> deptMap.containsKey(deptId) ? deptMap.get(deptId).getName() : null)) | |
| 129 | + .setEmployeeNames(convertList(group.getEmployeeIds(), | |
| 130 | + employeeId -> employeeMap.containsKey(employeeId) | |
| 131 | + ? employeeMap.get(employeeId).getName() : null))); | |
| 132 | + } | |
| 133 | + | |
| 134 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceHolidayController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 5 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 6 | +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.holiday.HrmAttendanceHolidayPageReqVO; | |
| 7 | +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.holiday.HrmAttendanceHolidayRespVO; | |
| 8 | +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.holiday.HrmAttendanceHolidaySaveReqVO; | |
| 9 | +import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.config.HrmAttendanceHolidayDO; | |
| 10 | +import cn.iocoder.yudao.module.hrm.service.attendance.config.HrmAttendanceHolidayService; | |
| 11 | +import io.swagger.v3.oas.annotations.Operation; | |
| 12 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 13 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 14 | +import jakarta.annotation.Resource; | |
| 15 | +import jakarta.validation.Valid; | |
| 16 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 17 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 18 | +import org.springframework.validation.annotation.Validated; | |
| 19 | +import org.springframework.web.bind.annotation.DeleteMapping; | |
| 20 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 21 | +import org.springframework.web.bind.annotation.PostMapping; | |
| 22 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 23 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 24 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 25 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 26 | +import org.springframework.web.bind.annotation.RestController; | |
| 27 | + | |
| 28 | +import java.time.LocalDateTime; | |
| 29 | + | |
| 30 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 31 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 32 | + | |
| 33 | +@Tag(name = "管理后台 - HRM 考勤节假日") | |
| 34 | +@RestController | |
| 35 | +@RequestMapping("/hrm/attendance/holiday") | |
| 36 | +@Validated | |
| 37 | +public class HrmAttendanceHolidayController { | |
| 38 | + | |
| 39 | + @Resource | |
| 40 | + private HrmAttendanceHolidayService attendanceHolidayService; | |
| 41 | + | |
| 42 | + @PostMapping("/create") | |
| 43 | + @Operation(summary = "创建考勤节假日") | |
| 44 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:holiday:create')") | |
| 45 | + public CommonResult<Long> createAttendanceHoliday(@Valid @RequestBody HrmAttendanceHolidaySaveReqVO createReqVO) { | |
| 46 | + return success(attendanceHolidayService.createAttendanceHoliday(createReqVO)); | |
| 47 | + } | |
| 48 | + | |
| 49 | + @PutMapping("/update") | |
| 50 | + @Operation(summary = "修改考勤节假日") | |
| 51 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:holiday:update')") | |
| 52 | + public CommonResult<Boolean> updateAttendanceHoliday(@Valid @RequestBody HrmAttendanceHolidaySaveReqVO updateReqVO) { | |
| 53 | + attendanceHolidayService.updateAttendanceHoliday(updateReqVO); | |
| 54 | + return success(true); | |
| 55 | + } | |
| 56 | + | |
| 57 | + @DeleteMapping("/delete") | |
| 58 | + @Operation(summary = "删除考勤节假日") | |
| 59 | + @Parameter(name = "id", description = "考勤节假日编号", required = true, example = "1024") | |
| 60 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:holiday:delete')") | |
| 61 | + public CommonResult<Boolean> deleteAttendanceHoliday(@RequestParam("id") Long id) { | |
| 62 | + attendanceHolidayService.deleteAttendanceHoliday(id); | |
| 63 | + return success(true); | |
| 64 | + } | |
| 65 | + | |
| 66 | + @GetMapping("/get") | |
| 67 | + @Operation(summary = "获得考勤节假日") | |
| 68 | + @Parameter(name = "id", description = "考勤节假日编号", required = true, example = "1024") | |
| 69 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:holiday:query')") | |
| 70 | + public CommonResult<HrmAttendanceHolidayRespVO> getAttendanceHoliday(@RequestParam("id") Long id) { | |
| 71 | + HrmAttendanceHolidayDO holiday = attendanceHolidayService.getAttendanceHoliday(id); | |
| 72 | + return success(BeanUtils.toBean(holiday, HrmAttendanceHolidayRespVO.class)); | |
| 73 | + } | |
| 74 | + | |
| 75 | + @GetMapping("/check") | |
| 76 | + @Operation(summary = "检查日期是否为考勤节假日") | |
| 77 | + @Parameter(name = "day", description = "日期", required = true) | |
| 78 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:holiday:query')") | |
| 79 | + public CommonResult<HrmAttendanceHolidayRespVO> checkAttendanceHoliday( | |
| 80 | + @RequestParam("day") | |
| 81 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) LocalDateTime day) { | |
| 82 | + HrmAttendanceHolidayDO holiday = attendanceHolidayService.checkAttendanceHoliday(day); | |
| 83 | + return success(BeanUtils.toBean(holiday, HrmAttendanceHolidayRespVO.class)); | |
| 84 | + } | |
| 85 | + | |
| 86 | + @GetMapping("/page") | |
| 87 | + @Operation(summary = "获得考勤节假日分页") | |
| 88 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:holiday:query')") | |
| 89 | + public CommonResult<PageResult<HrmAttendanceHolidayRespVO>> getAttendanceHolidayPage( | |
| 90 | + @Validated HrmAttendanceHolidayPageReqVO pageReqVO) { | |
| 91 | + PageResult<HrmAttendanceHolidayDO> pageResult = attendanceHolidayService.getAttendanceHolidayPage(pageReqVO); | |
| 92 | + return success(BeanUtils.toBean(pageResult, HrmAttendanceHolidayRespVO.class)); | |
| 93 | + } | |
| 94 | + | |
| 95 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceLeaveController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; | |
| 4 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 5 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 6 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 7 | +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; | |
| 8 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 9 | +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; | |
| 10 | +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.leave.HrmAttendanceLeavePageReqVO; | |
| 11 | +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.leave.HrmAttendanceLeaveRespVO; | |
| 12 | +import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.record.HrmAttendanceLeaveDO; | |
| 13 | +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; | |
| 14 | +import cn.iocoder.yudao.module.hrm.service.attendance.record.HrmAttendanceLeaveService; | |
| 15 | +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService; | |
| 16 | +import cn.iocoder.yudao.module.system.api.dept.DeptApi; | |
| 17 | +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; | |
| 18 | +import cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum; | |
| 19 | +import io.swagger.v3.oas.annotations.Operation; | |
| 20 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 21 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 22 | +import jakarta.annotation.Resource; | |
| 23 | +import jakarta.servlet.http.HttpServletResponse; | |
| 24 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 25 | +import org.springframework.validation.annotation.Validated; | |
| 26 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 27 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 28 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 29 | +import org.springframework.web.bind.annotation.RestController; | |
| 30 | + | |
| 31 | +import java.io.IOException; | |
| 32 | +import java.util.Collections; | |
| 33 | +import java.util.List; | |
| 34 | +import java.util.Map; | |
| 35 | + | |
| 36 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 37 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 38 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | |
| 39 | + | |
| 40 | +@Tag(name = "管理后台 - HRM 请假") | |
| 41 | +@RestController | |
| 42 | +@RequestMapping("/hrm/attendance/leave") | |
| 43 | +@Validated | |
| 44 | +public class HrmAttendanceLeaveController { | |
| 45 | + | |
| 46 | + @Resource | |
| 47 | + private HrmAttendanceLeaveService attendanceLeaveService; | |
| 48 | + @Resource | |
| 49 | + private HrmEmployeeService employeeService; | |
| 50 | + | |
| 51 | + @Resource | |
| 52 | + private DeptApi deptApi; | |
| 53 | + | |
| 54 | + @GetMapping("/get") | |
| 55 | + @Operation(summary = "获得请假") | |
| 56 | + @Parameter(name = "id", description = "请假记录编号", required = true, example = "1024") | |
| 57 | + @PreAuthorize("@ss.hasAnyPermissions('hrm:attendance:leave:query', " | |
| 58 | + + "'bpm:process-instance:query', 'bpm:task:query')") | |
| 59 | + public CommonResult<HrmAttendanceLeaveRespVO> getAttendanceLeave(@RequestParam("id") Long id) { | |
| 60 | + HrmAttendanceLeaveDO list = attendanceLeaveService.getAttendanceLeave(id); | |
| 61 | + return success(buildAttendanceLeaveRespVO(list)); | |
| 62 | + } | |
| 63 | + | |
| 64 | + @GetMapping("/page") | |
| 65 | + @Operation(summary = "获得请假分页") | |
| 66 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:leave:query')") | |
| 67 | + public CommonResult<PageResult<HrmAttendanceLeaveRespVO>> getAttendanceLeavePage( | |
| 68 | + @Validated HrmAttendanceLeavePageReqVO pageReqVO) { | |
| 69 | + PageResult<HrmAttendanceLeaveDO> pageResult = attendanceLeaveService.getAttendanceLeavePage(pageReqVO); | |
| 70 | + return success(new PageResult<>(buildAttendanceLeaveRespVOList(pageResult.getList()), pageResult.getTotal())); | |
| 71 | + } | |
| 72 | + | |
| 73 | + @GetMapping("/export-excel") | |
| 74 | + @Operation(summary = "导出请假") | |
| 75 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:leave:export')") | |
| 76 | + @ApiAccessLog(operateType = OperateTypeEnum.EXPORT) | |
| 77 | + public void exportAttendanceLeave(@Validated HrmAttendanceLeavePageReqVO exportReqVO, | |
| 78 | + HttpServletResponse response) throws IOException { | |
| 79 | + exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); | |
| 80 | + List<HrmAttendanceLeaveRespVO> list = buildAttendanceLeaveRespVOList( | |
| 81 | + attendanceLeaveService.getAttendanceLeaveList(exportReqVO)); | |
| 82 | + ExcelUtils.write(response, "请假记录.xls", "数据", HrmAttendanceLeaveRespVO.class, list); | |
| 83 | + } | |
| 84 | + | |
| 85 | + // ==================== 拼接 VO ==================== | |
| 86 | + | |
| 87 | + private HrmAttendanceLeaveRespVO buildAttendanceLeaveRespVO(HrmAttendanceLeaveDO attendanceLeave) { | |
| 88 | + if (attendanceLeave == null) { | |
| 89 | + return null; | |
| 90 | + } | |
| 91 | + return buildAttendanceLeaveRespVOList(Collections.singletonList(attendanceLeave)).get(0); | |
| 92 | + } | |
| 93 | + | |
| 94 | + private List<HrmAttendanceLeaveRespVO> buildAttendanceLeaveRespVOList(List<HrmAttendanceLeaveDO> list) { | |
| 95 | + Map<Long, HrmEmployeeDO> employeeMap = employeeService.getEmployeeMap( | |
| 96 | + convertSet(list, HrmAttendanceLeaveDO::getEmployeeId)); | |
| 97 | + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(employeeMap.values(), HrmEmployeeDO::getDeptId)); | |
| 98 | + return convertList(list, attendanceLeave -> { | |
| 99 | + HrmAttendanceLeaveRespVO respVO = BeanUtils.toBean(attendanceLeave, HrmAttendanceLeaveRespVO.class); | |
| 100 | + HrmEmployeeDO employee = employeeMap.get(attendanceLeave.getEmployeeId()); | |
| 101 | + if (employee == null) { | |
| 102 | + return respVO; | |
| 103 | + } | |
| 104 | + respVO.setEmployeeName(employee.getName()).setJobNumber(employee.getJobNumber()) | |
| 105 | + .setDeptId(employee.getDeptId()).setPostName(employee.getPostName()); | |
| 106 | + MapUtils.findAndThen(deptMap, employee.getDeptId(), dept -> respVO.setDeptName(dept.getName())); | |
| 107 | + return respVO; | |
| 108 | + }); | |
| 109 | + } | |
| 110 | + | |
| 111 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceStatisticsController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.hutool.core.map.MapUtil; | |
| 5 | +import cn.hutool.core.util.StrUtil; | |
| 6 | +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; | |
| 7 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 8 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 9 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 10 | +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; | |
| 11 | +import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils; | |
| 12 | +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; | |
| 13 | +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.clock.HrmAttendanceClockRespVO; | |
| 14 | +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics.HrmAttendanceDailyDetailReqVO; | |
| 15 | +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics.HrmAttendanceDailyDetailRespVO; | |
| 16 | +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics.HrmAttendanceDailyOverviewRespVO; | |
| 17 | +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics.HrmAttendanceMonthDailyOverviewRespVO; | |
| 18 | +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics.HrmAttendanceMonthDetailRespVO; | |
| 19 | +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics.HrmAttendanceMonthRecordPageReqVO; | |
| 20 | +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics.HrmAttendanceMonthRecordRespVO; | |
| 21 | +import cn.iocoder.yudao.module.hrm.enums.DictTypeConstants; | |
| 22 | +import cn.iocoder.yudao.module.hrm.service.attendance.statistics.HrmAttendanceStatisticsService; | |
| 23 | +import cn.iocoder.yudao.module.system.api.dept.DeptApi; | |
| 24 | +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; | |
| 25 | +import cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum; | |
| 26 | +import io.swagger.v3.oas.annotations.Operation; | |
| 27 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 28 | +import io.swagger.v3.oas.annotations.Parameters; | |
| 29 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 30 | +import jakarta.annotation.Resource; | |
| 31 | +import jakarta.servlet.http.HttpServletResponse; | |
| 32 | +import jakarta.validation.Valid; | |
| 33 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 34 | +import org.springframework.validation.annotation.Validated; | |
| 35 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 36 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 37 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 38 | +import org.springframework.web.bind.annotation.RestController; | |
| 39 | + | |
| 40 | +import java.io.IOException; | |
| 41 | +import java.time.YearMonth; | |
| 42 | +import java.util.ArrayList; | |
| 43 | +import java.util.Arrays; | |
| 44 | +import java.util.Collections; | |
| 45 | +import java.util.Comparator; | |
| 46 | +import java.util.List; | |
| 47 | +import java.util.Map; | |
| 48 | +import java.util.stream.Collectors; | |
| 49 | + | |
| 50 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 51 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 52 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | |
| 53 | + | |
| 54 | +@Tag(name = "管理后台 - HRM 考勤统计") | |
| 55 | +@RestController | |
| 56 | +@RequestMapping("/hrm/attendance/statistics") | |
| 57 | +@Validated | |
| 58 | +public class HrmAttendanceStatisticsController { | |
| 59 | + | |
| 60 | + @Resource | |
| 61 | + private HrmAttendanceStatisticsService attendanceStatisticsService; | |
| 62 | + | |
| 63 | + @Resource | |
| 64 | + private DeptApi deptApi; | |
| 65 | + | |
| 66 | + @GetMapping("/daily-detail") | |
| 67 | + @Operation(summary = "查询员工每日打卡明细") | |
| 68 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:clock:query')") | |
| 69 | + public CommonResult<HrmAttendanceDailyDetailRespVO> getAttendanceDailyDetail( | |
| 70 | + @Valid HrmAttendanceDailyDetailReqVO reqVO) { | |
| 71 | + return success(attendanceStatisticsService.getAttendanceDailyDetail(reqVO)); | |
| 72 | + } | |
| 73 | + | |
| 74 | + @GetMapping("/month-record-page") | |
| 75 | + @Operation(summary = "查询员工月度考勤汇总") | |
| 76 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:statistics:query')") | |
| 77 | + public CommonResult<PageResult<HrmAttendanceMonthRecordRespVO>> getAttendanceMonthRecordPage( | |
| 78 | + @Valid HrmAttendanceMonthRecordPageReqVO reqVO) { | |
| 79 | + PageResult<HrmAttendanceMonthRecordRespVO> pageResult = | |
| 80 | + attendanceStatisticsService.getAttendanceMonthRecordPage(reqVO); | |
| 81 | + fillAttendanceMonthRecordDeptName(pageResult.getList()); | |
| 82 | + return success(pageResult); | |
| 83 | + } | |
| 84 | + | |
| 85 | + @GetMapping("/month-daily-page") | |
| 86 | + @Operation(summary = "查询员工月度打卡概况") | |
| 87 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:clock:query')") | |
| 88 | + public CommonResult<PageResult<HrmAttendanceMonthDailyOverviewRespVO>> getAttendanceMonthDailyOverviewPage( | |
| 89 | + @Valid HrmAttendanceMonthRecordPageReqVO reqVO) { | |
| 90 | + PageResult<HrmAttendanceMonthDailyOverviewRespVO> pageResult = | |
| 91 | + attendanceStatisticsService.getAttendanceMonthDailyOverviewPage(reqVO); | |
| 92 | + fillAttendanceMonthDailyOverview(pageResult.getList()); | |
| 93 | + return success(pageResult); | |
| 94 | + } | |
| 95 | + | |
| 96 | + @GetMapping("/month-record-export-excel") | |
| 97 | + @Operation(summary = "导出员工月度考勤汇总") | |
| 98 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:statistics:export')") | |
| 99 | + @ApiAccessLog(operateType = OperateTypeEnum.EXPORT) | |
| 100 | + public void exportAttendanceMonthRecord(@Validated HrmAttendanceMonthRecordPageReqVO exportReqVO, | |
| 101 | + HttpServletResponse response) throws IOException { | |
| 102 | + exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); | |
| 103 | + List<HrmAttendanceMonthRecordRespVO> list = | |
| 104 | + attendanceStatisticsService.getAttendanceMonthRecordList(exportReqVO); | |
| 105 | + fillAttendanceMonthRecordDeptName(list); | |
| 106 | + ExcelUtils.write(response, "员工月度考勤汇总.xls", "数据", HrmAttendanceMonthRecordRespVO.class, list); | |
| 107 | + } | |
| 108 | + | |
| 109 | + @GetMapping("/month-daily-export-excel") | |
| 110 | + @Operation(summary = "导出员工月度打卡概况") | |
| 111 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:clock:export')") | |
| 112 | + @ApiAccessLog(operateType = OperateTypeEnum.EXPORT) | |
| 113 | + public void exportAttendanceMonthDailyOverview(@Validated HrmAttendanceMonthRecordPageReqVO exportReqVO, | |
| 114 | + HttpServletResponse response) throws IOException { | |
| 115 | + exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); | |
| 116 | + List<HrmAttendanceMonthDailyOverviewRespVO> list = | |
| 117 | + attendanceStatisticsService.getAttendanceMonthDailyOverviewList(exportReqVO); | |
| 118 | + fillAttendanceMonthDailyOverview(list); | |
| 119 | + writeAttendanceMonthDailyOverview(response, | |
| 120 | + YearMonth.of(exportReqVO.getYear(), exportReqVO.getMonth()), list); | |
| 121 | + } | |
| 122 | + | |
| 123 | + @GetMapping("/month-detail") | |
| 124 | + @Operation(summary = "查询员工月度考勤详情") | |
| 125 | + @Parameters({ | |
| 126 | + @Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024"), | |
| 127 | + @Parameter(name = "year", description = "年份", required = true, example = "2026"), | |
| 128 | + @Parameter(name = "month", description = "月份", required = true, example = "8") | |
| 129 | + }) | |
| 130 | + @PreAuthorize("@ss.hasPermission('hrm:attendance:statistics:query')") | |
| 131 | + public CommonResult<HrmAttendanceMonthDetailRespVO> getAttendanceMonthDetail( | |
| 132 | + @RequestParam("employeeId") Long employeeId, | |
| 133 | + @RequestParam("year") Integer year, | |
| 134 | + @RequestParam("month") Integer month) { | |
| 135 | + HrmAttendanceMonthDetailRespVO detail = | |
| 136 | + attendanceStatisticsService.getAttendanceMonthDetail(employeeId, year, month); | |
| 137 | + fillAttendanceMonthRecordDeptName(Collections.singletonList(detail.getSummary())); | |
| 138 | + return success(detail); | |
| 139 | + } | |
| 140 | + | |
| 141 | + private void writeAttendanceMonthDailyOverview(HttpServletResponse response, YearMonth month, | |
| 142 | + List<HrmAttendanceMonthDailyOverviewRespVO> list) | |
| 143 | + throws IOException { | |
| 144 | + List<List<String>> head = convertList( | |
| 145 | + Arrays.asList("员工编号", "员工姓名", "工号", "部门", "岗位"), Collections::singletonList); | |
| 146 | + for (int day = 1; day <= month.lengthOfMonth(); day++) { | |
| 147 | + head.add(Collections.singletonList(month.atDay(day).toString())); | |
| 148 | + } | |
| 149 | + ExcelUtils.write(response, "员工月度打卡概况.xls", "打卡概况", head, | |
| 150 | + buildAttendanceMonthDailyOverviewData(month, list)); | |
| 151 | + } | |
| 152 | + | |
| 153 | + private List<List<Object>> buildAttendanceMonthDailyOverviewData( | |
| 154 | + YearMonth month, List<HrmAttendanceMonthDailyOverviewRespVO> list) { | |
| 155 | + List<List<Object>> result = new ArrayList<>(list.size()); | |
| 156 | + for (HrmAttendanceMonthDailyOverviewRespVO row : list) { | |
| 157 | + List<Object> data = new ArrayList<>(Arrays.asList(row.getEmployeeId(), row.getEmployeeName(), | |
| 158 | + row.getJobNumber(), row.getDeptName(), row.getPostName())); | |
| 159 | + for (int day = 1; day <= month.lengthOfMonth(); day++) { | |
| 160 | + data.add(formatDailyClockOverview(row.getDailyClockMap().get(month.atDay(day)))); | |
| 161 | + } | |
| 162 | + result.add(data); | |
| 163 | + } | |
| 164 | + return result; | |
| 165 | + } | |
| 166 | + | |
| 167 | + private List<HrmAttendanceDailyOverviewRespVO.OverviewItem> buildDailyClockOverviewList( | |
| 168 | + HrmAttendanceDailyOverviewRespVO dailyOverview) { | |
| 169 | + if (CollUtil.isEmpty(dailyOverview.getClocks())) { | |
| 170 | + return Collections.singletonList(new HrmAttendanceDailyOverviewRespVO.OverviewItem() | |
| 171 | + .setText(StrUtil.emptyIfNull(dailyOverview.getAttendanceResult()))); | |
| 172 | + } | |
| 173 | + // 按打卡时间升序展示每日打卡概况 | |
| 174 | + dailyOverview.getClocks().sort(Comparator.comparing(HrmAttendanceClockRespVO::getClockTime)); | |
| 175 | + return convertList(dailyOverview.getClocks(), | |
| 176 | + clock -> new HrmAttendanceDailyOverviewRespVO.OverviewItem() | |
| 177 | + .setType(StrUtil.removeSuffix(StrUtil.emptyIfNull(DictFrameworkUtils.parseDictDataLabel( | |
| 178 | + DictTypeConstants.HRM_ATTENDANCE_CLOCK_TYPE, clock.getType())), "打卡")) | |
| 179 | + .setTime(clock.getClockTime() == null ? "-" : clock.getClockTime().toLocalTime().toString()) | |
| 180 | + .setStatus(StrUtil.emptyIfNull(DictFrameworkUtils.parseDictDataLabel( | |
| 181 | + DictTypeConstants.HRM_ATTENDANCE_CLOCK_STATUS, clock.getStatus())))); | |
| 182 | + } | |
| 183 | + | |
| 184 | + private String formatDailyClockOverview(HrmAttendanceDailyOverviewRespVO dailyOverview) { | |
| 185 | + if (dailyOverview == null) { | |
| 186 | + return ""; | |
| 187 | + } | |
| 188 | + return dailyOverview.getOverviews().stream() | |
| 189 | + .map(item -> StrUtil.isNotEmpty(item.getText()) ? item.getText() | |
| 190 | + : String.format("%s %s %s", item.getType(), item.getTime(), item.getStatus())) | |
| 191 | + .collect(Collectors.joining(";")); | |
| 192 | + } | |
| 193 | + | |
| 194 | + // ==================== 拼接 VO ==================== | |
| 195 | + | |
| 196 | + private void fillAttendanceMonthDailyOverview(List<HrmAttendanceMonthDailyOverviewRespVO> list) { | |
| 197 | + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap( | |
| 198 | + convertSet(list, HrmAttendanceMonthDailyOverviewRespVO::getDeptId)); | |
| 199 | + list.forEach(vo -> { | |
| 200 | + MapUtils.findAndThen(deptMap, vo.getDeptId(), dept -> vo.setDeptName(dept.getName())); | |
| 201 | + MapUtil.emptyIfNull(vo.getDailyClockMap()).values().forEach( | |
| 202 | + overview -> overview.setOverviews(buildDailyClockOverviewList(overview))); | |
| 203 | + }); | |
| 204 | + } | |
| 205 | + | |
| 206 | + private void fillAttendanceMonthRecordDeptName(List<HrmAttendanceMonthRecordRespVO> list) { | |
| 207 | + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap( | |
| 208 | + convertSet(list, HrmAttendanceMonthRecordRespVO::getDeptId)); | |
| 209 | + list.forEach(vo -> MapUtils.findAndThen(deptMap, vo.getDeptId(), dept -> vo.setDeptName(dept.getName()))); | |
| 210 | + } | |
| 211 | + | |
| 212 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/clock/HrmAttendanceClockPageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.clock; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 4 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 5 | +import cn.iocoder.yudao.module.hrm.enums.attendance.record.HrmAttendanceClockSourceEnum; | |
| 6 | +import cn.iocoder.yudao.module.hrm.enums.attendance.record.HrmAttendanceClockStatusEnum; | |
| 7 | +import cn.iocoder.yudao.module.hrm.enums.attendance.record.HrmAttendanceClockTypeEnum; | |
| 8 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 9 | +import lombok.Data; | |
| 10 | +import lombok.EqualsAndHashCode; | |
| 11 | +import lombok.ToString; | |
| 12 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 13 | + | |
| 14 | +import java.time.LocalDateTime; | |
| 15 | +import java.util.Collection; | |
| 16 | +import java.util.List; | |
| 17 | + | |
| 18 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 19 | + | |
| 20 | +@Schema(description = "管理后台 - HRM 打卡记录分页 Request VO") | |
| 21 | +@Data | |
| 22 | +@EqualsAndHashCode(callSuper = true) | |
| 23 | +@ToString(callSuper = true) | |
| 24 | +public class HrmAttendanceClockPageReqVO extends PageParam { | |
| 25 | + | |
| 26 | + @Schema(description = "员工姓名或工号,模糊匹配", example = "张三") | |
| 27 | + private String search; | |
| 28 | + | |
| 29 | + @Schema(description = "员工编号", example = "1024") | |
| 30 | + private Long employeeId; | |
| 31 | + | |
| 32 | + @Schema(description = "部门编号数组", example = "100,101") | |
| 33 | + private List<Long> deptIds; | |
| 34 | + | |
| 35 | + @Schema(hidden = true) | |
| 36 | + private Collection<Long> employeeIds; | |
| 37 | + | |
| 38 | + @Schema(description = "考勤打卡类型", example = "1") | |
| 39 | + @InEnum(value = HrmAttendanceClockTypeEnum.class, message = "打卡类型必须是 {value}") | |
| 40 | + private Integer type; | |
| 41 | + | |
| 42 | + @Schema(description = "考勤打卡状态", example = "1") | |
| 43 | + @InEnum(value = HrmAttendanceClockStatusEnum.class, message = "打卡状态必须是 {value}") | |
| 44 | + private Integer status; | |
| 45 | + | |
| 46 | + @Schema(description = "来源类型数组", example = "[1, 2]") | |
| 47 | + @InEnum(value = HrmAttendanceClockSourceEnum.class, message = "打卡来源必须是 {value}") | |
| 48 | + private List<Integer> sourceTypes; | |
| 49 | + | |
| 50 | + @Schema(description = "打卡地点,模糊匹配", example = "文三路") | |
| 51 | + private String address; | |
| 52 | + | |
| 53 | + @Schema(description = "上班日期范围") | |
| 54 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 55 | + private LocalDateTime[] attendanceTime; | |
| 56 | + | |
| 57 | + @Schema(description = "实际打卡时间范围") | |
| 58 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 59 | + private LocalDateTime[] clockTime; | |
| 60 | + | |
| 61 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/clock/HrmAttendanceClockRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.clock; | |
| 2 | + | |
| 3 | +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; | |
| 4 | +import cn.idev.excel.annotation.ExcelProperty; | |
| 5 | +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; | |
| 6 | +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; | |
| 7 | +import cn.iocoder.yudao.module.hrm.enums.DictTypeConstants; | |
| 8 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 9 | +import lombok.Data; | |
| 10 | + | |
| 11 | +import java.math.BigDecimal; | |
| 12 | +import java.time.LocalDateTime; | |
| 13 | + | |
| 14 | +@Schema(description = "管理后台 - HRM 打卡记录 Response VO") | |
| 15 | +@Data | |
| 16 | +@ExcelIgnoreUnannotated | |
| 17 | +public class HrmAttendanceClockRespVO { | |
| 18 | + | |
| 19 | + @Schema(description = "打卡记录编号", example = "1024") | |
| 20 | + @ExcelProperty(value = "记录编号", index = 0) | |
| 21 | + private Long id; | |
| 22 | + | |
| 23 | + @Schema(description = "打卡员工编号", example = "1001") | |
| 24 | + @ExcelProperty(value = "员工编号", index = 1) | |
| 25 | + private Long employeeId; | |
| 26 | + | |
| 27 | + @Schema(description = "员工姓名", example = "芋道") | |
| 28 | + @ExcelProperty(value = "员工姓名", index = 2) | |
| 29 | + private String employeeName; | |
| 30 | + | |
| 31 | + @Schema(description = "工号", example = "HRM001") | |
| 32 | + @ExcelProperty(value = "工号", index = 3) | |
| 33 | + private String jobNumber; | |
| 34 | + | |
| 35 | + @Schema(description = "部门编号", example = "100") | |
| 36 | + private Long deptId; | |
| 37 | + | |
| 38 | + @Schema(description = "部门名称", example = "研发部") | |
| 39 | + @ExcelProperty(value = "部门", index = 4) | |
| 40 | + private String deptName; | |
| 41 | + | |
| 42 | + @Schema(description = "职位名称", example = "Java 工程师") | |
| 43 | + @ExcelProperty(value = "岗位", index = 5) | |
| 44 | + private String postName; | |
| 45 | + | |
| 46 | + @Schema(description = "打卡时间") | |
| 47 | + @ExcelProperty(value = "打卡时间", index = 8) | |
| 48 | + private LocalDateTime clockTime; | |
| 49 | + | |
| 50 | + @Schema(description = "打卡类型", example = "1") | |
| 51 | + @ExcelProperty(value = "打卡类型", index = 6, converter = DictConvert.class) | |
| 52 | + @DictFormat(DictTypeConstants.HRM_ATTENDANCE_CLOCK_TYPE) | |
| 53 | + private Integer type; | |
| 54 | + | |
| 55 | + @Schema(description = "应打卡时间") | |
| 56 | + @ExcelProperty(value = "应打卡时间", index = 7) | |
| 57 | + private LocalDateTime attendanceTime; | |
| 58 | + | |
| 59 | + @Schema(description = "打卡来源") | |
| 60 | + @ExcelProperty(value = "来源", index = 10, converter = DictConvert.class) | |
| 61 | + @DictFormat(DictTypeConstants.HRM_ATTENDANCE_CLOCK_SOURCE) | |
| 62 | + private Integer sourceType; | |
| 63 | + | |
| 64 | + @Schema(description = "打卡状态") | |
| 65 | + @ExcelProperty(value = "打卡状态", index = 9, converter = DictConvert.class) | |
| 66 | + @DictFormat(DictTypeConstants.HRM_ATTENDANCE_CLOCK_STATUS) | |
| 67 | + private Integer status; | |
| 68 | + | |
| 69 | + @Schema(description = "打卡阶段") | |
| 70 | + private Integer stage; | |
| 71 | + | |
| 72 | + @Schema(description = "打卡地址", example = "总部大楼") | |
| 73 | + @ExcelProperty(value = "地点", index = 11) | |
| 74 | + private String address; | |
| 75 | + | |
| 76 | + @Schema(description = "经度", example = "121.473701") | |
| 77 | + private BigDecimal longitude; | |
| 78 | + | |
| 79 | + @Schema(description = "纬度", example = "31.230416") | |
| 80 | + private BigDecimal latitude; | |
| 81 | + | |
| 82 | + @Schema(description = "WiFi 名称", example = "office_wifi") | |
| 83 | + private String ssid; | |
| 84 | + | |
| 85 | + @Schema(description = "WiFi MAC 地址", example = "00:11:22:33:44:55") | |
| 86 | + private String mac; | |
| 87 | + | |
| 88 | + @Schema(description = "备注", example = "手工补卡") | |
| 89 | + @ExcelProperty(value = "备注", index = 12) | |
| 90 | + private String remark; | |
| 91 | + | |
| 92 | + @Schema(description = "创建时间") | |
| 93 | + private LocalDateTime createTime; | |
| 94 | + | |
| 95 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/clock/HrmAttendanceClockSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.clock; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 4 | +import cn.iocoder.yudao.module.hrm.enums.attendance.record.HrmAttendanceClockTypeEnum; | |
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 6 | +import jakarta.validation.constraints.DecimalMax; | |
| 7 | +import jakarta.validation.constraints.DecimalMin; | |
| 8 | +import jakarta.validation.constraints.NotNull; | |
| 9 | +import jakarta.validation.constraints.Size; | |
| 10 | +import lombok.Data; | |
| 11 | + | |
| 12 | +import java.math.BigDecimal; | |
| 13 | +import java.time.LocalDateTime; | |
| 14 | + | |
| 15 | +@Schema(description = "管理后台 - HRM 打卡记录保存 Request VO") | |
| 16 | +@Data | |
| 17 | +public class HrmAttendanceClockSaveReqVO { | |
| 18 | + | |
| 19 | + @Schema(description = "打卡记录编号", example = "1024") | |
| 20 | + private Long id; | |
| 21 | + | |
| 22 | + @Schema(description = "打卡员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1001") | |
| 23 | + @NotNull(message = "打卡员工不能为空") | |
| 24 | + private Long employeeId; | |
| 25 | + | |
| 26 | + @Schema(description = "打卡时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 27 | + @NotNull(message = "打卡时间不能为空") | |
| 28 | + private LocalDateTime clockTime; | |
| 29 | + | |
| 30 | + @Schema(description = "打卡类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 31 | + @NotNull(message = "打卡类型不能为空") | |
| 32 | + @InEnum(value = HrmAttendanceClockTypeEnum.class, message = "打卡类型必须是 {value}") | |
| 33 | + private Integer type; | |
| 34 | + | |
| 35 | + @Schema(description = "应打卡时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 36 | + @NotNull(message = "应打卡时间不能为空") | |
| 37 | + private LocalDateTime attendanceTime; | |
| 38 | + | |
| 39 | + @Schema(description = "打卡地址", example = "总部大楼") | |
| 40 | + @Size(max = 255, message = "打卡地址不能超过 255 个字符") | |
| 41 | + private String address; | |
| 42 | + | |
| 43 | + @Schema(description = "经度", example = "121.473701") | |
| 44 | + @DecimalMin(value = "-180", message = "经度必须在 -180 到 180 之间") | |
| 45 | + @DecimalMax(value = "180", message = "经度必须在 -180 到 180 之间") | |
| 46 | + private BigDecimal longitude; | |
| 47 | + | |
| 48 | + @Schema(description = "纬度", example = "31.230416") | |
| 49 | + @DecimalMin(value = "-90", message = "纬度必须在 -90 到 90 之间") | |
| 50 | + @DecimalMax(value = "90", message = "纬度必须在 -90 到 90 之间") | |
| 51 | + private BigDecimal latitude; | |
| 52 | + | |
| 53 | + @Schema(description = "WiFi 名称", example = "office_wifi") | |
| 54 | + @Size(max = 50, message = "WiFi 名称不能超过 50 个字符") | |
| 55 | + private String ssid; | |
| 56 | + | |
| 57 | + @Schema(description = "WiFi MAC 地址", example = "00:11:22:33:44:55") | |
| 58 | + @Size(max = 50, message = "WiFi MAC 地址不能超过 50 个字符") | |
| 59 | + private String mac; | |
| 60 | + | |
| 61 | + @Schema(description = "备注", example = "手工补卡") | |
| 62 | + @Size(max = 255, message = "备注不能超过 255 个字符") | |
| 63 | + private String remark; | |
| 64 | + | |
| 65 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/clock/HrmAttendanceClockShiftReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.clock; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import jakarta.validation.constraints.NotNull; | |
| 5 | +import lombok.Data; | |
| 6 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 7 | + | |
| 8 | +import java.time.LocalDateTime; | |
| 9 | + | |
| 10 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 11 | + | |
| 12 | +@Schema(description = "管理后台 - HRM 员工实际班次 Request VO") | |
| 13 | +@Data | |
| 14 | +public class HrmAttendanceClockShiftReqVO { | |
| 15 | + | |
| 16 | + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 17 | + @NotNull(message = "员工不能为空") | |
| 18 | + private Long employeeId; | |
| 19 | + | |
| 20 | + @Schema(description = "考勤时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 21 | + @NotNull(message = "考勤时间不能为空") | |
| 22 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 23 | + private LocalDateTime attendanceTime; | |
| 24 | + | |
| 25 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/clock/HrmAttendanceClockShiftRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.clock; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.time.LocalDateTime; | |
| 7 | + | |
| 8 | +@Schema(description = "管理后台 - HRM 员工实际班次 Response VO") | |
| 9 | +@Data | |
| 10 | +public class HrmAttendanceClockShiftRespVO { | |
| 11 | + | |
| 12 | + @Schema(description = "上班时间") | |
| 13 | + private LocalDateTime startTime; | |
| 14 | + | |
| 15 | + @Schema(description = "下班时间") | |
| 16 | + private LocalDateTime endTime; | |
| 17 | + | |
| 18 | + @Schema(description = "上班打卡开始时间") | |
| 19 | + private LocalDateTime clockInStartTime; | |
| 20 | + | |
| 21 | + @Schema(description = "上班打卡结束时间") | |
| 22 | + private LocalDateTime clockInEndTime; | |
| 23 | + | |
| 24 | + @Schema(description = "下班打卡开始时间") | |
| 25 | + private LocalDateTime clockOutStartTime; | |
| 26 | + | |
| 27 | + @Schema(description = "下班打卡结束时间") | |
| 28 | + private LocalDateTime clockOutEndTime; | |
| 29 | + | |
| 30 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/group/HrmAttendanceGroupPageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.group; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import lombok.Data; | |
| 6 | +import lombok.EqualsAndHashCode; | |
| 7 | +import lombok.ToString; | |
| 8 | + | |
| 9 | +@Schema(description = "管理后台 - HRM 考勤组分页 Request VO") | |
| 10 | +@Data | |
| 11 | +@EqualsAndHashCode(callSuper = true) | |
| 12 | +@ToString(callSuper = true) | |
| 13 | +public class HrmAttendanceGroupPageReqVO extends PageParam { | |
| 14 | + | |
| 15 | + @Schema(description = "考勤组名称", example = "默认考勤组") | |
| 16 | + private String name; | |
| 17 | + | |
| 18 | + @Schema(description = "是否默认考勤组", example = "true") | |
| 19 | + private Boolean defaultStatus; | |
| 20 | + | |
| 21 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/group/HrmAttendanceGroupRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.group; | |
| 2 | + | |
| 3 | +import com.fasterxml.jackson.annotation.JsonFormat; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import lombok.Data; | |
| 6 | + | |
| 7 | +import java.math.BigDecimal; | |
| 8 | +import java.time.LocalDateTime; | |
| 9 | +import java.time.LocalTime; | |
| 10 | +import java.util.List; | |
| 11 | + | |
| 12 | +@Schema(description = "管理后台 - HRM 考勤组 Response VO") | |
| 13 | +@Data | |
| 14 | +public class HrmAttendanceGroupRespVO { | |
| 15 | + | |
| 16 | + @Schema(description = "考勤组编号", example = "1024") | |
| 17 | + private Long id; | |
| 18 | + | |
| 19 | + @Schema(description = "名称", example = "总部考勤组") | |
| 20 | + private String name; | |
| 21 | + | |
| 22 | + @Schema(description = "是否启用 WiFi 打卡") | |
| 23 | + private Boolean openWifiCard; | |
| 24 | + | |
| 25 | + @Schema(description = "是否启用定位打卡") | |
| 26 | + private Boolean openPointCard; | |
| 27 | + | |
| 28 | + @Schema(description = "是否法定节假日休息") | |
| 29 | + private Boolean rest; | |
| 30 | + | |
| 31 | + @Schema(description = "是否默认配置") | |
| 32 | + private Boolean defaultStatus; | |
| 33 | + | |
| 34 | + @Schema(description = "特殊日期设置数组") | |
| 35 | + private List<SpecialDate> specialDates; | |
| 36 | + | |
| 37 | + @Schema(description = "部门范围") | |
| 38 | + private List<Long> deptIds; | |
| 39 | + | |
| 40 | + @Schema(description = "适用部门名称") | |
| 41 | + private List<String> deptNames; | |
| 42 | + | |
| 43 | + @Schema(description = "员工范围") | |
| 44 | + private List<Long> employeeIds; | |
| 45 | + | |
| 46 | + @Schema(description = "适用员工名称") | |
| 47 | + private List<String> employeeNames; | |
| 48 | + | |
| 49 | + @Schema(description = "班次配置") | |
| 50 | + private List<Shift> shifts; | |
| 51 | + | |
| 52 | + @Schema(description = "打卡地点") | |
| 53 | + private List<Point> points; | |
| 54 | + | |
| 55 | + @Schema(description = "打卡 WiFi") | |
| 56 | + private List<Wifi> wifis; | |
| 57 | + | |
| 58 | + @Schema(description = "扣款规则") | |
| 59 | + private DeductRule deductRule; | |
| 60 | + | |
| 61 | + @Schema(description = "创建时间") | |
| 62 | + private LocalDateTime createTime; | |
| 63 | + | |
| 64 | + @Schema(description = "管理后台 - HRM 考勤组特殊日期 Response VO") | |
| 65 | + @Data | |
| 66 | + public static class SpecialDate { | |
| 67 | + | |
| 68 | + @Schema(description = "日期类型", example = "1") | |
| 69 | + private Integer type; | |
| 70 | + | |
| 71 | + @Schema(description = "特殊日期") | |
| 72 | + private LocalDateTime date; | |
| 73 | + } | |
| 74 | + | |
| 75 | + @Schema(description = "管理后台 - HRM 考勤组班次配置 Response VO") | |
| 76 | + @Data | |
| 77 | + public static class Shift { | |
| 78 | + | |
| 79 | + @Schema(description = "工作日数组", example = "[1, 2, 3, 4, 5]") | |
| 80 | + private List<Integer> weeks; | |
| 81 | + | |
| 82 | + @Schema(description = "上班时间", example = "09:00") | |
| 83 | + @JsonFormat(pattern = "HH:mm") | |
| 84 | + private LocalTime startTime; | |
| 85 | + | |
| 86 | + @Schema(description = "下班时间", example = "18:00") | |
| 87 | + @JsonFormat(pattern = "HH:mm") | |
| 88 | + private LocalTime endTime; | |
| 89 | + | |
| 90 | + @Schema(description = "上班打卡开始时间", example = "08:00") | |
| 91 | + @JsonFormat(pattern = "HH:mm") | |
| 92 | + private LocalTime clockInStartTime; | |
| 93 | + | |
| 94 | + @Schema(description = "上班打卡结束时间", example = "10:00") | |
| 95 | + @JsonFormat(pattern = "HH:mm") | |
| 96 | + private LocalTime clockInEndTime; | |
| 97 | + | |
| 98 | + @Schema(description = "下班打卡开始时间", example = "17:00") | |
| 99 | + @JsonFormat(pattern = "HH:mm") | |
| 100 | + private LocalTime clockOutStartTime; | |
| 101 | + | |
| 102 | + @Schema(description = "下班打卡结束时间", example = "20:00") | |
| 103 | + @JsonFormat(pattern = "HH:mm") | |
| 104 | + private LocalTime clockOutEndTime; | |
| 105 | + | |
| 106 | + @Schema(description = "休息开始时间", example = "12:00") | |
| 107 | + @JsonFormat(pattern = "HH:mm") | |
| 108 | + private LocalTime restStartTime; | |
| 109 | + | |
| 110 | + @Schema(description = "休息结束时间", example = "13:00") | |
| 111 | + @JsonFormat(pattern = "HH:mm") | |
| 112 | + private LocalTime restEndTime; | |
| 113 | + | |
| 114 | + @Schema(description = "休息时间是否不计入工作时长") | |
| 115 | + private Boolean excludeRestTime; | |
| 116 | + } | |
| 117 | + | |
| 118 | + @Schema(description = "管理后台 - HRM 考勤组打卡地点 Response VO") | |
| 119 | + @Data | |
| 120 | + public static class Point { | |
| 121 | + | |
| 122 | + @Schema(description = "地点名称", example = "总部办公区") | |
| 123 | + private String name; | |
| 124 | + | |
| 125 | + @Schema(description = "定位地址", example = "北京市海淀区中关村大街") | |
| 126 | + private String address; | |
| 127 | + | |
| 128 | + @Schema(description = "纬度", example = "39.983424") | |
| 129 | + private BigDecimal latitude; | |
| 130 | + | |
| 131 | + @Schema(description = "经度", example = "116.322987") | |
| 132 | + private BigDecimal longitude; | |
| 133 | + | |
| 134 | + @Schema(description = "有效打卡半径,单位:米", example = "300") | |
| 135 | + private Integer radius; | |
| 136 | + } | |
| 137 | + | |
| 138 | + @Schema(description = "管理后台 - HRM 考勤组 WiFi 配置 Response VO") | |
| 139 | + @Data | |
| 140 | + public static class Wifi { | |
| 141 | + | |
| 142 | + @Schema(description = "WiFi 名称", example = "Office-WiFi") | |
| 143 | + private String ssid; | |
| 144 | + | |
| 145 | + @Schema(description = "MAC 地址", example = "00:11:22:33:44:55") | |
| 146 | + private String mac; | |
| 147 | + } | |
| 148 | + | |
| 149 | + @Schema(description = "管理后台 - HRM 考勤组扣款规则 Response VO") | |
| 150 | + @Data | |
| 151 | + public static class DeductRule { | |
| 152 | + | |
| 153 | + @Schema(description = "迟到扣款方式", example = "1") | |
| 154 | + private Integer lateMethod; | |
| 155 | + | |
| 156 | + @Schema(description = "迟到扣款金额", example = "10.00") | |
| 157 | + private BigDecimal lateDeductMoney; | |
| 158 | + | |
| 159 | + @Schema(description = "早退扣款方式", example = "1") | |
| 160 | + private Integer earlyMethod; | |
| 161 | + | |
| 162 | + @Schema(description = "早退扣款金额", example = "10.00") | |
| 163 | + private BigDecimal earlyDeductMoney; | |
| 164 | + | |
| 165 | + @Schema(description = "旷工扣款方式", example = "1") | |
| 166 | + private Integer absenteeismMethod; | |
| 167 | + | |
| 168 | + @Schema(description = "旷工扣款金额", example = "100.00") | |
| 169 | + private BigDecimal absenteeismDeductMoney; | |
| 170 | + | |
| 171 | + @Schema(description = "缺卡扣款方式", example = "1") | |
| 172 | + private Integer misscardMethod; | |
| 173 | + | |
| 174 | + @Schema(description = "缺卡扣款金额", example = "20.00") | |
| 175 | + private BigDecimal misscardDeductMoney; | |
| 176 | + } | |
| 177 | + | |
| 178 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/group/HrmAttendanceGroupSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.group; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; | |
| 5 | +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.TimeRange; | |
| 6 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 7 | +import cn.iocoder.yudao.module.hrm.enums.attendance.config.HrmAttendanceAbsenteeismDeductMethodEnum; | |
| 8 | +import cn.iocoder.yudao.module.hrm.enums.attendance.config.HrmAttendanceHolidayTypeEnum; | |
| 9 | +import cn.iocoder.yudao.module.hrm.enums.attendance.config.HrmAttendanceLateEarlyDeductMethodEnum; | |
| 10 | +import cn.iocoder.yudao.module.hrm.enums.attendance.config.HrmAttendanceMisscardDeductMethodEnum; | |
| 11 | +import com.fasterxml.jackson.annotation.JsonFormat; | |
| 12 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
| 13 | +import com.mzt.logapi.starter.annotation.DiffLogField; | |
| 14 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 15 | +import jakarta.validation.Valid; | |
| 16 | +import jakarta.validation.constraints.AssertTrue; | |
| 17 | +import jakarta.validation.constraints.DecimalMax; | |
| 18 | +import jakarta.validation.constraints.DecimalMin; | |
| 19 | +import jakarta.validation.constraints.Digits; | |
| 20 | +import jakarta.validation.constraints.Max; | |
| 21 | +import jakarta.validation.constraints.Min; | |
| 22 | +import jakarta.validation.constraints.NotBlank; | |
| 23 | +import jakarta.validation.constraints.NotEmpty; | |
| 24 | +import jakarta.validation.constraints.NotNull; | |
| 25 | +import jakarta.validation.constraints.Pattern; | |
| 26 | +import jakarta.validation.constraints.Positive; | |
| 27 | +import jakarta.validation.constraints.Size; | |
| 28 | +import lombok.Data; | |
| 29 | + | |
| 30 | +import java.math.BigDecimal; | |
| 31 | +import java.time.LocalDate; | |
| 32 | +import java.time.LocalDateTime; | |
| 33 | +import java.time.LocalTime; | |
| 34 | +import java.util.Arrays; | |
| 35 | +import java.util.HashSet; | |
| 36 | +import java.util.List; | |
| 37 | +import java.util.Set; | |
| 38 | + | |
| 39 | +@Schema(description = "管理后台 - HRM 考勤组保存 Request VO") | |
| 40 | +@Data | |
| 41 | +public class HrmAttendanceGroupSaveReqVO { | |
| 42 | + | |
| 43 | + private static final Set<Integer> POINT_RADIUS_VALUES = new HashSet<>( | |
| 44 | + Arrays.asList(100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 2000, 3000)); | |
| 45 | + | |
| 46 | + @Schema(description = "考勤组编号", example = "1024") | |
| 47 | + private Long id; | |
| 48 | + | |
| 49 | + @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "总部考勤组") | |
| 50 | + @NotBlank(message = "考勤组名称不能为空") | |
| 51 | + @Size(max = 50, message = "考勤组名称不能超过 50 个字符") | |
| 52 | + @DiffLogField(name = "考勤组名称") | |
| 53 | + private String name; | |
| 54 | + | |
| 55 | + @Schema(description = "是否启用 WiFi 打卡", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") | |
| 56 | + @NotNull(message = "是否启用 WiFi 打卡不能为空") | |
| 57 | + @DiffLogField(name = "是否启用 WiFi 打卡") | |
| 58 | + private Boolean openWifiCard; | |
| 59 | + | |
| 60 | + @Schema(description = "是否启用定位打卡", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") | |
| 61 | + @NotNull(message = "是否启用定位打卡不能为空") | |
| 62 | + @DiffLogField(name = "是否启用定位打卡") | |
| 63 | + private Boolean openPointCard; | |
| 64 | + | |
| 65 | + @Schema(description = "是否法定节假日休息", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") | |
| 66 | + @NotNull(message = "是否法定节假日休息不能为空") | |
| 67 | + @DiffLogField(name = "是否法定节假日休息") | |
| 68 | + private Boolean rest; | |
| 69 | + | |
| 70 | + @Schema(description = "特殊日期设置数组", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 71 | + @Valid | |
| 72 | + @NotNull(message = "特殊日期设置不能为空") | |
| 73 | + @DiffLogField(name = "特殊日期设置") | |
| 74 | + private List<SpecialDate> specialDates; | |
| 75 | + | |
| 76 | + @Schema(description = "部门范围", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1024, 2048]") | |
| 77 | + @NotNull(message = "部门范围不能为空") | |
| 78 | + @DiffLogField(name = "适用部门") | |
| 79 | + private List<@NotNull(message = "部门编号不能为空") Long> deptIds; | |
| 80 | + | |
| 81 | + @Schema(description = "员工范围", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1024, 2048]") | |
| 82 | + @NotNull(message = "员工范围不能为空") | |
| 83 | + @DiffLogField(name = "适用员工") | |
| 84 | + private List<@NotNull(message = "员工编号不能为空") Long> employeeIds; | |
| 85 | + | |
| 86 | + @Schema(description = "班次配置", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 87 | + @Valid | |
| 88 | + @NotEmpty(message = "班次配置不能为空") | |
| 89 | + @DiffLogField(name = "班次配置") | |
| 90 | + private List<Shift> shifts; | |
| 91 | + | |
| 92 | + @Schema(description = "打卡地点", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 93 | + @Valid | |
| 94 | + @NotNull(message = "打卡地点不能为空") | |
| 95 | + @DiffLogField(name = "打卡地点") | |
| 96 | + private List<Point> points; | |
| 97 | + | |
| 98 | + @Schema(description = "打卡 WiFi", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 99 | + @Valid | |
| 100 | + @NotNull(message = "打卡 WiFi 不能为空") | |
| 101 | + @DiffLogField(name = "打卡 WiFi") | |
| 102 | + private List<Wifi> wifis; | |
| 103 | + | |
| 104 | + @Schema(description = "扣款规则", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 105 | + @Valid | |
| 106 | + @NotNull(message = "扣款规则不能为空") | |
| 107 | + @DiffLogField(name = "扣款规则") | |
| 108 | + private DeductRule deductRule; | |
| 109 | + | |
| 110 | + @Schema(description = "管理后台 - HRM 考勤组特殊日期") | |
| 111 | + @Data | |
| 112 | + public static class SpecialDate { | |
| 113 | + | |
| 114 | + @Schema(description = "日期类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 115 | + @NotNull(message = "特殊日期类型不能为空") | |
| 116 | + @InEnum(value = HrmAttendanceHolidayTypeEnum.class, message = "特殊日期类型必须是 {value}") | |
| 117 | + private Integer type; | |
| 118 | + | |
| 119 | + @Schema(description = "特殊日期", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 120 | + @NotNull(message = "特殊日期不能为空") | |
| 121 | + private LocalDateTime date; | |
| 122 | + } | |
| 123 | + | |
| 124 | + @Schema(description = "管理后台 - HRM 考勤组班次配置") | |
| 125 | + @Data | |
| 126 | + public static class Shift { | |
| 127 | + | |
| 128 | + @Schema(description = "工作日数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2, 3, 4, 5]") | |
| 129 | + @NotEmpty(message = "班次工作日不能为空") | |
| 130 | + private List<@NotNull(message = "班次工作日不能为空") | |
| 131 | + @Min(value = 1, message = "班次工作日必须在 1 到 7 之间") | |
| 132 | + @Max(value = 7, message = "班次工作日必须在 1 到 7 之间") Integer> weeks; | |
| 133 | + | |
| 134 | + @Schema(description = "上班时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "09:00") | |
| 135 | + @JsonFormat(pattern = "HH:mm") | |
| 136 | + @NotNull(message = "上班时间不能为空") | |
| 137 | + private LocalTime startTime; | |
| 138 | + | |
| 139 | + @Schema(description = "下班时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "18:00") | |
| 140 | + @JsonFormat(pattern = "HH:mm") | |
| 141 | + @NotNull(message = "下班时间不能为空") | |
| 142 | + private LocalTime endTime; | |
| 143 | + | |
| 144 | + @Schema(description = "上班打卡开始时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "08:00") | |
| 145 | + @JsonFormat(pattern = "HH:mm") | |
| 146 | + @NotNull(message = "上班打卡开始时间不能为空") | |
| 147 | + private LocalTime clockInStartTime; | |
| 148 | + | |
| 149 | + @Schema(description = "上班打卡结束时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "10:00") | |
| 150 | + @JsonFormat(pattern = "HH:mm") | |
| 151 | + @NotNull(message = "上班打卡结束时间不能为空") | |
| 152 | + private LocalTime clockInEndTime; | |
| 153 | + | |
| 154 | + @Schema(description = "下班打卡开始时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "17:00") | |
| 155 | + @JsonFormat(pattern = "HH:mm") | |
| 156 | + @NotNull(message = "下班打卡开始时间不能为空") | |
| 157 | + private LocalTime clockOutStartTime; | |
| 158 | + | |
| 159 | + @Schema(description = "下班打卡结束时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "20:00") | |
| 160 | + @JsonFormat(pattern = "HH:mm") | |
| 161 | + @NotNull(message = "下班打卡结束时间不能为空") | |
| 162 | + private LocalTime clockOutEndTime; | |
| 163 | + | |
| 164 | + @Schema(description = "休息开始时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "12:00") | |
| 165 | + @JsonFormat(pattern = "HH:mm") | |
| 166 | + @NotNull(message = "休息开始时间不能为空") | |
| 167 | + private LocalTime restStartTime; | |
| 168 | + | |
| 169 | + @Schema(description = "休息结束时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "13:00") | |
| 170 | + @JsonFormat(pattern = "HH:mm") | |
| 171 | + @NotNull(message = "休息结束时间不能为空") | |
| 172 | + private LocalTime restEndTime; | |
| 173 | + | |
| 174 | + @Schema(description = "休息时间是否不计入工作时长", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 175 | + @NotNull(message = "休息时间计算方式不能为空") | |
| 176 | + private Boolean excludeRestTime; | |
| 177 | + } | |
| 178 | + | |
| 179 | + @Schema(description = "管理后台 - HRM 考勤组打卡地点") | |
| 180 | + @Data | |
| 181 | + public static class Point { | |
| 182 | + | |
| 183 | + @Schema(description = "地点名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "总部办公区") | |
| 184 | + @NotBlank(message = "地点名称不能为空") | |
| 185 | + @Size(max = 50, message = "地点名称不能超过 50 个字符") | |
| 186 | + private String name; | |
| 187 | + | |
| 188 | + @Schema(description = "定位地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "北京市海淀区中关村大街") | |
| 189 | + @NotBlank(message = "定位地址不能为空") | |
| 190 | + @Size(max = 255, message = "定位地址不能超过 255 个字符") | |
| 191 | + private String address; | |
| 192 | + | |
| 193 | + @Schema(description = "纬度", requiredMode = Schema.RequiredMode.REQUIRED, example = "39.983424") | |
| 194 | + @NotNull(message = "纬度不能为空") | |
| 195 | + @DecimalMin(value = "-90", message = "纬度必须在 -90 到 90 之间") | |
| 196 | + @DecimalMax(value = "90", message = "纬度必须在 -90 到 90 之间") | |
| 197 | + private BigDecimal latitude; | |
| 198 | + | |
| 199 | + @Schema(description = "经度", requiredMode = Schema.RequiredMode.REQUIRED, example = "116.322987") | |
| 200 | + @NotNull(message = "经度不能为空") | |
| 201 | + @DecimalMin(value = "-180", message = "经度必须在 -180 到 180 之间") | |
| 202 | + @DecimalMax(value = "180", message = "经度必须在 -180 到 180 之间") | |
| 203 | + private BigDecimal longitude; | |
| 204 | + | |
| 205 | + @Schema(description = "有效打卡半径,单位:米", requiredMode = Schema.RequiredMode.REQUIRED, example = "300") | |
| 206 | + @NotNull(message = "有效打卡半径不能为空") | |
| 207 | + @Positive(message = "有效打卡半径必须大于 0") | |
| 208 | + private Integer radius; | |
| 209 | + } | |
| 210 | + | |
| 211 | + @Schema(description = "管理后台 - HRM 考勤组 WiFi 配置") | |
| 212 | + @Data | |
| 213 | + public static class Wifi { | |
| 214 | + | |
| 215 | + @Schema(description = "WiFi 名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "Office-WiFi") | |
| 216 | + @NotBlank(message = "WiFi 名称不能为空") | |
| 217 | + @Size(max = 50, message = "WiFi 名称不能超过 50 个字符") | |
| 218 | + private String ssid; | |
| 219 | + | |
| 220 | + @Schema(description = "MAC 地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "00:11:22:33:44:55") | |
| 221 | + @NotBlank(message = "MAC 地址不能为空") | |
| 222 | + @Pattern(regexp = "^((([0-9a-fA-F]{2}:){5})|(([0-9a-fA-F]{2}-){5}))[0-9a-fA-F]{2}$", | |
| 223 | + message = "MAC 地址格式不正确") | |
| 224 | + private String mac; | |
| 225 | + } | |
| 226 | + | |
| 227 | + @Schema(description = "管理后台 - HRM 考勤组扣款规则") | |
| 228 | + @Data | |
| 229 | + public static class DeductRule { | |
| 230 | + | |
| 231 | + @Schema(description = "迟到扣款方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 232 | + @NotNull(message = "迟到扣款方式不能为空") | |
| 233 | + @InEnum(value = HrmAttendanceLateEarlyDeductMethodEnum.class, message = "迟到扣款方式必须是 {value}") | |
| 234 | + private Integer lateMethod; | |
| 235 | + | |
| 236 | + @Schema(description = "迟到扣款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "10.00") | |
| 237 | + @NotNull(message = "迟到扣款金额不能为空") | |
| 238 | + @DecimalMin(value = "0", message = "迟到扣款金额不能小于 0") | |
| 239 | + @Digits(integer = 8, fraction = 2, message = "迟到扣款金额最多 8 位整数和 2 位小数") | |
| 240 | + private BigDecimal lateDeductMoney; | |
| 241 | + | |
| 242 | + @Schema(description = "早退扣款方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 243 | + @NotNull(message = "早退扣款方式不能为空") | |
| 244 | + @InEnum(value = HrmAttendanceLateEarlyDeductMethodEnum.class, message = "早退扣款方式必须是 {value}") | |
| 245 | + private Integer earlyMethod; | |
| 246 | + | |
| 247 | + @Schema(description = "早退扣款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "10.00") | |
| 248 | + @NotNull(message = "早退扣款金额不能为空") | |
| 249 | + @DecimalMin(value = "0", message = "早退扣款金额不能小于 0") | |
| 250 | + @Digits(integer = 8, fraction = 2, message = "早退扣款金额最多 8 位整数和 2 位小数") | |
| 251 | + private BigDecimal earlyDeductMoney; | |
| 252 | + | |
| 253 | + @Schema(description = "旷工扣款方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 254 | + @NotNull(message = "旷工扣款方式不能为空") | |
| 255 | + @InEnum(value = HrmAttendanceAbsenteeismDeductMethodEnum.class, message = "旷工扣款方式必须是 {value}") | |
| 256 | + private Integer absenteeismMethod; | |
| 257 | + | |
| 258 | + @Schema(description = "旷工扣款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") | |
| 259 | + @NotNull(message = "旷工扣款金额不能为空") | |
| 260 | + @DecimalMin(value = "0", message = "旷工扣款金额不能小于 0") | |
| 261 | + @Digits(integer = 8, fraction = 2, message = "旷工扣款金额最多 8 位整数和 2 位小数") | |
| 262 | + private BigDecimal absenteeismDeductMoney; | |
| 263 | + | |
| 264 | + @Schema(description = "缺卡扣款方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 265 | + @NotNull(message = "缺卡扣款方式不能为空") | |
| 266 | + @InEnum(value = HrmAttendanceMisscardDeductMethodEnum.class, message = "缺卡扣款方式必须是 {value}") | |
| 267 | + private Integer misscardMethod; | |
| 268 | + | |
| 269 | + @Schema(description = "缺卡扣款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "20.00") | |
| 270 | + @NotNull(message = "缺卡扣款金额不能为空") | |
| 271 | + @DecimalMin(value = "0", message = "缺卡扣款金额不能小于 0") | |
| 272 | + @Digits(integer = 8, fraction = 2, message = "缺卡扣款金额最多 8 位整数和 2 位小数") | |
| 273 | + private BigDecimal misscardDeductMoney; | |
| 274 | + } | |
| 275 | + | |
| 276 | + @AssertTrue(message = "不同班次不能配置重复工作日") | |
| 277 | + @JsonIgnore | |
| 278 | + public boolean isShiftWeeksUnique() { | |
| 279 | + if (CollUtil.isEmpty(shifts)) { | |
| 280 | + return true; | |
| 281 | + } | |
| 282 | + Set<Integer> weeks = new HashSet<>(); | |
| 283 | + return shifts.stream().allMatch(shift -> shift != null && CollUtil.isNotEmpty(shift.getWeeks()) | |
| 284 | + && shift.getWeeks().stream().allMatch(weeks::add)); | |
| 285 | + } | |
| 286 | + | |
| 287 | + @AssertTrue(message = "班次上下班、打卡窗口或休息时间配置不正确") | |
| 288 | + @JsonIgnore | |
| 289 | + public boolean isShiftTimeValid() { | |
| 290 | + if (CollUtil.isEmpty(shifts)) { | |
| 291 | + return true; | |
| 292 | + } | |
| 293 | + return shifts.stream().allMatch(this::isShiftTimeValid); | |
| 294 | + } | |
| 295 | + | |
| 296 | + private boolean isShiftTimeValid(Shift shift) { | |
| 297 | + if (shift == null || shift.getStartTime() == null || shift.getEndTime() == null | |
| 298 | + || shift.getClockInStartTime() == null || shift.getClockInEndTime() == null | |
| 299 | + || shift.getClockOutStartTime() == null || shift.getClockOutEndTime() == null | |
| 300 | + || shift.getRestStartTime() == null || shift.getRestEndTime() == null | |
| 301 | + || shift.getExcludeRestTime() == null) { | |
| 302 | + // 交由内嵌字段的 @NotNull 输出具体校验错误 | |
| 303 | + return true; | |
| 304 | + } | |
| 305 | + if (shift.getStartTime().equals(shift.getEndTime()) | |
| 306 | + || shift.getClockInStartTime().equals(shift.getClockInEndTime()) | |
| 307 | + || shift.getClockOutStartTime().equals(shift.getClockOutEndTime())) { | |
| 308 | + return false; | |
| 309 | + } | |
| 310 | + LocalDate attendanceDate = LocalDate.of(2000, 1, 3); | |
| 311 | + TimeRange shiftTimeRange = CollUtil.getFirst(LocalDateTimeUtils.buildDailyTimeRanges( | |
| 312 | + attendanceDate, attendanceDate, shift.getStartTime(), shift.getEndTime())); | |
| 313 | + return LocalDateTimeUtils.findDailyTimeRange( | |
| 314 | + attendanceDate, shift.getClockInStartTime(), shift.getClockInEndTime(), | |
| 315 | + shiftTimeRange.getStartTime()) != null | |
| 316 | + && LocalDateTimeUtils.findDailyTimeRange( | |
| 317 | + attendanceDate, shift.getClockOutStartTime(), shift.getClockOutEndTime(), | |
| 318 | + shiftTimeRange.getEndTime()) != null | |
| 319 | + && isRestTimeValid(attendanceDate, shift, shiftTimeRange); | |
| 320 | + } | |
| 321 | + | |
| 322 | + private boolean isRestTimeValid(LocalDate attendanceDate, Shift shift, | |
| 323 | + TimeRange shiftTimeRange) { | |
| 324 | + if (Boolean.FALSE.equals(shift.getExcludeRestTime())) { | |
| 325 | + return true; | |
| 326 | + } | |
| 327 | + if (shift.getRestStartTime().equals(shift.getRestEndTime())) { | |
| 328 | + return false; | |
| 329 | + } | |
| 330 | + List<TimeRange> restTimeRanges = LocalDateTimeUtils.buildDailyTimeRanges( | |
| 331 | + attendanceDate.minusDays(1), attendanceDate.plusDays(1), | |
| 332 | + shift.getRestStartTime(), shift.getRestEndTime()); | |
| 333 | + return CollUtil.findOne(restTimeRanges, restTimeRange -> | |
| 334 | + LocalDateTimeUtils.isAfterOrEqual(restTimeRange.getStartTime(), shiftTimeRange.getStartTime()) | |
| 335 | + && LocalDateTimeUtils.isBeforeOrEqual( | |
| 336 | + restTimeRange.getEndTime(), shiftTimeRange.getEndTime())) != null; | |
| 337 | + } | |
| 338 | + | |
| 339 | + @AssertTrue(message = "特殊日期不能重复") | |
| 340 | + @JsonIgnore | |
| 341 | + public boolean isSpecialDatesUnique() { | |
| 342 | + if (CollUtil.isEmpty(specialDates)) { | |
| 343 | + return true; | |
| 344 | + } | |
| 345 | + Set<LocalDate> dates = new HashSet<>(); | |
| 346 | + return specialDates.stream().allMatch(specialDate -> specialDate != null && specialDate.getDate() != null | |
| 347 | + && dates.add(specialDate.getDate().toLocalDate())); | |
| 348 | + } | |
| 349 | + | |
| 350 | + @AssertTrue(message = "考勤组至少需要选择一个适用部门或员工") | |
| 351 | + @JsonIgnore | |
| 352 | + public boolean isScopeValid() { | |
| 353 | + if (deptIds == null || employeeIds == null) { | |
| 354 | + return true; | |
| 355 | + } | |
| 356 | + return CollUtil.isNotEmpty(deptIds) || CollUtil.isNotEmpty(employeeIds); | |
| 357 | + } | |
| 358 | + | |
| 359 | + @AssertTrue(message = "考勤组至少需要启用定位打卡或 WiFi 打卡") | |
| 360 | + @JsonIgnore | |
| 361 | + public boolean isClockMethodValid() { | |
| 362 | + if (openPointCard == null || openWifiCard == null) { | |
| 363 | + return true; | |
| 364 | + } | |
| 365 | + return openPointCard || openWifiCard; | |
| 366 | + } | |
| 367 | + | |
| 368 | + @AssertTrue(message = "启用定位打卡时,打卡地点不能为空") | |
| 369 | + @JsonIgnore | |
| 370 | + public boolean isPointSettingValid() { | |
| 371 | + if (Boolean.TRUE.equals(openPointCard)) { | |
| 372 | + return CollUtil.isNotEmpty(points); | |
| 373 | + } | |
| 374 | + return true; | |
| 375 | + } | |
| 376 | + | |
| 377 | + @AssertTrue(message = "启用 WiFi 打卡时,打卡 WiFi 不能为空") | |
| 378 | + @JsonIgnore | |
| 379 | + public boolean isWifiSettingValid() { | |
| 380 | + if (Boolean.TRUE.equals(openWifiCard)) { | |
| 381 | + return CollUtil.isNotEmpty(wifis); | |
| 382 | + } | |
| 383 | + return true; | |
| 384 | + } | |
| 385 | + | |
| 386 | + @AssertTrue(message = "有效打卡半径必须是 100 至 1000 米(间隔 100 米),或 2000、3000 米") | |
| 387 | + @JsonIgnore | |
| 388 | + public boolean isPointRadiusValid() { | |
| 389 | + if (CollUtil.isEmpty(points)) { | |
| 390 | + return true; | |
| 391 | + } | |
| 392 | + return points.stream().allMatch(point -> point != null | |
| 393 | + && point.getRadius() != null && POINT_RADIUS_VALUES.contains(point.getRadius())); | |
| 394 | + } | |
| 395 | + | |
| 396 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/holiday/HrmAttendanceHolidayPageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.holiday; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 4 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 5 | +import cn.iocoder.yudao.module.hrm.enums.attendance.config.HrmAttendanceHolidayTypeEnum; | |
| 6 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 7 | +import lombok.Data; | |
| 8 | +import lombok.EqualsAndHashCode; | |
| 9 | +import lombok.ToString; | |
| 10 | + | |
| 11 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 12 | + | |
| 13 | +import java.time.LocalDateTime; | |
| 14 | + | |
| 15 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 16 | + | |
| 17 | +@Schema(description = "管理后台 - HRM 考勤节假日分页 Request VO") | |
| 18 | +@Data | |
| 19 | +@EqualsAndHashCode(callSuper = true) | |
| 20 | +@ToString(callSuper = true) | |
| 21 | +public class HrmAttendanceHolidayPageReqVO extends PageParam { | |
| 22 | + | |
| 23 | + @Schema(description = "日期范围") | |
| 24 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 25 | + private LocalDateTime[] date; | |
| 26 | + | |
| 27 | + @Schema(description = "节假日类型", example = "1") | |
| 28 | + @InEnum(value = HrmAttendanceHolidayTypeEnum.class, message = "节假日类型必须是 {value}") | |
| 29 | + private Integer type; | |
| 30 | + | |
| 31 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/holiday/HrmAttendanceHolidayRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.holiday; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.time.LocalDateTime; | |
| 7 | + | |
| 8 | +@Schema(description = "管理后台 - HRM 考勤节假日 Response VO") | |
| 9 | +@Data | |
| 10 | +public class HrmAttendanceHolidayRespVO { | |
| 11 | + | |
| 12 | + @Schema(description = "编号") | |
| 13 | + private Long id; | |
| 14 | + | |
| 15 | + @Schema(description = "日期") | |
| 16 | + private LocalDateTime date; | |
| 17 | + | |
| 18 | + @Schema(description = "考勤假期类型") | |
| 19 | + private Integer type; | |
| 20 | + | |
| 21 | + @Schema(description = "创建时间") | |
| 22 | + private LocalDateTime createTime; | |
| 23 | + | |
| 24 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/holiday/HrmAttendanceHolidaySaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.holiday; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 4 | +import cn.iocoder.yudao.module.hrm.enums.attendance.config.HrmAttendanceHolidayTypeEnum; | |
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 6 | +import jakarta.validation.constraints.NotNull; | |
| 7 | +import lombok.Data; | |
| 8 | + | |
| 9 | +import java.time.LocalDateTime; | |
| 10 | + | |
| 11 | +@Schema(description = "管理后台 - HRM 考勤节假日保存 Request VO") | |
| 12 | +@Data | |
| 13 | +public class HrmAttendanceHolidaySaveReqVO { | |
| 14 | + | |
| 15 | + @Schema(description = "节假日编号", example = "1024") | |
| 16 | + private Long id; | |
| 17 | + | |
| 18 | + @Schema(description = "日期", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 19 | + @NotNull(message = "日期不能为空") | |
| 20 | + private LocalDateTime date; | |
| 21 | + | |
| 22 | + @Schema(description = "节假日类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | |
| 23 | + @NotNull(message = "类型不能为空") | |
| 24 | + @InEnum(value = HrmAttendanceHolidayTypeEnum.class, message = "节假日类型必须是 {value}") | |
| 25 | + private Integer type; | |
| 26 | + | |
| 27 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/leave/HrmAttendanceLeaveCancelReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.leave; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import jakarta.validation.constraints.NotBlank; | |
| 5 | +import jakarta.validation.constraints.NotNull; | |
| 6 | +import jakarta.validation.constraints.Size; | |
| 7 | +import lombok.Data; | |
| 8 | + | |
| 9 | +@Schema(description = "管理后台 - HRM 员工请假申请取消 Request VO") | |
| 10 | +@Data | |
| 11 | +public class HrmAttendanceLeaveCancelReqVO { | |
| 12 | + | |
| 13 | + @Schema(description = "请假记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 14 | + @NotNull(message = "请假记录编号不能为空") | |
| 15 | + private Long id; | |
| 16 | + | |
| 17 | + @Schema(description = "取消原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "行程取消") | |
| 18 | + @NotBlank(message = "取消原因不能为空") | |
| 19 | + @Size(max = 500, message = "取消原因不能超过 500 个字符") | |
| 20 | + private String reason; | |
| 21 | + | |
| 22 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/leave/HrmAttendanceLeaveCreateReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.leave; | |
| 2 | + | |
| 3 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import jakarta.validation.constraints.AssertTrue; | |
| 6 | +import jakarta.validation.constraints.DecimalMin; | |
| 7 | +import jakarta.validation.constraints.Digits; | |
| 8 | +import jakarta.validation.constraints.NotBlank; | |
| 9 | +import jakarta.validation.constraints.NotNull; | |
| 10 | +import jakarta.validation.constraints.Size; | |
| 11 | +import lombok.Data; | |
| 12 | + | |
| 13 | +import java.math.BigDecimal; | |
| 14 | +import java.time.LocalDateTime; | |
| 15 | + | |
| 16 | +@Schema(description = "管理后台 - HRM 员工请假申请创建 Request VO") | |
| 17 | +@Data | |
| 18 | +public class HrmAttendanceLeaveCreateReqVO { | |
| 19 | + | |
| 20 | + @Schema(description = "请假类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "事假") | |
| 21 | + @NotBlank(message = "请假类型不能为空") | |
| 22 | + @Size(max = 64, message = "请假类型不能超过 64 个字符") | |
| 23 | + private String type; | |
| 24 | + | |
| 25 | + @Schema(description = "请假开始时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 26 | + @NotNull(message = "请假开始时间不能为空") | |
| 27 | + private LocalDateTime startTime; | |
| 28 | + | |
| 29 | + @Schema(description = "请假结束时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 30 | + @NotNull(message = "请假结束时间不能为空") | |
| 31 | + private LocalDateTime endTime; | |
| 32 | + | |
| 33 | + @Schema(description = "请假天数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.00") | |
| 34 | + @NotNull(message = "请假天数不能为空") | |
| 35 | + @DecimalMin(value = "0.01", message = "请假天数必须大于 0") | |
| 36 | + @Digits(integer = 8, fraction = 2, message = "请假天数最多保留两位小数") | |
| 37 | + private BigDecimal day; | |
| 38 | + | |
| 39 | + @Schema(description = "请假事由", requiredMode = Schema.RequiredMode.REQUIRED, example = "个人事务") | |
| 40 | + @NotBlank(message = "请假事由不能为空") | |
| 41 | + @Size(max = 300, message = "请假事由不能超过 300 个字符") | |
| 42 | + private String reason; | |
| 43 | + | |
| 44 | + @Schema(description = "备注", example = "请审批") | |
| 45 | + @Size(max = 500, message = "备注不能超过 500 个字符") | |
| 46 | + private String remark; | |
| 47 | + | |
| 48 | + @Schema(hidden = true) | |
| 49 | + @AssertTrue(message = "请假结束时间必须晚于请假开始时间") | |
| 50 | + @JsonIgnore | |
| 51 | + public boolean isTimeRangeValid() { | |
| 52 | + return startTime == null || endTime == null || endTime.isAfter(startTime); | |
| 53 | + } | |
| 54 | + | |
| 55 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/leave/HrmAttendanceLeavePageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.leave; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.SortablePageParam; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import lombok.Data; | |
| 6 | +import lombok.EqualsAndHashCode; | |
| 7 | +import lombok.ToString; | |
| 8 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 9 | + | |
| 10 | +import java.time.LocalDateTime; | |
| 11 | +import java.util.Collection; | |
| 12 | +import java.util.List; | |
| 13 | + | |
| 14 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 15 | + | |
| 16 | +@Schema(description = "管理后台 - HRM 请假记录分页 Request VO") | |
| 17 | +@Data | |
| 18 | +@EqualsAndHashCode(callSuper = true) | |
| 19 | +@ToString(callSuper = true) | |
| 20 | +public class HrmAttendanceLeavePageReqVO extends SortablePageParam { | |
| 21 | + | |
| 22 | + @Schema(description = "员工编号", example = "1024") | |
| 23 | + private Long employeeId; | |
| 24 | + | |
| 25 | + @Schema(description = "部门编号数组", example = "[100, 101]") | |
| 26 | + private List<Long> deptIds; | |
| 27 | + | |
| 28 | + @Schema(description = "员工姓名或工号", example = "张三") | |
| 29 | + private String employeeKeyword; | |
| 30 | + | |
| 31 | + @Schema(description = "请假类型数组", example = "[事假, 病假]") | |
| 32 | + private List<String> types; | |
| 33 | + | |
| 34 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 35 | + @Schema(description = "开始时间") | |
| 36 | + private LocalDateTime[] startTime; | |
| 37 | + | |
| 38 | + @Schema(description = "审批状态", example = "2") | |
| 39 | + private Integer approvalStatus; | |
| 40 | + | |
| 41 | + @Schema(hidden = true) | |
| 42 | + private Collection<Long> employeeIds; | |
| 43 | + | |
| 44 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/leave/HrmAttendanceLeaveRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.leave; | |
| 2 | + | |
| 3 | +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; | |
| 4 | +import cn.idev.excel.annotation.ExcelProperty; | |
| 5 | +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; | |
| 6 | +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; | |
| 7 | +import cn.iocoder.yudao.module.hrm.enums.DictTypeConstants; | |
| 8 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 9 | +import lombok.Data; | |
| 10 | + | |
| 11 | +import java.math.BigDecimal; | |
| 12 | +import java.time.LocalDateTime; | |
| 13 | + | |
| 14 | +@Schema(description = "管理后台 - HRM 请假记录 Response VO") | |
| 15 | +@Data | |
| 16 | +@ExcelIgnoreUnannotated | |
| 17 | +public class HrmAttendanceLeaveRespVO { | |
| 18 | + | |
| 19 | + @Schema(description = "请假记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 20 | + @ExcelProperty(value = "记录编号", index = 0) | |
| 21 | + private Long id; | |
| 22 | + | |
| 23 | + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1001") | |
| 24 | + @ExcelProperty(value = "员工编号", index = 1) | |
| 25 | + private Long employeeId; | |
| 26 | + | |
| 27 | + @Schema(description = "员工姓名", example = "张三") | |
| 28 | + @ExcelProperty(value = "员工姓名", index = 2) | |
| 29 | + private String employeeName; | |
| 30 | + | |
| 31 | + @Schema(description = "工号", example = "HRM001") | |
| 32 | + @ExcelProperty(value = "工号", index = 3) | |
| 33 | + private String jobNumber; | |
| 34 | + | |
| 35 | + @Schema(description = "部门编号", example = "100") | |
| 36 | + private Long deptId; | |
| 37 | + | |
| 38 | + @Schema(description = "部门名称", example = "研发部") | |
| 39 | + @ExcelProperty(value = "部门", index = 4) | |
| 40 | + private String deptName; | |
| 41 | + | |
| 42 | + @Schema(description = "职位名称", example = "Java 工程师") | |
| 43 | + @ExcelProperty(value = "岗位", index = 5) | |
| 44 | + private String postName; | |
| 45 | + | |
| 46 | + @Schema(description = "请假类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "事假") | |
| 47 | + @ExcelProperty(value = "请假类型", index = 6, converter = DictConvert.class) | |
| 48 | + @DictFormat(DictTypeConstants.HRM_ATTENDANCE_LEAVE_TYPE) | |
| 49 | + private String type; | |
| 50 | + | |
| 51 | + @Schema(description = "开始时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 52 | + @ExcelProperty(value = "开始时间", index = 7) | |
| 53 | + private LocalDateTime startTime; | |
| 54 | + | |
| 55 | + @Schema(description = "结束时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 56 | + @ExcelProperty(value = "结束时间", index = 8) | |
| 57 | + private LocalDateTime endTime; | |
| 58 | + | |
| 59 | + @Schema(description = "请假天数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0") | |
| 60 | + @ExcelProperty(value = "请假天数", index = 9) | |
| 61 | + private BigDecimal day; | |
| 62 | + | |
| 63 | + @Schema(description = "请假事由", example = "个人事务") | |
| 64 | + @ExcelProperty(value = "请假事由", index = 10) | |
| 65 | + private String reason; | |
| 66 | + | |
| 67 | + @Schema(description = "备注") | |
| 68 | + @ExcelProperty(value = "备注", index = 11) | |
| 69 | + private String remark; | |
| 70 | + | |
| 71 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 72 | + @ExcelProperty(value = "创建时间", index = 12) | |
| 73 | + private LocalDateTime createTime; | |
| 74 | + | |
| 75 | + @Schema(description = "审批状态", example = "1") | |
| 76 | + private Integer approvalStatus; | |
| 77 | + | |
| 78 | + @Schema(description = "流程实例编号") | |
| 79 | + private String processInstanceId; | |
| 80 | + | |
| 81 | + @Schema(description = "审批时间") | |
| 82 | + private LocalDateTime approvalTime; | |
| 83 | + | |
| 84 | + @Schema(description = "审批意见") | |
| 85 | + private String approvalReason; | |
| 86 | + | |
| 87 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceDailyDetailReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import jakarta.validation.constraints.NotNull; | |
| 5 | +import lombok.Data; | |
| 6 | + | |
| 7 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 8 | + | |
| 9 | +import java.time.LocalDateTime; | |
| 10 | + | |
| 11 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 12 | + | |
| 13 | +@Schema(description = "管理后台 - HRM 员工每日考勤明细 Request VO") | |
| 14 | +@Data | |
| 15 | +public class HrmAttendanceDailyDetailReqVO { | |
| 16 | + | |
| 17 | + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 18 | + @NotNull(message = "员工不能为空") | |
| 19 | + private Long employeeId; | |
| 20 | + | |
| 21 | + @Schema(description = "考勤日期", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 22 | + @NotNull(message = "考勤日期不能为空") | |
| 23 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 24 | + private LocalDateTime attendanceTime; | |
| 25 | + | |
| 26 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceDailyDetailRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics; | |
| 2 | + | |
| 3 | +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; | |
| 4 | +import cn.idev.excel.annotation.ExcelProperty; | |
| 5 | +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.clock.HrmAttendanceClockRespVO; | |
| 6 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 7 | +import lombok.Data; | |
| 8 | + | |
| 9 | +import java.math.BigDecimal; | |
| 10 | +import java.time.LocalDateTime; | |
| 11 | +import java.util.List; | |
| 12 | + | |
| 13 | +@Schema(description = "管理后台 - HRM 员工每日考勤明细 Response VO") | |
| 14 | +@Data | |
| 15 | +@ExcelIgnoreUnannotated | |
| 16 | +public class HrmAttendanceDailyDetailRespVO { | |
| 17 | + | |
| 18 | + @Schema(description = "员工编号") | |
| 19 | + private Long employeeId; | |
| 20 | + | |
| 21 | + @Schema(description = "员工姓名") | |
| 22 | + private String employeeName; | |
| 23 | + | |
| 24 | + @Schema(description = "工号") | |
| 25 | + private String jobNumber; | |
| 26 | + | |
| 27 | + @Schema(description = "部门编号") | |
| 28 | + private Long deptId; | |
| 29 | + | |
| 30 | + @Schema(description = "部门名称") | |
| 31 | + private String deptName; | |
| 32 | + | |
| 33 | + @Schema(description = "职位名称") | |
| 34 | + private String postName; | |
| 35 | + | |
| 36 | + @ExcelProperty(value = "日期", index = 0) | |
| 37 | + @Schema(description = "日期") | |
| 38 | + private LocalDateTime attendanceTime; | |
| 39 | + | |
| 40 | + @ExcelProperty(value = "班次", index = 1) | |
| 41 | + @Schema(description = "班次") | |
| 42 | + private String shiftName; | |
| 43 | + | |
| 44 | + @Schema(description = "是否排班") | |
| 45 | + private Boolean scheduled; | |
| 46 | + | |
| 47 | + @Schema(description = "应打卡次数") | |
| 48 | + private Integer requiredClockCount; | |
| 49 | + | |
| 50 | + @Schema(description = "排班分钟数") | |
| 51 | + private Integer scheduledMinutes; | |
| 52 | + | |
| 53 | + @ExcelProperty(value = "缺卡次数", index = 8) | |
| 54 | + @Schema(description = "缺卡次数") | |
| 55 | + private Integer misscardCount; | |
| 56 | + | |
| 57 | + @Schema(description = "是否旷工") | |
| 58 | + private Boolean absenteeism; | |
| 59 | + | |
| 60 | + @Schema(description = "旷工分钟数") | |
| 61 | + private Integer absenteeismMinutes; | |
| 62 | + | |
| 63 | + @ExcelProperty(value = "旷工天数", index = 9) | |
| 64 | + @Schema(description = "旷工天数") | |
| 65 | + private BigDecimal absenteeismDays; | |
| 66 | + | |
| 67 | + @Schema(description = "是否请假") | |
| 68 | + private Boolean leaveStatus; | |
| 69 | + | |
| 70 | + @Schema(description = "请假分钟数") | |
| 71 | + private Integer leaveMinutes; | |
| 72 | + | |
| 73 | + @ExcelProperty(value = "请假天数", index = 10) | |
| 74 | + @Schema(description = "请假天数") | |
| 75 | + private BigDecimal leaveDays; | |
| 76 | + | |
| 77 | + @ExcelProperty(value = "考勤结果", index = 2) | |
| 78 | + @Schema(description = "考勤结果") | |
| 79 | + private String attendanceResult; | |
| 80 | + | |
| 81 | + @ExcelProperty(value = "打卡次数", index = 3) | |
| 82 | + @Schema(description = "打卡次数") | |
| 83 | + private Integer clockCount; | |
| 84 | + | |
| 85 | + @ExcelProperty(value = "迟到次数", index = 4) | |
| 86 | + @Schema(description = "迟到次数") | |
| 87 | + private Integer lateCount; | |
| 88 | + | |
| 89 | + @ExcelProperty(value = "迟到分钟", index = 5) | |
| 90 | + @Schema(description = "迟到分钟") | |
| 91 | + private Integer lateMinutes; | |
| 92 | + | |
| 93 | + @ExcelProperty(value = "早退次数", index = 6) | |
| 94 | + @Schema(description = "早退次数") | |
| 95 | + private Integer earlyCount; | |
| 96 | + | |
| 97 | + @ExcelProperty(value = "早退分钟", index = 7) | |
| 98 | + @Schema(description = "早退分钟") | |
| 99 | + private Integer earlyMinutes; | |
| 100 | + | |
| 101 | + @Schema(description = "打卡列表") | |
| 102 | + private List<HrmAttendanceClockRespVO> clockList; | |
| 103 | + | |
| 104 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceDailyOverviewRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.clock.HrmAttendanceClockRespVO; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import lombok.Data; | |
| 6 | + | |
| 7 | +import java.util.List; | |
| 8 | + | |
| 9 | +@Schema(description = "管理后台 - HRM 员工每日打卡概况 Response VO") | |
| 10 | +@Data | |
| 11 | +public class HrmAttendanceDailyOverviewRespVO { | |
| 12 | + | |
| 13 | + @Schema(description = "打卡记录") | |
| 14 | + private List<HrmAttendanceClockRespVO> clocks; | |
| 15 | + | |
| 16 | + @Schema(description = "考勤结果") | |
| 17 | + private String attendanceResult; | |
| 18 | + | |
| 19 | + @Schema(description = "打卡概况展示项") | |
| 20 | + private List<OverviewItem> overviews; | |
| 21 | + | |
| 22 | + @Schema(description = "管理后台 - HRM 员工每日打卡概况展示项 Response VO") | |
| 23 | + @Data | |
| 24 | + public static class OverviewItem { | |
| 25 | + | |
| 26 | + @Schema(description = "打卡类型") | |
| 27 | + private String type; | |
| 28 | + | |
| 29 | + @Schema(description = "打卡时间") | |
| 30 | + private String time; | |
| 31 | + | |
| 32 | + @Schema(description = "打卡状态") | |
| 33 | + private String status; | |
| 34 | + | |
| 35 | + @Schema(description = "考勤结果") | |
| 36 | + private String text; | |
| 37 | + | |
| 38 | + } | |
| 39 | + | |
| 40 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceMonthDailyOverviewRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.time.LocalDate; | |
| 7 | +import java.util.Map; | |
| 8 | + | |
| 9 | +@Schema(description = "管理后台 - HRM 员工月度打卡概况 Response VO") | |
| 10 | +@Data | |
| 11 | +public class HrmAttendanceMonthDailyOverviewRespVO { | |
| 12 | + | |
| 13 | + @Schema(description = "员工编号") | |
| 14 | + private Long employeeId; | |
| 15 | + | |
| 16 | + @Schema(description = "员工姓名") | |
| 17 | + private String employeeName; | |
| 18 | + | |
| 19 | + @Schema(description = "工号") | |
| 20 | + private String jobNumber; | |
| 21 | + | |
| 22 | + @Schema(description = "部门编号") | |
| 23 | + private Long deptId; | |
| 24 | + | |
| 25 | + @Schema(description = "部门名称") | |
| 26 | + private String deptName; | |
| 27 | + | |
| 28 | + @Schema(description = "职位名称") | |
| 29 | + private String postName; | |
| 30 | + | |
| 31 | + @Schema(description = "年份") | |
| 32 | + private Integer year; | |
| 33 | + | |
| 34 | + @Schema(description = "月份") | |
| 35 | + private Integer month; | |
| 36 | + | |
| 37 | + @Schema(description = "每日打卡概况,Key 为日期") | |
| 38 | + private Map<LocalDate, HrmAttendanceDailyOverviewRespVO> dailyClockMap; | |
| 39 | + | |
| 40 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceMonthDetailRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.leave.HrmAttendanceLeaveRespVO; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import lombok.Data; | |
| 6 | + | |
| 7 | +import java.util.List; | |
| 8 | + | |
| 9 | +@Schema(description = "管理后台 - HRM 员工月度考勤详情 Response VO") | |
| 10 | +@Data | |
| 11 | +public class HrmAttendanceMonthDetailRespVO { | |
| 12 | + | |
| 13 | + @Schema(description = "月度汇总") | |
| 14 | + private HrmAttendanceMonthRecordRespVO summary; | |
| 15 | + | |
| 16 | + @Schema(description = "每日打卡明细") | |
| 17 | + private List<HrmAttendanceDailyDetailRespVO> dailyDetails; | |
| 18 | + | |
| 19 | + @Schema(description = "请假记录") | |
| 20 | + private List<HrmAttendanceLeaveRespVO> leaves; | |
| 21 | + | |
| 22 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceMonthRecordPageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import jakarta.validation.constraints.Max; | |
| 6 | +import jakarta.validation.constraints.Min; | |
| 7 | +import jakarta.validation.constraints.NotNull; | |
| 8 | +import lombok.Data; | |
| 9 | +import lombok.EqualsAndHashCode; | |
| 10 | +import lombok.ToString; | |
| 11 | + | |
| 12 | +import java.util.List; | |
| 13 | + | |
| 14 | +@Schema(description = "管理后台 - HRM 员工月度考勤汇总分页 Request VO") | |
| 15 | +@Data | |
| 16 | +@EqualsAndHashCode(callSuper = true) | |
| 17 | +@ToString(callSuper = true) | |
| 18 | +public class HrmAttendanceMonthRecordPageReqVO extends PageParam { | |
| 19 | + | |
| 20 | + @Schema(description = "员工姓名或工号,模糊匹配", example = "张三") | |
| 21 | + private String search; | |
| 22 | + | |
| 23 | + @Schema(description = "员工编号", example = "1024") | |
| 24 | + private Long employeeId; | |
| 25 | + | |
| 26 | + @Schema(description = "部门编号数组", example = "100,101") | |
| 27 | + private List<Long> deptIds; | |
| 28 | + | |
| 29 | + @Schema(description = "年份", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026") | |
| 30 | + @NotNull(message = "年份不能为空") | |
| 31 | + private Integer year; | |
| 32 | + | |
| 33 | + @Schema(description = "月份", requiredMode = Schema.RequiredMode.REQUIRED, example = "7") | |
| 34 | + @NotNull(message = "月份不能为空") | |
| 35 | + @Min(value = 1, message = "月份必须在 1 到 12 之间") | |
| 36 | + @Max(value = 12, message = "月份必须在 1 到 12 之间") | |
| 37 | + private Integer month; | |
| 38 | + | |
| 39 | + @Schema(description = "是否全勤", example = "true") | |
| 40 | + private Boolean fullAttendance; | |
| 41 | + | |
| 42 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/statistics/HrmAttendanceMonthRecordRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.statistics; | |
| 2 | + | |
| 3 | +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; | |
| 4 | +import cn.idev.excel.annotation.ExcelProperty; | |
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 6 | +import lombok.Data; | |
| 7 | + | |
| 8 | +import java.math.BigDecimal; | |
| 9 | +import java.time.LocalDateTime; | |
| 10 | + | |
| 11 | +@Schema(description = "管理后台 - HRM 员工月度考勤汇总 Response VO") | |
| 12 | +@Data | |
| 13 | +@ExcelIgnoreUnannotated | |
| 14 | +public class HrmAttendanceMonthRecordRespVO { | |
| 15 | + | |
| 16 | + @ExcelProperty(value = "员工编号", index = 0) | |
| 17 | + @Schema(description = "员工编号") | |
| 18 | + private Long employeeId; | |
| 19 | + | |
| 20 | + @ExcelProperty(value = "员工姓名", index = 1) | |
| 21 | + @Schema(description = "员工姓名") | |
| 22 | + private String employeeName; | |
| 23 | + | |
| 24 | + @ExcelProperty(value = "工号", index = 2) | |
| 25 | + @Schema(description = "工号") | |
| 26 | + private String jobNumber; | |
| 27 | + | |
| 28 | + @Schema(description = "部门编号") | |
| 29 | + private Long deptId; | |
| 30 | + | |
| 31 | + @ExcelProperty(value = "部门", index = 3) | |
| 32 | + @Schema(description = "部门名称") | |
| 33 | + private String deptName; | |
| 34 | + | |
| 35 | + @Schema(description = "职位名称") | |
| 36 | + @ExcelProperty(value = "岗位", index = 4) | |
| 37 | + private String postName; | |
| 38 | + | |
| 39 | + @Schema(description = "考勤组名称") | |
| 40 | + private String attendanceGroupName; | |
| 41 | + | |
| 42 | + @Schema(description = "入职时间") | |
| 43 | + private LocalDateTime entryTime; | |
| 44 | + | |
| 45 | + @Schema(description = "员工状态") | |
| 46 | + private Integer employeeStatus; | |
| 47 | + | |
| 48 | + @Schema(description = "工作城市") | |
| 49 | + private String workCity; | |
| 50 | + | |
| 51 | + @ExcelProperty(value = "年份", index = 5) | |
| 52 | + @Schema(description = "年份") | |
| 53 | + private Integer year; | |
| 54 | + | |
| 55 | + @ExcelProperty(value = "月份", index = 6) | |
| 56 | + @Schema(description = "月份") | |
| 57 | + private Integer month; | |
| 58 | + | |
| 59 | + @ExcelProperty(value = "应出勤天数", index = 7) | |
| 60 | + @Schema(description = "应出勤天数") | |
| 61 | + private Integer attendDays; | |
| 62 | + | |
| 63 | + @ExcelProperty(value = "实际出勤天数", index = 8) | |
| 64 | + @Schema(description = "实际出勤天数") | |
| 65 | + private BigDecimal actualDays; | |
| 66 | + | |
| 67 | + @ExcelProperty(value = "迟到时长(分钟)", index = 9) | |
| 68 | + @Schema(description = "迟到时长(分钟)") | |
| 69 | + private Integer lateMinute; | |
| 70 | + | |
| 71 | + @ExcelProperty(value = "迟到次数", index = 10) | |
| 72 | + @Schema(description = "迟到次数") | |
| 73 | + private Integer lateCount; | |
| 74 | + | |
| 75 | + @ExcelProperty(value = "早退时长(分钟)", index = 11) | |
| 76 | + @Schema(description = "早退时长(分钟)") | |
| 77 | + private Integer earlyMinute; | |
| 78 | + | |
| 79 | + @ExcelProperty(value = "早退次数", index = 12) | |
| 80 | + @Schema(description = "早退次数") | |
| 81 | + private Integer earlyCount; | |
| 82 | + | |
| 83 | + @ExcelProperty(value = "缺卡次数", index = 13) | |
| 84 | + @Schema(description = "缺卡次数") | |
| 85 | + private Integer misscardCount; | |
| 86 | + | |
| 87 | + @ExcelProperty(value = "旷工天数", index = 14) | |
| 88 | + @Schema(description = "旷工天数") | |
| 89 | + private BigDecimal absenteeismDays; | |
| 90 | + | |
| 91 | + @ExcelProperty(value = "旷工时长(分钟)", index = 15) | |
| 92 | + @Schema(description = "旷工时长(分钟)") | |
| 93 | + private Integer absenteeismMinutes; | |
| 94 | + | |
| 95 | + @ExcelProperty(value = "请假天数", index = 16) | |
| 96 | + @Schema(description = "请假天数") | |
| 97 | + private BigDecimal leaveDays; | |
| 98 | + | |
| 99 | + @ExcelProperty(value = "请假时长(分钟)", index = 17) | |
| 100 | + @Schema(description = "请假时长(分钟)") | |
| 101 | + private Integer leaveMinutes; | |
| 102 | + | |
| 103 | + @ExcelProperty(value = "迟到扣款", index = 18) | |
| 104 | + @Schema(description = "迟到扣款") | |
| 105 | + private BigDecimal lateDeductAmount; | |
| 106 | + | |
| 107 | + @ExcelProperty(value = "早退扣款", index = 19) | |
| 108 | + @Schema(description = "早退扣款") | |
| 109 | + private BigDecimal earlyDeductAmount; | |
| 110 | + | |
| 111 | + @ExcelProperty(value = "缺卡扣款", index = 20) | |
| 112 | + @Schema(description = "缺卡扣款") | |
| 113 | + private BigDecimal misscardDeductAmount; | |
| 114 | + | |
| 115 | + @ExcelProperty(value = "旷工扣款", index = 21) | |
| 116 | + @Schema(description = "旷工扣款") | |
| 117 | + private BigDecimal absenteeismDeductAmount; | |
| 118 | + | |
| 119 | + @ExcelProperty(value = "考勤扣款合计", index = 22) | |
| 120 | + @Schema(description = "考勤扣款合计") | |
| 121 | + private BigDecimal attendanceDeductAmount; | |
| 122 | + | |
| 123 | + @Schema(description = "是否全勤") | |
| 124 | + private Boolean fullAttendance; | |
| 125 | + | |
| 126 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeCertificateController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 5 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.certificate.HrmEmployeeCertificateRespVO; | |
| 6 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.certificate.HrmEmployeeCertificateSaveReqVO; | |
| 7 | +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.experience.HrmEmployeeCertificateDO; | |
| 8 | +import cn.iocoder.yudao.module.hrm.service.employee.experience.HrmEmployeeCertificateService; | |
| 9 | +import io.swagger.v3.oas.annotations.Operation; | |
| 10 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 11 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 12 | +import jakarta.annotation.Resource; | |
| 13 | +import jakarta.validation.Valid; | |
| 14 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 15 | +import org.springframework.validation.annotation.Validated; | |
| 16 | +import org.springframework.web.bind.annotation.DeleteMapping; | |
| 17 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 18 | +import org.springframework.web.bind.annotation.PostMapping; | |
| 19 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 20 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 21 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 22 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 23 | +import org.springframework.web.bind.annotation.RestController; | |
| 24 | + | |
| 25 | +import java.util.List; | |
| 26 | + | |
| 27 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 28 | + | |
| 29 | +@Tag(name = "管理后台 - HRM 员工证书") | |
| 30 | +@RestController | |
| 31 | +@RequestMapping("/hrm/employee/certificate") | |
| 32 | +@Validated | |
| 33 | +public class HrmEmployeeCertificateController { | |
| 34 | + | |
| 35 | + @Resource | |
| 36 | + private HrmEmployeeCertificateService certificateService; | |
| 37 | + | |
| 38 | + @GetMapping("/list") | |
| 39 | + @Operation(summary = "获得员工证书列表") | |
| 40 | + @Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024") | |
| 41 | + @PreAuthorize("@ss.hasPermission('hrm:employee:query')") | |
| 42 | + public CommonResult<List<HrmEmployeeCertificateRespVO>> getCertificateList( | |
| 43 | + @RequestParam("employeeId") Long employeeId) { | |
| 44 | + List<HrmEmployeeCertificateDO> certificates = certificateService.getCertificateListByEmployeeId(employeeId); | |
| 45 | + return success(BeanUtils.toBean(certificates, HrmEmployeeCertificateRespVO.class)); | |
| 46 | + } | |
| 47 | + | |
| 48 | + @PostMapping("/create") | |
| 49 | + @Operation(summary = "创建员工证书") | |
| 50 | + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") | |
| 51 | + public CommonResult<Long> createCertificate( | |
| 52 | + @Valid @RequestBody HrmEmployeeCertificateSaveReqVO reqVO) { | |
| 53 | + return success(certificateService.createCertificate(reqVO)); | |
| 54 | + } | |
| 55 | + | |
| 56 | + @PutMapping("/update") | |
| 57 | + @Operation(summary = "更新员工证书") | |
| 58 | + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") | |
| 59 | + public CommonResult<Boolean> updateCertificate( | |
| 60 | + @Valid @RequestBody HrmEmployeeCertificateSaveReqVO reqVO) { | |
| 61 | + certificateService.updateCertificate(reqVO); | |
| 62 | + return success(true); | |
| 63 | + } | |
| 64 | + | |
| 65 | + @DeleteMapping("/delete") | |
| 66 | + @Operation(summary = "删除员工证书") | |
| 67 | + @Parameter(name = "id", description = "证书编号", required = true, example = "1024") | |
| 68 | + @PreAuthorize("@ss.hasPermission('hrm:employee:delete')") | |
| 69 | + public CommonResult<Boolean> deleteCertificate(@RequestParam("id") Long id) { | |
| 70 | + certificateService.deleteCertificate(id); | |
| 71 | + return success(true); | |
| 72 | + } | |
| 73 | + | |
| 74 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeChangeRecordController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; | |
| 5 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 6 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.changerecord.HrmEmployeeChangeRecordRespVO; | |
| 7 | +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.employment.HrmEmployeeChangeRecordDO; | |
| 8 | +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; | |
| 9 | +import cn.iocoder.yudao.module.hrm.service.employee.employment.HrmEmployeeChangeRecordService; | |
| 10 | +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService; | |
| 11 | +import cn.iocoder.yudao.module.system.api.dept.DeptApi; | |
| 12 | +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; | |
| 13 | +import io.swagger.v3.oas.annotations.Operation; | |
| 14 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 15 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 16 | +import jakarta.annotation.Resource; | |
| 17 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 18 | +import org.springframework.validation.annotation.Validated; | |
| 19 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 20 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 21 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 22 | +import org.springframework.web.bind.annotation.RestController; | |
| 23 | + | |
| 24 | +import java.util.List; | |
| 25 | +import java.util.Map; | |
| 26 | +import java.util.Set; | |
| 27 | +import java.util.stream.Stream; | |
| 28 | + | |
| 29 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 30 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; | |
| 31 | + | |
| 32 | +@Tag(name = "管理后台 - HRM 员工异动记录") | |
| 33 | +@RestController | |
| 34 | +@RequestMapping("/hrm/employee/change-record") | |
| 35 | +@Validated | |
| 36 | +public class HrmEmployeeChangeRecordController { | |
| 37 | + | |
| 38 | + @Resource | |
| 39 | + private HrmEmployeeChangeRecordService changeRecordService; | |
| 40 | + @Resource | |
| 41 | + private HrmEmployeeService employeeService; | |
| 42 | + @Resource | |
| 43 | + private DeptApi deptApi; | |
| 44 | + | |
| 45 | + @GetMapping("/list") | |
| 46 | + @Operation(summary = "获得员工异动记录列表") | |
| 47 | + @Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024") | |
| 48 | + @PreAuthorize("@ss.hasPermission('hrm:employee:query')") | |
| 49 | + public CommonResult<List<HrmEmployeeChangeRecordRespVO>> getEmployeeChangeRecordList( | |
| 50 | + @RequestParam("employeeId") Long employeeId) { | |
| 51 | + return success(buildEmployeeChangeRecordRespVOList( | |
| 52 | + changeRecordService.getEmployeeChangeRecordListByEmployeeId(employeeId))); | |
| 53 | + } | |
| 54 | + | |
| 55 | + // ==================== 拼接 VO ==================== | |
| 56 | + | |
| 57 | + private List<HrmEmployeeChangeRecordRespVO> buildEmployeeChangeRecordRespVOList( | |
| 58 | + List<HrmEmployeeChangeRecordDO> changeRecords) { | |
| 59 | + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap( | |
| 60 | + convertSetByFlatMap(changeRecords, changeRecord -> Stream.of(changeRecord.getOldDeptId(), changeRecord.getNewDeptId()))); | |
| 61 | + Map<Long, HrmEmployeeDO> leaderEmployeeMap = employeeService.getEmployeeMap( | |
| 62 | + convertSetByFlatMap(changeRecords, changeRecord -> Stream.of(changeRecord.getOldLeaderEmployeeId(), changeRecord.getNewLeaderEmployeeId()))); | |
| 63 | + return BeanUtils.toBean(changeRecords, HrmEmployeeChangeRecordRespVO.class, changeRecord -> { | |
| 64 | + MapUtils.findAndThen(deptMap, changeRecord.getOldDeptId(), | |
| 65 | + dept -> changeRecord.setOldDeptName(dept.getName())); | |
| 66 | + MapUtils.findAndThen(deptMap, changeRecord.getNewDeptId(), | |
| 67 | + dept -> changeRecord.setNewDeptName(dept.getName())); | |
| 68 | + MapUtils.findAndThen(leaderEmployeeMap, changeRecord.getOldLeaderEmployeeId(), | |
| 69 | + leader -> changeRecord.setOldLeaderEmployeeName(leader.getName())); | |
| 70 | + MapUtils.findAndThen(leaderEmployeeMap, changeRecord.getNewLeaderEmployeeId(), | |
| 71 | + leader -> changeRecord.setNewLeaderEmployeeName(leader.getName())); | |
| 72 | + }); | |
| 73 | + } | |
| 74 | + | |
| 75 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeContactController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 5 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.contact.HrmEmployeeContactRespVO; | |
| 6 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.contact.HrmEmployeeContactSaveReqVO; | |
| 7 | +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeContactDO; | |
| 8 | +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeContactService; | |
| 9 | +import io.swagger.v3.oas.annotations.Operation; | |
| 10 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 11 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 12 | +import jakarta.annotation.Resource; | |
| 13 | +import jakarta.validation.Valid; | |
| 14 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 15 | +import org.springframework.validation.annotation.Validated; | |
| 16 | +import org.springframework.web.bind.annotation.DeleteMapping; | |
| 17 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 18 | +import org.springframework.web.bind.annotation.PostMapping; | |
| 19 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 20 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 21 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 22 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 23 | +import org.springframework.web.bind.annotation.RestController; | |
| 24 | + | |
| 25 | +import java.util.List; | |
| 26 | + | |
| 27 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 28 | + | |
| 29 | +@Tag(name = "管理后台 - HRM 员工联系人") | |
| 30 | +@RestController | |
| 31 | +@RequestMapping("/hrm/employee/contact") | |
| 32 | +@Validated | |
| 33 | +public class HrmEmployeeContactController { | |
| 34 | + | |
| 35 | + @Resource | |
| 36 | + private HrmEmployeeContactService contactService; | |
| 37 | + | |
| 38 | + @GetMapping("/list") | |
| 39 | + @Operation(summary = "获得员工联系人列表") | |
| 40 | + @Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024") | |
| 41 | + @PreAuthorize("@ss.hasPermission('hrm:employee:query')") | |
| 42 | + public CommonResult<List<HrmEmployeeContactRespVO>> getContactList( | |
| 43 | + @RequestParam("employeeId") Long employeeId) { | |
| 44 | + List<HrmEmployeeContactDO> contacts = contactService.getContactListByEmployeeId(employeeId); | |
| 45 | + return success(BeanUtils.toBean(contacts, HrmEmployeeContactRespVO.class)); | |
| 46 | + } | |
| 47 | + | |
| 48 | + @PostMapping("/create") | |
| 49 | + @Operation(summary = "创建员工联系人") | |
| 50 | + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") | |
| 51 | + public CommonResult<Long> createContact(@Valid @RequestBody HrmEmployeeContactSaveReqVO reqVO) { | |
| 52 | + return success(contactService.createContact(reqVO)); | |
| 53 | + } | |
| 54 | + | |
| 55 | + @PutMapping("/update") | |
| 56 | + @Operation(summary = "更新员工联系人") | |
| 57 | + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") | |
| 58 | + public CommonResult<Boolean> updateContact(@Valid @RequestBody HrmEmployeeContactSaveReqVO reqVO) { | |
| 59 | + contactService.updateContact(reqVO); | |
| 60 | + return success(true); | |
| 61 | + } | |
| 62 | + | |
| 63 | + @DeleteMapping("/delete") | |
| 64 | + @Operation(summary = "删除员工联系人") | |
| 65 | + @Parameter(name = "id", description = "联系人编号", required = true, example = "1024") | |
| 66 | + @PreAuthorize("@ss.hasPermission('hrm:employee:delete')") | |
| 67 | + public CommonResult<Boolean> deleteContact(@RequestParam("id") Long id) { | |
| 68 | + contactService.deleteContact(id); | |
| 69 | + return success(true); | |
| 70 | + } | |
| 71 | + | |
| 72 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeContractController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 5 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.contract.HrmEmployeeContractRespVO; | |
| 6 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.contract.HrmEmployeeContractSaveReqVO; | |
| 7 | +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.employment.HrmEmployeeContractDO; | |
| 8 | +import cn.iocoder.yudao.module.hrm.service.employee.employment.HrmEmployeeContractService; | |
| 9 | +import io.swagger.v3.oas.annotations.Operation; | |
| 10 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 11 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 12 | +import jakarta.annotation.Resource; | |
| 13 | +import jakarta.validation.Valid; | |
| 14 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 15 | +import org.springframework.validation.annotation.Validated; | |
| 16 | +import org.springframework.web.bind.annotation.DeleteMapping; | |
| 17 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 18 | +import org.springframework.web.bind.annotation.PostMapping; | |
| 19 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 20 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 21 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 22 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 23 | +import org.springframework.web.bind.annotation.RestController; | |
| 24 | + | |
| 25 | +import java.util.List; | |
| 26 | + | |
| 27 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 28 | + | |
| 29 | +@Tag(name = "管理后台 - HRM 员工合同") | |
| 30 | +@RestController | |
| 31 | +@RequestMapping("/hrm/employee/contract") | |
| 32 | +@Validated | |
| 33 | +public class HrmEmployeeContractController { | |
| 34 | + | |
| 35 | + @Resource | |
| 36 | + private HrmEmployeeContractService contractService; | |
| 37 | + | |
| 38 | + @GetMapping("/list") | |
| 39 | + @Operation(summary = "获得员工合同列表") | |
| 40 | + @Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024") | |
| 41 | + @PreAuthorize("@ss.hasPermission('hrm:employee:query')") | |
| 42 | + public CommonResult<List<HrmEmployeeContractRespVO>> getContractList( | |
| 43 | + @RequestParam("employeeId") Long employeeId) { | |
| 44 | + List<HrmEmployeeContractDO> contracts = contractService.getContractListByEmployeeId(employeeId); | |
| 45 | + return success(BeanUtils.toBean(contracts, HrmEmployeeContractRespVO.class)); | |
| 46 | + } | |
| 47 | + | |
| 48 | + @PostMapping("/create") | |
| 49 | + @Operation(summary = "创建员工合同") | |
| 50 | + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") | |
| 51 | + public CommonResult<Long> createContract(@Valid @RequestBody HrmEmployeeContractSaveReqVO reqVO) { | |
| 52 | + return success(contractService.createContract(reqVO)); | |
| 53 | + } | |
| 54 | + | |
| 55 | + @PutMapping("/update") | |
| 56 | + @Operation(summary = "更新员工合同") | |
| 57 | + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") | |
| 58 | + public CommonResult<Boolean> updateContract(@Valid @RequestBody HrmEmployeeContractSaveReqVO reqVO) { | |
| 59 | + contractService.updateContract(reqVO); | |
| 60 | + return success(true); | |
| 61 | + } | |
| 62 | + | |
| 63 | + @DeleteMapping("/delete") | |
| 64 | + @Operation(summary = "删除员工合同") | |
| 65 | + @Parameter(name = "id", description = "合同编号", required = true, example = "1024") | |
| 66 | + @PreAuthorize("@ss.hasPermission('hrm:employee:delete')") | |
| 67 | + public CommonResult<Boolean> deleteContract(@RequestParam("id") Long id) { | |
| 68 | + contractService.deleteContract(id); | |
| 69 | + return success(true); | |
| 70 | + } | |
| 71 | + | |
| 72 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; | |
| 5 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 6 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 7 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 8 | +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; | |
| 9 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 10 | +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; | |
| 11 | +import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; | |
| 12 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.changerecord.HrmEmployeeConvertToFullTimeReqVO; | |
| 13 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.changerecord.HrmEmployeeDemoteReqVO; | |
| 14 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.changerecord.HrmEmployeePromoteReqVO; | |
| 15 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.changerecord.HrmEmployeeRegularReqVO; | |
| 16 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.changerecord.HrmEmployeeTransferReqVO; | |
| 17 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeCancelQuitReqVO; | |
| 18 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeConfirmEntryReqVO; | |
| 19 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeCreateFromUserReqVO; | |
| 20 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeDeptStatisticsRespVO; | |
| 21 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeImportExcelVO; | |
| 22 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeImportRespVO; | |
| 23 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeListReqVO; | |
| 24 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeePageReqVO; | |
| 25 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeNotifyRespVO; | |
| 26 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeQuitReqVO; | |
| 27 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeRehireReqVO; | |
| 28 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeRespVO; | |
| 29 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeSaveReqVO; | |
| 30 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee.HrmEmployeeStatusCountRespVO; | |
| 31 | +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeDO; | |
| 32 | +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.employment.HrmEmployeeSalaryCardDO; | |
| 33 | +import cn.iocoder.yudao.module.hrm.dal.dataobject.insurance.employee.HrmInsuranceEmployeeInfoDO; | |
| 34 | +import cn.iocoder.yudao.module.hrm.dal.dataobject.recruit.config.HrmRecruitChannelDO; | |
| 35 | +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeEntryStatusEnum; | |
| 36 | +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeStatusEnum; | |
| 37 | +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeStatusTabEnum; | |
| 38 | +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeTypeEnum; | |
| 39 | +import cn.iocoder.yudao.module.hrm.service.employee.employment.HrmEmployeeSalaryCardService; | |
| 40 | +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService; | |
| 41 | +import cn.iocoder.yudao.module.hrm.service.insurance.employee.HrmInsuranceEmployeeInfoService; | |
| 42 | +import cn.iocoder.yudao.module.hrm.service.recruit.config.HrmRecruitChannelService; | |
| 43 | +import cn.iocoder.yudao.module.system.api.dept.DeptApi; | |
| 44 | +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; | |
| 45 | +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | |
| 46 | +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; | |
| 47 | +import cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum; | |
| 48 | +import io.swagger.v3.oas.annotations.Operation; | |
| 49 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 50 | +import io.swagger.v3.oas.annotations.Parameters; | |
| 51 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 52 | +import jakarta.annotation.Resource; | |
| 53 | +import jakarta.servlet.http.HttpServletResponse; | |
| 54 | +import jakarta.validation.Valid; | |
| 55 | +import jakarta.validation.constraints.NotEmpty; | |
| 56 | +import jakarta.validation.constraints.Size; | |
| 57 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 58 | +import org.springframework.validation.annotation.Validated; | |
| 59 | +import org.springframework.web.bind.annotation.DeleteMapping; | |
| 60 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 61 | +import org.springframework.web.bind.annotation.PostMapping; | |
| 62 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 63 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 64 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 65 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 66 | +import org.springframework.web.bind.annotation.RestController; | |
| 67 | +import org.springframework.web.multipart.MultipartFile; | |
| 68 | + | |
| 69 | +import java.io.IOException; | |
| 70 | +import java.time.LocalDate; | |
| 71 | +import java.time.LocalDateTime; | |
| 72 | +import java.util.Arrays; | |
| 73 | +import java.util.Collections; | |
| 74 | +import java.util.List; | |
| 75 | +import java.util.Map; | |
| 76 | +import java.util.Set; | |
| 77 | + | |
| 78 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 79 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 80 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | |
| 81 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getFirst; | |
| 82 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.sum; | |
| 83 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.getYearsBetween; | |
| 84 | + | |
| 85 | +@Tag(name = "管理后台 - HRM 员工档案") | |
| 86 | +@RestController | |
| 87 | +@RequestMapping("/hrm/employee") | |
| 88 | +@Validated | |
| 89 | +public class HrmEmployeeController { | |
| 90 | + | |
| 91 | + @Resource | |
| 92 | + private HrmEmployeeService employeeService; | |
| 93 | + @Resource | |
| 94 | + private HrmRecruitChannelService recruitChannelService; | |
| 95 | + @Resource | |
| 96 | + private HrmInsuranceEmployeeInfoService insuranceEmployeeInfoService; | |
| 97 | + @Resource | |
| 98 | + private HrmEmployeeSalaryCardService employeeSalaryCardService; | |
| 99 | + | |
| 100 | + @Resource | |
| 101 | + private AdminUserApi adminUserApi; | |
| 102 | + @Resource | |
| 103 | + private DeptApi deptApi; | |
| 104 | + | |
| 105 | + @PostMapping("/create") | |
| 106 | + @Operation(summary = "创建员工档案") | |
| 107 | + @PreAuthorize("@ss.hasPermission('hrm:employee:create')") | |
| 108 | + public CommonResult<Long> createEmployee(@Valid @RequestBody HrmEmployeeSaveReqVO createReqVO) { | |
| 109 | + return success(employeeService.createEmployee(createReqVO)); | |
| 110 | + } | |
| 111 | + | |
| 112 | + @PostMapping("/create-list") | |
| 113 | + @Operation(summary = "从未建档后台用户批量创建员工档案") | |
| 114 | + @PreAuthorize("@ss.hasPermission('hrm:employee:create')") | |
| 115 | + public CommonResult<List<Long>> createEmployeeList( | |
| 116 | + @RequestBody @NotEmpty(message = "待创建员工列表不能为空") | |
| 117 | + @Size(max = 100, message = "单次最多创建 100 名员工") | |
| 118 | + List<@Valid HrmEmployeeCreateFromUserReqVO> createReqVOList) { | |
| 119 | + return success(employeeService.createEmployeeList(createReqVOList)); | |
| 120 | + } | |
| 121 | + | |
| 122 | + @GetMapping("/bound-user-id-list") | |
| 123 | + @Operation(summary = "获得已经建立员工档案的后台用户编号列表") | |
| 124 | + @PreAuthorize("@ss.hasPermission('hrm:employee:create')") | |
| 125 | + public CommonResult<List<Long>> getBoundUserIdList() { | |
| 126 | + return success(employeeService.getBoundUserIdList()); | |
| 127 | + } | |
| 128 | + | |
| 129 | + @PostMapping("/send-profile-fill-message") | |
| 130 | + @Operation(summary = "发送填写员工档案通知") | |
| 131 | + @Parameter(name = "ids", description = "员工编号数组", required = true, example = "1024,1025") | |
| 132 | + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") | |
| 133 | + public CommonResult<HrmEmployeeNotifyRespVO> sendEmployeeProfileFillMessage( | |
| 134 | + @RequestParam("ids") @NotEmpty(message = "员工不能为空") | |
| 135 | + @Size(max = 100, message = "单次最多通知 100 名员工") List<Long> ids) { | |
| 136 | + return success(employeeService.sendEmployeeProfileFillMessage(ids)); | |
| 137 | + } | |
| 138 | + | |
| 139 | + @PutMapping("/update") | |
| 140 | + @Operation(summary = "更新员工档案") | |
| 141 | + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") | |
| 142 | + public CommonResult<Boolean> updateEmployee(@Valid @RequestBody HrmEmployeeSaveReqVO updateReqVO) { | |
| 143 | + employeeService.updateEmployee(updateReqVO); | |
| 144 | + return success(true); | |
| 145 | + } | |
| 146 | + | |
| 147 | + @DeleteMapping("/delete") | |
| 148 | + @Operation(summary = "删除员工档案") | |
| 149 | + @Parameter(name = "id", description = "员工档案编号", required = true, example = "1024") | |
| 150 | + @PreAuthorize("@ss.hasPermission('hrm:employee:delete')") | |
| 151 | + public CommonResult<Boolean> deleteEmployee(@RequestParam("id") Long id) { | |
| 152 | + employeeService.deleteEmployee(id); | |
| 153 | + return success(true); | |
| 154 | + } | |
| 155 | + | |
| 156 | + @DeleteMapping("/delete-list") | |
| 157 | + @Operation(summary = "批量删除员工档案") | |
| 158 | + @Parameter(name = "ids", description = "员工档案编号数组", required = true, example = "1024,1025") | |
| 159 | + @PreAuthorize("@ss.hasPermission('hrm:employee:delete')") | |
| 160 | + public CommonResult<Boolean> deleteEmployeeList(@RequestParam("ids") @NotEmpty List<Long> ids) { | |
| 161 | + employeeService.deleteEmployeeList(ids); | |
| 162 | + return success(true); | |
| 163 | + } | |
| 164 | + | |
| 165 | + @GetMapping("/get") | |
| 166 | + @Operation(summary = "获得员工档案") | |
| 167 | + @Parameter(name = "id", description = "员工档案编号", required = true, example = "1024") | |
| 168 | + @PreAuthorize("@ss.hasPermission('hrm:employee:query')") | |
| 169 | + public CommonResult<HrmEmployeeRespVO> getEmployee(@RequestParam("id") Long id) { | |
| 170 | + return success(buildEmployeeRespVO(employeeService.getEmployee(id))); | |
| 171 | + } | |
| 172 | + | |
| 173 | + @PutMapping("/confirm-entry") | |
| 174 | + @Operation(summary = "确认员工入职") | |
| 175 | + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") | |
| 176 | + public CommonResult<Boolean> confirmEmployeeEntry(@Valid @RequestBody HrmEmployeeConfirmEntryReqVO reqVO) { | |
| 177 | + employeeService.confirmEmployeeEntry(reqVO); | |
| 178 | + return success(true); | |
| 179 | + } | |
| 180 | + | |
| 181 | + @PostMapping("/rehire") | |
| 182 | + @Operation(summary = "办理离职员工再入职") | |
| 183 | + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") | |
| 184 | + public CommonResult<Boolean> rehireEmployee(@Valid @RequestBody HrmEmployeeRehireReqVO reqVO) { | |
| 185 | + employeeService.rehireEmployee(reqVO); | |
| 186 | + return success(true); | |
| 187 | + } | |
| 188 | + | |
| 189 | + @PostMapping("/regular") | |
| 190 | + @Operation(summary = "办理员工转正") | |
| 191 | + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") | |
| 192 | + public CommonResult<Boolean> regularEmployee(@Valid @RequestBody HrmEmployeeRegularReqVO reqVO) { | |
| 193 | + employeeService.regularEmployee(reqVO); | |
| 194 | + return success(true); | |
| 195 | + } | |
| 196 | + | |
| 197 | + @PostMapping("/transfer") | |
| 198 | + @Operation(summary = "办理员工调岗") | |
| 199 | + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") | |
| 200 | + public CommonResult<Boolean> transferEmployee(@Valid @RequestBody HrmEmployeeTransferReqVO reqVO) { | |
| 201 | + employeeService.transferEmployee(reqVO); | |
| 202 | + return success(true); | |
| 203 | + } | |
| 204 | + | |
| 205 | + @PostMapping("/promote") | |
| 206 | + @Operation(summary = "办理员工晋升") | |
| 207 | + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") | |
| 208 | + public CommonResult<Boolean> promoteEmployee(@Valid @RequestBody HrmEmployeePromoteReqVO reqVO) { | |
| 209 | + employeeService.promoteEmployee(reqVO); | |
| 210 | + return success(true); | |
| 211 | + } | |
| 212 | + | |
| 213 | + @PostMapping("/demote") | |
| 214 | + @Operation(summary = "办理员工降级") | |
| 215 | + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") | |
| 216 | + public CommonResult<Boolean> demoteEmployee(@Valid @RequestBody HrmEmployeeDemoteReqVO reqVO) { | |
| 217 | + employeeService.demoteEmployee(reqVO); | |
| 218 | + return success(true); | |
| 219 | + } | |
| 220 | + | |
| 221 | + @PostMapping("/convert-to-full-time") | |
| 222 | + @Operation(summary = "办理员工转为全职") | |
| 223 | + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") | |
| 224 | + public CommonResult<Boolean> convertEmployeeToFullTime( | |
| 225 | + @Valid @RequestBody HrmEmployeeConvertToFullTimeReqVO reqVO) { | |
| 226 | + employeeService.convertEmployeeToFullTime(reqVO); | |
| 227 | + return success(true); | |
| 228 | + } | |
| 229 | + | |
| 230 | + @PostMapping("/quit") | |
| 231 | + @Operation(summary = "办理员工离职") | |
| 232 | + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") | |
| 233 | + public CommonResult<Boolean> quitEmployee(@Valid @RequestBody HrmEmployeeQuitReqVO reqVO) { | |
| 234 | + employeeService.quitEmployee(reqVO); | |
| 235 | + return success(true); | |
| 236 | + } | |
| 237 | + | |
| 238 | + @PutMapping("/cancel-quit") | |
| 239 | + @Operation(summary = "取消员工离职") | |
| 240 | + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") | |
| 241 | + public CommonResult<Boolean> cancelEmployeeQuit(@Valid @RequestBody HrmEmployeeCancelQuitReqVO reqVO) { | |
| 242 | + employeeService.cancelEmployeeQuit(reqVO); | |
| 243 | + return success(true); | |
| 244 | + } | |
| 245 | + | |
| 246 | + @GetMapping("/page") | |
| 247 | + @Operation(summary = "获得员工档案分页") | |
| 248 | + @PreAuthorize("@ss.hasPermission('hrm:employee:query')") | |
| 249 | + public CommonResult<PageResult<HrmEmployeeRespVO>> getEmployeePage(@Validated HrmEmployeePageReqVO pageReqVO) { | |
| 250 | + PageResult<HrmEmployeeDO> pageResult = employeeService.getEmployeePage(pageReqVO); | |
| 251 | + return success(new PageResult<>(buildEmployeeRespVOList(pageResult.getList()), pageResult.getTotal())); | |
| 252 | + } | |
| 253 | + | |
| 254 | + @GetMapping("/list") | |
| 255 | + @Operation(summary = "获得员工列表", description = "用于表单回显已选员工") | |
| 256 | + @Parameter(name = "ids", description = "员工编号列表", required = true, example = "[1024]") | |
| 257 | + @PreAuthorize("@ss.hasPermission('hrm:employee:query')") | |
| 258 | + public CommonResult<List<HrmEmployeeRespVO>> getEmployeeList(@RequestParam("ids") @NotEmpty List<Long> ids) { | |
| 259 | + return success(buildEmployeeRespVOList(employeeService.getEmployeeListByIds(ids))); | |
| 260 | + } | |
| 261 | + | |
| 262 | + @GetMapping("/simple-page") | |
| 263 | + @Operation(summary = "获得员工精简分页", description = "只返回员工选择器需要的非敏感字段") | |
| 264 | + public CommonResult<PageResult<HrmEmployeeRespVO>> getEmployeeSimplePage( | |
| 265 | + @Validated HrmEmployeePageReqVO pageReqVO) { | |
| 266 | + PageResult<HrmEmployeeDO> pageResult = employeeService.getEmployeePage(pageReqVO); | |
| 267 | + return success(new PageResult<>(buildEmployeeSimpleRespVOList(pageResult.getList()), pageResult.getTotal())); | |
| 268 | + } | |
| 269 | + | |
| 270 | + @GetMapping("/simple-list") | |
| 271 | + @Operation(summary = "获得员工精简列表", description = "用于员工选择器回显已选员工") | |
| 272 | + @Parameter(name = "ids", description = "员工编号列表", required = true, example = "[1024]") | |
| 273 | + public CommonResult<List<HrmEmployeeRespVO>> getEmployeeSimpleList( | |
| 274 | + @RequestParam("ids") @NotEmpty List<Long> ids) { | |
| 275 | + return success(buildEmployeeSimpleRespVOList(employeeService.getEmployeeListByIds(ids))); | |
| 276 | + } | |
| 277 | + | |
| 278 | + @GetMapping("/export-excel") | |
| 279 | + @Operation(summary = "导出员工档案") | |
| 280 | + @PreAuthorize("@ss.hasPermission('hrm:employee:export')") | |
| 281 | + @ApiAccessLog(operateType = OperateTypeEnum.EXPORT) | |
| 282 | + public void exportEmployee(@Validated HrmEmployeePageReqVO exportReqVO, | |
| 283 | + HttpServletResponse response) throws IOException { | |
| 284 | + exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); | |
| 285 | + List<HrmEmployeeRespVO> list = buildEmployeeExportRespVOList(employeeService.getEmployeeList( | |
| 286 | + BeanUtils.toBean(exportReqVO, HrmEmployeeListReqVO.class))); | |
| 287 | + ExcelUtils.write(response, "员工档案.xlsx", "数据", HrmEmployeeRespVO.class, list); | |
| 288 | + } | |
| 289 | + | |
| 290 | + @GetMapping("/get-import-template") | |
| 291 | + @Operation(summary = "获得员工档案导入模板") | |
| 292 | + @PreAuthorize("@ss.hasPermission('hrm:employee:import')") | |
| 293 | + @ApiAccessLog(operateType = OperateTypeEnum.EXPORT) | |
| 294 | + public void importTemplate(HttpServletResponse response) throws IOException { | |
| 295 | + List<HrmEmployeeImportExcelVO> list = Collections.singletonList( | |
| 296 | + HrmEmployeeImportExcelVO.builder() | |
| 297 | + .name("张三").jobNumber("HRM001").mobile("15601691300") | |
| 298 | + .country("中国").nation("汉族").idType(5).idNumber("OTHER-19900101") | |
| 299 | + .sex(1).email("hrm@example.com").nativePlace("浙江杭州") | |
| 300 | + .birthday(LocalDateTime.of(1990, 1, 1, 0, 0)) | |
| 301 | + .address("杭州市西湖区").highestEducation(8) | |
| 302 | + .deptId(1L).leaderJobNumber("HRM000") | |
| 303 | + .postName("Java 工程师").postLevel("P6") | |
| 304 | + .entryStatus(HrmEmployeeEntryStatusEnum.ACTIVE.getStatus()) | |
| 305 | + .status(HrmEmployeeStatusEnum.REGULAR.getStatus()) | |
| 306 | + .type(HrmEmployeeTypeEnum.FORMAL.getType()).probation(3) | |
| 307 | + .entryTime(LocalDateTime.of(2026, 7, 9, 9, 0)) | |
| 308 | + .regularTime(LocalDateTime.of(2026, 10, 9, 9, 0)) | |
| 309 | + .workCity("杭州").workAddress("西湖区").workDetailAddress("文三路") | |
| 310 | + .companyAgeStartTime(LocalDateTime.of(2026, 7, 9, 9, 0)) | |
| 311 | + .userMobile("15601691301") | |
| 312 | + .bankCardNumber("622202600001").bankAreaId(330100).bankName("招商银行") | |
| 313 | + .bankBranchName("杭州高新支行").firstSocialSecurity(false) | |
| 314 | + .firstAccumulationFund(false).socialSecurityNumber("SB20260001") | |
| 315 | + .accumulationFundNumber("GJJ20260001") | |
| 316 | + .socialSecurityStartMonth(LocalDateTime.of(2026, 7, 1, 0, 0)) | |
| 317 | + .schemeName("杭州标准参保方案") | |
| 318 | + .remark("示例员工").build() | |
| 319 | + ); | |
| 320 | + ExcelUtils.write(response, "员工档案导入模板.xlsx", "员工列表", HrmEmployeeImportExcelVO.class, list); | |
| 321 | + } | |
| 322 | + | |
| 323 | + @PostMapping("/import") | |
| 324 | + @Operation(summary = "导入员工档案") | |
| 325 | + @Parameters({ | |
| 326 | + @Parameter(name = "file", description = "Excel 文件", required = true), | |
| 327 | + @Parameter(name = "duplicateStrategy", description = "重复员工处理策略:1 跳过,2 覆盖,3 判失败", example = "3") | |
| 328 | + }) | |
| 329 | + @PreAuthorize("@ss.hasPermission('hrm:employee:import')") | |
| 330 | + @ApiAccessLog(operateType = OperateTypeEnum.IMPORT) | |
| 331 | + public CommonResult<HrmEmployeeImportRespVO> importExcel( | |
| 332 | + @RequestParam("file") MultipartFile file, | |
| 333 | + @RequestParam(value = "duplicateStrategy", required = false, defaultValue = "3") Integer duplicateStrategy) | |
| 334 | + throws Exception { | |
| 335 | + List<HrmEmployeeImportExcelVO> list = ExcelUtils.read(file, HrmEmployeeImportExcelVO.class); | |
| 336 | + return success(employeeService.importEmployeeList(list, duplicateStrategy)); | |
| 337 | + } | |
| 338 | + | |
| 339 | + @GetMapping("/status-count") | |
| 340 | + @Operation(summary = "获得员工状态统计") | |
| 341 | + @PreAuthorize("@ss.hasPermission('hrm:employee:query')") | |
| 342 | + public CommonResult<List<HrmEmployeeStatusCountRespVO>> getEmployeeStatusCount( | |
| 343 | + @Validated HrmEmployeePageReqVO pageReqVO) { | |
| 344 | + Map<Integer, Long> countMap = employeeService.getEmployeeStatusCount(pageReqVO); | |
| 345 | + return success(convertList(Arrays.asList(HrmEmployeeStatusTabEnum.values()), | |
| 346 | + status -> new HrmEmployeeStatusCountRespVO(status.getStatus(), | |
| 347 | + countMap.getOrDefault(status.getStatus(), 0L)))); | |
| 348 | + } | |
| 349 | + | |
| 350 | + @GetMapping("/dept-statistics") | |
| 351 | + @Operation(summary = "获得员工部门统计") | |
| 352 | + @PreAuthorize("@ss.hasPermission('hrm:employee:query')") | |
| 353 | + public CommonResult<List<HrmEmployeeDeptStatisticsRespVO>> getEmployeeDeptStatistics() { | |
| 354 | + Map<Long, Map<Integer, Long>> countMap = employeeService.getEmployeeCountMapByDeptAndType(); | |
| 355 | + return success(convertList(countMap.entrySet(), entry -> { | |
| 356 | + Map<Integer, Long> typeCountMap = entry.getValue(); | |
| 357 | + long activeCount = sum(typeCountMap.values(), Long::longValue); | |
| 358 | + return new HrmEmployeeDeptStatisticsRespVO(entry.getKey(), activeCount, | |
| 359 | + typeCountMap.getOrDefault(HrmEmployeeTypeEnum.FORMAL.getType(), 0L), | |
| 360 | + typeCountMap.getOrDefault(HrmEmployeeTypeEnum.INFORMAL.getType(), 0L)); | |
| 361 | + })); | |
| 362 | + } | |
| 363 | + | |
| 364 | + // ==================== 拼接 VO ==================== | |
| 365 | + | |
| 366 | + private HrmEmployeeRespVO buildEmployeeRespVO(HrmEmployeeDO employee) { | |
| 367 | + if (employee == null) { | |
| 368 | + return null; | |
| 369 | + } | |
| 370 | + return getFirst(buildEmployeeRespVOList(Collections.singletonList(employee))); | |
| 371 | + } | |
| 372 | + | |
| 373 | + private List<HrmEmployeeRespVO> buildEmployeeRespVOList(List<HrmEmployeeDO> list) { | |
| 374 | + if (CollUtil.isEmpty(list)) { | |
| 375 | + return Collections.emptyList(); | |
| 376 | + } | |
| 377 | + // 1.1 后台用户信息 | |
| 378 | + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap( | |
| 379 | + convertSet(list, HrmEmployeeDO::getUserId)); | |
| 380 | + // 1.2 部门信息 | |
| 381 | + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(list, HrmEmployeeDO::getDeptId)); | |
| 382 | + // 1.3 直属上级员工信息 | |
| 383 | + Map<Long, HrmEmployeeDO> leaderEmployeeMap = employeeService.getEmployeeMap( | |
| 384 | + convertSet(list, HrmEmployeeDO::getLeaderEmployeeId)); | |
| 385 | + // 1.4 招聘渠道信息 | |
| 386 | + Map<Long, HrmRecruitChannelDO> channelMap = recruitChannelService.getRecruitChannelMap( | |
| 387 | + convertSet(list, HrmEmployeeDO::getChannelId)); | |
| 388 | + // 1.5 工资卡和社保资料 | |
| 389 | + Set<Long> employeeIds = convertSet(list, HrmEmployeeDO::getId); | |
| 390 | + Map<Long, HrmEmployeeSalaryCardDO> salaryCardMap = | |
| 391 | + employeeSalaryCardService.getSalaryCardMap(employeeIds); | |
| 392 | + Map<Long, HrmInsuranceEmployeeInfoDO> insuranceEmployeeInfoMap = | |
| 393 | + insuranceEmployeeInfoService.getInsuranceEmployeeInfoMap(employeeIds); | |
| 394 | + | |
| 395 | + // 2. 拼接响应 | |
| 396 | + LocalDate today = LocalDate.now(); | |
| 397 | + return BeanUtils.toBean(list, HrmEmployeeRespVO.class, vo -> { | |
| 398 | + MapUtils.findAndThen(userMap, vo.getUserId(), user -> vo.setUserNickname(user.getNickname())); | |
| 399 | + MapUtils.findAndThen(deptMap, vo.getDeptId(), dept -> vo.setDeptName(dept.getName())); | |
| 400 | + MapUtils.findAndThen(leaderEmployeeMap, vo.getLeaderEmployeeId(), | |
| 401 | + leader -> vo.setLeaderEmployeeName(leader.getName())); | |
| 402 | + MapUtils.findAndThen(channelMap, vo.getChannelId(), | |
| 403 | + channel -> vo.setChannelName(channel.getName())); | |
| 404 | + MapUtils.findAndThen(salaryCardMap, vo.getId(), salaryCard -> vo | |
| 405 | + .setSalaryCardNumber(salaryCard.getBankCardNumber()) | |
| 406 | + .setSalaryCardAreaId(salaryCard.getBankAreaId()) | |
| 407 | + .setSalaryCardAreaName(AreaUtils.format(salaryCard.getBankAreaId())) | |
| 408 | + .setSalaryCardBankName(salaryCard.getBankName()) | |
| 409 | + .setSalaryCardBankBranchName(salaryCard.getBankBranchName())); | |
| 410 | + MapUtils.findAndThen(insuranceEmployeeInfoMap, vo.getId(), socialSecurity -> vo | |
| 411 | + .setSocialSecurityNumber(socialSecurity.getSocialSecurityNumber()) | |
| 412 | + .setAccumulationFundNumber(socialSecurity.getAccumulationFundNumber())); | |
| 413 | + if (vo.getBirthday() != null) { | |
| 414 | + vo.setAge(getYearsBetween(vo.getBirthday().toLocalDate(), today)); | |
| 415 | + } | |
| 416 | + if (vo.getCompanyAgeStartTime() != null) { | |
| 417 | + vo.setCompanyAge(getYearsBetween(vo.getCompanyAgeStartTime().toLocalDate(), today)); | |
| 418 | + } | |
| 419 | + }); | |
| 420 | + } | |
| 421 | + | |
| 422 | + private List<HrmEmployeeRespVO> buildEmployeeSimpleRespVOList(List<HrmEmployeeDO> list) { | |
| 423 | + if (CollUtil.isEmpty(list)) { | |
| 424 | + return Collections.emptyList(); | |
| 425 | + } | |
| 426 | + // 1. 查询部门和直属上级信息 | |
| 427 | + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(list, HrmEmployeeDO::getDeptId)); | |
| 428 | + Map<Long, HrmEmployeeDO> leaderEmployeeMap = employeeService.getEmployeeMap( | |
| 429 | + convertSet(list, HrmEmployeeDO::getLeaderEmployeeId)); | |
| 430 | + | |
| 431 | + // 2. 拼接精简响应 | |
| 432 | + return convertList(list, employee -> { | |
| 433 | + HrmEmployeeRespVO vo = new HrmEmployeeRespVO() | |
| 434 | + .setId(employee.getId()).setName(employee.getName()) | |
| 435 | + .setJobNumber(employee.getJobNumber()).setMobile(employee.getMobile()) | |
| 436 | + .setDeptId(employee.getDeptId()).setLeaderEmployeeId(employee.getLeaderEmployeeId()) | |
| 437 | + .setPostName(employee.getPostName()).setPostLevel(employee.getPostLevel()) | |
| 438 | + .setEntryStatus(employee.getEntryStatus()).setStatus(employee.getStatus()); | |
| 439 | + MapUtils.findAndThen(deptMap, employee.getDeptId(), dept -> vo.setDeptName(dept.getName())); | |
| 440 | + MapUtils.findAndThen(leaderEmployeeMap, employee.getLeaderEmployeeId(), | |
| 441 | + leader -> vo.setLeaderEmployeeName(leader.getName())); | |
| 442 | + return vo; | |
| 443 | + }); | |
| 444 | + } | |
| 445 | + | |
| 446 | + private List<HrmEmployeeRespVO> buildEmployeeExportRespVOList(List<HrmEmployeeDO> list) { | |
| 447 | + if (CollUtil.isEmpty(list)) { | |
| 448 | + return Collections.emptyList(); | |
| 449 | + } | |
| 450 | + // 1. 查询导出展示信息,不加载工资卡和社保等敏感聚合 | |
| 451 | + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(list, HrmEmployeeDO::getUserId)); | |
| 452 | + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(list, HrmEmployeeDO::getDeptId)); | |
| 453 | + Map<Long, HrmEmployeeDO> leaderEmployeeMap = employeeService.getEmployeeMap( | |
| 454 | + convertSet(list, HrmEmployeeDO::getLeaderEmployeeId)); | |
| 455 | + Map<Long, HrmRecruitChannelDO> channelMap = recruitChannelService.getRecruitChannelMap( | |
| 456 | + convertSet(list, HrmEmployeeDO::getChannelId)); | |
| 457 | + | |
| 458 | + // 2. 拼接安全导出字段 | |
| 459 | + return convertList(list, employee -> { | |
| 460 | + HrmEmployeeRespVO vo = new HrmEmployeeRespVO() | |
| 461 | + .setId(employee.getId()).setName(employee.getName()) | |
| 462 | + .setJobNumber(employee.getJobNumber()).setMobile(employee.getMobile()) | |
| 463 | + .setPostName(employee.getPostName()).setPostLevel(employee.getPostLevel()) | |
| 464 | + .setEntryStatus(employee.getEntryStatus()).setStatus(employee.getStatus()).setType(employee.getType()) | |
| 465 | + .setEntryTime(employee.getEntryTime()).setRegularTime(employee.getRegularTime()) | |
| 466 | + .setLeaveTime(employee.getLeaveTime()) | |
| 467 | + .setWorkCity(employee.getWorkCity()).setWorkAddress(employee.getWorkAddress()); | |
| 468 | + MapUtils.findAndThen(userMap, employee.getUserId(), user -> vo.setUserNickname(user.getNickname())); | |
| 469 | + MapUtils.findAndThen(deptMap, employee.getDeptId(), dept -> vo.setDeptName(dept.getName())); | |
| 470 | + MapUtils.findAndThen(leaderEmployeeMap, employee.getLeaderEmployeeId(), | |
| 471 | + leader -> vo.setLeaderEmployeeName(leader.getName())); | |
| 472 | + MapUtils.findAndThen(channelMap, employee.getChannelId(), channel -> vo.setChannelName(channel.getName())); | |
| 473 | + return vo; | |
| 474 | + }); | |
| 475 | + } | |
| 476 | + | |
| 477 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeEducationExperienceController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 5 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.educationexperience.HrmEmployeeEducationExperienceRespVO; | |
| 6 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.educationexperience.HrmEmployeeEducationExperienceSaveReqVO; | |
| 7 | +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.experience.HrmEmployeeEducationExperienceDO; | |
| 8 | +import cn.iocoder.yudao.module.hrm.service.employee.experience.HrmEmployeeEducationExperienceService; | |
| 9 | +import io.swagger.v3.oas.annotations.Operation; | |
| 10 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 11 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 12 | +import jakarta.annotation.Resource; | |
| 13 | +import jakarta.validation.Valid; | |
| 14 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 15 | +import org.springframework.validation.annotation.Validated; | |
| 16 | +import org.springframework.web.bind.annotation.DeleteMapping; | |
| 17 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 18 | +import org.springframework.web.bind.annotation.PostMapping; | |
| 19 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 20 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 21 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 22 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 23 | +import org.springframework.web.bind.annotation.RestController; | |
| 24 | + | |
| 25 | +import java.util.List; | |
| 26 | + | |
| 27 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 28 | + | |
| 29 | +@Tag(name = "管理后台 - HRM 员工教育经历") | |
| 30 | +@RestController | |
| 31 | +@RequestMapping("/hrm/employee/education-experience") | |
| 32 | +@Validated | |
| 33 | +public class HrmEmployeeEducationExperienceController { | |
| 34 | + | |
| 35 | + @Resource | |
| 36 | + private HrmEmployeeEducationExperienceService educationExperienceService; | |
| 37 | + | |
| 38 | + @GetMapping("/list") | |
| 39 | + @Operation(summary = "获得员工教育经历列表") | |
| 40 | + @Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024") | |
| 41 | + @PreAuthorize("@ss.hasPermission('hrm:employee:query')") | |
| 42 | + public CommonResult<List<HrmEmployeeEducationExperienceRespVO>> getEducationExperienceList( | |
| 43 | + @RequestParam("employeeId") Long employeeId) { | |
| 44 | + List<HrmEmployeeEducationExperienceDO> educationExperiences = | |
| 45 | + educationExperienceService.getEducationExperienceListByEmployeeId(employeeId); | |
| 46 | + return success(BeanUtils.toBean(educationExperiences, HrmEmployeeEducationExperienceRespVO.class)); | |
| 47 | + } | |
| 48 | + | |
| 49 | + @PostMapping("/create") | |
| 50 | + @Operation(summary = "创建员工教育经历") | |
| 51 | + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") | |
| 52 | + public CommonResult<Long> createEducationExperience( | |
| 53 | + @Valid @RequestBody HrmEmployeeEducationExperienceSaveReqVO reqVO) { | |
| 54 | + return success(educationExperienceService.createEducationExperience(reqVO)); | |
| 55 | + } | |
| 56 | + | |
| 57 | + @PutMapping("/update") | |
| 58 | + @Operation(summary = "更新员工教育经历") | |
| 59 | + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") | |
| 60 | + public CommonResult<Boolean> updateEducationExperience( | |
| 61 | + @Valid @RequestBody HrmEmployeeEducationExperienceSaveReqVO reqVO) { | |
| 62 | + educationExperienceService.updateEducationExperience(reqVO); | |
| 63 | + return success(true); | |
| 64 | + } | |
| 65 | + | |
| 66 | + @DeleteMapping("/delete") | |
| 67 | + @Operation(summary = "删除员工教育经历") | |
| 68 | + @Parameter(name = "id", description = "教育经历编号", required = true, example = "1024") | |
| 69 | + @PreAuthorize("@ss.hasPermission('hrm:employee:delete')") | |
| 70 | + public CommonResult<Boolean> deleteEducationExperience(@RequestParam("id") Long id) { | |
| 71 | + educationExperienceService.deleteEducationExperience(id); | |
| 72 | + return success(true); | |
| 73 | + } | |
| 74 | + | |
| 75 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeFieldConfigController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.config.HrmEmployeeArchiveFieldConfigSaveReqVO; | |
| 5 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.config.HrmEmployeeCreateFieldConfigSaveReqVO; | |
| 6 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.config.HrmEmployeeFieldConfigRespVO; | |
| 7 | +import cn.iocoder.yudao.module.hrm.service.employee.config.HrmEmployeeFieldConfigService; | |
| 8 | +import io.swagger.v3.oas.annotations.Operation; | |
| 9 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 10 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 11 | +import jakarta.annotation.Resource; | |
| 12 | +import jakarta.validation.Valid; | |
| 13 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 14 | +import org.springframework.validation.annotation.Validated; | |
| 15 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 16 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 17 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 18 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 19 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 20 | +import org.springframework.web.bind.annotation.RestController; | |
| 21 | + | |
| 22 | +import java.util.List; | |
| 23 | + | |
| 24 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 25 | + | |
| 26 | +@Tag(name = "管理后台 - HRM 员工字段配置") | |
| 27 | +@RestController | |
| 28 | +@RequestMapping("/hrm/employee/config") | |
| 29 | +@Validated | |
| 30 | +public class HrmEmployeeFieldConfigController { | |
| 31 | + | |
| 32 | + @Resource | |
| 33 | + private HrmEmployeeFieldConfigService employeeFieldConfigService; | |
| 34 | + | |
| 35 | + @GetMapping("/create-field/list") | |
| 36 | + @Operation(summary = "获得新建员工字段配置") | |
| 37 | + @Parameter(name = "entryStatus", description = "入职状态:1 在职,2 待入职", required = true, example = "1") | |
| 38 | + @PreAuthorize("@ss.hasPermission('hrm:employee:config:query') or @ss.hasPermission('hrm:employee:create')") | |
| 39 | + public CommonResult<List<HrmEmployeeFieldConfigRespVO>> getEmployeeCreateFieldConfigList( | |
| 40 | + @RequestParam("entryStatus") Integer entryStatus) { | |
| 41 | + return success(employeeFieldConfigService.getEmployeeCreateFieldConfigList(entryStatus)); | |
| 42 | + } | |
| 43 | + | |
| 44 | + @PutMapping("/create-field/save") | |
| 45 | + @Operation(summary = "保存新建员工字段配置") | |
| 46 | + @PreAuthorize("@ss.hasPermission('hrm:employee:config:update')") | |
| 47 | + public CommonResult<Boolean> saveEmployeeCreateFieldConfig( | |
| 48 | + @Valid @RequestBody HrmEmployeeCreateFieldConfigSaveReqVO reqVO) { | |
| 49 | + employeeFieldConfigService.saveEmployeeCreateFieldConfig(reqVO); | |
| 50 | + return success(true); | |
| 51 | + } | |
| 52 | + | |
| 53 | + @GetMapping("/archive-field/list") | |
| 54 | + @Operation(summary = "获得员工档案字段配置") | |
| 55 | + @PreAuthorize("@ss.hasPermission('hrm:employee:config:query')") | |
| 56 | + public CommonResult<List<HrmEmployeeFieldConfigRespVO>> getEmployeeArchiveFieldConfigList() { | |
| 57 | + return success(employeeFieldConfigService.getEmployeeArchiveFieldConfigList()); | |
| 58 | + } | |
| 59 | + | |
| 60 | + @PutMapping("/archive-field/save") | |
| 61 | + @Operation(summary = "保存员工档案字段配置") | |
| 62 | + @PreAuthorize("@ss.hasPermission('hrm:employee:config:update')") | |
| 63 | + public CommonResult<Boolean> saveEmployeeArchiveFieldConfig( | |
| 64 | + @Valid @RequestBody HrmEmployeeArchiveFieldConfigSaveReqVO reqVO) { | |
| 65 | + employeeFieldConfigService.saveEmployeeArchiveFieldConfig(reqVO); | |
| 66 | + return success(true); | |
| 67 | + } | |
| 68 | + | |
| 69 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeFileController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 5 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.file.HrmEmployeeFileRespVO; | |
| 6 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.file.HrmEmployeeFileSaveReqVO; | |
| 7 | +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.info.HrmEmployeeFileDO; | |
| 8 | +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeFileService; | |
| 9 | +import io.swagger.v3.oas.annotations.Operation; | |
| 10 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 11 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 12 | +import jakarta.annotation.Resource; | |
| 13 | +import jakarta.validation.Valid; | |
| 14 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 15 | +import org.springframework.validation.annotation.Validated; | |
| 16 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 17 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 18 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 19 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 20 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 21 | +import org.springframework.web.bind.annotation.RestController; | |
| 22 | + | |
| 23 | +import java.util.List; | |
| 24 | + | |
| 25 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 26 | + | |
| 27 | +@Tag(name = "管理后台 - HRM 员工材料附件") | |
| 28 | +@RestController | |
| 29 | +@RequestMapping("/hrm/employee/file") | |
| 30 | +@Validated | |
| 31 | +public class HrmEmployeeFileController { | |
| 32 | + | |
| 33 | + @Resource | |
| 34 | + private HrmEmployeeFileService employeeFileService; | |
| 35 | + | |
| 36 | + @GetMapping("/list") | |
| 37 | + @Operation(summary = "获得员工材料附件列表") | |
| 38 | + @Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024") | |
| 39 | + @PreAuthorize("@ss.hasPermission('hrm:employee:query')") | |
| 40 | + public CommonResult<List<HrmEmployeeFileRespVO>> getEmployeeFileList( | |
| 41 | + @RequestParam("employeeId") Long employeeId) { | |
| 42 | + List<HrmEmployeeFileDO> employeeFiles = employeeFileService.getEmployeeFileList(employeeId); | |
| 43 | + return success(BeanUtils.toBean(employeeFiles, HrmEmployeeFileRespVO.class)); | |
| 44 | + } | |
| 45 | + | |
| 46 | + @PutMapping("/save") | |
| 47 | + @Operation(summary = "保存员工材料附件") | |
| 48 | + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") | |
| 49 | + public CommonResult<Boolean> saveEmployeeFiles(@Valid @RequestBody HrmEmployeeFileSaveReqVO reqVO) { | |
| 50 | + employeeFileService.saveEmployeeFiles(reqVO); | |
| 51 | + return success(true); | |
| 52 | + } | |
| 53 | + | |
| 54 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeePersonalNoteController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.personalnote.HrmEmployeePersonalNoteCreateReqVO; | |
| 5 | +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeePersonalNoteService; | |
| 6 | +import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService; | |
| 7 | +import io.swagger.v3.oas.annotations.Operation; | |
| 8 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 9 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 10 | +import jakarta.annotation.Resource; | |
| 11 | +import jakarta.validation.Valid; | |
| 12 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 13 | +import org.springframework.validation.annotation.Validated; | |
| 14 | +import org.springframework.web.bind.annotation.DeleteMapping; | |
| 15 | +import org.springframework.web.bind.annotation.PostMapping; | |
| 16 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 17 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 18 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 19 | +import org.springframework.web.bind.annotation.RestController; | |
| 20 | + | |
| 21 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 22 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | |
| 23 | + | |
| 24 | +@Tag(name = "管理后台 - HRM 员工个人备忘") | |
| 25 | +@RestController | |
| 26 | +@RequestMapping("/hrm/employee/personal-note") | |
| 27 | +@Validated | |
| 28 | +public class HrmEmployeePersonalNoteController { | |
| 29 | + | |
| 30 | + @Resource | |
| 31 | + private HrmEmployeePersonalNoteService personalNoteService; | |
| 32 | + @Resource | |
| 33 | + private HrmEmployeeService employeeService; | |
| 34 | + | |
| 35 | + @PostMapping("/create") | |
| 36 | + @Operation(summary = "创建员工个人备忘") | |
| 37 | + @PreAuthorize("@ss.hasPermission('hrm:employee:personal-note:create')") | |
| 38 | + public CommonResult<Long> createPersonalNote( | |
| 39 | + @Valid @RequestBody HrmEmployeePersonalNoteCreateReqVO reqVO) { | |
| 40 | + Long employeeId = employeeService.validateEmployeeBySelf(getLoginUserId()).getId(); | |
| 41 | + return success(personalNoteService.createPersonalNote(employeeId, reqVO)); | |
| 42 | + } | |
| 43 | + | |
| 44 | + @DeleteMapping("/delete") | |
| 45 | + @Operation(summary = "删除员工个人备忘") | |
| 46 | + @Parameter(name = "id", description = "员工个人备忘编号", required = true, example = "1024") | |
| 47 | + @PreAuthorize("@ss.hasPermission('hrm:employee:personal-note:delete')") | |
| 48 | + public CommonResult<Boolean> deletePersonalNote(@RequestParam("id") Long id) { | |
| 49 | + Long employeeId = employeeService.validateEmployeeBySelf(getLoginUserId()).getId(); | |
| 50 | + personalNoteService.deletePersonalNote(employeeId, id); | |
| 51 | + return success(true); | |
| 52 | + } | |
| 53 | + | |
| 54 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeQuitInfoController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 5 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.quitinfo.HrmEmployeeQuitInfoRespVO; | |
| 6 | +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.employment.HrmEmployeeQuitInfoDO; | |
| 7 | +import cn.iocoder.yudao.module.hrm.service.employee.employment.HrmEmployeeQuitInfoService; | |
| 8 | +import io.swagger.v3.oas.annotations.Operation; | |
| 9 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 10 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 11 | +import jakarta.annotation.Resource; | |
| 12 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 13 | +import org.springframework.validation.annotation.Validated; | |
| 14 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 15 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 16 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 17 | +import org.springframework.web.bind.annotation.RestController; | |
| 18 | + | |
| 19 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 20 | + | |
| 21 | +@Tag(name = "管理后台 - HRM 员工离职信息") | |
| 22 | +@RestController | |
| 23 | +@RequestMapping("/hrm/employee/quit-info") | |
| 24 | +@Validated | |
| 25 | +public class HrmEmployeeQuitInfoController { | |
| 26 | + | |
| 27 | + @Resource | |
| 28 | + private HrmEmployeeQuitInfoService quitInfoService; | |
| 29 | + | |
| 30 | + @GetMapping("/get") | |
| 31 | + @Operation(summary = "获得员工离职信息") | |
| 32 | + @Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024") | |
| 33 | + @PreAuthorize("@ss.hasPermission('hrm:employee:query')") | |
| 34 | + public CommonResult<HrmEmployeeQuitInfoRespVO> getQuitInfo( | |
| 35 | + @RequestParam("employeeId") Long employeeId) { | |
| 36 | + HrmEmployeeQuitInfoDO quitInfo = quitInfoService.getQuitInfoByEmployeeId(employeeId); | |
| 37 | + return success(BeanUtils.toBean(quitInfo, HrmEmployeeQuitInfoRespVO.class)); | |
| 38 | + } | |
| 39 | + | |
| 40 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeSalaryCardController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 5 | +import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; | |
| 6 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.salarycard.HrmEmployeeSalaryCardRespVO; | |
| 7 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.salarycard.HrmEmployeeSalaryCardSaveReqVO; | |
| 8 | +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.employment.HrmEmployeeSalaryCardDO; | |
| 9 | +import cn.iocoder.yudao.module.hrm.service.employee.employment.HrmEmployeeSalaryCardService; | |
| 10 | +import io.swagger.v3.oas.annotations.Operation; | |
| 11 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 12 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 13 | +import jakarta.annotation.Resource; | |
| 14 | +import jakarta.validation.Valid; | |
| 15 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 16 | +import org.springframework.validation.annotation.Validated; | |
| 17 | +import org.springframework.web.bind.annotation.DeleteMapping; | |
| 18 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 19 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 20 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 21 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 22 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 23 | +import org.springframework.web.bind.annotation.RestController; | |
| 24 | + | |
| 25 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 26 | + | |
| 27 | +@Tag(name = "管理后台 - HRM 员工工资卡") | |
| 28 | +@RestController | |
| 29 | +@RequestMapping("/hrm/employee/salary-card") | |
| 30 | +@Validated | |
| 31 | +public class HrmEmployeeSalaryCardController { | |
| 32 | + | |
| 33 | + @Resource | |
| 34 | + private HrmEmployeeSalaryCardService salaryCardService; | |
| 35 | + | |
| 36 | + @GetMapping("/get") | |
| 37 | + @Operation(summary = "获得员工工资卡") | |
| 38 | + @Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024") | |
| 39 | + @PreAuthorize("@ss.hasPermission('hrm:employee:query')") | |
| 40 | + public CommonResult<HrmEmployeeSalaryCardRespVO> getEmployeeSalaryCard( | |
| 41 | + @RequestParam("employeeId") Long employeeId) { | |
| 42 | + HrmEmployeeSalaryCardDO salaryCard = salaryCardService.getSalaryCardByEmployeeId(employeeId); | |
| 43 | + if (salaryCard == null) { | |
| 44 | + return success(null); | |
| 45 | + } | |
| 46 | + HrmEmployeeSalaryCardRespVO respVO = BeanUtils.toBean(salaryCard, HrmEmployeeSalaryCardRespVO.class) | |
| 47 | + .setBankAreaName(AreaUtils.format(salaryCard.getBankAreaId())); | |
| 48 | + return success(respVO); | |
| 49 | + } | |
| 50 | + | |
| 51 | + @PutMapping("/save") | |
| 52 | + @Operation(summary = "保存员工工资卡") | |
| 53 | + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") | |
| 54 | + public CommonResult<Long> saveEmployeeSalaryCard( | |
| 55 | + @Valid @RequestBody HrmEmployeeSalaryCardSaveReqVO reqVO) { | |
| 56 | + return success(salaryCardService.saveSalaryCard(reqVO)); | |
| 57 | + } | |
| 58 | + | |
| 59 | + @DeleteMapping("/delete") | |
| 60 | + @Operation(summary = "删除员工工资卡") | |
| 61 | + @Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024") | |
| 62 | + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") | |
| 63 | + public CommonResult<Boolean> deleteEmployeeSalaryCard(@RequestParam("employeeId") Long employeeId) { | |
| 64 | + salaryCardService.deleteSalaryCardByEmployeeId(employeeId); | |
| 65 | + return success(true); | |
| 66 | + } | |
| 67 | + | |
| 68 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeTrainingExperienceController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 5 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.trainingexperience.HrmEmployeeTrainingExperienceRespVO; | |
| 6 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.trainingexperience.HrmEmployeeTrainingExperienceSaveReqVO; | |
| 7 | +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.experience.HrmEmployeeTrainingExperienceDO; | |
| 8 | +import cn.iocoder.yudao.module.hrm.service.employee.experience.HrmEmployeeTrainingExperienceService; | |
| 9 | +import io.swagger.v3.oas.annotations.Operation; | |
| 10 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 11 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 12 | +import jakarta.annotation.Resource; | |
| 13 | +import jakarta.validation.Valid; | |
| 14 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 15 | +import org.springframework.validation.annotation.Validated; | |
| 16 | +import org.springframework.web.bind.annotation.DeleteMapping; | |
| 17 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 18 | +import org.springframework.web.bind.annotation.PostMapping; | |
| 19 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 20 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 21 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 22 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 23 | +import org.springframework.web.bind.annotation.RestController; | |
| 24 | + | |
| 25 | +import java.util.List; | |
| 26 | + | |
| 27 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 28 | + | |
| 29 | +@Tag(name = "管理后台 - HRM 员工培训经历") | |
| 30 | +@RestController | |
| 31 | +@RequestMapping("/hrm/employee/training-experience") | |
| 32 | +@Validated | |
| 33 | +public class HrmEmployeeTrainingExperienceController { | |
| 34 | + | |
| 35 | + @Resource | |
| 36 | + private HrmEmployeeTrainingExperienceService trainingExperienceService; | |
| 37 | + | |
| 38 | + @GetMapping("/list") | |
| 39 | + @Operation(summary = "获得员工培训经历列表") | |
| 40 | + @Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024") | |
| 41 | + @PreAuthorize("@ss.hasPermission('hrm:employee:query')") | |
| 42 | + public CommonResult<List<HrmEmployeeTrainingExperienceRespVO>> getTrainingExperienceList( | |
| 43 | + @RequestParam("employeeId") Long employeeId) { | |
| 44 | + List<HrmEmployeeTrainingExperienceDO> trainingExperiences = | |
| 45 | + trainingExperienceService.getTrainingExperienceListByEmployeeId(employeeId); | |
| 46 | + return success(BeanUtils.toBean(trainingExperiences, HrmEmployeeTrainingExperienceRespVO.class)); | |
| 47 | + } | |
| 48 | + | |
| 49 | + @PostMapping("/create") | |
| 50 | + @Operation(summary = "创建员工培训经历") | |
| 51 | + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") | |
| 52 | + public CommonResult<Long> createTrainingExperience( | |
| 53 | + @Valid @RequestBody HrmEmployeeTrainingExperienceSaveReqVO reqVO) { | |
| 54 | + return success(trainingExperienceService.createTrainingExperience(reqVO)); | |
| 55 | + } | |
| 56 | + | |
| 57 | + @PutMapping("/update") | |
| 58 | + @Operation(summary = "更新员工培训经历") | |
| 59 | + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") | |
| 60 | + public CommonResult<Boolean> updateTrainingExperience( | |
| 61 | + @Valid @RequestBody HrmEmployeeTrainingExperienceSaveReqVO reqVO) { | |
| 62 | + trainingExperienceService.updateTrainingExperience(reqVO); | |
| 63 | + return success(true); | |
| 64 | + } | |
| 65 | + | |
| 66 | + @DeleteMapping("/delete") | |
| 67 | + @Operation(summary = "删除员工培训经历") | |
| 68 | + @Parameter(name = "id", description = "培训经历编号", required = true, example = "1024") | |
| 69 | + @PreAuthorize("@ss.hasPermission('hrm:employee:delete')") | |
| 70 | + public CommonResult<Boolean> deleteTrainingExperience(@RequestParam("id") Long id) { | |
| 71 | + trainingExperienceService.deleteTrainingExperience(id); | |
| 72 | + return success(true); | |
| 73 | + } | |
| 74 | + | |
| 75 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/HrmEmployeeWorkExperienceController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 5 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.workexperience.HrmEmployeeWorkExperienceRespVO; | |
| 6 | +import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.workexperience.HrmEmployeeWorkExperienceSaveReqVO; | |
| 7 | +import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.experience.HrmEmployeeWorkExperienceDO; | |
| 8 | +import cn.iocoder.yudao.module.hrm.service.employee.experience.HrmEmployeeWorkExperienceService; | |
| 9 | +import io.swagger.v3.oas.annotations.Operation; | |
| 10 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 11 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 12 | +import jakarta.annotation.Resource; | |
| 13 | +import jakarta.validation.Valid; | |
| 14 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 15 | +import org.springframework.validation.annotation.Validated; | |
| 16 | +import org.springframework.web.bind.annotation.DeleteMapping; | |
| 17 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 18 | +import org.springframework.web.bind.annotation.PostMapping; | |
| 19 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 20 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 21 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 22 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 23 | +import org.springframework.web.bind.annotation.RestController; | |
| 24 | + | |
| 25 | +import java.util.List; | |
| 26 | + | |
| 27 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 28 | + | |
| 29 | +@Tag(name = "管理后台 - HRM 员工工作经历") | |
| 30 | +@RestController | |
| 31 | +@RequestMapping("/hrm/employee/work-experience") | |
| 32 | +@Validated | |
| 33 | +public class HrmEmployeeWorkExperienceController { | |
| 34 | + | |
| 35 | + @Resource | |
| 36 | + private HrmEmployeeWorkExperienceService workExperienceService; | |
| 37 | + | |
| 38 | + @GetMapping("/list") | |
| 39 | + @Operation(summary = "获得员工工作经历列表") | |
| 40 | + @Parameter(name = "employeeId", description = "员工编号", required = true, example = "1024") | |
| 41 | + @PreAuthorize("@ss.hasPermission('hrm:employee:query')") | |
| 42 | + public CommonResult<List<HrmEmployeeWorkExperienceRespVO>> getWorkExperienceList( | |
| 43 | + @RequestParam("employeeId") Long employeeId) { | |
| 44 | + List<HrmEmployeeWorkExperienceDO> workExperiences = | |
| 45 | + workExperienceService.getWorkExperienceListByEmployeeId(employeeId); | |
| 46 | + return success(BeanUtils.toBean(workExperiences, HrmEmployeeWorkExperienceRespVO.class)); | |
| 47 | + } | |
| 48 | + | |
| 49 | + @PostMapping("/create") | |
| 50 | + @Operation(summary = "创建员工工作经历") | |
| 51 | + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") | |
| 52 | + public CommonResult<Long> createWorkExperience( | |
| 53 | + @Valid @RequestBody HrmEmployeeWorkExperienceSaveReqVO reqVO) { | |
| 54 | + return success(workExperienceService.createWorkExperience(reqVO)); | |
| 55 | + } | |
| 56 | + | |
| 57 | + @PutMapping("/update") | |
| 58 | + @Operation(summary = "更新员工工作经历") | |
| 59 | + @PreAuthorize("@ss.hasPermission('hrm:employee:update')") | |
| 60 | + public CommonResult<Boolean> updateWorkExperience( | |
| 61 | + @Valid @RequestBody HrmEmployeeWorkExperienceSaveReqVO reqVO) { | |
| 62 | + workExperienceService.updateWorkExperience(reqVO); | |
| 63 | + return success(true); | |
| 64 | + } | |
| 65 | + | |
| 66 | + @DeleteMapping("/delete") | |
| 67 | + @Operation(summary = "删除员工工作经历") | |
| 68 | + @Parameter(name = "id", description = "工作经历编号", required = true, example = "1024") | |
| 69 | + @PreAuthorize("@ss.hasPermission('hrm:employee:delete')") | |
| 70 | + public CommonResult<Boolean> deleteWorkExperience(@RequestParam("id") Long id) { | |
| 71 | + workExperienceService.deleteWorkExperience(id); | |
| 72 | + return success(true); | |
| 73 | + } | |
| 74 | + | |
| 75 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/certificate/HrmEmployeeCertificateRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.certificate; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.time.LocalDateTime; | |
| 7 | + | |
| 8 | +@Schema(description = "管理后台 - HRM 员工证书 Response VO") | |
| 9 | +@Data | |
| 10 | +public class HrmEmployeeCertificateRespVO { | |
| 11 | + | |
| 12 | + @Schema(description = "证书编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 13 | + private Long id; | |
| 14 | + | |
| 15 | + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 16 | + private Long employeeId; | |
| 17 | + | |
| 18 | + @Schema(description = "证书名称", example = "高级工程师") | |
| 19 | + private String name; | |
| 20 | + | |
| 21 | + @Schema(description = "证书级别", example = "高级") | |
| 22 | + private String level; | |
| 23 | + | |
| 24 | + @Schema(description = "证书编号", example = "CERT-001") | |
| 25 | + private String no; | |
| 26 | + | |
| 27 | + @Schema(description = "有效起始日期") | |
| 28 | + private LocalDateTime startTime; | |
| 29 | + | |
| 30 | + @Schema(description = "有效结束日期") | |
| 31 | + private LocalDateTime endTime; | |
| 32 | + | |
| 33 | + @Schema(description = "发证机构", example = "人社局") | |
| 34 | + private String issuingAuthority; | |
| 35 | + | |
| 36 | + @Schema(description = "发证日期") | |
| 37 | + private LocalDateTime issuingTime; | |
| 38 | + | |
| 39 | + @Schema(description = "备注") | |
| 40 | + private String remark; | |
| 41 | + | |
| 42 | + @Schema(description = "排序", example = "1") | |
| 43 | + private Integer sort; | |
| 44 | + | |
| 45 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 46 | + private LocalDateTime createTime; | |
| 47 | + | |
| 48 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/certificate/HrmEmployeeCertificateSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.certificate; | |
| 2 | + | |
| 3 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
| 4 | +import com.mzt.logapi.starter.annotation.DiffLogField; | |
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 6 | +import jakarta.validation.constraints.AssertTrue; | |
| 7 | +import jakarta.validation.constraints.NotBlank; | |
| 8 | +import jakarta.validation.constraints.NotNull; | |
| 9 | +import jakarta.validation.constraints.Size; | |
| 10 | +import lombok.Data; | |
| 11 | + | |
| 12 | +import java.time.LocalDateTime; | |
| 13 | + | |
| 14 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.isAfterOrEqual; | |
| 15 | + | |
| 16 | +@Schema(description = "管理后台 - HRM 员工证书保存 Request VO") | |
| 17 | +@Data | |
| 18 | +public class HrmEmployeeCertificateSaveReqVO { | |
| 19 | + | |
| 20 | + @Schema(description = "证书编号", example = "1024") | |
| 21 | + private Long id; | |
| 22 | + | |
| 23 | + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 24 | + @NotNull(message = "员工编号不能为空") | |
| 25 | + private Long employeeId; | |
| 26 | + | |
| 27 | + @Schema(description = "证书名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "高级工程师") | |
| 28 | + @NotBlank(message = "证书名称不能为空") | |
| 29 | + @Size(max = 255, message = "证书名称长度不能超过 255 个字符") | |
| 30 | + @DiffLogField(name = "证书名称") | |
| 31 | + private String name; | |
| 32 | + | |
| 33 | + @Schema(description = "证书级别", example = "高级") | |
| 34 | + @Size(max = 255, message = "证书级别长度不能超过 255 个字符") | |
| 35 | + @DiffLogField(name = "证书级别") | |
| 36 | + private String level; | |
| 37 | + | |
| 38 | + @Schema(description = "证书编号", example = "CERT-001") | |
| 39 | + @Size(max = 255, message = "证书编号长度不能超过 255 个字符") | |
| 40 | + @DiffLogField(name = "证书编号") | |
| 41 | + private String no; | |
| 42 | + | |
| 43 | + @Schema(description = "有效起始日期") | |
| 44 | + @DiffLogField(name = "有效起始日期") | |
| 45 | + private LocalDateTime startTime; | |
| 46 | + | |
| 47 | + @Schema(description = "有效结束日期") | |
| 48 | + @DiffLogField(name = "有效结束日期") | |
| 49 | + private LocalDateTime endTime; | |
| 50 | + | |
| 51 | + @Schema(description = "发证机构", example = "人社局") | |
| 52 | + @Size(max = 255, message = "发证机构长度不能超过 255 个字符") | |
| 53 | + @DiffLogField(name = "发证机构") | |
| 54 | + private String issuingAuthority; | |
| 55 | + | |
| 56 | + @Schema(description = "发证日期") | |
| 57 | + @DiffLogField(name = "发证日期") | |
| 58 | + private LocalDateTime issuingTime; | |
| 59 | + | |
| 60 | + @Schema(description = "备注", example = "专业技术资格证书") | |
| 61 | + @Size(max = 500, message = "备注长度不能超过 500 个字符") | |
| 62 | + @DiffLogField(name = "备注") | |
| 63 | + private String remark; | |
| 64 | + | |
| 65 | + @Schema(description = "排序", example = "1") | |
| 66 | + @DiffLogField(name = "排序") | |
| 67 | + private Integer sort; | |
| 68 | + | |
| 69 | + @AssertTrue(message = "证书有效期的结束日期不能早于开始日期") | |
| 70 | + @JsonIgnore | |
| 71 | + public boolean isTimeRangeValid() { | |
| 72 | + return startTime == null || endTime == null || isAfterOrEqual(endTime, startTime); | |
| 73 | + } | |
| 74 | + | |
| 75 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeeChangeRecordCreateReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.changerecord; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 4 | +import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeChangeReasonEnum; | |
| 5 | +import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeChangeTypeEnum; | |
| 6 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 7 | +import jakarta.validation.constraints.Max; | |
| 8 | +import jakarta.validation.constraints.Min; | |
| 9 | +import jakarta.validation.constraints.NotNull; | |
| 10 | +import jakarta.validation.constraints.Size; | |
| 11 | +import lombok.Data; | |
| 12 | + | |
| 13 | +import java.time.LocalDateTime; | |
| 14 | + | |
| 15 | +@Schema(description = "管理后台 - HRM 员工异动记录创建 Request VO") | |
| 16 | +@Data | |
| 17 | +public class HrmEmployeeChangeRecordCreateReqVO { | |
| 18 | + | |
| 19 | + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 20 | + @NotNull(message = "员工编号不能为空") | |
| 21 | + private Long employeeId; | |
| 22 | + | |
| 23 | + @Schema(description = "异动类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") | |
| 24 | + @NotNull(message = "异动类型不能为空") | |
| 25 | + @InEnum(value = HrmEmployeeChangeTypeEnum.class, message = "异动类型必须是 {value}") | |
| 26 | + private Integer type; | |
| 27 | + | |
| 28 | + @Schema(description = "异动原因", example = "1") | |
| 29 | + @InEnum(value = HrmEmployeeChangeReasonEnum.class, message = "异动原因必须是 {value}") | |
| 30 | + private Integer reason; | |
| 31 | + | |
| 32 | + @Schema(description = "原部门编号", example = "100") | |
| 33 | + private Long oldDeptId; | |
| 34 | + | |
| 35 | + @Schema(description = "原岗位名称", example = "Java 工程师") | |
| 36 | + @Size(max = 255, message = "原岗位名称不能超过 255 个字符") | |
| 37 | + private String oldPostName; | |
| 38 | + | |
| 39 | + @Schema(description = "原职级", example = "P5") | |
| 40 | + @Size(max = 255, message = "原职级不能超过 255 个字符") | |
| 41 | + private String oldPostLevel; | |
| 42 | + | |
| 43 | + @Schema(description = "原工作地点", example = "杭州") | |
| 44 | + @Size(max = 255, message = "原工作地点不能超过 255 个字符") | |
| 45 | + private String oldWorkAddress; | |
| 46 | + | |
| 47 | + @Schema(description = "原直属上级员工编号", example = "2") | |
| 48 | + private Long oldLeaderEmployeeId; | |
| 49 | + | |
| 50 | + @Schema(description = "新部门编号", example = "200") | |
| 51 | + private Long newDeptId; | |
| 52 | + | |
| 53 | + @Schema(description = "新岗位名称", example = "高级 Java 工程师") | |
| 54 | + @Size(max = 255, message = "新岗位名称不能超过 255 个字符") | |
| 55 | + private String newPostName; | |
| 56 | + | |
| 57 | + @Schema(description = "新职级", example = "P6") | |
| 58 | + @Size(max = 255, message = "新职级不能超过 255 个字符") | |
| 59 | + private String newPostLevel; | |
| 60 | + | |
| 61 | + @Schema(description = "新工作地点", example = "上海") | |
| 62 | + @Size(max = 255, message = "新工作地点不能超过 255 个字符") | |
| 63 | + private String newWorkAddress; | |
| 64 | + | |
| 65 | + @Schema(description = "新直属上级员工编号", example = "3") | |
| 66 | + private Long newLeaderEmployeeId; | |
| 67 | + | |
| 68 | + @Schema(description = "试用期,单位月", example = "3") | |
| 69 | + @Min(value = 0, message = "试用期不能小于 0") | |
| 70 | + @Max(value = 6, message = "试用期不能超过 6 个月") | |
| 71 | + private Integer probation; | |
| 72 | + | |
| 73 | + @Schema(description = "生效时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 74 | + @NotNull(message = "生效时间不能为空") | |
| 75 | + private LocalDateTime effectTime; | |
| 76 | + | |
| 77 | + @Schema(description = "备注", example = "年度晋升") | |
| 78 | + @Size(max = 500, message = "备注不能超过 500 个字符") | |
| 79 | + private String remark; | |
| 80 | + | |
| 81 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeeChangeRecordRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.changerecord; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.time.LocalDateTime; | |
| 7 | + | |
| 8 | +@Schema(description = "管理后台 - HRM 员工异动记录 Response VO") | |
| 9 | +@Data | |
| 10 | +public class HrmEmployeeChangeRecordRespVO { | |
| 11 | + | |
| 12 | + @Schema(description = "异动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 13 | + private Long id; | |
| 14 | + | |
| 15 | + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 16 | + private Long employeeId; | |
| 17 | + | |
| 18 | + @Schema(description = "变动类型", example = "5") | |
| 19 | + private Integer type; | |
| 20 | + | |
| 21 | + @Schema(description = "异动原因", example = "1") | |
| 22 | + private Integer reason; | |
| 23 | + | |
| 24 | + @Schema(description = "原部门编号", example = "100") | |
| 25 | + private Long oldDeptId; | |
| 26 | + | |
| 27 | + @Schema(description = "原部门名称", example = "研发部") | |
| 28 | + private String oldDeptName; | |
| 29 | + | |
| 30 | + @Schema(description = "新部门编号", example = "200") | |
| 31 | + private Long newDeptId; | |
| 32 | + | |
| 33 | + @Schema(description = "新部门名称", example = "产品部") | |
| 34 | + private String newDeptName; | |
| 35 | + | |
| 36 | + @Schema(description = "原岗位名称") | |
| 37 | + private String oldPostName; | |
| 38 | + | |
| 39 | + @Schema(description = "新岗位名称") | |
| 40 | + private String newPostName; | |
| 41 | + | |
| 42 | + @Schema(description = "原职级") | |
| 43 | + private String oldPostLevel; | |
| 44 | + | |
| 45 | + @Schema(description = "新职级") | |
| 46 | + private String newPostLevel; | |
| 47 | + | |
| 48 | + @Schema(description = "原工作地点") | |
| 49 | + private String oldWorkAddress; | |
| 50 | + | |
| 51 | + @Schema(description = "新工作地点") | |
| 52 | + private String newWorkAddress; | |
| 53 | + | |
| 54 | + @Schema(description = "原直属上级员工编号") | |
| 55 | + private Long oldLeaderEmployeeId; | |
| 56 | + | |
| 57 | + @Schema(description = "原直属上级员工姓名") | |
| 58 | + private String oldLeaderEmployeeName; | |
| 59 | + | |
| 60 | + @Schema(description = "新直属上级员工编号") | |
| 61 | + private Long newLeaderEmployeeId; | |
| 62 | + | |
| 63 | + @Schema(description = "新直属上级员工姓名") | |
| 64 | + private String newLeaderEmployeeName; | |
| 65 | + | |
| 66 | + @Schema(description = "试用期,单位月") | |
| 67 | + private Integer probation; | |
| 68 | + | |
| 69 | + @Schema(description = "生效日期") | |
| 70 | + private LocalDateTime effectTime; | |
| 71 | + | |
| 72 | + @Schema(description = "备注") | |
| 73 | + private String remark; | |
| 74 | + | |
| 75 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 76 | + private LocalDateTime createTime; | |
| 77 | + | |
| 78 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeeConvertToFullTimeReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.changerecord; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 4 | +import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeChangeReasonEnum; | |
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 6 | +import jakarta.validation.constraints.Max; | |
| 7 | +import jakarta.validation.constraints.Min; | |
| 8 | +import jakarta.validation.constraints.NotNull; | |
| 9 | +import jakarta.validation.constraints.Size; | |
| 10 | +import lombok.Data; | |
| 11 | + | |
| 12 | +import java.time.LocalDateTime; | |
| 13 | + | |
| 14 | +@Schema(description = "管理后台 - HRM 员工转为全职 Request VO") | |
| 15 | +@Data | |
| 16 | +public class HrmEmployeeConvertToFullTimeReqVO { | |
| 17 | + | |
| 18 | + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 19 | + @NotNull(message = "员工编号不能为空") | |
| 20 | + private Long employeeId; | |
| 21 | + | |
| 22 | + @Schema(description = "异动原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 23 | + @NotNull(message = "异动原因不能为空") | |
| 24 | + @InEnum(value = HrmEmployeeChangeReasonEnum.class, message = "异动原因必须是 {value}") | |
| 25 | + private Integer reason; | |
| 26 | + | |
| 27 | + @Schema(description = "试用期,单位:月;0 表示无试用期", requiredMode = Schema.RequiredMode.REQUIRED, | |
| 28 | + example = "3") | |
| 29 | + @NotNull(message = "试用期不能为空") | |
| 30 | + @Min(value = 0, message = "试用期不能小于 0") | |
| 31 | + @Max(value = 6, message = "试用期不能超过 6 个月") | |
| 32 | + private Integer probation; | |
| 33 | + | |
| 34 | + @Schema(description = "新部门编号;未填写表示不变", example = "200") | |
| 35 | + private Long newDeptId; | |
| 36 | + | |
| 37 | + @Schema(description = "新岗位名称;未填写表示不变", example = "Java 工程师") | |
| 38 | + @Size(max = 255, message = "新岗位名称不能超过 255 个字符") | |
| 39 | + private String newPostName; | |
| 40 | + | |
| 41 | + @Schema(description = "新职级;未填写表示不变", example = "P6") | |
| 42 | + @Size(max = 255, message = "新职级不能超过 255 个字符") | |
| 43 | + private String newPostLevel; | |
| 44 | + | |
| 45 | + @Schema(description = "新工作地点;未填写表示不变", example = "上海") | |
| 46 | + @Size(max = 255, message = "新工作地点不能超过 255 个字符") | |
| 47 | + private String newWorkAddress; | |
| 48 | + | |
| 49 | + @Schema(description = "新直属上级员工编号;未填写表示不变", example = "3") | |
| 50 | + private Long newLeaderEmployeeId; | |
| 51 | + | |
| 52 | + @Schema(description = "生效日期", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 53 | + @NotNull(message = "生效日期不能为空") | |
| 54 | + private LocalDateTime effectTime; | |
| 55 | + | |
| 56 | + @Schema(description = "备注", example = "转为全职员工") | |
| 57 | + @Size(max = 500, message = "备注不能超过 500 个字符") | |
| 58 | + private String remark; | |
| 59 | + | |
| 60 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeeDemoteReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.changerecord; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 4 | +import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeChangeReasonEnum; | |
| 5 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
| 6 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 7 | +import jakarta.validation.constraints.AssertTrue; | |
| 8 | +import jakarta.validation.constraints.NotNull; | |
| 9 | +import jakarta.validation.constraints.Size; | |
| 10 | +import lombok.Data; | |
| 11 | + | |
| 12 | +import java.time.LocalDateTime; | |
| 13 | + | |
| 14 | +@Schema(description = "管理后台 - HRM 员工降级 Request VO") | |
| 15 | +@Data | |
| 16 | +public class HrmEmployeeDemoteReqVO { | |
| 17 | + | |
| 18 | + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 19 | + @NotNull(message = "员工编号不能为空") | |
| 20 | + private Long employeeId; | |
| 21 | + | |
| 22 | + @Schema(description = "异动原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 23 | + @NotNull(message = "异动原因不能为空") | |
| 24 | + @InEnum(value = HrmEmployeeChangeReasonEnum.class, message = "异动原因必须是 {value}") | |
| 25 | + private Integer reason; | |
| 26 | + | |
| 27 | + @Schema(description = "新部门编号", example = "200") | |
| 28 | + private Long newDeptId; | |
| 29 | + | |
| 30 | + @Schema(description = "新岗位名称", example = "Java 工程师") | |
| 31 | + @Size(max = 255, message = "新岗位名称不能超过 255 个字符") | |
| 32 | + private String newPostName; | |
| 33 | + | |
| 34 | + @Schema(description = "新职级", example = "P5") | |
| 35 | + @Size(max = 255, message = "新职级不能超过 255 个字符") | |
| 36 | + private String newPostLevel; | |
| 37 | + | |
| 38 | + @Schema(description = "新工作地点", example = "杭州") | |
| 39 | + @Size(max = 255, message = "新工作地点不能超过 255 个字符") | |
| 40 | + private String newWorkAddress; | |
| 41 | + | |
| 42 | + @Schema(description = "新直属上级员工编号", example = "3") | |
| 43 | + private Long newLeaderEmployeeId; | |
| 44 | + | |
| 45 | + @Schema(description = "生效日期", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 46 | + @NotNull(message = "生效日期不能为空") | |
| 47 | + private LocalDateTime effectTime; | |
| 48 | + | |
| 49 | + @Schema(description = "备注", example = "岗位职级调整") | |
| 50 | + @Size(max = 500, message = "备注不能超过 500 个字符") | |
| 51 | + private String remark; | |
| 52 | + | |
| 53 | + @AssertTrue(message = "降级原因不合法") | |
| 54 | + @JsonIgnore | |
| 55 | + public boolean isReasonValid() { | |
| 56 | + return reason == null || HrmEmployeeChangeReasonEnum.DEMOTION_REASONS.contains(reason); | |
| 57 | + } | |
| 58 | + | |
| 59 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeePromoteReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.changerecord; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 4 | +import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeChangeReasonEnum; | |
| 5 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
| 6 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 7 | +import jakarta.validation.constraints.AssertTrue; | |
| 8 | +import jakarta.validation.constraints.NotNull; | |
| 9 | +import jakarta.validation.constraints.Size; | |
| 10 | +import lombok.Data; | |
| 11 | + | |
| 12 | +import java.time.LocalDateTime; | |
| 13 | + | |
| 14 | +@Schema(description = "管理后台 - HRM 员工晋升 Request VO") | |
| 15 | +@Data | |
| 16 | +public class HrmEmployeePromoteReqVO { | |
| 17 | + | |
| 18 | + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 19 | + @NotNull(message = "员工编号不能为空") | |
| 20 | + private Long employeeId; | |
| 21 | + | |
| 22 | + @Schema(description = "异动原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 23 | + @NotNull(message = "异动原因不能为空") | |
| 24 | + @InEnum(value = HrmEmployeeChangeReasonEnum.class, message = "异动原因必须是 {value}") | |
| 25 | + private Integer reason; | |
| 26 | + | |
| 27 | + @Schema(description = "新部门编号", example = "200") | |
| 28 | + private Long newDeptId; | |
| 29 | + | |
| 30 | + @Schema(description = "新岗位名称", example = "高级 Java 工程师") | |
| 31 | + @Size(max = 255, message = "新岗位名称不能超过 255 个字符") | |
| 32 | + private String newPostName; | |
| 33 | + | |
| 34 | + @Schema(description = "新职级", example = "P6") | |
| 35 | + @Size(max = 255, message = "新职级不能超过 255 个字符") | |
| 36 | + private String newPostLevel; | |
| 37 | + | |
| 38 | + @Schema(description = "新工作地点", example = "上海") | |
| 39 | + @Size(max = 255, message = "新工作地点不能超过 255 个字符") | |
| 40 | + private String newWorkAddress; | |
| 41 | + | |
| 42 | + @Schema(description = "新直属上级员工编号", example = "3") | |
| 43 | + private Long newLeaderEmployeeId; | |
| 44 | + | |
| 45 | + @Schema(description = "生效日期", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 46 | + @NotNull(message = "生效日期不能为空") | |
| 47 | + private LocalDateTime effectTime; | |
| 48 | + | |
| 49 | + @Schema(description = "备注", example = "年度晋升") | |
| 50 | + @Size(max = 500, message = "备注不能超过 500 个字符") | |
| 51 | + private String remark; | |
| 52 | + | |
| 53 | + @AssertTrue(message = "晋升原因不合法") | |
| 54 | + @JsonIgnore | |
| 55 | + public boolean isReasonValid() { | |
| 56 | + return reason == null || HrmEmployeeChangeReasonEnum.TRANSFER_AND_PROMOTION_REASONS.contains(reason); | |
| 57 | + } | |
| 58 | + | |
| 59 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeeRegularReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.changerecord; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 4 | +import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeChangeReasonEnum; | |
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 6 | +import jakarta.validation.constraints.NotNull; | |
| 7 | +import jakarta.validation.constraints.Size; | |
| 8 | +import lombok.Data; | |
| 9 | + | |
| 10 | +import java.time.LocalDateTime; | |
| 11 | + | |
| 12 | +@Schema(description = "管理后台 - HRM 员工转正 Request VO") | |
| 13 | +@Data | |
| 14 | +public class HrmEmployeeRegularReqVO { | |
| 15 | + | |
| 16 | + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 17 | + @NotNull(message = "员工编号不能为空") | |
| 18 | + private Long employeeId; | |
| 19 | + | |
| 20 | + @Schema(description = "异动原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 21 | + @NotNull(message = "异动原因不能为空") | |
| 22 | + @InEnum(value = HrmEmployeeChangeReasonEnum.class, message = "异动原因必须是 {value}") | |
| 23 | + private Integer reason; | |
| 24 | + | |
| 25 | + @Schema(description = "新部门编号;未填写表示不变", example = "200") | |
| 26 | + private Long newDeptId; | |
| 27 | + | |
| 28 | + @Schema(description = "新岗位名称;未填写表示不变", example = "Java 工程师") | |
| 29 | + @Size(max = 255, message = "新岗位名称不能超过 255 个字符") | |
| 30 | + private String newPostName; | |
| 31 | + | |
| 32 | + @Schema(description = "新职级;未填写表示不变", example = "P6") | |
| 33 | + @Size(max = 255, message = "新职级不能超过 255 个字符") | |
| 34 | + private String newPostLevel; | |
| 35 | + | |
| 36 | + @Schema(description = "新工作地点;未填写表示不变", example = "上海") | |
| 37 | + @Size(max = 255, message = "新工作地点不能超过 255 个字符") | |
| 38 | + private String newWorkAddress; | |
| 39 | + | |
| 40 | + @Schema(description = "新直属上级员工编号;未填写表示不变", example = "3") | |
| 41 | + private Long newLeaderEmployeeId; | |
| 42 | + | |
| 43 | + @Schema(description = "生效日期", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 44 | + @NotNull(message = "生效日期不能为空") | |
| 45 | + private LocalDateTime effectTime; | |
| 46 | + | |
| 47 | + @Schema(description = "备注", example = "试用期考核通过") | |
| 48 | + @Size(max = 500, message = "备注不能超过 500 个字符") | |
| 49 | + private String remark; | |
| 50 | + | |
| 51 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/changerecord/HrmEmployeeTransferReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.changerecord; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 4 | +import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeChangeReasonEnum; | |
| 5 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
| 6 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 7 | +import jakarta.validation.constraints.AssertTrue; | |
| 8 | +import jakarta.validation.constraints.NotNull; | |
| 9 | +import jakarta.validation.constraints.Size; | |
| 10 | +import lombok.Data; | |
| 11 | + | |
| 12 | +import java.time.LocalDateTime; | |
| 13 | + | |
| 14 | +@Schema(description = "管理后台 - HRM 员工调岗 Request VO") | |
| 15 | +@Data | |
| 16 | +public class HrmEmployeeTransferReqVO { | |
| 17 | + | |
| 18 | + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 19 | + @NotNull(message = "员工编号不能为空") | |
| 20 | + private Long employeeId; | |
| 21 | + | |
| 22 | + @Schema(description = "异动原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 23 | + @NotNull(message = "异动原因不能为空") | |
| 24 | + @InEnum(value = HrmEmployeeChangeReasonEnum.class, message = "异动原因必须是 {value}") | |
| 25 | + private Integer reason; | |
| 26 | + | |
| 27 | + @Schema(description = "新部门编号", example = "200") | |
| 28 | + private Long newDeptId; | |
| 29 | + | |
| 30 | + @Schema(description = "新岗位名称", example = "产品经理") | |
| 31 | + @Size(max = 255, message = "新岗位名称不能超过 255 个字符") | |
| 32 | + private String newPostName; | |
| 33 | + | |
| 34 | + @Schema(description = "新职级", example = "P6") | |
| 35 | + @Size(max = 255, message = "新职级不能超过 255 个字符") | |
| 36 | + private String newPostLevel; | |
| 37 | + | |
| 38 | + @Schema(description = "新工作地点", example = "上海") | |
| 39 | + @Size(max = 255, message = "新工作地点不能超过 255 个字符") | |
| 40 | + private String newWorkAddress; | |
| 41 | + | |
| 42 | + @Schema(description = "新直属上级员工编号", example = "3") | |
| 43 | + private Long newLeaderEmployeeId; | |
| 44 | + | |
| 45 | + @Schema(description = "生效日期", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 46 | + @NotNull(message = "生效日期不能为空") | |
| 47 | + private LocalDateTime effectTime; | |
| 48 | + | |
| 49 | + @Schema(description = "备注", example = "业务调整") | |
| 50 | + @Size(max = 500, message = "备注不能超过 500 个字符") | |
| 51 | + private String remark; | |
| 52 | + | |
| 53 | + @AssertTrue(message = "调岗原因不合法") | |
| 54 | + @JsonIgnore | |
| 55 | + public boolean isReasonValid() { | |
| 56 | + return reason == null || HrmEmployeeChangeReasonEnum.TRANSFER_AND_PROMOTION_REASONS.contains(reason); | |
| 57 | + } | |
| 58 | + | |
| 59 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/config/HrmEmployeeArchiveFieldConfigSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.config; | |
| 2 | + | |
| 3 | +import com.mzt.logapi.starter.annotation.DiffLogField; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import jakarta.validation.Valid; | |
| 6 | +import jakarta.validation.constraints.NotEmpty; | |
| 7 | +import lombok.Data; | |
| 8 | + | |
| 9 | +import java.util.List; | |
| 10 | + | |
| 11 | +@Schema(description = "管理后台 - HRM 员工档案字段配置保存 Request VO") | |
| 12 | +@Data | |
| 13 | +public class HrmEmployeeArchiveFieldConfigSaveReqVO { | |
| 14 | + | |
| 15 | + @Schema(description = "字段配置列表", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 16 | + @Valid | |
| 17 | + @NotEmpty(message = "字段配置列表不能为空") | |
| 18 | + @DiffLogField(name = "字段配置") | |
| 19 | + private List<HrmEmployeeFieldConfigSaveReqVO> fields; | |
| 20 | + | |
| 21 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/config/HrmEmployeeCreateFieldConfigSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.config; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 4 | +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeEntryStatusEnum; | |
| 5 | +import com.mzt.logapi.starter.annotation.DiffLogField; | |
| 6 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 7 | +import jakarta.validation.Valid; | |
| 8 | +import jakarta.validation.constraints.NotEmpty; | |
| 9 | +import jakarta.validation.constraints.NotNull; | |
| 10 | +import lombok.Data; | |
| 11 | +import org.hibernate.validator.constraints.Range; | |
| 12 | + | |
| 13 | +import java.util.List; | |
| 14 | + | |
| 15 | +@Schema(description = "管理后台 - HRM 新建员工字段配置保存 Request VO") | |
| 16 | +@Data | |
| 17 | +public class HrmEmployeeCreateFieldConfigSaveReqVO { | |
| 18 | + | |
| 19 | + @Schema(description = "入职状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 20 | + @NotNull(message = "入职状态不能为空") | |
| 21 | + @InEnum(value = HrmEmployeeEntryStatusEnum.class, message = "入职状态必须是 {value}") | |
| 22 | + @Range(min = 1, max = 2, message = "入职状态必须是 1 或 2") | |
| 23 | + @DiffLogField(name = "入职状态") | |
| 24 | + private Integer entryStatus; | |
| 25 | + | |
| 26 | + @Schema(description = "字段配置列表", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 27 | + @Valid | |
| 28 | + @NotEmpty(message = "字段配置列表不能为空") | |
| 29 | + @DiffLogField(name = "字段配置") | |
| 30 | + private List<HrmEmployeeFieldConfigSaveReqVO> fields; | |
| 31 | + | |
| 32 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/config/HrmEmployeeFieldConfigRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.config; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +@Schema(description = "管理后台 - HRM 员工字段配置 Response VO") | |
| 7 | +@Data | |
| 8 | +public class HrmEmployeeFieldConfigRespVO { | |
| 9 | + | |
| 10 | + @Schema(description = "字段名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "name") | |
| 11 | + private String name; | |
| 12 | + | |
| 13 | + @Schema(description = "字段标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "员工姓名") | |
| 14 | + private String title; | |
| 15 | + | |
| 16 | + @Schema(description = "字段分组名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "基本信息") | |
| 17 | + private String groupName; | |
| 18 | + | |
| 19 | + @Schema(description = "是否显示", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") | |
| 20 | + private Boolean visible; | |
| 21 | + | |
| 22 | + @Schema(description = "是否允许员工编辑", example = "true") | |
| 23 | + private Boolean editable; | |
| 24 | + | |
| 25 | + @Schema(description = "是否锁定显示", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") | |
| 26 | + private Boolean visibleLocked; | |
| 27 | + | |
| 28 | + @Schema(description = "是否锁定编辑", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") | |
| 29 | + private Boolean editableLocked; | |
| 30 | + | |
| 31 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/config/HrmEmployeeFieldConfigSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.config; | |
| 2 | + | |
| 3 | +import com.mzt.logapi.starter.annotation.DiffLogField; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import jakarta.validation.constraints.NotBlank; | |
| 6 | +import jakarta.validation.constraints.NotNull; | |
| 7 | +import jakarta.validation.constraints.Size; | |
| 8 | +import lombok.Data; | |
| 9 | + | |
| 10 | +@Schema(description = "管理后台 - HRM 员工字段配置保存 Request VO") | |
| 11 | +@Data | |
| 12 | +public class HrmEmployeeFieldConfigSaveReqVO { | |
| 13 | + | |
| 14 | + @Schema(description = "字段名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "name") | |
| 15 | + @NotBlank(message = "字段名称不能为空") | |
| 16 | + @Size(max = 40, message = "字段名称不能超过 40 个字符") | |
| 17 | + @DiffLogField(name = "字段名称") | |
| 18 | + private String name; | |
| 19 | + | |
| 20 | + @Schema(description = "是否显示", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") | |
| 21 | + @NotNull(message = "是否显示不能为空") | |
| 22 | + @DiffLogField(name = "是否显示") | |
| 23 | + private Boolean visible; | |
| 24 | + | |
| 25 | + @Schema(description = "是否允许员工编辑", example = "true") | |
| 26 | + @DiffLogField(name = "是否允许员工编辑") | |
| 27 | + private Boolean editable; | |
| 28 | + | |
| 29 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/config/HrmEmployeeFieldConfigValueVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.config; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.AllArgsConstructor; | |
| 5 | +import lombok.Data; | |
| 6 | +import lombok.NoArgsConstructor; | |
| 7 | + | |
| 8 | +@Schema(description = "管理后台 - HRM 员工字段配置值 VO") | |
| 9 | +@Data | |
| 10 | +@NoArgsConstructor | |
| 11 | +@AllArgsConstructor | |
| 12 | +public class HrmEmployeeFieldConfigValueVO { | |
| 13 | + | |
| 14 | + @Schema(description = "字段名称", example = "mobile") | |
| 15 | + private String name; | |
| 16 | + | |
| 17 | + @Schema(description = "是否显示", example = "true") | |
| 18 | + private Boolean visible; | |
| 19 | + | |
| 20 | + @Schema(description = "是否允许员工编辑", example = "true") | |
| 21 | + private Boolean editable; | |
| 22 | + | |
| 23 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/contact/HrmEmployeeContactRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.contact; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.time.LocalDateTime; | |
| 7 | + | |
| 8 | +@Schema(description = "管理后台 - HRM 员工联系人 Response VO") | |
| 9 | +@Data | |
| 10 | +public class HrmEmployeeContactRespVO { | |
| 11 | + | |
| 12 | + @Schema(description = "联系人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 13 | + private Long id; | |
| 14 | + | |
| 15 | + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 16 | + private Long employeeId; | |
| 17 | + | |
| 18 | + @Schema(description = "联系人名称", example = "李四") | |
| 19 | + private String name; | |
| 20 | + | |
| 21 | + @Schema(description = "关系", example = "配偶") | |
| 22 | + private String relation; | |
| 23 | + | |
| 24 | + @Schema(description = "联系人电话", example = "15601691301") | |
| 25 | + private String phone; | |
| 26 | + | |
| 27 | + @Schema(description = "联系人工作单位") | |
| 28 | + private String workUnit; | |
| 29 | + | |
| 30 | + @Schema(description = "联系人职务") | |
| 31 | + private String postName; | |
| 32 | + | |
| 33 | + @Schema(description = "联系人地址") | |
| 34 | + private String address; | |
| 35 | + | |
| 36 | + @Schema(description = "排序", example = "1") | |
| 37 | + private Integer sort; | |
| 38 | + | |
| 39 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 40 | + private LocalDateTime createTime; | |
| 41 | + | |
| 42 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/contact/HrmEmployeeContactSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.contact; | |
| 2 | + | |
| 3 | +import com.mzt.logapi.starter.annotation.DiffLogField; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import jakarta.validation.constraints.NotBlank; | |
| 6 | +import jakarta.validation.constraints.NotNull; | |
| 7 | +import jakarta.validation.constraints.Size; | |
| 8 | +import lombok.Data; | |
| 9 | + | |
| 10 | +@Schema(description = "管理后台 - HRM 员工联系人保存 Request VO") | |
| 11 | +@Data | |
| 12 | +public class HrmEmployeeContactSaveReqVO { | |
| 13 | + | |
| 14 | + @Schema(description = "联系人编号", example = "1024") | |
| 15 | + private Long id; | |
| 16 | + | |
| 17 | + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 18 | + @NotNull(message = "员工编号不能为空") | |
| 19 | + private Long employeeId; | |
| 20 | + | |
| 21 | + @Schema(description = "联系人名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") | |
| 22 | + @NotBlank(message = "联系人名称不能为空") | |
| 23 | + @Size(max = 64, message = "联系人名称长度不能超过 64 个字符") | |
| 24 | + @DiffLogField(name = "联系人名称") | |
| 25 | + private String name; | |
| 26 | + | |
| 27 | + @Schema(description = "关系", example = "配偶") | |
| 28 | + @Size(max = 64, message = "关系长度不能超过 64 个字符") | |
| 29 | + @DiffLogField(name = "关系") | |
| 30 | + private String relation; | |
| 31 | + | |
| 32 | + @Schema(description = "联系人电话", example = "15601691301") | |
| 33 | + @Size(max = 40, message = "联系人电话长度不能超过 40 个字符") | |
| 34 | + @DiffLogField(name = "联系人电话") | |
| 35 | + private String phone; | |
| 36 | + | |
| 37 | + @Schema(description = "联系人工作单位", example = "示例科技") | |
| 38 | + @Size(max = 128, message = "联系人工作单位长度不能超过 128 个字符") | |
| 39 | + @DiffLogField(name = "联系人工作单位") | |
| 40 | + private String workUnit; | |
| 41 | + | |
| 42 | + @Schema(description = "联系人职务", example = "产品经理") | |
| 43 | + @Size(max = 128, message = "联系人职务长度不能超过 128 个字符") | |
| 44 | + @DiffLogField(name = "联系人职务") | |
| 45 | + private String postName; | |
| 46 | + | |
| 47 | + @Schema(description = "联系人地址", example = "杭州市西湖区") | |
| 48 | + @Size(max = 255, message = "联系人地址长度不能超过 255 个字符") | |
| 49 | + @DiffLogField(name = "联系人地址") | |
| 50 | + private String address; | |
| 51 | + | |
| 52 | + @Schema(description = "排序", example = "1") | |
| 53 | + @DiffLogField(name = "排序") | |
| 54 | + private Integer sort; | |
| 55 | + | |
| 56 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/contract/HrmEmployeeContractRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.contract; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.time.LocalDateTime; | |
| 7 | +import java.util.List; | |
| 8 | + | |
| 9 | +@Schema(description = "管理后台 - HRM 员工合同 Response VO") | |
| 10 | +@Data | |
| 11 | +public class HrmEmployeeContractRespVO { | |
| 12 | + | |
| 13 | + @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 14 | + private Long id; | |
| 15 | + | |
| 16 | + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 17 | + private Long employeeId; | |
| 18 | + | |
| 19 | + @Schema(description = "合同编号", example = "HT-2026-001") | |
| 20 | + private String no; | |
| 21 | + | |
| 22 | + @Schema(description = "合同类型", example = "1") | |
| 23 | + private Integer type; | |
| 24 | + | |
| 25 | + @Schema(description = "合同开始日期") | |
| 26 | + private LocalDateTime startTime; | |
| 27 | + | |
| 28 | + @Schema(description = "合同结束日期") | |
| 29 | + private LocalDateTime endTime; | |
| 30 | + | |
| 31 | + @Schema(description = "期限", example = "36") | |
| 32 | + private Integer term; | |
| 33 | + | |
| 34 | + @Schema(description = "合同状态", example = "1") | |
| 35 | + private Integer status; | |
| 36 | + | |
| 37 | + @Schema(description = "签约公司") | |
| 38 | + private String signCompany; | |
| 39 | + | |
| 40 | + @Schema(description = "合同签订日期") | |
| 41 | + private LocalDateTime signTime; | |
| 42 | + | |
| 43 | + @Schema(description = "备注") | |
| 44 | + private String remark; | |
| 45 | + | |
| 46 | + @Schema(description = "是否到期提醒", example = "true") | |
| 47 | + private Boolean expireRemind; | |
| 48 | + | |
| 49 | + @Schema(description = "附件地址数组") | |
| 50 | + private List<String> fileUrls; | |
| 51 | + | |
| 52 | + @Schema(description = "排序", example = "1") | |
| 53 | + private Integer sort; | |
| 54 | + | |
| 55 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 56 | + private LocalDateTime createTime; | |
| 57 | + | |
| 58 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/contract/HrmEmployeeContractSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.contract; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 4 | +import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeContractStatusEnum; | |
| 5 | +import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeContractTypeEnum; | |
| 6 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
| 7 | +import com.mzt.logapi.starter.annotation.DiffLogField; | |
| 8 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 9 | +import jakarta.validation.constraints.AssertTrue; | |
| 10 | +import jakarta.validation.constraints.Max; | |
| 11 | +import jakarta.validation.constraints.Min; | |
| 12 | +import jakarta.validation.constraints.NotNull; | |
| 13 | +import jakarta.validation.constraints.Size; | |
| 14 | +import lombok.Data; | |
| 15 | + | |
| 16 | +import java.time.LocalDateTime; | |
| 17 | +import java.util.List; | |
| 18 | + | |
| 19 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.isAfterOrEqual; | |
| 20 | + | |
| 21 | +@Schema(description = "管理后台 - HRM 员工合同保存 Request VO") | |
| 22 | +@Data | |
| 23 | +public class HrmEmployeeContractSaveReqVO { | |
| 24 | + | |
| 25 | + @Schema(description = "合同编号", example = "1024") | |
| 26 | + private Long id; | |
| 27 | + | |
| 28 | + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 29 | + @NotNull(message = "员工编号不能为空") | |
| 30 | + private Long employeeId; | |
| 31 | + | |
| 32 | + @Schema(description = "合同编号", example = "HT-2026-001") | |
| 33 | + @Size(max = 128, message = "合同编号长度不能超过 128 个字符") | |
| 34 | + @DiffLogField(name = "合同编号") | |
| 35 | + private String no; | |
| 36 | + | |
| 37 | + @Schema(description = "合同类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 38 | + @NotNull(message = "合同类型不能为空") | |
| 39 | + @InEnum(value = HrmEmployeeContractTypeEnum.class, message = "合同类型必须是 {value}") | |
| 40 | + @DiffLogField(name = "合同类型") | |
| 41 | + private Integer type; | |
| 42 | + | |
| 43 | + @Schema(description = "合同开始日期", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 44 | + @NotNull(message = "合同开始日期不能为空") | |
| 45 | + @DiffLogField(name = "合同开始日期") | |
| 46 | + private LocalDateTime startTime; | |
| 47 | + | |
| 48 | + @Schema(description = "合同结束日期", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 49 | + @NotNull(message = "合同结束日期不能为空") | |
| 50 | + @DiffLogField(name = "合同结束日期") | |
| 51 | + private LocalDateTime endTime; | |
| 52 | + | |
| 53 | + @Schema(description = "期限,单位:年", example = "3") | |
| 54 | + @Min(value = 1, message = "合同期限不能小于 1 年") | |
| 55 | + @Max(value = 10, message = "合同期限不能超过 10 年") | |
| 56 | + @DiffLogField(name = "合同期限") | |
| 57 | + private Integer term; | |
| 58 | + | |
| 59 | + @Schema(description = "合同状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 60 | + @NotNull(message = "合同状态不能为空") | |
| 61 | + @InEnum(value = HrmEmployeeContractStatusEnum.class, message = "合同状态必须是 {value}") | |
| 62 | + @DiffLogField(name = "合同状态") | |
| 63 | + private Integer status; | |
| 64 | + | |
| 65 | + @Schema(description = "签约公司", example = "示例科技有限公司") | |
| 66 | + @Size(max = 255, message = "签约公司长度不能超过 255 个字符") | |
| 67 | + @DiffLogField(name = "签约公司") | |
| 68 | + private String signCompany; | |
| 69 | + | |
| 70 | + @Schema(description = "合同签订日期", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 71 | + @NotNull(message = "合同签订日期不能为空") | |
| 72 | + @DiffLogField(name = "合同签订日期") | |
| 73 | + private LocalDateTime signTime; | |
| 74 | + | |
| 75 | + @Schema(description = "备注", example = "首次签订劳动合同") | |
| 76 | + @Size(max = 500, message = "备注长度不能超过 500 个字符") | |
| 77 | + @DiffLogField(name = "备注") | |
| 78 | + private String remark; | |
| 79 | + | |
| 80 | + @Schema(description = "是否到期提醒", example = "true") | |
| 81 | + @DiffLogField(name = "是否到期提醒") | |
| 82 | + private Boolean expireRemind; | |
| 83 | + | |
| 84 | + @Schema(description = "附件地址数组", example = "[https://example.com/contract.pdf]") | |
| 85 | + @DiffLogField(name = "附件") | |
| 86 | + private List<String> fileUrls; | |
| 87 | + | |
| 88 | + @Schema(description = "排序", example = "1") | |
| 89 | + @DiffLogField(name = "排序") | |
| 90 | + private Integer sort; | |
| 91 | + | |
| 92 | + @AssertTrue(message = "合同结束日期不能早于开始日期") | |
| 93 | + @JsonIgnore | |
| 94 | + public boolean isTimeRangeValid() { | |
| 95 | + return startTime == null || endTime == null || isAfterOrEqual(endTime, startTime); | |
| 96 | + } | |
| 97 | + | |
| 98 | + @AssertTrue(message = "合同期限不能为空") | |
| 99 | + @JsonIgnore | |
| 100 | + public boolean isTermValid() { | |
| 101 | + return type == null | |
| 102 | + || HrmEmployeeContractTypeEnum.NON_FIXED_TERM_LABOR_CONTRACT.getType().equals(type) | |
| 103 | + || term != null; | |
| 104 | + } | |
| 105 | + | |
| 106 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/educationexperience/HrmEmployeeEducationExperienceRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.educationexperience; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.time.LocalDateTime; | |
| 7 | + | |
| 8 | +@Schema(description = "管理后台 - HRM 员工教育经历 Response VO") | |
| 9 | +@Data | |
| 10 | +public class HrmEmployeeEducationExperienceRespVO { | |
| 11 | + | |
| 12 | + @Schema(description = "教育经历编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 13 | + private Long id; | |
| 14 | + | |
| 15 | + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 16 | + private Long employeeId; | |
| 17 | + | |
| 18 | + @Schema(description = "学历", example = "8") | |
| 19 | + private Integer education; | |
| 20 | + | |
| 21 | + @Schema(description = "毕业院校", example = "浙江大学") | |
| 22 | + private String graduateSchool; | |
| 23 | + | |
| 24 | + @Schema(description = "专业", example = "软件工程") | |
| 25 | + private String major; | |
| 26 | + | |
| 27 | + @Schema(description = "入学日期") | |
| 28 | + private LocalDateTime admissionTime; | |
| 29 | + | |
| 30 | + @Schema(description = "毕业日期") | |
| 31 | + private LocalDateTime graduationTime; | |
| 32 | + | |
| 33 | + @Schema(description = "教学方式", example = "1") | |
| 34 | + private Integer teachingMethods; | |
| 35 | + | |
| 36 | + @Schema(description = "是否第一学历", example = "true") | |
| 37 | + private Boolean firstDegree; | |
| 38 | + | |
| 39 | + @Schema(description = "排序", example = "1") | |
| 40 | + private Integer sort; | |
| 41 | + | |
| 42 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 43 | + private LocalDateTime createTime; | |
| 44 | + | |
| 45 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/educationexperience/HrmEmployeeEducationExperienceSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.educationexperience; | |
| 2 | + | |
| 3 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
| 4 | +import com.mzt.logapi.starter.annotation.DiffLogField; | |
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 6 | +import jakarta.validation.constraints.AssertTrue; | |
| 7 | +import jakarta.validation.constraints.NotBlank; | |
| 8 | +import jakarta.validation.constraints.NotNull; | |
| 9 | +import jakarta.validation.constraints.Size; | |
| 10 | +import lombok.Data; | |
| 11 | + | |
| 12 | +import java.time.LocalDateTime; | |
| 13 | + | |
| 14 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.isAfterOrEqual; | |
| 15 | + | |
| 16 | +@Schema(description = "管理后台 - HRM 员工教育经历保存 Request VO") | |
| 17 | +@Data | |
| 18 | +public class HrmEmployeeEducationExperienceSaveReqVO { | |
| 19 | + | |
| 20 | + @Schema(description = "教育经历编号", example = "1024") | |
| 21 | + private Long id; | |
| 22 | + | |
| 23 | + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 24 | + @NotNull(message = "员工编号不能为空") | |
| 25 | + private Long employeeId; | |
| 26 | + | |
| 27 | + @Schema(description = "学历", requiredMode = Schema.RequiredMode.REQUIRED, example = "8") | |
| 28 | + @NotNull(message = "学历不能为空") | |
| 29 | + @DiffLogField(name = "学历") | |
| 30 | + private Integer education; | |
| 31 | + | |
| 32 | + @Schema(description = "毕业院校", requiredMode = Schema.RequiredMode.REQUIRED, example = "浙江大学") | |
| 33 | + @NotBlank(message = "毕业院校不能为空") | |
| 34 | + @Size(max = 255, message = "毕业院校长度不能超过 255 个字符") | |
| 35 | + @DiffLogField(name = "毕业院校") | |
| 36 | + private String graduateSchool; | |
| 37 | + | |
| 38 | + @Schema(description = "专业", requiredMode = Schema.RequiredMode.REQUIRED, example = "软件工程") | |
| 39 | + @NotBlank(message = "专业不能为空") | |
| 40 | + @Size(max = 255, message = "专业长度不能超过 255 个字符") | |
| 41 | + @DiffLogField(name = "专业") | |
| 42 | + private String major; | |
| 43 | + | |
| 44 | + @Schema(description = "入学日期") | |
| 45 | + @DiffLogField(name = "入学日期") | |
| 46 | + private LocalDateTime admissionTime; | |
| 47 | + | |
| 48 | + @Schema(description = "毕业日期") | |
| 49 | + @DiffLogField(name = "毕业日期") | |
| 50 | + private LocalDateTime graduationTime; | |
| 51 | + | |
| 52 | + @Schema(description = "教学方式", example = "1") | |
| 53 | + @DiffLogField(name = "教学方式") | |
| 54 | + private Integer teachingMethods; | |
| 55 | + | |
| 56 | + @Schema(description = "是否第一学历", example = "true") | |
| 57 | + @DiffLogField(name = "是否第一学历") | |
| 58 | + private Boolean firstDegree; | |
| 59 | + | |
| 60 | + @Schema(description = "排序", example = "1") | |
| 61 | + @DiffLogField(name = "排序") | |
| 62 | + private Integer sort; | |
| 63 | + | |
| 64 | + @AssertTrue(message = "毕业日期不能早于入学日期") | |
| 65 | + @JsonIgnore | |
| 66 | + public boolean isTimeRangeValid() { | |
| 67 | + return admissionTime == null || graduationTime == null | |
| 68 | + || isAfterOrEqual(graduationTime, admissionTime); | |
| 69 | + } | |
| 70 | + | |
| 71 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeCancelQuitReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import jakarta.validation.constraints.NotBlank; | |
| 5 | +import jakarta.validation.constraints.NotNull; | |
| 6 | +import jakarta.validation.constraints.Size; | |
| 7 | +import lombok.Data; | |
| 8 | + | |
| 9 | +@Schema(description = "管理后台 - HRM 员工取消离职 Request VO") | |
| 10 | +@Data | |
| 11 | +public class HrmEmployeeCancelQuitReqVO { | |
| 12 | + | |
| 13 | + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 14 | + @NotNull(message = "员工编号不能为空") | |
| 15 | + private Long employeeId; | |
| 16 | + | |
| 17 | + @Schema(description = "取消原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "员工撤回离职申请") | |
| 18 | + @NotBlank(message = "取消原因不能为空") | |
| 19 | + @Size(max = 500, message = "取消原因不能超过 500 个字符") | |
| 20 | + private String reason; | |
| 21 | + | |
| 22 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeConfirmEntryReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee; | |
| 2 | + | |
| 3 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import jakarta.validation.constraints.AssertTrue; | |
| 6 | +import lombok.Data; | |
| 7 | +import lombok.EqualsAndHashCode; | |
| 8 | + | |
| 9 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.beforeOrEqualNow; | |
| 10 | + | |
| 11 | +@Schema(description = "管理后台 - HRM 员工确认入职 Request VO") | |
| 12 | +@Data | |
| 13 | +@EqualsAndHashCode(callSuper = true) | |
| 14 | +public class HrmEmployeeConfirmEntryReqVO extends HrmEmployeeSaveReqVO { | |
| 15 | + | |
| 16 | + @AssertTrue(message = "入职时间不能晚于当前时间") | |
| 17 | + @JsonIgnore | |
| 18 | + public boolean isEntryTimeValid() { | |
| 19 | + return getEntryTime() == null || beforeOrEqualNow(getEntryTime()); | |
| 20 | + } | |
| 21 | + | |
| 22 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeCreateFromUserReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.ObjUtil; | |
| 4 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 5 | +import cn.iocoder.yudao.framework.common.validation.Mobile; | |
| 6 | +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeStatusEnum; | |
| 7 | +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeTypeEnum; | |
| 8 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
| 9 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 10 | +import jakarta.validation.constraints.AssertTrue; | |
| 11 | +import jakarta.validation.constraints.Max; | |
| 12 | +import jakarta.validation.constraints.Min; | |
| 13 | +import jakarta.validation.constraints.NotBlank; | |
| 14 | +import jakarta.validation.constraints.NotNull; | |
| 15 | +import jakarta.validation.constraints.Size; | |
| 16 | +import lombok.Data; | |
| 17 | + | |
| 18 | +import java.time.LocalDateTime; | |
| 19 | + | |
| 20 | +@Schema(description = "管理后台 - HRM 从后台用户创建员工 Request VO") | |
| 21 | +@Data | |
| 22 | +public class HrmEmployeeCreateFromUserReqVO { | |
| 23 | + | |
| 24 | + @Schema(description = "后台用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 25 | + @NotNull(message = "后台用户编号不能为空") | |
| 26 | + private Long userId; | |
| 27 | + | |
| 28 | + @Schema(description = "工号", requiredMode = Schema.RequiredMode.REQUIRED, example = "HRM001") | |
| 29 | + @NotBlank(message = "工号不能为空") | |
| 30 | + @Size(max = 64, message = "工号不能超过 64 个字符") | |
| 31 | + private String jobNumber; | |
| 32 | + | |
| 33 | + @Schema(description = "员工手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300") | |
| 34 | + @NotBlank(message = "员工手机号不能为空") | |
| 35 | + @Mobile | |
| 36 | + private String mobile; | |
| 37 | + | |
| 38 | + @Schema(description = "部门编号", example = "100") | |
| 39 | + private Long deptId; | |
| 40 | + | |
| 41 | + @Schema(description = "直属上级员工编号", example = "1") | |
| 42 | + private Long leaderEmployeeId; | |
| 43 | + | |
| 44 | + @Schema(description = "聘用形式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 45 | + @NotNull(message = "聘用形式不能为空") | |
| 46 | + @InEnum(value = HrmEmployeeTypeEnum.class, message = "聘用形式必须是 {value}") | |
| 47 | + private Integer type; | |
| 48 | + | |
| 49 | + @Schema(description = "员工状态;非正式员工必填", example = "3") | |
| 50 | + @InEnum(value = HrmEmployeeStatusEnum.class, message = "员工状态必须是 {value}") | |
| 51 | + private Integer status; | |
| 52 | + | |
| 53 | + @Schema(description = "入职时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 54 | + @NotNull(message = "入职时间不能为空") | |
| 55 | + private LocalDateTime entryTime; | |
| 56 | + | |
| 57 | + @Schema(description = "试用期,单位:月;正式员工必填", example = "3") | |
| 58 | + @Min(value = 0, message = "试用期不能小于 0") | |
| 59 | + @Max(value = 6, message = "试用期不能超过 6 个月") | |
| 60 | + private Integer probation; | |
| 61 | + | |
| 62 | + @Schema(description = "职位名称", example = "Java 工程师") | |
| 63 | + @Size(max = 255, message = "职位名称不能超过 255 个字符") | |
| 64 | + private String postName; | |
| 65 | + | |
| 66 | + @Schema(description = "岗位职级", example = "P6") | |
| 67 | + @Size(max = 255, message = "岗位职级不能超过 255 个字符") | |
| 68 | + private String postLevel; | |
| 69 | + | |
| 70 | + @Schema(description = "工作城市", example = "杭州") | |
| 71 | + @Size(max = 64, message = "工作城市不能超过 64 个字符") | |
| 72 | + private String workCity; | |
| 73 | + | |
| 74 | + @Schema(description = "工作地点", example = "西湖区") | |
| 75 | + @Size(max = 255, message = "工作地点不能超过 255 个字符") | |
| 76 | + private String workAddress; | |
| 77 | + | |
| 78 | + @Schema(description = "备注", example = "从后台用户批量建档") | |
| 79 | + @Size(max = 500, message = "备注不能超过 500 个字符") | |
| 80 | + private String remark; | |
| 81 | + | |
| 82 | + @AssertTrue(message = "正式员工试用期不能为空") | |
| 83 | + @JsonIgnore | |
| 84 | + public boolean isProbationValid() { | |
| 85 | + return ObjUtil.notEqual(HrmEmployeeTypeEnum.FORMAL.getType(), type) || probation != null; | |
| 86 | + } | |
| 87 | + | |
| 88 | + @AssertTrue(message = "非正式员工状态不能为空") | |
| 89 | + @JsonIgnore | |
| 90 | + public boolean isStatusRequired() { | |
| 91 | + return ObjUtil.notEqual(HrmEmployeeTypeEnum.INFORMAL.getType(), type) || status != null; | |
| 92 | + } | |
| 93 | + | |
| 94 | + @AssertTrue(message = "员工状态与聘用形式不匹配") | |
| 95 | + @JsonIgnore | |
| 96 | + public boolean isStatusValid() { | |
| 97 | + return ObjUtil.notEqual(HrmEmployeeTypeEnum.INFORMAL.getType(), type) | |
| 98 | + || status == null || HrmEmployeeStatusEnum.INFORMAL_STATUSES.contains(status); | |
| 99 | + } | |
| 100 | + | |
| 101 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeDeptStatisticsRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.AllArgsConstructor; | |
| 5 | +import lombok.Data; | |
| 6 | + | |
| 7 | +@Schema(description = "管理后台 - HRM 员工部门统计 Response VO") | |
| 8 | +@Data | |
| 9 | +@AllArgsConstructor | |
| 10 | +public class HrmEmployeeDeptStatisticsRespVO { | |
| 11 | + | |
| 12 | + @Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") | |
| 13 | + private Long deptId; | |
| 14 | + | |
| 15 | + @Schema(description = "在职员工人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") | |
| 16 | + private Long activeCount; | |
| 17 | + | |
| 18 | + @Schema(description = "全职员工人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "8") | |
| 19 | + private Long fullTimeCount; | |
| 20 | + | |
| 21 | + @Schema(description = "非全职员工人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | |
| 22 | + private Long nonFullTimeCount; | |
| 23 | + | |
| 24 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeImportExcelVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee; | |
| 2 | + | |
| 3 | +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; | |
| 4 | +import cn.idev.excel.annotation.ExcelProperty; | |
| 5 | +import cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect; | |
| 6 | +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; | |
| 7 | +import cn.iocoder.yudao.framework.excel.core.convert.AreaConvert; | |
| 8 | +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; | |
| 9 | +import cn.iocoder.yudao.module.hrm.enums.DictTypeConstants; | |
| 10 | +import cn.iocoder.yudao.module.hrm.framework.excel.core.HrmEmployeeProbationExcelColumnSelectFunction; | |
| 11 | +import cn.iocoder.yudao.module.hrm.framework.excel.core.HrmRecruitChannelExcelColumnSelectFunction; | |
| 12 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 13 | +import lombok.AllArgsConstructor; | |
| 14 | +import lombok.Builder; | |
| 15 | +import lombok.Data; | |
| 16 | +import lombok.NoArgsConstructor; | |
| 17 | + | |
| 18 | +import java.time.LocalDateTime; | |
| 19 | + | |
| 20 | +@Schema(description = "管理后台 - HRM 员工档案 Excel 导入 VO") | |
| 21 | +@Data | |
| 22 | +@Builder | |
| 23 | +@AllArgsConstructor | |
| 24 | +@NoArgsConstructor | |
| 25 | +@ExcelIgnoreUnannotated | |
| 26 | +public class HrmEmployeeImportExcelVO { | |
| 27 | + | |
| 28 | + @ExcelProperty("员工姓名") | |
| 29 | + @Schema(description = "员工姓名") | |
| 30 | + private String name; | |
| 31 | + | |
| 32 | + @ExcelProperty("工号") | |
| 33 | + @Schema(description = "工号") | |
| 34 | + private String jobNumber; | |
| 35 | + | |
| 36 | + @ExcelProperty("手机号") | |
| 37 | + @Schema(description = "手机号") | |
| 38 | + private String mobile; | |
| 39 | + | |
| 40 | + @ExcelProperty("国家或地区") | |
| 41 | + @Schema(description = "国家或地区") | |
| 42 | + private String country; | |
| 43 | + | |
| 44 | + @ExcelProperty("民族") | |
| 45 | + @Schema(description = "民族") | |
| 46 | + private String nation; | |
| 47 | + | |
| 48 | + @ExcelProperty(value = "证件类型", converter = DictConvert.class) | |
| 49 | + @DictFormat(DictTypeConstants.HRM_EMPLOYEE_ID_TYPE) | |
| 50 | + @ExcelColumnSelect(dictType = DictTypeConstants.HRM_EMPLOYEE_ID_TYPE) | |
| 51 | + @Schema(description = "证件类型") | |
| 52 | + private Integer idType; | |
| 53 | + | |
| 54 | + @ExcelProperty("证件号码") | |
| 55 | + @Schema(description = "证件号码") | |
| 56 | + private String idNumber; | |
| 57 | + | |
| 58 | + @ExcelProperty(value = "性别", converter = DictConvert.class) | |
| 59 | + @DictFormat(cn.iocoder.yudao.module.system.enums.DictTypeConstants.USER_SEX) | |
| 60 | + @ExcelColumnSelect(dictType = cn.iocoder.yudao.module.system.enums.DictTypeConstants.USER_SEX) | |
| 61 | + @Schema(description = "性别") | |
| 62 | + private Integer sex; | |
| 63 | + | |
| 64 | + @ExcelProperty("邮箱") | |
| 65 | + @Schema(description = "邮箱") | |
| 66 | + private String email; | |
| 67 | + | |
| 68 | + @ExcelProperty("籍贯") | |
| 69 | + @Schema(description = "籍贯") | |
| 70 | + private String nativePlace; | |
| 71 | + | |
| 72 | + @ExcelProperty("出生日期") | |
| 73 | + @Schema(description = "出生日期") | |
| 74 | + private LocalDateTime birthday; | |
| 75 | + | |
| 76 | + @ExcelProperty("户籍地址") | |
| 77 | + @Schema(description = "户籍地址") | |
| 78 | + private String address; | |
| 79 | + | |
| 80 | + @ExcelProperty(value = "最高学历", converter = DictConvert.class) | |
| 81 | + @DictFormat(DictTypeConstants.HRM_EMPLOYEE_EDUCATION) | |
| 82 | + @ExcelColumnSelect(dictType = DictTypeConstants.HRM_EMPLOYEE_EDUCATION) | |
| 83 | + @Schema(description = "最高学历") | |
| 84 | + private Integer highestEducation; | |
| 85 | + | |
| 86 | + @ExcelProperty("部门编号") | |
| 87 | + @Schema(description = "部门编号") | |
| 88 | + private Long deptId; | |
| 89 | + | |
| 90 | + @ExcelProperty("直属上级工号") | |
| 91 | + @Schema(description = "直属上级工号") | |
| 92 | + private String leaderJobNumber; | |
| 93 | + | |
| 94 | + @ExcelProperty("职位名称") | |
| 95 | + @Schema(description = "职位名称") | |
| 96 | + private String postName; | |
| 97 | + | |
| 98 | + @ExcelProperty("岗位职级") | |
| 99 | + @Schema(description = "岗位职级") | |
| 100 | + private String postLevel; | |
| 101 | + | |
| 102 | + @ExcelProperty(value = "入职状态", converter = DictConvert.class) | |
| 103 | + @DictFormat(DictTypeConstants.HRM_EMPLOYEE_ENTRY_STATUS) | |
| 104 | + @ExcelColumnSelect(dictType = DictTypeConstants.HRM_EMPLOYEE_ENTRY_STATUS) | |
| 105 | + @Schema(description = "入职状态") | |
| 106 | + private Integer entryStatus; | |
| 107 | + | |
| 108 | + @ExcelProperty(value = "员工状态", converter = DictConvert.class) | |
| 109 | + @DictFormat(DictTypeConstants.HRM_EMPLOYEE_STATUS) | |
| 110 | + @ExcelColumnSelect(dictType = DictTypeConstants.HRM_EMPLOYEE_STATUS) | |
| 111 | + @Schema(description = "员工状态") | |
| 112 | + private Integer status; | |
| 113 | + | |
| 114 | + @ExcelProperty(value = "聘用形式", converter = DictConvert.class) | |
| 115 | + @DictFormat(DictTypeConstants.HRM_EMPLOYEE_TYPE) | |
| 116 | + @ExcelColumnSelect(dictType = DictTypeConstants.HRM_EMPLOYEE_TYPE) | |
| 117 | + @Schema(description = "聘用形式") | |
| 118 | + private Integer type; | |
| 119 | + | |
| 120 | + @ExcelProperty("入职时间") | |
| 121 | + @Schema(description = "入职时间") | |
| 122 | + private LocalDateTime entryTime; | |
| 123 | + | |
| 124 | + @ExcelProperty("试用期(月)") | |
| 125 | + @ExcelColumnSelect(functionName = HrmEmployeeProbationExcelColumnSelectFunction.NAME) | |
| 126 | + @Schema(description = "试用期(月)") | |
| 127 | + private Integer probation; | |
| 128 | + | |
| 129 | + @ExcelProperty("转正时间") | |
| 130 | + @Schema(description = "转正时间") | |
| 131 | + private LocalDateTime regularTime; | |
| 132 | + | |
| 133 | + @ExcelProperty("离职时间") | |
| 134 | + @Schema(description = "离职时间") | |
| 135 | + private LocalDateTime leaveTime; | |
| 136 | + | |
| 137 | + @ExcelProperty("工作城市") | |
| 138 | + @Schema(description = "工作城市") | |
| 139 | + private String workCity; | |
| 140 | + | |
| 141 | + @ExcelProperty("工作地点") | |
| 142 | + @Schema(description = "工作地点") | |
| 143 | + private String workAddress; | |
| 144 | + | |
| 145 | + @ExcelProperty("工作详细地址") | |
| 146 | + @Schema(description = "工作详细地址") | |
| 147 | + private String workDetailAddress; | |
| 148 | + | |
| 149 | + @ExcelProperty("招聘渠道") | |
| 150 | + @ExcelColumnSelect(functionName = HrmRecruitChannelExcelColumnSelectFunction.NAME) | |
| 151 | + @Schema(description = "招聘渠道选项,格式为名称(ID)") | |
| 152 | + private String channelName; | |
| 153 | + | |
| 154 | + @ExcelProperty("司龄起算时间") | |
| 155 | + @Schema(description = "司龄起算时间") | |
| 156 | + private LocalDateTime companyAgeStartTime; | |
| 157 | + | |
| 158 | + @ExcelProperty("绑定用户手机号") | |
| 159 | + @Schema(description = "绑定后台用户的手机号") | |
| 160 | + private String userMobile; | |
| 161 | + | |
| 162 | + @ExcelProperty("银行卡号") | |
| 163 | + @Schema(description = "银行卡号") | |
| 164 | + private String bankCardNumber; | |
| 165 | + | |
| 166 | + @ExcelProperty(value = "开户地区", converter = AreaConvert.class) | |
| 167 | + @Schema(description = "开户地区") | |
| 168 | + private Integer bankAreaId; | |
| 169 | + | |
| 170 | + @ExcelProperty("银行名称") | |
| 171 | + @Schema(description = "银行名称") | |
| 172 | + private String bankName; | |
| 173 | + | |
| 174 | + @ExcelProperty("开户支行名称") | |
| 175 | + @Schema(description = "开户支行名称") | |
| 176 | + private String bankBranchName; | |
| 177 | + | |
| 178 | + @ExcelProperty(value = "是否首次参保", converter = DictConvert.class) | |
| 179 | + @DictFormat(DictTypeConstants.BOOLEAN_STRING) | |
| 180 | + @ExcelColumnSelect(dictType = DictTypeConstants.BOOLEAN_STRING) | |
| 181 | + @Schema(description = "是否首次参保") | |
| 182 | + private Boolean firstSocialSecurity; | |
| 183 | + | |
| 184 | + @ExcelProperty(value = "是否首次缴纳公积金", converter = DictConvert.class) | |
| 185 | + @DictFormat(DictTypeConstants.BOOLEAN_STRING) | |
| 186 | + @ExcelColumnSelect(dictType = DictTypeConstants.BOOLEAN_STRING) | |
| 187 | + @Schema(description = "是否首次缴纳公积金") | |
| 188 | + private Boolean firstAccumulationFund; | |
| 189 | + | |
| 190 | + @ExcelProperty("个人社保账号") | |
| 191 | + @Schema(description = "个人社保账号") | |
| 192 | + private String socialSecurityNumber; | |
| 193 | + | |
| 194 | + @ExcelProperty("个人公积金账号") | |
| 195 | + @Schema(description = "个人公积金账号") | |
| 196 | + private String accumulationFundNumber; | |
| 197 | + | |
| 198 | + @ExcelProperty("社保起缴月份") | |
| 199 | + @Schema(description = "社保起缴月份") | |
| 200 | + private LocalDateTime socialSecurityStartMonth; | |
| 201 | + | |
| 202 | + @ExcelProperty("参保方案名称") | |
| 203 | + @Schema(description = "参保方案名称") | |
| 204 | + private String schemeName; | |
| 205 | + | |
| 206 | + @ExcelProperty("备注") | |
| 207 | + @Schema(description = "备注") | |
| 208 | + private String remark; | |
| 209 | + | |
| 210 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeImportRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Builder; | |
| 5 | +import lombok.Data; | |
| 6 | + | |
| 7 | +import java.util.List; | |
| 8 | +import java.util.Map; | |
| 9 | + | |
| 10 | +@Schema(description = "管理后台 - HRM 员工档案导入 Response VO") | |
| 11 | +@Data | |
| 12 | +@Builder | |
| 13 | +public class HrmEmployeeImportRespVO { | |
| 14 | + | |
| 15 | + @Schema(description = "创建成功的工号数组", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 16 | + private List<String> createJobNumbers; | |
| 17 | + | |
| 18 | + @Schema(description = "更新成功的工号数组", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 19 | + private List<String> updateJobNumbers; | |
| 20 | + | |
| 21 | + @Schema(description = "因重复而跳过的工号数组", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 22 | + private List<String> skipJobNumbers; | |
| 23 | + | |
| 24 | + @Schema(description = "导入失败的员工集合,key 为原始行号和员工标识,value 为失败原因", | |
| 25 | + requiredMode = Schema.RequiredMode.REQUIRED) | |
| 26 | + private Map<String, String> failureJobNumbers; | |
| 27 | + | |
| 28 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeListReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 4 | +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeEntryStatusEnum; | |
| 5 | +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeStatusEnum; | |
| 6 | +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeStatusTabEnum; | |
| 7 | +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeSurveyTypeEnum; | |
| 8 | +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeTypeEnum; | |
| 9 | +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeTodoTypeEnum; | |
| 10 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 11 | +import lombok.Data; | |
| 12 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 13 | + | |
| 14 | +import java.time.LocalDateTime; | |
| 15 | +import java.util.List; | |
| 16 | + | |
| 17 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 18 | + | |
| 19 | +@Schema(description = "管理后台 - HRM 员工档案列表 Request VO") | |
| 20 | +@Data | |
| 21 | +public class HrmEmployeeListReqVO { | |
| 22 | + | |
| 23 | + @Schema(description = "员工编号列表", example = "1,2") | |
| 24 | + private List<Long> ids; | |
| 25 | + | |
| 26 | + @Schema(hidden = true) | |
| 27 | + private List<Long> excludeIds; | |
| 28 | + | |
| 29 | + @Schema(description = "员工姓名或工号,模糊匹配", example = "张三") | |
| 30 | + private String search; | |
| 31 | + | |
| 32 | + @Schema(description = "员工姓名,模糊匹配", example = "张") | |
| 33 | + private String name; | |
| 34 | + | |
| 35 | + @Schema(description = "工号,模糊匹配", example = "HRM") | |
| 36 | + private String jobNumber; | |
| 37 | + | |
| 38 | + @Schema(description = "手机号,模糊匹配", example = "156") | |
| 39 | + private String mobile; | |
| 40 | + | |
| 41 | + @Schema(description = "部门编号", example = "100") | |
| 42 | + private Long deptId; | |
| 43 | + | |
| 44 | + @Schema(description = "部门编号数组", example = "100,101") | |
| 45 | + private List<Long> deptIds; | |
| 46 | + | |
| 47 | + @Schema(description = "直属上级员工编号", example = "1") | |
| 48 | + private Long leaderEmployeeId; | |
| 49 | + | |
| 50 | + @Schema(description = "性别", example = "1") | |
| 51 | + private Integer sex; | |
| 52 | + | |
| 53 | + @Schema(description = "入职时间范围") | |
| 54 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 55 | + private LocalDateTime[] entryTime; | |
| 56 | + | |
| 57 | + @Schema(description = "职位名称,模糊匹配", example = "Java") | |
| 58 | + private String postName; | |
| 59 | + | |
| 60 | + @Schema(description = "转正时间范围") | |
| 61 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 62 | + private LocalDateTime[] regularTime; | |
| 63 | + | |
| 64 | + @Schema(description = "工作地点,模糊匹配", example = "西湖区") | |
| 65 | + private String workAddress; | |
| 66 | + | |
| 67 | + @Schema(description = "招聘渠道编号", example = "10") | |
| 68 | + private Long channelId; | |
| 69 | + | |
| 70 | + @Schema(description = "聘用形式", example = "1") | |
| 71 | + @InEnum(value = HrmEmployeeTypeEnum.class, message = "聘用形式必须是 {value}") | |
| 72 | + private Integer type; | |
| 73 | + | |
| 74 | + @Schema(description = "入职状态", example = "1") | |
| 75 | + @InEnum(value = HrmEmployeeEntryStatusEnum.class, message = "入职状态必须是 {value}") | |
| 76 | + private Integer entryStatus; | |
| 77 | + | |
| 78 | + @Schema(description = "员工状态", example = "1") | |
| 79 | + @InEnum(value = HrmEmployeeStatusEnum.class, message = "员工状态必须是 {value}") | |
| 80 | + private Integer status; | |
| 81 | + | |
| 82 | + @Schema(description = "员工状态分类", example = "12") | |
| 83 | + @InEnum(value = HrmEmployeeStatusTabEnum.class, message = "员工状态分类必须是 {value}") | |
| 84 | + private Integer statusCategory; | |
| 85 | + | |
| 86 | + @Schema(description = "首页人事概况筛选类型", example = "1") | |
| 87 | + @InEnum(value = HrmEmployeeSurveyTypeEnum.class, message = "首页人事概况筛选类型必须是 {value}") | |
| 88 | + private Integer surveyType; | |
| 89 | + | |
| 90 | + @Schema(description = "首页待办筛选类型", example = "5") | |
| 91 | + @InEnum(value = HrmEmployeeTodoTypeEnum.class, message = "首页待办筛选类型必须是 {value}") | |
| 92 | + private Integer todoType; | |
| 93 | + | |
| 94 | + @Schema(hidden = true) | |
| 95 | + private LocalDateTime[] activeTime; | |
| 96 | + | |
| 97 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeNotifyRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import lombok.experimental.Accessors; | |
| 6 | + | |
| 7 | +@Schema(description = "管理后台 - HRM 员工通知发送结果 Response VO") | |
| 8 | +@Data | |
| 9 | +@Accessors(chain = true) | |
| 10 | +public class HrmEmployeeNotifyRespVO { | |
| 11 | + | |
| 12 | + @Schema(description = "发送成功数量", example = "10") | |
| 13 | + private Integer successCount; | |
| 14 | + | |
| 15 | + @Schema(description = "无后台账号跳过数量", example = "2") | |
| 16 | + private Integer skippedCount; | |
| 17 | + | |
| 18 | + @Schema(description = "发送失败数量", example = "1") | |
| 19 | + private Integer failureCount; | |
| 20 | + | |
| 21 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeePageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.SortablePageParam; | |
| 4 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 5 | +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeEntryStatusEnum; | |
| 6 | +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeStatusEnum; | |
| 7 | +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeStatusTabEnum; | |
| 8 | +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeSurveyTypeEnum; | |
| 9 | +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeTypeEnum; | |
| 10 | +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeTodoTypeEnum; | |
| 11 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 12 | +import lombok.Data; | |
| 13 | +import lombok.EqualsAndHashCode; | |
| 14 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 15 | + | |
| 16 | +import java.time.LocalDateTime; | |
| 17 | +import java.util.List; | |
| 18 | + | |
| 19 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 20 | + | |
| 21 | +@Schema(description = "管理后台 - HRM 员工档案分页 Request VO") | |
| 22 | +@Data | |
| 23 | +@EqualsAndHashCode(callSuper = true) | |
| 24 | +public class HrmEmployeePageReqVO extends SortablePageParam { | |
| 25 | + | |
| 26 | + @Schema(description = "员工编号列表", example = "1,2") | |
| 27 | + private List<Long> ids; | |
| 28 | + | |
| 29 | + @Schema(hidden = true) | |
| 30 | + private List<Long> excludeIds; | |
| 31 | + | |
| 32 | + @Schema(description = "员工姓名、工号或手机号,模糊匹配", example = "张三") | |
| 33 | + private String search; | |
| 34 | + | |
| 35 | + @Schema(description = "员工姓名,模糊匹配", example = "张") | |
| 36 | + private String name; | |
| 37 | + | |
| 38 | + @Schema(description = "工号,模糊匹配", example = "HRM") | |
| 39 | + private String jobNumber; | |
| 40 | + | |
| 41 | + @Schema(description = "手机号,模糊匹配", example = "156") | |
| 42 | + private String mobile; | |
| 43 | + | |
| 44 | + @Schema(description = "部门编号", example = "100") | |
| 45 | + private Long deptId; | |
| 46 | + | |
| 47 | + @Schema(description = "部门编号数组", example = "100,101") | |
| 48 | + private List<Long> deptIds; | |
| 49 | + | |
| 50 | + @Schema(description = "直属上级员工编号", example = "1") | |
| 51 | + private Long leaderEmployeeId; | |
| 52 | + | |
| 53 | + @Schema(description = "性别", example = "1") | |
| 54 | + private Integer sex; | |
| 55 | + | |
| 56 | + @Schema(description = "入职时间范围") | |
| 57 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 58 | + private LocalDateTime[] entryTime; | |
| 59 | + | |
| 60 | + @Schema(description = "职位名称,模糊匹配", example = "Java") | |
| 61 | + private String postName; | |
| 62 | + | |
| 63 | + @Schema(description = "转正时间范围") | |
| 64 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 65 | + private LocalDateTime[] regularTime; | |
| 66 | + | |
| 67 | + @Schema(description = "工作地点,模糊匹配", example = "西湖区") | |
| 68 | + private String workAddress; | |
| 69 | + | |
| 70 | + @Schema(description = "招聘渠道编号", example = "10") | |
| 71 | + private Long channelId; | |
| 72 | + | |
| 73 | + @Schema(description = "聘用形式", example = "1") | |
| 74 | + @InEnum(value = HrmEmployeeTypeEnum.class, message = "聘用形式必须是 {value}") | |
| 75 | + private Integer type; | |
| 76 | + | |
| 77 | + @Schema(description = "入职状态", example = "1") | |
| 78 | + @InEnum(value = HrmEmployeeEntryStatusEnum.class, message = "入职状态必须是 {value}") | |
| 79 | + private Integer entryStatus; | |
| 80 | + | |
| 81 | + @Schema(description = "员工状态", example = "1") | |
| 82 | + @InEnum(value = HrmEmployeeStatusEnum.class, message = "员工状态必须是 {value}") | |
| 83 | + private Integer status; | |
| 84 | + | |
| 85 | + @Schema(description = "员工状态分类", example = "12") | |
| 86 | + @InEnum(value = HrmEmployeeStatusTabEnum.class, message = "员工状态分类必须是 {value}") | |
| 87 | + private Integer statusCategory; | |
| 88 | + | |
| 89 | + @Schema(description = "首页人事概况筛选类型", example = "1") | |
| 90 | + @InEnum(value = HrmEmployeeSurveyTypeEnum.class, message = "首页人事概况筛选类型必须是 {value}") | |
| 91 | + private Integer surveyType; | |
| 92 | + | |
| 93 | + @Schema(description = "首页待办筛选类型", example = "5") | |
| 94 | + @InEnum(value = HrmEmployeeTodoTypeEnum.class, message = "首页待办筛选类型必须是 {value}") | |
| 95 | + private Integer todoType; | |
| 96 | + | |
| 97 | + @Schema(hidden = true) | |
| 98 | + private LocalDateTime[] activeTime; | |
| 99 | + | |
| 100 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeQuitReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 4 | +import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeQuitReasonEnum; | |
| 5 | +import cn.iocoder.yudao.module.hrm.enums.employee.employment.HrmEmployeeQuitTypeEnum; | |
| 6 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
| 7 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 8 | +import jakarta.validation.constraints.AssertTrue; | |
| 9 | +import jakarta.validation.constraints.NotNull; | |
| 10 | +import jakarta.validation.constraints.Size; | |
| 11 | +import lombok.Data; | |
| 12 | + | |
| 13 | +import java.time.LocalDateTime; | |
| 14 | + | |
| 15 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.isAfterOrEqual; | |
| 16 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.isBeforeOrEqual; | |
| 17 | + | |
| 18 | +@Schema(description = "管理后台 - HRM 员工离职 Request VO") | |
| 19 | +@Data | |
| 20 | +public class HrmEmployeeQuitReqVO { | |
| 21 | + | |
| 22 | + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 23 | + @NotNull(message = "员工编号不能为空") | |
| 24 | + private Long employeeId; | |
| 25 | + | |
| 26 | + @Schema(description = "计划离职时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 27 | + @NotNull(message = "计划离职时间不能为空") | |
| 28 | + private LocalDateTime planQuitTime; | |
| 29 | + | |
| 30 | + @Schema(description = "申请离职时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 31 | + @NotNull(message = "申请离职时间不能为空") | |
| 32 | + private LocalDateTime applyQuitTime; | |
| 33 | + | |
| 34 | + @Schema(description = "薪资结算时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 35 | + @NotNull(message = "薪资结算时间不能为空") | |
| 36 | + private LocalDateTime salarySettlementTime; | |
| 37 | + | |
| 38 | + @Schema(description = "离职类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 39 | + @NotNull(message = "离职类型不能为空") | |
| 40 | + @InEnum(value = HrmEmployeeQuitTypeEnum.class, message = "离职类型必须是 {value}") | |
| 41 | + private Integer type; | |
| 42 | + | |
| 43 | + @Schema(description = "离职原因", example = "1") | |
| 44 | + @InEnum(value = HrmEmployeeQuitReasonEnum.class, message = "离职原因必须是 {value}") | |
| 45 | + private Integer reason; | |
| 46 | + | |
| 47 | + @Schema(description = "备注", example = "个人原因离职") | |
| 48 | + @Size(max = 500, message = "备注不能超过 500 个字符") | |
| 49 | + private String remark; | |
| 50 | + | |
| 51 | + @AssertTrue(message = "申请离职时间不能晚于计划离职时间") | |
| 52 | + @JsonIgnore | |
| 53 | + public boolean isApplyQuitTimeValid() { | |
| 54 | + return applyQuitTime == null || planQuitTime == null | |
| 55 | + || isBeforeOrEqual(applyQuitTime.toLocalDate(), planQuitTime.toLocalDate()); | |
| 56 | + } | |
| 57 | + | |
| 58 | + @AssertTrue(message = "薪资结算时间不能早于计划离职时间") | |
| 59 | + @JsonIgnore | |
| 60 | + public boolean isSalarySettlementTimeValid() { | |
| 61 | + return salarySettlementTime == null || planQuitTime == null | |
| 62 | + || isAfterOrEqual(salarySettlementTime.toLocalDate(), planQuitTime.toLocalDate()); | |
| 63 | + } | |
| 64 | + | |
| 65 | + @AssertTrue(message = "离职原因与离职类型不匹配") | |
| 66 | + @JsonIgnore | |
| 67 | + public boolean isReasonValid() { | |
| 68 | + if (type == null || HrmEmployeeQuitTypeEnum.RETIREMENT.getType().equals(type)) { | |
| 69 | + return true; | |
| 70 | + } | |
| 71 | + HrmEmployeeQuitReasonEnum quitReason = HrmEmployeeQuitReasonEnum.valueOf(reason); | |
| 72 | + return quitReason != null && quitReason.getQuitType().equals(type); | |
| 73 | + } | |
| 74 | + | |
| 75 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeRehireReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee; | |
| 2 | + | |
| 3 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import jakarta.validation.constraints.AssertTrue; | |
| 6 | +import jakarta.validation.constraints.NotNull; | |
| 7 | +import lombok.Data; | |
| 8 | +import lombok.EqualsAndHashCode; | |
| 9 | + | |
| 10 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.beforeOrEqualNow; | |
| 11 | + | |
| 12 | +@Schema(description = "管理后台 - HRM 员工再入职 Request VO") | |
| 13 | +@Data | |
| 14 | +@EqualsAndHashCode(callSuper = true) | |
| 15 | +public class HrmEmployeeRehireReqVO extends HrmEmployeeSaveReqVO { | |
| 16 | + | |
| 17 | + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 18 | + @NotNull(message = "员工编号不能为空") | |
| 19 | + private Long employeeId; | |
| 20 | + | |
| 21 | + @AssertTrue(message = "入职时间不能晚于当前时间") | |
| 22 | + @JsonIgnore | |
| 23 | + public boolean isEntryTimeValid() { | |
| 24 | + return getEntryTime() == null || beforeOrEqualNow(getEntryTime()); | |
| 25 | + } | |
| 26 | + | |
| 27 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; | |
| 4 | +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; | |
| 5 | +import cn.iocoder.yudao.module.hrm.enums.DictTypeConstants; | |
| 6 | +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; | |
| 7 | +import cn.idev.excel.annotation.ExcelProperty; | |
| 8 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 9 | +import lombok.Data; | |
| 10 | +import lombok.experimental.Accessors; | |
| 11 | + | |
| 12 | +import java.time.LocalDateTime; | |
| 13 | + | |
| 14 | +@Schema(description = "管理后台 - HRM 员工档案 Response VO") | |
| 15 | +@Data | |
| 16 | +@Accessors(chain = true) | |
| 17 | +@ExcelIgnoreUnannotated | |
| 18 | +public class HrmEmployeeRespVO { | |
| 19 | + | |
| 20 | + @ExcelProperty(value = "员工编号", index = 0) | |
| 21 | + @Schema(description = "员工档案编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 22 | + private Long id; | |
| 23 | + | |
| 24 | + @ExcelProperty(value = "员工姓名", index = 1) | |
| 25 | + @Schema(description = "员工姓名", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三") | |
| 26 | + private String name; | |
| 27 | + | |
| 28 | + @ExcelProperty(value = "工号", index = 2) | |
| 29 | + @Schema(description = "工号", example = "HRM001") | |
| 30 | + private String jobNumber; | |
| 31 | + | |
| 32 | + @Schema(description = "系统用户编号", example = "1024") | |
| 33 | + private Long userId; | |
| 34 | + | |
| 35 | + @ExcelProperty(value = "绑定用户", index = 17) | |
| 36 | + @Schema(description = "后台用户昵称", example = "芋道") | |
| 37 | + private String userNickname; | |
| 38 | + | |
| 39 | + @ExcelProperty(value = "手机号", index = 3) | |
| 40 | + @Schema(description = "手机号", example = "15601691300") | |
| 41 | + private String mobile; | |
| 42 | + | |
| 43 | + @Schema(description = "国家地区", example = "中国") | |
| 44 | + private String country; | |
| 45 | + | |
| 46 | + @Schema(description = "民族", example = "汉族") | |
| 47 | + private String nation; | |
| 48 | + | |
| 49 | + @Schema(description = "证件类型", example = "1") | |
| 50 | + private Integer idType; | |
| 51 | + | |
| 52 | + @Schema(description = "证件号码", example = "310101199001011234") | |
| 53 | + private String idNumber; | |
| 54 | + | |
| 55 | + @Schema(description = "性别", example = "1") | |
| 56 | + private Integer sex; | |
| 57 | + | |
| 58 | + @Schema(description = "邮箱", example = "hrm@example.com") | |
| 59 | + private String email; | |
| 60 | + | |
| 61 | + @Schema(description = "籍贯", example = "浙江杭州") | |
| 62 | + private String nativePlace; | |
| 63 | + | |
| 64 | + @Schema(description = "出生时间") | |
| 65 | + private LocalDateTime birthday; | |
| 66 | + | |
| 67 | + @Schema(description = "年龄", example = "28") | |
| 68 | + private Integer age; | |
| 69 | + | |
| 70 | + @Schema(description = "户籍地址", example = "杭州市西湖区") | |
| 71 | + private String address; | |
| 72 | + | |
| 73 | + @Schema(description = "最高学历", example = "8") | |
| 74 | + private Integer highestEducation; | |
| 75 | + | |
| 76 | + @Schema(description = "部门编号", example = "100") | |
| 77 | + private Long deptId; | |
| 78 | + | |
| 79 | + @ExcelProperty(value = "部门", index = 4) | |
| 80 | + @Schema(description = "部门名称", example = "研发部") | |
| 81 | + private String deptName; | |
| 82 | + | |
| 83 | + @Schema(description = "直属上级员工编号", example = "1") | |
| 84 | + private Long leaderEmployeeId; | |
| 85 | + | |
| 86 | + @ExcelProperty(value = "直属上级", index = 7) | |
| 87 | + @Schema(description = "直属上级员工姓名", example = "李四") | |
| 88 | + private String leaderEmployeeName; | |
| 89 | + | |
| 90 | + @ExcelProperty(value = "入职状态", index = 8, converter = DictConvert.class) | |
| 91 | + @DictFormat(DictTypeConstants.HRM_EMPLOYEE_ENTRY_STATUS) | |
| 92 | + @Schema(description = "入职状态", example = "1") | |
| 93 | + private Integer entryStatus; | |
| 94 | + | |
| 95 | + @ExcelProperty(value = "员工状态", index = 9, converter = DictConvert.class) | |
| 96 | + @DictFormat(DictTypeConstants.HRM_EMPLOYEE_STATUS) | |
| 97 | + @Schema(description = "员工状态", example = "1") | |
| 98 | + private Integer status; | |
| 99 | + | |
| 100 | + @ExcelProperty(value = "聘用形式", index = 10, converter = DictConvert.class) | |
| 101 | + @DictFormat(DictTypeConstants.HRM_EMPLOYEE_TYPE) | |
| 102 | + @Schema(description = "聘用形式", example = "1") | |
| 103 | + private Integer type; | |
| 104 | + | |
| 105 | + @ExcelProperty(value = "入职时间", index = 11) | |
| 106 | + @Schema(description = "入职时间") | |
| 107 | + private LocalDateTime entryTime; | |
| 108 | + | |
| 109 | + @Schema(description = "试用期,单位月;0 表示无试用期", example = "3") | |
| 110 | + private Integer probation; | |
| 111 | + | |
| 112 | + @ExcelProperty(value = "转正时间", index = 12) | |
| 113 | + @Schema(description = "转正时间") | |
| 114 | + private LocalDateTime regularTime; | |
| 115 | + | |
| 116 | + @ExcelProperty(value = "离职时间", index = 13) | |
| 117 | + @Schema(description = "离职时间") | |
| 118 | + private LocalDateTime leaveTime; | |
| 119 | + | |
| 120 | + @ExcelProperty(value = "岗位", index = 5) | |
| 121 | + @Schema(description = "职位名称", example = "Java 工程师") | |
| 122 | + private String postName; | |
| 123 | + | |
| 124 | + @ExcelProperty(value = "岗位职级", index = 6) | |
| 125 | + @Schema(description = "岗位职级", example = "P6") | |
| 126 | + private String postLevel; | |
| 127 | + | |
| 128 | + @ExcelProperty(value = "工作城市", index = 14) | |
| 129 | + @Schema(description = "工作城市", example = "上海") | |
| 130 | + private String workCity; | |
| 131 | + | |
| 132 | + @ExcelProperty(value = "工作地点", index = 15) | |
| 133 | + @Schema(description = "工作地址", example = "浦东新区") | |
| 134 | + private String workAddress; | |
| 135 | + | |
| 136 | + @Schema(description = "工作详细地址", example = "张江高科 1 号楼") | |
| 137 | + private String workDetailAddress; | |
| 138 | + | |
| 139 | + @Schema(description = "招聘渠道编号", example = "10") | |
| 140 | + private Long channelId; | |
| 141 | + | |
| 142 | + @ExcelProperty(value = "招聘渠道", index = 16) | |
| 143 | + @Schema(description = "招聘渠道名称", example = "BOSS直聘") | |
| 144 | + private String channelName; | |
| 145 | + | |
| 146 | + @Schema(description = "司龄开始时间") | |
| 147 | + private LocalDateTime companyAgeStartTime; | |
| 148 | + | |
| 149 | + @Schema(description = "司龄,单位年", example = "2") | |
| 150 | + private Integer companyAge; | |
| 151 | + | |
| 152 | + @Schema(description = "关联招聘候选人编号", example = "200") | |
| 153 | + private Long candidateId; | |
| 154 | + | |
| 155 | + // ==================== 工资卡信息 ==================== | |
| 156 | + | |
| 157 | + @Schema(description = "银行卡号", example = "622202600001") | |
| 158 | + private String salaryCardNumber; | |
| 159 | + | |
| 160 | + @Schema(description = "开户地区编号", example = "440300") | |
| 161 | + private Integer salaryCardAreaId; | |
| 162 | + | |
| 163 | + @Schema(description = "开户地区名称", example = "广东省 深圳市") | |
| 164 | + private String salaryCardAreaName; | |
| 165 | + | |
| 166 | + @Schema(description = "银行名称", example = "招商银行") | |
| 167 | + private String salaryCardBankName; | |
| 168 | + | |
| 169 | + @Schema(description = "开户支行名称", example = "科技园支行") | |
| 170 | + private String salaryCardBankBranchName; | |
| 171 | + | |
| 172 | + // ==================== 社保信息 ==================== | |
| 173 | + | |
| 174 | + @Schema(description = "个人社保账号", example = "SB20260001") | |
| 175 | + private String socialSecurityNumber; | |
| 176 | + | |
| 177 | + @Schema(description = "个人公积金账号", example = "GJJ20260001") | |
| 178 | + private String accumulationFundNumber; | |
| 179 | + | |
| 180 | + @Schema(description = "备注", example = "首批迁移员工") | |
| 181 | + private String remark; | |
| 182 | + | |
| 183 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 184 | + private LocalDateTime createTime; | |
| 185 | + | |
| 186 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.IdcardUtil; | |
| 4 | +import cn.hutool.core.util.ObjUtil; | |
| 5 | +import cn.hutool.core.util.StrUtil; | |
| 6 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 7 | +import cn.iocoder.yudao.framework.common.validation.Mobile; | |
| 8 | +import cn.iocoder.yudao.module.hrm.enums.employee.experience.HrmEmployeeEducationEnum; | |
| 9 | +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeEntryStatusEnum; | |
| 10 | +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeIdTypeEnum; | |
| 11 | +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeStatusEnum; | |
| 12 | +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeTypeEnum; | |
| 13 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
| 14 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 15 | +import jakarta.validation.constraints.AssertTrue; | |
| 16 | +import jakarta.validation.constraints.Email; | |
| 17 | +import jakarta.validation.constraints.Max; | |
| 18 | +import jakarta.validation.constraints.Min; | |
| 19 | +import jakarta.validation.constraints.NotBlank; | |
| 20 | +import jakarta.validation.constraints.NotNull; | |
| 21 | +import jakarta.validation.constraints.PastOrPresent; | |
| 22 | +import jakarta.validation.constraints.Size; | |
| 23 | +import lombok.Data; | |
| 24 | + | |
| 25 | +import java.time.LocalDateTime; | |
| 26 | + | |
| 27 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.isBeforeOrEqual; | |
| 28 | + | |
| 29 | +@Schema(description = "管理后台 - HRM 员工档案新增/修改 Request VO") | |
| 30 | +@Data | |
| 31 | +public class HrmEmployeeSaveReqVO { | |
| 32 | + | |
| 33 | + @Schema(description = "员工档案编号", example = "1024") | |
| 34 | + private Long id; | |
| 35 | + | |
| 36 | + @Schema(description = "员工姓名", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三") | |
| 37 | + @NotBlank(message = "员工姓名不能为空") | |
| 38 | + @Size(max = 255, message = "员工姓名不能超过 255 个字符") | |
| 39 | + private String name; | |
| 40 | + | |
| 41 | + @Schema(description = "工号", example = "HRM001") | |
| 42 | + @Size(max = 64, message = "工号不能超过 64 个字符") | |
| 43 | + private String jobNumber; | |
| 44 | + | |
| 45 | + @Schema(description = "系统用户编号", example = "1024") | |
| 46 | + private Long userId; | |
| 47 | + | |
| 48 | + @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300") | |
| 49 | + @NotBlank(message = "手机号不能为空") | |
| 50 | + @Mobile | |
| 51 | + private String mobile; | |
| 52 | + | |
| 53 | + @Schema(description = "国家地区", example = "中国") | |
| 54 | + @Size(max = 64, message = "国家地区不能超过 64 个字符") | |
| 55 | + private String country; | |
| 56 | + | |
| 57 | + @Schema(description = "民族", example = "汉族") | |
| 58 | + @Size(max = 64, message = "民族不能超过 64 个字符") | |
| 59 | + private String nation; | |
| 60 | + | |
| 61 | + @Schema(description = "证件类型", example = "1") | |
| 62 | + @InEnum(value = HrmEmployeeIdTypeEnum.class, message = "证件类型必须是 {value}") | |
| 63 | + private Integer idType; | |
| 64 | + | |
| 65 | + @Schema(description = "证件号码", example = "310101199001011234") | |
| 66 | + @Size(max = 255, message = "证件号码不能超过 255 个字符") | |
| 67 | + private String idNumber; | |
| 68 | + | |
| 69 | + @Schema(description = "性别", example = "1") | |
| 70 | + private Integer sex; | |
| 71 | + | |
| 72 | + @Schema(description = "邮箱", example = "hrm@example.com") | |
| 73 | + @Email(message = "邮箱格式不正确") | |
| 74 | + @Size(max = 255, message = "邮箱不能超过 255 个字符") | |
| 75 | + private String email; | |
| 76 | + | |
| 77 | + @Schema(description = "籍贯", example = "浙江杭州") | |
| 78 | + @Size(max = 128, message = "籍贯不能超过 128 个字符") | |
| 79 | + private String nativePlace; | |
| 80 | + | |
| 81 | + @Schema(description = "出生时间") | |
| 82 | + @PastOrPresent(message = "出生时间不能晚于当前时间") | |
| 83 | + private LocalDateTime birthday; | |
| 84 | + | |
| 85 | + @Schema(description = "年龄", example = "28") | |
| 86 | + private Integer age; | |
| 87 | + | |
| 88 | + @Schema(description = "户籍地址", example = "杭州市西湖区") | |
| 89 | + @Size(max = 255, message = "户籍地址不能超过 255 个字符") | |
| 90 | + private String address; | |
| 91 | + | |
| 92 | + @Schema(description = "最高学历", example = "8") | |
| 93 | + @InEnum(value = HrmEmployeeEducationEnum.class, message = "最高学历必须是 {value}") | |
| 94 | + private Integer highestEducation; | |
| 95 | + | |
| 96 | + @Schema(description = "部门编号", example = "100") | |
| 97 | + private Long deptId; | |
| 98 | + | |
| 99 | + @Schema(description = "直属上级员工编号", example = "1") | |
| 100 | + private Long leaderEmployeeId; | |
| 101 | + | |
| 102 | + @Schema(description = "入职状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 103 | + @NotNull(message = "入职状态不能为空") | |
| 104 | + @InEnum(value = HrmEmployeeEntryStatusEnum.class, message = "入职状态必须是 {value}") | |
| 105 | + private Integer entryStatus; | |
| 106 | + | |
| 107 | + @Schema(description = "员工状态", example = "1") | |
| 108 | + @InEnum(value = HrmEmployeeStatusEnum.class, message = "员工状态必须是 {value}") | |
| 109 | + private Integer status; | |
| 110 | + | |
| 111 | + @Schema(description = "聘用形式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 112 | + @NotNull(message = "聘用形式不能为空") | |
| 113 | + @InEnum(value = HrmEmployeeTypeEnum.class, message = "聘用形式必须是 {value}") | |
| 114 | + private Integer type; | |
| 115 | + | |
| 116 | + @Schema(description = "入职时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 117 | + @NotNull(message = "入职时间不能为空") | |
| 118 | + private LocalDateTime entryTime; | |
| 119 | + | |
| 120 | + @Schema(description = "试用期,单位月;0 表示无试用期", example = "3") | |
| 121 | + @Min(value = 0, message = "试用期不能小于 0 个月") | |
| 122 | + @Max(value = 6, message = "试用期不能超过 6 个月") | |
| 123 | + private Integer probation; | |
| 124 | + | |
| 125 | + @Schema(description = "转正时间") | |
| 126 | + private LocalDateTime regularTime; | |
| 127 | + | |
| 128 | + @Schema(description = "离职时间") | |
| 129 | + private LocalDateTime leaveTime; | |
| 130 | + | |
| 131 | + @Schema(description = "职位名称", example = "Java 工程师") | |
| 132 | + @Size(max = 255, message = "职位名称不能超过 255 个字符") | |
| 133 | + private String postName; | |
| 134 | + | |
| 135 | + @Schema(description = "岗位职级", example = "P6") | |
| 136 | + @Size(max = 255, message = "岗位职级不能超过 255 个字符") | |
| 137 | + private String postLevel; | |
| 138 | + | |
| 139 | + @Schema(description = "工作城市", example = "上海") | |
| 140 | + @Size(max = 64, message = "工作城市不能超过 64 个字符") | |
| 141 | + private String workCity; | |
| 142 | + | |
| 143 | + @Schema(description = "工作地址", example = "浦东新区") | |
| 144 | + @Size(max = 255, message = "工作地址不能超过 255 个字符") | |
| 145 | + private String workAddress; | |
| 146 | + | |
| 147 | + @Schema(description = "工作详细地址", example = "张江高科 1 号楼") | |
| 148 | + @Size(max = 255, message = "工作详细地址不能超过 255 个字符") | |
| 149 | + private String workDetailAddress; | |
| 150 | + | |
| 151 | + @Schema(description = "招聘渠道编号", example = "10") | |
| 152 | + private Long channelId; | |
| 153 | + | |
| 154 | + @Schema(description = "司龄开始时间") | |
| 155 | + private LocalDateTime companyAgeStartTime; | |
| 156 | + | |
| 157 | + @Schema(description = "司龄,单位年", example = "2") | |
| 158 | + private Integer companyAge; | |
| 159 | + | |
| 160 | + @Schema(description = "关联招聘候选人编号", example = "200") | |
| 161 | + private Long candidateId; | |
| 162 | + | |
| 163 | + @Schema(description = "备注", example = "首批迁移员工") | |
| 164 | + @Size(max = 500, message = "备注不能超过 500 个字符") | |
| 165 | + private String remark; | |
| 166 | + | |
| 167 | + @AssertTrue(message = "身份证号码格式不正确") | |
| 168 | + @JsonIgnore | |
| 169 | + public boolean isIdNumberValid() { | |
| 170 | + return ObjUtil.notEqual(HrmEmployeeIdTypeEnum.ID_CARD.getType(), idType) | |
| 171 | + || StrUtil.isBlank(idNumber) || IdcardUtil.isValidCard(idNumber); | |
| 172 | + } | |
| 173 | + | |
| 174 | + @AssertTrue(message = "员工状态与聘用形式不匹配") | |
| 175 | + @JsonIgnore | |
| 176 | + public boolean isStatusValid() { | |
| 177 | + return ObjUtil.notEqual(HrmEmployeeTypeEnum.INFORMAL.getType(), type) | |
| 178 | + || status == null || HrmEmployeeStatusEnum.INFORMAL_STATUSES.contains(status); | |
| 179 | + } | |
| 180 | + | |
| 181 | + @AssertTrue(message = "非正式员工状态不能为空") | |
| 182 | + @JsonIgnore | |
| 183 | + public boolean isStatusRequired() { | |
| 184 | + return ObjUtil.notEqual(HrmEmployeeTypeEnum.INFORMAL.getType(), type) || status != null; | |
| 185 | + } | |
| 186 | + | |
| 187 | + @AssertTrue(message = "在职员工工号不能为空") | |
| 188 | + @JsonIgnore | |
| 189 | + public boolean isJobNumberValid() { | |
| 190 | + return ObjUtil.notEqual(HrmEmployeeEntryStatusEnum.ACTIVE.getStatus(), entryStatus) | |
| 191 | + || StrUtil.isNotBlank(jobNumber); | |
| 192 | + } | |
| 193 | + | |
| 194 | + @AssertTrue(message = "正式员工试用期不能为空") | |
| 195 | + @JsonIgnore | |
| 196 | + public boolean isProbationValid() { | |
| 197 | + return ObjUtil.notEqual(HrmEmployeeTypeEnum.FORMAL.getType(), type) || probation != null; | |
| 198 | + } | |
| 199 | + | |
| 200 | + @AssertTrue(message = "司龄起算时间不能晚于入职时间") | |
| 201 | + @JsonIgnore | |
| 202 | + public boolean isCompanyAgeStartTimeValid() { | |
| 203 | + return entryTime == null || companyAgeStartTime == null | |
| 204 | + || isBeforeOrEqual(companyAgeStartTime, entryTime); | |
| 205 | + } | |
| 206 | + | |
| 207 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/employee/HrmEmployeeStatusCountRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.employee; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.AllArgsConstructor; | |
| 5 | +import lombok.Data; | |
| 6 | + | |
| 7 | +@Schema(description = "管理后台 - HRM 员工状态数量 Response VO") | |
| 8 | +@Data | |
| 9 | +@AllArgsConstructor | |
| 10 | +public class HrmEmployeeStatusCountRespVO { | |
| 11 | + | |
| 12 | + @Schema(description = "状态页签", requiredMode = Schema.RequiredMode.REQUIRED, example = "12") | |
| 13 | + private Integer status; | |
| 14 | + | |
| 15 | + @Schema(description = "数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") | |
| 16 | + private Long count; | |
| 17 | + | |
| 18 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/file/HrmEmployeeFileRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.file; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.time.LocalDateTime; | |
| 7 | + | |
| 8 | +@Schema(description = "管理后台 - HRM 员工材料附件 Response VO") | |
| 9 | +@Data | |
| 10 | +public class HrmEmployeeFileRespVO { | |
| 11 | + | |
| 12 | + @Schema(description = "附件编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 13 | + private Long id; | |
| 14 | + | |
| 15 | + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 16 | + private Long employeeId; | |
| 17 | + | |
| 18 | + @Schema(description = "附件类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "11") | |
| 19 | + private Integer type; | |
| 20 | + | |
| 21 | + @Schema(description = "附件地址", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 22 | + private String url; | |
| 23 | + | |
| 24 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 25 | + private LocalDateTime createTime; | |
| 26 | + | |
| 27 | +} | ... | ... |
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/file/HrmEmployeeFileSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.file; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 4 | +import cn.iocoder.yudao.module.hrm.enums.employee.info.HrmEmployeeFileTypeEnum; | |
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 6 | +import jakarta.validation.constraints.NotBlank; | |
| 7 | +import jakarta.validation.constraints.NotNull; | |
| 8 | +import jakarta.validation.constraints.Size; | |
| 9 | +import lombok.Data; | |
| 10 | + | |
| 11 | +import java.util.List; | |
| 12 | + | |
| 13 | +@Schema(description = "管理后台 - HRM 员工材料附件保存 Request VO") | |
| 14 | +@Data | |
| 15 | +public class HrmEmployeeFileSaveReqVO { | |
| 16 | + | |
| 17 | + @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 18 | + @NotNull(message = "员工编号不能为空") | |
| 19 | + private Long employeeId; | |
| 20 | + | |
| 21 | + @Schema(description = "附件类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "11") | |
| 22 | + @NotNull(message = "附件类型不能为空") | |
| 23 | + @InEnum(value = HrmEmployeeFileTypeEnum.class, message = "附件类型必须是 {value}") | |
| 24 | + private Integer type; | |
| 25 | + | |
| 26 | + @Schema(description = "附件地址数组", requiredMode = Schema.RequiredMode.REQUIRED, | |
| 27 | + example = "[https://example.com/archive.pdf]") | |
| 28 | + @NotNull(message = "附件地址数组不能为空") | |
| 29 | + @Size(max = 20, message = "同类附件不能超过 20 个") | |
| 30 | + private List<@NotBlank(message = "附件地址不能为空") | |
| 31 | + @Size(max = 512, message = "附件地址长度不能超过 512 个字符") String> fileUrls; | |
| 32 | + | |
| 33 | +} | ... | ... |