Commit d7944d6c19559ba90760c5823df8c70dea40d328
Merge remote-tracking branch 'origin/master-jdk17' into master-jdk17
Showing
954 changed files
with
112551 additions
and
23 deletions
Too many changes.
To preserve performance only 100 of 954 files are displayed.
pom.xml
| ... | ... | @@ -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> | ... | ... |
yudao-dependencies/pom.xml
| ... | ... | @@ -58,6 +58,7 @@ |
| 58 | 58 | <hutool-5.version>5.8.46</hutool-5.version> |
| 59 | 59 | <hutool-6.version>6.0.0-M22</hutool-6.version> |
| 60 | 60 | <fastexcel.version>1.3.0</fastexcel.version> |
| 61 | + <aviator.version>5.3.3</aviator.version> | |
| 61 | 62 | <velocity.version>2.4.1</velocity.version> |
| 62 | 63 | <fastjson2.version>2.0.63</fastjson2.version> |
| 63 | 64 | <guava.version>33.6.0-jre</guava.version> |
| ... | ... | @@ -506,6 +507,12 @@ |
| 506 | 507 | </dependency> |
| 507 | 508 | |
| 508 | 509 | <dependency> |
| 510 | + <groupId>com.googlecode.aviator</groupId> | |
| 511 | + <artifactId>aviator</artifactId> | |
| 512 | + <version>${aviator.version}</version> | |
| 513 | + </dependency> | |
| 514 | + | |
| 515 | + <dependency> | |
| 509 | 516 | <groupId>org.apache.tika</groupId> |
| 510 | 517 | <artifactId>tika-core</artifactId> <!-- 文件类型的识别 --> |
| 511 | 518 | <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-fms/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 | + <modelVersion>4.0.0</modelVersion> | |
| 6 | + <parent> | |
| 7 | + <groupId>cn.iocoder.boot</groupId> | |
| 8 | + <artifactId>yudao</artifactId> | |
| 9 | + <version>${revision}</version> | |
| 10 | + </parent> | |
| 11 | + | |
| 12 | + <artifactId>yudao-module-fms</artifactId> | |
| 13 | + <packaging>jar</packaging> | |
| 14 | + | |
| 15 | + <name>${project.artifactId}</name> | |
| 16 | + <description> | |
| 17 | + fms 包下,财务管理(Financial Management System) | |
| 18 | + 例如说:凭证、账簿、报表、结账等等 | |
| 19 | + </description> | |
| 20 | + | |
| 21 | + <dependencies> | |
| 22 | + <dependency> | |
| 23 | + <groupId>cn.iocoder.boot</groupId> | |
| 24 | + <artifactId>yudao-module-system</artifactId> | |
| 25 | + <version>${revision}</version> | |
| 26 | + </dependency> | |
| 27 | + <dependency> | |
| 28 | + <groupId>cn.iocoder.boot</groupId> | |
| 29 | + <artifactId>yudao-module-infra</artifactId> | |
| 30 | + <version>${revision}</version> | |
| 31 | + </dependency> | |
| 32 | + | |
| 33 | + <!-- Web 相关 --> | |
| 34 | + <dependency> | |
| 35 | + <groupId>cn.iocoder.boot</groupId> | |
| 36 | + <artifactId>yudao-spring-boot-starter-web</artifactId> | |
| 37 | + </dependency> | |
| 38 | + <dependency> | |
| 39 | + <groupId>cn.iocoder.boot</groupId> | |
| 40 | + <artifactId>yudao-spring-boot-starter-security</artifactId> | |
| 41 | + </dependency> | |
| 42 | + <dependency> | |
| 43 | + <groupId>cn.iocoder.boot</groupId> | |
| 44 | + <artifactId>yudao-spring-boot-starter-excel</artifactId> | |
| 45 | + </dependency> | |
| 46 | + | |
| 47 | + <!-- DB 相关 --> | |
| 48 | + <dependency> | |
| 49 | + <groupId>cn.iocoder.boot</groupId> | |
| 50 | + <artifactId>yudao-spring-boot-starter-mybatis</artifactId> | |
| 51 | + </dependency> | |
| 52 | + | |
| 53 | + <!-- 现金流量表公式计算 --> | |
| 54 | + <dependency> | |
| 55 | + <groupId>com.googlecode.aviator</groupId> | |
| 56 | + <artifactId>aviator</artifactId> | |
| 57 | + </dependency> | |
| 58 | + | |
| 59 | + <!-- Test 测试相关 --> | |
| 60 | + <dependency> | |
| 61 | + <groupId>cn.iocoder.boot</groupId> | |
| 62 | + <artifactId>yudao-spring-boot-starter-test</artifactId> | |
| 63 | + <scope>test</scope> | |
| 64 | + </dependency> | |
| 65 | + </dependencies> | |
| 66 | + | |
| 67 | +</project> | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/closing/FmsClosingPeriodController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.closing; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.module.fms.controller.admin.closing.vo.FmsClosingOverviewRespVO; | |
| 5 | +import cn.iocoder.yudao.module.fms.controller.admin.closing.vo.FmsClosingQueryReqVO; | |
| 6 | +import cn.iocoder.yudao.module.fms.service.closing.FmsClosingPeriodService; | |
| 7 | +import io.swagger.v3.oas.annotations.Operation; | |
| 8 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 9 | +import jakarta.annotation.Resource; | |
| 10 | +import jakarta.validation.Valid; | |
| 11 | +import jakarta.validation.constraints.NotNull; | |
| 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.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 static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 23 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | |
| 24 | + | |
| 25 | +@Tag(name = "管理后台 - FMS 结账期间") | |
| 26 | +@RestController | |
| 27 | +@RequestMapping("/fms/closing/period") | |
| 28 | +@Validated | |
| 29 | +public class FmsClosingPeriodController { | |
| 30 | + | |
| 31 | + @Resource | |
| 32 | + private FmsClosingPeriodService closingPeriodService; | |
| 33 | + | |
| 34 | + @GetMapping("/current-month") | |
| 35 | + @Operation(summary = "获得当前会计期间") | |
| 36 | + public CommonResult<String> getCurrentMonth(@RequestParam("accountSetId") @NotNull Long accountSetId) { | |
| 37 | + return success(closingPeriodService.getCurrentMonth(accountSetId, getLoginUserId())); | |
| 38 | + } | |
| 39 | + | |
| 40 | + @GetMapping("/overview") | |
| 41 | + @Operation(summary = "获得结账概况") | |
| 42 | + @PreAuthorize("@ss.hasPermission('fms:closing:query')") | |
| 43 | + public CommonResult<FmsClosingOverviewRespVO> getClosingOverview(@Valid FmsClosingQueryReqVO queryReqVO) { | |
| 44 | + return success(closingPeriodService.getClosingOverview(queryReqVO, getLoginUserId())); | |
| 45 | + } | |
| 46 | + | |
| 47 | + @PutMapping("/close") | |
| 48 | + @Operation(summary = "结账") | |
| 49 | + @PreAuthorize("@ss.hasPermission('fms:closing:close')") | |
| 50 | + public CommonResult<Boolean> closePeriod(@Valid @RequestBody FmsClosingQueryReqVO queryReqVO) { | |
| 51 | + closingPeriodService.closePeriod(queryReqVO, getLoginUserId()); | |
| 52 | + return success(true); | |
| 53 | + } | |
| 54 | + | |
| 55 | + @DeleteMapping("/cancel") | |
| 56 | + @Operation(summary = "反结账") | |
| 57 | + @PreAuthorize("@ss.hasPermission('fms:closing:cancel')") | |
| 58 | + public CommonResult<Boolean> cancelClosePeriod(@Valid FmsClosingQueryReqVO queryReqVO) { | |
| 59 | + closingPeriodService.cancelClosePeriod(queryReqVO, getLoginUserId()); | |
| 60 | + return success(true); | |
| 61 | + } | |
| 62 | + | |
| 63 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/closing/FmsClosingSchemeController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.closing; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.module.fms.controller.admin.closing.vo.FmsClosingQueryReqVO; | |
| 5 | +import cn.iocoder.yudao.module.fms.controller.admin.closing.vo.FmsClosingSchemeRespVO; | |
| 6 | +import cn.iocoder.yudao.module.fms.controller.admin.closing.vo.FmsClosingSchemeSaveReqVO; | |
| 7 | +import cn.iocoder.yudao.module.fms.controller.admin.closing.vo.FmsProfitLossSettingsSaveReqVO; | |
| 8 | +import cn.iocoder.yudao.module.fms.controller.admin.closing.vo.FmsSpecialClosingSettingsSaveReqVO; | |
| 9 | +import cn.iocoder.yudao.module.fms.service.closing.FmsClosingSchemeService; | |
| 10 | +import io.swagger.v3.oas.annotations.Operation; | |
| 11 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 12 | +import jakarta.annotation.Resource; | |
| 13 | +import jakarta.validation.Valid; | |
| 14 | +import jakarta.validation.constraints.NotNull; | |
| 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.PostMapping; | |
| 20 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 21 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 22 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 23 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 24 | +import org.springframework.web.bind.annotation.RestController; | |
| 25 | + | |
| 26 | +import java.util.List; | |
| 27 | + | |
| 28 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 29 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | |
| 30 | + | |
| 31 | +@Tag(name = "管理后台 - FMS 结账方案") | |
| 32 | +@RestController | |
| 33 | +@RequestMapping("/fms/closing/scheme") | |
| 34 | +@Validated | |
| 35 | +public class FmsClosingSchemeController { | |
| 36 | + | |
| 37 | + @Resource | |
| 38 | + private FmsClosingSchemeService closingSchemeService; | |
| 39 | + | |
| 40 | + @GetMapping("/list") | |
| 41 | + @Operation(summary = "获得结账方案列表") | |
| 42 | + @PreAuthorize("@ss.hasPermission('fms:closing:query')") | |
| 43 | + public CommonResult<List<FmsClosingSchemeRespVO>> getClosingSchemeList(@Valid FmsClosingQueryReqVO queryReqVO) { | |
| 44 | + return success(closingSchemeService.getClosingSchemeList(queryReqVO, getLoginUserId())); | |
| 45 | + } | |
| 46 | + | |
| 47 | + @PostMapping("/create") | |
| 48 | + @Operation(summary = "创建结账方案") | |
| 49 | + @PreAuthorize("@ss.hasPermission('fms:closing:update')") | |
| 50 | + public CommonResult<Long> createClosingScheme(@Valid @RequestBody FmsClosingSchemeSaveReqVO createReqVO) { | |
| 51 | + return success(closingSchemeService.createClosingScheme(createReqVO, getLoginUserId())); | |
| 52 | + } | |
| 53 | + | |
| 54 | + @PutMapping("/update") | |
| 55 | + @Operation(summary = "修改结账方案") | |
| 56 | + @PreAuthorize("@ss.hasPermission('fms:closing:update')") | |
| 57 | + public CommonResult<Boolean> updateClosingScheme(@Valid @RequestBody FmsClosingSchemeSaveReqVO updateReqVO) { | |
| 58 | + closingSchemeService.updateClosingScheme(updateReqVO, getLoginUserId()); | |
| 59 | + return success(true); | |
| 60 | + } | |
| 61 | + | |
| 62 | + @PutMapping("/update-profit-loss-settings") | |
| 63 | + @Operation(summary = "保存结转损益设置") | |
| 64 | + @PreAuthorize("@ss.hasPermission('fms:closing:update')") | |
| 65 | + public CommonResult<Long> saveProfitLossSettings(@Valid @RequestBody FmsProfitLossSettingsSaveReqVO saveReqVO) { | |
| 66 | + return success(closingSchemeService.saveProfitLossSettings(saveReqVO, getLoginUserId())); | |
| 67 | + } | |
| 68 | + | |
| 69 | + @PutMapping("/update-special-settings") | |
| 70 | + @Operation(summary = "保存专用结转设置") | |
| 71 | + @PreAuthorize("@ss.hasPermission('fms:closing:update')") | |
| 72 | + public CommonResult<Boolean> updateSpecialClosingSettings(@Valid @RequestBody FmsSpecialClosingSettingsSaveReqVO updateReqVO) { | |
| 73 | + closingSchemeService.updateSpecialClosingSettings(updateReqVO, getLoginUserId()); | |
| 74 | + return success(true); | |
| 75 | + } | |
| 76 | + | |
| 77 | + @DeleteMapping("/delete") | |
| 78 | + @Operation(summary = "删除结账方案") | |
| 79 | + @PreAuthorize("@ss.hasPermission('fms:closing:update')") | |
| 80 | + public CommonResult<Boolean> deleteClosingScheme(@RequestParam("accountSetId") @NotNull Long accountSetId, | |
| 81 | + @RequestParam("id") @NotNull Long id) { | |
| 82 | + closingSchemeService.deleteClosingScheme(accountSetId, id, getLoginUserId()); | |
| 83 | + return success(true); | |
| 84 | + } | |
| 85 | + | |
| 86 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/closing/FmsClosingTemplateController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.closing; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.module.fms.controller.admin.closing.vo.FmsClosingTemplateRespVO; | |
| 5 | +import cn.iocoder.yudao.module.fms.controller.admin.closing.vo.FmsClosingTemplateSaveReqVO; | |
| 6 | +import cn.iocoder.yudao.module.fms.service.closing.FmsClosingTemplateService; | |
| 7 | +import io.swagger.v3.oas.annotations.Operation; | |
| 8 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 9 | +import io.swagger.v3.oas.annotations.Parameters; | |
| 10 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 11 | +import jakarta.annotation.Resource; | |
| 12 | +import jakarta.validation.Valid; | |
| 13 | +import jakarta.validation.constraints.NotNull; | |
| 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 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | |
| 29 | + | |
| 30 | +@Tag(name = "管理后台 - FMS 结账模板") | |
| 31 | +@RestController | |
| 32 | +@RequestMapping("/fms/closing/template") | |
| 33 | +@Validated | |
| 34 | +public class FmsClosingTemplateController { | |
| 35 | + | |
| 36 | + @Resource | |
| 37 | + private FmsClosingTemplateService closingTemplateService; | |
| 38 | + | |
| 39 | + @GetMapping("/list") | |
| 40 | + @Operation(summary = "获得结账模板列表") | |
| 41 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024") | |
| 42 | + @PreAuthorize("@ss.hasPermission('fms:closing:query')") | |
| 43 | + public CommonResult<List<FmsClosingTemplateRespVO>> getClosingTemplateList( | |
| 44 | + @RequestParam("accountSetId") @NotNull Long accountSetId) { | |
| 45 | + return success(closingTemplateService.getClosingTemplateList(accountSetId, getLoginUserId())); | |
| 46 | + } | |
| 47 | + | |
| 48 | + @PostMapping("/create") | |
| 49 | + @Operation(summary = "创建结账模板") | |
| 50 | + @PreAuthorize("@ss.hasPermission('fms:closing:update')") | |
| 51 | + public CommonResult<Long> createClosingTemplate( | |
| 52 | + @Valid @RequestBody FmsClosingTemplateSaveReqVO createReqVO) { | |
| 53 | + return success(closingTemplateService.createClosingTemplate(createReqVO, getLoginUserId())); | |
| 54 | + } | |
| 55 | + | |
| 56 | + @PutMapping("/update") | |
| 57 | + @Operation(summary = "更新结账模板") | |
| 58 | + @PreAuthorize("@ss.hasPermission('fms:closing:update')") | |
| 59 | + public CommonResult<Boolean> updateClosingTemplate( | |
| 60 | + @Valid @RequestBody FmsClosingTemplateSaveReqVO updateReqVO) { | |
| 61 | + closingTemplateService.updateClosingTemplate(updateReqVO, getLoginUserId()); | |
| 62 | + return success(true); | |
| 63 | + } | |
| 64 | + | |
| 65 | + @DeleteMapping("/delete") | |
| 66 | + @Operation(summary = "删除结账模板") | |
| 67 | + @Parameters({ | |
| 68 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024"), | |
| 69 | + @Parameter(name = "id", description = "结账模板编号", required = true, example = "1024") | |
| 70 | + }) | |
| 71 | + @PreAuthorize("@ss.hasPermission('fms:closing:update')") | |
| 72 | + public CommonResult<Boolean> deleteClosingTemplate(@RequestParam("accountSetId") @NotNull Long accountSetId, | |
| 73 | + @RequestParam("id") @NotNull Long id) { | |
| 74 | + closingTemplateService.deleteClosingTemplate(accountSetId, id, getLoginUserId()); | |
| 75 | + return success(true); | |
| 76 | + } | |
| 77 | + | |
| 78 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/closing/FmsClosingVoucherController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.closing; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.module.fms.controller.admin.closing.vo.FmsClosingSchemeGenerateReqVO; | |
| 5 | +import cn.iocoder.yudao.module.fms.controller.admin.closing.vo.FmsClosingVoucherGenerateReqVO; | |
| 6 | +import cn.iocoder.yudao.module.fms.controller.admin.closing.vo.FmsProfitLossGenerateReqVO; | |
| 7 | +import cn.iocoder.yudao.module.fms.service.closing.FmsClosingVoucherService; | |
| 8 | +import io.swagger.v3.oas.annotations.Operation; | |
| 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.PostMapping; | |
| 15 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 16 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 17 | +import org.springframework.web.bind.annotation.RestController; | |
| 18 | + | |
| 19 | +import java.util.List; | |
| 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 = "管理后台 - FMS 结转凭证") | |
| 25 | +@RestController | |
| 26 | +@RequestMapping("/fms/closing/voucher") | |
| 27 | +@Validated | |
| 28 | +public class FmsClosingVoucherController { | |
| 29 | + | |
| 30 | + @Resource | |
| 31 | + private FmsClosingVoucherService closingVoucherService; | |
| 32 | + | |
| 33 | + @PostMapping("/generate-profit-loss") | |
| 34 | + @Operation(summary = "生成结转损益凭证") | |
| 35 | + @PreAuthorize("@ss.hasPermission('fms:closing:profit-loss')") | |
| 36 | + public CommonResult<Long> generateProfitLossVoucher(@Valid @RequestBody FmsProfitLossGenerateReqVO generateReqVO) { | |
| 37 | + return success(closingVoucherService.generateProfitLossVoucher(generateReqVO, getLoginUserId())); | |
| 38 | + } | |
| 39 | + | |
| 40 | + @PostMapping("/generate-scheme") | |
| 41 | + @Operation(summary = "生成结账方案凭证") | |
| 42 | + @PreAuthorize("@ss.hasPermission('fms:closing:profit-loss')") | |
| 43 | + public CommonResult<Long> generateClosingSchemeVoucher(@Valid @RequestBody FmsClosingSchemeGenerateReqVO generateReqVO) { | |
| 44 | + return success(closingVoucherService.generateClosingSchemeVoucher(generateReqVO, getLoginUserId())); | |
| 45 | + } | |
| 46 | + | |
| 47 | + @PostMapping("/generate-list") | |
| 48 | + @Operation(summary = "批量生成结转凭证") | |
| 49 | + @PreAuthorize("@ss.hasPermission('fms:closing:profit-loss')") | |
| 50 | + public CommonResult<List<Long>> generateClosingVoucherList(@Valid @RequestBody FmsClosingVoucherGenerateReqVO generateReqVO) { | |
| 51 | + return success(closingVoucherService.generateClosingVoucherList(generateReqVO, getLoginUserId())); | |
| 52 | + } | |
| 53 | + | |
| 54 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/closing/vo/FmsClosingOverviewRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.closing.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.math.BigDecimal; | |
| 7 | + | |
| 8 | +@Schema(description = "管理后台 - FMS 结账概况 Response VO") | |
| 9 | +@Data | |
| 10 | +public class FmsClosingOverviewRespVO { | |
| 11 | + | |
| 12 | + @Schema(description = "会计期间", example = "2026-08") | |
| 13 | + private String month; | |
| 14 | + | |
| 15 | + @Schema(description = "是否已结账") | |
| 16 | + private Boolean closed; | |
| 17 | + | |
| 18 | + @Schema(description = "是否要求凭证审核") | |
| 19 | + private Boolean voucherReviewRequired; | |
| 20 | + | |
| 21 | + @Schema(description = "未审核凭证数量", example = "2") | |
| 22 | + private Long pendingVoucherCount; | |
| 23 | + | |
| 24 | + @Schema(description = "凭证数量", example = "12") | |
| 25 | + private Long voucherCount; | |
| 26 | + | |
| 27 | + @Schema(description = "损益类科目余额", example = "9100.00") | |
| 28 | + private BigDecimal profitLossBalance; | |
| 29 | + | |
| 30 | + @Schema(description = "资产负债表差额", example = "0.00") | |
| 31 | + private BigDecimal balanceSheetDifference; | |
| 32 | + | |
| 33 | + @Schema(description = "结转损益凭证编号", example = "1024") | |
| 34 | + private Long profitLossVoucherId; | |
| 35 | + | |
| 36 | + @Schema(description = "初始余额是否试算平衡") | |
| 37 | + private Boolean initialBalanceBalanced; | |
| 38 | + | |
| 39 | + @Schema(description = "凭证编号是否连续") | |
| 40 | + private Boolean voucherNumberContinuous; | |
| 41 | + | |
| 42 | + @Schema(description = "需要结转损益时是否已生成凭证") | |
| 43 | + private Boolean profitLossVoucherGenerated; | |
| 44 | + | |
| 45 | + @Schema(description = "利润表勾稽是否平衡") | |
| 46 | + private Boolean incomeStatementBalanced; | |
| 47 | + | |
| 48 | + @Schema(description = "利润表未纳入公式的科目数量", example = "0") | |
| 49 | + private Integer incomeStatementUnmappedSubjectCount; | |
| 50 | + | |
| 51 | + @Schema(description = "资产负债表损益是否已结转") | |
| 52 | + private Boolean balanceSheetProfitLossTransferred; | |
| 53 | + | |
| 54 | + @Schema(description = "资产负债表是否平衡") | |
| 55 | + private Boolean balanceSheetBalanced; | |
| 56 | + | |
| 57 | + @Schema(description = "资产负债表未纳入公式的科目数量", example = "0") | |
| 58 | + private Integer balanceSheetUnmappedSubjectCount; | |
| 59 | + | |
| 60 | + @Schema(description = "是否满足全部结账条件") | |
| 61 | + private Boolean canClose; | |
| 62 | + | |
| 63 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/closing/vo/FmsClosingQueryReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.closing.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import jakarta.validation.constraints.NotNull; | |
| 5 | +import jakarta.validation.constraints.NotBlank; | |
| 6 | +import jakarta.validation.constraints.Pattern; | |
| 7 | +import lombok.Data; | |
| 8 | + | |
| 9 | +@Schema(description = "管理后台 - FMS 结账查询 Request VO") | |
| 10 | +@Data | |
| 11 | +public class FmsClosingQueryReqVO { | |
| 12 | + | |
| 13 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 14 | + @NotNull(message = "账套不能为空") | |
| 15 | + private Long accountSetId; | |
| 16 | + | |
| 17 | + @Schema(description = "目标会计期间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-08") | |
| 18 | + @NotBlank(message = "会计期间不能为空") | |
| 19 | + @Pattern(regexp = "\\d{4}-(0[1-9]|1[0-2])", message = "会计期间格式不正确") | |
| 20 | + private String month; | |
| 21 | + | |
| 22 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/closing/vo/FmsClosingSchemeGenerateReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.closing.vo; | |
| 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.Pattern; | |
| 7 | +import lombok.Data; | |
| 8 | + | |
| 9 | +@Schema(description = "管理后台 - FMS 结账方案生成凭证 Request VO") | |
| 10 | +@Data | |
| 11 | +public class FmsClosingSchemeGenerateReqVO { | |
| 12 | + | |
| 13 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 14 | + @NotNull(message = "账套不能为空") | |
| 15 | + private Long accountSetId; | |
| 16 | + | |
| 17 | + @Schema(description = "方案编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 18 | + @NotNull(message = "方案编号不能为空") | |
| 19 | + private Long id; | |
| 20 | + | |
| 21 | + @Schema(description = "会计期间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-08") | |
| 22 | + @NotBlank(message = "会计期间不能为空") | |
| 23 | + @Pattern(regexp = "\\d{4}-(0[1-9]|1[0-2])", message = "会计期间格式不正确") | |
| 24 | + private String month; | |
| 25 | + | |
| 26 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/closing/vo/FmsClosingSchemeRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.closing.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.math.BigDecimal; | |
| 7 | +import java.util.List; | |
| 8 | + | |
| 9 | +@Schema(description = "管理后台 - FMS 结账方案 Response VO") | |
| 10 | +@Data | |
| 11 | +public class FmsClosingSchemeRespVO { | |
| 12 | + | |
| 13 | + @Schema(description = "方案编号", example = "1024") | |
| 14 | + private Long id; | |
| 15 | + | |
| 16 | + @Schema(description = "账套编号", example = "1") | |
| 17 | + private Long accountSetId; | |
| 18 | + | |
| 19 | + @Schema(description = "方案名称", example = "结转本月房租") | |
| 20 | + private String name; | |
| 21 | + | |
| 22 | + @Schema(description = "是否期末结转") | |
| 23 | + private Boolean periodEnd; | |
| 24 | + | |
| 25 | + @Schema(description = "来源科目编号", example = "1024") | |
| 26 | + private Long subjectId; | |
| 27 | + | |
| 28 | + @Schema(description = "取数规则", example = "0") | |
| 29 | + private Integer formulaRule; | |
| 30 | + | |
| 31 | + @Schema(description = "取数时间类型", example = "1") | |
| 32 | + private Integer timeType; | |
| 33 | + | |
| 34 | + @Schema(description = "凭证字编号", example = "11") | |
| 35 | + private Long voucherWordId; | |
| 36 | + | |
| 37 | + @Schema(description = "凭证摘要", example = "结转损益") | |
| 38 | + private String digest; | |
| 39 | + | |
| 40 | + @Schema(description = "结转凭证类型", example = "2") | |
| 41 | + private Integer voucherType; | |
| 42 | + | |
| 43 | + @Schema(description = "以前年度损益调整科目编号", example = "1024") | |
| 44 | + private Long priorYearAdjustmentSubjectId; | |
| 45 | + | |
| 46 | + @Schema(description = "以前年度损益调整结转科目编号", example = "1025") | |
| 47 | + private Long adjustmentClosingSubjectId; | |
| 48 | + | |
| 49 | + @Schema(description = "其他损益结转科目编号", example = "1026") | |
| 50 | + private Long otherClosingSubjectId; | |
| 51 | + | |
| 52 | + @Schema(description = "是否按余额反向结转") | |
| 53 | + private Boolean reverseBalance; | |
| 54 | + | |
| 55 | + @Schema(description = "结转日期", example = "31") | |
| 56 | + private Integer closingDay; | |
| 57 | + | |
| 58 | + @Schema(description = "类型", example = "1") | |
| 59 | + private Integer type; | |
| 60 | + | |
| 61 | + @Schema(description = "结转科目规则数组") | |
| 62 | + private List<SubjectRule> subjects; | |
| 63 | + | |
| 64 | + @Schema(description = "待结转金额", example = "12000.00") | |
| 65 | + private BigDecimal balance; | |
| 66 | + | |
| 67 | + @Schema(description = "当前期间已生成凭证编号数组") | |
| 68 | + private List<Long> voucherIds; | |
| 69 | + | |
| 70 | + @Schema(description = "管理后台 - FMS 结账方案科目规则") | |
| 71 | + @Data | |
| 72 | + public static class SubjectRule { | |
| 73 | + | |
| 74 | + @Schema(description = "科目编号", example = "1024") | |
| 75 | + private Long subjectId; | |
| 76 | + | |
| 77 | + @Schema(description = "科目编码快照", example = "6602") | |
| 78 | + private String subjectCode; | |
| 79 | + | |
| 80 | + @Schema(description = "摘要", example = "结转本月房租") | |
| 81 | + private String digest; | |
| 82 | + | |
| 83 | + @Schema(description = "借贷方向", example = "1") | |
| 84 | + private Integer direction; | |
| 85 | + | |
| 86 | + @Schema(description = "金额比例", example = "100") | |
| 87 | + private BigDecimal amountRatio; | |
| 88 | + } | |
| 89 | + | |
| 90 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/closing/vo/FmsClosingSchemeSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.closing.vo; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 4 | +import cn.iocoder.yudao.module.fms.enums.common.FmsDebitCreditDirectionEnum; | |
| 5 | +import cn.iocoder.yudao.module.fms.enums.closing.FmsClosingTimeTypeEnum; | |
| 6 | +import cn.iocoder.yudao.module.fms.enums.report.FmsFormulaRuleEnum; | |
| 7 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 8 | +import jakarta.validation.Valid; | |
| 9 | +import jakarta.validation.constraints.DecimalMax; | |
| 10 | +import jakarta.validation.constraints.DecimalMin; | |
| 11 | +import jakarta.validation.constraints.NotBlank; | |
| 12 | +import jakarta.validation.constraints.NotEmpty; | |
| 13 | +import jakarta.validation.constraints.NotNull; | |
| 14 | +import jakarta.validation.constraints.Size; | |
| 15 | +import lombok.Data; | |
| 16 | + | |
| 17 | +import java.math.BigDecimal; | |
| 18 | +import java.util.List; | |
| 19 | + | |
| 20 | +@Schema(description = "管理后台 - FMS 结账方案保存 Request VO") | |
| 21 | +@Data | |
| 22 | +public class FmsClosingSchemeSaveReqVO { | |
| 23 | + | |
| 24 | + @Schema(description = "方案编号", example = "1024") | |
| 25 | + private Long id; | |
| 26 | + | |
| 27 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 28 | + @NotNull(message = "账套不能为空") | |
| 29 | + private Long accountSetId; | |
| 30 | + | |
| 31 | + @Schema(description = "方案名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "结转本月房租") | |
| 32 | + @NotBlank(message = "方案名称不能为空") | |
| 33 | + @Size(max = 255, message = "方案名称长度不能超过 255 个字符") | |
| 34 | + private String name; | |
| 35 | + | |
| 36 | + @Schema(description = "是否期末结转", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 37 | + @NotNull(message = "是否期末结转不能为空") | |
| 38 | + private Boolean periodEnd; | |
| 39 | + | |
| 40 | + @Schema(description = "来源科目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 41 | + @NotNull(message = "来源科目不能为空") | |
| 42 | + private Long subjectId; | |
| 43 | + | |
| 44 | + @Schema(description = "取数规则", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") | |
| 45 | + @NotNull(message = "取数规则不能为空") | |
| 46 | + @InEnum(FmsFormulaRuleEnum.class) | |
| 47 | + private Integer formulaRule; | |
| 48 | + | |
| 49 | + @Schema(description = "取数时间类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 50 | + @NotNull(message = "取数时间类型不能为空") | |
| 51 | + @InEnum(FmsClosingTimeTypeEnum.class) | |
| 52 | + private Integer timeType; | |
| 53 | + | |
| 54 | + @Schema(description = "凭证字编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11") | |
| 55 | + @NotNull(message = "凭证字不能为空") | |
| 56 | + private Long voucherWordId; | |
| 57 | + | |
| 58 | + @Schema(description = "结转科目规则数组", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 59 | + @Valid | |
| 60 | + @NotEmpty(message = "结转科目规则不能为空") | |
| 61 | + @Size(min = 2, message = "结转科目规则至少需要两条") | |
| 62 | + private List<SubjectRule> subjects; | |
| 63 | + | |
| 64 | + @Schema(description = "管理后台 - FMS 结账方案科目规则") | |
| 65 | + @Data | |
| 66 | + public static class SubjectRule { | |
| 67 | + | |
| 68 | + @Schema(description = "科目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 69 | + @NotNull(message = "科目不能为空") | |
| 70 | + private Long subjectId; | |
| 71 | + | |
| 72 | + @Schema(description = "摘要", requiredMode = Schema.RequiredMode.REQUIRED, example = "结转本月房租") | |
| 73 | + @NotBlank(message = "摘要不能为空") | |
| 74 | + @Size(max = 500, message = "摘要长度不能超过 500 个字符") | |
| 75 | + private String digest; | |
| 76 | + | |
| 77 | + @Schema(description = "借贷方向", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 78 | + @NotNull(message = "借贷方向不能为空") | |
| 79 | + @InEnum(FmsDebitCreditDirectionEnum.class) | |
| 80 | + private Integer direction; | |
| 81 | + | |
| 82 | + @Schema(description = "金额比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") | |
| 83 | + @NotNull(message = "金额比例不能为空") | |
| 84 | + @DecimalMin(value = "0", inclusive = false, message = "金额比例必须大于 0") | |
| 85 | + @DecimalMax(value = "100", message = "金额比例不能超过 100") | |
| 86 | + private BigDecimal amountRatio; | |
| 87 | + } | |
| 88 | + | |
| 89 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/closing/vo/FmsClosingTemplateRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.closing.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.math.BigDecimal; | |
| 7 | +import java.time.LocalDateTime; | |
| 8 | +import java.util.List; | |
| 9 | + | |
| 10 | +@Schema(description = "管理后台 - FMS 结账模板 Response VO") | |
| 11 | +@Data | |
| 12 | +public class FmsClosingTemplateRespVO { | |
| 13 | + | |
| 14 | + @Schema(description = "模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 15 | + private Long id; | |
| 16 | + | |
| 17 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 18 | + private Long accountSetId; | |
| 19 | + | |
| 20 | + @Schema(description = "系统预置编码", example = "daily-travel-reimbursement") | |
| 21 | + private String presetCode; | |
| 22 | + | |
| 23 | + @Schema(description = "模板名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "报销差旅费") | |
| 24 | + private String name; | |
| 25 | + | |
| 26 | + @Schema(description = "模板分类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 27 | + private Integer category; | |
| 28 | + | |
| 29 | + @Schema(description = "是否期末结转", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 30 | + private Boolean periodEnd; | |
| 31 | + | |
| 32 | + @Schema(description = "来源科目编号", example = "1024") | |
| 33 | + private Long subjectId; | |
| 34 | + | |
| 35 | + @Schema(description = "取数规则", example = "0") | |
| 36 | + private Integer formulaRule; | |
| 37 | + | |
| 38 | + @Schema(description = "取数时间类型", example = "1") | |
| 39 | + private Integer timeType; | |
| 40 | + | |
| 41 | + @Schema(description = "结转科目规则数组", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 42 | + private List<SubjectRule> subjects; | |
| 43 | + | |
| 44 | + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") | |
| 45 | + private Integer sort; | |
| 46 | + | |
| 47 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 48 | + private LocalDateTime createTime; | |
| 49 | + | |
| 50 | + @Schema(description = "管理后台 - FMS 结账模板科目规则") | |
| 51 | + @Data | |
| 52 | + public static class SubjectRule { | |
| 53 | + | |
| 54 | + @Schema(description = "科目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 55 | + private Long subjectId; | |
| 56 | + | |
| 57 | + @Schema(description = "科目编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "560107") | |
| 58 | + private String subjectCode; | |
| 59 | + | |
| 60 | + @Schema(description = "摘要", requiredMode = Schema.RequiredMode.REQUIRED, example = "报销差旅费") | |
| 61 | + private String digest; | |
| 62 | + | |
| 63 | + @Schema(description = "借贷方向", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 64 | + private Integer direction; | |
| 65 | + | |
| 66 | + @Schema(description = "金额比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") | |
| 67 | + private BigDecimal amountRatio; | |
| 68 | + } | |
| 69 | + | |
| 70 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/closing/vo/FmsClosingTemplateSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.closing.vo; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 4 | +import cn.iocoder.yudao.module.fms.enums.closing.FmsClosingTemplateCategoryEnum; | |
| 5 | +import cn.iocoder.yudao.module.fms.enums.closing.FmsClosingTimeTypeEnum; | |
| 6 | +import cn.iocoder.yudao.module.fms.enums.common.FmsDebitCreditDirectionEnum; | |
| 7 | +import cn.iocoder.yudao.module.fms.enums.report.FmsFormulaRuleEnum; | |
| 8 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 9 | +import jakarta.validation.Valid; | |
| 10 | +import jakarta.validation.constraints.DecimalMax; | |
| 11 | +import jakarta.validation.constraints.DecimalMin; | |
| 12 | +import jakarta.validation.constraints.NotBlank; | |
| 13 | +import jakarta.validation.constraints.NotEmpty; | |
| 14 | +import jakarta.validation.constraints.NotNull; | |
| 15 | +import jakarta.validation.constraints.Size; | |
| 16 | +import lombok.Data; | |
| 17 | + | |
| 18 | +import java.math.BigDecimal; | |
| 19 | +import java.util.List; | |
| 20 | + | |
| 21 | +@Schema(description = "管理后台 - FMS 结账模板保存 Request VO") | |
| 22 | +@Data | |
| 23 | +public class FmsClosingTemplateSaveReqVO { | |
| 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 accountSetId; | |
| 31 | + | |
| 32 | + @Schema(description = "模板名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "报销差旅费") | |
| 33 | + @NotBlank(message = "模板名称不能为空") | |
| 34 | + @Size(max = 255, message = "模板名称长度不能超过 255 个字符") | |
| 35 | + private String name; | |
| 36 | + | |
| 37 | + @Schema(description = "模板分类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 38 | + @NotNull(message = "模板分类不能为空") | |
| 39 | + @InEnum(FmsClosingTemplateCategoryEnum.class) | |
| 40 | + private Integer category; | |
| 41 | + | |
| 42 | + @Schema(description = "是否期末结转", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 43 | + @NotNull(message = "是否期末结转不能为空") | |
| 44 | + private Boolean periodEnd; | |
| 45 | + | |
| 46 | + @Schema(description = "来源科目编号", example = "1024") | |
| 47 | + private Long subjectId; | |
| 48 | + | |
| 49 | + @Schema(description = "取数规则", example = "0") | |
| 50 | + @InEnum(FmsFormulaRuleEnum.class) | |
| 51 | + private Integer formulaRule; | |
| 52 | + | |
| 53 | + @Schema(description = "取数时间类型", example = "1") | |
| 54 | + @InEnum(FmsClosingTimeTypeEnum.class) | |
| 55 | + private Integer timeType; | |
| 56 | + | |
| 57 | + @Schema(description = "结转科目规则数组", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 58 | + @Valid | |
| 59 | + @NotEmpty(message = "结转科目规则不能为空") | |
| 60 | + @Size(min = 2, message = "结转科目规则至少需要两条") | |
| 61 | + private List<SubjectRule> subjects; | |
| 62 | + | |
| 63 | + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") | |
| 64 | + @NotNull(message = "显示顺序不能为空") | |
| 65 | + private Integer sort; | |
| 66 | + | |
| 67 | + @Schema(description = "管理后台 - FMS 结账模板科目规则") | |
| 68 | + @Data | |
| 69 | + public static class SubjectRule { | |
| 70 | + | |
| 71 | + @Schema(description = "科目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 72 | + @NotNull(message = "科目不能为空") | |
| 73 | + private Long subjectId; | |
| 74 | + | |
| 75 | + @Schema(description = "摘要", requiredMode = Schema.RequiredMode.REQUIRED, example = "报销差旅费") | |
| 76 | + @NotBlank(message = "摘要不能为空") | |
| 77 | + @Size(max = 500, message = "摘要长度不能超过 500 个字符") | |
| 78 | + private String digest; | |
| 79 | + | |
| 80 | + @Schema(description = "借贷方向", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 81 | + @NotNull(message = "借贷方向不能为空") | |
| 82 | + @InEnum(FmsDebitCreditDirectionEnum.class) | |
| 83 | + private Integer direction; | |
| 84 | + | |
| 85 | + @Schema(description = "金额比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") | |
| 86 | + @NotNull(message = "金额比例不能为空") | |
| 87 | + @DecimalMin(value = "0", inclusive = false, message = "金额比例必须大于 0") | |
| 88 | + @DecimalMax(value = "100", message = "金额比例不能超过 100") | |
| 89 | + private BigDecimal amountRatio; | |
| 90 | + } | |
| 91 | + | |
| 92 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/closing/vo/FmsClosingVoucherGenerateReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.closing.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import jakarta.validation.constraints.NotBlank; | |
| 5 | +import jakarta.validation.constraints.NotEmpty; | |
| 6 | +import jakarta.validation.constraints.NotNull; | |
| 7 | +import jakarta.validation.constraints.Pattern; | |
| 8 | +import lombok.Data; | |
| 9 | + | |
| 10 | +import java.util.List; | |
| 11 | + | |
| 12 | +@Schema(description = "管理后台 - FMS 批量生成结转凭证 Request VO") | |
| 13 | +@Data | |
| 14 | +public class FmsClosingVoucherGenerateReqVO { | |
| 15 | + | |
| 16 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 17 | + @NotNull(message = "账套不能为空") | |
| 18 | + private Long accountSetId; | |
| 19 | + | |
| 20 | + @Schema(description = "方案编号数组", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 21 | + @NotEmpty(message = "方案编号不能为空") | |
| 22 | + private List<@NotNull(message = "方案编号不能为空") Long> ids; | |
| 23 | + | |
| 24 | + @Schema(description = "会计期间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-08") | |
| 25 | + @NotBlank(message = "会计期间不能为空") | |
| 26 | + @Pattern(regexp = "\\d{4}-(0[1-9]|1[0-2])", message = "会计期间格式不正确") | |
| 27 | + private String month; | |
| 28 | + | |
| 29 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/closing/vo/FmsProfitLossGenerateReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.closing.vo; | |
| 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.Pattern; | |
| 7 | +import lombok.Data; | |
| 8 | + | |
| 9 | +@Schema(description = "管理后台 - FMS 生成结转损益凭证 Request VO") | |
| 10 | +@Data | |
| 11 | +public class FmsProfitLossGenerateReqVO { | |
| 12 | + | |
| 13 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 14 | + @NotNull(message = "账套不能为空") | |
| 15 | + private Long accountSetId; | |
| 16 | + | |
| 17 | + @Schema(description = "会计期间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-08") | |
| 18 | + @NotBlank(message = "会计期间不能为空") | |
| 19 | + @Pattern(regexp = "\\d{4}-(0[1-9]|1[0-2])", message = "会计期间格式不正确") | |
| 20 | + private String month; | |
| 21 | + | |
| 22 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/closing/vo/FmsProfitLossSettingsSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.closing.vo; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 4 | +import cn.iocoder.yudao.module.fms.enums.closing.FmsClosingVoucherTypeEnum; | |
| 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.NotBlank; | |
| 9 | +import jakarta.validation.constraints.NotNull; | |
| 10 | +import jakarta.validation.constraints.Size; | |
| 11 | +import lombok.Data; | |
| 12 | + | |
| 13 | +@Schema(description = "管理后台 - FMS 结转损益设置保存 Request VO") | |
| 14 | +@Data | |
| 15 | +public class FmsProfitLossSettingsSaveReqVO { | |
| 16 | + | |
| 17 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 18 | + @NotNull(message = "账套不能为空") | |
| 19 | + private Long accountSetId; | |
| 20 | + | |
| 21 | + @Schema(description = "凭证字编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11") | |
| 22 | + @NotNull(message = "凭证字不能为空") | |
| 23 | + private Long voucherWordId; | |
| 24 | + | |
| 25 | + @Schema(description = "凭证摘要", requiredMode = Schema.RequiredMode.REQUIRED, example = "结转损益") | |
| 26 | + @NotBlank(message = "凭证摘要不能为空") | |
| 27 | + @Size(max = 500, message = "凭证摘要长度不能超过 500 个字符") | |
| 28 | + private String digest; | |
| 29 | + | |
| 30 | + @Schema(description = "结转凭证类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | |
| 31 | + @NotNull(message = "结转凭证类型不能为空") | |
| 32 | + @InEnum(FmsClosingVoucherTypeEnum.class) | |
| 33 | + private Integer voucherType; | |
| 34 | + | |
| 35 | + @Schema(description = "以前年度损益调整科目编号", requiredMode = Schema.RequiredMode.REQUIRED, | |
| 36 | + example = "1024") | |
| 37 | + @NotNull(message = "以前年度损益调整科目不能为空") | |
| 38 | + private Long priorYearAdjustmentSubjectId; | |
| 39 | + | |
| 40 | + @Schema(description = "以前年度损益调整结转科目编号", requiredMode = Schema.RequiredMode.REQUIRED, | |
| 41 | + example = "1025") | |
| 42 | + @NotNull(message = "以前年度损益调整结转科目不能为空") | |
| 43 | + private Long adjustmentClosingSubjectId; | |
| 44 | + | |
| 45 | + @Schema(description = "其他损益结转科目编号", requiredMode = Schema.RequiredMode.REQUIRED, | |
| 46 | + example = "1026") | |
| 47 | + @NotNull(message = "其他损益结转科目不能为空") | |
| 48 | + private Long otherClosingSubjectId; | |
| 49 | + | |
| 50 | + @Schema(description = "是否按余额反向结转", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 51 | + @NotNull(message = "结转方式不能为空") | |
| 52 | + private Boolean reverseBalance; | |
| 53 | + | |
| 54 | + @Schema(description = "结转日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "31") | |
| 55 | + @NotNull(message = "结转日期不能为空") | |
| 56 | + @Min(value = 1, message = "结转日期不能小于 1") | |
| 57 | + @Max(value = 31, message = "结转日期不能大于 31") | |
| 58 | + private Integer closingDay; | |
| 59 | + | |
| 60 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/closing/vo/FmsSpecialClosingSettingsSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.closing.vo; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 4 | +import cn.iocoder.yudao.module.fms.enums.common.FmsDebitCreditDirectionEnum; | |
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 6 | +import jakarta.validation.Valid; | |
| 7 | +import jakarta.validation.constraints.DecimalMax; | |
| 8 | +import jakarta.validation.constraints.DecimalMin; | |
| 9 | +import jakarta.validation.constraints.NotBlank; | |
| 10 | +import jakarta.validation.constraints.NotEmpty; | |
| 11 | +import jakarta.validation.constraints.NotNull; | |
| 12 | +import jakarta.validation.constraints.Size; | |
| 13 | +import lombok.Data; | |
| 14 | + | |
| 15 | +import java.math.BigDecimal; | |
| 16 | +import java.util.List; | |
| 17 | + | |
| 18 | +@Schema(description = "管理后台 - FMS 专用结转设置保存 Request VO") | |
| 19 | +@Data | |
| 20 | +public class FmsSpecialClosingSettingsSaveReqVO { | |
| 21 | + | |
| 22 | + @Schema(description = "方案编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 23 | + @NotNull(message = "方案编号不能为空") | |
| 24 | + private Long id; | |
| 25 | + | |
| 26 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 27 | + @NotNull(message = "账套不能为空") | |
| 28 | + private Long accountSetId; | |
| 29 | + | |
| 30 | + @Schema(description = "凭证字编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11") | |
| 31 | + @NotNull(message = "凭证字不能为空") | |
| 32 | + private Long voucherWordId; | |
| 33 | + | |
| 34 | + @Schema(description = "结转科目规则数组", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 35 | + @Valid | |
| 36 | + @NotEmpty(message = "结转科目规则不能为空") | |
| 37 | + @Size(min = 2, message = "结转科目规则至少需要两条") | |
| 38 | + private List<SubjectRule> subjects; | |
| 39 | + | |
| 40 | + @Schema(description = "管理后台 - FMS 专用结转科目规则") | |
| 41 | + @Data | |
| 42 | + public static class SubjectRule { | |
| 43 | + | |
| 44 | + @Schema(description = "科目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 45 | + @NotNull(message = "科目不能为空") | |
| 46 | + private Long subjectId; | |
| 47 | + | |
| 48 | + @Schema(description = "摘要", requiredMode = Schema.RequiredMode.REQUIRED, example = "计提所得税") | |
| 49 | + @NotBlank(message = "摘要不能为空") | |
| 50 | + @Size(max = 500, message = "摘要长度不能超过 500 个字符") | |
| 51 | + private String digest; | |
| 52 | + | |
| 53 | + @Schema(description = "借贷方向", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 54 | + @NotNull(message = "借贷方向不能为空") | |
| 55 | + @InEnum(FmsDebitCreditDirectionEnum.class) | |
| 56 | + private Integer direction; | |
| 57 | + | |
| 58 | + @Schema(description = "金额比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "25") | |
| 59 | + @NotNull(message = "金额比例不能为空") | |
| 60 | + @DecimalMin(value = "0", inclusive = false, message = "金额比例必须大于 0") | |
| 61 | + @DecimalMax(value = "100", message = "金额比例不能超过 100") | |
| 62 | + private BigDecimal amountRatio; | |
| 63 | + } | |
| 64 | + | |
| 65 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/FmsAccountSetController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config; | |
| 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.fms.controller.admin.config.vo.accountset.FmsAccountSetInitializeReqVO; | |
| 6 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.accountset.FmsAccountSetRespVO; | |
| 7 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.accountset.FmsAccountSetSaveReqVO; | |
| 8 | +import cn.iocoder.yudao.module.fms.dal.dataobject.config.FmsAccountSetDO; | |
| 9 | +import cn.iocoder.yudao.module.fms.dal.dataobject.config.FmsAccountUserDO; | |
| 10 | +import cn.iocoder.yudao.module.fms.service.config.FmsAccountSetService; | |
| 11 | +import cn.iocoder.yudao.module.fms.service.config.FmsAccountUserService; | |
| 12 | +import io.swagger.v3.oas.annotations.Operation; | |
| 13 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 14 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 15 | +import jakarta.annotation.Resource; | |
| 16 | +import jakarta.validation.Valid; | |
| 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.PostMapping; | |
| 21 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 22 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 23 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 24 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 25 | +import org.springframework.web.bind.annotation.RestController; | |
| 26 | + | |
| 27 | +import java.util.List; | |
| 28 | +import java.util.Map; | |
| 29 | + | |
| 30 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 31 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 32 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | |
| 33 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | |
| 34 | + | |
| 35 | +@Tag(name = "管理后台 - FMS 账套") | |
| 36 | +@RestController | |
| 37 | +@RequestMapping("/fms/config/account-set") | |
| 38 | +@Validated | |
| 39 | +public class FmsAccountSetController { | |
| 40 | + | |
| 41 | + @Resource | |
| 42 | + private FmsAccountSetService accountSetService; | |
| 43 | + @Resource | |
| 44 | + private FmsAccountUserService accountUserService; | |
| 45 | + | |
| 46 | + @PostMapping("/create") | |
| 47 | + @Operation(summary = "创建账套") | |
| 48 | + @PreAuthorize("@ss.hasPermission('fms:config:account-set:create')") | |
| 49 | + public CommonResult<Long> createAccountSet(@Valid @RequestBody FmsAccountSetSaveReqVO createReqVO) { | |
| 50 | + return success(accountSetService.createAccountSet(createReqVO, getLoginUserId())); | |
| 51 | + } | |
| 52 | + | |
| 53 | + @PutMapping("/update") | |
| 54 | + @Operation(summary = "更新账套") | |
| 55 | + @PreAuthorize("@ss.hasPermission('fms:config:account-set:update')") | |
| 56 | + public CommonResult<Boolean> updateAccountSet(@Valid @RequestBody FmsAccountSetSaveReqVO updateReqVO) { | |
| 57 | + accountSetService.updateAccountSet(updateReqVO, getLoginUserId()); | |
| 58 | + return success(true); | |
| 59 | + } | |
| 60 | + | |
| 61 | + @PutMapping("/initialize") | |
| 62 | + @Operation(summary = "初始化账套") | |
| 63 | + @PreAuthorize("@ss.hasPermission('fms:config:account-set:initialize')") | |
| 64 | + public CommonResult<Boolean> initializeAccountSet( | |
| 65 | + @Valid @RequestBody FmsAccountSetInitializeReqVO initializeReqVO) { | |
| 66 | + accountSetService.initializeAccountSet(initializeReqVO, getLoginUserId()); | |
| 67 | + return success(true); | |
| 68 | + } | |
| 69 | + | |
| 70 | + @GetMapping("/get") | |
| 71 | + @Operation(summary = "获得账套") | |
| 72 | + @Parameter(name = "id", description = "账套编号", required = true, example = "1024") | |
| 73 | + @PreAuthorize("@ss.hasPermission('fms:config:account-set:query')") | |
| 74 | + public CommonResult<FmsAccountSetRespVO> getAccountSet(@RequestParam("id") Long id) { | |
| 75 | + FmsAccountSetDO accountSet = accountSetService.validateAccountSetReadPermission(id, getLoginUserId()); | |
| 76 | + FmsAccountUserDO accountUser = accountUserService.getAccountUser(id, getLoginUserId()); | |
| 77 | + return success(buildAccountSetRespVO(accountSet, accountUser)); | |
| 78 | + } | |
| 79 | + | |
| 80 | + @GetMapping("/list") | |
| 81 | + @Operation(summary = "获得当前用户账套列表") | |
| 82 | + @PreAuthorize("@ss.hasPermission('fms:config:account-set:query')") | |
| 83 | + public CommonResult<List<FmsAccountSetRespVO>> getAccountSetList() { | |
| 84 | + List<FmsAccountUserDO> accountUsers = accountUserService.getAccountUserList(getLoginUserId()); | |
| 85 | + Map<Long, FmsAccountSetDO> accountSetMap = accountSetService.getAccountSetMap( | |
| 86 | + convertSet(accountUsers, FmsAccountUserDO::getAccountSetId)); | |
| 87 | + return success(buildAccountSetRespVOList(accountSetMap, accountUsers)); | |
| 88 | + } | |
| 89 | + | |
| 90 | + // ==================== 拼接 VO ==================== | |
| 91 | + | |
| 92 | + private FmsAccountSetRespVO buildAccountSetRespVO( | |
| 93 | + FmsAccountSetDO accountSet, FmsAccountUserDO accountUser) { | |
| 94 | + return BeanUtils.toBean(accountSet, FmsAccountSetRespVO.class) | |
| 95 | + .setDefaultStatus(accountUser.getDefaultStatus()).setFounder(accountUser.getFounder()) | |
| 96 | + .setLevel(accountUser.getLevel()); | |
| 97 | + } | |
| 98 | + | |
| 99 | + private List<FmsAccountSetRespVO> buildAccountSetRespVOList( | |
| 100 | + Map<Long, FmsAccountSetDO> accountSetMap, List<FmsAccountUserDO> accountUsers) { | |
| 101 | + return convertList(accountUsers, accountUser -> buildAccountSetRespVO( | |
| 102 | + accountSetMap.get(accountUser.getAccountSetId()), accountUser)); | |
| 103 | + } | |
| 104 | + | |
| 105 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/FmsAccountUserController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config; | |
| 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.fms.controller.admin.config.vo.accountuser.FmsAccountUserRespVO; | |
| 6 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.accountuser.FmsAccountUserUpdateReqVO; | |
| 7 | +import cn.iocoder.yudao.module.fms.dal.dataobject.config.FmsAccountUserDO; | |
| 8 | +import cn.iocoder.yudao.module.fms.service.config.FmsAccountUserService; | |
| 9 | +import cn.iocoder.yudao.module.system.api.dept.DeptApi; | |
| 10 | +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; | |
| 11 | +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | |
| 12 | +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; | |
| 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 jakarta.validation.Valid; | |
| 18 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 19 | +import org.springframework.validation.annotation.Validated; | |
| 20 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 21 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 22 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 23 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 24 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 25 | +import org.springframework.web.bind.annotation.RestController; | |
| 26 | + | |
| 27 | +import java.util.List; | |
| 28 | +import java.util.Map; | |
| 29 | + | |
| 30 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 31 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 32 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | |
| 33 | +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; | |
| 34 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | |
| 35 | + | |
| 36 | +@Tag(name = "管理后台 - FMS 账套用户") | |
| 37 | +@RestController | |
| 38 | +@RequestMapping("/fms/config/account-user") | |
| 39 | +@Validated | |
| 40 | +public class FmsAccountUserController { | |
| 41 | + | |
| 42 | + @Resource | |
| 43 | + private FmsAccountUserService accountUserService; | |
| 44 | + @Resource | |
| 45 | + private AdminUserApi adminUserApi; | |
| 46 | + @Resource | |
| 47 | + private DeptApi deptApi; | |
| 48 | + | |
| 49 | + @GetMapping("/list") | |
| 50 | + @Operation(summary = "获得账套用户列表") | |
| 51 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024") | |
| 52 | + @PreAuthorize("@ss.hasPermission('fms:config:account-set:authorize')") | |
| 53 | + public CommonResult<List<FmsAccountUserRespVO>> getAccountUserList( | |
| 54 | + @RequestParam("accountSetId") Long accountSetId) { | |
| 55 | + List<FmsAccountUserDO> accountUsers = accountUserService.getAccountUserList( | |
| 56 | + accountSetId, getLoginUserId()); | |
| 57 | + return success(buildAccountUserRespVOList(accountUsers)); | |
| 58 | + } | |
| 59 | + | |
| 60 | + @PutMapping("/update") | |
| 61 | + @Operation(summary = "更新账套用户列表") | |
| 62 | + @PreAuthorize("@ss.hasPermission('fms:config:account-set:authorize')") | |
| 63 | + public CommonResult<Boolean> updateAccountUserList( | |
| 64 | + @Valid @RequestBody FmsAccountUserUpdateReqVO updateReqVO) { | |
| 65 | + accountUserService.updateAccountUserList(updateReqVO, getLoginUserId()); | |
| 66 | + return success(true); | |
| 67 | + } | |
| 68 | + | |
| 69 | + @PutMapping("/update-default-status") | |
| 70 | + @Operation(summary = "更新默认账套状态") | |
| 71 | + @PreAuthorize("@ss.hasPermission('fms:config:account-set:query')") | |
| 72 | + public CommonResult<Boolean> updateAccountSetDefaultStatus(@RequestParam("accountSetId") Long accountSetId) { | |
| 73 | + accountUserService.updateAccountSetDefaultStatus(accountSetId, getLoginUserId()); | |
| 74 | + return success(true); | |
| 75 | + } | |
| 76 | + | |
| 77 | + // ==================== 拼接 VO ==================== | |
| 78 | + | |
| 79 | + private List<FmsAccountUserRespVO> buildAccountUserRespVOList(List<FmsAccountUserDO> accountUsers) { | |
| 80 | + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap( | |
| 81 | + convertSet(accountUsers, FmsAccountUserDO::getUserId)); | |
| 82 | + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap( | |
| 83 | + convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); | |
| 84 | + return convertList(accountUsers, accountUser -> { | |
| 85 | + FmsAccountUserRespVO respVO = BeanUtils.toBean(accountUser, FmsAccountUserRespVO.class); | |
| 86 | + findAndThen(userMap, accountUser.getUserId(), user -> { | |
| 87 | + respVO.setNickname(user.getNickname()).setMobile(user.getMobile()) | |
| 88 | + .setEmail(user.getEmail()).setStatus(user.getStatus()); | |
| 89 | + findAndThen(deptMap, user.getDeptId(), dept -> respVO.setDeptName(dept.getName())); | |
| 90 | + }); | |
| 91 | + return respVO; | |
| 92 | + }); | |
| 93 | + } | |
| 94 | + | |
| 95 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/FmsAuxiliaryItemController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; | |
| 4 | +import cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum; | |
| 5 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | |
| 6 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 7 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 8 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 9 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 10 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 11 | +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; | |
| 12 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.auxiliaryitem.FmsAuxiliaryItemImportExcelVO; | |
| 13 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.auxiliaryitem.FmsAuxiliaryItemImportRespVO; | |
| 14 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.auxiliaryitem.FmsAuxiliaryItemPageReqVO; | |
| 15 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.auxiliaryitem.FmsAuxiliaryItemRespVO; | |
| 16 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.auxiliaryitem.FmsAuxiliaryItemSaveReqVO; | |
| 17 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.auxiliaryitem.FmsAuxiliaryItemStatusReqVO; | |
| 18 | +import cn.iocoder.yudao.module.fms.dal.dataobject.config.FmsAuxiliaryItemDO; | |
| 19 | +import cn.iocoder.yudao.module.fms.dal.dataobject.config.FmsAuxiliaryTypeDO; | |
| 20 | +import cn.iocoder.yudao.module.fms.enums.config.FmsAuxiliaryTypeEnum; | |
| 21 | +import cn.iocoder.yudao.module.fms.service.config.FmsAuxiliaryItemService; | |
| 22 | +import cn.iocoder.yudao.module.fms.service.config.FmsAuxiliaryTypeService; | |
| 23 | +import io.swagger.v3.oas.annotations.Operation; | |
| 24 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 25 | +import io.swagger.v3.oas.annotations.Parameters; | |
| 26 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 27 | +import jakarta.annotation.Resource; | |
| 28 | +import jakarta.servlet.http.HttpServletResponse; | |
| 29 | +import jakarta.validation.Valid; | |
| 30 | +import jakarta.validation.constraints.NotEmpty; | |
| 31 | +import jakarta.validation.constraints.NotNull; | |
| 32 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 33 | +import org.springframework.validation.annotation.Validated; | |
| 34 | +import org.springframework.web.bind.annotation.DeleteMapping; | |
| 35 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 36 | +import org.springframework.web.bind.annotation.PostMapping; | |
| 37 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 38 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 39 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 40 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 41 | +import org.springframework.web.bind.annotation.RestController; | |
| 42 | +import org.springframework.web.multipart.MultipartFile; | |
| 43 | + | |
| 44 | +import java.io.IOException; | |
| 45 | +import java.util.ArrayList; | |
| 46 | +import java.util.Arrays; | |
| 47 | +import java.util.Collections; | |
| 48 | +import java.util.List; | |
| 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.security.core.util.SecurityFrameworkUtils.getLoginUserId; | |
| 53 | + | |
| 54 | +@Tag(name = "管理后台 - FMS 辅助核算项目") | |
| 55 | +@RestController | |
| 56 | +@RequestMapping("/fms/config/auxiliary-item") | |
| 57 | +@Validated | |
| 58 | +public class FmsAuxiliaryItemController { | |
| 59 | + | |
| 60 | + @Resource | |
| 61 | + private FmsAuxiliaryItemService auxiliaryItemService; | |
| 62 | + @Resource | |
| 63 | + private FmsAuxiliaryTypeService auxiliaryTypeService; | |
| 64 | + | |
| 65 | + @PostMapping("/create") | |
| 66 | + @Operation(summary = "创建辅助核算项目") | |
| 67 | + @PreAuthorize("@ss.hasPermission('fms:config:auxiliary:create')") | |
| 68 | + public CommonResult<Long> createAuxiliaryItem(@Valid @RequestBody FmsAuxiliaryItemSaveReqVO createReqVO) { | |
| 69 | + return success(auxiliaryItemService.createAuxiliaryItem(createReqVO, getLoginUserId())); | |
| 70 | + } | |
| 71 | + | |
| 72 | + @PutMapping("/update") | |
| 73 | + @Operation(summary = "更新辅助核算项目") | |
| 74 | + @PreAuthorize("@ss.hasPermission('fms:config:auxiliary:update')") | |
| 75 | + public CommonResult<Boolean> updateAuxiliaryItem( | |
| 76 | + @Valid @RequestBody FmsAuxiliaryItemSaveReqVO updateReqVO) { | |
| 77 | + auxiliaryItemService.updateAuxiliaryItem(updateReqVO, getLoginUserId()); | |
| 78 | + return success(true); | |
| 79 | + } | |
| 80 | + | |
| 81 | + @DeleteMapping("/delete-list") | |
| 82 | + @Operation(summary = "删除辅助核算项目") | |
| 83 | + @Parameters({ | |
| 84 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024"), | |
| 85 | + @Parameter(name = "ids", description = "辅助核算项目编号数组", required = true, example = "[1024]") | |
| 86 | + }) | |
| 87 | + @PreAuthorize("@ss.hasPermission('fms:config:auxiliary:delete')") | |
| 88 | + public CommonResult<Boolean> deleteAuxiliaryItemList( | |
| 89 | + @RequestParam("accountSetId") @NotNull Long accountSetId, | |
| 90 | + @RequestParam("ids") @NotEmpty List<Long> ids) { | |
| 91 | + auxiliaryItemService.deleteAuxiliaryItemList(accountSetId, ids, getLoginUserId()); | |
| 92 | + return success(true); | |
| 93 | + } | |
| 94 | + | |
| 95 | + @PutMapping("/update-status") | |
| 96 | + @Operation(summary = "更新辅助核算项目状态") | |
| 97 | + @PreAuthorize("@ss.hasPermission('fms:config:auxiliary:update')") | |
| 98 | + public CommonResult<Boolean> updateAuxiliaryItemStatus( | |
| 99 | + @Valid @RequestBody FmsAuxiliaryItemStatusReqVO reqVO) { | |
| 100 | + auxiliaryItemService.updateAuxiliaryItemStatus( | |
| 101 | + reqVO.getAccountSetId(), reqVO.getId(), reqVO.getStatus(), getLoginUserId()); | |
| 102 | + return success(true); | |
| 103 | + } | |
| 104 | + | |
| 105 | + @GetMapping("/get") | |
| 106 | + @Operation(summary = "获得辅助核算项目") | |
| 107 | + @Parameters({ | |
| 108 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024"), | |
| 109 | + @Parameter(name = "id", description = "辅助核算项目编号", required = true, example = "1024") | |
| 110 | + }) | |
| 111 | + @PreAuthorize("@ss.hasPermission('fms:config:auxiliary:query')") | |
| 112 | + public CommonResult<FmsAuxiliaryItemRespVO> getAuxiliaryItem(@RequestParam("accountSetId") @NotNull Long accountSetId, | |
| 113 | + @RequestParam("id") @NotNull Long id) { | |
| 114 | + FmsAuxiliaryItemDO item = auxiliaryItemService.getAuxiliaryItem(accountSetId, id, getLoginUserId()); | |
| 115 | + return success(BeanUtils.toBean(item, FmsAuxiliaryItemRespVO.class)); | |
| 116 | + } | |
| 117 | + | |
| 118 | + @GetMapping("/page") | |
| 119 | + @Operation(summary = "获得辅助核算项目分页") | |
| 120 | + @PreAuthorize("@ss.hasAnyPermissions('fms:config:auxiliary:query', 'fms:voucher:create', " | |
| 121 | + + "'fms:voucher:update')") | |
| 122 | + public CommonResult<PageResult<FmsAuxiliaryItemRespVO>> getAuxiliaryItemPage( | |
| 123 | + @Valid FmsAuxiliaryItemPageReqVO pageReqVO) { | |
| 124 | + PageResult<FmsAuxiliaryItemDO> pageResult = auxiliaryItemService | |
| 125 | + .getAuxiliaryItemPage(pageReqVO, getLoginUserId()); | |
| 126 | + return success(BeanUtils.toBean(pageResult, FmsAuxiliaryItemRespVO.class)); | |
| 127 | + } | |
| 128 | + | |
| 129 | + @GetMapping("/simple-list") | |
| 130 | + @Operation(summary = "获得辅助核算项目精简列表", description = "只包含启用的项目,主要用于前端下拉选项") | |
| 131 | + @Parameters({ | |
| 132 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024"), | |
| 133 | + @Parameter(name = "auxiliaryTypeId", description = "辅助核算类别编号", required = true, | |
| 134 | + example = "1024") | |
| 135 | + }) | |
| 136 | + public CommonResult<List<FmsAuxiliaryItemRespVO>> getAuxiliaryItemSimpleList( | |
| 137 | + @RequestParam("accountSetId") @NotNull Long accountSetId, | |
| 138 | + @RequestParam("auxiliaryTypeId") @NotNull Long auxiliaryTypeId) { | |
| 139 | + List<FmsAuxiliaryItemDO> list = auxiliaryItemService | |
| 140 | + .getAuxiliaryItemListByAccountSetIdAndAuxiliaryTypeIdAndStatus( | |
| 141 | + accountSetId, auxiliaryTypeId, CommonStatusEnum.ENABLE.getStatus(), getLoginUserId()); | |
| 142 | + return success(convertList(list, item -> new FmsAuxiliaryItemRespVO() | |
| 143 | + .setId(item.getId()).setCode(item.getCode()).setName(item.getName()))); | |
| 144 | + } | |
| 145 | + | |
| 146 | + @GetMapping("/export-excel") | |
| 147 | + @Operation(summary = "导出辅助核算项目") | |
| 148 | + @PreAuthorize("@ss.hasPermission('fms:config:auxiliary:export')") | |
| 149 | + @ApiAccessLog(operateType = OperateTypeEnum.EXPORT) | |
| 150 | + public void exportAuxiliaryItemExcel( | |
| 151 | + @Valid FmsAuxiliaryItemPageReqVO pageReqVO, HttpServletResponse response) throws IOException { | |
| 152 | + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); | |
| 153 | + List<FmsAuxiliaryItemDO> list = auxiliaryItemService | |
| 154 | + .getAuxiliaryItemPage(pageReqVO, getLoginUserId()).getList(); | |
| 155 | + FmsAuxiliaryTypeDO auxiliaryType = auxiliaryTypeService.getAuxiliaryType( | |
| 156 | + pageReqVO.getAccountSetId(), pageReqVO.getAuxiliaryTypeId(), getLoginUserId()); | |
| 157 | + ExcelUtils.write(response, auxiliaryType.getName() + ".xlsx", "辅助核算项目", | |
| 158 | + buildAuxiliaryItemExcelHead(auxiliaryType.getType()), buildAuxiliaryItemExcelData(list, | |
| 159 | + auxiliaryType.getType())); | |
| 160 | + } | |
| 161 | + | |
| 162 | + @GetMapping("/get-import-template") | |
| 163 | + @Operation(summary = "获得辅助核算项目导入模板") | |
| 164 | + @Parameter(name = "type", description = "辅助核算类别类型", required = true, example = "1") | |
| 165 | + @PreAuthorize("@ss.hasPermission('fms:config:auxiliary:import')") | |
| 166 | + @ApiAccessLog(operateType = OperateTypeEnum.EXPORT) | |
| 167 | + public void getAuxiliaryItemImportTemplate( | |
| 168 | + @RequestParam("type") @InEnum(FmsAuxiliaryTypeEnum.class) Integer type, | |
| 169 | + HttpServletResponse response) throws IOException { | |
| 170 | + FmsAuxiliaryItemImportExcelVO example = new FmsAuxiliaryItemImportExcelVO() | |
| 171 | + .setCode("001").setName("示例项目").setRemark("示例备注"); | |
| 172 | + if (FmsAuxiliaryTypeEnum.INVENTORY.getType().equals(type)) { | |
| 173 | + example.setSpecification("标准版").setUnit("个"); | |
| 174 | + } | |
| 175 | + ExcelUtils.write(response, "辅助核算项目导入模板.xlsx", "辅助核算项目", | |
| 176 | + FmsAuxiliaryItemImportExcelVO.class, Collections.singletonList(example)); | |
| 177 | + } | |
| 178 | + | |
| 179 | + @PostMapping("/import") | |
| 180 | + @Operation(summary = "导入辅助核算项目") | |
| 181 | + @Parameters({ | |
| 182 | + @Parameter(name = "accountSetId", description = "账套编号", required = true), | |
| 183 | + @Parameter(name = "auxiliaryTypeId", description = "辅助核算类别编号", required = true), | |
| 184 | + @Parameter(name = "file", description = "Excel 文件", required = true) | |
| 185 | + }) | |
| 186 | + @PreAuthorize("@ss.hasPermission('fms:config:auxiliary:import')") | |
| 187 | + @ApiAccessLog(operateType = OperateTypeEnum.IMPORT) | |
| 188 | + public CommonResult<FmsAuxiliaryItemImportRespVO> importAuxiliaryItem( | |
| 189 | + @RequestParam("accountSetId") @NotNull Long accountSetId, | |
| 190 | + @RequestParam("auxiliaryTypeId") @NotNull Long auxiliaryTypeId, | |
| 191 | + @RequestParam("file") MultipartFile file) throws Exception { | |
| 192 | + List<FmsAuxiliaryItemImportExcelVO> importItems = ExcelUtils.read( | |
| 193 | + file, FmsAuxiliaryItemImportExcelVO.class); | |
| 194 | + return success(auxiliaryItemService.importAuxiliaryItemList( | |
| 195 | + accountSetId, auxiliaryTypeId, importItems, getLoginUserId())); | |
| 196 | + } | |
| 197 | + | |
| 198 | + /** 构建辅助核算项目导出的动态表头 */ | |
| 199 | + private List<List<String>> buildAuxiliaryItemExcelHead(Integer type) { | |
| 200 | + if (FmsAuxiliaryTypeEnum.CUSTOMER.getType().equals(type) | |
| 201 | + || FmsAuxiliaryTypeEnum.SUPPLIER.getType().equals(type)) { | |
| 202 | + return Arrays.asList(Collections.singletonList("编码"), Collections.singletonList("名称"), | |
| 203 | + Collections.singletonList("备注")); | |
| 204 | + } | |
| 205 | + if (FmsAuxiliaryTypeEnum.INVENTORY.getType().equals(type)) { | |
| 206 | + return Arrays.asList(Collections.singletonList("编码"), Collections.singletonList("名称"), | |
| 207 | + Collections.singletonList("规格"), Collections.singletonList("单位")); | |
| 208 | + } | |
| 209 | + return Arrays.asList(Collections.singletonList("编码"), Collections.singletonList("名称")); | |
| 210 | + } | |
| 211 | + | |
| 212 | + /** 构建辅助核算项目导出的动态数据 */ | |
| 213 | + private List<List<Object>> buildAuxiliaryItemExcelData(List<FmsAuxiliaryItemDO> items, Integer type) { | |
| 214 | + List<List<Object>> rows = new ArrayList<>(items.size()); | |
| 215 | + for (FmsAuxiliaryItemDO item : items) { | |
| 216 | + if (FmsAuxiliaryTypeEnum.CUSTOMER.getType().equals(type) | |
| 217 | + || FmsAuxiliaryTypeEnum.SUPPLIER.getType().equals(type)) { | |
| 218 | + rows.add(Arrays.asList(item.getCode(), item.getName(), item.getRemark())); | |
| 219 | + } else if (FmsAuxiliaryTypeEnum.INVENTORY.getType().equals(type)) { | |
| 220 | + rows.add(Arrays.asList(item.getCode(), item.getName(), item.getSpecification(), item.getUnit())); | |
| 221 | + } else { | |
| 222 | + rows.add(Arrays.asList(item.getCode(), item.getName())); | |
| 223 | + } | |
| 224 | + } | |
| 225 | + return rows; | |
| 226 | + } | |
| 227 | + | |
| 228 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/FmsAuxiliaryTypeController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config; | |
| 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.fms.controller.admin.config.vo.auxiliarytype.FmsAuxiliaryTypeRespVO; | |
| 6 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.auxiliarytype.FmsAuxiliaryTypeSaveReqVO; | |
| 7 | +import cn.iocoder.yudao.module.fms.dal.dataobject.config.FmsAuxiliaryTypeDO; | |
| 8 | +import cn.iocoder.yudao.module.fms.service.config.FmsAuxiliaryTypeService; | |
| 9 | +import io.swagger.v3.oas.annotations.Operation; | |
| 10 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 11 | +import io.swagger.v3.oas.annotations.Parameters; | |
| 12 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 13 | +import jakarta.annotation.Resource; | |
| 14 | +import jakarta.validation.Valid; | |
| 15 | +import jakarta.validation.constraints.NotNull; | |
| 16 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 17 | +import org.springframework.validation.annotation.Validated; | |
| 18 | +import org.springframework.web.bind.annotation.DeleteMapping; | |
| 19 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 20 | +import org.springframework.web.bind.annotation.PostMapping; | |
| 21 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 22 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 23 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 24 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 25 | +import org.springframework.web.bind.annotation.RestController; | |
| 26 | + | |
| 27 | +import java.util.List; | |
| 28 | + | |
| 29 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 30 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 31 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | |
| 32 | + | |
| 33 | +@Tag(name = "管理后台 - FMS 辅助核算类别") | |
| 34 | +@RestController | |
| 35 | +@RequestMapping("/fms/config/auxiliary-type") | |
| 36 | +@Validated | |
| 37 | +public class FmsAuxiliaryTypeController { | |
| 38 | + | |
| 39 | + @Resource | |
| 40 | + private FmsAuxiliaryTypeService auxiliaryTypeService; | |
| 41 | + | |
| 42 | + @PostMapping("/create") | |
| 43 | + @Operation(summary = "创建辅助核算类别") | |
| 44 | + @PreAuthorize("@ss.hasPermission('fms:config:auxiliary:create')") | |
| 45 | + public CommonResult<Long> createAuxiliaryType(@Valid @RequestBody FmsAuxiliaryTypeSaveReqVO createReqVO) { | |
| 46 | + return success(auxiliaryTypeService.createAuxiliaryType(createReqVO, getLoginUserId())); | |
| 47 | + } | |
| 48 | + | |
| 49 | + @PutMapping("/update") | |
| 50 | + @Operation(summary = "更新辅助核算类别") | |
| 51 | + @PreAuthorize("@ss.hasPermission('fms:config:auxiliary:update')") | |
| 52 | + public CommonResult<Boolean> updateAuxiliaryType(@Valid @RequestBody FmsAuxiliaryTypeSaveReqVO updateReqVO) { | |
| 53 | + auxiliaryTypeService.updateAuxiliaryType(updateReqVO, getLoginUserId()); | |
| 54 | + return success(true); | |
| 55 | + } | |
| 56 | + | |
| 57 | + @DeleteMapping("/delete") | |
| 58 | + @Operation(summary = "删除辅助核算类别") | |
| 59 | + @Parameters({ | |
| 60 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024"), | |
| 61 | + @Parameter(name = "id", description = "辅助核算类别编号", required = true, example = "1024") | |
| 62 | + }) | |
| 63 | + @PreAuthorize("@ss.hasPermission('fms:config:auxiliary:delete')") | |
| 64 | + public CommonResult<Boolean> deleteAuxiliaryType( | |
| 65 | + @RequestParam("accountSetId") @NotNull Long accountSetId, | |
| 66 | + @RequestParam("id") @NotNull Long id) { | |
| 67 | + auxiliaryTypeService.deleteAuxiliaryType(accountSetId, id, getLoginUserId()); | |
| 68 | + return success(true); | |
| 69 | + } | |
| 70 | + | |
| 71 | + @GetMapping("/list") | |
| 72 | + @Operation(summary = "获得辅助核算类别列表") | |
| 73 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024") | |
| 74 | + @PreAuthorize("@ss.hasPermission('fms:config:auxiliary:query')") | |
| 75 | + public CommonResult<List<FmsAuxiliaryTypeRespVO>> getAuxiliaryTypeList( | |
| 76 | + @RequestParam("accountSetId") @NotNull Long accountSetId) { | |
| 77 | + return success(BeanUtils.toBean(auxiliaryTypeService.getAuxiliaryTypeList( | |
| 78 | + accountSetId, getLoginUserId()), FmsAuxiliaryTypeRespVO.class)); | |
| 79 | + } | |
| 80 | + | |
| 81 | + @GetMapping("/simple-list") | |
| 82 | + @Operation(summary = "获得辅助核算类别精简列表", description = "主要用于前端的下拉选项") | |
| 83 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024") | |
| 84 | + public CommonResult<List<FmsAuxiliaryTypeRespVO>> getAuxiliaryTypeSimpleList( | |
| 85 | + @RequestParam("accountSetId") @NotNull Long accountSetId) { | |
| 86 | + List<FmsAuxiliaryTypeDO> list = auxiliaryTypeService.getAuxiliaryTypeList( | |
| 87 | + accountSetId, getLoginUserId()); | |
| 88 | + return success(convertList(list, auxiliaryType -> new FmsAuxiliaryTypeRespVO() | |
| 89 | + .setId(auxiliaryType.getId()).setName(auxiliaryType.getName()).setType(auxiliaryType.getType()))); | |
| 90 | + } | |
| 91 | + | |
| 92 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/FmsCurrencyController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config; | |
| 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.fms.controller.admin.config.vo.currency.FmsCurrencyRespVO; | |
| 6 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.currency.FmsCurrencySaveReqVO; | |
| 7 | +import cn.iocoder.yudao.module.fms.dal.dataobject.config.FmsCurrencyDO; | |
| 8 | +import cn.iocoder.yudao.module.fms.service.config.FmsCurrencyService; | |
| 9 | +import io.swagger.v3.oas.annotations.Operation; | |
| 10 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 11 | +import io.swagger.v3.oas.annotations.Parameters; | |
| 12 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 13 | +import jakarta.annotation.Resource; | |
| 14 | +import jakarta.validation.Valid; | |
| 15 | +import jakarta.validation.constraints.NotNull; | |
| 16 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 17 | +import org.springframework.validation.annotation.Validated; | |
| 18 | +import org.springframework.web.bind.annotation.DeleteMapping; | |
| 19 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 20 | +import org.springframework.web.bind.annotation.PostMapping; | |
| 21 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 22 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 23 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 24 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 25 | +import org.springframework.web.bind.annotation.RestController; | |
| 26 | + | |
| 27 | +import java.util.List; | |
| 28 | + | |
| 29 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 30 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 31 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | |
| 32 | + | |
| 33 | +@Tag(name = "管理后台 - FMS 币别") | |
| 34 | +@RestController | |
| 35 | +@RequestMapping("/fms/config/currency") | |
| 36 | +@Validated | |
| 37 | +public class FmsCurrencyController { | |
| 38 | + | |
| 39 | + @Resource | |
| 40 | + private FmsCurrencyService currencyService; | |
| 41 | + | |
| 42 | + @PostMapping("/create") | |
| 43 | + @Operation(summary = "创建币别") | |
| 44 | + @PreAuthorize("@ss.hasPermission('fms:config:currency:create')") | |
| 45 | + public CommonResult<Long> createCurrency(@Valid @RequestBody FmsCurrencySaveReqVO createReqVO) { | |
| 46 | + return success(currencyService.createCurrency(createReqVO, getLoginUserId())); | |
| 47 | + } | |
| 48 | + | |
| 49 | + @PutMapping("/update") | |
| 50 | + @Operation(summary = "更新币别") | |
| 51 | + @PreAuthorize("@ss.hasPermission('fms:config:currency:update')") | |
| 52 | + public CommonResult<Boolean> updateCurrency(@Valid @RequestBody FmsCurrencySaveReqVO updateReqVO) { | |
| 53 | + currencyService.updateCurrency(updateReqVO, getLoginUserId()); | |
| 54 | + return success(true); | |
| 55 | + } | |
| 56 | + | |
| 57 | + @DeleteMapping("/delete") | |
| 58 | + @Operation(summary = "删除币别") | |
| 59 | + @Parameters({ | |
| 60 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024"), | |
| 61 | + @Parameter(name = "id", description = "币别编号", required = true, example = "1024") | |
| 62 | + }) | |
| 63 | + @PreAuthorize("@ss.hasPermission('fms:config:currency:delete')") | |
| 64 | + public CommonResult<Boolean> deleteCurrency( | |
| 65 | + @RequestParam("accountSetId") @NotNull Long accountSetId, | |
| 66 | + @RequestParam("id") @NotNull Long id) { | |
| 67 | + currencyService.deleteCurrency(accountSetId, id, getLoginUserId()); | |
| 68 | + return success(true); | |
| 69 | + } | |
| 70 | + | |
| 71 | + @GetMapping("/list") | |
| 72 | + @Operation(summary = "获得币别列表") | |
| 73 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024") | |
| 74 | + @PreAuthorize("@ss.hasPermission('fms:config:currency:query')") | |
| 75 | + public CommonResult<List<FmsCurrencyRespVO>> getCurrencyList( | |
| 76 | + @RequestParam("accountSetId") @NotNull Long accountSetId) { | |
| 77 | + List<FmsCurrencyDO> list = currencyService.getCurrencyList(accountSetId, getLoginUserId()); | |
| 78 | + return success(BeanUtils.toBean(list, FmsCurrencyRespVO.class)); | |
| 79 | + } | |
| 80 | + | |
| 81 | + @GetMapping("/simple-list") | |
| 82 | + @Operation(summary = "获得币别精简列表", description = "主要用于前端的下拉选项") | |
| 83 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024") | |
| 84 | + public CommonResult<List<FmsCurrencyRespVO>> getCurrencySimpleList( | |
| 85 | + @RequestParam("accountSetId") @NotNull Long accountSetId) { | |
| 86 | + List<FmsCurrencyDO> list = currencyService.getCurrencyList(accountSetId, getLoginUserId()); | |
| 87 | + return success(convertList(list, currency -> new FmsCurrencyRespVO() | |
| 88 | + .setId(currency.getId()).setCode(currency.getCode()).setName(currency.getName()) | |
| 89 | + .setExchangeRate(currency.getExchangeRate()).setStandard(currency.getStandard()))); | |
| 90 | + } | |
| 91 | + | |
| 92 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/FmsDigestController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config; | |
| 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.fms.controller.admin.config.vo.digest.FmsDigestRespVO; | |
| 6 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.digest.FmsDigestSaveReqVO; | |
| 7 | +import cn.iocoder.yudao.module.fms.dal.dataobject.config.FmsDigestDO; | |
| 8 | +import cn.iocoder.yudao.module.fms.service.config.FmsDigestService; | |
| 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 jakarta.validation.constraints.NotNull; | |
| 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.PostMapping; | |
| 20 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 21 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 22 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 23 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 24 | +import org.springframework.web.bind.annotation.RestController; | |
| 25 | + | |
| 26 | +import java.util.List; | |
| 27 | + | |
| 28 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 29 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 30 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | |
| 31 | + | |
| 32 | +/** | |
| 33 | + * FMS 常用摘要 Controller | |
| 34 | + * | |
| 35 | + * @author 芋道源码 | |
| 36 | + */ | |
| 37 | +@Tag(name = "管理后台 - FMS 常用摘要") | |
| 38 | +@RestController | |
| 39 | +@RequestMapping("/fms/config/digest") | |
| 40 | +@Validated | |
| 41 | +public class FmsDigestController { | |
| 42 | + | |
| 43 | + @Resource | |
| 44 | + private FmsDigestService digestService; | |
| 45 | + | |
| 46 | + @PostMapping("/create") | |
| 47 | + @Operation(summary = "创建常用摘要") | |
| 48 | + @PreAuthorize("@ss.hasPermission('fms:config:digest:create')") | |
| 49 | + public CommonResult<Long> createDigest(@Valid @RequestBody FmsDigestSaveReqVO createReqVO) { | |
| 50 | + return success(digestService.createDigest(createReqVO, getLoginUserId())); | |
| 51 | + } | |
| 52 | + | |
| 53 | + @PutMapping("/update") | |
| 54 | + @Operation(summary = "修改常用摘要") | |
| 55 | + @PreAuthorize("@ss.hasPermission('fms:config:digest:update')") | |
| 56 | + public CommonResult<Boolean> updateDigest(@Valid @RequestBody FmsDigestSaveReqVO updateReqVO) { | |
| 57 | + digestService.updateDigest(updateReqVO, getLoginUserId()); | |
| 58 | + return success(true); | |
| 59 | + } | |
| 60 | + | |
| 61 | + @DeleteMapping("/delete") | |
| 62 | + @Operation(summary = "删除常用摘要") | |
| 63 | + @Parameter(name = "id", description = "常用摘要编号", required = true, example = "1024") | |
| 64 | + @PreAuthorize("@ss.hasPermission('fms:config:digest:delete')") | |
| 65 | + public CommonResult<Boolean> deleteDigest(@RequestParam("accountSetId") @NotNull Long accountSetId, | |
| 66 | + @RequestParam("id") @NotNull Long id) { | |
| 67 | + digestService.deleteDigest(accountSetId, id, getLoginUserId()); | |
| 68 | + return success(true); | |
| 69 | + } | |
| 70 | + | |
| 71 | + @GetMapping("/list") | |
| 72 | + @Operation(summary = "获得常用摘要管理列表") | |
| 73 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024") | |
| 74 | + @PreAuthorize("@ss.hasPermission('fms:config:digest:query')") | |
| 75 | + public CommonResult<List<FmsDigestRespVO>> getDigestList( | |
| 76 | + @RequestParam("accountSetId") @NotNull Long accountSetId) { | |
| 77 | + List<FmsDigestDO> list = digestService.getDigestList(accountSetId, getLoginUserId()); | |
| 78 | + return success(BeanUtils.toBean(list, FmsDigestRespVO.class)); | |
| 79 | + } | |
| 80 | + | |
| 81 | + @GetMapping("/simple-list") | |
| 82 | + @Operation(summary = "获得常用摘要精简列表") | |
| 83 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024") | |
| 84 | + public CommonResult<List<FmsDigestRespVO>> getDigestSimpleList( | |
| 85 | + @RequestParam("accountSetId") @NotNull Long accountSetId) { | |
| 86 | + List<FmsDigestDO> list = digestService.getDigestList(accountSetId, getLoginUserId()); | |
| 87 | + return success(convertList(list, digest -> new FmsDigestRespVO() | |
| 88 | + .setId(digest.getId()).setAccountSetId(digest.getAccountSetId()).setContent(digest.getContent()))); | |
| 89 | + } | |
| 90 | + | |
| 91 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/FmsFinanceIndicatorController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config; | |
| 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.fms.controller.admin.config.vo.financeindicator.FmsFinanceIndicatorRespVO; | |
| 6 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.financeindicator.FmsFinanceIndicatorSaveReqVO; | |
| 7 | +import cn.iocoder.yudao.module.fms.dal.dataobject.config.FmsFinanceIndicatorDO; | |
| 8 | +import cn.iocoder.yudao.module.fms.service.config.FmsFinanceIndicatorService; | |
| 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 jakarta.validation.constraints.NotNull; | |
| 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.PostMapping; | |
| 20 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 21 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 22 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 23 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 24 | +import org.springframework.web.bind.annotation.RestController; | |
| 25 | + | |
| 26 | +import java.util.List; | |
| 27 | + | |
| 28 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 29 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | |
| 30 | + | |
| 31 | +@Tag(name = "管理后台 - FMS 财务指标") | |
| 32 | +@RestController | |
| 33 | +@RequestMapping("/fms/config/finance-indicator") | |
| 34 | +@Validated | |
| 35 | +public class FmsFinanceIndicatorController { | |
| 36 | + | |
| 37 | + @Resource | |
| 38 | + private FmsFinanceIndicatorService financeIndicatorService; | |
| 39 | + | |
| 40 | + @PostMapping("/create") | |
| 41 | + @Operation(summary = "创建首页财务指标") | |
| 42 | + @PreAuthorize("@ss.hasPermission('fms:config:finance-indicator:create')") | |
| 43 | + public CommonResult<Long> create(@Valid @RequestBody FmsFinanceIndicatorSaveReqVO reqVO) { | |
| 44 | + return success(financeIndicatorService.createFinanceIndicator(reqVO, getLoginUserId())); | |
| 45 | + } | |
| 46 | + | |
| 47 | + @PutMapping("/update") | |
| 48 | + @Operation(summary = "修改首页财务指标") | |
| 49 | + @PreAuthorize("@ss.hasPermission('fms:config:finance-indicator:update')") | |
| 50 | + public CommonResult<Boolean> update(@Valid @RequestBody FmsFinanceIndicatorSaveReqVO reqVO) { | |
| 51 | + financeIndicatorService.updateFinanceIndicator(reqVO, getLoginUserId()); | |
| 52 | + return success(true); | |
| 53 | + } | |
| 54 | + | |
| 55 | + @DeleteMapping("/delete") | |
| 56 | + @Operation(summary = "删除首页财务指标") | |
| 57 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024") | |
| 58 | + @Parameter(name = "id", description = "指标编号", required = true, example = "1024") | |
| 59 | + @PreAuthorize("@ss.hasPermission('fms:config:finance-indicator:delete')") | |
| 60 | + public CommonResult<Boolean> delete(@RequestParam("accountSetId") @NotNull Long accountSetId, | |
| 61 | + @RequestParam("id") @NotNull Long id) { | |
| 62 | + financeIndicatorService.deleteFinanceIndicator(accountSetId, id, getLoginUserId()); | |
| 63 | + return success(true); | |
| 64 | + } | |
| 65 | + | |
| 66 | + @GetMapping("/get") | |
| 67 | + @Operation(summary = "获得首页财务指标") | |
| 68 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024") | |
| 69 | + @Parameter(name = "id", description = "指标编号", required = true, example = "1024") | |
| 70 | + @PreAuthorize("@ss.hasPermission('fms:config:finance-indicator:query')") | |
| 71 | + public CommonResult<FmsFinanceIndicatorRespVO> get(@RequestParam("accountSetId") @NotNull Long accountSetId, | |
| 72 | + @RequestParam("id") @NotNull Long id) { | |
| 73 | + FmsFinanceIndicatorDO indicator = financeIndicatorService.getFinanceIndicator(accountSetId, id, getLoginUserId()); | |
| 74 | + return success(BeanUtils.toBean(indicator, FmsFinanceIndicatorRespVO.class)); | |
| 75 | + } | |
| 76 | + | |
| 77 | + @GetMapping("/list") | |
| 78 | + @Operation(summary = "获得首页财务指标列表") | |
| 79 | + @PreAuthorize("@ss.hasPermission('fms:config:finance-indicator:query')") | |
| 80 | + public CommonResult<List<FmsFinanceIndicatorRespVO>> getList(@RequestParam("accountSetId") @NotNull Long accountSetId) { | |
| 81 | + List<FmsFinanceIndicatorDO> list = financeIndicatorService.getFinanceIndicatorList(accountSetId, | |
| 82 | + getLoginUserId()); | |
| 83 | + return success(BeanUtils.toBean(list, FmsFinanceIndicatorRespVO.class)); | |
| 84 | + } | |
| 85 | + | |
| 86 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/FmsFinanceParameterController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config; | |
| 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.fms.controller.admin.config.vo.financeparameter.FmsFinanceParameterRespVO; | |
| 6 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.financeparameter.FmsFinanceParameterUpdateReqVO; | |
| 7 | +import cn.iocoder.yudao.module.fms.dal.dataobject.config.FmsFinanceParameterDO; | |
| 8 | +import cn.iocoder.yudao.module.fms.service.config.FmsFinanceParameterService; | |
| 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 static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 24 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | |
| 25 | + | |
| 26 | +@Tag(name = "管理后台 - FMS 财务参数") | |
| 27 | +@RestController | |
| 28 | +@RequestMapping("/fms/config/finance-parameter") | |
| 29 | +@Validated | |
| 30 | +public class FmsFinanceParameterController { | |
| 31 | + | |
| 32 | + @Resource | |
| 33 | + private FmsFinanceParameterService financeParameterService; | |
| 34 | + | |
| 35 | + @GetMapping("/get") | |
| 36 | + @Operation(summary = "获得财务参数") | |
| 37 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024") | |
| 38 | + @PreAuthorize("@ss.hasPermission('fms:config:finance-parameter:query')") | |
| 39 | + public CommonResult<FmsFinanceParameterRespVO> getFinanceParameter( | |
| 40 | + @RequestParam("accountSetId") Long accountSetId) { | |
| 41 | + FmsFinanceParameterDO financeParameter = financeParameterService.getFinanceParameter( | |
| 42 | + accountSetId, getLoginUserId()); | |
| 43 | + return success(BeanUtils.toBean(financeParameter, FmsFinanceParameterRespVO.class)); | |
| 44 | + } | |
| 45 | + | |
| 46 | + @PutMapping("/update") | |
| 47 | + @Operation(summary = "更新财务参数") | |
| 48 | + @PreAuthorize("@ss.hasPermission('fms:config:finance-parameter:update')") | |
| 49 | + public CommonResult<Boolean> updateFinanceParameter( | |
| 50 | + @Valid @RequestBody FmsFinanceParameterUpdateReqVO updateReqVO) { | |
| 51 | + financeParameterService.updateFinanceParameter(updateReqVO, getLoginUserId()); | |
| 52 | + return success(true); | |
| 53 | + } | |
| 54 | + | |
| 55 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/FmsInitialBalanceController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; | |
| 5 | +import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; | |
| 6 | +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; | |
| 7 | +import cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum; | |
| 8 | +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; | |
| 9 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.initialbalance.FmsInitialBalanceRespVO; | |
| 10 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.initialbalance.FmsInitialBalanceSaveReqVO; | |
| 11 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.initialbalance.FmsTrialBalanceRespVO; | |
| 12 | +import cn.iocoder.yudao.module.fms.dal.dataobject.config.FmsAccountSetDO; | |
| 13 | +import cn.iocoder.yudao.module.fms.service.config.FmsAccountSetService; | |
| 14 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.initialbalance.FmsInitialBalanceExcelVO; | |
| 15 | +import cn.iocoder.yudao.module.fms.service.config.FmsInitialBalanceService; | |
| 16 | +import io.swagger.v3.oas.annotations.Operation; | |
| 17 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 18 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 19 | +import jakarta.annotation.Resource; | |
| 20 | +import jakarta.validation.Valid; | |
| 21 | +import jakarta.validation.constraints.NotNull; | |
| 22 | +import jakarta.servlet.http.HttpServletResponse; | |
| 23 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 24 | +import org.springframework.validation.annotation.Validated; | |
| 25 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 26 | +import org.springframework.web.bind.annotation.PostMapping; | |
| 27 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 28 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 29 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 30 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 31 | +import org.springframework.web.bind.annotation.RestController; | |
| 32 | +import org.springframework.web.multipart.MultipartFile; | |
| 33 | + | |
| 34 | +import java.io.IOException; | |
| 35 | +import java.util.List; | |
| 36 | + | |
| 37 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 38 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | |
| 39 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | |
| 40 | +import static cn.iocoder.yudao.module.fms.enums.ErrorCodeConstants.INITIAL_BALANCE_IMPORT_TEMPLATE_INVALID; | |
| 41 | + | |
| 42 | +@Tag(name = "管理后台 - FMS 初始余额") | |
| 43 | +@RestController | |
| 44 | +@RequestMapping("/fms/config/initial-balance") | |
| 45 | +@Validated | |
| 46 | +public class FmsInitialBalanceController { | |
| 47 | + | |
| 48 | + @Resource | |
| 49 | + private FmsInitialBalanceService initialBalanceService; | |
| 50 | + @Resource | |
| 51 | + private FmsAccountSetService accountSetService; | |
| 52 | + | |
| 53 | + @GetMapping("/list") | |
| 54 | + @Operation(summary = "获得初始余额列表") | |
| 55 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024") | |
| 56 | + @Parameter(name = "subjectType", description = "科目类型", required = true, example = "1") | |
| 57 | + @PreAuthorize("@ss.hasPermission('fms:config:initial-balance:query')") | |
| 58 | + public CommonResult<List<FmsInitialBalanceRespVO>> getInitialBalanceList( | |
| 59 | + @RequestParam("accountSetId") @NotNull Long accountSetId, | |
| 60 | + @RequestParam("subjectType") @NotNull Integer subjectType) { | |
| 61 | + return success(initialBalanceService.getInitialBalanceList(accountSetId, subjectType, getLoginUserId())); | |
| 62 | + } | |
| 63 | + | |
| 64 | + @PutMapping("/save") | |
| 65 | + @Operation(summary = "保存初始余额") | |
| 66 | + @PreAuthorize("@ss.hasPermission('fms:config:initial-balance:update')") | |
| 67 | + public CommonResult<Boolean> saveInitialBalance(@Valid @RequestBody FmsInitialBalanceSaveReqVO saveReqVO) { | |
| 68 | + initialBalanceService.saveInitialBalance(saveReqVO, getLoginUserId()); | |
| 69 | + return success(true); | |
| 70 | + } | |
| 71 | + | |
| 72 | + @GetMapping("/trial-balance") | |
| 73 | + @Operation(summary = "获得试算平衡结果") | |
| 74 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024") | |
| 75 | + @PreAuthorize("@ss.hasPermission('fms:config:initial-balance:query')") | |
| 76 | + public CommonResult<FmsTrialBalanceRespVO> getTrialBalance( | |
| 77 | + @RequestParam("accountSetId") @NotNull Long accountSetId) { | |
| 78 | + return success(initialBalanceService.getTrialBalance(accountSetId, getLoginUserId())); | |
| 79 | + } | |
| 80 | + | |
| 81 | + @GetMapping("/export-excel") | |
| 82 | + @Operation(summary = "导出初始余额") | |
| 83 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024") | |
| 84 | + @PreAuthorize("@ss.hasPermission('fms:config:initial-balance:export')") | |
| 85 | + @ApiAccessLog(operateType = OperateTypeEnum.EXPORT) | |
| 86 | + public void exportInitialBalance( | |
| 87 | + @RequestParam("accountSetId") @NotNull Long accountSetId, | |
| 88 | + HttpServletResponse response) throws IOException { | |
| 89 | + // 1. 读取初始余额列表,并展开为 Excel 行 | |
| 90 | + List<FmsInitialBalanceExcelVO> rows = FmsInitialBalanceExcelHelper.toRows( | |
| 91 | + initialBalanceService.getInitialBalanceList(accountSetId, null, getLoginUserId()), true); | |
| 92 | + // 2. 输出 Excel | |
| 93 | + ExcelUtils.write(response, "财务初始余额.xlsx", "财务初始余额", | |
| 94 | + FmsInitialBalanceExcelVO.class, rows); | |
| 95 | + } | |
| 96 | + | |
| 97 | + @GetMapping("/get-import-template") | |
| 98 | + @Operation(summary = "获得初始余额导入模板") | |
| 99 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024") | |
| 100 | + @PreAuthorize("@ss.hasPermission('fms:config:initial-balance:import')") | |
| 101 | + @ApiAccessLog(operateType = OperateTypeEnum.EXPORT) | |
| 102 | + public void getInitialBalanceImportTemplate(@RequestParam("accountSetId") @NotNull Long accountSetId, | |
| 103 | + HttpServletResponse response) | |
| 104 | + throws IOException { | |
| 105 | + // 1. 读取初始余额列表,并展开为 Excel 行 | |
| 106 | + Long userId = getLoginUserId(); | |
| 107 | + List<FmsInitialBalanceRespVO> initialBalances = initialBalanceService.getInitialBalanceList(accountSetId, null, userId); | |
| 108 | + List<FmsInitialBalanceExcelVO> rows = FmsInitialBalanceExcelHelper.toRows(initialBalances, false); | |
| 109 | + // 2. 生成导入模板,一月启用时只需要录入期初余额 | |
| 110 | + FmsAccountSetDO accountSet = accountSetService.validateAccountSetReadPermission(accountSetId, userId); | |
| 111 | + byte[] content = FmsInitialBalanceExcelHelper.writeTemplate( | |
| 112 | + rows, LocalDateTimeUtils.isJanuary(accountSet.getStartTime())); | |
| 113 | + // 3. 写入响应 | |
| 114 | + ServletUtils.writeAttachment(response, "财务初始余额导入模板.xlsx", content); | |
| 115 | + } | |
| 116 | + | |
| 117 | + @PostMapping("/import") | |
| 118 | + @Operation(summary = "导入初始余额") | |
| 119 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024") | |
| 120 | + @PreAuthorize("@ss.hasPermission('fms:config:initial-balance:import')") | |
| 121 | + @ApiAccessLog(operateType = OperateTypeEnum.IMPORT) | |
| 122 | + public CommonResult<Integer> importInitialBalance( | |
| 123 | + @RequestParam("accountSetId") @NotNull Long accountSetId, | |
| 124 | + @RequestParam("file") MultipartFile file) throws IOException { | |
| 125 | + // 1. 读取 Excel | |
| 126 | + List<FmsInitialBalanceExcelVO> rows; | |
| 127 | + try { | |
| 128 | + rows = FmsInitialBalanceExcelHelper.read(file); | |
| 129 | + } catch (IllegalArgumentException | IOException ex) { | |
| 130 | + throw exception(INITIAL_BALANCE_IMPORT_TEMPLATE_INVALID); | |
| 131 | + } | |
| 132 | + // 2. 导入数据 | |
| 133 | + return success(initialBalanceService.importInitialBalance(accountSetId, rows, getLoginUserId())); | |
| 134 | + } | |
| 135 | + | |
| 136 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/FmsInitialBalanceExcelHelper.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.initialbalance.FmsInitialBalanceRespVO; | |
| 5 | +import cn.iocoder.yudao.module.fms.enums.common.FmsDebitCreditDirectionEnum; | |
| 6 | +import cn.iocoder.yudao.module.fms.enums.config.FmsSubjectTypeEnum; | |
| 7 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.initialbalance.FmsInitialBalanceExcelVO; | |
| 8 | +import lombok.experimental.UtilityClass; | |
| 9 | +import org.apache.poi.ss.usermodel.Cell; | |
| 10 | +import org.apache.poi.ss.usermodel.CellStyle; | |
| 11 | +import org.apache.poi.ss.usermodel.FormulaEvaluator; | |
| 12 | +import org.apache.poi.ss.usermodel.IndexedColors; | |
| 13 | +import org.apache.poi.ss.usermodel.Row; | |
| 14 | +import org.apache.poi.ss.usermodel.Sheet; | |
| 15 | +import org.apache.poi.ss.usermodel.Workbook; | |
| 16 | +import org.apache.poi.ss.usermodel.WorkbookFactory; | |
| 17 | +import org.apache.poi.ss.util.CellRangeAddress; | |
| 18 | +import org.apache.poi.xssf.usermodel.XSSFWorkbook; | |
| 19 | +import org.springframework.web.multipart.MultipartFile; | |
| 20 | + | |
| 21 | +import java.io.ByteArrayOutputStream; | |
| 22 | +import java.io.IOException; | |
| 23 | +import java.math.BigDecimal; | |
| 24 | +import java.util.ArrayList; | |
| 25 | +import java.util.Arrays; | |
| 26 | +import java.util.List; | |
| 27 | +import java.util.Map; | |
| 28 | +import java.util.Set; | |
| 29 | + | |
| 30 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 31 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; | |
| 32 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | |
| 33 | +import static cn.iocoder.yudao.framework.common.util.number.NumberUtils.zeroIfNull; | |
| 34 | +import static cn.iocoder.yudao.framework.excel.core.util.PoiExcelUtils.createDataStyle; | |
| 35 | +import static cn.iocoder.yudao.framework.excel.core.util.PoiExcelUtils.createHeaderStyle; | |
| 36 | +import static cn.iocoder.yudao.framework.excel.core.util.PoiExcelUtils.createNumberStyle; | |
| 37 | +import static cn.iocoder.yudao.framework.excel.core.util.PoiExcelUtils.getCellText; | |
| 38 | +import static cn.iocoder.yudao.framework.excel.core.util.PoiExcelUtils.getDecimal; | |
| 39 | +import static cn.iocoder.yudao.framework.excel.core.util.PoiExcelUtils.isEmptyRow; | |
| 40 | +import static cn.iocoder.yudao.framework.excel.core.util.PoiExcelUtils.mergeHeader; | |
| 41 | +import static cn.iocoder.yudao.framework.excel.core.util.PoiExcelUtils.writeFormula; | |
| 42 | +import static cn.iocoder.yudao.framework.excel.core.util.PoiExcelUtils.writeNumber; | |
| 43 | +import static cn.iocoder.yudao.framework.excel.core.util.PoiExcelUtils.writeText; | |
| 44 | + | |
| 45 | +/** | |
| 46 | + * FMS 初始余额 Excel 工具 | |
| 47 | + * | |
| 48 | + * @author 芋道源码 | |
| 49 | + */ | |
| 50 | +@UtilityClass | |
| 51 | +public final class FmsInitialBalanceExcelHelper { | |
| 52 | + | |
| 53 | + private static final String TEMPLATE_SHEET_NAME = "初始余额模板"; | |
| 54 | + | |
| 55 | + /** | |
| 56 | + * 展开初始余额平铺列表为 Excel 行 | |
| 57 | + * | |
| 58 | + * 父级科目仅在 includeParent 时输出行;启用辅助核算且存在明细的科目按明细逐行输出 | |
| 59 | + */ | |
| 60 | + public static List<FmsInitialBalanceExcelVO> toRows(List<FmsInitialBalanceRespVO> results, | |
| 61 | + boolean includeParent) { | |
| 62 | + Set<Long> parentIds = convertSet(results, FmsInitialBalanceRespVO::getParentId); | |
| 63 | + List<FmsInitialBalanceExcelVO> rows = new ArrayList<>(); | |
| 64 | + results.forEach(result -> { | |
| 65 | + if (parentIds.contains(result.getSubjectId())) { | |
| 66 | + if (includeParent) { | |
| 67 | + rows.add(buildRow(result, null)); | |
| 68 | + } | |
| 69 | + return; | |
| 70 | + } | |
| 71 | + if (Boolean.FALSE.equals(result.getAuxiliaryAccounting()) | |
| 72 | + || CollUtil.isEmpty(result.getAssistBalances())) { | |
| 73 | + rows.add(buildRow(result, null)); | |
| 74 | + return; | |
| 75 | + } | |
| 76 | + result.getAssistBalances().forEach(assist -> rows.add(buildRow(result, assist))); | |
| 77 | + }); | |
| 78 | + return rows; | |
| 79 | + } | |
| 80 | + | |
| 81 | + /** | |
| 82 | + * 构建单个科目或辅助核算明细对应的 Excel 行 | |
| 83 | + */ | |
| 84 | + private static FmsInitialBalanceExcelVO buildRow(FmsInitialBalanceRespVO result, | |
| 85 | + FmsInitialBalanceRespVO.AssistBalance assist) { | |
| 86 | + FmsInitialBalanceExcelVO row = new FmsInitialBalanceExcelVO() | |
| 87 | + .setSubjectCode(result.getSubjectCode()).setSubjectName(result.getSubjectName()) | |
| 88 | + .setDirectionName(FmsDebitCreditDirectionEnum.valueOf(result.getBalanceDirection()).getName()) | |
| 89 | + .setSubjectType(result.getType()).setQuantityAccounting(result.getQuantityAccounting()) | |
| 90 | + .setAuxiliaryAccounting(result.getAuxiliaryAccounting()); | |
| 91 | + if (assist == null) { | |
| 92 | + copyAmounts(result, row); | |
| 93 | + } else { | |
| 94 | + // 辅助核算项目拼接为“类别:名称/类别:名称”,与导入解析的格式对应 | |
| 95 | + Map<Long, String> auxiliaryTypeNameMap = convertMap(result.getAuxiliaryConfigs(), | |
| 96 | + FmsInitialBalanceRespVO.AuxiliaryConfig::getAuxiliaryTypeId, | |
| 97 | + FmsInitialBalanceRespVO.AuxiliaryConfig::getName); | |
| 98 | + row.setAuxiliaryItems(String.join("/", convertList(assist.getAuxiliaries(), | |
| 99 | + item -> auxiliaryTypeNameMap.get(item.getTypeId()) + ":" + item.getName()))); | |
| 100 | + copyAmounts(assist, row); | |
| 101 | + } | |
| 102 | + return row; | |
| 103 | + } | |
| 104 | + | |
| 105 | + private static void copyAmounts(FmsInitialBalanceRespVO source, FmsInitialBalanceExcelVO target) { | |
| 106 | + target.setOpeningAmount(zeroIfNull(source.getOpeningAmount())) | |
| 107 | + .setOpeningQuantity(zeroIfNull(source.getOpeningQuantity())) | |
| 108 | + .setYearDebitAmount(zeroIfNull(source.getYearDebitAmount())) | |
| 109 | + .setYearDebitQuantity(zeroIfNull(source.getYearDebitQuantity())) | |
| 110 | + .setYearCreditAmount(zeroIfNull(source.getYearCreditAmount())) | |
| 111 | + .setYearCreditQuantity(zeroIfNull(source.getYearCreditQuantity())) | |
| 112 | + .setYearOpeningAmount(zeroIfNull(source.getYearOpeningAmount())) | |
| 113 | + .setYearOpeningQuantity(zeroIfNull(source.getYearOpeningQuantity())) | |
| 114 | + .setProfitLossAmount(zeroIfNull(source.getProfitLossAmount())) | |
| 115 | + .setProfitLossQuantity(zeroIfNull(source.getProfitLossQuantity())); | |
| 116 | + } | |
| 117 | + | |
| 118 | + private static void copyAmounts(FmsInitialBalanceRespVO.AssistBalance source, | |
| 119 | + FmsInitialBalanceExcelVO target) { | |
| 120 | + target.setOpeningAmount(zeroIfNull(source.getOpeningAmount())) | |
| 121 | + .setOpeningQuantity(zeroIfNull(source.getOpeningQuantity())) | |
| 122 | + .setYearDebitAmount(zeroIfNull(source.getYearDebitAmount())) | |
| 123 | + .setYearDebitQuantity(zeroIfNull(source.getYearDebitQuantity())) | |
| 124 | + .setYearCreditAmount(zeroIfNull(source.getYearCreditAmount())) | |
| 125 | + .setYearCreditQuantity(zeroIfNull(source.getYearCreditQuantity())) | |
| 126 | + .setYearOpeningAmount(zeroIfNull(source.getYearOpeningAmount())) | |
| 127 | + .setYearOpeningQuantity(zeroIfNull(source.getYearOpeningQuantity())) | |
| 128 | + .setProfitLossAmount(zeroIfNull(source.getProfitLossAmount())) | |
| 129 | + .setProfitLossQuantity(zeroIfNull(source.getProfitLossQuantity())); | |
| 130 | + } | |
| 131 | + | |
| 132 | + /** | |
| 133 | + * 生成导入模板 | |
| 134 | + * | |
| 135 | + * 模板锁定系统生成列,年初余额列写入公式,并通过 protectSheet 防止误改 | |
| 136 | + */ | |
| 137 | + public static byte[] writeTemplate(List<FmsInitialBalanceExcelVO> rows, | |
| 138 | + boolean january) throws IOException { | |
| 139 | + try (Workbook workbook = new XSSFWorkbook(); ByteArrayOutputStream output = new ByteArrayOutputStream()) { | |
| 140 | + // 1. 写入表头,一月启用时只保留期初余额 | |
| 141 | + Sheet sheet = workbook.createSheet(TEMPLATE_SHEET_NAME); | |
| 142 | + writeHeaders(workbook, sheet, 0, january); | |
| 143 | + // 2. 写入科目数据,系统生成列锁定,可填写列解锁 | |
| 144 | + CellStyle lockedStyle = createDataStyle(workbook, true, IndexedColors.GREY_25_PERCENT); | |
| 145 | + CellStyle editableStyle = createDataStyle(workbook, false, IndexedColors.WHITE); | |
| 146 | + CellStyle numberLockedStyle = createNumberStyle(workbook, lockedStyle, "#,##0.0000"); | |
| 147 | + CellStyle amountLockedStyle = createNumberStyle(workbook, lockedStyle, "#,##0.00"); | |
| 148 | + CellStyle numberEditableStyle = createNumberStyle(workbook, editableStyle, "#,##0.0000"); | |
| 149 | + CellStyle amountEditableStyle = createNumberStyle(workbook, editableStyle, "#,##0.00"); | |
| 150 | + for (int index = 0; index < rows.size(); index++) { | |
| 151 | + writeDataRow(sheet.createRow(index + 2), rows.get(index), january, true, | |
| 152 | + lockedStyle, editableStyle, numberLockedStyle, amountLockedStyle, | |
| 153 | + numberEditableStyle, amountEditableStyle); | |
| 154 | + } | |
| 155 | + // 3. 保护工作表并补充填写说明 | |
| 156 | + sheet.protectSheet("yudao-fms"); | |
| 157 | + sheet.createFreezePane(4, 2); | |
| 158 | + writeInstructions(workbook); | |
| 159 | + workbook.write(output); | |
| 160 | + return output.toByteArray(); | |
| 161 | + } | |
| 162 | + } | |
| 163 | + | |
| 164 | + /** | |
| 165 | + * 读取导入文件 | |
| 166 | + * | |
| 167 | + * 按模板表头校验文件合法性,年初余额等公式单元格取公式计算值;不限制导入行数 | |
| 168 | + */ | |
| 169 | + public static List<FmsInitialBalanceExcelVO> read(MultipartFile file) throws IOException { | |
| 170 | + try (Workbook workbook = WorkbookFactory.create(file.getInputStream())) { | |
| 171 | + // 1. 校验模板 sheet 和表头 | |
| 172 | + Sheet sheet = workbook.getSheet(TEMPLATE_SHEET_NAME); | |
| 173 | + if (sheet == null || !hasValidHeaders(sheet)) { | |
| 174 | + throw new IllegalArgumentException("初始余额模板表头不正确"); | |
| 175 | + } | |
| 176 | + // 2. 逐行解析,跳过空行 | |
| 177 | + FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator(); | |
| 178 | + boolean january = sheet.getRow(1).getLastCellNum() <= 6; | |
| 179 | + List<FmsInitialBalanceExcelVO> rows = new ArrayList<>(); | |
| 180 | + for (int rowIndex = 2; rowIndex <= sheet.getLastRowNum(); rowIndex++) { | |
| 181 | + Row row = sheet.getRow(rowIndex); | |
| 182 | + if (isEmptyRow(row, evaluator, Arrays.asList(0, 1, 3, 4, 5))) { | |
| 183 | + continue; | |
| 184 | + } | |
| 185 | + rows.add(parseRow(row, january, evaluator)); | |
| 186 | + } | |
| 187 | + return rows; | |
| 188 | + } | |
| 189 | + } | |
| 190 | + | |
| 191 | + /** | |
| 192 | + * 写入双行表头,数量和金额成组;一月启用时只保留期初余额组 | |
| 193 | + */ | |
| 194 | + private static void writeHeaders(Workbook workbook, Sheet sheet, int rowIndex, boolean january) { | |
| 195 | + CellStyle style = createHeaderStyle(workbook, IndexedColors.GREY_25_PERCENT); | |
| 196 | + int lastColumn = january ? 5 : 13; | |
| 197 | + Row firstRow = sheet.createRow(rowIndex); | |
| 198 | + Row secondRow = sheet.createRow(rowIndex + 1); | |
| 199 | + mergeHeader(sheet, firstRow, secondRow, 0, 0, "科目编码", style); | |
| 200 | + mergeHeader(sheet, firstRow, secondRow, 1, 1, "科目名称", style); | |
| 201 | + mergeHeader(sheet, firstRow, secondRow, 2, 2, "方向", style); | |
| 202 | + mergeHeader(sheet, firstRow, secondRow, 3, 3, "辅助核算项目", style); | |
| 203 | + writeGroupHeader(sheet, firstRow, secondRow, 4, "期初余额", style); | |
| 204 | + if (!january) { | |
| 205 | + writeGroupHeader(sheet, firstRow, secondRow, 6, "本年累计借方", style); | |
| 206 | + writeGroupHeader(sheet, firstRow, secondRow, 8, "本年累计贷方", style); | |
| 207 | + writeGroupHeader(sheet, firstRow, secondRow, 10, "年初余额", style); | |
| 208 | + writeGroupHeader(sheet, firstRow, secondRow, 12, "实际损益发生额", style); | |
| 209 | + } | |
| 210 | + for (int column = 0; column <= lastColumn; column++) { | |
| 211 | + sheet.setColumnWidth(column, column == 1 || column == 3 ? 7_000 : 4_000); | |
| 212 | + } | |
| 213 | + firstRow.setHeightInPoints(28); | |
| 214 | + secondRow.setHeightInPoints(24); | |
| 215 | + } | |
| 216 | + | |
| 217 | + /** | |
| 218 | + * 写入“数量/金额”成组表头,第一行合并组名,第二行拆出数量和金额 | |
| 219 | + */ | |
| 220 | + private static void writeGroupHeader(Sheet sheet, Row firstRow, Row secondRow, | |
| 221 | + int beginColumn, String title, CellStyle style) { | |
| 222 | + sheet.addMergedRegion(new CellRangeAddress(firstRow.getRowNum(), firstRow.getRowNum(), beginColumn, beginColumn + 1)); | |
| 223 | + Cell groupCell = firstRow.createCell(beginColumn); | |
| 224 | + groupCell.setCellValue(title); | |
| 225 | + groupCell.setCellStyle(style); | |
| 226 | + firstRow.createCell(beginColumn + 1).setCellStyle(style); | |
| 227 | + Cell quantityCell = secondRow.createCell(beginColumn); | |
| 228 | + quantityCell.setCellValue("数量"); | |
| 229 | + quantityCell.setCellStyle(style); | |
| 230 | + Cell amountCell = secondRow.createCell(beginColumn + 1); | |
| 231 | + amountCell.setCellValue("金额"); | |
| 232 | + amountCell.setCellStyle(style); | |
| 233 | + } | |
| 234 | + | |
| 235 | + /** | |
| 236 | + * 写入一行数据 | |
| 237 | + * | |
| 238 | + * 模板模式下按科目配置解锁可填写单元格(数量列仅数量核算科目可填,损益列仅损益类科目可填), | |
| 239 | + * 年初余额列写入“期初 ± 累计借贷”公式 | |
| 240 | + */ | |
| 241 | + private static void writeDataRow(Row excelRow, FmsInitialBalanceExcelVO data, | |
| 242 | + boolean january, boolean template, CellStyle lockedStyle, CellStyle editableStyle, | |
| 243 | + CellStyle numberLockedStyle, CellStyle amountLockedStyle, | |
| 244 | + CellStyle numberEditableStyle, CellStyle amountEditableStyle) { | |
| 245 | + writeText(excelRow, 0, data.getSubjectCode(), lockedStyle); | |
| 246 | + writeText(excelRow, 1, data.getSubjectName(), lockedStyle); | |
| 247 | + writeText(excelRow, 2, data.getDirectionName(), lockedStyle); | |
| 248 | + writeText(excelRow, 3, data.getAuxiliaryItems(), | |
| 249 | + template && Boolean.TRUE.equals(data.getAuxiliaryAccounting()) ? editableStyle : lockedStyle); | |
| 250 | + writeNumber(excelRow, 4, data.getOpeningQuantity(), | |
| 251 | + template && Boolean.TRUE.equals(data.getQuantityAccounting()) | |
| 252 | + ? numberEditableStyle : numberLockedStyle); | |
| 253 | + writeNumber(excelRow, 5, data.getOpeningAmount(), template ? amountEditableStyle : amountLockedStyle); | |
| 254 | + if (january) { | |
| 255 | + return; | |
| 256 | + } | |
| 257 | + writeNumber(excelRow, 6, data.getYearDebitQuantity(), | |
| 258 | + template && Boolean.TRUE.equals(data.getQuantityAccounting()) | |
| 259 | + ? numberEditableStyle : numberLockedStyle); | |
| 260 | + writeNumber(excelRow, 7, data.getYearDebitAmount(), template ? amountEditableStyle : amountLockedStyle); | |
| 261 | + writeNumber(excelRow, 8, data.getYearCreditQuantity(), | |
| 262 | + template && Boolean.TRUE.equals(data.getQuantityAccounting()) | |
| 263 | + ? numberEditableStyle : numberLockedStyle); | |
| 264 | + writeNumber(excelRow, 9, data.getYearCreditAmount(), template ? amountEditableStyle : amountLockedStyle); | |
| 265 | + if (template) { | |
| 266 | + String quantityFormula = "借".equals(data.getDirectionName()) | |
| 267 | + ? "E" + (excelRow.getRowNum() + 1) + "-G" + (excelRow.getRowNum() + 1) | |
| 268 | + + "+I" + (excelRow.getRowNum() + 1) | |
| 269 | + : "E" + (excelRow.getRowNum() + 1) + "+G" + (excelRow.getRowNum() + 1) | |
| 270 | + + "-I" + (excelRow.getRowNum() + 1); | |
| 271 | + String amountFormula = "借".equals(data.getDirectionName()) | |
| 272 | + ? "F" + (excelRow.getRowNum() + 1) + "-H" + (excelRow.getRowNum() + 1) | |
| 273 | + + "+J" + (excelRow.getRowNum() + 1) | |
| 274 | + : "F" + (excelRow.getRowNum() + 1) + "+H" + (excelRow.getRowNum() + 1) | |
| 275 | + + "-J" + (excelRow.getRowNum() + 1); | |
| 276 | + writeFormula(excelRow, 10, quantityFormula, numberLockedStyle); | |
| 277 | + writeFormula(excelRow, 11, amountFormula, amountLockedStyle); | |
| 278 | + } else { | |
| 279 | + writeNumber(excelRow, 10, data.getYearOpeningQuantity(), numberLockedStyle); | |
| 280 | + writeNumber(excelRow, 11, data.getYearOpeningAmount(), amountLockedStyle); | |
| 281 | + } | |
| 282 | + boolean profitLoss = FmsSubjectTypeEnum.PROFIT_LOSS.getType().equals(data.getSubjectType()); | |
| 283 | + writeNumber(excelRow, 12, profitLoss ? data.getProfitLossQuantity() : null, | |
| 284 | + template && profitLoss && Boolean.TRUE.equals(data.getQuantityAccounting()) | |
| 285 | + ? numberEditableStyle : numberLockedStyle); | |
| 286 | + writeNumber(excelRow, 13, profitLoss ? data.getProfitLossAmount() : null, | |
| 287 | + template && profitLoss ? amountEditableStyle : amountLockedStyle); | |
| 288 | + } | |
| 289 | + | |
| 290 | + /** | |
| 291 | + * 解析一行数据,公式单元格通过 evaluator 取计算值 | |
| 292 | + */ | |
| 293 | + private static FmsInitialBalanceExcelVO parseRow(Row row, boolean january, FormulaEvaluator evaluator) { | |
| 294 | + FmsInitialBalanceExcelVO result = new FmsInitialBalanceExcelVO() | |
| 295 | + .setRowNumber(row.getRowNum() + 1) | |
| 296 | + .setSubjectCode(getCellText(row, 0, evaluator)) | |
| 297 | + .setSubjectName(getCellText(row, 1, evaluator)) | |
| 298 | + .setDirectionName(getCellText(row, 2, evaluator)) | |
| 299 | + .setAuxiliaryItems(getCellText(row, 3, evaluator)) | |
| 300 | + .setOpeningQuantity(getDecimal(row, 4, evaluator)) | |
| 301 | + .setOpeningAmount(getDecimal(row, 5, evaluator)); | |
| 302 | + if (!january) { | |
| 303 | + result.setYearDebitQuantity(getDecimal(row, 6, evaluator)) | |
| 304 | + .setYearDebitAmount(getDecimal(row, 7, evaluator)) | |
| 305 | + .setYearCreditQuantity(getDecimal(row, 8, evaluator)) | |
| 306 | + .setYearCreditAmount(getDecimal(row, 9, evaluator)) | |
| 307 | + .setYearOpeningQuantity(getDecimal(row, 10, evaluator)) | |
| 308 | + .setYearOpeningAmount(getDecimal(row, 11, evaluator)) | |
| 309 | + .setProfitLossQuantity(getDecimal(row, 12, evaluator)) | |
| 310 | + .setProfitLossAmount(getDecimal(row, 13, evaluator)); | |
| 311 | + } | |
| 312 | + return result; | |
| 313 | + } | |
| 314 | + | |
| 315 | + private static boolean hasValidHeaders(Sheet sheet) { | |
| 316 | + Row firstRow = sheet.getRow(0); | |
| 317 | + Row secondRow = sheet.getRow(1); | |
| 318 | + return firstRow != null && secondRow != null | |
| 319 | + && "科目编码".equals(getCellText(firstRow, 0, null)) | |
| 320 | + && "科目名称".equals(getCellText(firstRow, 1, null)) | |
| 321 | + && "方向".equals(getCellText(firstRow, 2, null)) | |
| 322 | + && "辅助核算项目".equals(getCellText(firstRow, 3, null)) | |
| 323 | + && "期初余额".equals(getCellText(firstRow, 4, null)) | |
| 324 | + && "数量".equals(getCellText(secondRow, 4, null)) | |
| 325 | + && "金额".equals(getCellText(secondRow, 5, null)); | |
| 326 | + } | |
| 327 | + | |
| 328 | + private static void writeInstructions(Workbook workbook) { | |
| 329 | + Sheet sheet = workbook.createSheet("填写说明"); | |
| 330 | + String[] instructions = { | |
| 331 | + "1. 灰色单元格由系统生成,请勿修改", | |
| 332 | + "2. 仅末级科目可以导入初始余额", | |
| 333 | + "3. 辅助核算项目使用“类别:名称/类别:名称”格式,例如“客户:北京客户/部门:销售部”", | |
| 334 | + "4. 数量核算科目可填写数量,金额和数量不能小于 0", | |
| 335 | + "5. 年初余额由期初余额和本年累计发生额自动计算" | |
| 336 | + }; | |
| 337 | + for (int index = 0; index < instructions.length; index++) { | |
| 338 | + sheet.createRow(index).createCell(0).setCellValue(instructions[index]); | |
| 339 | + } | |
| 340 | + sheet.setColumnWidth(0, 20_000); | |
| 341 | + } | |
| 342 | + | |
| 343 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/FmsSubjectController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; | |
| 5 | +import cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum; | |
| 6 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 7 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 8 | +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; | |
| 9 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.subject.FmsSubjectDeleteReqVO; | |
| 10 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.subject.FmsSubjectImportExcelVO; | |
| 11 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.subject.FmsSubjectImportRespVO; | |
| 12 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.subject.FmsSubjectListReqVO; | |
| 13 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.subject.FmsSubjectRespVO; | |
| 14 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.subject.FmsSubjectSaveReqVO; | |
| 15 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.subject.FmsSubjectStatusReqVO; | |
| 16 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.subject.FmsSubjectUsageRespVO; | |
| 17 | +import cn.iocoder.yudao.module.fms.dal.dataobject.config.FmsAuxiliaryTypeDO; | |
| 18 | +import cn.iocoder.yudao.module.fms.dal.dataobject.config.FmsSubjectDO; | |
| 19 | +import cn.iocoder.yudao.module.fms.service.config.FmsAuxiliaryTypeService; | |
| 20 | +import cn.iocoder.yudao.module.fms.service.config.FmsSubjectService; | |
| 21 | +import io.swagger.v3.oas.annotations.Operation; | |
| 22 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 23 | +import io.swagger.v3.oas.annotations.Parameters; | |
| 24 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 25 | +import jakarta.annotation.Resource; | |
| 26 | +import jakarta.servlet.http.HttpServletResponse; | |
| 27 | +import jakarta.validation.Valid; | |
| 28 | +import jakarta.validation.constraints.NotNull; | |
| 29 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 30 | +import org.springframework.validation.annotation.Validated; | |
| 31 | +import org.springframework.web.bind.annotation.*; | |
| 32 | +import org.springframework.web.multipart.MultipartFile; | |
| 33 | + | |
| 34 | +import java.io.IOException; | |
| 35 | +import java.util.Arrays; | |
| 36 | +import java.util.Collections; | |
| 37 | +import java.util.List; | |
| 38 | +import java.util.Map; | |
| 39 | + | |
| 40 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 41 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 42 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; | |
| 43 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | |
| 44 | + | |
| 45 | +@Tag(name = "管理后台 - FMS 会计科目") | |
| 46 | +@RestController | |
| 47 | +@RequestMapping("/fms/config/subject") | |
| 48 | +@Validated | |
| 49 | +public class FmsSubjectController { | |
| 50 | + | |
| 51 | + @Resource | |
| 52 | + private FmsSubjectService subjectService; | |
| 53 | + @Resource | |
| 54 | + private FmsAuxiliaryTypeService auxiliaryTypeService; | |
| 55 | + | |
| 56 | + @PostMapping("/create") | |
| 57 | + @Operation(summary = "创建科目") | |
| 58 | + @PreAuthorize("@ss.hasPermission('fms:config:subject:create')") | |
| 59 | + public CommonResult<Long> createSubject(@Valid @RequestBody FmsSubjectSaveReqVO createReqVO) { | |
| 60 | + return success(subjectService.createSubject(createReqVO, getLoginUserId())); | |
| 61 | + } | |
| 62 | + | |
| 63 | + @PutMapping("/update") | |
| 64 | + @Operation(summary = "更新科目") | |
| 65 | + @PreAuthorize("@ss.hasPermission('fms:config:subject:update')") | |
| 66 | + public CommonResult<Boolean> updateSubject(@Valid @RequestBody FmsSubjectSaveReqVO updateReqVO) { | |
| 67 | + subjectService.updateSubject(updateReqVO, getLoginUserId()); | |
| 68 | + return success(true); | |
| 69 | + } | |
| 70 | + | |
| 71 | + @DeleteMapping("/delete-list") | |
| 72 | + @Operation(summary = "删除科目") | |
| 73 | + @PreAuthorize("@ss.hasPermission('fms:config:subject:delete')") | |
| 74 | + public CommonResult<Boolean> deleteSubjectList(@Valid @RequestBody FmsSubjectDeleteReqVO deleteReqVO) { | |
| 75 | + subjectService.deleteSubjectList(deleteReqVO, getLoginUserId()); | |
| 76 | + return success(true); | |
| 77 | + } | |
| 78 | + | |
| 79 | + @PutMapping("/update-status") | |
| 80 | + @Operation(summary = "更新科目状态") | |
| 81 | + @PreAuthorize("@ss.hasPermission('fms:config:subject:update')") | |
| 82 | + public CommonResult<Boolean> updateSubjectStatus(@Valid @RequestBody FmsSubjectStatusReqVO statusReqVO) { | |
| 83 | + subjectService.updateSubjectStatus(statusReqVO, getLoginUserId()); | |
| 84 | + return success(true); | |
| 85 | + } | |
| 86 | + | |
| 87 | + @GetMapping("/get") | |
| 88 | + @Operation(summary = "获得科目") | |
| 89 | + @Parameters({ | |
| 90 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024"), | |
| 91 | + @Parameter(name = "id", description = "科目编号", required = true, example = "1024") | |
| 92 | + }) | |
| 93 | + @PreAuthorize("@ss.hasPermission('fms:config:subject:query')") | |
| 94 | + public CommonResult<FmsSubjectRespVO> getSubject(@RequestParam("accountSetId") @NotNull Long accountSetId, | |
| 95 | + @RequestParam("id") @NotNull Long id) { | |
| 96 | + FmsSubjectDO subject = subjectService.getSubject(accountSetId, id, getLoginUserId()); | |
| 97 | + return success(buildSubjectRespVO(subject)); | |
| 98 | + } | |
| 99 | + | |
| 100 | + @GetMapping("/get-usage") | |
| 101 | + @Operation(summary = "获得科目使用情况") | |
| 102 | + @Parameters({ | |
| 103 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024"), | |
| 104 | + @Parameter(name = "id", description = "科目编号", required = true, example = "1024") | |
| 105 | + }) | |
| 106 | + @PreAuthorize("@ss.hasPermission('fms:config:subject:query')") | |
| 107 | + public CommonResult<FmsSubjectUsageRespVO> getSubjectUsage( | |
| 108 | + @RequestParam("accountSetId") @NotNull Long accountSetId, | |
| 109 | + @RequestParam("id") @NotNull Long id) { | |
| 110 | + return success(subjectService.getSubjectUsage(accountSetId, id, getLoginUserId())); | |
| 111 | + } | |
| 112 | + | |
| 113 | + @GetMapping("/list") | |
| 114 | + @Operation(summary = "获得科目列表") | |
| 115 | + @PreAuthorize("@ss.hasPermission('fms:config:subject:query')") | |
| 116 | + public CommonResult<List<FmsSubjectRespVO>> getSubjectList(@Valid FmsSubjectListReqVO listReqVO) { | |
| 117 | + List<FmsSubjectDO> subjects = subjectService.getSubjectList(listReqVO, getLoginUserId()); | |
| 118 | + return success(buildSubjectRespVOList(subjects)); | |
| 119 | + } | |
| 120 | + | |
| 121 | + @GetMapping("/simple-list") | |
| 122 | + @Operation(summary = "获得科目精简列表") | |
| 123 | + public CommonResult<List<FmsSubjectRespVO>> getSubjectSimpleList(@Valid FmsSubjectListReqVO listReqVO) { | |
| 124 | + List<FmsSubjectDO> subjects = subjectService.getSubjectList(listReqVO, getLoginUserId()); | |
| 125 | + return success(convertList(subjects, subject -> new FmsSubjectRespVO() | |
| 126 | + .setId(subject.getId()).setParentId(subject.getParentId()) | |
| 127 | + .setCode(subject.getCode()).setName(subject.getName()).setType(subject.getType()) | |
| 128 | + .setBalanceDirection(subject.getBalanceDirection()).setStatus(subject.getStatus()) | |
| 129 | + .setLevel(subject.getLevel()).setAuxiliaryTypeIds(subject.getAuxiliaryTypeIds()) | |
| 130 | + .setQuantityAccounting(subject.getQuantityAccounting()).setQuantityUnit(subject.getQuantityUnit()))); | |
| 131 | + } | |
| 132 | + | |
| 133 | + @GetMapping("/export-excel") | |
| 134 | + @Operation(summary = "导出科目") | |
| 135 | + @PreAuthorize("@ss.hasPermission('fms:config:subject:export')") | |
| 136 | + @ApiAccessLog(operateType = OperateTypeEnum.EXPORT) | |
| 137 | + public void exportSubject(@Valid FmsSubjectListReqVO listReqVO, | |
| 138 | + HttpServletResponse response) throws IOException { | |
| 139 | + List<FmsSubjectDO> subjects = subjectService.getSubjectList(listReqVO, getLoginUserId()); | |
| 140 | + ExcelUtils.write(response, "科目设置.xlsx", "科目设置", FmsSubjectRespVO.class, | |
| 141 | + buildSubjectRespVOList(subjects)); | |
| 142 | + } | |
| 143 | + | |
| 144 | + @GetMapping("/get-import-template") | |
| 145 | + @Operation(summary = "获得科目导入模板") | |
| 146 | + @PreAuthorize("@ss.hasPermission('fms:config:subject:import')") | |
| 147 | + @ApiAccessLog(operateType = OperateTypeEnum.EXPORT) | |
| 148 | + public void getSubjectImportTemplate(HttpServletResponse response) throws IOException { | |
| 149 | + List<FmsSubjectImportExcelVO> list = Arrays.asList( | |
| 150 | + FmsSubjectImportExcelVO.builder().code("1001").name("库存现金") | |
| 151 | + .parentSubjectCode("0").balanceDirection("借").categoryName("流动资产").build(), | |
| 152 | + FmsSubjectImportExcelVO.builder().code("100101").name("人民币现金") | |
| 153 | + .parentSubjectCode("1001").balanceDirection("借").categoryName("流动资产") | |
| 154 | + .auxiliaryNames("部门/职员").build()); | |
| 155 | + ExcelUtils.write(response, "科目导入模板.xlsx", "科目列表", FmsSubjectImportExcelVO.class, list); | |
| 156 | + } | |
| 157 | + | |
| 158 | + @PostMapping("/import") | |
| 159 | + @Operation(summary = "导入科目") | |
| 160 | + @Parameters({ | |
| 161 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024"), | |
| 162 | + @Parameter(name = "file", description = "Excel 文件", required = true) | |
| 163 | + }) | |
| 164 | + @PreAuthorize("@ss.hasPermission('fms:config:subject:import')") | |
| 165 | + @ApiAccessLog(operateType = OperateTypeEnum.IMPORT) | |
| 166 | + public CommonResult<FmsSubjectImportRespVO> importSubject( | |
| 167 | + @RequestParam("accountSetId") @NotNull Long accountSetId, | |
| 168 | + @RequestParam("file") MultipartFile file) throws IOException { | |
| 169 | + List<FmsSubjectImportExcelVO> importSubjects = ExcelUtils.read(file, FmsSubjectImportExcelVO.class); | |
| 170 | + return success(subjectService.importSubjectList(accountSetId, importSubjects, getLoginUserId())); | |
| 171 | + } | |
| 172 | + | |
| 173 | + // ==================== 拼接 VO ==================== | |
| 174 | + | |
| 175 | + private List<FmsSubjectRespVO> buildSubjectRespVOList(List<FmsSubjectDO> subjects) { | |
| 176 | + Map<Long, FmsAuxiliaryTypeDO> auxiliaryTypeMap = auxiliaryTypeService.getAuxiliaryTypeMap( | |
| 177 | + convertSetByFlatMap(subjects, FmsSubjectDO::getAuxiliaryTypeIds, List::stream)); | |
| 178 | + return convertList(subjects, subject -> { | |
| 179 | + FmsSubjectRespVO subjectVO = BeanUtils.toBean(subject, FmsSubjectRespVO.class) | |
| 180 | + .setCategoryDictValue(subject.getType() + "-" + subject.getCategory()); | |
| 181 | + subjectVO.setAuxiliaryTypeNames(convertList(subject.getAuxiliaryTypeIds(), | |
| 182 | + auxiliaryTypeId -> auxiliaryTypeMap.get(auxiliaryTypeId).getName())); | |
| 183 | + if (Boolean.FALSE.equals(subject.getQuantityAccounting())) { | |
| 184 | + subjectVO.setQuantityUnit(""); | |
| 185 | + } | |
| 186 | + return subjectVO; | |
| 187 | + }); | |
| 188 | + } | |
| 189 | + | |
| 190 | + private FmsSubjectRespVO buildSubjectRespVO(FmsSubjectDO subject) { | |
| 191 | + return subject == null ? null | |
| 192 | + : CollUtil.getFirst(buildSubjectRespVOList(Collections.singletonList(subject))); | |
| 193 | + } | |
| 194 | + | |
| 195 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/FmsVoucherTemplateCategoryController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config; | |
| 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.fms.controller.admin.config.vo.vouchertemplatecategory.FmsVoucherTemplateCategoryRespVO; | |
| 6 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.vouchertemplatecategory.FmsVoucherTemplateCategorySaveReqVO; | |
| 7 | +import cn.iocoder.yudao.module.fms.dal.dataobject.config.FmsVoucherTemplateCategoryDO; | |
| 8 | +import cn.iocoder.yudao.module.fms.service.config.FmsAccountSetService; | |
| 9 | +import cn.iocoder.yudao.module.fms.service.config.FmsVoucherTemplateService; | |
| 10 | +import io.swagger.v3.oas.annotations.Operation; | |
| 11 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 12 | +import jakarta.annotation.Resource; | |
| 13 | +import jakarta.validation.Valid; | |
| 14 | +import jakarta.validation.constraints.NotNull; | |
| 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.PostMapping; | |
| 20 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 21 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 22 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 23 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 24 | +import org.springframework.web.bind.annotation.RestController; | |
| 25 | + | |
| 26 | +import java.util.List; | |
| 27 | + | |
| 28 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 29 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 30 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | |
| 31 | + | |
| 32 | +@Tag(name = "管理后台 - FMS 凭证模板分类") | |
| 33 | +@RestController | |
| 34 | +@RequestMapping("/fms/config/voucher-template-category") | |
| 35 | +@Validated | |
| 36 | +public class FmsVoucherTemplateCategoryController { | |
| 37 | + | |
| 38 | + @Resource | |
| 39 | + private FmsVoucherTemplateService voucherTemplateService; | |
| 40 | + @Resource | |
| 41 | + private FmsAccountSetService accountSetService; | |
| 42 | + | |
| 43 | + @PostMapping("/create") | |
| 44 | + @Operation(summary = "创建凭证模板分类") | |
| 45 | + @PreAuthorize("@ss.hasPermission('fms:config:voucher-template-category:create')") | |
| 46 | + public CommonResult<Long> createTemplateCategory( | |
| 47 | + @Valid @RequestBody FmsVoucherTemplateCategorySaveReqVO createReqVO) { | |
| 48 | + return success(voucherTemplateService.createTemplateCategory(createReqVO, getLoginUserId())); | |
| 49 | + } | |
| 50 | + | |
| 51 | + @PutMapping("/update") | |
| 52 | + @Operation(summary = "修改凭证模板分类") | |
| 53 | + @PreAuthorize("@ss.hasPermission('fms:config:voucher-template-category:update')") | |
| 54 | + public CommonResult<Boolean> updateTemplateCategory( | |
| 55 | + @Valid @RequestBody FmsVoucherTemplateCategorySaveReqVO updateReqVO) { | |
| 56 | + voucherTemplateService.updateTemplateCategory(updateReqVO, getLoginUserId()); | |
| 57 | + return success(true); | |
| 58 | + } | |
| 59 | + | |
| 60 | + @DeleteMapping("/delete") | |
| 61 | + @Operation(summary = "删除凭证模板分类") | |
| 62 | + @PreAuthorize("@ss.hasPermission('fms:config:voucher-template-category:delete')") | |
| 63 | + public CommonResult<Boolean> deleteTemplateCategory(@RequestParam("accountSetId") @NotNull Long accountSetId, | |
| 64 | + @RequestParam("id") @NotNull Long id) { | |
| 65 | + voucherTemplateService.deleteTemplateCategory(accountSetId, id, getLoginUserId()); | |
| 66 | + return success(true); | |
| 67 | + } | |
| 68 | + | |
| 69 | + @GetMapping("/list") | |
| 70 | + @Operation(summary = "获得凭证模板分类列表") | |
| 71 | + @PreAuthorize("@ss.hasPermission('fms:config:voucher-template-category:query')") | |
| 72 | + public CommonResult<List<FmsVoucherTemplateCategoryRespVO>> getTemplateCategoryList( | |
| 73 | + @RequestParam("accountSetId") @NotNull Long accountSetId) { | |
| 74 | + accountSetService.validateAccountSetReadPermission(accountSetId, getLoginUserId()); | |
| 75 | + List<FmsVoucherTemplateCategoryDO> categories = voucherTemplateService.getTemplateCategoryList(accountSetId); | |
| 76 | + return success(BeanUtils.toBean(categories, FmsVoucherTemplateCategoryRespVO.class)); | |
| 77 | + } | |
| 78 | + | |
| 79 | + @GetMapping("/simple-list") | |
| 80 | + @Operation(summary = "获得凭证模板分类精简列表", description = "主要用于前端的下拉选项") | |
| 81 | + public CommonResult<List<FmsVoucherTemplateCategoryRespVO>> getTemplateCategorySimpleList( | |
| 82 | + @RequestParam("accountSetId") @NotNull Long accountSetId) { | |
| 83 | + accountSetService.validateAccountSetReadPermission(accountSetId, getLoginUserId()); | |
| 84 | + List<FmsVoucherTemplateCategoryDO> categories = voucherTemplateService.getTemplateCategoryList(accountSetId); | |
| 85 | + return success(convertList(categories, category -> new FmsVoucherTemplateCategoryRespVO() | |
| 86 | + .setId(category.getId()).setName(category.getName()))); | |
| 87 | + } | |
| 88 | + | |
| 89 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/FmsVoucherTemplateController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config; | |
| 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.fms.controller.admin.config.vo.vouchertemplate.FmsVoucherTemplateRespVO; | |
| 6 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.vouchertemplate.FmsVoucherTemplateSaveReqVO; | |
| 7 | +import cn.iocoder.yudao.module.fms.dal.dataobject.config.FmsVoucherTemplateCategoryDO; | |
| 8 | +import cn.iocoder.yudao.module.fms.dal.dataobject.config.FmsVoucherTemplateDO; | |
| 9 | +import cn.iocoder.yudao.module.fms.service.config.FmsAccountSetService; | |
| 10 | +import cn.iocoder.yudao.module.fms.service.config.FmsVoucherTemplateService; | |
| 11 | +import io.swagger.v3.oas.annotations.Operation; | |
| 12 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 13 | +import jakarta.annotation.Resource; | |
| 14 | +import jakarta.validation.Valid; | |
| 15 | +import jakarta.validation.constraints.NotNull; | |
| 16 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 17 | +import org.springframework.validation.annotation.Validated; | |
| 18 | +import org.springframework.web.bind.annotation.DeleteMapping; | |
| 19 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 20 | +import org.springframework.web.bind.annotation.PostMapping; | |
| 21 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 22 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 23 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 24 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 25 | +import org.springframework.web.bind.annotation.RestController; | |
| 26 | + | |
| 27 | +import java.util.List; | |
| 28 | +import java.util.Map; | |
| 29 | + | |
| 30 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 31 | +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; | |
| 32 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | |
| 33 | + | |
| 34 | +@Tag(name = "管理后台 - FMS 凭证模板") | |
| 35 | +@RestController | |
| 36 | +@RequestMapping("/fms/config/voucher-template") | |
| 37 | +@Validated | |
| 38 | +public class FmsVoucherTemplateController { | |
| 39 | + | |
| 40 | + @Resource | |
| 41 | + private FmsVoucherTemplateService voucherTemplateService; | |
| 42 | + @Resource | |
| 43 | + private FmsAccountSetService accountSetService; | |
| 44 | + | |
| 45 | + @PostMapping("/create") | |
| 46 | + @Operation(summary = "创建凭证模板") | |
| 47 | + @PreAuthorize("@ss.hasPermission('fms:config:voucher-template:create')") | |
| 48 | + public CommonResult<Long> createVoucherTemplate( | |
| 49 | + @Valid @RequestBody FmsVoucherTemplateSaveReqVO createReqVO) { | |
| 50 | + return success(voucherTemplateService.createVoucherTemplate(createReqVO, getLoginUserId())); | |
| 51 | + } | |
| 52 | + | |
| 53 | + @PutMapping("/update") | |
| 54 | + @Operation(summary = "修改凭证模板") | |
| 55 | + @PreAuthorize("@ss.hasPermission('fms:config:voucher-template:update')") | |
| 56 | + public CommonResult<Boolean> updateVoucherTemplate( | |
| 57 | + @Valid @RequestBody FmsVoucherTemplateSaveReqVO updateReqVO) { | |
| 58 | + voucherTemplateService.updateVoucherTemplate(updateReqVO, getLoginUserId()); | |
| 59 | + return success(true); | |
| 60 | + } | |
| 61 | + | |
| 62 | + @DeleteMapping("/delete") | |
| 63 | + @Operation(summary = "删除凭证模板") | |
| 64 | + @PreAuthorize("@ss.hasPermission('fms:config:voucher-template:delete')") | |
| 65 | + public CommonResult<Boolean> deleteVoucherTemplate(@RequestParam("accountSetId") @NotNull Long accountSetId, | |
| 66 | + @RequestParam("id") @NotNull Long id) { | |
| 67 | + voucherTemplateService.deleteVoucherTemplate(accountSetId, id, getLoginUserId()); | |
| 68 | + return success(true); | |
| 69 | + } | |
| 70 | + | |
| 71 | + @GetMapping("/list") | |
| 72 | + @Operation(summary = "获得凭证模板列表") | |
| 73 | + @PreAuthorize("@ss.hasPermission('fms:config:voucher-template:query')") | |
| 74 | + public CommonResult<List<FmsVoucherTemplateRespVO>> getVoucherTemplateList( | |
| 75 | + @RequestParam("accountSetId") @NotNull Long accountSetId) { | |
| 76 | + accountSetService.validateAccountSetReadPermission(accountSetId, getLoginUserId()); | |
| 77 | + return success(buildVoucherTemplateRespVOList(accountSetId)); | |
| 78 | + } | |
| 79 | + | |
| 80 | + @GetMapping("/simple-list") | |
| 81 | + @Operation(summary = "获得凭证模板精简列表", description = "主要用于录凭证时套用凭证模板") | |
| 82 | + public CommonResult<List<FmsVoucherTemplateRespVO>> getVoucherTemplateSimpleList( | |
| 83 | + @RequestParam("accountSetId") @NotNull Long accountSetId) { | |
| 84 | + accountSetService.validateAccountSetReadPermission(accountSetId, getLoginUserId()); | |
| 85 | + return success(buildVoucherTemplateRespVOList(accountSetId)); | |
| 86 | + } | |
| 87 | + | |
| 88 | + // ==================== 拼接 VO ==================== | |
| 89 | + | |
| 90 | + private List<FmsVoucherTemplateRespVO> buildVoucherTemplateRespVOList(Long accountSetId) { | |
| 91 | + Map<Long, FmsVoucherTemplateCategoryDO> categoryMap = | |
| 92 | + voucherTemplateService.getTemplateCategoryMap(accountSetId); | |
| 93 | + List<FmsVoucherTemplateDO> templates = voucherTemplateService.getVoucherTemplateList(accountSetId); | |
| 94 | + return BeanUtils.toBean(templates, FmsVoucherTemplateRespVO.class, template -> { | |
| 95 | + findAndThen(categoryMap, template.getCategoryId(), | |
| 96 | + category -> template.setCategoryName(category.getName())); | |
| 97 | + }); | |
| 98 | + } | |
| 99 | + | |
| 100 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/FmsVoucherWordController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config; | |
| 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.fms.controller.admin.config.vo.voucherword.FmsVoucherWordRespVO; | |
| 6 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.voucherword.FmsVoucherWordSaveReqVO; | |
| 7 | +import cn.iocoder.yudao.module.fms.dal.dataobject.config.FmsVoucherWordDO; | |
| 8 | +import cn.iocoder.yudao.module.fms.service.config.FmsVoucherWordService; | |
| 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 jakarta.validation.constraints.NotNull; | |
| 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.PostMapping; | |
| 20 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 21 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 22 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 23 | +import org.springframework.web.bind.annotation.RequestParam; | |
| 24 | +import org.springframework.web.bind.annotation.RestController; | |
| 25 | + | |
| 26 | +import java.util.List; | |
| 27 | + | |
| 28 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 29 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 30 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | |
| 31 | + | |
| 32 | +/** | |
| 33 | + * FMS 凭证字 Controller | |
| 34 | + * | |
| 35 | + * @author 芋道源码 | |
| 36 | + */ | |
| 37 | +@Tag(name = "管理后台 - FMS 凭证字") | |
| 38 | +@RestController | |
| 39 | +@RequestMapping("/fms/config/voucher-word") | |
| 40 | +@Validated | |
| 41 | +public class FmsVoucherWordController { | |
| 42 | + | |
| 43 | + @Resource | |
| 44 | + private FmsVoucherWordService voucherWordService; | |
| 45 | + | |
| 46 | + @PostMapping("/create") | |
| 47 | + @Operation(summary = "创建凭证字") | |
| 48 | + @PreAuthorize("@ss.hasPermission('fms:config:voucher-word:create')") | |
| 49 | + public CommonResult<Long> createVoucherWord(@Valid @RequestBody FmsVoucherWordSaveReqVO createReqVO) { | |
| 50 | + return success(voucherWordService.createVoucherWord(createReqVO, getLoginUserId())); | |
| 51 | + } | |
| 52 | + | |
| 53 | + @PutMapping("/update") | |
| 54 | + @Operation(summary = "修改凭证字") | |
| 55 | + @PreAuthorize("@ss.hasPermission('fms:config:voucher-word:update')") | |
| 56 | + public CommonResult<Boolean> updateVoucherWord(@Valid @RequestBody FmsVoucherWordSaveReqVO updateReqVO) { | |
| 57 | + voucherWordService.updateVoucherWord(updateReqVO, getLoginUserId()); | |
| 58 | + return success(true); | |
| 59 | + } | |
| 60 | + | |
| 61 | + @DeleteMapping("/delete") | |
| 62 | + @Operation(summary = "删除凭证字") | |
| 63 | + @Parameter(name = "id", description = "凭证字编号", required = true, example = "1024") | |
| 64 | + @PreAuthorize("@ss.hasPermission('fms:config:voucher-word:delete')") | |
| 65 | + public CommonResult<Boolean> deleteVoucherWord( | |
| 66 | + @RequestParam("accountSetId") @NotNull Long accountSetId, | |
| 67 | + @RequestParam("id") @NotNull Long id) { | |
| 68 | + voucherWordService.deleteVoucherWord(accountSetId, id, getLoginUserId()); | |
| 69 | + return success(true); | |
| 70 | + } | |
| 71 | + | |
| 72 | + @GetMapping("/list") | |
| 73 | + @Operation(summary = "获得凭证字管理列表") | |
| 74 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024") | |
| 75 | + @PreAuthorize("@ss.hasPermission('fms:config:voucher-word:query')") | |
| 76 | + public CommonResult<List<FmsVoucherWordRespVO>> getVoucherWordList( | |
| 77 | + @RequestParam("accountSetId") @NotNull Long accountSetId) { | |
| 78 | + List<FmsVoucherWordDO> list = voucherWordService.getVoucherWordList(accountSetId, getLoginUserId()); | |
| 79 | + return success(BeanUtils.toBean(list, FmsVoucherWordRespVO.class)); | |
| 80 | + } | |
| 81 | + | |
| 82 | + @GetMapping("/simple-list") | |
| 83 | + @Operation(summary = "获得凭证字精简列表") | |
| 84 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024") | |
| 85 | + public CommonResult<List<FmsVoucherWordRespVO>> getVoucherWordSimpleList( | |
| 86 | + @RequestParam("accountSetId") @NotNull Long accountSetId) { | |
| 87 | + return success(convertList(voucherWordService.getVoucherWordList( | |
| 88 | + accountSetId, getLoginUserId()), voucherWord -> new FmsVoucherWordRespVO() | |
| 89 | + .setId(voucherWord.getId()).setName(voucherWord.getName()) | |
| 90 | + .setDefaultStatus(voucherWord.getDefaultStatus()))); | |
| 91 | + } | |
| 92 | + | |
| 93 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/accountset/FmsAccountSetInitializeReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.accountset; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 4 | +import cn.iocoder.yudao.module.fms.enums.config.FmsAccountingStandardEnum; | |
| 5 | +import cn.iocoder.yudao.module.fms.enums.config.FmsCurrencyPresetEnum; | |
| 6 | +import cn.iocoder.yudao.module.fms.enums.ledger.FmsLedgerBalanceModeEnum; | |
| 7 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 8 | +import jakarta.validation.constraints.NotBlank; | |
| 9 | +import jakarta.validation.constraints.NotNull; | |
| 10 | +import jakarta.validation.constraints.Max; | |
| 11 | +import jakarta.validation.constraints.Min; | |
| 12 | +import jakarta.validation.constraints.Size; | |
| 13 | +import lombok.Data; | |
| 14 | + | |
| 15 | +import java.time.LocalDateTime; | |
| 16 | + | |
| 17 | +@Schema(description = "管理后台 - FMS 账套初始化 Request VO") | |
| 18 | +@Data | |
| 19 | +public class FmsAccountSetInitializeReqVO { | |
| 20 | + | |
| 21 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 22 | + @NotNull(message = "账套编号不能为空") | |
| 23 | + private Long accountSetId; | |
| 24 | + | |
| 25 | + @Schema(description = "本位币编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "RMB") | |
| 26 | + @NotBlank(message = "本位币不能为空") | |
| 27 | + @InEnum(value = FmsCurrencyPresetEnum.class, message = "本位币必须是 {value}") | |
| 28 | + private String currencyCode; | |
| 29 | + | |
| 30 | + @Schema(description = "启用期间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 31 | + @NotNull(message = "启用期间不能为空") | |
| 32 | + private LocalDateTime startTime; | |
| 33 | + | |
| 34 | + @Schema(description = "会计制度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 35 | + @NotNull(message = "会计制度不能为空") | |
| 36 | + @InEnum(value = FmsAccountingStandardEnum.class, message = "会计制度必须是 {value}") | |
| 37 | + private Integer standard; | |
| 38 | + | |
| 39 | + @Schema(description = "科目层级", requiredMode = Schema.RequiredMode.REQUIRED, example = "4") | |
| 40 | + @NotNull(message = "科目层级不能为空") | |
| 41 | + @Min(value = 1, message = "科目层级不能小于 1") | |
| 42 | + @Max(value = 8, message = "科目层级不能大于 8") | |
| 43 | + private Integer level; | |
| 44 | + | |
| 45 | + @Schema(description = "科目编码规则", requiredMode = Schema.RequiredMode.REQUIRED, example = "4-2-2-2") | |
| 46 | + @NotBlank(message = "科目编码规则不能为空") | |
| 47 | + @Size(max = 64, message = "科目编码规则不能超过 64 个字符") | |
| 48 | + private String subjectCodeRule; | |
| 49 | + | |
| 50 | + @Schema(description = "账簿余额方向模式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 51 | + @NotNull(message = "账簿余额方向模式不能为空") | |
| 52 | + @InEnum(FmsLedgerBalanceModeEnum.class) | |
| 53 | + private Integer ledgerBalanceMode; | |
| 54 | + | |
| 55 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/accountset/FmsAccountSetRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.accountset; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.time.LocalDateTime; | |
| 7 | + | |
| 8 | +@Schema(description = "管理后台 - FMS 账套 Response VO") | |
| 9 | +@Data | |
| 10 | +public class FmsAccountSetRespVO { | |
| 11 | + | |
| 12 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 13 | + private Long id; | |
| 14 | + | |
| 15 | + @Schema(description = "公司编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "WK001") | |
| 16 | + private String companyCode; | |
| 17 | + | |
| 18 | + @Schema(description = "公司名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试科技有限公司") | |
| 19 | + private String companyName; | |
| 20 | + | |
| 21 | + @Schema(description = "公司简介", example = "专注企业数字化服务") | |
| 22 | + private String companyProfile; | |
| 23 | + | |
| 24 | + @Schema(description = "所在行业", example = "软件和信息技术服务业") | |
| 25 | + private String industry; | |
| 26 | + | |
| 27 | + @Schema(description = "所在地", example = "上海市浦东新区") | |
| 28 | + private String location; | |
| 29 | + | |
| 30 | + @Schema(description = "法人代表", example = "张三") | |
| 31 | + private String legalRepresentative; | |
| 32 | + | |
| 33 | + @Schema(description = "法人身份证号", example = "310101199001011234") | |
| 34 | + private String legalRepresentativeIdNumber; | |
| 35 | + | |
| 36 | + @Schema(description = "营业执照号", example = "91310000MA1K123456") | |
| 37 | + private String businessLicenseNumber; | |
| 38 | + | |
| 39 | + @Schema(description = "组织机构代码", example = "MA1K12345") | |
| 40 | + private String organizationCode; | |
| 41 | + | |
| 42 | + @Schema(description = "备注", example = "一般纳税人") | |
| 43 | + private String remark; | |
| 44 | + | |
| 45 | + @Schema(description = "联系人", example = "李四") | |
| 46 | + private String contactName; | |
| 47 | + | |
| 48 | + @Schema(description = "办公电话", example = "021-12345678") | |
| 49 | + private String officeTelephone; | |
| 50 | + | |
| 51 | + @Schema(description = "手机号码", example = "15601691399") | |
| 52 | + private String mobile; | |
| 53 | + | |
| 54 | + @Schema(description = "传真号码", example = "021-87654321") | |
| 55 | + private String faxNumber; | |
| 56 | + | |
| 57 | + @Schema(description = "QQ 号码", example = "12345678") | |
| 58 | + private String qqNumber; | |
| 59 | + | |
| 60 | + @Schema(description = "邮箱", example = "finance@example.com") | |
| 61 | + private String email; | |
| 62 | + | |
| 63 | + @Schema(description = "其他联系方式", example = "企业微信:wk-finance") | |
| 64 | + private String otherContact; | |
| 65 | + | |
| 66 | + @Schema(description = "详细地址", example = "张江高科技园区 1 号楼") | |
| 67 | + private String address; | |
| 68 | + | |
| 69 | + @Schema(description = "币种编号", example = "1024") | |
| 70 | + private Long currencyId; | |
| 71 | + | |
| 72 | + @Schema(description = "启用期间") | |
| 73 | + private LocalDateTime startTime; | |
| 74 | + | |
| 75 | + @Schema(description = "会计制度", example = "1") | |
| 76 | + private Integer standard; | |
| 77 | + | |
| 78 | + @Schema(description = "是否已初始化", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") | |
| 79 | + private Boolean initialized; | |
| 80 | + | |
| 81 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 82 | + private LocalDateTime createTime; | |
| 83 | + | |
| 84 | + // ==================== 来自 FmsAccountUserDO ==================== | |
| 85 | + | |
| 86 | + @Schema(description = "是否默认账套", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") | |
| 87 | + private Boolean defaultStatus; | |
| 88 | + | |
| 89 | + @Schema(description = "是否账套创建人", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") | |
| 90 | + private Boolean founder; | |
| 91 | + | |
| 92 | + @Schema(description = "当前用户的成员权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "3") | |
| 93 | + private Integer level; | |
| 94 | + | |
| 95 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/accountset/FmsAccountSetSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.accountset; | |
| 2 | + | |
| 3 | +import com.mzt.logapi.starter.annotation.DiffLogField; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import jakarta.validation.constraints.Email; | |
| 6 | +import jakarta.validation.constraints.NotBlank; | |
| 7 | +import jakarta.validation.constraints.Size; | |
| 8 | +import lombok.Data; | |
| 9 | + | |
| 10 | +@Schema(description = "管理后台 - FMS 账套新增/修改 Request VO") | |
| 11 | +@Data | |
| 12 | +public class FmsAccountSetSaveReqVO { | |
| 13 | + | |
| 14 | + @Schema(description = "账套编号", example = "1024") | |
| 15 | + private Long id; | |
| 16 | + | |
| 17 | + @Schema(description = "公司编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "WK001") | |
| 18 | + @NotBlank(message = "公司编码不能为空") | |
| 19 | + @Size(max = 64, message = "公司编码不能超过 64 个字符") | |
| 20 | + @DiffLogField(name = "公司编码") | |
| 21 | + private String companyCode; | |
| 22 | + | |
| 23 | + @Schema(description = "公司名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试科技有限公司") | |
| 24 | + @NotBlank(message = "公司名称不能为空") | |
| 25 | + @Size(max = 255, message = "公司名称不能超过 255 个字符") | |
| 26 | + @DiffLogField(name = "公司名称") | |
| 27 | + private String companyName; | |
| 28 | + | |
| 29 | + @Schema(description = "公司简介", example = "专注企业数字化服务") | |
| 30 | + @Size(max = 500, message = "公司简介不能超过 500 个字符") | |
| 31 | + @DiffLogField(name = "公司简介") | |
| 32 | + private String companyProfile; | |
| 33 | + | |
| 34 | + @Schema(description = "所在行业", example = "软件和信息技术服务业") | |
| 35 | + @Size(max = 255, message = "所在行业不能超过 255 个字符") | |
| 36 | + @DiffLogField(name = "所在行业") | |
| 37 | + private String industry; | |
| 38 | + | |
| 39 | + @Schema(description = "所在地", example = "上海市浦东新区") | |
| 40 | + @Size(max = 255, message = "所在地不能超过 255 个字符") | |
| 41 | + @DiffLogField(name = "所在地") | |
| 42 | + private String location; | |
| 43 | + | |
| 44 | + @Schema(description = "法人代表", example = "张三") | |
| 45 | + @Size(max = 255, message = "法人代表不能超过 255 个字符") | |
| 46 | + @DiffLogField(name = "法人代表") | |
| 47 | + private String legalRepresentative; | |
| 48 | + | |
| 49 | + @Schema(description = "法人身份证号", example = "310101199001011234") | |
| 50 | + @Size(max = 255, message = "法人身份证号不能超过 255 个字符") | |
| 51 | + @DiffLogField(name = "法人身份证号") | |
| 52 | + private String legalRepresentativeIdNumber; | |
| 53 | + | |
| 54 | + @Schema(description = "营业执照号", example = "91310000MA1K123456") | |
| 55 | + @Size(max = 255, message = "营业执照号不能超过 255 个字符") | |
| 56 | + @DiffLogField(name = "营业执照号") | |
| 57 | + private String businessLicenseNumber; | |
| 58 | + | |
| 59 | + @Schema(description = "组织机构代码", example = "MA1K12345") | |
| 60 | + @Size(max = 255, message = "组织机构代码不能超过 255 个字符") | |
| 61 | + @DiffLogField(name = "组织机构代码") | |
| 62 | + private String organizationCode; | |
| 63 | + | |
| 64 | + @Schema(description = "备注", example = "一般纳税人") | |
| 65 | + @Size(max = 500, message = "备注不能超过 500 个字符") | |
| 66 | + @DiffLogField(name = "备注") | |
| 67 | + private String remark; | |
| 68 | + | |
| 69 | + @Schema(description = "联系人", example = "李四") | |
| 70 | + @Size(max = 255, message = "联系人不能超过 255 个字符") | |
| 71 | + @DiffLogField(name = "联系人") | |
| 72 | + private String contactName; | |
| 73 | + | |
| 74 | + @Schema(description = "办公电话", example = "021-12345678") | |
| 75 | + @Size(max = 32, message = "办公电话不能超过 32 个字符") | |
| 76 | + @DiffLogField(name = "办公电话") | |
| 77 | + private String officeTelephone; | |
| 78 | + | |
| 79 | + @Schema(description = "手机号码", example = "15601691399") | |
| 80 | + @Size(max = 32, message = "手机号码不能超过 32 个字符") | |
| 81 | + @DiffLogField(name = "手机号码") | |
| 82 | + private String mobile; | |
| 83 | + | |
| 84 | + @Schema(description = "传真号码", example = "021-87654321") | |
| 85 | + @Size(max = 32, message = "传真号码不能超过 32 个字符") | |
| 86 | + @DiffLogField(name = "传真号码") | |
| 87 | + private String faxNumber; | |
| 88 | + | |
| 89 | + @Schema(description = "QQ 号码", example = "12345678") | |
| 90 | + @Size(max = 255, message = "QQ 号码不能超过 255 个字符") | |
| 91 | + @DiffLogField(name = "QQ 号码") | |
| 92 | + private String qqNumber; | |
| 93 | + | |
| 94 | + @Schema(description = "邮箱", example = "finance@example.com") | |
| 95 | + @Email(message = "邮箱格式不正确") | |
| 96 | + @Size(max = 255, message = "邮箱不能超过 255 个字符") | |
| 97 | + @DiffLogField(name = "邮箱") | |
| 98 | + private String email; | |
| 99 | + | |
| 100 | + @Schema(description = "其他联系方式", example = "企业微信:wk-finance") | |
| 101 | + @Size(max = 255, message = "其他联系方式不能超过 255 个字符") | |
| 102 | + @DiffLogField(name = "其他联系方式") | |
| 103 | + private String otherContact; | |
| 104 | + | |
| 105 | + @Schema(description = "详细地址", example = "张江高科技园区 1 号楼") | |
| 106 | + @Size(max = 255, message = "详细地址不能超过 255 个字符") | |
| 107 | + @DiffLogField(name = "详细地址") | |
| 108 | + private String address; | |
| 109 | + | |
| 110 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/accountuser/FmsAccountUserRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.accountuser; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +@Schema(description = "管理后台 - FMS 账套用户 Response VO") | |
| 7 | +@Data | |
| 8 | +public class FmsAccountUserRespVO { | |
| 9 | + | |
| 10 | + @Schema(description = "后台用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 11 | + private Long userId; | |
| 12 | + | |
| 13 | + @Schema(description = "用户昵称", example = "芋道") | |
| 14 | + private String nickname; | |
| 15 | + | |
| 16 | + @Schema(description = "部门名称", example = "财务部") | |
| 17 | + private String deptName; | |
| 18 | + | |
| 19 | + @Schema(description = "手机号码", example = "15601691399") | |
| 20 | + private String mobile; | |
| 21 | + | |
| 22 | + @Schema(description = "用户邮箱", example = "finance@example.com") | |
| 23 | + private String email; | |
| 24 | + | |
| 25 | + @Schema(description = "用户状态", example = "0") | |
| 26 | + private Integer status; | |
| 27 | + | |
| 28 | + @Schema(description = "是否默认账套", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") | |
| 29 | + private Boolean defaultStatus; | |
| 30 | + | |
| 31 | + @Schema(description = "是否账套创建人", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") | |
| 32 | + private Boolean founder; | |
| 33 | + | |
| 34 | + @Schema(description = "成员权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | |
| 35 | + private Integer level; | |
| 36 | + | |
| 37 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/accountuser/FmsAccountUserUpdateReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.accountuser; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 4 | +import cn.iocoder.yudao.module.fms.enums.config.FmsAccountUserLevelEnum; | |
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 6 | +import jakarta.validation.Valid; | |
| 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 = "管理后台 - FMS 账套用户更新 Request VO") | |
| 14 | +@Data | |
| 15 | +public class FmsAccountUserUpdateReqVO { | |
| 16 | + | |
| 17 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 18 | + @NotNull(message = "账套编号不能为空") | |
| 19 | + private Long accountSetId; | |
| 20 | + | |
| 21 | + @Schema(description = "账套成员数组", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 22 | + @NotNull(message = "账套成员数组不能为空") | |
| 23 | + @Size(max = 100, message = "单个账套最多授权 100 名成员") | |
| 24 | + @Valid | |
| 25 | + private List<Member> members; | |
| 26 | + | |
| 27 | + @Schema(description = "管理后台 - FMS 账套成员") | |
| 28 | + @Data | |
| 29 | + public static class Member { | |
| 30 | + | |
| 31 | + @Schema(description = "后台用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 32 | + @NotNull(message = "成员用户编号不能为空") | |
| 33 | + private Long userId; | |
| 34 | + | |
| 35 | + @Schema(description = "成员权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | |
| 36 | + @NotNull(message = "成员权限级别不能为空") | |
| 37 | + @InEnum(FmsAccountUserLevelEnum.class) | |
| 38 | + private Integer level; | |
| 39 | + | |
| 40 | + } | |
| 41 | + | |
| 42 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/auxiliaryitem/FmsAuxiliaryItemImportExcelVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.auxiliaryitem; | |
| 2 | + | |
| 3 | +import cn.idev.excel.annotation.ExcelProperty; | |
| 4 | +import jakarta.validation.constraints.NotBlank; | |
| 5 | +import jakarta.validation.constraints.Size; | |
| 6 | +import lombok.Data; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * FMS 辅助核算项目 Excel 导入 VO | |
| 10 | + * | |
| 11 | + * @author 芋道源码 | |
| 12 | + */ | |
| 13 | +@Data | |
| 14 | +public class FmsAuxiliaryItemImportExcelVO { | |
| 15 | + | |
| 16 | + @ExcelProperty("编码") | |
| 17 | + @NotBlank(message = "编码不能为空") | |
| 18 | + @Size(max = 64, message = "编码长度不能超过 64 个字符") | |
| 19 | + private String code; | |
| 20 | + | |
| 21 | + @ExcelProperty("名称") | |
| 22 | + @NotBlank(message = "名称不能为空") | |
| 23 | + @Size(max = 255, message = "名称长度不能超过 255 个字符") | |
| 24 | + private String name; | |
| 25 | + | |
| 26 | + @ExcelProperty("备注") | |
| 27 | + @Size(max = 500, message = "备注长度不能超过 500 个字符") | |
| 28 | + private String remark; | |
| 29 | + | |
| 30 | + @ExcelProperty("规格") | |
| 31 | + @Size(max = 255, message = "规格长度不能超过 255 个字符") | |
| 32 | + private String specification; | |
| 33 | + | |
| 34 | + @ExcelProperty("单位") | |
| 35 | + @Size(max = 255, message = "单位长度不能超过 255 个字符") | |
| 36 | + private String unit; | |
| 37 | + | |
| 38 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/auxiliaryitem/FmsAuxiliaryItemImportRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.auxiliaryitem; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.util.List; | |
| 7 | +import java.util.Map; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * FMS 辅助核算项目导入结果 Response VO | |
| 11 | + * | |
| 12 | + * @author 芋道源码 | |
| 13 | + */ | |
| 14 | +@Schema(description = "管理后台 - FMS 辅助核算项目导入结果 Response VO") | |
| 15 | +@Data | |
| 16 | +public class FmsAuxiliaryItemImportRespVO { | |
| 17 | + | |
| 18 | + @Schema(description = "总数量", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 19 | + private Integer totalCount; | |
| 20 | + | |
| 21 | + @Schema(description = "成功编码数组", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 22 | + private List<String> successItemCodes; | |
| 23 | + | |
| 24 | + @Schema(description = "失败原因 Map", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 25 | + private Map<String, String> failureReasons; | |
| 26 | + | |
| 27 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/auxiliaryitem/FmsAuxiliaryItemPageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.auxiliaryitem; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import jakarta.validation.constraints.NotNull; | |
| 6 | +import lombok.Data; | |
| 7 | +import lombok.EqualsAndHashCode; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * FMS 辅助核算项目分页 Request VO | |
| 11 | + * | |
| 12 | + * @author 芋道源码 | |
| 13 | + */ | |
| 14 | +@Schema(description = "管理后台 - FMS 辅助核算项目分页 Request VO") | |
| 15 | +@Data | |
| 16 | +@EqualsAndHashCode(callSuper = true) | |
| 17 | +public class FmsAuxiliaryItemPageReqVO extends PageParam { | |
| 18 | + | |
| 19 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 20 | + @NotNull(message = "账套编号不能为空") | |
| 21 | + private Long accountSetId; | |
| 22 | + | |
| 23 | + @Schema(description = "辅助核算类别编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 24 | + @NotNull(message = "辅助核算类别编号不能为空") | |
| 25 | + private Long auxiliaryTypeId; | |
| 26 | + | |
| 27 | + @Schema(description = "关键词,匹配编码或名称", example = "测试") | |
| 28 | + private String search; | |
| 29 | + | |
| 30 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/auxiliaryitem/FmsAuxiliaryItemRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.auxiliaryitem; | |
| 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.time.LocalDateTime; | |
| 9 | + | |
| 10 | +/** | |
| 11 | + * FMS 辅助核算项目 Response VO | |
| 12 | + * | |
| 13 | + * @author 芋道源码 | |
| 14 | + */ | |
| 15 | +@Schema(description = "管理后台 - FMS 辅助核算项目 Response VO") | |
| 16 | +@Data | |
| 17 | +@ExcelIgnoreUnannotated | |
| 18 | +public class FmsAuxiliaryItemRespVO { | |
| 19 | + | |
| 20 | + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 21 | + private Long id; | |
| 22 | + | |
| 23 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 24 | + private Long accountSetId; | |
| 25 | + | |
| 26 | + @Schema(description = "辅助核算类别编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 27 | + private Long auxiliaryTypeId; | |
| 28 | + | |
| 29 | + @Schema(description = "编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "KH001") | |
| 30 | + @ExcelProperty("编码") | |
| 31 | + private String code; | |
| 32 | + | |
| 33 | + @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "上海测试数字科技有限公司") | |
| 34 | + @ExcelProperty("名称") | |
| 35 | + private String name; | |
| 36 | + | |
| 37 | + @Schema(description = "备注", example = "重点客户") | |
| 38 | + @ExcelProperty("备注") | |
| 39 | + private String remark; | |
| 40 | + | |
| 41 | + @Schema(description = "规格", example = "标准版") | |
| 42 | + @ExcelProperty("规格") | |
| 43 | + private String specification; | |
| 44 | + | |
| 45 | + @Schema(description = "单位", example = "台") | |
| 46 | + @ExcelProperty("单位") | |
| 47 | + private String unit; | |
| 48 | + | |
| 49 | + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 50 | + private Integer status; | |
| 51 | + | |
| 52 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 53 | + private LocalDateTime createTime; | |
| 54 | + | |
| 55 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/auxiliaryitem/FmsAuxiliaryItemSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.auxiliaryitem; | |
| 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 | +/** | |
| 10 | + * FMS 辅助核算项目创建或修改 Request VO | |
| 11 | + * | |
| 12 | + * @author 芋道源码 | |
| 13 | + */ | |
| 14 | +@Schema(description = "管理后台 - FMS 辅助核算项目创建或修改 Request VO") | |
| 15 | +@Data | |
| 16 | +public class FmsAuxiliaryItemSaveReqVO { | |
| 17 | + | |
| 18 | + @Schema(description = "编号", example = "1024") | |
| 19 | + private Long id; | |
| 20 | + | |
| 21 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 22 | + @NotNull(message = "账套编号不能为空") | |
| 23 | + private Long accountSetId; | |
| 24 | + | |
| 25 | + @Schema(description = "辅助核算类别编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 26 | + @NotNull(message = "辅助核算类别编号不能为空") | |
| 27 | + private Long auxiliaryTypeId; | |
| 28 | + | |
| 29 | + @Schema(description = "编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "KH001") | |
| 30 | + @NotBlank(message = "编码不能为空") | |
| 31 | + @Size(max = 64, message = "编码长度不能超过 64 个字符") | |
| 32 | + private String code; | |
| 33 | + | |
| 34 | + @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "上海测试数字科技有限公司") | |
| 35 | + @NotBlank(message = "名称不能为空") | |
| 36 | + @Size(max = 255, message = "名称长度不能超过 255 个字符") | |
| 37 | + private String name; | |
| 38 | + | |
| 39 | + @Schema(description = "备注", example = "重点客户") | |
| 40 | + @Size(max = 500, message = "备注长度不能超过 500 个字符") | |
| 41 | + private String remark; | |
| 42 | + | |
| 43 | + @Schema(description = "规格", example = "标准版") | |
| 44 | + @Size(max = 255, message = "规格长度不能超过 255 个字符") | |
| 45 | + private String specification; | |
| 46 | + | |
| 47 | + @Schema(description = "单位", example = "台") | |
| 48 | + @Size(max = 255, message = "单位长度不能超过 255 个字符") | |
| 49 | + private String unit; | |
| 50 | + | |
| 51 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/auxiliaryitem/FmsAuxiliaryItemStatusReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.auxiliaryitem; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 4 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | |
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 6 | +import jakarta.validation.constraints.NotNull; | |
| 7 | +import lombok.Data; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * FMS 辅助核算项目状态更新 Request VO | |
| 11 | + * | |
| 12 | + * @author 芋道源码 | |
| 13 | + */ | |
| 14 | +@Schema(description = "管理后台 - FMS 辅助核算项目状态更新 Request VO") | |
| 15 | +@Data | |
| 16 | +public class FmsAuxiliaryItemStatusReqVO { | |
| 17 | + | |
| 18 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 19 | + @NotNull(message = "账套编号不能为空") | |
| 20 | + private Long accountSetId; | |
| 21 | + | |
| 22 | + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 23 | + @NotNull(message = "编号不能为空") | |
| 24 | + private Long id; | |
| 25 | + | |
| 26 | + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") | |
| 27 | + @NotNull(message = "状态不能为空") | |
| 28 | + @InEnum(CommonStatusEnum.class) | |
| 29 | + private Integer status; | |
| 30 | + | |
| 31 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/auxiliarytype/FmsAuxiliaryTypeRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.auxiliarytype; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * FMS 辅助核算类别 Response VO | |
| 8 | + * | |
| 9 | + * @author 芋道源码 | |
| 10 | + */ | |
| 11 | +@Schema(description = "管理后台 - FMS 辅助核算类别 Response VO") | |
| 12 | +@Data | |
| 13 | +public class FmsAuxiliaryTypeRespVO { | |
| 14 | + | |
| 15 | + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 16 | + private Long id; | |
| 17 | + | |
| 18 | + @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "客户") | |
| 19 | + private String name; | |
| 20 | + | |
| 21 | + @Schema(description = "是否系统预置", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 22 | + private Boolean systemPreset; | |
| 23 | + | |
| 24 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 25 | + private Long accountSetId; | |
| 26 | + | |
| 27 | + @Schema(description = "类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 28 | + private Integer type; | |
| 29 | + | |
| 30 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/auxiliarytype/FmsAuxiliaryTypeSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.auxiliarytype; | |
| 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 | +/** | |
| 10 | + * FMS 辅助核算类别创建或修改 Request VO | |
| 11 | + * | |
| 12 | + * @author 芋道源码 | |
| 13 | + */ | |
| 14 | +@Schema(description = "管理后台 - FMS 辅助核算类别创建或修改 Request VO") | |
| 15 | +@Data | |
| 16 | +public class FmsAuxiliaryTypeSaveReqVO { | |
| 17 | + | |
| 18 | + @Schema(description = "编号", example = "1024") | |
| 19 | + private Long id; | |
| 20 | + | |
| 21 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 22 | + @NotNull(message = "账套编号不能为空") | |
| 23 | + private Long accountSetId; | |
| 24 | + | |
| 25 | + @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "门店") | |
| 26 | + @NotBlank(message = "名称不能为空") | |
| 27 | + @Size(max = 255, message = "名称长度不能超过 255 个字符") | |
| 28 | + private String name; | |
| 29 | + | |
| 30 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/currency/FmsCurrencyRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.currency; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.math.BigDecimal; | |
| 7 | +import java.time.LocalDateTime; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * FMS 币别 Response VO | |
| 11 | + * | |
| 12 | + * @author 芋道源码 | |
| 13 | + */ | |
| 14 | +@Schema(description = "管理后台 - FMS 币别 Response VO") | |
| 15 | +@Data | |
| 16 | +public class FmsCurrencyRespVO { | |
| 17 | + | |
| 18 | + @Schema(description = "币别编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 19 | + private Long id; | |
| 20 | + | |
| 21 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 22 | + private Long accountSetId; | |
| 23 | + | |
| 24 | + @Schema(description = "币别编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "USD") | |
| 25 | + private String code; | |
| 26 | + | |
| 27 | + @Schema(description = "币别名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "美元") | |
| 28 | + private String name; | |
| 29 | + | |
| 30 | + @Schema(description = "汇率", requiredMode = Schema.RequiredMode.REQUIRED, example = "7.120000") | |
| 31 | + private BigDecimal exchangeRate; | |
| 32 | + | |
| 33 | + @Schema(description = "是否本位币", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 34 | + private Boolean standard; | |
| 35 | + | |
| 36 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 37 | + private LocalDateTime createTime; | |
| 38 | + | |
| 39 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/currency/FmsCurrencySaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.currency; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import jakarta.validation.constraints.DecimalMin; | |
| 5 | +import jakarta.validation.constraints.Digits; | |
| 6 | +import jakarta.validation.constraints.NotBlank; | |
| 7 | +import jakarta.validation.constraints.NotNull; | |
| 8 | +import jakarta.validation.constraints.Pattern; | |
| 9 | +import jakarta.validation.constraints.Size; | |
| 10 | +import lombok.Data; | |
| 11 | + | |
| 12 | +import java.math.BigDecimal; | |
| 13 | + | |
| 14 | +/** | |
| 15 | + * FMS 币别保存 Request VO | |
| 16 | + * | |
| 17 | + * @author 芋道源码 | |
| 18 | + */ | |
| 19 | +@Schema(description = "管理后台 - FMS 币别保存 Request VO") | |
| 20 | +@Data | |
| 21 | +public class FmsCurrencySaveReqVO { | |
| 22 | + | |
| 23 | + @Schema(description = "币别编号", example = "1024") | |
| 24 | + private Long id; | |
| 25 | + | |
| 26 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 27 | + @NotNull(message = "账套不能为空") | |
| 28 | + private Long accountSetId; | |
| 29 | + | |
| 30 | + @Schema(description = "币别编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "USD") | |
| 31 | + @NotBlank(message = "币别编码不能为空") | |
| 32 | + @Size(max = 64, message = "币别编码长度不能超过 64 个字符") | |
| 33 | + @Pattern(regexp = "^[A-Za-z][A-Za-z0-9_]*$", message = "币别编码必须以字母开头,只能包含字母、数字和下划线") | |
| 34 | + private String code; | |
| 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 = "汇率", requiredMode = Schema.RequiredMode.REQUIRED, example = "7.120000") | |
| 42 | + @NotNull(message = "汇率不能为空") | |
| 43 | + @DecimalMin(value = "0", inclusive = false, message = "汇率必须大于 0") | |
| 44 | + @Digits(integer = 12, fraction = 6, message = "汇率最多 12 位整数和 6 位小数") | |
| 45 | + private BigDecimal exchangeRate; | |
| 46 | + | |
| 47 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/digest/FmsDigestRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.digest; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.time.LocalDateTime; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * FMS 常用摘要 Response VO | |
| 10 | + * | |
| 11 | + * @author 芋道源码 | |
| 12 | + */ | |
| 13 | +@Schema(description = "管理后台 - FMS 常用摘要 Response VO") | |
| 14 | +@Data | |
| 15 | +public class FmsDigestRespVO { | |
| 16 | + | |
| 17 | + @Schema(description = "常用摘要编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 18 | + private Long id; | |
| 19 | + | |
| 20 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 21 | + private Long accountSetId; | |
| 22 | + | |
| 23 | + @Schema(description = "摘要内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "购买办公用品") | |
| 24 | + private String content; | |
| 25 | + | |
| 26 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 27 | + private LocalDateTime createTime; | |
| 28 | + | |
| 29 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/digest/FmsDigestSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.digest; | |
| 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 | +/** | |
| 10 | + * FMS 常用摘要保存 Request VO | |
| 11 | + * | |
| 12 | + * @author 芋道源码 | |
| 13 | + */ | |
| 14 | +@Schema(description = "管理后台 - FMS 常用摘要保存 Request VO") | |
| 15 | +@Data | |
| 16 | +public class FmsDigestSaveReqVO { | |
| 17 | + | |
| 18 | + @Schema(description = "常用摘要编号", example = "1024") | |
| 19 | + private Long id; | |
| 20 | + | |
| 21 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 22 | + @NotNull(message = "账套编号不能为空") | |
| 23 | + private Long accountSetId; | |
| 24 | + | |
| 25 | + @Schema(description = "摘要内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "购买办公用品") | |
| 26 | + @NotBlank(message = "摘要内容不能为空") | |
| 27 | + @Size(max = 500, message = "摘要内容长度不能超过 500 个字符") | |
| 28 | + private String content; | |
| 29 | + | |
| 30 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/financeindicator/FmsFinanceIndicatorRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.financeindicator; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.time.LocalDateTime; | |
| 7 | + | |
| 8 | +// TODO DONE @AI:字段 Swagger 注解和空行已补齐。 | |
| 9 | +@Schema(description = "管理后台 - FMS 财务指标 Response VO") | |
| 10 | +@Data | |
| 11 | +public class FmsFinanceIndicatorRespVO { | |
| 12 | + | |
| 13 | + @Schema(description = "指标编号", example = "1024") | |
| 14 | + private Long id; | |
| 15 | + | |
| 16 | + @Schema(description = "账套编号", example = "1") | |
| 17 | + private Long accountSetId; | |
| 18 | + | |
| 19 | + @Schema(description = "指标名称", example = "现金") | |
| 20 | + private String name; | |
| 21 | + | |
| 22 | + @Schema(description = "指标编码", example = "cash") | |
| 23 | + private String code; | |
| 24 | + | |
| 25 | + @Schema(description = "取数报表类型", example = "1") | |
| 26 | + private Integer type; | |
| 27 | + | |
| 28 | + @Schema(description = "指标公式") | |
| 29 | + private String formula; | |
| 30 | + | |
| 31 | + @Schema(description = "展示顺序", example = "10") | |
| 32 | + private Integer sort; | |
| 33 | + | |
| 34 | + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") | |
| 35 | + private Integer status; | |
| 36 | + | |
| 37 | + @Schema(description = "创建时间") | |
| 38 | + private LocalDateTime createTime; | |
| 39 | + | |
| 40 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/financeindicator/FmsFinanceIndicatorSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.financeindicator; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | |
| 4 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 5 | +import cn.iocoder.yudao.module.fms.enums.config.FmsFinanceIndicatorTypeEnum; | |
| 6 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 7 | +import jakarta.validation.constraints.NotBlank; | |
| 8 | +import jakarta.validation.constraints.NotNull; | |
| 9 | +import jakarta.validation.constraints.Size; | |
| 10 | +import lombok.Data; | |
| 11 | + | |
| 12 | +// TODO DONE @AI:字段空行已对齐现有 SaveReqVO。 | |
| 13 | +@Schema(description = "管理后台 - FMS 财务指标保存 Request VO") | |
| 14 | +@Data | |
| 15 | +public class FmsFinanceIndicatorSaveReqVO { | |
| 16 | + | |
| 17 | + @Schema(description = "指标编号", example = "1024") | |
| 18 | + private Long id; | |
| 19 | + | |
| 20 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 21 | + @NotNull(message = "账套编号不能为空") | |
| 22 | + private Long accountSetId; | |
| 23 | + | |
| 24 | + @Schema(description = "指标名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "现金") | |
| 25 | + @NotBlank(message = "指标名称不能为空") | |
| 26 | + @Size(max = 100, message = "指标名称长度不能超过 100 个字符") | |
| 27 | + private String name; | |
| 28 | + | |
| 29 | + @Schema(description = "指标编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "cash") | |
| 30 | + @NotBlank(message = "指标编码不能为空") | |
| 31 | + @Size(max = 64, message = "指标编码长度不能超过 64 个字符") | |
| 32 | + private String code; | |
| 33 | + | |
| 34 | + @Schema(description = "取数报表类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | |
| 35 | + @NotNull(message = "取数报表类型不能为空") | |
| 36 | + @InEnum(FmsFinanceIndicatorTypeEnum.class) | |
| 37 | + private Integer type; | |
| 38 | + | |
| 39 | + @Schema(description = "指标公式,支持 L1+L2 或科目公式 JSON", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 40 | + @NotBlank(message = "指标公式不能为空") | |
| 41 | + @Size(max = 2000, message = "指标公式长度不能超过 2000 个字符") | |
| 42 | + private String formula; | |
| 43 | + | |
| 44 | + @Schema(description = "展示顺序", example = "10") | |
| 45 | + @NotNull(message = "展示顺序不能为空") | |
| 46 | + private Integer sort; | |
| 47 | + | |
| 48 | + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") | |
| 49 | + @NotNull(message = "状态不能为空") | |
| 50 | + @InEnum(CommonStatusEnum.class) | |
| 51 | + private Integer status; | |
| 52 | + | |
| 53 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/financeparameter/FmsFinanceParameterRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.financeparameter; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +@Schema(description = "管理后台 - FMS 财务参数 Response VO") | |
| 7 | +@Data | |
| 8 | +public class FmsFinanceParameterRespVO { | |
| 9 | + | |
| 10 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 11 | + private Long accountSetId; | |
| 12 | + | |
| 13 | + @Schema(description = "科目层级", requiredMode = Schema.RequiredMode.REQUIRED, example = "4") | |
| 14 | + private Integer level; | |
| 15 | + | |
| 16 | + @Schema(description = "科目编码规则", requiredMode = Schema.RequiredMode.REQUIRED, example = "4-2-2-2") | |
| 17 | + private String subjectCodeRule; | |
| 18 | + | |
| 19 | + @Schema(description = "账簿余额方向模式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 20 | + private Integer ledgerBalanceMode; | |
| 21 | + | |
| 22 | + @Schema(description = "结账前是否要求凭证审核", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") | |
| 23 | + private Boolean voucherReviewRequired; | |
| 24 | + | |
| 25 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/financeparameter/FmsFinanceParameterUpdateReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.financeparameter; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 4 | +import cn.iocoder.yudao.module.fms.enums.ledger.FmsLedgerBalanceModeEnum; | |
| 5 | +import cn.iocoder.yudao.module.fms.enums.config.FmsAccountingStandardEnum; | |
| 6 | +import com.mzt.logapi.starter.annotation.DiffLogField; | |
| 7 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 8 | +import jakarta.validation.constraints.Max; | |
| 9 | +import jakarta.validation.constraints.Min; | |
| 10 | +import jakarta.validation.constraints.NotBlank; | |
| 11 | +import jakarta.validation.constraints.NotNull; | |
| 12 | +import jakarta.validation.constraints.Size; | |
| 13 | +import lombok.Data; | |
| 14 | + | |
| 15 | +@Schema(description = "管理后台 - FMS 财务参数更新 Request VO") | |
| 16 | +@Data | |
| 17 | +public class FmsFinanceParameterUpdateReqVO { | |
| 18 | + | |
| 19 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 20 | + @NotNull(message = "账套编号不能为空") | |
| 21 | + private Long accountSetId; | |
| 22 | + | |
| 23 | + @Schema(description = "会计制度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 24 | + @NotNull(message = "会计制度不能为空") | |
| 25 | + @InEnum(FmsAccountingStandardEnum.class) | |
| 26 | + @DiffLogField(name = "会计制度") | |
| 27 | + private Integer standard; | |
| 28 | + | |
| 29 | + @Schema(description = "科目层级", requiredMode = Schema.RequiredMode.REQUIRED, example = "4") | |
| 30 | + @NotNull(message = "科目层级不能为空") | |
| 31 | + @Min(value = 1, message = "科目层级不能小于 1") | |
| 32 | + @Max(value = 8, message = "科目层级不能大于 8") | |
| 33 | + @DiffLogField(name = "科目层级") | |
| 34 | + private Integer level; | |
| 35 | + | |
| 36 | + @Schema(description = "科目编码规则", requiredMode = Schema.RequiredMode.REQUIRED, example = "4-2-2-2") | |
| 37 | + @NotBlank(message = "科目编码规则不能为空") | |
| 38 | + @Size(max = 64, message = "科目编码规则不能超过 64 个字符") | |
| 39 | + @DiffLogField(name = "科目编码规则") | |
| 40 | + private String subjectCodeRule; | |
| 41 | + | |
| 42 | + @Schema(description = "账簿余额方向模式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 43 | + @NotNull(message = "账簿余额方向模式不能为空") | |
| 44 | + @InEnum(FmsLedgerBalanceModeEnum.class) | |
| 45 | + @DiffLogField(name = "账簿余额方向") | |
| 46 | + private Integer ledgerBalanceMode; | |
| 47 | + | |
| 48 | + @Schema(description = "结账前是否要求凭证审核", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") | |
| 49 | + @NotNull(message = "结账前凭证审核设置不能为空") | |
| 50 | + @DiffLogField(name = "凭证审核后才允许结账") | |
| 51 | + private Boolean voucherReviewRequired; | |
| 52 | + | |
| 53 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/initialbalance/FmsInitialBalanceExcelVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.initialbalance; | |
| 2 | + | |
| 3 | +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; | |
| 4 | +import cn.idev.excel.annotation.ExcelProperty; | |
| 5 | +import lombok.Data; | |
| 6 | + | |
| 7 | +import java.math.BigDecimal; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * FMS 初始余额 Excel VO | |
| 11 | + * | |
| 12 | + * @author 芋道源码 | |
| 13 | + */ | |
| 14 | +@Data | |
| 15 | +@ExcelIgnoreUnannotated | |
| 16 | +public class FmsInitialBalanceExcelVO { | |
| 17 | + | |
| 18 | + /** | |
| 19 | + * Excel 行号 | |
| 20 | + */ | |
| 21 | + private Integer rowNumber; | |
| 22 | + /** | |
| 23 | + * 科目编码 | |
| 24 | + */ | |
| 25 | + @ExcelProperty("科目编码") | |
| 26 | + private String subjectCode; | |
| 27 | + /** | |
| 28 | + * 科目名称 | |
| 29 | + */ | |
| 30 | + @ExcelProperty("科目名称") | |
| 31 | + private String subjectName; | |
| 32 | + /** | |
| 33 | + * 余额方向名称 | |
| 34 | + */ | |
| 35 | + @ExcelProperty("方向") | |
| 36 | + private String directionName; | |
| 37 | + /** | |
| 38 | + * 辅助核算项目,格式为“类别:名称/类别:名称” | |
| 39 | + */ | |
| 40 | + @ExcelProperty("辅助核算项目") | |
| 41 | + private String auxiliaryItems; | |
| 42 | + /** | |
| 43 | + * 科目类型 | |
| 44 | + */ | |
| 45 | + private Integer subjectType; | |
| 46 | + /** | |
| 47 | + * 是否启用数量核算 | |
| 48 | + */ | |
| 49 | + private Boolean quantityAccounting; | |
| 50 | + /** | |
| 51 | + * 是否启用辅助核算 | |
| 52 | + */ | |
| 53 | + private Boolean auxiliaryAccounting; | |
| 54 | + /** | |
| 55 | + * 期初数量 | |
| 56 | + */ | |
| 57 | + @ExcelProperty("期初数量") | |
| 58 | + private BigDecimal openingQuantity; | |
| 59 | + /** | |
| 60 | + * 期初金额 | |
| 61 | + */ | |
| 62 | + @ExcelProperty("期初金额") | |
| 63 | + private BigDecimal openingAmount; | |
| 64 | + /** | |
| 65 | + * 本年累计借方数量 | |
| 66 | + */ | |
| 67 | + @ExcelProperty("本年累计借方数量") | |
| 68 | + private BigDecimal yearDebitQuantity; | |
| 69 | + /** | |
| 70 | + * 本年累计借方金额 | |
| 71 | + */ | |
| 72 | + @ExcelProperty("本年累计借方金额") | |
| 73 | + private BigDecimal yearDebitAmount; | |
| 74 | + /** | |
| 75 | + * 本年累计贷方数量 | |
| 76 | + */ | |
| 77 | + @ExcelProperty("本年累计贷方数量") | |
| 78 | + private BigDecimal yearCreditQuantity; | |
| 79 | + /** | |
| 80 | + * 本年累计贷方金额 | |
| 81 | + */ | |
| 82 | + @ExcelProperty("本年累计贷方金额") | |
| 83 | + private BigDecimal yearCreditAmount; | |
| 84 | + /** | |
| 85 | + * 年初数量 | |
| 86 | + */ | |
| 87 | + @ExcelProperty("年初数量") | |
| 88 | + private BigDecimal yearOpeningQuantity; | |
| 89 | + /** | |
| 90 | + * 年初金额 | |
| 91 | + */ | |
| 92 | + @ExcelProperty("年初金额") | |
| 93 | + private BigDecimal yearOpeningAmount; | |
| 94 | + /** | |
| 95 | + * 实际损益发生数量 | |
| 96 | + */ | |
| 97 | + @ExcelProperty("实际损益发生数量") | |
| 98 | + private BigDecimal profitLossQuantity; | |
| 99 | + /** | |
| 100 | + * 实际损益发生金额 | |
| 101 | + */ | |
| 102 | + @ExcelProperty("实际损益发生金额") | |
| 103 | + private BigDecimal profitLossAmount; | |
| 104 | + | |
| 105 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/initialbalance/FmsInitialBalanceRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.initialbalance; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.math.BigDecimal; | |
| 7 | +import java.util.List; | |
| 8 | + | |
| 9 | +@Schema(description = "管理后台 - FMS 初始余额 Response VO") | |
| 10 | +@Data | |
| 11 | +public class FmsInitialBalanceRespVO { | |
| 12 | + | |
| 13 | + @Schema(description = "初始余额编号", example = "1024") | |
| 14 | + private Long id; | |
| 15 | + | |
| 16 | + @Schema(description = "科目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 17 | + private Long subjectId; | |
| 18 | + | |
| 19 | + @Schema(description = "科目编码", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 20 | + private String subjectCode; | |
| 21 | + | |
| 22 | + @Schema(description = "科目名称", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 23 | + private String subjectName; | |
| 24 | + | |
| 25 | + @Schema(description = "上级科目编号", example = "1024") | |
| 26 | + private Long parentId; | |
| 27 | + | |
| 28 | + @Schema(description = "科目类型", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 29 | + private Integer type; | |
| 30 | + | |
| 31 | + @Schema(description = "余额方向", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 32 | + private Integer balanceDirection; | |
| 33 | + | |
| 34 | + @Schema(description = "是否启用数量核算", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 35 | + private Boolean quantityAccounting; | |
| 36 | + | |
| 37 | + @Schema(description = "数量单位") | |
| 38 | + private String quantityUnit; | |
| 39 | + | |
| 40 | + @Schema(description = "是否启用辅助核算", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 41 | + private Boolean auxiliaryAccounting; | |
| 42 | + | |
| 43 | + @Schema(description = "期初金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000.00") | |
| 44 | + private BigDecimal openingAmount; | |
| 45 | + | |
| 46 | + @Schema(description = "期初数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10.0000") | |
| 47 | + private BigDecimal openingQuantity; | |
| 48 | + | |
| 49 | + @Schema(description = "本年累计借方金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") | |
| 50 | + private BigDecimal yearDebitAmount; | |
| 51 | + | |
| 52 | + @Schema(description = "本年累计借方数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0000") | |
| 53 | + private BigDecimal yearDebitQuantity; | |
| 54 | + | |
| 55 | + @Schema(description = "本年累计贷方金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "50.00") | |
| 56 | + private BigDecimal yearCreditAmount; | |
| 57 | + | |
| 58 | + @Schema(description = "本年累计贷方数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0000") | |
| 59 | + private BigDecimal yearCreditQuantity; | |
| 60 | + | |
| 61 | + @Schema(description = "年初金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "950.00") | |
| 62 | + private BigDecimal yearOpeningAmount; | |
| 63 | + | |
| 64 | + @Schema(description = "年初数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10.0000") | |
| 65 | + private BigDecimal yearOpeningQuantity; | |
| 66 | + | |
| 67 | + @Schema(description = "实际损益发生额", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.00") | |
| 68 | + private BigDecimal profitLossAmount; | |
| 69 | + | |
| 70 | + @Schema(description = "实际损益发生数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0000") | |
| 71 | + private BigDecimal profitLossQuantity; | |
| 72 | + | |
| 73 | + @Schema(description = "辅助核算配置数组") | |
| 74 | + private List<AuxiliaryConfig> auxiliaryConfigs; | |
| 75 | + | |
| 76 | + @Schema(description = "辅助核算余额数组") | |
| 77 | + private List<AssistBalance> assistBalances; | |
| 78 | + | |
| 79 | + @Schema(description = "管理后台 - FMS 初始余额辅助核算配置") | |
| 80 | + @Data | |
| 81 | + public static class AuxiliaryConfig { | |
| 82 | + | |
| 83 | + @Schema(description = "辅助核算类别编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 84 | + private Long auxiliaryTypeId; | |
| 85 | + | |
| 86 | + @Schema(description = "辅助核算类型", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 87 | + private Integer type; | |
| 88 | + | |
| 89 | + @Schema(description = "辅助核算类别名称", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 90 | + private String name; | |
| 91 | + | |
| 92 | + } | |
| 93 | + | |
| 94 | + @Schema(description = "管理后台 - FMS 初始余额辅助核算余额") | |
| 95 | + @Data | |
| 96 | + public static class AssistBalance { | |
| 97 | + | |
| 98 | + @Schema(description = "辅助核算组合编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 99 | + private Long assistCombinationId; | |
| 100 | + | |
| 101 | + @Schema(description = "辅助核算项目数组", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 102 | + private List<AuxiliaryItem> auxiliaries; | |
| 103 | + | |
| 104 | + @Schema(description = "期初金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000.00") | |
| 105 | + private BigDecimal openingAmount; | |
| 106 | + | |
| 107 | + @Schema(description = "期初数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10.0000") | |
| 108 | + private BigDecimal openingQuantity; | |
| 109 | + | |
| 110 | + @Schema(description = "本年累计借方金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") | |
| 111 | + private BigDecimal yearDebitAmount; | |
| 112 | + | |
| 113 | + @Schema(description = "本年累计借方数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0000") | |
| 114 | + private BigDecimal yearDebitQuantity; | |
| 115 | + | |
| 116 | + @Schema(description = "本年累计贷方金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "50.00") | |
| 117 | + private BigDecimal yearCreditAmount; | |
| 118 | + | |
| 119 | + @Schema(description = "本年累计贷方数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0000") | |
| 120 | + private BigDecimal yearCreditQuantity; | |
| 121 | + | |
| 122 | + @Schema(description = "年初金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "950.00") | |
| 123 | + private BigDecimal yearOpeningAmount; | |
| 124 | + | |
| 125 | + @Schema(description = "年初数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10.0000") | |
| 126 | + private BigDecimal yearOpeningQuantity; | |
| 127 | + | |
| 128 | + @Schema(description = "实际损益发生额", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.00") | |
| 129 | + private BigDecimal profitLossAmount; | |
| 130 | + | |
| 131 | + @Schema(description = "实际损益发生数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0000") | |
| 132 | + private BigDecimal profitLossQuantity; | |
| 133 | + | |
| 134 | + } | |
| 135 | + | |
| 136 | + @Schema(description = "管理后台 - FMS 初始余额辅助核算项目") | |
| 137 | + @Data | |
| 138 | + public static class AuxiliaryItem { | |
| 139 | + | |
| 140 | + @Schema(description = "辅助核算类型", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 141 | + private Integer type; | |
| 142 | + | |
| 143 | + @Schema(description = "辅助核算类别编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 144 | + private Long typeId; | |
| 145 | + | |
| 146 | + @Schema(description = "辅助核算项目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 147 | + private Long itemId; | |
| 148 | + | |
| 149 | + @Schema(description = "辅助核算项目名称", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 150 | + private String name; | |
| 151 | + | |
| 152 | + } | |
| 153 | + | |
| 154 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/initialbalance/FmsInitialBalanceSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.initialbalance; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import jakarta.validation.Valid; | |
| 5 | +import jakarta.validation.constraints.DecimalMin; | |
| 6 | +import jakarta.validation.constraints.NotEmpty; | |
| 7 | +import jakarta.validation.constraints.NotNull; | |
| 8 | +import lombok.Data; | |
| 9 | + | |
| 10 | +import java.math.BigDecimal; | |
| 11 | +import java.util.List; | |
| 12 | + | |
| 13 | +@Schema(description = "管理后台 - FMS 初始余额保存 Request VO") | |
| 14 | +@Data | |
| 15 | +public class FmsInitialBalanceSaveReqVO { | |
| 16 | + | |
| 17 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 18 | + @NotNull(message = "账套编号不能为空") | |
| 19 | + private Long accountSetId; | |
| 20 | + | |
| 21 | + @Schema(description = "科目余额数组", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 22 | + @Valid | |
| 23 | + @NotEmpty(message = "科目余额不能为空") | |
| 24 | + private List<Balance> balances; | |
| 25 | + | |
| 26 | + @Data | |
| 27 | + public static class Balance extends Amounts { | |
| 28 | + | |
| 29 | + @Schema(description = "科目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 30 | + @NotNull(message = "科目编号不能为空") | |
| 31 | + private Long subjectId; | |
| 32 | + | |
| 33 | + @Schema(description = "辅助核算余额数组") | |
| 34 | + @Valid | |
| 35 | + private List<AssistBalance> assistBalances; | |
| 36 | + | |
| 37 | + } | |
| 38 | + | |
| 39 | + @Data | |
| 40 | + public static class AssistBalance extends Amounts { | |
| 41 | + | |
| 42 | + @Schema(description = "辅助核算项目编号数组", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 43 | + @NotEmpty(message = "辅助核算项目不能为空") | |
| 44 | + private List<Long> auxiliaryItemIds; | |
| 45 | + } | |
| 46 | + | |
| 47 | + @Data | |
| 48 | + public static class Amounts { | |
| 49 | + | |
| 50 | + @Schema(description = "期初金额", example = "1000.00") | |
| 51 | + @DecimalMin(value = "0", message = "期初金额不能小于 0") | |
| 52 | + private BigDecimal openingAmount; | |
| 53 | + | |
| 54 | + @Schema(description = "期初数量", example = "10.0000") | |
| 55 | + @DecimalMin(value = "0", message = "期初数量不能小于 0") | |
| 56 | + private BigDecimal openingQuantity; | |
| 57 | + | |
| 58 | + @Schema(description = "本年累计借方金额", example = "100.00") | |
| 59 | + @DecimalMin(value = "0", message = "本年累计借方金额不能小于 0") | |
| 60 | + private BigDecimal yearDebitAmount; | |
| 61 | + | |
| 62 | + @Schema(description = "本年累计借方数量", example = "1.0000") | |
| 63 | + @DecimalMin(value = "0", message = "本年累计借方数量不能小于 0") | |
| 64 | + private BigDecimal yearDebitQuantity; | |
| 65 | + | |
| 66 | + @Schema(description = "本年累计贷方金额", example = "50.00") | |
| 67 | + @DecimalMin(value = "0", message = "本年累计贷方金额不能小于 0") | |
| 68 | + private BigDecimal yearCreditAmount; | |
| 69 | + | |
| 70 | + @Schema(description = "本年累计贷方数量", example = "1.0000") | |
| 71 | + @DecimalMin(value = "0", message = "本年累计贷方数量不能小于 0") | |
| 72 | + private BigDecimal yearCreditQuantity; | |
| 73 | + | |
| 74 | + @Schema(description = "年初金额", example = "950.00") | |
| 75 | + @DecimalMin(value = "0", message = "年初金额不能小于 0") | |
| 76 | + private BigDecimal yearOpeningAmount; | |
| 77 | + | |
| 78 | + @Schema(description = "年初数量", example = "10.0000") | |
| 79 | + @DecimalMin(value = "0", message = "年初数量不能小于 0") | |
| 80 | + private BigDecimal yearOpeningQuantity; | |
| 81 | + | |
| 82 | + @Schema(description = "实际损益发生额", example = "0.00") | |
| 83 | + @DecimalMin(value = "0", message = "实际损益发生额不能小于 0") | |
| 84 | + private BigDecimal profitLossAmount; | |
| 85 | + | |
| 86 | + @Schema(description = "实际损益发生数量", example = "1.0000") | |
| 87 | + @DecimalMin(value = "0", message = "实际损益发生数量不能小于 0") | |
| 88 | + private BigDecimal profitLossQuantity; | |
| 89 | + } | |
| 90 | + | |
| 91 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/initialbalance/FmsTrialBalanceRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.initialbalance; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.math.BigDecimal; | |
| 7 | + | |
| 8 | +@Schema(description = "管理后台 - FMS 试算平衡 Response VO") | |
| 9 | +@Data | |
| 10 | +public class FmsTrialBalanceRespVO { | |
| 11 | + | |
| 12 | + @Schema(description = "期初借方金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000.00") | |
| 13 | + private BigDecimal openingDebitAmount; | |
| 14 | + | |
| 15 | + @Schema(description = "期初贷方金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000.00") | |
| 16 | + private BigDecimal openingCreditAmount; | |
| 17 | + | |
| 18 | + @Schema(description = "期初差额", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.00") | |
| 19 | + private BigDecimal openingDifferenceAmount; | |
| 20 | + | |
| 21 | + @Schema(description = "本年累计借方金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") | |
| 22 | + private BigDecimal yearDebitAmount; | |
| 23 | + | |
| 24 | + @Schema(description = "本年累计贷方金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") | |
| 25 | + private BigDecimal yearCreditAmount; | |
| 26 | + | |
| 27 | + @Schema(description = "本年累计差额", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.00") | |
| 28 | + private BigDecimal yearDifferenceAmount; | |
| 29 | + | |
| 30 | + @Schema(description = "是否平衡", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 31 | + private Boolean balanced; | |
| 32 | + | |
| 33 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/subject/FmsSubjectDeleteReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.subject; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import jakarta.validation.constraints.NotEmpty; | |
| 5 | +import jakarta.validation.constraints.NotNull; | |
| 6 | +import lombok.Data; | |
| 7 | + | |
| 8 | +import java.util.List; | |
| 9 | + | |
| 10 | +/** | |
| 11 | + * FMS 科目删除 Request VO | |
| 12 | + * | |
| 13 | + * @author 芋道源码 | |
| 14 | + */ | |
| 15 | +@Schema(description = "管理后台 - FMS 科目删除 Request VO") | |
| 16 | +@Data | |
| 17 | +public class FmsSubjectDeleteReqVO { | |
| 18 | + | |
| 19 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 20 | + @NotNull(message = "账套编号不能为空") | |
| 21 | + private Long accountSetId; | |
| 22 | + | |
| 23 | + @Schema(description = "科目编号数组", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 24 | + @NotEmpty(message = "科目编号数组不能为空") | |
| 25 | + private List<Long> ids; | |
| 26 | + | |
| 27 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/subject/FmsSubjectImportExcelVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.subject; | |
| 2 | + | |
| 3 | +import cn.idev.excel.annotation.ExcelProperty; | |
| 4 | +import jakarta.validation.constraints.NotBlank; | |
| 5 | +import lombok.AllArgsConstructor; | |
| 6 | +import lombok.Builder; | |
| 7 | +import lombok.Data; | |
| 8 | +import lombok.NoArgsConstructor; | |
| 9 | + | |
| 10 | +/** | |
| 11 | + * FMS 科目 Excel 导入 VO | |
| 12 | + * | |
| 13 | + * @author 芋道源码 | |
| 14 | + */ | |
| 15 | +@Data | |
| 16 | +@Builder | |
| 17 | +@AllArgsConstructor | |
| 18 | +@NoArgsConstructor | |
| 19 | +public class FmsSubjectImportExcelVO { | |
| 20 | + | |
| 21 | + @ExcelProperty("科目编码") | |
| 22 | + @NotBlank(message = "科目编码不能为空") | |
| 23 | + private String code; | |
| 24 | + | |
| 25 | + @ExcelProperty("科目名称") | |
| 26 | + @NotBlank(message = "科目名称不能为空") | |
| 27 | + private String name; | |
| 28 | + | |
| 29 | + @ExcelProperty("上级科目编码") | |
| 30 | + private String parentSubjectCode; | |
| 31 | + | |
| 32 | + @ExcelProperty("余额方向") | |
| 33 | + @NotBlank(message = "余额方向不能为空") | |
| 34 | + private String balanceDirection; | |
| 35 | + | |
| 36 | + @ExcelProperty("类别") | |
| 37 | + @NotBlank(message = "类别不能为空") | |
| 38 | + private String categoryName; | |
| 39 | + | |
| 40 | + @ExcelProperty("辅助核算") | |
| 41 | + private String auxiliaryNames; | |
| 42 | + | |
| 43 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/subject/FmsSubjectImportRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.subject; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.util.List; | |
| 7 | +import java.util.Map; | |
| 8 | + | |
| 9 | +@Schema(description = "管理后台 - FMS 科目导入结果 Response VO") | |
| 10 | +@Data | |
| 11 | +public class FmsSubjectImportRespVO { | |
| 12 | + | |
| 13 | + @Schema(description = "总数量", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 14 | + private Integer totalCount; | |
| 15 | + | |
| 16 | + @Schema(description = "成功科目编码数组", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 17 | + private List<String> successSubjectCodes; | |
| 18 | + | |
| 19 | + @Schema(description = "失败原因 Map", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 20 | + private Map<String, String> failureReasons; | |
| 21 | + | |
| 22 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/subject/FmsSubjectListReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.subject; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 4 | +import cn.iocoder.yudao.module.fms.enums.config.FmsSubjectTypeEnum; | |
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 6 | +import jakarta.validation.constraints.NotNull; | |
| 7 | +import lombok.Data; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * FMS 科目列表 Request VO | |
| 11 | + * | |
| 12 | + * @author 芋道源码 | |
| 13 | + */ | |
| 14 | +@Schema(description = "管理后台 - FMS 科目列表 Request VO") | |
| 15 | +@Data | |
| 16 | +public class FmsSubjectListReqVO { | |
| 17 | + | |
| 18 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 19 | + @NotNull(message = "账套编号不能为空") | |
| 20 | + private Long accountSetId; | |
| 21 | + | |
| 22 | + @Schema(description = "科目类型", example = "1") | |
| 23 | + @InEnum(FmsSubjectTypeEnum.class) | |
| 24 | + private Integer type; | |
| 25 | + | |
| 26 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/subject/FmsSubjectRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.subject; | |
| 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.framework.excel.core.convert.StringListConvert; | |
| 6 | +import cn.iocoder.yudao.module.fms.enums.DictTypeConstants; | |
| 7 | +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; | |
| 8 | +import cn.idev.excel.annotation.ExcelProperty; | |
| 9 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
| 10 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 11 | +import lombok.Data; | |
| 12 | + | |
| 13 | +import java.time.LocalDateTime; | |
| 14 | +import java.util.List; | |
| 15 | + | |
| 16 | +import static cn.iocoder.yudao.module.infra.enums.DictTypeConstants.BOOLEAN_STRING; | |
| 17 | + | |
| 18 | +/** | |
| 19 | + * FMS 科目 Response VO | |
| 20 | + * | |
| 21 | + * @author 芋道源码 | |
| 22 | + */ | |
| 23 | +@Schema(description = "管理后台 - FMS 科目 Response VO") | |
| 24 | +@Data | |
| 25 | +@ExcelIgnoreUnannotated | |
| 26 | +public class FmsSubjectRespVO { | |
| 27 | + | |
| 28 | + @Schema(description = "科目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 29 | + private Long id; | |
| 30 | + | |
| 31 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 32 | + private Long accountSetId; | |
| 33 | + | |
| 34 | + @Schema(description = "科目编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1001") | |
| 35 | + @ExcelProperty(value = "编码", index = 0) | |
| 36 | + private String code; | |
| 37 | + | |
| 38 | + @Schema(description = "科目名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "库存现金") | |
| 39 | + @ExcelProperty(value = "名称", index = 1) | |
| 40 | + private String name; | |
| 41 | + | |
| 42 | + @Schema(description = "上级科目编号", example = "1024") | |
| 43 | + private Long parentId; | |
| 44 | + | |
| 45 | + @Schema(description = "科目类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 46 | + private Integer type; | |
| 47 | + | |
| 48 | + @Schema(description = "科目类别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 49 | + private Integer category; | |
| 50 | + | |
| 51 | + @JsonIgnore // 仅 excel 导出使用 | |
| 52 | + @ExcelProperty(value = "类别", index = 2, converter = DictConvert.class) | |
| 53 | + @DictFormat(DictTypeConstants.SUBJECT_CATEGORY) | |
| 54 | + private String categoryDictValue; | |
| 55 | + | |
| 56 | + @Schema(description = "余额方向", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 57 | + @ExcelProperty(value = "余额方向", index = 3, converter = DictConvert.class) | |
| 58 | + @DictFormat(DictTypeConstants.DEBIT_CREDIT_DIRECTION) | |
| 59 | + private Integer balanceDirection; | |
| 60 | + | |
| 61 | + @Schema(description = "辅助核算类别编号数组") | |
| 62 | + private List<Long> auxiliaryTypeIds; | |
| 63 | + | |
| 64 | + @Schema(description = "外币核算币别编号数组") | |
| 65 | + private List<Long> currencyIds; | |
| 66 | + | |
| 67 | + @Schema(description = "是否启用数量核算", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 68 | + private Boolean quantityAccounting; | |
| 69 | + | |
| 70 | + @Schema(description = "数量单位", example = "件") | |
| 71 | + @ExcelProperty(value = "数量", index = 5) | |
| 72 | + private String quantityUnit; | |
| 73 | + | |
| 74 | + @Schema(description = "是否现金及现金等价物", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 75 | + @ExcelProperty(value = "是否现金项", index = 6, converter = DictConvert.class) | |
| 76 | + @DictFormat(BOOLEAN_STRING) | |
| 77 | + private Boolean cash; | |
| 78 | + | |
| 79 | + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 80 | + private Integer status; | |
| 81 | + | |
| 82 | + @Schema(description = "层级", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 83 | + private Integer level; | |
| 84 | + | |
| 85 | + @Schema(description = "辅助核算类别名称数组") | |
| 86 | + @ExcelProperty(value = "辅助核算", index = 4, converter = StringListConvert.class) | |
| 87 | + private List<String> auxiliaryTypeNames; | |
| 88 | + | |
| 89 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 90 | + private LocalDateTime createTime; | |
| 91 | + | |
| 92 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/subject/FmsSubjectSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.subject; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.StrUtil; | |
| 4 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 5 | +import cn.iocoder.yudao.module.fms.enums.common.FmsDebitCreditDirectionEnum; | |
| 6 | +import cn.iocoder.yudao.module.fms.enums.config.FmsSubjectCategoryEnum; | |
| 7 | +import cn.iocoder.yudao.module.fms.enums.config.FmsSubjectTypeEnum; | |
| 8 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 9 | +import jakarta.validation.constraints.AssertTrue; | |
| 10 | +import jakarta.validation.constraints.NotBlank; | |
| 11 | +import jakarta.validation.constraints.NotNull; | |
| 12 | +import jakarta.validation.constraints.Size; | |
| 13 | +import lombok.Data; | |
| 14 | + | |
| 15 | +import java.util.List; | |
| 16 | + | |
| 17 | +/** | |
| 18 | + * FMS 科目保存 Request VO | |
| 19 | + * | |
| 20 | + * @author 芋道源码 | |
| 21 | + */ | |
| 22 | +@Schema(description = "管理后台 - FMS 科目保存 Request VO") | |
| 23 | +@Data | |
| 24 | +public class FmsSubjectSaveReqVO { | |
| 25 | + | |
| 26 | + @Schema(description = "科目编号", example = "1024") | |
| 27 | + private Long id; | |
| 28 | + | |
| 29 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 30 | + @NotNull(message = "账套编号不能为空") | |
| 31 | + private Long accountSetId; | |
| 32 | + | |
| 33 | + @Schema(description = "科目编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1001") | |
| 34 | + @NotBlank(message = "科目编码不能为空") | |
| 35 | + @Size(max = 64, message = "科目编码长度不能超过 64 个字符") | |
| 36 | + private String code; | |
| 37 | + | |
| 38 | + @Schema(description = "科目名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "库存现金") | |
| 39 | + @NotBlank(message = "科目名称不能为空") | |
| 40 | + @Size(max = 255, message = "科目名称长度不能超过 255 个字符") | |
| 41 | + private String name; | |
| 42 | + | |
| 43 | + @Schema(description = "上级科目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 44 | + @NotNull(message = "上级科目编号不能为空") | |
| 45 | + private Long parentId; | |
| 46 | + | |
| 47 | + @Schema(description = "科目类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 48 | + @NotNull(message = "科目类型不能为空") | |
| 49 | + @InEnum(FmsSubjectTypeEnum.class) | |
| 50 | + private Integer type; | |
| 51 | + | |
| 52 | + @Schema(description = "科目类别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 53 | + @NotNull(message = "科目类别不能为空") | |
| 54 | + private Integer category; | |
| 55 | + | |
| 56 | + @Schema(description = "余额方向", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 57 | + @NotNull(message = "余额方向不能为空") | |
| 58 | + @InEnum(FmsDebitCreditDirectionEnum.class) | |
| 59 | + private Integer balanceDirection; | |
| 60 | + | |
| 61 | + @Schema(description = "辅助核算类别编号数组") | |
| 62 | + @NotNull(message = "辅助核算类别编号数组不能为空") | |
| 63 | + private List<Long> auxiliaryTypeIds; | |
| 64 | + | |
| 65 | + @Schema(description = "外币核算币别编号数组") | |
| 66 | + @NotNull(message = "外币核算币别编号数组不能为空") | |
| 67 | + private List<Long> currencyIds; | |
| 68 | + | |
| 69 | + @Schema(description = "是否启用数量核算", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 70 | + @NotNull(message = "是否启用数量核算不能为空") | |
| 71 | + private Boolean quantityAccounting; | |
| 72 | + | |
| 73 | + @Schema(description = "数量单位", example = "件") | |
| 74 | + @Size(max = 255, message = "数量单位长度不能超过 255 个字符") | |
| 75 | + private String quantityUnit; | |
| 76 | + | |
| 77 | + @Schema(description = "是否现金及现金等价物", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 78 | + @NotNull(message = "是否现金及现金等价物不能为空") | |
| 79 | + private Boolean cash; | |
| 80 | + | |
| 81 | + @Schema(description = "是否迁移上级科目历史数据") | |
| 82 | + private Boolean migrateParentData; | |
| 83 | + | |
| 84 | + @Schema(description = "辅助核算历史数据迁移项目数组") | |
| 85 | + private List<AuxiliaryMapping> auxiliaryMappings; | |
| 86 | + | |
| 87 | + @AssertTrue(message = "科目类别与科目类型不匹配") | |
| 88 | + public boolean isCategoryValid() { | |
| 89 | + return type == null || category == null || FmsSubjectCategoryEnum.valueOf(type, category) != null; | |
| 90 | + } | |
| 91 | + | |
| 92 | + @AssertTrue(message = "启用数量核算时数量单位不能为空") | |
| 93 | + public boolean isQuantityUnitValid() { | |
| 94 | + return !Boolean.TRUE.equals(quantityAccounting) || StrUtil.isNotBlank(quantityUnit); | |
| 95 | + } | |
| 96 | + | |
| 97 | + /** | |
| 98 | + * 辅助核算历史数据迁移项目 | |
| 99 | + */ | |
| 100 | + @Data | |
| 101 | + public static class AuxiliaryMapping { | |
| 102 | + | |
| 103 | + @Schema(description = "辅助核算类别编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 104 | + @NotNull(message = "辅助核算类别编号不能为空") | |
| 105 | + private Long typeId; | |
| 106 | + | |
| 107 | + @Schema(description = "辅助核算项目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 108 | + @NotNull(message = "辅助核算项目编号不能为空") | |
| 109 | + private Long itemId; | |
| 110 | + | |
| 111 | + } | |
| 112 | + | |
| 113 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/subject/FmsSubjectStatusReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.subject; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 4 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | |
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 6 | +import jakarta.validation.constraints.NotEmpty; | |
| 7 | +import jakarta.validation.constraints.NotNull; | |
| 8 | +import lombok.Data; | |
| 9 | + | |
| 10 | +import java.util.List; | |
| 11 | + | |
| 12 | +/** | |
| 13 | + * FMS 科目状态更新 Request VO | |
| 14 | + * | |
| 15 | + * @author 芋道源码 | |
| 16 | + */ | |
| 17 | +@Schema(description = "管理后台 - FMS 科目状态更新 Request VO") | |
| 18 | +@Data | |
| 19 | +public class FmsSubjectStatusReqVO { | |
| 20 | + | |
| 21 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 22 | + @NotNull(message = "账套编号不能为空") | |
| 23 | + private Long accountSetId; | |
| 24 | + | |
| 25 | + @Schema(description = "科目编号数组", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 26 | + @NotEmpty(message = "科目编号数组不能为空") | |
| 27 | + private List<Long> ids; | |
| 28 | + | |
| 29 | + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 30 | + @NotNull(message = "状态不能为空") | |
| 31 | + @InEnum(CommonStatusEnum.class) | |
| 32 | + private Integer status; | |
| 33 | + | |
| 34 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/subject/FmsSubjectUsageRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.subject; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * FMS 科目使用情况 Response VO | |
| 8 | + * | |
| 9 | + * @author 芋道源码 | |
| 10 | + */ | |
| 11 | +@Schema(description = "管理后台 - FMS 科目使用情况 Response VO") | |
| 12 | +@Data | |
| 13 | +public class FmsSubjectUsageRespVO { | |
| 14 | + | |
| 15 | + @Schema(description = "下级科目数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | |
| 16 | + private Long childCount; | |
| 17 | + | |
| 18 | + @Schema(description = "凭证分录数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") | |
| 19 | + private Long voucherEntryCount; | |
| 20 | + | |
| 21 | + @Schema(description = "初始余额数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 22 | + private Long initialBalanceCount; | |
| 23 | + | |
| 24 | + @Schema(description = "辅助核算组合数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "3") | |
| 25 | + private Long auxiliaryCombinationCount; | |
| 26 | + | |
| 27 | + @Schema(description = "包含数量数据的记录数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 28 | + private Long quantityDataCount; | |
| 29 | + | |
| 30 | + @Schema(description = "是否已被业务使用", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 31 | + private Boolean used; | |
| 32 | + | |
| 33 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/vouchertemplate/FmsVoucherTemplateEntryVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.vouchertemplate; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import jakarta.validation.Valid; | |
| 5 | +import jakarta.validation.constraints.DecimalMin; | |
| 6 | +import jakarta.validation.constraints.Digits; | |
| 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.math.BigDecimal; | |
| 13 | +import java.util.List; | |
| 14 | + | |
| 15 | +@Schema(description = "管理后台 - FMS 凭证模板分录 VO") | |
| 16 | +@Data | |
| 17 | +public class FmsVoucherTemplateEntryVO { | |
| 18 | + | |
| 19 | + @Schema(description = "摘要内容", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 20 | + @NotBlank(message = "摘要不能为空") | |
| 21 | + @Size(max = 500, message = "摘要长度不能超过 500 个字符") | |
| 22 | + private String digest; | |
| 23 | + | |
| 24 | + @Schema(description = "科目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 25 | + @NotNull(message = "科目不能为空") | |
| 26 | + private Long subjectId; | |
| 27 | + | |
| 28 | + @Schema(description = "数量") | |
| 29 | + @DecimalMin(value = "0", message = "数量不能小于 0") | |
| 30 | + @Digits(integer = 14, fraction = 4, message = "数量最多保留 4 位小数") | |
| 31 | + private BigDecimal quantity; | |
| 32 | + | |
| 33 | + @Schema(description = "单价") | |
| 34 | + @DecimalMin(value = "0", message = "单价不能小于 0") | |
| 35 | + @Digits(integer = 12, fraction = 6, message = "单价最多保留 6 位小数") | |
| 36 | + private BigDecimal unitPrice; | |
| 37 | + | |
| 38 | + @Schema(description = "借方金额") | |
| 39 | + @Digits(integer = 16, fraction = 2, message = "借方金额最多保留 2 位小数") | |
| 40 | + private BigDecimal debitAmount; | |
| 41 | + | |
| 42 | + @Schema(description = "贷方金额") | |
| 43 | + @Digits(integer = 16, fraction = 2, message = "贷方金额最多保留 2 位小数") | |
| 44 | + private BigDecimal creditAmount; | |
| 45 | + | |
| 46 | + @Schema(description = "辅助核算项目数组") | |
| 47 | + @Valid | |
| 48 | + private List<AuxiliaryItem> auxiliaries; | |
| 49 | + | |
| 50 | + @Schema(description = "管理后台 - FMS 凭证模板分录辅助核算项目") | |
| 51 | + @Data | |
| 52 | + public static class AuxiliaryItem { | |
| 53 | + | |
| 54 | + @Schema(description = "辅助核算类型") | |
| 55 | + private Integer type; | |
| 56 | + | |
| 57 | + @Schema(description = "辅助核算类别编号", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 58 | + @NotNull(message = "辅助核算类别不能为空") | |
| 59 | + private Long typeId; | |
| 60 | + | |
| 61 | + @Schema(description = "辅助核算项目编号", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 62 | + @NotNull(message = "辅助核算项目不能为空") | |
| 63 | + private Long itemId; | |
| 64 | + | |
| 65 | + @Schema(description = "辅助核算项目名称") | |
| 66 | + private String name; | |
| 67 | + } | |
| 68 | + | |
| 69 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/vouchertemplate/FmsVoucherTemplateRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.vouchertemplate; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.util.List; | |
| 7 | + | |
| 8 | +@Schema(description = "管理后台 - FMS 凭证模板 Response VO") | |
| 9 | +@Data | |
| 10 | +public class FmsVoucherTemplateRespVO { | |
| 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 accountSetId; | |
| 17 | + | |
| 18 | + @Schema(description = "模板名称", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 19 | + private String name; | |
| 20 | + | |
| 21 | + @Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 22 | + private Long categoryId; | |
| 23 | + | |
| 24 | + @Schema(description = "分类名称", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 25 | + private String categoryName; | |
| 26 | + | |
| 27 | + @Schema(description = "凭证模板分录数组", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 28 | + private List<FmsVoucherTemplateEntryVO> entries; | |
| 29 | + | |
| 30 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/vouchertemplate/FmsVoucherTemplateSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.vouchertemplate; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import jakarta.validation.Valid; | |
| 5 | +import jakarta.validation.constraints.NotBlank; | |
| 6 | +import jakarta.validation.constraints.NotEmpty; | |
| 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 = "管理后台 - FMS 凭证模板保存 Request VO") | |
| 14 | +@Data | |
| 15 | +public class FmsVoucherTemplateSaveReqVO { | |
| 16 | + | |
| 17 | + @Schema(description = "模板编号", example = "1024") | |
| 18 | + private Long id; | |
| 19 | + | |
| 20 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 21 | + @NotNull(message = "账套编号不能为空") | |
| 22 | + private Long accountSetId; | |
| 23 | + | |
| 24 | + @Schema(description = "模板名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "办公用品采购") | |
| 25 | + @NotBlank(message = "模板名称不能为空") | |
| 26 | + @Size(max = 255, message = "模板名称长度不能超过 255 个字符") | |
| 27 | + private String name; | |
| 28 | + | |
| 29 | + @Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 30 | + @NotNull(message = "模板分类不能为空") | |
| 31 | + private Long categoryId; | |
| 32 | + | |
| 33 | + @Schema(description = "凭证模板分录数组", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 34 | + @Valid | |
| 35 | + @NotEmpty(message = "凭证模板分录不能为空") | |
| 36 | + @Size(min = 2, message = "凭证模板至少需要两条分录") | |
| 37 | + private List<FmsVoucherTemplateEntryVO> entries; | |
| 38 | + | |
| 39 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/vouchertemplatecategory/FmsVoucherTemplateCategoryRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.vouchertemplatecategory; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +@Schema(description = "管理后台 - FMS 凭证模板分类 Response VO") | |
| 7 | +@Data | |
| 8 | +public class FmsVoucherTemplateCategoryRespVO { | |
| 9 | + | |
| 10 | + @Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 11 | + private Long id; | |
| 12 | + | |
| 13 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 14 | + private Long accountSetId; | |
| 15 | + | |
| 16 | + @Schema(description = "分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "日常收支") | |
| 17 | + private String name; | |
| 18 | + | |
| 19 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/vouchertemplatecategory/FmsVoucherTemplateCategorySaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.vouchertemplatecategory; | |
| 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 = "管理后台 - FMS 凭证模板分类保存 Request VO") | |
| 10 | +@Data | |
| 11 | +public class FmsVoucherTemplateCategorySaveReqVO { | |
| 12 | + | |
| 13 | + @Schema(description = "分类编号", example = "1024") | |
| 14 | + private Long id; | |
| 15 | + | |
| 16 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 17 | + @NotNull(message = "账套编号不能为空") | |
| 18 | + private Long accountSetId; | |
| 19 | + | |
| 20 | + @Schema(description = "分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "日常收支") | |
| 21 | + @NotBlank(message = "分类名称不能为空") | |
| 22 | + @Size(max = 255, message = "分类名称长度不能超过 255 个字符") | |
| 23 | + private String name; | |
| 24 | + | |
| 25 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/voucherword/FmsVoucherWordRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.voucherword; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.time.LocalDateTime; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * FMS 凭证字 Response VO | |
| 10 | + * | |
| 11 | + * @author 芋道源码 | |
| 12 | + */ | |
| 13 | +@Schema(description = "管理后台 - FMS 凭证字 Response VO") | |
| 14 | +@Data | |
| 15 | +public class FmsVoucherWordRespVO { | |
| 16 | + | |
| 17 | + @Schema(description = "凭证字编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 18 | + private Long id; | |
| 19 | + | |
| 20 | + @Schema(description = "凭证字", requiredMode = Schema.RequiredMode.REQUIRED, example = "记") | |
| 21 | + private String name; | |
| 22 | + | |
| 23 | + @Schema(description = "打印标题", example = "记账凭证") | |
| 24 | + private String printTitle; | |
| 25 | + | |
| 26 | + @Schema(description = "是否默认凭证字", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 27 | + private Boolean defaultStatus; | |
| 28 | + | |
| 29 | + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 30 | + private Integer sort; | |
| 31 | + | |
| 32 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 33 | + private Long accountSetId; | |
| 34 | + | |
| 35 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 36 | + private LocalDateTime createTime; | |
| 37 | + | |
| 38 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/config/vo/voucherword/FmsVoucherWordSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.config.vo.voucherword; | |
| 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 | +/** | |
| 10 | + * FMS 凭证字保存 Request VO | |
| 11 | + * | |
| 12 | + * @author 芋道源码 | |
| 13 | + */ | |
| 14 | +@Schema(description = "管理后台 - FMS 凭证字保存 Request VO") | |
| 15 | +@Data | |
| 16 | +public class FmsVoucherWordSaveReqVO { | |
| 17 | + | |
| 18 | + @Schema(description = "凭证字编号", example = "1024") | |
| 19 | + private Long id; | |
| 20 | + | |
| 21 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 22 | + @NotNull(message = "账套编号不能为空") | |
| 23 | + private Long accountSetId; | |
| 24 | + | |
| 25 | + @Schema(description = "凭证字", requiredMode = Schema.RequiredMode.REQUIRED, example = "记") | |
| 26 | + @NotBlank(message = "凭证字不能为空") | |
| 27 | + @Size(max = 255, message = "凭证字长度不能超过 255 个字符") | |
| 28 | + private String name; | |
| 29 | + | |
| 30 | + @Schema(description = "打印标题", example = "记账凭证") | |
| 31 | + @Size(max = 255, message = "打印标题长度不能超过 255 个字符") | |
| 32 | + private String printTitle; | |
| 33 | + | |
| 34 | + @Schema(description = "是否默认凭证字", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 35 | + @NotNull(message = "是否默认凭证字不能为空") | |
| 36 | + private Boolean defaultStatus; | |
| 37 | + | |
| 38 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/home/FmsHomeController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.home; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.module.fms.controller.admin.home.vo.FmsHomeMetricDetailRespVO; | |
| 5 | +import cn.iocoder.yudao.module.fms.controller.admin.home.vo.FmsHomeRespVO; | |
| 6 | +import cn.iocoder.yudao.module.fms.service.home.FmsHomeService; | |
| 7 | +import io.swagger.v3.oas.annotations.Operation; | |
| 8 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 9 | +import io.swagger.v3.oas.annotations.Parameters; | |
| 10 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 11 | +import jakarta.annotation.Resource; | |
| 12 | +import jakarta.validation.constraints.NotBlank; | |
| 13 | +import jakarta.validation.constraints.NotNull; | |
| 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.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 = "管理后台 - FMS 首页") | |
| 25 | +@RestController | |
| 26 | +@RequestMapping("/fms/home") | |
| 27 | +@Validated | |
| 28 | +public class FmsHomeController { | |
| 29 | + | |
| 30 | + @Resource | |
| 31 | + private FmsHomeService homeService; | |
| 32 | + | |
| 33 | + @GetMapping("/get") | |
| 34 | + @Operation(summary = "获得首页") | |
| 35 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024") | |
| 36 | + @PreAuthorize("@ss.hasPermission('fms:home:query')") | |
| 37 | + public CommonResult<FmsHomeRespVO> getHome(@RequestParam("accountSetId") @NotNull Long accountSetId) { | |
| 38 | + return success(homeService.getHome(accountSetId, getLoginUserId())); | |
| 39 | + } | |
| 40 | + | |
| 41 | + @GetMapping("/metric-detail") | |
| 42 | + @Operation(summary = "获得财务指标明细") | |
| 43 | + @Parameters({ | |
| 44 | + @Parameter(name = "accountSetId", description = "账套编号", required = true, example = "1024"), | |
| 45 | + @Parameter(name = "metricKey", description = "财务指标标识", required = true, example = "income") | |
| 46 | + }) | |
| 47 | + @PreAuthorize("@ss.hasPermission('fms:home:query')") | |
| 48 | + public CommonResult<FmsHomeMetricDetailRespVO> getMetricDetail( | |
| 49 | + @RequestParam("accountSetId") @NotNull Long accountSetId, | |
| 50 | + @RequestParam("metricKey") @NotBlank String metricKey) { | |
| 51 | + return success(homeService.getMetricDetail(accountSetId, metricKey, getLoginUserId())); | |
| 52 | + } | |
| 53 | + | |
| 54 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/home/vo/FmsHomeMetricDetailRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.home.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.math.BigDecimal; | |
| 7 | +import java.util.List; | |
| 8 | + | |
| 9 | +@Schema(description = "管理后台 - FMS 首页财务指标明细 Response VO") | |
| 10 | +@Data | |
| 11 | +public class FmsHomeMetricDetailRespVO { | |
| 12 | + | |
| 13 | + @Schema(description = "指标标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "income") | |
| 14 | + private String key; | |
| 15 | + | |
| 16 | + @Schema(description = "指标名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "收入") | |
| 17 | + private String name; | |
| 18 | + | |
| 19 | + @Schema(description = "财务指标趋势数组", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 20 | + private List<Trend> trends; | |
| 21 | + | |
| 22 | + @Schema(description = "当期科目构成数组", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 23 | + private List<Structure> structure; | |
| 24 | + | |
| 25 | + @Schema(description = "管理后台 - FMS 首页财务指标趋势 Response VO") | |
| 26 | + @Data | |
| 27 | + public static class Trend { | |
| 28 | + | |
| 29 | + @Schema(description = "会计期间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-08") | |
| 30 | + private String month; | |
| 31 | + | |
| 32 | + @Schema(description = "指标金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000.00") | |
| 33 | + private BigDecimal amount; | |
| 34 | + | |
| 35 | + } | |
| 36 | + | |
| 37 | + @Schema(description = "管理后台 - FMS 首页财务指标科目构成 Response VO") | |
| 38 | + @Data | |
| 39 | + public static class Structure { | |
| 40 | + | |
| 41 | + @Schema(description = "科目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 42 | + private Long subjectId; | |
| 43 | + | |
| 44 | + @Schema(description = "科目编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "5001") | |
| 45 | + private String subjectCode; | |
| 46 | + | |
| 47 | + @Schema(description = "科目名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "主营业务收入") | |
| 48 | + private String subjectName; | |
| 49 | + | |
| 50 | + @Schema(description = "科目金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "8000.00") | |
| 51 | + private BigDecimal amount; | |
| 52 | + | |
| 53 | + } | |
| 54 | + | |
| 55 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/home/vo/FmsHomeRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.home.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.math.BigDecimal; | |
| 7 | +import java.util.List; | |
| 8 | + | |
| 9 | +@Schema(description = "管理后台 - FMS 首页 Response VO") | |
| 10 | +@Data | |
| 11 | +public class FmsHomeRespVO { | |
| 12 | + | |
| 13 | + @Schema(description = "当前会计期间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-08") | |
| 14 | + private String currentMonth; | |
| 15 | + | |
| 16 | + @Schema(description = "当期财务指标数组", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 17 | + private List<Metric> metrics; | |
| 18 | + | |
| 19 | + @Schema(description = "财务指标趋势数组", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 20 | + private List<Trend> trends; | |
| 21 | + | |
| 22 | + @Schema(description = "管理后台 - FMS 首页财务指标 Response VO") | |
| 23 | + @Data | |
| 24 | + public static class Metric { | |
| 25 | + | |
| 26 | + @Schema(description = "指标标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "income") | |
| 27 | + private String key; | |
| 28 | + | |
| 29 | + @Schema(description = "指标名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "收入") | |
| 30 | + private String name; | |
| 31 | + | |
| 32 | + @Schema(description = "指标金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000.00") | |
| 33 | + private BigDecimal amount; | |
| 34 | + | |
| 35 | + } | |
| 36 | + | |
| 37 | + @Schema(description = "管理后台 - FMS 首页财务指标趋势 Response VO") | |
| 38 | + @Data | |
| 39 | + public static class Trend { | |
| 40 | + | |
| 41 | + @Schema(description = "会计期间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-08") | |
| 42 | + private String month; | |
| 43 | + | |
| 44 | + @Schema(description = "动态财务指标数组", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 45 | + private List<Metric> metrics; | |
| 46 | + | |
| 47 | + @Schema(description = "收入", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 48 | + private BigDecimal income; | |
| 49 | + | |
| 50 | + @Schema(description = "成本", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 51 | + private BigDecimal operatingCost; | |
| 52 | + | |
| 53 | + @Schema(description = "利润", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 54 | + private BigDecimal profit; | |
| 55 | + | |
| 56 | + @Schema(description = "费用", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 57 | + private BigDecimal expense; | |
| 58 | + | |
| 59 | + @Schema(description = "其他", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 60 | + private BigDecimal other; | |
| 61 | + | |
| 62 | + } | |
| 63 | + | |
| 64 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/ledger/FmsLedgerController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.ledger; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; | |
| 5 | +import cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum; | |
| 6 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 7 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 8 | +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; | |
| 9 | +import cn.iocoder.yudao.module.fms.controller.admin.config.vo.subject.FmsSubjectRespVO; | |
| 10 | +import cn.iocoder.yudao.module.fms.controller.admin.ledger.vo.FmsLedgerAuxiliaryListReqVO; | |
| 11 | +import cn.iocoder.yudao.module.fms.controller.admin.ledger.vo.FmsLedgerListReqVO; | |
| 12 | +import cn.iocoder.yudao.module.fms.controller.admin.ledger.vo.auxiliarybalance.FmsLedgerAuxiliaryBalanceRespVO; | |
| 13 | +import cn.iocoder.yudao.module.fms.controller.admin.ledger.vo.auxiliarydetail.FmsLedgerAuxiliaryDetailRespVO; | |
| 14 | +import cn.iocoder.yudao.module.fms.controller.admin.ledger.vo.detail.FmsLedgerDetailExportRespVO; | |
| 15 | +import cn.iocoder.yudao.module.fms.controller.admin.ledger.vo.detail.FmsLedgerDetailRespVO; | |
| 16 | +import cn.iocoder.yudao.module.fms.controller.admin.ledger.vo.general.FmsLedgerGeneralRespVO; | |
| 17 | +import cn.iocoder.yudao.module.fms.controller.admin.ledger.vo.multicolumn.FmsLedgerMultiColumnRespVO; | |
| 18 | +import cn.iocoder.yudao.module.fms.controller.admin.ledger.vo.quantitydetail.FmsLedgerQuantityDetailRespVO; | |
| 19 | +import cn.iocoder.yudao.module.fms.controller.admin.ledger.vo.quantitygeneral.FmsLedgerQuantityGeneralRespVO; | |
| 20 | +import cn.iocoder.yudao.module.fms.controller.admin.ledger.vo.subjectbalance.FmsLedgerSubjectBalanceRespVO; | |
| 21 | +import cn.iocoder.yudao.module.fms.service.ledger.FmsLedgerService; | |
| 22 | +import io.swagger.v3.oas.annotations.Operation; | |
| 23 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 24 | +import jakarta.annotation.Resource; | |
| 25 | +import jakarta.servlet.http.HttpServletResponse; | |
| 26 | +import jakarta.validation.Valid; | |
| 27 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 28 | +import org.springframework.validation.annotation.Validated; | |
| 29 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 30 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 31 | +import org.springframework.web.bind.annotation.RestController; | |
| 32 | + | |
| 33 | +import java.io.IOException; | |
| 34 | +import java.util.ArrayList; | |
| 35 | +import java.util.List; | |
| 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.security.core.util.SecurityFrameworkUtils.getLoginUserId; | |
| 40 | + | |
| 41 | +@Tag(name = "管理后台 - FMS 账簿") | |
| 42 | +@RestController | |
| 43 | +@RequestMapping("/fms/ledger") | |
| 44 | +@Validated | |
| 45 | +public class FmsLedgerController { | |
| 46 | + | |
| 47 | + @Resource | |
| 48 | + private FmsLedgerService ledgerService; | |
| 49 | + | |
| 50 | + // ==================== 明细账 ==================== | |
| 51 | + | |
| 52 | + @GetMapping("/detail/list") | |
| 53 | + @Operation(summary = "获得明细账") | |
| 54 | + @PreAuthorize("@ss.hasPermission('fms:ledger:detail:query')") | |
| 55 | + public CommonResult<List<FmsLedgerDetailRespVO>> getDetailList(@Valid FmsLedgerListReqVO listReqVO) { | |
| 56 | + return success(ledgerService.getDetailList(listReqVO, getLoginUserId())); | |
| 57 | + } | |
| 58 | + | |
| 59 | + @GetMapping("/detail/subject-list") | |
| 60 | + @Operation(summary = "获得指定期间有发生额的明细账科目") | |
| 61 | + public CommonResult<List<FmsSubjectRespVO>> getDetailSubjectList(@Valid FmsLedgerListReqVO listReqVO) { | |
| 62 | + // TODO @AI:先查询出来,list 变量;然后在去 convert;字段不多,倒是可以考虑 beanutils 去 copy 下? | |
| 63 | + return success(convertList(ledgerService.getDetailSubjectList(listReqVO, getLoginUserId()), subject -> | |
| 64 | + new FmsSubjectRespVO().setId(subject.getId()).setParentId(subject.getParentId()) | |
| 65 | + .setCode(subject.getCode()).setName(subject.getName()).setType(subject.getType()) | |
| 66 | + .setBalanceDirection(subject.getBalanceDirection()).setStatus(subject.getStatus()) | |
| 67 | + .setLevel(subject.getLevel()).setAuxiliaryTypeIds(subject.getAuxiliaryTypeIds()) | |
| 68 | + .setQuantityAccounting(subject.getQuantityAccounting()).setQuantityUnit(subject.getQuantityUnit()))); | |
| 69 | + } | |
| 70 | + | |
| 71 | + @GetMapping("/detail/export-excel") | |
| 72 | + @Operation(summary = "导出明细账") | |
| 73 | + @PreAuthorize("@ss.hasPermission('fms:ledger:detail:export')") | |
| 74 | + @ApiAccessLog(operateType = OperateTypeEnum.EXPORT) | |
| 75 | + public void exportDetail(@Valid FmsLedgerListReqVO listReqVO, HttpServletResponse response) throws IOException { | |
| 76 | + List<FmsLedgerDetailExportRespVO> rows = convertList( | |
| 77 | + ledgerService.getDetailList(listReqVO, getLoginUserId()), item -> | |
| 78 | + BeanUtils.toBean(item, FmsLedgerDetailExportRespVO.class) | |
| 79 | + .setSubject(item.getSubjectCode() + " " + item.getSubjectName())); | |
| 80 | + ExcelUtils.write(response, "明细账.xls", "明细账", FmsLedgerDetailExportRespVO.class, rows); | |
| 81 | + } | |
| 82 | + | |
| 83 | + // ==================== 总账 ==================== | |
| 84 | + | |
| 85 | + @GetMapping("/general/list") | |
| 86 | + @Operation(summary = "获得总账") | |
| 87 | + @PreAuthorize("@ss.hasPermission('fms:ledger:general:query')") | |
| 88 | + public CommonResult<List<FmsLedgerGeneralRespVO>> getGeneralList(@Valid FmsLedgerListReqVO listReqVO) { | |
| 89 | + return success(ledgerService.getGeneralList(listReqVO, getLoginUserId())); | |
| 90 | + } | |
| 91 | + | |
| 92 | + @GetMapping("/general/export-excel") | |
| 93 | + @Operation(summary = "导出总账") | |
| 94 | + @PreAuthorize("@ss.hasPermission('fms:ledger:general:export')") | |
| 95 | + @ApiAccessLog(operateType = OperateTypeEnum.EXPORT) | |
| 96 | + public void exportGeneral(@Valid FmsLedgerListReqVO listReqVO, HttpServletResponse response) throws IOException { | |
| 97 | + ExcelUtils.write(response, "总账.xls", "总账", FmsLedgerGeneralRespVO.class, | |
| 98 | + ledgerService.getGeneralList(listReqVO, getLoginUserId())); | |
| 99 | + } | |
| 100 | + | |
| 101 | + // ==================== 科目余额表 ==================== | |
| 102 | + | |
| 103 | + @GetMapping("/subject-balance/list") | |
| 104 | + @Operation(summary = "获得科目余额表") | |
| 105 | + @PreAuthorize("@ss.hasPermission('fms:ledger:subject-balance:query')") | |
| 106 | + public CommonResult<List<FmsLedgerSubjectBalanceRespVO>> getSubjectBalanceList(@Valid FmsLedgerListReqVO listReqVO) { | |
| 107 | + return success(ledgerService.getSubjectBalanceList(listReqVO, getLoginUserId())); | |
| 108 | + } | |
| 109 | + | |
| 110 | + @GetMapping("/subject-balance/export-excel") | |
| 111 | + @Operation(summary = "导出科目余额表") | |
| 112 | + @PreAuthorize("@ss.hasPermission('fms:ledger:subject-balance:export')") | |
| 113 | + @ApiAccessLog(operateType = OperateTypeEnum.EXPORT) | |
| 114 | + public void exportSubjectBalance(@Valid FmsLedgerListReqVO listReqVO, HttpServletResponse response) throws IOException { | |
| 115 | + ExcelUtils.write(response, "科目余额表.xls", "科目余额表", FmsLedgerSubjectBalanceRespVO.class, | |
| 116 | + flattenSubjectBalanceList(ledgerService.getSubjectBalanceList(listReqVO, getLoginUserId()))); | |
| 117 | + } | |
| 118 | + | |
| 119 | + // ==================== 多栏账 ==================== | |
| 120 | + | |
| 121 | + @GetMapping("/multi-column/list") | |
| 122 | + @Operation(summary = "获得多栏账") | |
| 123 | + @PreAuthorize("@ss.hasPermission('fms:ledger:multi-column:query')") | |
| 124 | + public CommonResult<FmsLedgerMultiColumnRespVO> getMultiColumn(@Valid FmsLedgerListReqVO listReqVO) { | |
| 125 | + return success(ledgerService.getMultiColumn(listReqVO, getLoginUserId())); | |
| 126 | + } | |
| 127 | + | |
| 128 | + @GetMapping("/multi-column/export-excel") | |
| 129 | + @Operation(summary = "导出多栏账") | |
| 130 | + @PreAuthorize("@ss.hasPermission('fms:ledger:multi-column:export')") | |
| 131 | + @ApiAccessLog(operateType = OperateTypeEnum.EXPORT) | |
| 132 | + public void exportMultiColumn(@Valid FmsLedgerListReqVO listReqVO, HttpServletResponse response) throws IOException { | |
| 133 | + FmsLedgerMultiColumnRespVO result = ledgerService.getMultiColumn(listReqVO, getLoginUserId()); | |
| 134 | + ExcelUtils.write(response, "多栏账.xls", "多栏账", | |
| 135 | + FmsLedgerMultiColumnExcelHelper.buildHead(result), | |
| 136 | + FmsLedgerMultiColumnExcelHelper.buildData(result)); | |
| 137 | + } | |
| 138 | + | |
| 139 | + // ==================== 核算项目明细账 ==================== | |
| 140 | + | |
| 141 | + @GetMapping("/auxiliary-detail/list") | |
| 142 | + @Operation(summary = "获得核算项目明细账") | |
| 143 | + @PreAuthorize("@ss.hasPermission('fms:ledger:detail:query')") | |
| 144 | + public CommonResult<List<FmsLedgerAuxiliaryDetailRespVO>> getAuxiliaryDetailList(@Valid FmsLedgerAuxiliaryListReqVO listReqVO) { | |
| 145 | + return success(ledgerService.getAuxiliaryDetailList(listReqVO, getLoginUserId())); | |
| 146 | + } | |
| 147 | + | |
| 148 | + @GetMapping("/auxiliary-detail/export-excel") | |
| 149 | + @Operation(summary = "导出核算项目明细账") | |
| 150 | + @PreAuthorize("@ss.hasPermission('fms:ledger:detail:export')") | |
| 151 | + @ApiAccessLog(operateType = OperateTypeEnum.EXPORT) | |
| 152 | + public void exportAuxiliaryDetail(@Valid FmsLedgerAuxiliaryListReqVO listReqVO, HttpServletResponse response) throws IOException { | |
| 153 | + ExcelUtils.write(response, "核算项目明细账.xls", "核算项目明细账", | |
| 154 | + FmsLedgerAuxiliaryDetailRespVO.class, | |
| 155 | + ledgerService.getAuxiliaryDetailList(listReqVO, getLoginUserId())); | |
| 156 | + } | |
| 157 | + | |
| 158 | + // ==================== 核算项目余额表 ==================== | |
| 159 | + | |
| 160 | + @GetMapping("/auxiliary-balance/list") | |
| 161 | + @Operation(summary = "获得核算项目余额表") | |
| 162 | + @PreAuthorize("@ss.hasPermission('fms:ledger:subject-balance:query')") | |
| 163 | + public CommonResult<List<FmsLedgerAuxiliaryBalanceRespVO>> getAuxiliaryBalanceList(@Valid FmsLedgerAuxiliaryListReqVO listReqVO) { | |
| 164 | + return success(ledgerService.getAuxiliaryBalanceList(listReqVO, getLoginUserId())); | |
| 165 | + } | |
| 166 | + | |
| 167 | + @GetMapping("/auxiliary-balance/export-excel") | |
| 168 | + @Operation(summary = "导出核算项目余额表") | |
| 169 | + @PreAuthorize("@ss.hasPermission('fms:ledger:subject-balance:export')") | |
| 170 | + @ApiAccessLog(operateType = OperateTypeEnum.EXPORT) | |
| 171 | + public void exportAuxiliaryBalance(@Valid FmsLedgerAuxiliaryListReqVO listReqVO, HttpServletResponse response) throws IOException { | |
| 172 | + ExcelUtils.write(response, "核算项目余额表.xls", "核算项目余额表", | |
| 173 | + FmsLedgerAuxiliaryBalanceRespVO.class, | |
| 174 | + ledgerService.getAuxiliaryBalanceList(listReqVO, getLoginUserId())); | |
| 175 | + } | |
| 176 | + | |
| 177 | + // ==================== 数量金额明细账 ==================== | |
| 178 | + | |
| 179 | + @GetMapping("/quantity-detail/list") | |
| 180 | + @Operation(summary = "获得数量金额明细账") | |
| 181 | + @PreAuthorize("@ss.hasPermission('fms:ledger:detail:query')") | |
| 182 | + public CommonResult<List<FmsLedgerQuantityDetailRespVO>> getQuantityDetailList(@Valid FmsLedgerListReqVO listReqVO) { | |
| 183 | + return success(ledgerService.getQuantityDetailList(listReqVO, getLoginUserId())); | |
| 184 | + } | |
| 185 | + | |
| 186 | + @GetMapping("/quantity-detail/export-excel") | |
| 187 | + @Operation(summary = "导出数量金额明细账") | |
| 188 | + @PreAuthorize("@ss.hasPermission('fms:ledger:detail:export')") | |
| 189 | + @ApiAccessLog(operateType = OperateTypeEnum.EXPORT) | |
| 190 | + public void exportQuantityDetail(@Valid FmsLedgerListReqVO listReqVO, HttpServletResponse response) throws IOException { | |
| 191 | + ExcelUtils.write(response, "数量金额明细账.xls", "数量金额明细账", | |
| 192 | + FmsLedgerQuantityDetailRespVO.class, | |
| 193 | + ledgerService.getQuantityDetailList(listReqVO, getLoginUserId())); | |
| 194 | + } | |
| 195 | + | |
| 196 | + // ==================== 数量金额总账 ==================== | |
| 197 | + | |
| 198 | + @GetMapping("/quantity-general/list") | |
| 199 | + @Operation(summary = "获得数量金额总账") | |
| 200 | + @PreAuthorize("@ss.hasPermission('fms:ledger:general:query')") | |
| 201 | + public CommonResult<List<FmsLedgerQuantityGeneralRespVO>> getQuantityGeneralList(@Valid FmsLedgerListReqVO listReqVO) { | |
| 202 | + return success(ledgerService.getQuantityGeneralList(listReqVO, getLoginUserId())); | |
| 203 | + } | |
| 204 | + | |
| 205 | + @GetMapping("/quantity-general/export-excel") | |
| 206 | + @Operation(summary = "导出数量金额总账") | |
| 207 | + @PreAuthorize("@ss.hasPermission('fms:ledger:general:export')") | |
| 208 | + @ApiAccessLog(operateType = OperateTypeEnum.EXPORT) | |
| 209 | + public void exportQuantityGeneral(@Valid FmsLedgerListReqVO listReqVO, HttpServletResponse response) throws IOException { | |
| 210 | + ExcelUtils.write(response, "数量金额总账.xls", "数量金额总账", | |
| 211 | + FmsLedgerQuantityGeneralRespVO.class, | |
| 212 | + flattenQuantityGeneralList(ledgerService.getQuantityGeneralList( | |
| 213 | + listReqVO, getLoginUserId()))); | |
| 214 | + } | |
| 215 | + | |
| 216 | + private static List<FmsLedgerSubjectBalanceRespVO> flattenSubjectBalanceList(List<FmsLedgerSubjectBalanceRespVO> list) { | |
| 217 | + List<FmsLedgerSubjectBalanceRespVO> result = new ArrayList<>(); | |
| 218 | + list.forEach(item -> { | |
| 219 | + result.add(item); | |
| 220 | + result.addAll(flattenSubjectBalanceList(item.getChildren())); | |
| 221 | + }); | |
| 222 | + return result; | |
| 223 | + } | |
| 224 | + | |
| 225 | + private static List<FmsLedgerQuantityGeneralRespVO> flattenQuantityGeneralList(List<FmsLedgerQuantityGeneralRespVO> list) { | |
| 226 | + List<FmsLedgerQuantityGeneralRespVO> result = new ArrayList<>(); | |
| 227 | + list.forEach(item -> { | |
| 228 | + if (Boolean.TRUE.equals(item.getQuantityAccounting()) || CollUtil.isNotEmpty(item.getChildren())) { | |
| 229 | + result.add(item); | |
| 230 | + } | |
| 231 | + result.addAll(flattenQuantityGeneralList(item.getChildren())); | |
| 232 | + }); | |
| 233 | + return result; | |
| 234 | + } | |
| 235 | + | |
| 236 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/ledger/FmsLedgerMultiColumnExcelHelper.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.ledger; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.iocoder.yudao.module.fms.controller.admin.ledger.vo.detail.FmsLedgerDetailRespVO; | |
| 5 | +import cn.iocoder.yudao.module.fms.controller.admin.ledger.vo.multicolumn.FmsLedgerMultiColumnRespVO; | |
| 6 | +import cn.iocoder.yudao.module.fms.enums.common.FmsDebitCreditDirectionEnum; | |
| 7 | + | |
| 8 | +import java.util.ArrayList; | |
| 9 | +import java.util.Arrays; | |
| 10 | +import java.util.List; | |
| 11 | + | |
| 12 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; | |
| 13 | + | |
| 14 | +/** | |
| 15 | + * FMS 多栏账 Excel 构建工具 | |
| 16 | + * | |
| 17 | + * @author 芋道源码 | |
| 18 | + */ | |
| 19 | +public class FmsLedgerMultiColumnExcelHelper { | |
| 20 | + | |
| 21 | + private FmsLedgerMultiColumnExcelHelper() { | |
| 22 | + } | |
| 23 | + | |
| 24 | + /** | |
| 25 | + * 构建多栏账动态表头 | |
| 26 | + * | |
| 27 | + * @param result 多栏账结果 | |
| 28 | + * @return 动态表头 | |
| 29 | + */ | |
| 30 | + public static List<List<String>> buildHead(FmsLedgerMultiColumnRespVO result) { | |
| 31 | + List<List<String>> head = new ArrayList<>(); | |
| 32 | + head.add(Arrays.asList("日期", "日期")); | |
| 33 | + head.add(Arrays.asList("凭证字号", "凭证字号")); | |
| 34 | + head.add(Arrays.asList("摘要", "摘要")); | |
| 35 | + head.add(Arrays.asList("发生额", "借方")); | |
| 36 | + head.add(Arrays.asList("发生额", "贷方")); | |
| 37 | + head.add(Arrays.asList("余额", "方向")); | |
| 38 | + head.add(Arrays.asList("余额", "金额")); | |
| 39 | + for (FmsLedgerMultiColumnRespVO.Column column : getColumns(result, | |
| 40 | + FmsDebitCreditDirectionEnum.DEBIT.getType())) { | |
| 41 | + head.add(Arrays.asList(FmsDebitCreditDirectionEnum.DEBIT.getName(), | |
| 42 | + column.getSubjectCode() + "/" + column.getSubjectName())); | |
| 43 | + } | |
| 44 | + for (FmsLedgerMultiColumnRespVO.Column column : getColumns(result, | |
| 45 | + FmsDebitCreditDirectionEnum.CREDIT.getType())) { | |
| 46 | + head.add(Arrays.asList(FmsDebitCreditDirectionEnum.CREDIT.getName(), | |
| 47 | + column.getSubjectCode() + "/" + column.getSubjectName())); | |
| 48 | + } | |
| 49 | + return head; | |
| 50 | + } | |
| 51 | + | |
| 52 | + /** | |
| 53 | + * 构建多栏账动态数据 | |
| 54 | + * | |
| 55 | + * @param result 多栏账结果 | |
| 56 | + * @return 动态数据 | |
| 57 | + */ | |
| 58 | + public static List<List<Object>> buildData(FmsLedgerMultiColumnRespVO result) { | |
| 59 | + List<FmsLedgerMultiColumnRespVO.Column> debitColumns = getColumns( | |
| 60 | + result, FmsDebitCreditDirectionEnum.DEBIT.getType()); | |
| 61 | + List<FmsLedgerMultiColumnRespVO.Column> creditColumns = getColumns( | |
| 62 | + result, FmsDebitCreditDirectionEnum.CREDIT.getType()); | |
| 63 | + List<List<Object>> data = new ArrayList<>(); | |
| 64 | + for (FmsLedgerDetailRespVO row : CollUtil.emptyIfNull(result.getRows())) { | |
| 65 | + List<Object> values = new ArrayList<>(Arrays.asList(row.getAccountDate(), row.getVoucherNumber(), | |
| 66 | + row.getDigest(), row.getDebitAmount(), row.getCreditAmount(), | |
| 67 | + row.getBalanceDirection(), row.getBalance())); | |
| 68 | + debitColumns.forEach(column -> values.add(row.getColumnAmounts().get(column.getSubjectId()))); | |
| 69 | + creditColumns.forEach(column -> values.add(row.getColumnAmounts().get(column.getSubjectId()))); | |
| 70 | + data.add(values); | |
| 71 | + } | |
| 72 | + return data; | |
| 73 | + } | |
| 74 | + | |
| 75 | + private static List<FmsLedgerMultiColumnRespVO.Column> getColumns( | |
| 76 | + FmsLedgerMultiColumnRespVO result, Integer balanceDirection) { | |
| 77 | + return filterList(CollUtil.emptyIfNull(result.getColumns()), | |
| 78 | + column -> balanceDirection.equals(column.getBalanceDirection())); | |
| 79 | + } | |
| 80 | + | |
| 81 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/ledger/vo/FmsLedgerAuxiliaryListReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.ledger.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import jakarta.validation.constraints.NotNull; | |
| 5 | +import jakarta.validation.constraints.Pattern; | |
| 6 | +import lombok.Data; | |
| 7 | + | |
| 8 | +@Schema(description = "管理后台 - FMS 核算项目账簿列表查询 Request VO") | |
| 9 | +@Data | |
| 10 | +public class FmsLedgerAuxiliaryListReqVO { | |
| 11 | + | |
| 12 | + @Schema(description = "账套编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 13 | + @NotNull(message = "账套编号不能为空") | |
| 14 | + private Long accountSetId; | |
| 15 | + | |
| 16 | + @Schema(description = "开始会计期间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-08") | |
| 17 | + @NotNull(message = "开始会计期间不能为空") | |
| 18 | + @Pattern(regexp = "\\d{4}-(0[1-9]|1[0-2])", message = "开始会计期间格式不正确") | |
| 19 | + private String startMonth; | |
| 20 | + | |
| 21 | + @Schema(description = "结束会计期间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-12") | |
| 22 | + @NotNull(message = "结束会计期间不能为空") | |
| 23 | + @Pattern(regexp = "\\d{4}-(0[1-9]|1[0-2])", message = "结束会计期间格式不正确") | |
| 24 | + private String endMonth; | |
| 25 | + | |
| 26 | + @Schema(description = "辅助核算类别编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 27 | + @NotNull(message = "辅助核算类别编号不能为空") | |
| 28 | + private Long auxiliaryTypeId; | |
| 29 | + | |
| 30 | + @Schema(description = "科目编号", example = "1024") | |
| 31 | + private Long subjectId; | |
| 32 | + | |
| 33 | + @Schema(description = "辅助核算项目编号", example = "1024") | |
| 34 | + private Long auxiliaryItemId; | |
| 35 | + | |
| 36 | +} | ... | ... |
yudao-module-fms/src/main/java/cn/iocoder/yudao/module/fms/controller/admin/ledger/vo/FmsLedgerEntryVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.fms.controller.admin.ledger.vo; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.fms.dal.dataobject.voucher.FmsVoucherEntryDO; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.math.BigDecimal; | |
| 7 | +import java.time.LocalDateTime; | |
| 8 | +import java.util.List; | |
| 9 | + | |
| 10 | +/** | |
| 11 | + * FMS 账簿凭证分录 VO | |
| 12 | + * | |
| 13 | + * @author 芋道源码 | |
| 14 | + */ | |
| 15 | +@Data | |
| 16 | +public class FmsLedgerEntryVO { | |
| 17 | + | |
| 18 | + /** | |
| 19 | + * 分录编号 | |
| 20 | + */ | |
| 21 | + private Long entryId; | |
| 22 | + /** | |
| 23 | + * 凭证编号 | |
| 24 | + */ | |
| 25 | + private Long voucherId; | |
| 26 | + /** | |
| 27 | + * 科目编号 | |
| 28 | + */ | |
| 29 | + private Long subjectId; | |
| 30 | + /** | |
| 31 | + * 凭证日期 | |
| 32 | + */ | |
| 33 | + private LocalDateTime voucherTime; | |
| 34 | + /** | |
| 35 | + * 凭证号 | |
| 36 | + */ | |
| 37 | + private Integer voucherNumber; | |
| 38 | + /** | |
| 39 | + * 凭证字名称 | |
| 40 | + */ | |
| 41 | + private String voucherWordName; | |
| 42 | + /** | |
| 43 | + * 摘要内容 | |
| 44 | + */ | |
| 45 | + private String digest; | |
| 46 | + /** | |
| 47 | + * 借方金额 | |
| 48 | + */ | |
| 49 | + private BigDecimal debitAmount; | |
| 50 | + /** | |
| 51 | + * 贷方金额 | |
| 52 | + */ | |
| 53 | + private BigDecimal creditAmount; | |
| 54 | + /** | |
| 55 | + * 数量 | |
| 56 | + */ | |
| 57 | + private BigDecimal quantity; | |
| 58 | + /** | |
| 59 | + * 单价 | |
| 60 | + */ | |
| 61 | + private BigDecimal unitPrice; | |
| 62 | + /** | |
| 63 | + * 显示顺序 | |
| 64 | + */ | |
| 65 | + private Integer sort; | |
| 66 | + /** | |
| 67 | + * 辅助核算组合编号 | |
| 68 | + */ | |
| 69 | + private Long assistCombinationId; | |
| 70 | + /** | |
| 71 | + * 辅助核算项目数组 | |
| 72 | + */ | |
| 73 | + private List<FmsVoucherEntryDO.AuxiliaryItem> auxiliaries; | |
| 74 | + | |
| 75 | +} | ... | ... |