Commit 9cba258d8fccdf7436d9e4241390c125f6cd672c
Merge branch 'develop' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into master-jdk21
Showing
109 changed files
with
3196 additions
and
408 deletions
Too many changes.
To preserve performance only 100 of 109 files are displayed.
pom.xml
| ... | ... | @@ -16,12 +16,12 @@ |
| 16 | 16 | <module>yudao-module-system</module> |
| 17 | 17 | <module>yudao-module-infra</module> |
| 18 | 18 | <!-- <module>yudao-module-member</module>--> |
| 19 | -<!-- <module>yudao-module-bpm</module>--> | |
| 19 | + <module>yudao-module-bpm</module> | |
| 20 | 20 | <!-- <module>yudao-module-report</module>--> |
| 21 | 21 | <!-- <module>yudao-module-mp</module>--> |
| 22 | 22 | <!-- <module>yudao-module-pay</module>--> |
| 23 | 23 | <!-- <module>yudao-module-mall</module>--> |
| 24 | -<!-- <module>yudao-module-crm</module>--> | |
| 24 | + <module>yudao-module-crm</module> | |
| 25 | 25 | <!-- <module>yudao-module-erp</module>--> |
| 26 | 26 | <!-- 示例项目 --> |
| 27 | 27 | <!-- <module>yudao-example</module>--> | ... | ... |
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DateIntervalEnum.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.common.enums; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.ArrayUtil; | |
| 4 | +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; | |
| 5 | +import lombok.AllArgsConstructor; | |
| 6 | +import lombok.Getter; | |
| 7 | + | |
| 8 | +import java.util.Arrays; | |
| 9 | + | |
| 10 | +/** | |
| 11 | + * 时间间隔的枚举 | |
| 12 | + * | |
| 13 | + * @author dhb52 | |
| 14 | + */ | |
| 15 | +@Getter | |
| 16 | +@AllArgsConstructor | |
| 17 | +public enum DateIntervalEnum implements IntArrayValuable { | |
| 18 | + | |
| 19 | + DAY(1, "天"), | |
| 20 | + WEEK(2, "周"), | |
| 21 | + MONTH(3, "月"), | |
| 22 | + QUARTER(4, "季度"), | |
| 23 | + YEAR(5, "年") | |
| 24 | + ; | |
| 25 | + | |
| 26 | + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(DateIntervalEnum::getInterval).toArray(); | |
| 27 | + | |
| 28 | + /** | |
| 29 | + * 类型 | |
| 30 | + */ | |
| 31 | + private final Integer interval; | |
| 32 | + /** | |
| 33 | + * 名称 | |
| 34 | + */ | |
| 35 | + private final String name; | |
| 36 | + | |
| 37 | + @Override | |
| 38 | + public int[] array() { | |
| 39 | + return ARRAYS; | |
| 40 | + } | |
| 41 | + | |
| 42 | + public static DateIntervalEnum valueOf(Integer interval) { | |
| 43 | + return ArrayUtil.firstMatch(item -> item.getInterval().equals(interval), DateIntervalEnum.values()); | |
| 44 | + } | |
| 45 | + | |
| 46 | +} | |
| 0 | 47 | \ No newline at end of file | ... | ... |
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/cache/CacheUtils.java
| ... | ... | @@ -7,6 +7,7 @@ import com.google.common.cache.LoadingCache; |
| 7 | 7 | |
| 8 | 8 | import java.time.Duration; |
| 9 | 9 | import java.util.concurrent.Executor; |
| 10 | +import java.util.concurrent.ExecutorService; | |
| 10 | 11 | import java.util.concurrent.Executors; |
| 11 | 12 | |
| 12 | 13 | /** |
| ... | ... | @@ -17,8 +18,11 @@ import java.util.concurrent.Executors; |
| 17 | 18 | public class CacheUtils { |
| 18 | 19 | |
| 19 | 20 | public static <K, V> LoadingCache<K, V> buildAsyncReloadingCache(Duration duration, CacheLoader<K, V> loader) { |
| 20 | - Executor executor = Executors.newCachedThreadPool( // TODO 芋艿:可能要思考下,未来要不要做成可配置 | |
| 21 | - TtlExecutors.getDefaultDisableInheritableThreadFactory()); // TTL 保证 ThreadLocal 可以透传 | |
| 21 | + // 1. 使用 TTL 包装 ExecutorService,实现 ThreadLocal 的透传 | |
| 22 | + // https://github.com/YunaiV/ruoyi-vue-pro/issues/432 | |
| 23 | + ExecutorService executorService = Executors.newCachedThreadPool(); // TODO 芋艿:可能要思考下,未来要不要做成可配置 | |
| 24 | + Executor executor = TtlExecutors.getTtlExecutorService(executorService); | |
| 25 | + // 2. 创建 Guava LoadingCache | |
| 22 | 26 | return CacheBuilder.newBuilder() |
| 23 | 27 | // 只阻塞当前数据加载线程,其他线程返回旧值 |
| 24 | 28 | .refreshAfterWrite(duration) | ... | ... |
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
| ... | ... | @@ -78,7 +78,7 @@ public class CollectionUtils { |
| 78 | 78 | if (CollUtil.isEmpty(from)) { |
| 79 | 79 | return new ArrayList<>(); |
| 80 | 80 | } |
| 81 | - return from.stream().flatMap(func).filter(Objects::nonNull).collect(Collectors.toList()); | |
| 81 | + return from.stream().filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList()); | |
| 82 | 82 | } |
| 83 | 83 | |
| 84 | 84 | public static <T, U, R> List<R> convertListByFlatMap(Collection<T> from, |
| ... | ... | @@ -87,7 +87,7 @@ public class CollectionUtils { |
| 87 | 87 | if (CollUtil.isEmpty(from)) { |
| 88 | 88 | return new ArrayList<>(); |
| 89 | 89 | } |
| 90 | - return from.stream().map(mapper).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList()); | |
| 90 | + return from.stream().map(mapper).filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList()); | |
| 91 | 91 | } |
| 92 | 92 | |
| 93 | 93 | public static <K, V> List<V> mergeValuesFromMap(Map<K, List<V>> map) { |
| ... | ... | @@ -123,7 +123,7 @@ public class CollectionUtils { |
| 123 | 123 | if (CollUtil.isEmpty(from)) { |
| 124 | 124 | return new HashSet<>(); |
| 125 | 125 | } |
| 126 | - return from.stream().flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet()); | |
| 126 | + return from.stream().filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet()); | |
| 127 | 127 | } |
| 128 | 128 | |
| 129 | 129 | public static <T, U, R> Set<R> convertSetByFlatMap(Collection<T> from, |
| ... | ... | @@ -132,7 +132,7 @@ public class CollectionUtils { |
| 132 | 132 | if (CollUtil.isEmpty(from)) { |
| 133 | 133 | return new HashSet<>(); |
| 134 | 134 | } |
| 135 | - return from.stream().map(mapper).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet()); | |
| 135 | + return from.stream().map(mapper).filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet()); | |
| 136 | 136 | } |
| 137 | 137 | |
| 138 | 138 | public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc) { |
| ... | ... | @@ -315,4 +315,4 @@ public class CollectionUtils { |
| 315 | 315 | return list.stream().flatMap(Collection::stream).collect(Collectors.toList()); |
| 316 | 316 | } |
| 317 | 317 | |
| 318 | 318 | -} |
| 319 | +} | |
| 319 | 320 | \ No newline at end of file | ... | ... |
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/MapUtils.java
| ... | ... | @@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.common.util.collection; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.collection.CollectionUtil; |
| 5 | +import cn.hutool.core.util.ObjUtil; | |
| 5 | 6 | import cn.iocoder.yudao.framework.common.core.KeyValue; |
| 6 | 7 | import com.google.common.collect.Maps; |
| 7 | 8 | import com.google.common.collect.Multimap; |
| ... | ... | @@ -40,6 +41,7 @@ public class MapUtils { |
| 40 | 41 | |
| 41 | 42 | /** |
| 42 | 43 | * 从哈希表查找到 key 对应的 value,然后进一步处理 |
| 44 | + * key 为 null 时, 不处理 | |
| 43 | 45 | * 注意,如果查找到的 value 为 null 时,不进行处理 |
| 44 | 46 | * |
| 45 | 47 | * @param map 哈希表 |
| ... | ... | @@ -47,7 +49,7 @@ public class MapUtils { |
| 47 | 49 | * @param consumer 进一步处理的逻辑 |
| 48 | 50 | */ |
| 49 | 51 | public static <K, V> void findAndThen(Map<K, V> map, K key, Consumer<V> consumer) { |
| 50 | - if (CollUtil.isEmpty(map)) { | |
| 52 | + if (ObjUtil.isNull(key) || CollUtil.isEmpty(map)) { | |
| 51 | 53 | return; |
| 52 | 54 | } |
| 53 | 55 | V value = map.get(key); | ... | ... |
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java
| ... | ... | @@ -27,8 +27,6 @@ public class DateUtils { |
| 27 | 27 | |
| 28 | 28 | public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss"; |
| 29 | 29 | |
| 30 | - public static final String FORMAT_HOUR_MINUTE_SECOND = "HH:mm:ss"; | |
| 31 | - | |
| 32 | 30 | /** |
| 33 | 31 | * 将 LocalDateTime 转换成 Date |
| 34 | 32 | * |
| ... | ... | @@ -67,19 +65,11 @@ public class DateUtils { |
| 67 | 65 | return new Date(System.currentTimeMillis() + duration.toMillis()); |
| 68 | 66 | } |
| 69 | 67 | |
| 70 | - public static boolean isExpired(Date time) { | |
| 71 | - return System.currentTimeMillis() > time.getTime(); | |
| 72 | - } | |
| 73 | - | |
| 74 | 68 | public static boolean isExpired(LocalDateTime time) { |
| 75 | 69 | LocalDateTime now = LocalDateTime.now(); |
| 76 | 70 | return now.isAfter(time); |
| 77 | 71 | } |
| 78 | 72 | |
| 79 | - public static long diff(Date endTime, Date startTime) { | |
| 80 | - return endTime.getTime() - startTime.getTime(); | |
| 81 | - } | |
| 82 | - | |
| 83 | 73 | /** |
| 84 | 74 | * 创建指定时间 |
| 85 | 75 | * |
| ... | ... | @@ -137,37 +127,6 @@ public class DateUtils { |
| 137 | 127 | } |
| 138 | 128 | |
| 139 | 129 | /** |
| 140 | - * 计算当期时间相差的日期 | |
| 141 | - * | |
| 142 | - * @param field 日历字段.<br/>eg:Calendar.MONTH,Calendar.DAY_OF_MONTH,<br/>Calendar.HOUR_OF_DAY等. | |
| 143 | - * @param amount 相差的数值 | |
| 144 | - * @return 计算后的日志 | |
| 145 | - */ | |
| 146 | - public static Date addDate(int field, int amount) { | |
| 147 | - return addDate(null, field, amount); | |
| 148 | - } | |
| 149 | - | |
| 150 | - /** | |
| 151 | - * 计算当期时间相差的日期 | |
| 152 | - * | |
| 153 | - * @param date 设置时间 | |
| 154 | - * @param field 日历字段 例如说,{@link Calendar#DAY_OF_MONTH} 等 | |
| 155 | - * @param amount 相差的数值 | |
| 156 | - * @return 计算后的日志 | |
| 157 | - */ | |
| 158 | - public static Date addDate(Date date, int field, int amount) { | |
| 159 | - if (amount == 0) { | |
| 160 | - return date; | |
| 161 | - } | |
| 162 | - Calendar c = Calendar.getInstance(); | |
| 163 | - if (date != null) { | |
| 164 | - c.setTime(date); | |
| 165 | - } | |
| 166 | - c.add(field, amount); | |
| 167 | - return c.getTime(); | |
| 168 | - } | |
| 169 | - | |
| 170 | - /** | |
| 171 | 130 | * 是否今天 |
| 172 | 131 | * |
| 173 | 132 | * @param date 日期 | ... | ... |
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java
| 1 | 1 | package cn.iocoder.yudao.framework.common.util.date; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.hutool.core.date.DatePattern; | |
| 3 | 5 | import cn.hutool.core.date.LocalDateTimeUtil; |
| 6 | +import cn.hutool.core.lang.Assert; | |
| 7 | +import cn.hutool.core.util.StrUtil; | |
| 8 | +import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum; | |
| 4 | 9 | |
| 5 | -import java.time.Duration; | |
| 6 | -import java.time.LocalDate; | |
| 7 | -import java.time.LocalDateTime; | |
| 8 | -import java.time.LocalTime; | |
| 10 | +import java.time.*; | |
| 11 | +import java.time.format.DateTimeParseException; | |
| 9 | 12 | import java.time.temporal.ChronoUnit; |
| 10 | 13 | import java.time.temporal.TemporalAdjusters; |
| 14 | +import java.util.ArrayList; | |
| 15 | +import java.util.List; | |
| 11 | 16 | |
| 12 | 17 | /** |
| 13 | 18 | * 时间工具类,用于 {@link java.time.LocalDateTime} |
| ... | ... | @@ -21,6 +26,22 @@ public class LocalDateTimeUtils { |
| 21 | 26 | */ |
| 22 | 27 | public static LocalDateTime EMPTY = buildTime(1970, 1, 1); |
| 23 | 28 | |
| 29 | + /** | |
| 30 | + * 解析时间 | |
| 31 | + * | |
| 32 | + * 相比 {@link LocalDateTimeUtil#parse(CharSequence)} 方法来说,会尽量去解析,直到成功 | |
| 33 | + * | |
| 34 | + * @param time 时间 | |
| 35 | + * @return 时间字符串 | |
| 36 | + */ | |
| 37 | + public static LocalDateTime parse(String time) { | |
| 38 | + try { | |
| 39 | + return LocalDateTimeUtil.parse(time, DatePattern.NORM_DATE_PATTERN); | |
| 40 | + } catch (DateTimeParseException e) { | |
| 41 | + return LocalDateTimeUtil.parse(time); | |
| 42 | + } | |
| 43 | + } | |
| 44 | + | |
| 24 | 45 | public static LocalDateTime addTime(Duration duration) { |
| 25 | 46 | return LocalDateTime.now().plus(duration); |
| 26 | 47 | } |
| ... | ... | @@ -55,6 +76,21 @@ public class LocalDateTimeUtils { |
| 55 | 76 | } |
| 56 | 77 | |
| 57 | 78 | /** |
| 79 | + * 判指定断时间,是否在该时间范围内 | |
| 80 | + * | |
| 81 | + * @param startTime 开始时间 | |
| 82 | + * @param endTime 结束时间 | |
| 83 | + * @param time 指定时间 | |
| 84 | + * @return 是否 | |
| 85 | + */ | |
| 86 | + public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime, String time) { | |
| 87 | + if (startTime == null || endTime == null || time == null) { | |
| 88 | + return false; | |
| 89 | + } | |
| 90 | + return LocalDateTimeUtil.isIn(parse(time), startTime, endTime); | |
| 91 | + } | |
| 92 | + | |
| 93 | + /** | |
| 58 | 94 | * 判断当前时间是否在该时间范围内 |
| 59 | 95 | * |
| 60 | 96 | * @param startTime 开始时间 |
| ... | ... | @@ -123,6 +159,16 @@ public class LocalDateTimeUtils { |
| 123 | 159 | } |
| 124 | 160 | |
| 125 | 161 | /** |
| 162 | + * 获得指定日期所在季度 | |
| 163 | + * | |
| 164 | + * @param date 日期 | |
| 165 | + * @return 所在季度 | |
| 166 | + */ | |
| 167 | + public static int getQuarterOfYear(LocalDateTime date) { | |
| 168 | + return (date.getMonthValue() - 1) / 3 + 1; | |
| 169 | + } | |
| 170 | + | |
| 171 | + /** | |
| 126 | 172 | * 获取指定日期到现在过了几天,如果指定日期在当前日期之后,获取结果为负 |
| 127 | 173 | * |
| 128 | 174 | * @param dateTime 日期 |
| ... | ... | @@ -168,4 +214,96 @@ public class LocalDateTimeUtils { |
| 168 | 214 | return LocalDateTime.now().with(TemporalAdjusters.firstDayOfYear()).with(LocalTime.MIN); |
| 169 | 215 | } |
| 170 | 216 | |
| 217 | + public static List<LocalDateTime[]> getDateRangeList(LocalDateTime startTime, | |
| 218 | + LocalDateTime endTime, | |
| 219 | + Integer interval) { | |
| 220 | + // 1.1 找到枚举 | |
| 221 | + DateIntervalEnum intervalEnum = DateIntervalEnum.valueOf(interval); | |
| 222 | + Assert.notNull(intervalEnum, "interval({}} 找不到对应的枚举", interval); | |
| 223 | + // 1.2 将时间对齐 | |
| 224 | + startTime = LocalDateTimeUtil.beginOfDay(startTime); | |
| 225 | + endTime = LocalDateTimeUtil.endOfDay(endTime); | |
| 226 | + | |
| 227 | + // 2. 循环,生成时间范围 | |
| 228 | + List<LocalDateTime[]> timeRanges = new ArrayList<>(); | |
| 229 | + switch (intervalEnum) { | |
| 230 | + case DateIntervalEnum.DAY: | |
| 231 | + while (startTime.isBefore(endTime)) { | |
| 232 | + timeRanges.add(new LocalDateTime[]{startTime, startTime.plusDays(1).minusNanos(1)}); | |
| 233 | + startTime = startTime.plusDays(1); | |
| 234 | + } | |
| 235 | + break; | |
| 236 | + case DateIntervalEnum.WEEK: | |
| 237 | + while (startTime.isBefore(endTime)) { | |
| 238 | + LocalDateTime endOfWeek = startTime.with(DayOfWeek.SUNDAY).plusDays(1).minusNanos(1); | |
| 239 | + timeRanges.add(new LocalDateTime[]{startTime, endOfWeek}); | |
| 240 | + startTime = endOfWeek.plusNanos(1); | |
| 241 | + } | |
| 242 | + break; | |
| 243 | + case DateIntervalEnum.MONTH: | |
| 244 | + while (startTime.isBefore(endTime)) { | |
| 245 | + LocalDateTime endOfMonth = startTime.with(TemporalAdjusters.lastDayOfMonth()).plusDays(1).minusNanos(1); | |
| 246 | + timeRanges.add(new LocalDateTime[]{startTime, endOfMonth}); | |
| 247 | + startTime = endOfMonth.plusNanos(1); | |
| 248 | + } | |
| 249 | + break; | |
| 250 | + case DateIntervalEnum.QUARTER: | |
| 251 | + while (startTime.isBefore(endTime)) { | |
| 252 | + int quarterOfYear = getQuarterOfYear(startTime); | |
| 253 | + LocalDateTime quarterEnd = quarterOfYear == 4 | |
| 254 | + ? startTime.with(TemporalAdjusters.lastDayOfYear()).plusDays(1).minusNanos(1) | |
| 255 | + : startTime.withMonth(quarterOfYear * 3 + 1).withDayOfMonth(1).minusNanos(1); | |
| 256 | + timeRanges.add(new LocalDateTime[]{startTime, quarterEnd}); | |
| 257 | + startTime = quarterEnd.plusNanos(1); | |
| 258 | + } | |
| 259 | + break; | |
| 260 | + case DateIntervalEnum.YEAR: | |
| 261 | + while (startTime.isBefore(endTime)) { | |
| 262 | + LocalDateTime endOfYear = startTime.with(TemporalAdjusters.lastDayOfYear()).plusDays(1).minusNanos(1); | |
| 263 | + timeRanges.add(new LocalDateTime[]{startTime, endOfYear}); | |
| 264 | + startTime = endOfYear.plusNanos(1); | |
| 265 | + } | |
| 266 | + break; | |
| 267 | + default: | |
| 268 | + throw new IllegalArgumentException("Invalid interval: " + interval); | |
| 269 | + } | |
| 270 | + // 3. 兜底,最后一个时间,需要保持在 endTime 之前 | |
| 271 | + LocalDateTime[] lastTimeRange = CollUtil.getLast(timeRanges); | |
| 272 | + if (lastTimeRange != null) { | |
| 273 | + lastTimeRange[1] = endTime; | |
| 274 | + } | |
| 275 | + return timeRanges; | |
| 276 | + } | |
| 277 | + | |
| 278 | + /** | |
| 279 | + * 格式化时间范围 | |
| 280 | + * | |
| 281 | + * @param startTime 开始时间 | |
| 282 | + * @param endTime 结束时间 | |
| 283 | + * @param interval 时间间隔 | |
| 284 | + * @return 时间范围 | |
| 285 | + */ | |
| 286 | + public static String formatDateRange(LocalDateTime startTime, LocalDateTime endTime, Integer interval) { | |
| 287 | + // 1. 找到枚举 | |
| 288 | + DateIntervalEnum intervalEnum = DateIntervalEnum.valueOf(interval); | |
| 289 | + Assert.notNull(intervalEnum, "interval({}} 找不到对应的枚举", interval); | |
| 290 | + | |
| 291 | + // 2. 循环,生成时间范围 | |
| 292 | + switch (intervalEnum) { | |
| 293 | + case DateIntervalEnum.DAY: | |
| 294 | + return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN); | |
| 295 | + case DateIntervalEnum.WEEK: | |
| 296 | + return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN) | |
| 297 | + + StrUtil.format("(第 {} 周)", LocalDateTimeUtil.weekOfYear(startTime)); | |
| 298 | + case DateIntervalEnum.MONTH: | |
| 299 | + return LocalDateTimeUtil.format(startTime, DatePattern.NORM_MONTH_PATTERN); | |
| 300 | + case DateIntervalEnum.QUARTER: | |
| 301 | + return StrUtil.format("{}-Q{}", startTime.getYear(), getQuarterOfYear(startTime)); | |
| 302 | + case DateIntervalEnum.YEAR: | |
| 303 | + return LocalDateTimeUtil.format(startTime, DatePattern.NORM_YEAR_PATTERN); | |
| 304 | + default: | |
| 305 | + throw new IllegalArgumentException("Invalid interval: " + interval); | |
| 306 | + } | |
| 307 | + } | |
| 308 | + | |
| 171 | 309 | } | ... | ... |
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/core/DictFrameworkUtils.java
| ... | ... | @@ -11,6 +11,7 @@ import lombok.SneakyThrows; |
| 11 | 11 | import lombok.extern.slf4j.Slf4j; |
| 12 | 12 | |
| 13 | 13 | import java.time.Duration; |
| 14 | +import java.util.List; | |
| 14 | 15 | |
| 15 | 16 | /** |
| 16 | 17 | * 字典工具类 |
| ... | ... | @@ -24,6 +25,7 @@ public class DictFrameworkUtils { |
| 24 | 25 | |
| 25 | 26 | private static final DictDataRespDTO DICT_DATA_NULL = new DictDataRespDTO(); |
| 26 | 27 | |
| 28 | + // TODO @puhui999:GET_DICT_DATA_CACHE、GET_DICT_DATA_LIST_CACHE、PARSE_DICT_DATA_CACHE 这 3 个缓存是有点重叠,可以思考下,有没可能减少 1 个。微信讨论好私聊,再具体改哈 | |
| 27 | 29 | /** |
| 28 | 30 | * 针对 {@link #getDictDataLabel(String, String)} 的缓存 |
| 29 | 31 | */ |
| ... | ... | @@ -39,6 +41,20 @@ public class DictFrameworkUtils { |
| 39 | 41 | }); |
| 40 | 42 | |
| 41 | 43 | /** |
| 44 | + * 针对 {@link #getDictDataLabelList(String)} 的缓存 | |
| 45 | + */ | |
| 46 | + private static final LoadingCache<String, List<String>> GET_DICT_DATA_LIST_CACHE = CacheUtils.buildAsyncReloadingCache( | |
| 47 | + Duration.ofMinutes(1L), // 过期时间 1 分钟 | |
| 48 | + new CacheLoader<String, List<String>>() { | |
| 49 | + | |
| 50 | + @Override | |
| 51 | + public List<String> load(String dictType) { | |
| 52 | + return dictDataApi.getDictDataLabelList(dictType); | |
| 53 | + } | |
| 54 | + | |
| 55 | + }); | |
| 56 | + | |
| 57 | + /** | |
| 42 | 58 | * 针对 {@link #parseDictDataValue(String, String)} 的缓存 |
| 43 | 59 | */ |
| 44 | 60 | private static final LoadingCache<KeyValue<String, String>, DictDataRespDTO> PARSE_DICT_DATA_CACHE = CacheUtils.buildAsyncReloadingCache( |
| ... | ... | @@ -68,6 +84,11 @@ public class DictFrameworkUtils { |
| 68 | 84 | } |
| 69 | 85 | |
| 70 | 86 | @SneakyThrows |
| 87 | + public static List<String> getDictDataLabelList(String dictType) { | |
| 88 | + return GET_DICT_DATA_LIST_CACHE.get(dictType); | |
| 89 | + } | |
| 90 | + | |
| 91 | + @SneakyThrows | |
| 71 | 92 | public static String parseDictDataValue(String dictType, String label) { |
| 72 | 93 | return PARSE_DICT_DATA_CACHE.get(new KeyValue<>(dictType, label)).getValue(); |
| 73 | 94 | } | ... | ... |
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/annotations/ExcelColumnSelect.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.excel.core.annotations; | |
| 2 | + | |
| 3 | +import java.lang.annotation.*; | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * 给 Excel 列添加下拉选择数据 | |
| 7 | + * | |
| 8 | + * 其中 {@link #dictType()} 和 {@link #functionName()} 二选一 | |
| 9 | + * | |
| 10 | + * @author HUIHUI | |
| 11 | + */ | |
| 12 | +@Target({ElementType.FIELD}) | |
| 13 | +@Retention(RetentionPolicy.RUNTIME) | |
| 14 | +@Inherited | |
| 15 | +public @interface ExcelColumnSelect { | |
| 16 | + | |
| 17 | + /** | |
| 18 | + * @return 字典类型 | |
| 19 | + */ | |
| 20 | + String dictType() default ""; | |
| 21 | + | |
| 22 | + /** | |
| 23 | + * @return 获取下拉数据源的方法名称 | |
| 24 | + */ | |
| 25 | + String functionName() default ""; | |
| 26 | + | |
| 27 | +} | ... | ... |
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/enums/ExcelColumn.java deleted
| 1 | -package cn.iocoder.yudao.framework.excel.core.enums; | |
| 2 | - | |
| 3 | -import lombok.AllArgsConstructor; | |
| 4 | -import lombok.Getter; | |
| 5 | - | |
| 6 | -// TODO @puhui999:列表有办法通过 field name 么?主要考虑一个点,可能导入模版的顺序可能会变 | |
| 7 | -/** | |
| 8 | - * Excel 列名枚举 | |
| 9 | - * 默认枚举 26 列列名如果有需求更多的列名请自行补充 | |
| 10 | - * | |
| 11 | - * @author HUIHUI | |
| 12 | - */ | |
| 13 | -@Getter | |
| 14 | -@AllArgsConstructor | |
| 15 | -public enum ExcelColumn { | |
| 16 | - | |
| 17 | - A(0), B(1), C(2), D(3), E(4), F(5), G(6), H(7), I(8), | |
| 18 | - J(9), K(10), L(11), M(12), N(13), O(14), P(15), Q(16), | |
| 19 | - R(17), S(18), T(19), U(20), V(21), W(22), X(23), Y(24), | |
| 20 | - Z(25); | |
| 21 | - | |
| 22 | - /** | |
| 23 | - * 列索引 | |
| 24 | - */ | |
| 25 | - private final int colNum; | |
| 26 | - | |
| 27 | -} |
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/function/ExcelColumnSelectFunction.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.excel.core.function; | |
| 2 | + | |
| 3 | +import java.util.List; | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * Excel 列下拉数据源获取接口 | |
| 7 | + * | |
| 8 | + * 为什么不直接解析字典还搞个接口?考虑到有的下拉数据不是从字典中获取的所有需要做一个兼容 | |
| 9 | + | |
| 10 | + * @author HUIHUI | |
| 11 | + */ | |
| 12 | +public interface ExcelColumnSelectFunction { | |
| 13 | + | |
| 14 | + /** | |
| 15 | + * 获得方法名称 | |
| 16 | + * | |
| 17 | + * @return 方法名称 | |
| 18 | + */ | |
| 19 | + String getName(); | |
| 20 | + | |
| 21 | + /** | |
| 22 | + * 获得列下拉数据源 | |
| 23 | + * | |
| 24 | + * @return 下拉数据源 | |
| 25 | + */ | |
| 26 | + List<String> getOptions(); | |
| 27 | + | |
| 28 | +} | ... | ... |
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java
| ... | ... | @@ -2,25 +2,38 @@ package cn.iocoder.yudao.framework.excel.core.handler; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.lang.Assert; |
| 5 | +import cn.hutool.core.map.MapUtil; | |
| 6 | +import cn.hutool.core.util.ObjectUtil; | |
| 7 | +import cn.hutool.core.util.StrUtil; | |
| 8 | +import cn.hutool.extra.spring.SpringUtil; | |
| 9 | +import cn.hutool.poi.excel.ExcelUtil; | |
| 5 | 10 | import cn.iocoder.yudao.framework.common.core.KeyValue; |
| 6 | -import cn.iocoder.yudao.framework.excel.core.enums.ExcelColumn; | |
| 11 | +import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils; | |
| 12 | +import cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect; | |
| 13 | +import cn.iocoder.yudao.framework.excel.core.function.ExcelColumnSelectFunction; | |
| 14 | +import com.alibaba.excel.annotation.ExcelProperty; | |
| 7 | 15 | import com.alibaba.excel.write.handler.SheetWriteHandler; |
| 8 | 16 | import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; |
| 9 | 17 | import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; |
| 18 | +import lombok.extern.slf4j.Slf4j; | |
| 10 | 19 | import org.apache.poi.hssf.usermodel.HSSFDataValidation; |
| 11 | 20 | import org.apache.poi.ss.usermodel.*; |
| 12 | 21 | import org.apache.poi.ss.util.CellRangeAddressList; |
| 13 | 22 | |
| 23 | +import java.lang.reflect.Field; | |
| 14 | 24 | import java.util.Comparator; |
| 25 | +import java.util.HashMap; | |
| 15 | 26 | import java.util.List; |
| 16 | 27 | import java.util.Map; |
| 17 | -import java.util.stream.Collectors; | |
| 28 | + | |
| 29 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 18 | 30 | |
| 19 | 31 | /** |
| 20 | 32 | * 基于固定 sheet 实现下拉框 |
| 21 | 33 | * |
| 22 | 34 | * @author HUIHUI |
| 23 | 35 | */ |
| 36 | +@Slf4j | |
| 24 | 37 | public class SelectSheetWriteHandler implements SheetWriteHandler { |
| 25 | 38 | |
| 26 | 39 | /** |
| ... | ... | @@ -36,21 +49,56 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { |
| 36 | 49 | |
| 37 | 50 | private static final String DICT_SHEET_NAME = "字典sheet"; |
| 38 | 51 | |
| 39 | - // TODO @puhui999:Map<ExcelColumn, List<String>> 可以么?之前用 keyvalue 的原因,返回给前端,无法用 linkedhashmap,默认 key 会乱序 | |
| 40 | - private final List<KeyValue<ExcelColumn, List<String>>> selectMap; | |
| 52 | + /** | |
| 53 | + * key: 列 value: 下拉数据源 | |
| 54 | + */ | |
| 55 | + private final Map<Integer, List<String>> selectMap = new HashMap<>(); | |
| 41 | 56 | |
| 42 | - public SelectSheetWriteHandler(List<KeyValue<ExcelColumn, List<String>>> selectMap) { | |
| 43 | - if (CollUtil.isEmpty(selectMap)) { | |
| 44 | - this.selectMap = null; | |
| 57 | + public SelectSheetWriteHandler(Class<?> head) { | |
| 58 | + // 加载下拉数据获取接口 | |
| 59 | + Map<String, ExcelColumnSelectFunction> beansMap = SpringUtil.getBeanFactory().getBeansOfType(ExcelColumnSelectFunction.class); | |
| 60 | + if (MapUtil.isEmpty(beansMap)) { | |
| 61 | + return; | |
| 62 | + } | |
| 63 | + | |
| 64 | + // 解析下拉数据 | |
| 65 | + int colIndex = 0; | |
| 66 | + for (Field field : head.getDeclaredFields()) { | |
| 67 | + if (field.isAnnotationPresent(ExcelColumnSelect.class)) { | |
| 68 | + ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class); | |
| 69 | + if (excelProperty != null && excelProperty.index() != -1) { | |
| 70 | + colIndex = excelProperty.index(); | |
| 71 | + } | |
| 72 | + getSelectDataList(colIndex, field); | |
| 73 | + } | |
| 74 | + colIndex++; | |
| 75 | + } | |
| 76 | + } | |
| 77 | + | |
| 78 | + /** | |
| 79 | + * 获得下拉数据,并添加到 {@link #selectMap} 中 | |
| 80 | + * | |
| 81 | + * @param colIndex 列索引 | |
| 82 | + * @param field 字段 | |
| 83 | + */ | |
| 84 | + private void getSelectDataList(int colIndex, Field field) { | |
| 85 | + ExcelColumnSelect columnSelect = field.getAnnotation(ExcelColumnSelect.class); | |
| 86 | + String dictType = columnSelect.dictType(); | |
| 87 | + String functionName = columnSelect.functionName(); | |
| 88 | + Assert.isTrue(ObjectUtil.isNotEmpty(dictType) || ObjectUtil.isNotEmpty(functionName), | |
| 89 | + "Field({}) 的 @ExcelColumnSelect 注解,dictType 和 functionName 不能同时为空", field.getName()); | |
| 90 | + | |
| 91 | + // 情况一:使用 dictType 获得下拉数据 | |
| 92 | + if (StrUtil.isNotEmpty(dictType)) { // 情况一: 字典数据 (默认) | |
| 93 | + selectMap.put(colIndex, DictFrameworkUtils.getDictDataLabelList(dictType)); | |
| 45 | 94 | return; |
| 46 | 95 | } |
| 47 | - // 校验一下 key 是否唯一 | |
| 48 | - Map<String, Long> nameCounts = selectMap.stream() | |
| 49 | - .collect(Collectors.groupingBy(item -> item.getKey().name(), Collectors.counting())); | |
| 50 | - Assert.isFalse(nameCounts.entrySet().stream().allMatch(entry -> entry.getValue() > 1), "下拉数据 key 重复请排查!!!"); | |
| 51 | 96 | |
| 52 | - selectMap.sort(Comparator.comparing(item -> item.getValue().size())); // 升序不然创建下拉会报错 | |
| 53 | - this.selectMap = selectMap; | |
| 97 | + // 情况二:使用 functionName 获得下拉数据 | |
| 98 | + Map<String, ExcelColumnSelectFunction> functionMap = SpringUtil.getApplicationContext().getBeansOfType(ExcelColumnSelectFunction.class); | |
| 99 | + ExcelColumnSelectFunction function = CollUtil.findOne(functionMap.values(), item -> item.getName().equals(functionName)); | |
| 100 | + Assert.notNull(function, "未找到对应的 function({})", functionName); | |
| 101 | + selectMap.put(colIndex, function.getOptions()); | |
| 54 | 102 | } |
| 55 | 103 | |
| 56 | 104 | @Override |
| ... | ... | @@ -62,18 +110,20 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { |
| 62 | 110 | // 1. 获取相应操作对象 |
| 63 | 111 | DataValidationHelper helper = writeSheetHolder.getSheet().getDataValidationHelper(); // 需要设置下拉框的 sheet 页的数据验证助手 |
| 64 | 112 | Workbook workbook = writeWorkbookHolder.getWorkbook(); // 获得工作簿 |
| 113 | + List<KeyValue<Integer, List<String>>> keyValues = convertList(selectMap.entrySet(), entry -> new KeyValue<>(entry.getKey(), entry.getValue())); | |
| 114 | + keyValues.sort(Comparator.comparing(item -> item.getValue().size())); // 升序不然创建下拉会报错 | |
| 65 | 115 | |
| 66 | 116 | // 2. 创建数据字典的 sheet 页 |
| 67 | 117 | Sheet dictSheet = workbook.createSheet(DICT_SHEET_NAME); |
| 68 | - for (KeyValue<ExcelColumn, List<String>> keyValue : selectMap) { | |
| 118 | + for (KeyValue<Integer, List<String>> keyValue : keyValues) { | |
| 69 | 119 | int rowLength = keyValue.getValue().size(); |
| 70 | - // 2.1 设置字典 sheet 页的值 每一列一个字典项 | |
| 120 | + // 2.1 设置字典 sheet 页的值,每一列一个字典项 | |
| 71 | 121 | for (int i = 0; i < rowLength; i++) { |
| 72 | 122 | Row row = dictSheet.getRow(i); |
| 73 | 123 | if (row == null) { |
| 74 | 124 | row = dictSheet.createRow(i); |
| 75 | 125 | } |
| 76 | - row.createCell(keyValue.getKey().getColNum()).setCellValue(keyValue.getValue().get(i)); | |
| 126 | + row.createCell(keyValue.getKey()).setCellValue(keyValue.getValue().get(i)); | |
| 77 | 127 | } |
| 78 | 128 | // 2.2 设置单元格下拉选择 |
| 79 | 129 | setColumnSelect(writeSheetHolder, workbook, helper, keyValue); |
| ... | ... | @@ -84,10 +134,10 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { |
| 84 | 134 | * 设置单元格下拉选择 |
| 85 | 135 | */ |
| 86 | 136 | private static void setColumnSelect(WriteSheetHolder writeSheetHolder, Workbook workbook, DataValidationHelper helper, |
| 87 | - KeyValue<ExcelColumn, List<String>> keyValue) { | |
| 137 | + KeyValue<Integer, List<String>> keyValue) { | |
| 88 | 138 | // 1.1 创建可被其他单元格引用的名称 |
| 89 | 139 | Name name = workbook.createName(); |
| 90 | - String excelColumn = keyValue.getKey().name(); | |
| 140 | + String excelColumn = ExcelUtil.indexToColName(keyValue.getKey()); | |
| 91 | 141 | // 1.2 下拉框数据来源 eg:字典sheet!$B1:$B2 |
| 92 | 142 | String refers = DICT_SHEET_NAME + "!$" + excelColumn + "$1:$" + excelColumn + "$" + keyValue.getValue().size(); |
| 93 | 143 | name.setNameName("dict" + keyValue.getKey()); // 设置名称的名字 |
| ... | ... | @@ -97,7 +147,7 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { |
| 97 | 147 | DataValidationConstraint constraint = helper.createFormulaListConstraint("dict" + keyValue.getKey()); // 设置引用约束 |
| 98 | 148 | // 设置下拉单元格的首行、末行、首列、末列 |
| 99 | 149 | CellRangeAddressList rangeAddressList = new CellRangeAddressList(FIRST_ROW, LAST_ROW, |
| 100 | - keyValue.getKey().getColNum(), keyValue.getKey().getColNum()); | |
| 150 | + keyValue.getKey(), keyValue.getKey()); | |
| 101 | 151 | DataValidation validation = helper.createValidation(constraint, rangeAddressList); |
| 102 | 152 | if (validation instanceof HSSFDataValidation) { |
| 103 | 153 | validation.setSuppressDropDownArrow(false); | ... | ... |
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 | -import cn.iocoder.yudao.framework.common.core.KeyValue; | |
| 4 | -import cn.iocoder.yudao.framework.excel.core.enums.ExcelColumn; | |
| 5 | 3 | import cn.iocoder.yudao.framework.excel.core.handler.SelectSheetWriteHandler; |
| 6 | 4 | import com.alibaba.excel.EasyExcel; |
| 7 | 5 | import com.alibaba.excel.converters.longconverter.LongStringConverter; |
| ... | ... | @@ -34,27 +32,11 @@ public class ExcelUtils { |
| 34 | 32 | */ |
| 35 | 33 | public static <T> void write(HttpServletResponse response, String filename, String sheetName, |
| 36 | 34 | Class<T> head, List<T> data) throws IOException { |
| 37 | - write(response, filename, sheetName, head, data, null); | |
| 38 | - } | |
| 39 | - | |
| 40 | - /** | |
| 41 | - * 将列表以 Excel 响应给前端 | |
| 42 | - * | |
| 43 | - * @param response 响应 | |
| 44 | - * @param filename 文件名 | |
| 45 | - * @param sheetName Excel sheet 名 | |
| 46 | - * @param head Excel head 头 | |
| 47 | - * @param data 数据列表哦 | |
| 48 | - * @param selectMap 下拉选择数据 Map<下拉所对应的列表名,下拉数据> | |
| 49 | - * @throws IOException 写入失败的情况 | |
| 50 | - */ | |
| 51 | - public static <T> void write(HttpServletResponse response, String filename, String sheetName, | |
| 52 | - Class<T> head, List<T> data, List<KeyValue<ExcelColumn, List<String>>> selectMap) throws IOException { | |
| 53 | 35 | // 输出 Excel |
| 54 | 36 | EasyExcel.write(response.getOutputStream(), head) |
| 55 | 37 | .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 |
| 56 | 38 | .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度 |
| 57 | - .registerWriteHandler(new SelectSheetWriteHandler(selectMap)) // 基于固定 sheet 实现下拉框 | |
| 39 | + .registerWriteHandler(new SelectSheetWriteHandler(head)) // 基于固定 sheet 实现下拉框 | |
| 58 | 40 | .registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度 |
| 59 | 41 | .sheet(sheetName).doWrite(data); |
| 60 | 42 | // 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了 | ... | ... |
yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/core/TimeoutRedisCacheManager.java
| ... | ... | @@ -40,12 +40,15 @@ public class TimeoutRedisCacheManager extends RedisCacheManager { |
| 40 | 40 | // 核心:通过修改 cacheConfig 的过期时间,实现自定义过期时间 |
| 41 | 41 | if (cacheConfig != null) { |
| 42 | 42 | // 移除 # 后面的 : 以及后面的内容,避免影响解析 |
| 43 | - names[1] = StrUtil.subBefore(names[1], StrUtil.COLON, false); | |
| 43 | + String ttlStr = StrUtil.subBefore(names[1], StrUtil.COLON, false); // 获得 ttlStr 时间部分 | |
| 44 | + names[1] = StrUtil.subAfter(names[1], ttlStr, false); // 移除掉 ttlStr 时间部分 | |
| 44 | 45 | // 解析时间 |
| 45 | - Duration duration = parseDuration(names[1]); | |
| 46 | + Duration duration = parseDuration(ttlStr); | |
| 46 | 47 | cacheConfig = cacheConfig.entryTtl(duration); |
| 47 | 48 | } |
| 48 | - return super.createRedisCache(name, cacheConfig); | |
| 49 | + | |
| 50 | + // 创建 RedisCache 对象,需要忽略掉 ttlStr | |
| 51 | + return super.createRedisCache(names[0] + names[1], cacheConfig); | |
| 49 | 52 | } |
| 50 | 53 | |
| 51 | 54 | /** | ... | ... |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/operatelog/config/YudaoOperateLogV2Configuration.java renamed to yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/operatelog/config/YudaoOperateLogConfiguration.java
| ... | ... | @@ -16,7 +16,7 @@ import org.springframework.context.annotation.Primary; |
| 16 | 16 | @EnableLogRecord(tenant = "") // 貌似用不上 tenant 这玩意给个空好啦 |
| 17 | 17 | @AutoConfiguration |
| 18 | 18 | @Slf4j |
| 19 | -public class YudaoOperateLogV2Configuration { | |
| 19 | +public class YudaoOperateLogConfiguration { | |
| 20 | 20 | |
| 21 | 21 | @Bean |
| 22 | 22 | @Primary | ... | ... |
yudao-framework/yudao-spring-boot-starter-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
| 1 | 1 | cn.iocoder.yudao.framework.security.config.YudaoSecurityAutoConfiguration |
| 2 | 2 | cn.iocoder.yudao.framework.security.config.YudaoWebSecurityConfigurerAdapter |
| 3 | -cn.iocoder.yudao.framework.operatelog.config.YudaoOperateLogV2Configuration | |
| 4 | 3 | \ No newline at end of file |
| 4 | +cn.iocoder.yudao.framework.operatelog.config.YudaoOperateLogConfiguration | |
| 5 | 5 | \ No newline at end of file | ... | ... |
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
| ... | ... | @@ -15,6 +15,7 @@ public interface ErrorCodeConstants { |
| 15 | 15 | ErrorCode CONTRACT_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_020_000_002, "合同提交审核失败,原因:合同没处在未提交状态"); |
| 16 | 16 | ErrorCode CONTRACT_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS = new ErrorCode(1_020_000_003, "更新合同审核状态失败,原因:合同不是审核中状态"); |
| 17 | 17 | ErrorCode CONTRACT_NO_EXISTS = new ErrorCode(1_020_000_004, "生成合同序列号重复,请重试"); |
| 18 | + ErrorCode CONTRACT_DELETE_FAIL = new ErrorCode(1_020_000_005, "删除合同失败,原因:有被回款所使用"); | |
| 18 | 19 | |
| 19 | 20 | // ========== 线索管理 1-020-001-000 ========== |
| 20 | 21 | ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在"); |
| ... | ... | @@ -40,6 +41,7 @@ public interface ErrorCodeConstants { |
| 40 | 41 | ErrorCode RECEIVABLE_NO_EXISTS = new ErrorCode(1_020_004_005, "生成回款序列号重复,请重试"); |
| 41 | 42 | ErrorCode RECEIVABLE_CREATE_FAIL_CONTRACT_NOT_APPROVE = new ErrorCode(1_020_004_006, "创建回款失败,原因:合同不是审核通过状态"); |
| 42 | 43 | ErrorCode RECEIVABLE_CREATE_FAIL_PRICE_EXCEEDS_LIMIT = new ErrorCode(1_020_004_007, "创建回款失败,原因:回款金额超出合同金额,目前剩余可退:{} 元"); |
| 44 | + ErrorCode RECEIVABLE_DELETE_FAIL_IS_APPROVE = new ErrorCode(1_020_004_008, "删除回款失败,原因:回款审批已通过"); | |
| 43 | 45 | |
| 44 | 46 | // ========== 回款计划 1-020-005-000 ========== |
| 45 | 47 | ErrorCode RECEIVABLE_PLAN_NOT_EXISTS = new ErrorCode(1_020_005_000, "回款计划不存在"); |
| ... | ... | @@ -72,6 +74,7 @@ public interface ErrorCodeConstants { |
| 72 | 74 | ErrorCode CRM_PERMISSION_DELETE_DENIED = new ErrorCode(1_020_007_006, "删除数据权限失败,原因:没有权限"); |
| 73 | 75 | ErrorCode CRM_PERMISSION_DELETE_SELF_PERMISSION_FAIL_EXIST_OWNER = new ErrorCode(1_020_007_007, "删除数据权限失败,原因:不能删除负责人"); |
| 74 | 76 | ErrorCode CRM_PERMISSION_CREATE_FAIL = new ErrorCode(1_020_007_008, "创建数据权限失败,原因:所加用户已有权限"); |
| 77 | + ErrorCode CRM_PERMISSION_CREATE_FAIL_EXISTS = new ErrorCode(1_020_007_009, "同时添加数据权限失败,原因:用户【{}】已有模块【{}】数据【{}】的【{}】权限"); | |
| 75 | 78 | |
| 76 | 79 | // ========== 产品 1_020_008_000 ========== |
| 77 | 80 | ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_020_008_000, "产品不存在"); |
| ... | ... | @@ -100,4 +103,6 @@ public interface ErrorCodeConstants { |
| 100 | 103 | ErrorCode FOLLOW_UP_RECORD_NOT_EXISTS = new ErrorCode(1_020_013_000, "跟进记录不存在"); |
| 101 | 104 | ErrorCode FOLLOW_UP_RECORD_DELETE_DENIED = new ErrorCode(1_020_013_001, "删除跟进记录失败,原因:没有权限"); |
| 102 | 105 | |
| 106 | + // ========== 数据统计 1_020_014_000 ========== | |
| 107 | + | |
| 103 | 108 | } | ... | ... |
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java
| ... | ... | @@ -142,11 +142,11 @@ public interface LogRecordConstants { |
| 142 | 142 | |
| 143 | 143 | String CRM_RECEIVABLE_TYPE = "CRM 回款"; |
| 144 | 144 | String CRM_RECEIVABLE_CREATE_SUB_TYPE = "创建回款"; |
| 145 | - String CRM_RECEIVABLE_CREATE_SUCCESS = "创建了合同【{getContractById{#receivable.contractId}}】的第【{{#receivable.period}}】期回款"; | |
| 145 | + String CRM_RECEIVABLE_CREATE_SUCCESS = "创建了合同【{getContractById{#receivable.contractId}}】的{{#period != null ? '【第'+ #period +'期】' : '编号为【'+ #receivable.no +'】的'}}回款"; | |
| 146 | 146 | String CRM_RECEIVABLE_UPDATE_SUB_TYPE = "更新回款"; |
| 147 | - String CRM_RECEIVABLE_UPDATE_SUCCESS = "更新了合同【{getContractById{#receivable.contractId}}】的第【{{#receivable.period}}】期回款: {_DIFF{#updateReqVO}}"; | |
| 147 | + String CRM_RECEIVABLE_UPDATE_SUCCESS = "更新了合同【{getContractById{#receivable.contractId}}】的{{#period != null ? '【第'+ #period +'期】' : '编号为【'+ #receivable.no +'】的'}}回款: {_DIFF{#updateReqVO}}"; | |
| 148 | 148 | String CRM_RECEIVABLE_DELETE_SUB_TYPE = "删除回款"; |
| 149 | - String CRM_RECEIVABLE_DELETE_SUCCESS = "删除了合同【{getContractById{#receivable.contractId}}】的第【{{#receivable.period}}】期回款"; | |
| 149 | + String CRM_RECEIVABLE_DELETE_SUCCESS = "删除了合同【{getContractById{#receivable.contractId}}】的{{#period != null ? '【第'+ #period +'期】' : '编号为【'+ #receivable.no +'】的'}}回款"; | |
| 150 | 150 | String CRM_RECEIVABLE_SUBMIT_SUB_TYPE = "提交回款审批"; |
| 151 | 151 | String CRM_RECEIVABLE_SUBMIT_SUCCESS = "提交编号为【{{#receivableNo}}】的回款审批成功"; |
| 152 | 152 | ... | ... |
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/permission/CrmPermissionLevelEnum.java
| 1 | 1 | package cn.iocoder.yudao.module.crm.enums.permission; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 3 | 4 | import cn.hutool.core.util.ObjUtil; |
| 4 | 5 | import cn.iocoder.yudao.framework.common.core.IntArrayValuable; |
| 5 | 6 | import lombok.AllArgsConstructor; |
| ... | ... | @@ -50,4 +51,10 @@ public enum CrmPermissionLevelEnum implements IntArrayValuable { |
| 50 | 51 | return ObjUtil.equal(WRITE.level, level); |
| 51 | 52 | } |
| 52 | 53 | |
| 54 | + public static String getNameByLevel(Integer level) { | |
| 55 | + CrmPermissionLevelEnum typeEnum = CollUtil.findOne(CollUtil.newArrayList(CrmPermissionLevelEnum.values()), | |
| 56 | + item -> ObjUtil.equal(item.level, level)); | |
| 57 | + return typeEnum == null ? null : typeEnum.getName(); | |
| 58 | + } | |
| 59 | + | |
| 53 | 60 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessSaveReqVO.java
| ... | ... | @@ -66,13 +66,13 @@ public class CrmBusinessSaveReqVO { |
| 66 | 66 | private Long contactId; // 使用场景,在【联系人详情】添加商机时,如果需要关联两者,需要传递 contactId 字段 |
| 67 | 67 | |
| 68 | 68 | @Schema(description = "产品列表") |
| 69 | - private List<Product> products; | |
| 69 | + private List<BusinessProduct> businessProducts; | |
| 70 | 70 | |
| 71 | 71 | @Schema(description = "产品列表") |
| 72 | 72 | @Data |
| 73 | 73 | @NoArgsConstructor |
| 74 | 74 | @AllArgsConstructor |
| 75 | - public static class Product { | |
| 75 | + public static class BusinessProduct { | |
| 76 | 76 | |
| 77 | 77 | @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20529") |
| 78 | 78 | @NotNull(message = "产品编号不能为空") | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessTransferReqVO.java
| ... | ... | @@ -3,10 +3,14 @@ package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business; |
| 3 | 3 | import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; |
| 4 | 4 | import io.swagger.v3.oas.annotations.media.Schema; |
| 5 | 5 | import jakarta.validation.constraints.NotNull; |
| 6 | +import lombok.AllArgsConstructor; | |
| 6 | 7 | import lombok.Data; |
| 8 | +import lombok.NoArgsConstructor; | |
| 7 | 9 | |
| 8 | 10 | @Schema(description = "管理后台 - 商机转移 Request VO") |
| 9 | 11 | @Data |
| 12 | +@NoArgsConstructor | |
| 13 | +@AllArgsConstructor | |
| 10 | 14 | public class CrmBusinessTransferReqVO { |
| 11 | 15 | |
| 12 | 16 | @Schema(description = "商机编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactTransferReqVO.java
| ... | ... | @@ -2,12 +2,16 @@ package cn.iocoder.yudao.module.crm.controller.admin.contact.vo; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; |
| 4 | 4 | import io.swagger.v3.oas.annotations.media.Schema; |
| 5 | +import lombok.AllArgsConstructor; | |
| 5 | 6 | import lombok.Data; |
| 6 | 7 | |
| 7 | 8 | import jakarta.validation.constraints.NotNull; |
| 9 | +import lombok.NoArgsConstructor; | |
| 8 | 10 | |
| 9 | 11 | @Schema(description = "管理后台 - CRM 联系人转移 Request VO") |
| 10 | 12 | @Data |
| 13 | +@NoArgsConstructor | |
| 14 | +@AllArgsConstructor | |
| 11 | 15 | public class CrmContactTransferReqVO { |
| 12 | 16 | |
| 13 | 17 | @Schema(description = "联系人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractTransferReqVO.java
| ... | ... | @@ -3,12 +3,16 @@ package cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract; |
| 3 | 3 | import cn.iocoder.yudao.framework.common.validation.InEnum; |
| 4 | 4 | import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; |
| 5 | 5 | import io.swagger.v3.oas.annotations.media.Schema; |
| 6 | +import lombok.AllArgsConstructor; | |
| 6 | 7 | import lombok.Data; |
| 7 | 8 | |
| 8 | 9 | import jakarta.validation.constraints.NotNull; |
| 10 | +import lombok.NoArgsConstructor; | |
| 9 | 11 | |
| 10 | 12 | @Schema(description = "管理后台 - CRM 合同转移 Request VO") |
| 11 | 13 | @Data |
| 14 | +@NoArgsConstructor | |
| 15 | +@AllArgsConstructor | |
| 12 | 16 | public class CrmContractTransferReqVO { |
| 13 | 17 | |
| 14 | 18 | @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
| ... | ... | @@ -11,9 +11,7 @@ import cn.iocoder.yudao.framework.common.util.collection.MapUtils; |
| 11 | 11 | import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; |
| 12 | 12 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; |
| 13 | 13 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 14 | -import cn.iocoder.yudao.framework.excel.core.enums.ExcelColumn; | |
| 15 | 14 | import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; |
| 16 | -import cn.iocoder.yudao.framework.ip.core.Area; | |
| 17 | 15 | import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; |
| 18 | 16 | import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer.*; |
| 19 | 17 | import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; |
| ... | ... | @@ -38,7 +36,6 @@ import org.springframework.web.bind.annotation.*; |
| 38 | 36 | |
| 39 | 37 | import java.io.IOException; |
| 40 | 38 | import java.time.LocalDateTime; |
| 41 | -import java.util.ArrayList; | |
| 42 | 39 | import java.util.Arrays; |
| 43 | 40 | import java.util.List; |
| 44 | 41 | import java.util.Map; |
| ... | ... | @@ -49,7 +46,6 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 49 | 46 | import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; |
| 50 | 47 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; |
| 51 | 48 | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; |
| 52 | -import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*; | |
| 53 | 49 | import static java.util.Collections.singletonList; |
| 54 | 50 | |
| 55 | 51 | @Tag(name = "管理后台 - CRM 客户") |
| ... | ... | @@ -67,8 +63,6 @@ public class CrmCustomerController { |
| 67 | 63 | private DeptApi deptApi; |
| 68 | 64 | @Resource |
| 69 | 65 | private AdminUserApi adminUserApi; |
| 70 | - @Resource | |
| 71 | - private DictDataApi dictDataApi; | |
| 72 | 66 | |
| 73 | 67 | @PostMapping("/create") |
| 74 | 68 | @Operation(summary = "创建客户") |
| ... | ... | @@ -142,7 +136,7 @@ public class CrmCustomerController { |
| 142 | 136 | return java.util.Collections.emptyList(); |
| 143 | 137 | } |
| 144 | 138 | // 1.1 获取创建人、负责人列表 |
| 145 | - Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(list, | |
| 139 | + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSetByFlatMap(list, | |
| 146 | 140 | contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId()))); |
| 147 | 141 | Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); |
| 148 | 142 | // 1.2 获取距离进入公海的时间 |
| ... | ... | @@ -265,25 +259,7 @@ public class CrmCustomerController { |
| 265 | 259 | .areaId(null).detailAddress("").remark("").build() |
| 266 | 260 | ); |
| 267 | 261 | // 输出 |
| 268 | - ExcelUtils.write(response, "客户导入模板.xls", "客户列表", CrmCustomerImportExcelVO.class, list, builderSelectMap()); | |
| 269 | - } | |
| 270 | - | |
| 271 | - private List<KeyValue<ExcelColumn, List<String>>> builderSelectMap() { | |
| 272 | - List<KeyValue<ExcelColumn, List<String>>> selectMap = new ArrayList<>(); | |
| 273 | - // 获取地区下拉数据 | |
| 274 | - // TODO @puhui999:嘿嘿,这里改成省份、城市、区域,三个选项,难度大么? | |
| 275 | - Area area = AreaUtils.getArea(Area.ID_CHINA); | |
| 276 | - selectMap.add(new KeyValue<>(ExcelColumn.G, AreaUtils.getAreaNodePathList(area.getChildren()))); | |
| 277 | - // 获取客户所属行业 | |
| 278 | - List<String> customerIndustries = dictDataApi.getDictDataLabelList(CRM_CUSTOMER_INDUSTRY); | |
| 279 | - selectMap.add(new KeyValue<>(ExcelColumn.I, customerIndustries)); | |
| 280 | - // 获取客户等级 | |
| 281 | - List<String> customerLevels = dictDataApi.getDictDataLabelList(CRM_CUSTOMER_LEVEL); | |
| 282 | - selectMap.add(new KeyValue<>(ExcelColumn.J, customerLevels)); | |
| 283 | - // 获取客户来源 | |
| 284 | - List<String> customerSources = dictDataApi.getDictDataLabelList(CRM_CUSTOMER_SOURCE); | |
| 285 | - selectMap.add(new KeyValue<>(ExcelColumn.K, customerSources)); | |
| 286 | - return selectMap; | |
| 262 | + ExcelUtils.write(response, "客户导入模板.xls", "客户列表", CrmCustomerImportExcelVO.class, list); | |
| 287 | 263 | } |
| 288 | 264 | |
| 289 | 265 | @PostMapping("/import") | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportExcelVO.java
| 1 | 1 | package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; |
| 4 | +import cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect; | |
| 4 | 5 | import cn.iocoder.yudao.framework.excel.core.convert.AreaConvert; |
| 5 | 6 | import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; |
| 7 | +import cn.iocoder.yudao.module.crm.framework.excel.core.AreaExcelColumnSelectFunction; | |
| 6 | 8 | import com.alibaba.excel.annotation.ExcelProperty; |
| 7 | 9 | import lombok.AllArgsConstructor; |
| 8 | 10 | import lombok.Builder; |
| ... | ... | @@ -41,6 +43,7 @@ public class CrmCustomerImportExcelVO { |
| 41 | 43 | private String email; |
| 42 | 44 | |
| 43 | 45 | @ExcelProperty(value = "地区", converter = AreaConvert.class) |
| 46 | + @ExcelColumnSelect(functionName = AreaExcelColumnSelectFunction.NAME) | |
| 44 | 47 | private Integer areaId; |
| 45 | 48 | |
| 46 | 49 | @ExcelProperty("详细地址") |
| ... | ... | @@ -48,14 +51,17 @@ public class CrmCustomerImportExcelVO { |
| 48 | 51 | |
| 49 | 52 | @ExcelProperty(value = "所属行业", converter = DictConvert.class) |
| 50 | 53 | @DictFormat(CRM_CUSTOMER_INDUSTRY) |
| 54 | + @ExcelColumnSelect(dictType = CRM_CUSTOMER_INDUSTRY) | |
| 51 | 55 | private Integer industryId; |
| 52 | 56 | |
| 53 | 57 | @ExcelProperty(value = "客户等级", converter = DictConvert.class) |
| 54 | 58 | @DictFormat(CRM_CUSTOMER_LEVEL) |
| 59 | + @ExcelColumnSelect(dictType = CRM_CUSTOMER_LEVEL) | |
| 55 | 60 | private Integer level; |
| 56 | 61 | |
| 57 | 62 | @ExcelProperty(value = "客户来源", converter = DictConvert.class) |
| 58 | 63 | @DictFormat(CRM_CUSTOMER_SOURCE) |
| 64 | + @ExcelColumnSelect(dictType = CRM_CUSTOMER_SOURCE) | |
| 59 | 65 | private Integer source; |
| 60 | 66 | |
| 61 | 67 | @ExcelProperty("备注") | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerTransferReqVO.java
| ... | ... | @@ -5,6 +5,8 @@ import io.swagger.v3.oas.annotations.media.Schema; |
| 5 | 5 | import jakarta.validation.constraints.NotNull; |
| 6 | 6 | import lombok.Data; |
| 7 | 7 | |
| 8 | +import java.util.List; | |
| 9 | + | |
| 8 | 10 | @Schema(description = "管理后台 - CRM 客户转移 Request VO") |
| 9 | 11 | @Data |
| 10 | 12 | public class CrmCustomerTransferReqVO { |
| ... | ... | @@ -28,4 +30,10 @@ public class CrmCustomerTransferReqVO { |
| 28 | 30 | @Schema(description = "老负责人加入团队后的权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") |
| 29 | 31 | private Integer oldOwnerPermissionLevel; |
| 30 | 32 | |
| 33 | + /** | |
| 34 | + * 转移客户时,需要额外有【联系人】【商机】【合同】的 checkbox 选择。选中时,也一起转移 | |
| 35 | + */ | |
| 36 | + @Schema(description = "同时转移", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") | |
| 37 | + private List<Integer> toBizTypes; | |
| 38 | + | |
| 31 | 39 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.java
| 1 | 1 | package cn.iocoder.yudao.module.crm.controller.admin.permission; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | +import cn.hutool.extra.spring.SpringUtil; | |
| 4 | 5 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 5 | 6 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 6 | 7 | import cn.iocoder.yudao.framework.common.util.collection.MapUtils; |
| 7 | 8 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 8 | -import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionCreateReqVO; | |
| 9 | 9 | import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionRespVO; |
| 10 | +import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionSaveReqVO; | |
| 10 | 11 | import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO; |
| 12 | +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; | |
| 13 | +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; | |
| 14 | +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; | |
| 11 | 15 | import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; |
| 16 | +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; | |
| 12 | 17 | import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; |
| 13 | 18 | import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; |
| 19 | +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; | |
| 20 | +import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; | |
| 21 | +import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; | |
| 14 | 22 | import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; |
| 15 | 23 | import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; |
| 16 | 24 | import cn.iocoder.yudao.module.system.api.dept.DeptApi; |
| ... | ... | @@ -27,13 +35,11 @@ import io.swagger.v3.oas.annotations.tags.Tag; |
| 27 | 35 | import jakarta.annotation.Resource; |
| 28 | 36 | import jakarta.validation.Valid; |
| 29 | 37 | import org.springframework.security.access.prepost.PreAuthorize; |
| 38 | +import org.springframework.transaction.annotation.Transactional; | |
| 30 | 39 | import org.springframework.validation.annotation.Validated; |
| 31 | 40 | import org.springframework.web.bind.annotation.*; |
| 32 | 41 | |
| 33 | -import java.util.Collection; | |
| 34 | -import java.util.Collections; | |
| 35 | -import java.util.List; | |
| 36 | -import java.util.Map; | |
| 42 | +import java.util.*; | |
| 37 | 43 | import java.util.stream.Stream; |
| 38 | 44 | |
| 39 | 45 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| ... | ... | @@ -50,7 +56,6 @@ public class CrmPermissionController { |
| 50 | 56 | |
| 51 | 57 | @Resource |
| 52 | 58 | private CrmPermissionService permissionService; |
| 53 | - | |
| 54 | 59 | @Resource |
| 55 | 60 | private AdminUserApi adminUserApi; |
| 56 | 61 | @Resource |
| ... | ... | @@ -61,9 +66,8 @@ public class CrmPermissionController { |
| 61 | 66 | @PostMapping("/create") |
| 62 | 67 | @Operation(summary = "创建数据权限") |
| 63 | 68 | @PreAuthorize("@ss.hasPermission('crm:permission:create')") |
| 64 | - @CrmPermission(bizTypeValue = "#reqVO.bizType", bizId = "#reqVO.bizId", level = CrmPermissionLevelEnum.OWNER) | |
| 65 | - public CommonResult<Boolean> addPermission(@Valid @RequestBody CrmPermissionCreateReqVO reqVO) { | |
| 66 | - permissionService.createPermission(BeanUtils.toBean(reqVO, CrmPermissionCreateReqBO.class)); | |
| 69 | + public CommonResult<Boolean> create(@Valid @RequestBody CrmPermissionSaveReqVO reqVO) { | |
| 70 | + permissionService.createPermission(reqVO, getLoginUserId()); | |
| 67 | 71 | return success(true); |
| 68 | 72 | } |
| 69 | 73 | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionCreateReqVO.java deleted
| 1 | -package cn.iocoder.yudao.module.crm.controller.admin.permission.vo; | |
| 2 | - | |
| 3 | -import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | -import lombok.Data; | |
| 5 | -import lombok.EqualsAndHashCode; | |
| 6 | -import lombok.ToString; | |
| 7 | - | |
| 8 | -@Schema(description = "管理后台 - CRM 数据权限创建 Request VO") | |
| 9 | -@Data | |
| 10 | -@EqualsAndHashCode(callSuper = true) | |
| 11 | -@ToString(callSuper = true) | |
| 12 | -public class CrmPermissionCreateReqVO extends CrmPermissionBaseVO { | |
| 13 | - | |
| 14 | -} |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionRespVO.java
| 1 | 1 | package cn.iocoder.yudao.module.crm.controller.admin.permission.vo; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 4 | +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; | |
| 5 | +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; | |
| 3 | 6 | import io.swagger.v3.oas.annotations.media.Schema; |
| 7 | +import jakarta.validation.constraints.NotNull; | |
| 4 | 8 | import lombok.Data; |
| 5 | 9 | |
| 6 | 10 | import java.time.LocalDateTime; |
| ... | ... | @@ -8,11 +12,29 @@ import java.util.Set; |
| 8 | 12 | |
| 9 | 13 | @Schema(description = "管理后台 - CRM 数据权限 Response VO") |
| 10 | 14 | @Data |
| 11 | -public class CrmPermissionRespVO extends CrmPermissionBaseVO { | |
| 15 | +public class CrmPermissionRespVO { | |
| 12 | 16 | |
| 13 | 17 | @Schema(description = "数据权限编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") |
| 14 | 18 | private Long id; |
| 15 | 19 | |
| 20 | + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") | |
| 21 | + @NotNull(message = "用户编号不能为空") | |
| 22 | + private Long userId; | |
| 23 | + | |
| 24 | + @Schema(description = "CRM 类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | |
| 25 | + @InEnum(CrmBizTypeEnum.class) | |
| 26 | + @NotNull(message = "CRM 类型不能为空") | |
| 27 | + private Integer bizType; | |
| 28 | + | |
| 29 | + @Schema(description = "CRM 类型数据编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 30 | + @NotNull(message = "CRM 类型数据编号不能为空") | |
| 31 | + private Long bizId; | |
| 32 | + | |
| 33 | + @Schema(description = "权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | |
| 34 | + @InEnum(CrmPermissionLevelEnum.class) | |
| 35 | + @NotNull(message = "权限级别不能为空") | |
| 36 | + private Integer level; | |
| 37 | + | |
| 16 | 38 | @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") |
| 17 | 39 | private String nickname; |
| 18 | 40 | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionBaseVO.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionSaveReqVO.java
| ... | ... | @@ -8,14 +8,11 @@ import lombok.Data; |
| 8 | 8 | |
| 9 | 9 | import jakarta.validation.constraints.NotNull; |
| 10 | 10 | |
| 11 | -/** | |
| 12 | - * 数据权限 Base VO,提供给添加、修改、详细的子 VO 使用 | |
| 13 | - * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 | |
| 14 | - * | |
| 15 | - * @author HUIHUI | |
| 16 | - */ | |
| 11 | +import java.util.List; | |
| 12 | + | |
| 13 | +@Schema(description = "管理后台 - CRM 数据权限创建/更新 Request VO") | |
| 17 | 14 | @Data |
| 18 | -public class CrmPermissionBaseVO { | |
| 15 | +public class CrmPermissionSaveReqVO { | |
| 19 | 16 | |
| 20 | 17 | @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") |
| 21 | 18 | @NotNull(message = "用户编号不能为空") |
| ... | ... | @@ -35,4 +32,11 @@ public class CrmPermissionBaseVO { |
| 35 | 32 | @NotNull(message = "权限级别不能为空") |
| 36 | 33 | private Integer level; |
| 37 | 34 | |
| 35 | + /** | |
| 36 | + * 添加客户团队成员时,需要额外有【联系人】【商机】【合同】的 checkbox 选择。 | |
| 37 | + * 选中时,同时添加对应的权限 | |
| 38 | + */ | |
| 39 | + @Schema(description = "同时添加", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") | |
| 40 | + private List<Integer> toBizTypes; | |
| 41 | + | |
| 38 | 42 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java
| 1 | 1 | package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO; |
| 4 | +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; | |
| 4 | 5 | import com.alibaba.excel.annotation.ExcelProperty; |
| 5 | 6 | import io.swagger.v3.oas.annotations.media.Schema; |
| 6 | 7 | import lombok.Data; |
| ... | ... | @@ -8,53 +9,69 @@ import lombok.Data; |
| 8 | 9 | import java.math.BigDecimal; |
| 9 | 10 | import java.time.LocalDateTime; |
| 10 | 11 | |
| 11 | -// TODO @puhui999:缺导出 | |
| 12 | 12 | @Schema(description = "管理后台 - CRM 回款计划 Response VO") |
| 13 | 13 | @Data |
| 14 | +@ExcelIgnoreUnannotated | |
| 14 | 15 | public class CrmReceivablePlanRespVO { |
| 15 | 16 | |
| 16 | 17 | @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") |
| 18 | + @ExcelProperty("编号") | |
| 17 | 19 | private Long id; |
| 18 | 20 | |
| 19 | 21 | @Schema(description = "期数", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") |
| 22 | + @ExcelProperty("期数") | |
| 20 | 23 | private Integer period; |
| 21 | 24 | |
| 22 | 25 | @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") |
| 26 | + @ExcelProperty("客户编号") | |
| 23 | 27 | private Long customerId; |
| 24 | 28 | @Schema(description = "客户名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "test") |
| 29 | + @ExcelProperty("客户名字") | |
| 25 | 30 | private String customerName; |
| 26 | 31 | |
| 27 | 32 | @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") |
| 33 | + @ExcelProperty("合同编号") | |
| 28 | 34 | private Long contractId; |
| 29 | 35 | @Schema(description = "合同编号", example = "Q110") |
| 36 | + @ExcelProperty("合同编号") | |
| 30 | 37 | private String contractNo; |
| 31 | 38 | |
| 32 | 39 | @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") |
| 40 | + @ExcelProperty("负责人编号") | |
| 33 | 41 | private Long ownerUserId; |
| 34 | 42 | @Schema(description = "负责人", example = "test") |
| 43 | + @ExcelProperty("负责人") | |
| 35 | 44 | private String ownerUserName; |
| 36 | 45 | |
| 37 | 46 | @Schema(description = "计划回款日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02") |
| 47 | + @ExcelProperty("计划回款日期") | |
| 38 | 48 | private LocalDateTime returnTime; |
| 39 | 49 | |
| 40 | 50 | @Schema(description = "计划回款方式", example = "1") |
| 51 | + @ExcelProperty("计划回款方式") | |
| 41 | 52 | private Integer returnType; |
| 42 | 53 | |
| 43 | 54 | @Schema(description = "计划回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000") |
| 55 | + @ExcelProperty("计划回款金额") | |
| 44 | 56 | private BigDecimal price; |
| 45 | 57 | |
| 46 | 58 | @Schema(description = "回款编号", example = "19852") |
| 59 | + @ExcelProperty("回款编号") | |
| 47 | 60 | private Long receivableId; |
| 48 | 61 | @Schema(description = "回款信息") |
| 62 | + @ExcelProperty("回款信息") | |
| 49 | 63 | private CrmReceivableRespVO receivable; |
| 50 | 64 | |
| 51 | 65 | @Schema(description = "提前几天提醒", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 66 | + @ExcelProperty("提前几天提醒") | |
| 52 | 67 | private Integer remindDays; |
| 53 | 68 | |
| 54 | 69 | @Schema(description = "提醒日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02") |
| 70 | + @ExcelProperty("提醒日期") | |
| 55 | 71 | private LocalDateTime remindTime; |
| 56 | 72 | |
| 57 | 73 | @Schema(description = "备注", example = "备注") |
| 74 | + @ExcelProperty("备注") | |
| 58 | 75 | private String remark; |
| 59 | 76 | |
| 60 | 77 | @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableRespVO.java
| 1 | 1 | package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractRespVO; |
| 4 | +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; | |
| 4 | 5 | import com.alibaba.excel.annotation.ExcelProperty; |
| 5 | 6 | import io.swagger.v3.oas.annotations.media.Schema; |
| 6 | 7 | import lombok.Data; |
| ... | ... | @@ -8,37 +9,47 @@ import lombok.Data; |
| 8 | 9 | import java.math.BigDecimal; |
| 9 | 10 | import java.time.LocalDateTime; |
| 10 | 11 | |
| 11 | -// TODO 芋艿:导出的 VO,可以考虑使用 @Excel 注解,实现导出功能 | |
| 12 | 12 | @Schema(description = "管理后台 - CRM 回款 Response VO") |
| 13 | 13 | @Data |
| 14 | +@ExcelIgnoreUnannotated | |
| 14 | 15 | public class CrmReceivableRespVO { |
| 15 | 16 | |
| 16 | 17 | @Schema(description = "编号", example = "25787") |
| 18 | + @ExcelProperty("编号") | |
| 17 | 19 | private Long id; |
| 18 | 20 | |
| 19 | 21 | @Schema(description = "回款编号", example = "31177") |
| 22 | + @ExcelProperty("回款编号") | |
| 20 | 23 | private String no; |
| 21 | 24 | |
| 22 | 25 | @Schema(description = "回款计划编号", example = "1024") |
| 26 | + @ExcelProperty("回款计划编号") | |
| 23 | 27 | private Long planId; |
| 24 | 28 | |
| 25 | 29 | @Schema(description = "回款方式", example = "2") |
| 30 | + @ExcelProperty("回款方式") | |
| 26 | 31 | private Integer returnType; |
| 27 | 32 | |
| 28 | 33 | @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000") |
| 34 | + @ExcelProperty("回款金额") | |
| 29 | 35 | private BigDecimal price; |
| 30 | 36 | |
| 31 | 37 | @Schema(description = "计划回款日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02") |
| 38 | + @ExcelProperty("计划回款日期") | |
| 32 | 39 | private LocalDateTime returnTime; |
| 33 | 40 | |
| 34 | 41 | @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") |
| 42 | + @ExcelProperty("客户编号") | |
| 35 | 43 | private Long customerId; |
| 36 | 44 | @Schema(description = "客户名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "test") |
| 45 | + @ExcelProperty("客户名字") | |
| 37 | 46 | private String customerName; |
| 38 | 47 | |
| 39 | 48 | @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") |
| 49 | + @ExcelProperty("合同编号") | |
| 40 | 50 | private Long contractId; |
| 41 | 51 | @Schema(description = "合同信息") |
| 52 | + @ExcelProperty("合同信息") | |
| 42 | 53 | private CrmContractRespVO contract; |
| 43 | 54 | |
| 44 | 55 | @Schema(description = "负责人的用户编号", example = "25682") |
| ... | ... | @@ -56,20 +67,26 @@ public class CrmReceivableRespVO { |
| 56 | 67 | private String processInstanceId; |
| 57 | 68 | |
| 58 | 69 | @Schema(description = "审批状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| 70 | + @ExcelProperty("审批状态") | |
| 59 | 71 | private Integer auditStatus; |
| 60 | 72 | |
| 61 | - @Schema(description = "备注", example = "备注") | |
| 73 | + @Schema(description = "工作流编号", example = "备注") | |
| 74 | + @ExcelProperty("工作流编号") | |
| 62 | 75 | private String remark; |
| 63 | 76 | |
| 64 | 77 | @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) |
| 78 | + @ExcelProperty("创建时间") | |
| 65 | 79 | private LocalDateTime createTime; |
| 66 | 80 | |
| 67 | 81 | @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) |
| 82 | + @ExcelProperty("更新时间") | |
| 68 | 83 | private LocalDateTime updateTime; |
| 69 | 84 | |
| 70 | 85 | @Schema(description = "创建人", example = "25682") |
| 86 | + @ExcelProperty("创建人") | |
| 71 | 87 | private String creator; |
| 72 | 88 | @Schema(description = "创建人名字", example = "test") |
| 89 | + @ExcelProperty("创建人名字") | |
| 73 | 90 | private String creatorName; |
| 74 | 91 | |
| 75 | 92 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http
0 → 100644
| 1 | +# == 1. 客户总量分析 == | |
| 2 | +### 1.1 客户总量分析(按日期) | |
| 3 | +GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-date?deptId=100&interval=2×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59 | |
| 4 | +Authorization: Bearer {{token}} | |
| 5 | +tenant-id: {{adminTenentId}} | |
| 6 | + | |
| 7 | +### 1.2 客户总量统计(按用户) | |
| 8 | +GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-user?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 | |
| 9 | +Authorization: Bearer {{token}} | |
| 10 | +tenant-id: {{adminTenentId}} | |
| 11 | + | |
| 12 | +# == 2. 客户跟进次数分析 == | |
| 13 | +### 2.1 客户跟进次数分析(按日期) | |
| 14 | +GET {{baseUrl}}/crm/statistics-customer/get-follow-up-summary-by-date?deptId=100&interval=2×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59 | |
| 15 | +Authorization: Bearer {{token}} | |
| 16 | +tenant-id: {{adminTenentId}} | |
| 17 | + | |
| 18 | +### 2.2 客户总量统计(按用户) | |
| 19 | +GET {{baseUrl}}/crm/statistics-customer/get-follow-up-summary-by-user?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 | |
| 20 | +Authorization: Bearer {{token}} | |
| 21 | +tenant-id: {{adminTenentId}} | |
| 22 | + | |
| 23 | +# == 3. 客户跟进方式分析 == | |
| 24 | +### 3.1 客户跟进方式分析 | |
| 25 | +GET {{baseUrl}}/crm/statistics-customer/get-follow-up-summary-by-type?deptId=100&interval=2×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 | |
| 26 | +Authorization: Bearer {{token}} | |
| 27 | +tenant-id: {{adminTenentId}} | |
| 28 | + | |
| 29 | +# == 4. 客户成交周期 == | |
| 30 | +### 4.1 合同摘要信息(客户转化率页面) | |
| 31 | +GET {{baseUrl}}/crm/statistics-customer/get-contract-summary?deptId=100&interval=2×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 | |
| 32 | +Authorization: Bearer {{token}} | |
| 33 | +tenant-id: {{adminTenentId}} | |
| 34 | + | |
| 35 | +# == 5. 客户成交周期 == | |
| 36 | +### 5.1 获取客户公海分析(按日期) | |
| 37 | +GET {{baseUrl}}/crm/statistics-customer/get-pool-summary-by-date?deptId=100&interval=2×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 | |
| 38 | +Authorization: Bearer {{token}} | |
| 39 | +tenant-id: {{adminTenentId}} | |
| 40 | + | |
| 41 | +### 5.2 获取客户公海分析(按用户) | |
| 42 | +GET {{baseUrl}}/crm/statistics-customer/get-pool-summary-by-user?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 | |
| 43 | +Authorization: Bearer {{token}} | |
| 44 | +tenant-id: {{adminTenentId}} | |
| 45 | + | |
| 46 | +# == 6. 客户成交周期 == | |
| 47 | +### 6.1 客户成交周期(按日期) | |
| 48 | +GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-date?deptId=100&interval=2×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59 | |
| 49 | +Authorization: Bearer {{token}} | |
| 50 | +tenant-id: {{adminTenentId}} | |
| 51 | + | |
| 52 | +### 6.2 获取客户成交周期(按用户) | |
| 53 | +GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-user?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 | |
| 54 | +Authorization: Bearer {{token}} | |
| 55 | +tenant-id: {{adminTenentId}} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.statistics; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; | |
| 5 | +import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsCustomerService; | |
| 6 | +import io.swagger.v3.oas.annotations.Operation; | |
| 7 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 8 | +import jakarta.annotation.Resource; | |
| 9 | +import jakarta.validation.Valid; | |
| 10 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 11 | +import org.springframework.validation.annotation.Validated; | |
| 12 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 13 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 14 | +import org.springframework.web.bind.annotation.RestController; | |
| 15 | + | |
| 16 | +import java.util.List; | |
| 17 | + | |
| 18 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 19 | + | |
| 20 | +@Tag(name = "管理后台 - CRM 客户统计") | |
| 21 | +@RestController | |
| 22 | +@RequestMapping("/crm/statistics-customer") | |
| 23 | +@Validated | |
| 24 | +public class CrmStatisticsCustomerController { | |
| 25 | + | |
| 26 | + @Resource | |
| 27 | + private CrmStatisticsCustomerService customerService; | |
| 28 | + | |
| 29 | + @GetMapping("/get-customer-summary-by-date") | |
| 30 | + @Operation(summary = "获取客户总量分析(按日期)") | |
| 31 | + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") | |
| 32 | + public CommonResult<List<CrmStatisticsCustomerSummaryByDateRespVO>> getCustomerSummaryByDate(@Valid CrmStatisticsCustomerReqVO reqVO) { | |
| 33 | + return success(customerService.getCustomerSummaryByDate(reqVO)); | |
| 34 | + } | |
| 35 | + | |
| 36 | + @GetMapping("/get-customer-summary-by-user") | |
| 37 | + @Operation(summary = "获取客户总量分析(按用户)") | |
| 38 | + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") | |
| 39 | + public CommonResult<List<CrmStatisticsCustomerSummaryByUserRespVO>> getCustomerSummaryByUser(@Valid CrmStatisticsCustomerReqVO reqVO) { | |
| 40 | + return success(customerService.getCustomerSummaryByUser(reqVO)); | |
| 41 | + } | |
| 42 | + | |
| 43 | + @GetMapping("/get-follow-up-summary-by-date") | |
| 44 | + @Operation(summary = "获取客户跟进次数分析(按日期)") | |
| 45 | + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") | |
| 46 | + public CommonResult<List<CrmStatisticsFollowUpSummaryByDateRespVO>> getFollowupSummaryByDate(@Valid CrmStatisticsCustomerReqVO reqVO) { | |
| 47 | + return success(customerService.getFollowUpSummaryByDate(reqVO)); | |
| 48 | + } | |
| 49 | + | |
| 50 | + @GetMapping("/get-follow-up-summary-by-user") | |
| 51 | + @Operation(summary = "获取客户跟进次数分析(按用户)") | |
| 52 | + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") | |
| 53 | + public CommonResult<List<CrmStatisticsFollowUpSummaryByUserRespVO>> getFollowUpSummaryByUser(@Valid CrmStatisticsCustomerReqVO reqVO) { | |
| 54 | + return success(customerService.getFollowUpSummaryByUser(reqVO)); | |
| 55 | + } | |
| 56 | + | |
| 57 | + @GetMapping("/get-follow-up-summary-by-type") | |
| 58 | + @Operation(summary = "获取客户跟进次数分析(按类型)") | |
| 59 | + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") | |
| 60 | + public CommonResult<List<CrmStatisticsFollowUpSummaryByTypeRespVO>> getFollowUpSummaryByType(@Valid CrmStatisticsCustomerReqVO reqVO) { | |
| 61 | + return success(customerService.getFollowUpSummaryByType(reqVO)); | |
| 62 | + } | |
| 63 | + | |
| 64 | + @GetMapping("/get-contract-summary") | |
| 65 | + @Operation(summary = "获取客户的首次合同、回款信息列表", description = "用于【客户转化率】页面") | |
| 66 | + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") | |
| 67 | + public CommonResult<List<CrmStatisticsCustomerContractSummaryRespVO>> getContractSummary(@Valid CrmStatisticsCustomerReqVO reqVO) { | |
| 68 | + return success(customerService.getContractSummary(reqVO)); | |
| 69 | + } | |
| 70 | + | |
| 71 | + @GetMapping("/get-pool-summary-by-date") | |
| 72 | + @Operation(summary = "获取公海客户分析(按日期)") | |
| 73 | + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") | |
| 74 | + public CommonResult<List<CrmStatisticsPoolSummaryByDateRespVO>> getPoolSummaryByDate(@Valid CrmStatisticsCustomerReqVO reqVO) { | |
| 75 | + return success(customerService.getPoolSummaryByDate(reqVO)); | |
| 76 | + } | |
| 77 | + | |
| 78 | + @GetMapping("/get-pool-summary-by-user") | |
| 79 | + @Operation(summary = "获取公海客户分析(按用户)") | |
| 80 | + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") | |
| 81 | + public CommonResult<List<CrmStatisticsPoolSummaryByUserRespVO>> getPoolSummaryByUser(@Valid CrmStatisticsCustomerReqVO reqVO) { | |
| 82 | + return success(customerService.getPoolSummaryByUser(reqVO)); | |
| 83 | + } | |
| 84 | + | |
| 85 | + @GetMapping("/get-customer-deal-cycle-by-date") | |
| 86 | + @Operation(summary = "获取客户成交周期(按日期)") | |
| 87 | + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") | |
| 88 | + public CommonResult<List<CrmStatisticsCustomerDealCycleByDateRespVO>> getCustomerDealCycleByDate(@Valid CrmStatisticsCustomerReqVO reqVO) { | |
| 89 | + return success(customerService.getCustomerDealCycleByDate(reqVO)); | |
| 90 | + } | |
| 91 | + | |
| 92 | + @GetMapping("/get-customer-deal-cycle-by-user") | |
| 93 | + @Operation(summary = "获取客户成交周期(按用户)") | |
| 94 | + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") | |
| 95 | + public CommonResult<List<CrmStatisticsCustomerDealCycleByUserRespVO>> getCustomerDealCycleByUser(@Valid CrmStatisticsCustomerReqVO reqVO) { | |
| 96 | + return success(customerService.getCustomerDealCycleByUser(reqVO)); | |
| 97 | + } | |
| 98 | + | |
| 99 | + // TODO dhb52:【成交周期分析】里,有按照员工(已实现)、地区(未实现)、产品(未实现),需要在看看哈;可以把 CustomerDealCycle 拆成 3 个 tab,员工客户成交周期分析、地区客户成交周期分析、产品客户成交周期分析; | |
| 100 | + | |
| 101 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsPerformanceController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.statistics; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceReqVO; | |
| 5 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO; | |
| 6 | +import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsPerformanceService; | |
| 7 | +import io.swagger.v3.oas.annotations.Operation; | |
| 8 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 9 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 10 | +import org.springframework.validation.annotation.Validated; | |
| 11 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 12 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 13 | +import org.springframework.web.bind.annotation.RestController; | |
| 14 | + | |
| 15 | +import jakarta.annotation.Resource; | |
| 16 | +import jakarta.validation.Valid; | |
| 17 | +import java.util.List; | |
| 18 | + | |
| 19 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 20 | + | |
| 21 | + | |
| 22 | +@Tag(name = "管理后台 - CRM 员工业绩统计") | |
| 23 | +@RestController | |
| 24 | +@RequestMapping("/crm/statistics-performance") | |
| 25 | +@Validated | |
| 26 | +public class CrmStatisticsPerformanceController { | |
| 27 | + | |
| 28 | + @Resource | |
| 29 | + private CrmStatisticsPerformanceService performanceService; | |
| 30 | + | |
| 31 | + @GetMapping("/get-contract-count-performance") | |
| 32 | + @Operation(summary = "合同数量统计", description = "用于【合同数量分析】页面") | |
| 33 | + @PreAuthorize("@ss.hasPermission('crm:statistics-performance:query')") | |
| 34 | + public CommonResult<List<CrmStatisticsPerformanceRespVO>> getContractCountPerformance(@Valid CrmStatisticsPerformanceReqVO performanceReqVO) { | |
| 35 | + return success(performanceService.getContractCountPerformance(performanceReqVO)); | |
| 36 | + } | |
| 37 | + | |
| 38 | + @GetMapping("/get-contract-price-performance") | |
| 39 | + @Operation(summary = "合同金额统计") | |
| 40 | + @PreAuthorize("@ss.hasPermission('crm:statistics-performance:query')") | |
| 41 | + public CommonResult<List<CrmStatisticsPerformanceRespVO>> getContractPriceStaffPerformance(@Valid CrmStatisticsPerformanceReqVO performanceReqVO) { | |
| 42 | + return success(performanceService.getContractPricePerformance(performanceReqVO)); | |
| 43 | + } | |
| 44 | + | |
| 45 | + @GetMapping("/get-receivable-price-performance") | |
| 46 | + @Operation(summary = "回款金额统计") | |
| 47 | + @PreAuthorize("@ss.hasPermission('crm:statistics-performance:query')") | |
| 48 | + public CommonResult<List<CrmStatisticsPerformanceRespVO>> getReceivablePriceStaffPerformance(@Valid CrmStatisticsPerformanceReqVO performanceReqVO) { | |
| 49 | + return success(performanceService.getReceivablePricePerformance(performanceReqVO)); | |
| 50 | + } | |
| 51 | + | |
| 52 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsPortraitController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.statistics; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticsPortraitReqVO; | |
| 5 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerAreaRespVO; | |
| 6 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerIndustryRespVO; | |
| 7 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerLevelRespVO; | |
| 8 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerSourceRespVO; | |
| 9 | +import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsPortraitService; | |
| 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 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.RestController; | |
| 19 | + | |
| 20 | +import java.util.List; | |
| 21 | + | |
| 22 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 23 | + | |
| 24 | +@Tag(name = "管理后台 - CRM 客户画像") | |
| 25 | +@RestController | |
| 26 | +@RequestMapping("/crm/statistics-portrait") | |
| 27 | +@Validated | |
| 28 | +public class CrmStatisticsPortraitController { | |
| 29 | + | |
| 30 | + @Resource | |
| 31 | + private CrmStatisticsPortraitService statisticsPortraitService; | |
| 32 | + | |
| 33 | + @GetMapping("/get-customer-area-summary") | |
| 34 | + @Operation(summary = "获取客户地区统计数据", description = "用于【城市分布分析】页面") | |
| 35 | + @PreAuthorize("@ss.hasPermission('crm:statistics-portrait:query')") | |
| 36 | + public CommonResult<List<CrmStatisticCustomerAreaRespVO>> getCustomerAreaSummary(@Valid CrmStatisticsPortraitReqVO reqVO) { | |
| 37 | + return success(statisticsPortraitService.getCustomerSummaryByArea(reqVO)); | |
| 38 | + } | |
| 39 | + | |
| 40 | + @GetMapping("/get-customer-industry-summary") | |
| 41 | + @Operation(summary = "获取客户行业统计数据", description = "用于【客户行业分析】页面") | |
| 42 | + @PreAuthorize("@ss.hasPermission('crm:statistics-portrait:query')") | |
| 43 | + public CommonResult<List<CrmStatisticCustomerIndustryRespVO>> getCustomerIndustrySummary(@Valid CrmStatisticsPortraitReqVO reqVO) { | |
| 44 | + return success(statisticsPortraitService.getCustomerSummaryByIndustry(reqVO)); | |
| 45 | + } | |
| 46 | + | |
| 47 | + @GetMapping("/get-customer-level-summary") | |
| 48 | + @Operation(summary = "获取客户级别统计数据", description = "用于【客户级别分析】页面") | |
| 49 | + @PreAuthorize("@ss.hasPermission('crm:statistics-portrait:query')") | |
| 50 | + public CommonResult<List<CrmStatisticCustomerLevelRespVO>> getCustomerLevelSummary(@Valid CrmStatisticsPortraitReqVO reqVO) { | |
| 51 | + return success(statisticsPortraitService.getCustomerSummaryByLevel(reqVO)); | |
| 52 | + } | |
| 53 | + | |
| 54 | + @GetMapping("/get-customer-source-summary") | |
| 55 | + @Operation(summary = "获取客户来源统计数据", description = "用于【客户来源分析】页面") | |
| 56 | + @PreAuthorize("@ss.hasPermission('crm:statistics-portrait:query')") | |
| 57 | + public CommonResult<List<CrmStatisticCustomerSourceRespVO>> getCustomerSourceSummary(@Valid CrmStatisticsPortraitReqVO reqVO) { | |
| 58 | + return success(statisticsPortraitService.getCustomerSummaryBySource(reqVO)); | |
| 59 | + } | |
| 60 | + | |
| 61 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsRankController.java
| 1 | 1 | package cn.iocoder.yudao.module.crm.controller.admin.statistics; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO; | |
| 5 | -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRankReqVO; | |
| 6 | -import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsRankingService; | |
| 4 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO; | |
| 5 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankReqVO; | |
| 6 | +import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsRankService; | |
| 7 | 7 | import io.swagger.v3.oas.annotations.Operation; |
| 8 | 8 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 9 | 9 | import jakarta.annotation.Resource; |
| ... | ... | @@ -18,7 +18,6 @@ import java.util.List; |
| 18 | 18 | |
| 19 | 19 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 20 | 20 | |
| 21 | - | |
| 22 | 21 | @Tag(name = "管理后台 - CRM 排行榜统计") |
| 23 | 22 | @RestController |
| 24 | 23 | @RequestMapping("/crm/statistics-rank") |
| ... | ... | @@ -26,62 +25,62 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 26 | 25 | public class CrmStatisticsRankController { |
| 27 | 26 | |
| 28 | 27 | @Resource |
| 29 | - private CrmStatisticsRankingService rankingService; | |
| 28 | + private CrmStatisticsRankService rankService; | |
| 30 | 29 | |
| 31 | 30 | @GetMapping("/get-contract-price-rank") |
| 32 | 31 | @Operation(summary = "获得合同金额排行榜") |
| 33 | 32 | @PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')") |
| 34 | - public CommonResult<List<CrmStatisticsRanKRespVO>> getContractPriceRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { | |
| 35 | - return success(rankingService.getContractPriceRank(rankingReqVO)); | |
| 33 | + public CommonResult<List<CrmStatisticsRankRespVO>> getContractPriceRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { | |
| 34 | + return success(rankService.getContractPriceRank(rankingReqVO)); | |
| 36 | 35 | } |
| 37 | 36 | |
| 38 | 37 | @GetMapping("/get-receivable-price-rank") |
| 39 | 38 | @Operation(summary = "获得回款金额排行榜") |
| 40 | 39 | @PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')") |
| 41 | - public CommonResult<List<CrmStatisticsRanKRespVO>> getReceivablePriceRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { | |
| 42 | - return success(rankingService.getReceivablePriceRank(rankingReqVO)); | |
| 40 | + public CommonResult<List<CrmStatisticsRankRespVO>> getReceivablePriceRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { | |
| 41 | + return success(rankService.getReceivablePriceRank(rankingReqVO)); | |
| 43 | 42 | } |
| 44 | 43 | |
| 45 | 44 | @GetMapping("/get-contract-count-rank") |
| 46 | 45 | @Operation(summary = "获得签约合同数量排行榜") |
| 47 | 46 | @PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')") |
| 48 | - public CommonResult<List<CrmStatisticsRanKRespVO>> getContractCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { | |
| 49 | - return success(rankingService.getContractCountRank(rankingReqVO)); | |
| 47 | + public CommonResult<List<CrmStatisticsRankRespVO>> getContractCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { | |
| 48 | + return success(rankService.getContractCountRank(rankingReqVO)); | |
| 50 | 49 | } |
| 51 | 50 | |
| 52 | 51 | @GetMapping("/get-product-sales-rank") |
| 53 | 52 | @Operation(summary = "获得产品销量排行榜") |
| 54 | 53 | @PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')") |
| 55 | - public CommonResult<List<CrmStatisticsRanKRespVO>> getProductSalesRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { | |
| 56 | - return success(rankingService.getProductSalesRank(rankingReqVO)); | |
| 54 | + public CommonResult<List<CrmStatisticsRankRespVO>> getProductSalesRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { | |
| 55 | + return success(rankService.getProductSalesRank(rankingReqVO)); | |
| 57 | 56 | } |
| 58 | 57 | |
| 59 | 58 | @GetMapping("/get-customer-count-rank") |
| 60 | 59 | @Operation(summary = "获得新增客户数排行榜") |
| 61 | 60 | @PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')") |
| 62 | - public CommonResult<List<CrmStatisticsRanKRespVO>> getCustomerCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { | |
| 63 | - return success(rankingService.getCustomerCountRank(rankingReqVO)); | |
| 61 | + public CommonResult<List<CrmStatisticsRankRespVO>> getCustomerCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { | |
| 62 | + return success(rankService.getCustomerCountRank(rankingReqVO)); | |
| 64 | 63 | } |
| 65 | 64 | |
| 66 | 65 | @GetMapping("/get-contacts-count-rank") |
| 67 | 66 | @Operation(summary = "获得新增联系人数排行榜") |
| 68 | 67 | @PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')") |
| 69 | - public CommonResult<List<CrmStatisticsRanKRespVO>> getContactsCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { | |
| 70 | - return success(rankingService.getContactsCountRank(rankingReqVO)); | |
| 68 | + public CommonResult<List<CrmStatisticsRankRespVO>> getContactsCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { | |
| 69 | + return success(rankService.getContactsCountRank(rankingReqVO)); | |
| 71 | 70 | } |
| 72 | 71 | |
| 73 | 72 | @GetMapping("/get-follow-count-rank") |
| 74 | 73 | @Operation(summary = "获得跟进次数排行榜") |
| 75 | 74 | @PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')") |
| 76 | - public CommonResult<List<CrmStatisticsRanKRespVO>> getFollowCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { | |
| 77 | - return success(rankingService.getFollowCountRank(rankingReqVO)); | |
| 75 | + public CommonResult<List<CrmStatisticsRankRespVO>> getFollowCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { | |
| 76 | + return success(rankService.getFollowCountRank(rankingReqVO)); | |
| 78 | 77 | } |
| 79 | 78 | |
| 80 | 79 | @GetMapping("/get-follow-customer-count-rank") |
| 81 | 80 | @Operation(summary = "获得跟进客户数排行榜") |
| 82 | 81 | @PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')") |
| 83 | - public CommonResult<List<CrmStatisticsRanKRespVO>> getFollowCustomerCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { | |
| 84 | - return success(rankingService.getFollowCustomerCountRank(rankingReqVO)); | |
| 82 | + public CommonResult<List<CrmStatisticsRankRespVO>> getFollowCustomerCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { | |
| 83 | + return success(rankService.getFollowCustomerCountRank(rankingReqVO)); | |
| 85 | 84 | } |
| 86 | 85 | |
| 87 | 86 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerByUserBaseRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * 用户客户统计响应 Base Response VO | |
| 8 | + * | |
| 9 | + * 目的:可以统一拼接子 VO 的 ownerUserId、ownerUserName 属性 | |
| 10 | + */ | |
| 11 | +@Data | |
| 12 | +public class CrmStatisticsCustomerByUserBaseRespVO { | |
| 13 | + | |
| 14 | + @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 15 | + private Long ownerUserId; | |
| 16 | + | |
| 17 | + @Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") | |
| 18 | + private String ownerUserName; | |
| 19 | + | |
| 20 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerContractSummaryRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; | |
| 2 | + | |
| 3 | + | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import lombok.Data; | |
| 6 | + | |
| 7 | +import java.math.BigDecimal; | |
| 8 | +import java.time.LocalDateTime; | |
| 9 | + | |
| 10 | +@Schema(description = "管理后台 - CRM 客户转化率分析 VO") | |
| 11 | +@Data | |
| 12 | +public class CrmStatisticsCustomerContractSummaryRespVO { | |
| 13 | + | |
| 14 | + @Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") | |
| 15 | + private String customerName; | |
| 16 | + | |
| 17 | + @Schema(description = "合同名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "演示合同") | |
| 18 | + private String contractName; | |
| 19 | + | |
| 20 | + @Schema(description = "合同总金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1200.00") | |
| 21 | + private BigDecimal totalPrice; | |
| 22 | + | |
| 23 | + @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1200.00") | |
| 24 | + private BigDecimal receivablePrice; | |
| 25 | + | |
| 26 | + @Schema(description = "客户行业编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | |
| 27 | + private Integer industryId; | |
| 28 | + | |
| 29 | + @Schema(description = "客户来源编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 30 | + private Integer source; | |
| 31 | + | |
| 32 | + @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 33 | + private Long ownerUserId; | |
| 34 | + @Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") | |
| 35 | + private String ownerUserName; | |
| 36 | + | |
| 37 | + @Schema(description = "创建人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | |
| 38 | + private String creator; | |
| 39 | + @Schema(description = "创建人", requiredMode = Schema.RequiredMode.REQUIRED, example = "源码") | |
| 40 | + private String creatorUserName; | |
| 41 | + | |
| 42 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-01 13:24:26") | |
| 43 | + private LocalDateTime createTime; | |
| 44 | + | |
| 45 | + @Schema(description = "下单日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02 00:00:00") | |
| 46 | + private LocalDateTime orderDate; | |
| 47 | + | |
| 48 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByDateRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +@Schema(description = "管理后台 - CRM 客户成交周期分析(按日期) VO") | |
| 7 | +@Data | |
| 8 | +public class CrmStatisticsCustomerDealCycleByDateRespVO { | |
| 9 | + | |
| 10 | + @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401") | |
| 11 | + private String time; | |
| 12 | + | |
| 13 | + @Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0") | |
| 14 | + private Double customerDealCycle; | |
| 15 | + | |
| 16 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByUserRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +@Schema(description = "管理后台 - CRM 成交周期分析(按用户) VO") | |
| 7 | +@Data | |
| 8 | +public class CrmStatisticsCustomerDealCycleByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO { | |
| 9 | + | |
| 10 | + @Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0") | |
| 11 | + private Double customerDealCycle; | |
| 12 | + | |
| 13 | + @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 14 | + private Integer customerDealCount; | |
| 15 | + | |
| 16 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum; | |
| 4 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 6 | +import jakarta.validation.constraints.NotNull; | |
| 7 | +import jakarta.validation.constraints.Size; | |
| 8 | +import lombok.Data; | |
| 9 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 10 | + | |
| 11 | +import java.time.LocalDateTime; | |
| 12 | +import java.util.List; | |
| 13 | + | |
| 14 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 15 | + | |
| 16 | +@Schema(description = "管理后台 - CRM 数据统计的员工客户分析 Request VO") | |
| 17 | +@Data | |
| 18 | +public class CrmStatisticsCustomerReqVO { | |
| 19 | + | |
| 20 | + @Schema(description = "部门 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 21 | + @NotNull(message = "部门 id 不能为空") | |
| 22 | + private Long deptId; | |
| 23 | + | |
| 24 | + /** | |
| 25 | + * 负责人用户 id, 当用户为空, 则计算部门下用户 | |
| 26 | + */ | |
| 27 | + @Schema(description = "负责人用户 id", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1") | |
| 28 | + private Long userId; | |
| 29 | + | |
| 30 | + /** | |
| 31 | + * userIds 目前不用前端传递,目前是方便后端通过 deptId 读取编号后,设置回来 | |
| 32 | + * 后续,可能会支持选择部分用户进行查询 | |
| 33 | + */ | |
| 34 | + @Schema(description = "负责人用户 id 集合", hidden = true, example = "2") | |
| 35 | + private List<Long> userIds; | |
| 36 | + | |
| 37 | + @Schema(description = "时间间隔类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 38 | + @InEnum(value = DateIntervalEnum.class, message = "时间间隔类型,必须是 {value}") | |
| 39 | + private Integer interval; | |
| 40 | + | |
| 41 | + @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED) | |
| 42 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 43 | + @Size(min = 2, max = 2, message = "请选择时间范围") | |
| 44 | + private LocalDateTime[] times; | |
| 45 | + | |
| 46 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByDateRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +@Schema(description = "管理后台 - CRM 客户总量分析(按日期) VO") | |
| 7 | +@Data | |
| 8 | +public class CrmStatisticsCustomerSummaryByDateRespVO { | |
| 9 | + | |
| 10 | + @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401") | |
| 11 | + private String time; | |
| 12 | + | |
| 13 | + @Schema(description = "新建客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 14 | + private Integer customerCreateCount; | |
| 15 | + | |
| 16 | + @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 17 | + private Integer customerDealCount; | |
| 18 | + | |
| 19 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByUserRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.math.BigDecimal; | |
| 7 | + | |
| 8 | +@Schema(description = "管理后台 - CRM 客户总量分析(按用户) VO") | |
| 9 | +@Data | |
| 10 | +public class CrmStatisticsCustomerSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO { | |
| 11 | + | |
| 12 | + @Schema(description = "新建客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 13 | + private Integer customerCreateCount; | |
| 14 | + | |
| 15 | + @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 16 | + private Integer customerDealCount; | |
| 17 | + | |
| 18 | + @Schema(description = "合同总金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") | |
| 19 | + private BigDecimal contractPrice; | |
| 20 | + | |
| 21 | + @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") | |
| 22 | + private BigDecimal receivablePrice; | |
| 23 | + | |
| 24 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowUpSummaryByDateRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +@Schema(description = "管理后台 - CRM 跟进次数分析(按日期) VO") | |
| 7 | +@Data | |
| 8 | +public class CrmStatisticsFollowUpSummaryByDateRespVO { | |
| 9 | + | |
| 10 | + @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401") | |
| 11 | + private String time; | |
| 12 | + | |
| 13 | + @Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 14 | + private Integer followUpRecordCount; | |
| 15 | + | |
| 16 | + @Schema(description = "跟进客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 17 | + private Integer followUpCustomerCount; | |
| 18 | + | |
| 19 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowUpSummaryByTypeRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; | |
| 2 | + | |
| 3 | + | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import lombok.Data; | |
| 6 | + | |
| 7 | +@Schema(description = "管理后台 - CRM 跟进次数分析(按类型) VO") | |
| 8 | +@Data | |
| 9 | +public class CrmStatisticsFollowUpSummaryByTypeRespVO { | |
| 10 | + | |
| 11 | + @Schema(description = "跟进类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 12 | + private Integer followUpType; | |
| 13 | + | |
| 14 | + @Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 15 | + private Integer followUpRecordCount; | |
| 16 | + | |
| 17 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowUpSummaryByUserRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +@Schema(description = "管理后台 - CRM 跟进次数分析(按用户) VO") | |
| 7 | +@Data | |
| 8 | +public class CrmStatisticsFollowUpSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO { | |
| 9 | + | |
| 10 | + @Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 11 | + private Integer followUpRecordCount; | |
| 12 | + | |
| 13 | + @Schema(description = "跟进客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 14 | + private Integer followUpCustomerCount; | |
| 15 | + | |
| 16 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsPoolSummaryByDateRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +@Schema(description = "管理后台 - CRM 公海客户分析(按日期) VO") | |
| 7 | +@Data | |
| 8 | +public class CrmStatisticsPoolSummaryByDateRespVO { | |
| 9 | + | |
| 10 | + @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401") | |
| 11 | + private String time; | |
| 12 | + | |
| 13 | + @Schema(description = "进入公海客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 14 | + private Integer customerPutCount; | |
| 15 | + | |
| 16 | + @Schema(description = "公海领取客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 17 | + private Integer customerTakeCount; | |
| 18 | + | |
| 19 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsPoolSummaryByUserRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +@Schema(description = "管理后台 - CRM 公海客户分析(按用户) VO") | |
| 7 | +@Data | |
| 8 | +public class CrmStatisticsPoolSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO { | |
| 9 | + | |
| 10 | + @Schema(description = "进入公海客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 11 | + private Integer customerPutCount; | |
| 12 | + | |
| 13 | + @Schema(description = "公海领取客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 14 | + private Integer customerTakeCount; | |
| 15 | + | |
| 16 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/performance/CrmStatisticsPerformanceReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 6 | + | |
| 7 | +import jakarta.validation.constraints.NotEmpty; | |
| 8 | +import jakarta.validation.constraints.NotNull; | |
| 9 | +import java.time.LocalDateTime; | |
| 10 | +import java.util.List; | |
| 11 | + | |
| 12 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 13 | + | |
| 14 | +@Schema(description = "管理后台 - CRM 员工业绩统计 Request VO") | |
| 15 | +@Data | |
| 16 | +public class CrmStatisticsPerformanceReqVO { | |
| 17 | + | |
| 18 | + @Schema(description = "部门 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 19 | + @NotNull(message = "部门 id 不能为空") | |
| 20 | + private Long deptId; | |
| 21 | + | |
| 22 | + /** | |
| 23 | + * 负责人用户 id, 当用户为空, 则计算部门下用户 | |
| 24 | + */ | |
| 25 | + @Schema(description = "负责人用户 id", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1") | |
| 26 | + private Long userId; | |
| 27 | + | |
| 28 | + /** | |
| 29 | + * userIds 目前不用前端传递,目前是方便后端通过 deptId 读取编号后,设置回来 | |
| 30 | + * <p> | |
| 31 | + * 后续,可能会支持选择部分用户进行查询 | |
| 32 | + */ | |
| 33 | + @Schema(description = "负责人用户 id 集合", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2") | |
| 34 | + private List<Long> userIds; | |
| 35 | + | |
| 36 | + // TODO @scholar:应该传递的是 int year;年份 | |
| 37 | + @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 38 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 39 | + @NotEmpty(message = "时间范围不能为空") | |
| 40 | + private LocalDateTime[] times; | |
| 41 | + | |
| 42 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/performance/CrmStatisticsPerformanceRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import java.math.BigDecimal; | |
| 6 | + | |
| 7 | + | |
| 8 | +@Schema(description = "管理后台 - CRM 员工业绩统计 Response VO") | |
| 9 | +@Data | |
| 10 | +public class CrmStatisticsPerformanceRespVO { | |
| 11 | + | |
| 12 | + @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401") | |
| 13 | + private String time; | |
| 14 | + | |
| 15 | + @Schema(description = "当月统计结果", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 16 | + private BigDecimal currentMonthCount; | |
| 17 | + | |
| 18 | + @Schema(description = "上月统计结果", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | |
| 19 | + private BigDecimal lastMonthCount; | |
| 20 | + | |
| 21 | + @Schema(description = "去年同期统计结果", requiredMode = Schema.RequiredMode.REQUIRED, example = "3") | |
| 22 | + private BigDecimal lastYearCount; | |
| 23 | + | |
| 24 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerAreaRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +@Schema(description = "管理后台 - CRM 客户省份分析 VO") | |
| 7 | +@Data | |
| 8 | +public class CrmStatisticCustomerAreaRespVO { | |
| 9 | + | |
| 10 | + @Schema(description = "省份编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 11 | + private Integer areaId; | |
| 12 | + @Schema(description = "省份名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "浙江省") | |
| 13 | + private String areaName; | |
| 14 | + | |
| 15 | + @Schema(description = "客户个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 16 | + private Integer customerCount; | |
| 17 | + | |
| 18 | + @Schema(description = "成交个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 19 | + private Integer dealCount; | |
| 20 | + | |
| 21 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerIndustryRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +@Schema(description = "管理后台 - CRM 客户行业分析 VO") | |
| 7 | +@Data | |
| 8 | +public class CrmStatisticCustomerIndustryRespVO { | |
| 9 | + | |
| 10 | + @Schema(description = "客户行业ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | |
| 11 | + private Integer industryId; | |
| 12 | + | |
| 13 | + @Schema(description = "客户个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 14 | + private Integer customerCount; | |
| 15 | + | |
| 16 | + @Schema(description = "成交个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 17 | + private Integer dealCount; | |
| 18 | + | |
| 19 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerLevelRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +@Schema(description = "管理后台 - CRM 客户级别分析 VO") | |
| 7 | +@Data | |
| 8 | +public class CrmStatisticCustomerLevelRespVO { | |
| 9 | + | |
| 10 | + @Schema(description = "客户级别编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | |
| 11 | + private Integer level; | |
| 12 | + | |
| 13 | + @Schema(description = "客户个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 14 | + private Integer customerCount; | |
| 15 | + | |
| 16 | + @Schema(description = "成交个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 17 | + private Integer dealCount; | |
| 18 | + | |
| 19 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerSourceRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +@Schema(description = "管理后台 - CRM 客户来源分析 VO") | |
| 7 | +@Data | |
| 8 | +public class CrmStatisticCustomerSourceRespVO { | |
| 9 | + | |
| 10 | + @Schema(description = "客户来源编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | |
| 11 | + private Integer source; | |
| 12 | + | |
| 13 | + @Schema(description = "客户个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 14 | + private Integer customerCount; | |
| 15 | + | |
| 16 | + @Schema(description = "成交个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 17 | + private Integer dealCount; | |
| 18 | + | |
| 19 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticsPortraitReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import jakarta.validation.constraints.NotNull; | |
| 5 | +import lombok.Data; | |
| 6 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 7 | + | |
| 8 | +import java.time.LocalDateTime; | |
| 9 | +import java.util.List; | |
| 10 | + | |
| 11 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 12 | + | |
| 13 | +@Schema(description = "管理后台 - CRM 客户画像 Request VO") | |
| 14 | +@Data | |
| 15 | +public class CrmStatisticsPortraitReqVO { | |
| 16 | + | |
| 17 | + @Schema(description = "部门 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 18 | + @NotNull(message = "部门 id 不能为空") | |
| 19 | + private Long deptId; | |
| 20 | + | |
| 21 | + /** | |
| 22 | + * 负责人用户 id, 当用户为空, 则计算部门下用户 | |
| 23 | + */ | |
| 24 | + @Schema(description = "负责人用户 id", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1") | |
| 25 | + private Long userId; | |
| 26 | + | |
| 27 | + /** | |
| 28 | + * userIds 目前不用前端传递,目前是方便后端通过 deptId 读取编号后,设置回来 | |
| 29 | + * 后续,可能会支持选择部分用户进行查询 | |
| 30 | + */ | |
| 31 | + @Schema(description = "负责人用户 id 集合", hidden = true, example = "2") | |
| 32 | + private List<Long> userIds; | |
| 33 | + | |
| 34 | + /** | |
| 35 | + * 前端如果选择自定义时间, 那么前端传递起始-终止时间, 如果选择其他时间间隔类型, 则由后台计算起始-终止时间 | |
| 36 | + * 并作为参数传递给Mapper | |
| 37 | + */ | |
| 38 | + @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED) | |
| 39 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 40 | + private LocalDateTime[] times; | |
| 41 | + | |
| 42 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/CrmStatisticsRankReqVO.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankReqVO.java
| 1 | -package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo; | |
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank; | |
| 2 | 2 | |
| 3 | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | 4 | import jakarta.validation.constraints.NotEmpty; |
| ... | ... | @@ -21,7 +21,7 @@ public class CrmStatisticsRankReqVO { |
| 21 | 21 | |
| 22 | 22 | /** |
| 23 | 23 | * userIds 目前不用前端传递,目前是方便后端通过 deptId 读取编号后,设置回来 |
| 24 | - * | |
| 24 | + * <p> | |
| 25 | 25 | * 后续,可能会支持选择部分用户进行查询 |
| 26 | 26 | */ |
| 27 | 27 | @Schema(description = "负责人用户 id 集合", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2") | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/CrmStatisticsRanKRespVO.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankRespVO.java
| 1 | -package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo; | |
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank; | |
| 2 | 2 | |
| 3 | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | 4 | import lombok.Data; |
| 5 | 5 | |
| 6 | +import java.math.BigDecimal; | |
| 6 | 7 | |
| 7 | -@Schema(description = "管理后台 - CRM BI 排行榜统计 Response VO") | |
| 8 | + | |
| 9 | +@Schema(description = "管理后台 - CRM 排行榜统计 Response VO") | |
| 8 | 10 | @Data |
| 9 | -public class CrmStatisticsRanKRespVO { | |
| 11 | +public class CrmStatisticsRankRespVO { | |
| 10 | 12 | |
| 11 | 13 | @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 12 | 14 | private Long ownerUserId; |
| ... | ... | @@ -22,8 +24,10 @@ public class CrmStatisticsRanKRespVO { |
| 22 | 24 | * |
| 23 | 25 | * 1. 金额:合同金额排行、回款金额排行 |
| 24 | 26 | * 2. 个数:签约合同排行、产品销量排行、产品销量排行、新增客户数排行、新增联系人排行、跟进次数排行、跟进客户数排行 |
| 27 | + * | |
| 28 | + * 为什么使用 BigDecimal 的原因: | |
| 25 | 29 | */ |
| 26 | 30 | @Schema(description = "数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 27 | - private Integer count; | |
| 31 | + private BigDecimal count; | |
| 28 | 32 | |
| 29 | 33 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java
| ... | ... | @@ -6,12 +6,14 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 6 | 6 | import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; |
| 7 | 7 | import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO; |
| 8 | 8 | import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; |
| 9 | +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; | |
| 9 | 10 | import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; |
| 10 | 11 | import cn.iocoder.yudao.module.crm.util.CrmPermissionUtils; |
| 11 | 12 | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| 12 | 13 | import org.apache.ibatis.annotations.Mapper; |
| 13 | 14 | |
| 14 | 15 | import java.util.Collection; |
| 16 | +import java.util.List; | |
| 15 | 17 | |
| 16 | 18 | /** |
| 17 | 19 | * 商机 Mapper |
| ... | ... | @@ -57,4 +59,10 @@ public interface CrmBusinessMapper extends BaseMapperX<CrmBusinessDO> { |
| 57 | 59 | return selectCount(CrmBusinessDO::getStatusTypeId, statusTypeId); |
| 58 | 60 | } |
| 59 | 61 | |
| 62 | + default List<CrmBusinessDO> selectListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId){ | |
| 63 | + return selectList(new LambdaQueryWrapperX<CrmBusinessDO>() | |
| 64 | + .eq(CrmBusinessDO::getCustomerId, customerId) | |
| 65 | + .eq(CrmBusinessDO::getOwnerUserId, ownerUserId)); | |
| 66 | + } | |
| 67 | + | |
| 60 | 68 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java
| ... | ... | @@ -73,4 +73,9 @@ public interface CrmContactMapper extends BaseMapperX<CrmContactDO> { |
| 73 | 73 | return selectList(CrmContactDO::getCustomerId, customerId); |
| 74 | 74 | } |
| 75 | 75 | |
| 76 | + default List<CrmContactDO> selectListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) { | |
| 77 | + return selectList(CrmContactDO::getCustomerId, customerId, | |
| 78 | + CrmContactDO::getOwnerUserId, ownerUserId); | |
| 79 | + } | |
| 80 | + | |
| 76 | 81 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java
| ... | ... | @@ -117,4 +117,10 @@ public interface CrmContractMapper extends BaseMapperX<CrmContractDO> { |
| 117 | 117 | return selectCount(query); |
| 118 | 118 | } |
| 119 | 119 | |
| 120 | + default List<CrmContractDO> selectListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) { | |
| 121 | + return selectList(new LambdaQueryWrapperX<CrmContractDO>() | |
| 122 | + .eq(CrmContractDO::getCustomerId, customerId) | |
| 123 | + .eq(CrmContractDO::getOwnerUserId, ownerUserId)); | |
| 124 | + } | |
| 125 | + | |
| 120 | 126 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/permission/CrmPermissionMapper.java
| ... | ... | @@ -53,9 +53,11 @@ public interface CrmPermissionMapper extends BaseMapperX<CrmPermissionDO> { |
| 53 | 53 | CrmPermissionDO::getUserId, userId); |
| 54 | 54 | } |
| 55 | 55 | |
| 56 | - default CrmPermissionDO selectByBizIdAndUserId(Long bizId, Long userId) { | |
| 57 | - return selectOne(CrmPermissionDO::getBizId, bizId, | |
| 58 | - CrmPermissionDO::getUserId, userId); | |
| 56 | + default CrmPermissionDO selectByBizAndUserId(Integer bizType, Long bizId, Long userId) { | |
| 57 | + return selectOne(new LambdaQueryWrapperX<CrmPermissionDO>() | |
| 58 | + .eq(CrmPermissionDO::getBizType, bizType) | |
| 59 | + .eq(CrmPermissionDO::getBizId, bizId) | |
| 60 | + .eq(CrmPermissionDO::getUserId, userId)); | |
| 59 | 61 | } |
| 60 | 62 | |
| 61 | 63 | default int deletePermission(Integer bizType, Long bizId) { | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java
| ... | ... | @@ -99,4 +99,8 @@ public interface CrmReceivableMapper extends BaseMapperX<CrmReceivableDO> { |
| 99 | 99 | return convertMap(result, obj -> (Long) obj.get("contract_id"), obj -> (BigDecimal) obj.get("total_price")); |
| 100 | 100 | } |
| 101 | 101 | |
| 102 | + default Long selectCountByContractId(Long contractId) { | |
| 103 | + return selectCount(CrmReceivableDO::getContractId, contractId); | |
| 104 | + } | |
| 105 | + | |
| 102 | 106 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.dal.mysql.statistics; | |
| 2 | + | |
| 3 | +import cn.hutool.core.date.LocalDateTimeUtil; | |
| 4 | +import cn.hutool.core.util.RandomUtil; | |
| 5 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; | |
| 6 | +import org.apache.ibatis.annotations.Mapper; | |
| 7 | + | |
| 8 | +import java.time.LocalDateTime; | |
| 9 | +import java.util.ArrayList; | |
| 10 | +import java.util.List; | |
| 11 | + | |
| 12 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 13 | + | |
| 14 | +/** | |
| 15 | + * CRM 客户分析 Mapper | |
| 16 | + * | |
| 17 | + * @author dhb52 | |
| 18 | + */ | |
| 19 | +@Mapper | |
| 20 | +public interface CrmStatisticsCustomerMapper { | |
| 21 | + | |
| 22 | + /** | |
| 23 | + * 新建客户数(按日期) | |
| 24 | + * | |
| 25 | + * @param reqVO 请求参数 | |
| 26 | + * @return 统计数据 | |
| 27 | + */ | |
| 28 | + List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerCreateCountGroupByDate(CrmStatisticsCustomerReqVO reqVO); | |
| 29 | + | |
| 30 | + /** | |
| 31 | + * 成交客户数(按日期) | |
| 32 | + * | |
| 33 | + * @param reqVO 请求参数 | |
| 34 | + * @return 统计数据 | |
| 35 | + */ | |
| 36 | + List<CrmStatisticsCustomerSummaryByDateRespVO> selectCustomerDealCountGroupByDate(CrmStatisticsCustomerReqVO reqVO); | |
| 37 | + | |
| 38 | + /** | |
| 39 | + * 新建客户数(按用户) | |
| 40 | + * | |
| 41 | + * @param reqVO 请求参数 | |
| 42 | + * @return 统计数据 | |
| 43 | + */ | |
| 44 | + List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerCreateCountGroupByUser(CrmStatisticsCustomerReqVO reqVO); | |
| 45 | + | |
| 46 | + /** | |
| 47 | + * 成交客户数(按用户) | |
| 48 | + * | |
| 49 | + * @param reqVO 请求参数@param reqVO 请求参数@param reqVO 请求参数 | |
| 50 | + * @return 统计数据 | |
| 51 | + */ | |
| 52 | + List<CrmStatisticsCustomerSummaryByUserRespVO> selectCustomerDealCountGroupByUser(CrmStatisticsCustomerReqVO reqVO); | |
| 53 | + | |
| 54 | + /** | |
| 55 | + * 合同总金额(按用户) | |
| 56 | + * @return 统计数据@return 统计数据@param reqVO 请求参数 | |
| 57 | + * @return 统计数据 | |
| 58 | + */ | |
| 59 | + List<CrmStatisticsCustomerSummaryByUserRespVO> selectContractPriceGroupByUser(CrmStatisticsCustomerReqVO reqVO); | |
| 60 | + | |
| 61 | + /** | |
| 62 | + * 合同回款金额(按用户) | |
| 63 | + * | |
| 64 | + * @param reqVO 请求参数 | |
| 65 | + * @return 统计数据 | |
| 66 | + */ | |
| 67 | + List<CrmStatisticsCustomerSummaryByUserRespVO> selectReceivablePriceGroupByUser(CrmStatisticsCustomerReqVO reqVO); | |
| 68 | + | |
| 69 | + /** | |
| 70 | + * 跟进次数(按日期) | |
| 71 | + * | |
| 72 | + * @param reqVO 请求参数 | |
| 73 | + * @return 统计数据 | |
| 74 | + */ | |
| 75 | + List<CrmStatisticsFollowUpSummaryByDateRespVO> selectFollowUpRecordCountGroupByDate(CrmStatisticsCustomerReqVO reqVO); | |
| 76 | + | |
| 77 | + /** | |
| 78 | + * 跟进客户数(按日期) | |
| 79 | + * | |
| 80 | + * @param reqVO 请求参数 | |
| 81 | + * @return 统计数据 | |
| 82 | + */ | |
| 83 | + List<CrmStatisticsFollowUpSummaryByDateRespVO> selectFollowUpCustomerCountGroupByDate(CrmStatisticsCustomerReqVO reqVO); | |
| 84 | + | |
| 85 | + /** | |
| 86 | + * 跟进次数(按用户) | |
| 87 | + * | |
| 88 | + * @param reqVO 请求参数 | |
| 89 | + * @return 统计数据 | |
| 90 | + */ | |
| 91 | + List<CrmStatisticsFollowUpSummaryByUserRespVO> selectFollowUpRecordCountGroupByUser(CrmStatisticsCustomerReqVO reqVO); | |
| 92 | + | |
| 93 | + /** | |
| 94 | + * 跟进客户数(按用户) | |
| 95 | + * | |
| 96 | + * @param reqVO 请求参数 | |
| 97 | + * @return 统计数据 | |
| 98 | + */ | |
| 99 | + List<CrmStatisticsFollowUpSummaryByUserRespVO> selectFollowUpCustomerCountGroupByUser(CrmStatisticsCustomerReqVO reqVO); | |
| 100 | + | |
| 101 | + | |
| 102 | + /** | |
| 103 | + * 首次合同、回款信息(用于【客户转化率】页面) | |
| 104 | + * | |
| 105 | + * @param reqVO 请求参数 | |
| 106 | + * @return 统计数据 | |
| 107 | + */ | |
| 108 | + List<CrmStatisticsCustomerContractSummaryRespVO> selectContractSummary(CrmStatisticsCustomerReqVO reqVO); | |
| 109 | + | |
| 110 | + /** | |
| 111 | + * 跟进次数(按类型) | |
| 112 | + * | |
| 113 | + * @param reqVO 请求参数 | |
| 114 | + * @return 统计数据 | |
| 115 | + */ | |
| 116 | + List<CrmStatisticsFollowUpSummaryByTypeRespVO> selectFollowUpRecordCountGroupByType(CrmStatisticsCustomerReqVO reqVO); | |
| 117 | + | |
| 118 | + | |
| 119 | + /** | |
| 120 | + * 进入公海客户数(按日期) | |
| 121 | + * | |
| 122 | + * @param reqVO 请求参数 | |
| 123 | + * @return 统计数据 | |
| 124 | + */ | |
| 125 | + // TODO: @芋艿 模拟数据, 需要增加 crm_owner_record 表 | |
| 126 | + default List<CrmStatisticsPoolSummaryByDateRespVO> selectPoolCustomerPutCountByDate(CrmStatisticsCustomerReqVO reqVO) { | |
| 127 | + LocalDateTime currrentDate = LocalDateTimeUtil.beginOfDay(reqVO.getTimes()[0]); | |
| 128 | + LocalDateTime endDate = LocalDateTimeUtil.endOfDay(reqVO.getTimes()[1]); | |
| 129 | + List<CrmStatisticsPoolSummaryByDateRespVO> voList = new ArrayList<>(); | |
| 130 | + while (currrentDate.isBefore(endDate)) { | |
| 131 | + voList.add(new CrmStatisticsPoolSummaryByDateRespVO() | |
| 132 | + .setTime(LocalDateTimeUtil.format(currrentDate, "yyyy-MM-dd")) | |
| 133 | + .setCustomerPutCount(RandomUtil.randomInt(0, 10)) | |
| 134 | + .setCustomerTakeCount(RandomUtil.randomInt(0, 10))); | |
| 135 | + currrentDate = currrentDate.plusDays(1); | |
| 136 | + } | |
| 137 | + | |
| 138 | + return voList; | |
| 139 | + } | |
| 140 | + | |
| 141 | + /** | |
| 142 | + * 公海领取客户数(按日期) | |
| 143 | + * | |
| 144 | + * @param reqVO 请求参数 | |
| 145 | + * @return 统计数据 | |
| 146 | + */ | |
| 147 | + // TODO: @芋艿 模拟数据, 需要增加 crm_owner_record 表 | |
| 148 | + default List<CrmStatisticsPoolSummaryByDateRespVO> selectPoolCustomerTakeCountByDate(CrmStatisticsCustomerReqVO reqVO) { | |
| 149 | + return selectPoolCustomerPutCountByDate(reqVO); | |
| 150 | + } | |
| 151 | + | |
| 152 | + /** | |
| 153 | + * 进入公海客户数(按用户) | |
| 154 | + * | |
| 155 | + * @param reqVO 请求参数 | |
| 156 | + * @return 统计数据 | |
| 157 | + */ | |
| 158 | + // TODO: @芋艿 模拟数据, 需要增加 crm_owner_record 表 | |
| 159 | + default List<CrmStatisticsPoolSummaryByUserRespVO> selectPoolCustomerPutCountByUser(CrmStatisticsCustomerReqVO reqVO) { | |
| 160 | + return convertList(reqVO.getUserIds(), userId -> | |
| 161 | + (CrmStatisticsPoolSummaryByUserRespVO) new CrmStatisticsPoolSummaryByUserRespVO() | |
| 162 | + .setCustomerPutCount(RandomUtil.randomInt(0, 10)) | |
| 163 | + .setCustomerTakeCount(RandomUtil.randomInt(0, 10)) | |
| 164 | + .setOwnerUserId(userId)); | |
| 165 | + } | |
| 166 | + | |
| 167 | + /** | |
| 168 | + * 公海领取客户数(按用户) | |
| 169 | + * | |
| 170 | + * @param reqVO 请求参数 | |
| 171 | + * @return 统计数据 | |
| 172 | + */ | |
| 173 | + // TODO: @芋艿 模拟数据, 需要增加 crm_owner_record 表 | |
| 174 | + default List<CrmStatisticsPoolSummaryByUserRespVO> selectPoolCustomerTakeCountByUser(CrmStatisticsCustomerReqVO reqVO) { | |
| 175 | + return selectPoolCustomerPutCountByUser(reqVO); | |
| 176 | + } | |
| 177 | + | |
| 178 | + /** | |
| 179 | + * 客户成交周期(按日期) | |
| 180 | + * | |
| 181 | + * @param reqVO 请求参数 | |
| 182 | + * @return 统计数据 | |
| 183 | + */ | |
| 184 | + List<CrmStatisticsCustomerDealCycleByDateRespVO> selectCustomerDealCycleGroupByDate(CrmStatisticsCustomerReqVO reqVO); | |
| 185 | + | |
| 186 | + /** | |
| 187 | + * 客户成交周期(按用户) | |
| 188 | + * | |
| 189 | + * @param reqVO 请求参数 | |
| 190 | + * @return 统计数据 | |
| 191 | + */ | |
| 192 | + List<CrmStatisticsCustomerDealCycleByUserRespVO> selectCustomerDealCycleGroupByUser(CrmStatisticsCustomerReqVO reqVO); | |
| 193 | + | |
| 194 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsPerformanceMapper.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.dal.mysql.statistics; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceReqVO; | |
| 4 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO; | |
| 5 | +import org.apache.ibatis.annotations.Mapper; | |
| 6 | + | |
| 7 | +import java.util.List; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * CRM 员工业绩分析 Mapper | |
| 11 | + * | |
| 12 | + * @author scholar | |
| 13 | + */ | |
| 14 | +@Mapper | |
| 15 | +public interface CrmStatisticsPerformanceMapper { | |
| 16 | + | |
| 17 | + /** | |
| 18 | + * 员工签约合同数量 | |
| 19 | + * | |
| 20 | + * @param performanceReqVO 参数 | |
| 21 | + * @return 员工签约合同数量 | |
| 22 | + */ | |
| 23 | + List<CrmStatisticsPerformanceRespVO> selectContractCountPerformance(CrmStatisticsPerformanceReqVO performanceReqVO); | |
| 24 | + | |
| 25 | + /** | |
| 26 | + * 员工签约合同金额 | |
| 27 | + * | |
| 28 | + * @param performanceReqVO 参数 | |
| 29 | + * @return 员工签约合同金额 | |
| 30 | + */ | |
| 31 | + List<CrmStatisticsPerformanceRespVO> selectContractPricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO); | |
| 32 | + | |
| 33 | + /** | |
| 34 | + * 员工回款金额 | |
| 35 | + * | |
| 36 | + * @param performanceReqVO 参数 | |
| 37 | + * @return 员工回款金额 | |
| 38 | + */ | |
| 39 | + List<CrmStatisticsPerformanceRespVO> selectReceivablePricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO); | |
| 40 | + | |
| 41 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsPortraitMapper.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.dal.mysql.statistics; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.*; | |
| 4 | +import org.apache.ibatis.annotations.Mapper; | |
| 5 | + | |
| 6 | +import java.util.List; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * CRM 数据画像 Mapper | |
| 10 | + * | |
| 11 | + * @author HUIHUI | |
| 12 | + */ | |
| 13 | +@Mapper | |
| 14 | +public interface CrmStatisticsPortraitMapper { | |
| 15 | + | |
| 16 | + List<CrmStatisticCustomerAreaRespVO> selectSummaryListGroupByAreaId(CrmStatisticsPortraitReqVO reqVO); | |
| 17 | + | |
| 18 | + List<CrmStatisticCustomerIndustryRespVO> selectCustomerIndustryListGroupByIndustryId(CrmStatisticsPortraitReqVO reqVO); | |
| 19 | + | |
| 20 | + List<CrmStatisticCustomerSourceRespVO> selectCustomerSourceListGroupBySource(CrmStatisticsPortraitReqVO reqVO); | |
| 21 | + | |
| 22 | + List<CrmStatisticCustomerLevelRespVO> selectCustomerLevelListGroupByLevel(CrmStatisticsPortraitReqVO reqVO); | |
| 23 | + | |
| 24 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsRankingMapper.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsRankMapper.java
| 1 | 1 | package cn.iocoder.yudao.module.crm.dal.mysql.statistics; |
| 2 | 2 | |
| 3 | -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO; | |
| 4 | -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRankReqVO; | |
| 3 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO; | |
| 4 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankReqVO; | |
| 5 | 5 | import org.apache.ibatis.annotations.Mapper; |
| 6 | 6 | |
| 7 | 7 | import java.util.List; |
| ... | ... | @@ -12,7 +12,7 @@ import java.util.List; |
| 12 | 12 | * @author anhaohao |
| 13 | 13 | */ |
| 14 | 14 | @Mapper |
| 15 | -public interface CrmStatisticsRankingMapper { | |
| 15 | +public interface CrmStatisticsRankMapper { | |
| 16 | 16 | |
| 17 | 17 | /** |
| 18 | 18 | * 查询合同金额排行榜 |
| ... | ... | @@ -20,7 +20,7 @@ public interface CrmStatisticsRankingMapper { |
| 20 | 20 | * @param rankReqVO 参数 |
| 21 | 21 | * @return 合同金额排行榜 |
| 22 | 22 | */ |
| 23 | - List<CrmStatisticsRanKRespVO> selectContractPriceRank(CrmStatisticsRankReqVO rankReqVO); | |
| 23 | + List<CrmStatisticsRankRespVO> selectContractPriceRank(CrmStatisticsRankReqVO rankReqVO); | |
| 24 | 24 | |
| 25 | 25 | /** |
| 26 | 26 | * 查询回款金额排行榜 |
| ... | ... | @@ -28,7 +28,7 @@ public interface CrmStatisticsRankingMapper { |
| 28 | 28 | * @param rankReqVO 参数 |
| 29 | 29 | * @return 回款金额排行榜 |
| 30 | 30 | */ |
| 31 | - List<CrmStatisticsRanKRespVO> selectReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO); | |
| 31 | + List<CrmStatisticsRankRespVO> selectReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO); | |
| 32 | 32 | |
| 33 | 33 | /** |
| 34 | 34 | * 查询签约合同数量排行榜 |
| ... | ... | @@ -36,7 +36,7 @@ public interface CrmStatisticsRankingMapper { |
| 36 | 36 | * @param rankReqVO 参数 |
| 37 | 37 | * @return 签约合同数量排行榜 |
| 38 | 38 | */ |
| 39 | - List<CrmStatisticsRanKRespVO> selectContractCountRank(CrmStatisticsRankReqVO rankReqVO); | |
| 39 | + List<CrmStatisticsRankRespVO> selectContractCountRank(CrmStatisticsRankReqVO rankReqVO); | |
| 40 | 40 | |
| 41 | 41 | /** |
| 42 | 42 | * 查询产品销量排行榜 |
| ... | ... | @@ -44,7 +44,7 @@ public interface CrmStatisticsRankingMapper { |
| 44 | 44 | * @param rankReqVO 参数 |
| 45 | 45 | * @return 产品销量排行榜 |
| 46 | 46 | */ |
| 47 | - List<CrmStatisticsRanKRespVO> selectProductSalesRank(CrmStatisticsRankReqVO rankReqVO); | |
| 47 | + List<CrmStatisticsRankRespVO> selectProductSalesRank(CrmStatisticsRankReqVO rankReqVO); | |
| 48 | 48 | |
| 49 | 49 | /** |
| 50 | 50 | * 查询新增客户数排行榜 |
| ... | ... | @@ -52,7 +52,7 @@ public interface CrmStatisticsRankingMapper { |
| 52 | 52 | * @param rankReqVO 参数 |
| 53 | 53 | * @return 新增客户数排行榜 |
| 54 | 54 | */ |
| 55 | - List<CrmStatisticsRanKRespVO> selectCustomerCountRank(CrmStatisticsRankReqVO rankReqVO); | |
| 55 | + List<CrmStatisticsRankRespVO> selectCustomerCountRank(CrmStatisticsRankReqVO rankReqVO); | |
| 56 | 56 | |
| 57 | 57 | /** |
| 58 | 58 | * 查询联系人数量排行榜 |
| ... | ... | @@ -60,7 +60,7 @@ public interface CrmStatisticsRankingMapper { |
| 60 | 60 | * @param rankReqVO 参数 |
| 61 | 61 | * @return 联系人数量排行榜 |
| 62 | 62 | */ |
| 63 | - List<CrmStatisticsRanKRespVO> selectContactsCountRank(CrmStatisticsRankReqVO rankReqVO); | |
| 63 | + List<CrmStatisticsRankRespVO> selectContactsCountRank(CrmStatisticsRankReqVO rankReqVO); | |
| 64 | 64 | |
| 65 | 65 | /** |
| 66 | 66 | * 查询跟进次数排行榜 |
| ... | ... | @@ -68,7 +68,7 @@ public interface CrmStatisticsRankingMapper { |
| 68 | 68 | * @param rankReqVO 参数 |
| 69 | 69 | * @return 跟进次数排行榜 |
| 70 | 70 | */ |
| 71 | - List<CrmStatisticsRanKRespVO> selectFollowCountRank(CrmStatisticsRankReqVO rankReqVO); | |
| 71 | + List<CrmStatisticsRankRespVO> selectFollowCountRank(CrmStatisticsRankReqVO rankReqVO); | |
| 72 | 72 | |
| 73 | 73 | /** |
| 74 | 74 | * 查询跟进客户数排行榜 |
| ... | ... | @@ -76,6 +76,6 @@ public interface CrmStatisticsRankingMapper { |
| 76 | 76 | * @param rankReqVO 参数 |
| 77 | 77 | * @return 跟进客户数排行榜 |
| 78 | 78 | */ |
| 79 | - List<CrmStatisticsRanKRespVO> selectFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO); | |
| 79 | + List<CrmStatisticsRankRespVO> selectFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO); | |
| 80 | 80 | |
| 81 | 81 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/core/AreaExcelColumnSelectFunction.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.framework.excel.core; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.excel.core.function.ExcelColumnSelectFunction; | |
| 4 | +import cn.iocoder.yudao.framework.ip.core.Area; | |
| 5 | +import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; | |
| 6 | +import org.springframework.stereotype.Service; | |
| 7 | + | |
| 8 | +import java.util.List; | |
| 9 | + | |
| 10 | +/** | |
| 11 | + * 地区下拉框数据源的 {@link ExcelColumnSelectFunction} 实现类 | |
| 12 | + * | |
| 13 | + * @author HUIHUI | |
| 14 | + */ | |
| 15 | +@Service | |
| 16 | +public class AreaExcelColumnSelectFunction implements ExcelColumnSelectFunction { | |
| 17 | + | |
| 18 | + public static final String NAME = "getCrmAreaNameList"; // 防止和别的模块重名 | |
| 19 | + | |
| 20 | + @Override | |
| 21 | + public String getName() { | |
| 22 | + return NAME; | |
| 23 | + } | |
| 24 | + | |
| 25 | + @Override | |
| 26 | + public List<String> getOptions() { | |
| 27 | + // 获取地区下拉数据 | |
| 28 | + // TODO @puhui999:嘿嘿,这里改成省份、城市、区域,三个选项,难度大么? | |
| 29 | + Area area = AreaUtils.getArea(Area.ID_CHINA); | |
| 30 | + return AreaUtils.getAreaNodePathList(area.getChildren()); | |
| 31 | + } | |
| 32 | + | |
| 33 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/package-info.java
0 → 100644
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/package-info.java
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/package-info.java
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/web/package-info.java
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java
| ... | ... | @@ -46,8 +46,8 @@ public interface CrmBusinessService { |
| 46 | 46 | /** |
| 47 | 47 | * 更新商机相关跟进信息 |
| 48 | 48 | * |
| 49 | - * @param id 编号 | |
| 50 | - * @param contactNextTime 下次联系时间 | |
| 49 | + * @param id 编号 | |
| 50 | + * @param contactNextTime 下次联系时间 | |
| 51 | 51 | * @param contactLastContent 最后联系内容 |
| 52 | 52 | */ |
| 53 | 53 | void updateBusinessFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent); |
| ... | ... | @@ -55,7 +55,7 @@ public interface CrmBusinessService { |
| 55 | 55 | /** |
| 56 | 56 | * 更新商机的下次联系时间 |
| 57 | 57 | * |
| 58 | - * @param ids 编号数组 | |
| 58 | + * @param ids 编号数组 | |
| 59 | 59 | * @param contactNextTime 下次联系时间 |
| 60 | 60 | */ |
| 61 | 61 | void updateBusinessContactNextTime(Collection<Long> ids, LocalDateTime contactNextTime); |
| ... | ... | @@ -185,4 +185,13 @@ public interface CrmBusinessService { |
| 185 | 185 | return status.getName(); |
| 186 | 186 | } |
| 187 | 187 | |
| 188 | + /** | |
| 189 | + * 获得商机列表 | |
| 190 | + * | |
| 191 | + * @param customerId 客户编号 | |
| 192 | + * @param ownerUserId 负责人编号 | |
| 193 | + * @return 商机列表 | |
| 194 | + */ | |
| 195 | + List<CrmBusinessDO> getBusinessListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId); | |
| 196 | + | |
| 188 | 197 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java
| ... | ... | @@ -88,7 +88,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { |
| 88 | 88 | success = CRM_BUSINESS_CREATE_SUCCESS) |
| 89 | 89 | public Long createBusiness(CrmBusinessSaveReqVO createReqVO, Long userId) { |
| 90 | 90 | // 1.1 校验产品项的有效性 |
| 91 | - List<CrmBusinessProductDO> businessProducts = validateBusinessProducts(createReqVO.getProducts()); | |
| 91 | + List<CrmBusinessProductDO> businessProducts = validateBusinessProducts(createReqVO.getBusinessProducts()); | |
| 92 | 92 | // 1.2 校验关联字段 |
| 93 | 93 | validateRelationDataExists(createReqVO); |
| 94 | 94 | |
| ... | ... | @@ -129,7 +129,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { |
| 129 | 129 | // 1.1 校验存在 |
| 130 | 130 | CrmBusinessDO oldBusiness = validateBusinessExists(updateReqVO.getId()); |
| 131 | 131 | // 1.2 校验产品项的有效性 |
| 132 | - List<CrmBusinessProductDO> businessProducts = validateBusinessProducts(updateReqVO.getProducts()); | |
| 132 | + List<CrmBusinessProductDO> businessProducts = validateBusinessProducts(updateReqVO.getBusinessProducts()); | |
| 133 | 133 | // 1.3 校验关联字段 |
| 134 | 134 | validateRelationDataExists(updateReqVO); |
| 135 | 135 | |
| ... | ... | @@ -202,9 +202,9 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { |
| 202 | 202 | } |
| 203 | 203 | } |
| 204 | 204 | |
| 205 | - private List<CrmBusinessProductDO> validateBusinessProducts(List<CrmBusinessSaveReqVO.Product> list) { | |
| 205 | + private List<CrmBusinessProductDO> validateBusinessProducts(List<CrmBusinessSaveReqVO.BusinessProduct> list) { | |
| 206 | 206 | // 1. 校验产品存在 |
| 207 | - productService.validProductList(convertSet(list, CrmBusinessSaveReqVO.Product::getProductId)); | |
| 207 | + productService.validProductList(convertSet(list, CrmBusinessSaveReqVO.BusinessProduct::getProductId)); | |
| 208 | 208 | // 2. 转化为 CrmBusinessProductDO 列表 |
| 209 | 209 | return convertList(list, o -> BeanUtils.toBean(o, CrmBusinessProductDO.class, |
| 210 | 210 | item -> item.setTotalPrice(MoneyUtils.priceMultiply(item.getBusinessPrice(), item.getCount())))); |
| ... | ... | @@ -234,7 +234,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { |
| 234 | 234 | } |
| 235 | 235 | // 1.4 校验是不是状态没变更 |
| 236 | 236 | if ((reqVO.getStatusId() != null && reqVO.getStatusId().equals(business.getStatusId())) |
| 237 | - || (reqVO.getEndStatus() != null && reqVO.getEndStatus().equals(business.getEndStatus()))) { | |
| 237 | + || (reqVO.getEndStatus() != null && reqVO.getEndStatus().equals(business.getEndStatus()))) { | |
| 238 | 238 | throw exception(BUSINESS_UPDATE_STATUS_FAIL_STATUS_EQUALS); |
| 239 | 239 | } |
| 240 | 240 | |
| ... | ... | @@ -301,7 +301,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { |
| 301 | 301 | |
| 302 | 302 | // 2.1 数据权限转移 |
| 303 | 303 | permissionService.transferPermission(new CrmPermissionTransferReqBO(userId, CrmBizTypeEnum.CRM_BUSINESS.getType(), |
| 304 | - reqVO.getNewOwnerUserId(), reqVO.getId(), CrmPermissionLevelEnum.OWNER.getLevel())); | |
| 304 | + reqVO.getId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel())); | |
| 305 | 305 | // 2.2 设置新的负责人 |
| 306 | 306 | businessMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId()); |
| 307 | 307 | |
| ... | ... | @@ -370,4 +370,9 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { |
| 370 | 370 | return businessMapper.selectCountByStatusTypeId(statusTypeId); |
| 371 | 371 | } |
| 372 | 372 | |
| 373 | + @Override | |
| 374 | + public List<CrmBusinessDO> getBusinessListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) { | |
| 375 | + return businessMapper.selectListByCustomerIdOwnerUserId(customerId, ownerUserId); | |
| 376 | + } | |
| 377 | + | |
| 373 | 378 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java
| ... | ... | @@ -75,8 +75,8 @@ public interface CrmContactService { |
| 75 | 75 | /** |
| 76 | 76 | * 更新联系人的下次联系时间 |
| 77 | 77 | * |
| 78 | - * @param ids 编号数组 | |
| 79 | - * @param contactNextTime 下次联系时间 | |
| 78 | + * @param ids 编号数组 | |
| 79 | + * @param contactNextTime 下次联系时间 | |
| 80 | 80 | */ |
| 81 | 81 | void updateContactContactNextTime(Collection<Long> ids, LocalDateTime contactNextTime); |
| 82 | 82 | |
| ... | ... | @@ -160,4 +160,13 @@ public interface CrmContactService { |
| 160 | 160 | */ |
| 161 | 161 | Long getContactCountByCustomerId(Long customerId); |
| 162 | 162 | |
| 163 | + /** | |
| 164 | + * 获得联系人列表 | |
| 165 | + * | |
| 166 | + * @param customerId 客户编号 | |
| 167 | + * @param ownerUserId 负责人编号 | |
| 168 | + * @return 联系人列表 | |
| 169 | + */ | |
| 170 | + List<CrmContactDO> getContactListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId); | |
| 171 | + | |
| 163 | 172 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java
| ... | ... | @@ -298,4 +298,9 @@ public class CrmContactServiceImpl implements CrmContactService { |
| 298 | 298 | return contactMapper.selectCount(CrmContactDO::getCustomerId, customerId); |
| 299 | 299 | } |
| 300 | 300 | |
| 301 | + @Override | |
| 302 | + public List<CrmContactDO> getContactListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) { | |
| 303 | + return contactMapper.selectListByCustomerIdOwnerUserId(customerId, ownerUserId); | |
| 304 | + } | |
| 305 | + | |
| 301 | 306 | } |
| 302 | 307 | \ No newline at end of file | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java
| ... | ... | @@ -58,8 +58,8 @@ public interface CrmContractService { |
| 58 | 58 | /** |
| 59 | 59 | * 更新合同相关的更进信息 |
| 60 | 60 | * |
| 61 | - * @param id 合同编号 | |
| 62 | - * @param contactNextTime 下次联系时间 | |
| 61 | + * @param id 合同编号 | |
| 62 | + * @param contactNextTime 下次联系时间 | |
| 63 | 63 | * @param contactLastContent 最后联系内容 |
| 64 | 64 | */ |
| 65 | 65 | void updateContractFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent); |
| ... | ... | @@ -75,7 +75,7 @@ public interface CrmContractService { |
| 75 | 75 | /** |
| 76 | 76 | * 更新合同流程审批结果 |
| 77 | 77 | * |
| 78 | - * @param id 合同编号 | |
| 78 | + * @param id 合同编号 | |
| 79 | 79 | * @param bpmResult BPM 审批结果 |
| 80 | 80 | */ |
| 81 | 81 | void updateContractAuditStatus(Long id, Integer bpmResult); |
| ... | ... | @@ -193,4 +193,13 @@ public interface CrmContractService { |
| 193 | 193 | */ |
| 194 | 194 | Long getRemindContractCount(Long userId); |
| 195 | 195 | |
| 196 | + /** | |
| 197 | + * 获得合同列表 | |
| 198 | + * | |
| 199 | + * @param customerId 客户编号 | |
| 200 | + * @param ownerUserId 负责人编号 | |
| 201 | + * @return 合同列表 | |
| 202 | + */ | |
| 203 | + List<CrmContractDO> getContractListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId); | |
| 204 | + | |
| 196 | 205 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
| ... | ... | @@ -30,12 +30,14 @@ import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; |
| 30 | 30 | import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; |
| 31 | 31 | import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; |
| 32 | 32 | import cn.iocoder.yudao.module.crm.service.product.CrmProductService; |
| 33 | +import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableService; | |
| 33 | 34 | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| 34 | 35 | import com.mzt.logapi.context.LogRecordContext; |
| 35 | 36 | import com.mzt.logapi.service.impl.DiffParseFunction; |
| 36 | 37 | import com.mzt.logapi.starter.annotation.LogRecord; |
| 37 | 38 | import jakarta.annotation.Resource; |
| 38 | 39 | import lombok.extern.slf4j.Slf4j; |
| 40 | +import org.springframework.context.annotation.Lazy; | |
| 39 | 41 | import org.springframework.stereotype.Service; |
| 40 | 42 | import org.springframework.transaction.annotation.Transactional; |
| 41 | 43 | import org.springframework.validation.annotation.Validated; |
| ... | ... | @@ -86,7 +88,9 @@ public class CrmContractServiceImpl implements CrmContractService { |
| 86 | 88 | private CrmContactService contactService; |
| 87 | 89 | @Resource |
| 88 | 90 | private CrmContractConfigService contractConfigService; |
| 89 | - | |
| 91 | + @Resource | |
| 92 | + @Lazy // 延迟加载,避免循环依赖 | |
| 93 | + private CrmReceivableService receivableService; | |
| 90 | 94 | @Resource |
| 91 | 95 | private AdminUserApi adminUserApi; |
| 92 | 96 | @Resource |
| ... | ... | @@ -222,15 +226,19 @@ public class CrmContractServiceImpl implements CrmContractService { |
| 222 | 226 | success = CRM_CONTRACT_DELETE_SUCCESS) |
| 223 | 227 | @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) |
| 224 | 228 | public void deleteContract(Long id) { |
| 225 | - // TODO @puhui999:如果被 CrmReceivableDO 所使用,则不允许删除 | |
| 226 | - // 校验存在 | |
| 229 | + // 1.1 校验存在 | |
| 227 | 230 | CrmContractDO contract = validateContractExists(id); |
| 228 | - // 删除 | |
| 231 | + // 1.2 如果被 CrmReceivableDO 所使用,则不允许删除 | |
| 232 | + if (receivableService.getReceivableCountByContractId(contract.getId()) > 0) { | |
| 233 | + throw exception(CONTRACT_DELETE_FAIL); | |
| 234 | + } | |
| 235 | + | |
| 236 | + // 2.1 删除合同 | |
| 229 | 237 | contractMapper.deleteById(id); |
| 230 | - // 删除数据权限 | |
| 238 | + // 2.2 删除数据权限 | |
| 231 | 239 | crmPermissionService.deletePermission(CrmBizTypeEnum.CRM_CONTRACT.getType(), id); |
| 232 | 240 | |
| 233 | - // 记录操作日志上下文 | |
| 241 | + // 3. 记录操作日志上下文 | |
| 234 | 242 | LogRecordContext.putVariable("contractName", contract.getName()); |
| 235 | 243 | } |
| 236 | 244 | |
| ... | ... | @@ -399,4 +407,9 @@ public class CrmContractServiceImpl implements CrmContractService { |
| 399 | 407 | return contractMapper.selectCountByRemind(userId, config); |
| 400 | 408 | } |
| 401 | 409 | |
| 410 | + @Override | |
| 411 | + public List<CrmContractDO> getContractListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) { | |
| 412 | + return contractMapper.selectListByCustomerIdOwnerUserId(customerId, ownerUserId); | |
| 413 | + } | |
| 414 | + | |
| 402 | 415 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
| ... | ... | @@ -9,7 +9,13 @@ import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| 9 | 9 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 10 | 10 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 11 | 11 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 12 | +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO; | |
| 13 | +import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactTransferReqVO; | |
| 14 | +import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractTransferReqVO; | |
| 12 | 15 | import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer.*; |
| 16 | +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; | |
| 17 | +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; | |
| 18 | +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; | |
| 13 | 19 | import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; |
| 14 | 20 | import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO; |
| 15 | 21 | import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO; |
| ... | ... | @@ -201,7 +207,6 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { |
| 201 | 207 | CrmCustomerDO customer = validateCustomerExists(reqVO.getId()); |
| 202 | 208 | // 1.2 校验拥有客户是否到达上限 |
| 203 | 209 | validateCustomerExceedOwnerLimit(reqVO.getNewOwnerUserId(), 1); |
| 204 | - | |
| 205 | 210 | // 2.1 数据权限转移 |
| 206 | 211 | permissionService.transferPermission(new CrmPermissionTransferReqBO(userId, CrmBizTypeEnum.CRM_CUSTOMER.getType(), |
| 207 | 212 | reqVO.getId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel())); |
| ... | ... | @@ -209,10 +214,45 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { |
| 209 | 214 | customerMapper.updateById(new CrmCustomerDO().setId(reqVO.getId()) |
| 210 | 215 | .setOwnerUserId(reqVO.getNewOwnerUserId()).setOwnerTime(LocalDateTime.now())); |
| 211 | 216 | |
| 217 | + // 2.3 同时转移 | |
| 218 | + if (CollUtil.isNotEmpty(reqVO.getToBizTypes())) { | |
| 219 | + transfer(reqVO, userId); | |
| 220 | + } | |
| 221 | + | |
| 212 | 222 | // 3. 记录转移日志 |
| 213 | 223 | LogRecordContext.putVariable("customer", customer); |
| 214 | 224 | } |
| 215 | 225 | |
| 226 | + /** | |
| 227 | + * 转移客户时,需要额外有【联系人】【商机】【合同】 | |
| 228 | + * | |
| 229 | + * @param reqVO 请求 | |
| 230 | + * @param userId 用户编号 | |
| 231 | + */ | |
| 232 | + private void transfer(CrmCustomerTransferReqVO reqVO, Long userId) { | |
| 233 | + if (reqVO.getToBizTypes().contains(CrmBizTypeEnum.CRM_CONTACT.getType())) { | |
| 234 | + List<CrmContactDO> contactList = contactService.getContactListByCustomerIdOwnerUserId(reqVO.getId(), userId); | |
| 235 | + contactList.forEach(item -> { | |
| 236 | + contactService.transferContact(new CrmContactTransferReqVO(item.getId(), reqVO.getNewOwnerUserId(), | |
| 237 | + reqVO.getOldOwnerPermissionLevel()), userId); | |
| 238 | + }); | |
| 239 | + } | |
| 240 | + if (reqVO.getToBizTypes().contains(CrmBizTypeEnum.CRM_BUSINESS.getType())) { | |
| 241 | + List<CrmBusinessDO> businessList = businessService.getBusinessListByCustomerIdOwnerUserId(reqVO.getId(), userId); | |
| 242 | + businessList.forEach(item -> { | |
| 243 | + businessService.transferBusiness(new CrmBusinessTransferReqVO(item.getId(), reqVO.getNewOwnerUserId(), | |
| 244 | + reqVO.getOldOwnerPermissionLevel()), userId); | |
| 245 | + }); | |
| 246 | + } | |
| 247 | + if (reqVO.getToBizTypes().contains(CrmBizTypeEnum.CRM_CONTRACT.getType())) { | |
| 248 | + List<CrmContractDO> contractList = contractService.getContractListByCustomerIdOwnerUserId(reqVO.getId(), userId); | |
| 249 | + contractList.forEach(item -> { | |
| 250 | + contractService.transferContract(new CrmContractTransferReqVO(item.getId(), reqVO.getNewOwnerUserId(), | |
| 251 | + reqVO.getOldOwnerPermissionLevel()), userId); | |
| 252 | + }); | |
| 253 | + } | |
| 254 | + } | |
| 255 | + | |
| 216 | 256 | @Override |
| 217 | 257 | @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_LOCK_SUB_TYPE, bizNo = "{{#lockReqVO.id}}", |
| 218 | 258 | success = CRM_CUSTOMER_LOCK_SUCCESS) | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionService.java
| 1 | 1 | package cn.iocoder.yudao.module.crm.service.permission; |
| 2 | 2 | |
| 3 | 3 | |
| 4 | +import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionSaveReqVO; | |
| 4 | 5 | import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO; |
| 5 | 6 | import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; |
| 6 | 7 | import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; |
| ... | ... | @@ -22,6 +23,14 @@ public interface CrmPermissionService { |
| 22 | 23 | /** |
| 23 | 24 | * 创建数据权限 |
| 24 | 25 | * |
| 26 | + * @param reqVO 创建信息 | |
| 27 | + * @param userId 用户编号 | |
| 28 | + */ | |
| 29 | + void createPermission(CrmPermissionSaveReqVO reqVO, Long userId); | |
| 30 | + | |
| 31 | + /** | |
| 32 | + * 创建数据权限 | |
| 33 | + * | |
| 25 | 34 | * @param createReqBO 创建信息 |
| 26 | 35 | * @return 编号 |
| 27 | 36 | */ |
| ... | ... | @@ -111,10 +120,10 @@ public interface CrmPermissionService { |
| 111 | 120 | /** |
| 112 | 121 | * 校验是否有指定数据的操作权限 |
| 113 | 122 | * |
| 114 | - * @param bizType 数据类型,关联 {@link CrmBizTypeEnum} | |
| 115 | - * @param bizId 数据编号,关联 {@link CrmBizTypeEnum} 对应模块 DO#getId() | |
| 116 | - * @param userId 用户编号 | |
| 117 | - * @param level 权限级别 | |
| 123 | + * @param bizType 数据类型,关联 {@link CrmBizTypeEnum} | |
| 124 | + * @param bizId 数据编号,关联 {@link CrmBizTypeEnum} 对应模块 DO#getId() | |
| 125 | + * @param userId 用户编号 | |
| 126 | + * @param level 权限级别 | |
| 118 | 127 | * @return 是否有权限 |
| 119 | 128 | */ |
| 120 | 129 | boolean hasPermission(Integer bizType, Long bizId, Long userId, CrmPermissionLevelEnum level); | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java
| ... | ... | @@ -4,28 +4,34 @@ import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.util.ObjUtil; |
| 5 | 5 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 6 | 6 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 7 | +import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionSaveReqVO; | |
| 7 | 8 | import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO; |
| 9 | +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; | |
| 10 | +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; | |
| 11 | +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; | |
| 8 | 12 | import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; |
| 9 | 13 | import cn.iocoder.yudao.module.crm.dal.mysql.permission.CrmPermissionMapper; |
| 10 | 14 | import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; |
| 11 | 15 | import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; |
| 16 | +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; | |
| 17 | +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; | |
| 18 | +import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; | |
| 19 | +import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; | |
| 12 | 20 | import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; |
| 13 | 21 | import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; |
| 14 | 22 | import cn.iocoder.yudao.module.crm.util.CrmPermissionUtils; |
| 15 | 23 | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| 24 | +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; | |
| 16 | 25 | import jakarta.annotation.Resource; |
| 26 | +import org.springframework.context.annotation.Lazy; | |
| 17 | 27 | import org.springframework.stereotype.Service; |
| 18 | 28 | import org.springframework.transaction.annotation.Transactional; |
| 19 | 29 | import org.springframework.validation.annotation.Validated; |
| 20 | 30 | |
| 21 | -import java.util.Collection; | |
| 22 | -import java.util.Collections; | |
| 23 | -import java.util.List; | |
| 24 | -import java.util.Set; | |
| 31 | +import java.util.*; | |
| 25 | 32 | |
| 26 | 33 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 27 | -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.anyMatch; | |
| 28 | -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | |
| 34 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; | |
| 29 | 35 | import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; |
| 30 | 36 | import static cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum.isOwner; |
| 31 | 37 | |
| ... | ... | @@ -40,18 +46,119 @@ public class CrmPermissionServiceImpl implements CrmPermissionService { |
| 40 | 46 | |
| 41 | 47 | @Resource |
| 42 | 48 | private CrmPermissionMapper permissionMapper; |
| 43 | - | |
| 49 | + @Resource | |
| 50 | + @Lazy // 解决依赖循环 | |
| 51 | + private CrmContactService contactService; | |
| 52 | + @Resource | |
| 53 | + @Lazy // 解决依赖循环 | |
| 54 | + private CrmBusinessService businessService; | |
| 55 | + @Resource | |
| 56 | + @Lazy // 解决依赖循环 | |
| 57 | + private CrmContractService contractService; | |
| 44 | 58 | @Resource |
| 45 | 59 | private AdminUserApi adminUserApi; |
| 46 | 60 | |
| 61 | + | |
| 62 | + @Override | |
| 63 | + @Transactional(rollbackFor = Exception.class) | |
| 64 | + @CrmPermission(bizTypeValue = "#reqVO.bizType", bizId = "#reqVO.bizId", level = CrmPermissionLevelEnum.OWNER) | |
| 65 | + public void createPermission(CrmPermissionSaveReqVO reqVO, Long userId) { | |
| 66 | + // 1. 创建数据权限 | |
| 67 | + createPermission0(BeanUtils.toBean(reqVO, CrmPermissionCreateReqBO.class)); | |
| 68 | + | |
| 69 | + // 2. 处理【同时添加至】的权限 | |
| 70 | + if (CollUtil.isEmpty(reqVO.getToBizTypes())) { | |
| 71 | + return; | |
| 72 | + } | |
| 73 | + List<CrmPermissionCreateReqBO> createPermissions = new ArrayList<>(); | |
| 74 | + buildContactPermissions(reqVO, userId, createPermissions); | |
| 75 | + buildBusinessPermissions(reqVO, userId, createPermissions); | |
| 76 | + buildContractPermissions(reqVO, userId, createPermissions); | |
| 77 | + if (CollUtil.isEmpty(createPermissions)) { | |
| 78 | + return; | |
| 79 | + } | |
| 80 | + createPermissionBatch(createPermissions); | |
| 81 | + } | |
| 82 | + | |
| 83 | + /** | |
| 84 | + * 处理同时添加至联系人 | |
| 85 | + * | |
| 86 | + * @param reqVO 请求 | |
| 87 | + * @param userId 操作人 | |
| 88 | + * @param createPermissions 待添加权限列表 | |
| 89 | + */ | |
| 90 | + private void buildContactPermissions(CrmPermissionSaveReqVO reqVO, Long userId, List<CrmPermissionCreateReqBO> createPermissions) { | |
| 91 | + // 1. 校验是否被同时添加 | |
| 92 | + Integer type = CrmBizTypeEnum.CRM_CONTACT.getType(); | |
| 93 | + if (!reqVO.getToBizTypes().contains(type)) { | |
| 94 | + return; | |
| 95 | + } | |
| 96 | + // 2. 添加数据权限 | |
| 97 | + List<CrmContactDO> contactList = contactService.getContactListByCustomerIdOwnerUserId(reqVO.getBizId(), userId); | |
| 98 | + contactList.forEach(item -> createBizTypePermissions(reqVO, type, item.getId(), item.getName(), createPermissions)); | |
| 99 | + } | |
| 100 | + | |
| 101 | + /** | |
| 102 | + * 处理同时添加至商机 | |
| 103 | + * | |
| 104 | + * @param reqVO 请求 | |
| 105 | + * @param userId 操作人 | |
| 106 | + * @param createPermissions 待添加权限列表 | |
| 107 | + */ | |
| 108 | + private void buildBusinessPermissions(CrmPermissionSaveReqVO reqVO, Long userId, List<CrmPermissionCreateReqBO> createPermissions) { | |
| 109 | + // 1. 校验是否被同时添加 | |
| 110 | + Integer type = CrmBizTypeEnum.CRM_BUSINESS.getType(); | |
| 111 | + if (!reqVO.getToBizTypes().contains(type)) { | |
| 112 | + return; | |
| 113 | + } | |
| 114 | + // 2. 添加数据权限 | |
| 115 | + List<CrmBusinessDO> businessList = businessService.getBusinessListByCustomerIdOwnerUserId(reqVO.getBizId(), userId); | |
| 116 | + businessList.forEach(item -> createBizTypePermissions(reqVO, type, item.getId(), item.getName(), createPermissions)); | |
| 117 | + } | |
| 118 | + | |
| 119 | + /** | |
| 120 | + * 处理同时添加至合同 | |
| 121 | + * | |
| 122 | + * @param reqVO 请求 | |
| 123 | + * @param userId 操作人 | |
| 124 | + * @param createPermissions 待添加权限列表 | |
| 125 | + */ | |
| 126 | + private void buildContractPermissions(CrmPermissionSaveReqVO reqVO, Long userId, List<CrmPermissionCreateReqBO> createPermissions) { | |
| 127 | + // 1. 校验是否被同时添加 | |
| 128 | + Integer type = CrmBizTypeEnum.CRM_CONTRACT.getType(); | |
| 129 | + if (!reqVO.getToBizTypes().contains(type)) { | |
| 130 | + return; | |
| 131 | + } | |
| 132 | + // 2. 添加数据权限 | |
| 133 | + List<CrmContractDO> contractList = contractService.getContractListByCustomerIdOwnerUserId(reqVO.getBizId(), userId); | |
| 134 | + contractList.forEach(item -> createBizTypePermissions(reqVO, type, item.getId(), item.getName(), createPermissions)); | |
| 135 | + } | |
| 136 | + | |
| 137 | + private void createBizTypePermissions(CrmPermissionSaveReqVO reqVO, Integer type, Long bizId, String name, | |
| 138 | + List<CrmPermissionCreateReqBO> createPermissions) { | |
| 139 | + AdminUserRespDTO user = adminUserApi.getUser(reqVO.getUserId()); | |
| 140 | + // 1. 需要考虑,被添加人,是不是应该有对应的权限了; | |
| 141 | + CrmPermissionDO permission = hasAnyPermission(type, bizId, reqVO.getUserId()); | |
| 142 | + if (ObjUtil.isNotNull(permission)) { | |
| 143 | + throw exception(CRM_PERMISSION_CREATE_FAIL_EXISTS, user.getNickname(), CrmBizTypeEnum.getNameByType(type), | |
| 144 | + name, CrmPermissionLevelEnum.getNameByLevel(permission.getLevel())); | |
| 145 | + } | |
| 146 | + // 2. 添加数据权限 | |
| 147 | + createPermissions.add(new CrmPermissionCreateReqBO().setBizType(type) | |
| 148 | + .setBizId(bizId).setUserId(reqVO.getUserId()).setLevel(reqVO.getLevel())); | |
| 149 | + } | |
| 150 | + | |
| 47 | 151 | @Override |
| 48 | 152 | @Transactional(rollbackFor = Exception.class) |
| 49 | 153 | public Long createPermission(CrmPermissionCreateReqBO createReqBO) { |
| 154 | + return createPermission0(createReqBO); | |
| 155 | + } | |
| 156 | + | |
| 157 | + private Long createPermission0(CrmPermissionCreateReqBO createReqBO) { | |
| 50 | 158 | validatePermissionNotExists(Collections.singletonList(createReqBO)); |
| 51 | 159 | // 1. 校验用户是否存在 |
| 52 | 160 | adminUserApi.validateUserList(Collections.singletonList(createReqBO.getUserId())); |
| 53 | - | |
| 54 | - // 2. 创建 | |
| 161 | + // 2. 插入权限 | |
| 55 | 162 | CrmPermissionDO permission = BeanUtils.toBean(createReqBO, CrmPermissionDO.class); |
| 56 | 163 | permissionMapper.insert(permission); |
| 57 | 164 | return permission.getId(); |
| ... | ... | @@ -170,7 +277,7 @@ public class CrmPermissionServiceImpl implements CrmPermissionService { |
| 170 | 277 | throw exception(CRM_PERMISSION_DELETE_FAIL); |
| 171 | 278 | } |
| 172 | 279 | // 校验操作人是否为负责人 |
| 173 | - CrmPermissionDO permission = permissionMapper.selectByBizIdAndUserId(permissions.get(0).getBizId(), userId); | |
| 280 | + CrmPermissionDO permission = permissionMapper.selectByBizAndUserId(permissions.get(0).getBizType(), permissions.get(0).getBizId(), userId); | |
| 174 | 281 | if (permission == null) { |
| 175 | 282 | throw exception(CRM_PERMISSION_DELETE_DENIED); |
| 176 | 283 | } |
| ... | ... | @@ -220,4 +327,9 @@ public class CrmPermissionServiceImpl implements CrmPermissionService { |
| 220 | 327 | ObjUtil.equal(permission.getUserId(), userId) && ObjUtil.equal(permission.getLevel(), level.getLevel())); |
| 221 | 328 | } |
| 222 | 329 | |
| 330 | + public CrmPermissionDO hasAnyPermission(Integer bizType, Long bizId, Long userId) { | |
| 331 | + List<CrmPermissionDO> permissionList = permissionMapper.selectByBizTypeAndBizId(bizType, bizId); | |
| 332 | + return findFirst(permissionList, permission -> ObjUtil.equal(permission.getUserId(), userId)); | |
| 333 | + } | |
| 334 | + | |
| 223 | 335 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java
| ... | ... | @@ -20,7 +20,6 @@ import com.mzt.logapi.context.LogRecordContext; |
| 20 | 20 | import com.mzt.logapi.service.impl.DiffParseFunction; |
| 21 | 21 | import com.mzt.logapi.starter.annotation.LogRecord; |
| 22 | 22 | import jakarta.annotation.Resource; |
| 23 | -import org.springframework.context.annotation.Lazy; | |
| 24 | 23 | import org.springframework.stereotype.Service; |
| 25 | 24 | import org.springframework.transaction.annotation.Transactional; |
| 26 | 25 | import org.springframework.validation.annotation.Validated; |
| ... | ... | @@ -47,9 +46,6 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { |
| 47 | 46 | private CrmReceivablePlanMapper receivablePlanMapper; |
| 48 | 47 | |
| 49 | 48 | @Resource |
| 50 | - @Lazy // 延迟加载,避免循环依赖 | |
| 51 | - private CrmReceivableService receivableService; | |
| 52 | - @Resource | |
| 53 | 49 | private CrmContractService contractService; |
| 54 | 50 | @Resource |
| 55 | 51 | private CrmPermissionService permissionService; |
| ... | ... | @@ -144,7 +140,7 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { |
| 144 | 140 | // 2. 删除 |
| 145 | 141 | receivablePlanMapper.deleteById(id); |
| 146 | 142 | // 3. 删除数据权限 |
| 147 | - permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id); | |
| 143 | + permissionService.deletePermission(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(), id); | |
| 148 | 144 | |
| 149 | 145 | // 4. 记录操作日志上下文 |
| 150 | 146 | LogRecordContext.putVariable("receivablePlan", receivablePlan); | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java
| ... | ... | @@ -122,4 +122,12 @@ public interface CrmReceivableService { |
| 122 | 122 | */ |
| 123 | 123 | Map<Long, BigDecimal> getReceivablePriceMapByContractId(Collection<Long> contractIds); |
| 124 | 124 | |
| 125 | + /** | |
| 126 | + * 根据合同编号查询回款数量 | |
| 127 | + * | |
| 128 | + * @param contractId 合同编号 | |
| 129 | + * @return 回款数量 | |
| 130 | + */ | |
| 131 | + Long getReceivableCountByContractId(Long contractId); | |
| 132 | + | |
| 125 | 133 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java
| ... | ... | @@ -37,10 +37,7 @@ import org.springframework.transaction.annotation.Transactional; |
| 37 | 37 | import org.springframework.validation.annotation.Validated; |
| 38 | 38 | |
| 39 | 39 | import java.math.BigDecimal; |
| 40 | -import java.util.Arrays; | |
| 41 | -import java.util.Collection; | |
| 42 | -import java.util.List; | |
| 43 | -import java.util.Map; | |
| 40 | +import java.util.*; | |
| 44 | 41 | |
| 45 | 42 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 46 | 43 | import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; |
| ... | ... | @@ -81,7 +78,6 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { |
| 81 | 78 | @Resource |
| 82 | 79 | private BpmProcessInstanceApi bpmProcessInstanceApi; |
| 83 | 80 | |
| 84 | - // TODO @puhui999:操作日志没记录上 | |
| 85 | 81 | @Override |
| 86 | 82 | @Transactional(rollbackFor = Exception.class) |
| 87 | 83 | @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_CREATE_SUB_TYPE, bizNo = "{{#receivable.id}}", |
| ... | ... | @@ -115,6 +111,7 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { |
| 115 | 111 | |
| 116 | 112 | // 5. 记录操作日志上下文 |
| 117 | 113 | LogRecordContext.putVariable("receivable", receivable); |
| 114 | + LogRecordContext.putVariable("period", getReceivablePeriod(receivable.getPlanId())); | |
| 118 | 115 | return receivable.getId(); |
| 119 | 116 | } |
| 120 | 117 | |
| ... | ... | @@ -156,7 +153,6 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { |
| 156 | 153 | } |
| 157 | 154 | } |
| 158 | 155 | |
| 159 | - // TODO @puhui999:操作日志没记录上 | |
| 160 | 156 | @Override |
| 161 | 157 | @Transactional(rollbackFor = Exception.class) |
| 162 | 158 | @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", |
| ... | ... | @@ -164,11 +160,14 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { |
| 164 | 160 | @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) |
| 165 | 161 | public void updateReceivable(CrmReceivableSaveReqVO updateReqVO) { |
| 166 | 162 | Assert.notNull(updateReqVO.getId(), "回款编号不能为空"); |
| 167 | - // 1.1 校验可回款金额超过上限 | |
| 168 | - validateReceivablePriceExceedsLimit(updateReqVO); | |
| 169 | 163 | updateReqVO.setOwnerUserId(null).setCustomerId(null).setContractId(null).setPlanId(null); // 不允许修改的字段 |
| 170 | - // 1.2 校验存在 | |
| 164 | + // 1.1 校验存在 | |
| 171 | 165 | CrmReceivableDO receivable = validateReceivableExists(updateReqVO.getId()); |
| 166 | + updateReqVO.setOwnerUserId(receivable.getOwnerUserId()).setCustomerId(receivable.getCustomerId()) | |
| 167 | + .setContractId(receivable.getContractId()).setPlanId(receivable.getPlanId()); // 设置已存在的值 | |
| 168 | + // 1.2 校验可回款金额超过上限 | |
| 169 | + validateReceivablePriceExceedsLimit(updateReqVO); | |
| 170 | + | |
| 172 | 171 | // 1.3 只有草稿、审批中,可以编辑; |
| 173 | 172 | if (!ObjectUtils.equalsAny(receivable.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus(), |
| 174 | 173 | CrmAuditStatusEnum.PROCESS.getStatus())) { |
| ... | ... | @@ -180,8 +179,17 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { |
| 180 | 179 | receivableMapper.updateById(updateObj); |
| 181 | 180 | |
| 182 | 181 | // 3. 记录操作日志上下文 |
| 183 | - LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(receivable, CrmReceivableSaveReqVO.class)); | |
| 184 | 182 | LogRecordContext.putVariable("receivable", receivable); |
| 183 | + LogRecordContext.putVariable("period", getReceivablePeriod(receivable.getPlanId())); | |
| 184 | + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(receivable, CrmReceivableSaveReqVO.class)); | |
| 185 | + } | |
| 186 | + | |
| 187 | + private Integer getReceivablePeriod(Long planId) { | |
| 188 | + if (Objects.isNull(planId)) { | |
| 189 | + return null; | |
| 190 | + } | |
| 191 | + CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(planId); | |
| 192 | + return receivablePlan.getPeriod(); | |
| 185 | 193 | } |
| 186 | 194 | |
| 187 | 195 | @Override |
| ... | ... | @@ -212,15 +220,19 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { |
| 212 | 220 | if (receivable.getPlanId() != null && receivablePlanService.getReceivablePlan(receivable.getPlanId()) != null) { |
| 213 | 221 | throw exception(RECEIVABLE_DELETE_FAIL); |
| 214 | 222 | } |
| 215 | - // TODO @puhui999:审批通过时,不允许删除; | |
| 223 | + // 1.3 审批通过时,不允许删除 | |
| 224 | + if (ObjUtil.equal(receivable.getAuditStatus(), CrmAuditStatusEnum.APPROVE.getStatus())) { | |
| 225 | + throw exception(RECEIVABLE_DELETE_FAIL_IS_APPROVE); | |
| 226 | + } | |
| 216 | 227 | |
| 217 | - // 2. 删除 | |
| 228 | + // 2.1 删除回款 | |
| 218 | 229 | receivableMapper.deleteById(id); |
| 219 | - // 3. 删除数据权限 | |
| 230 | + // 2.2 删除数据权限 | |
| 220 | 231 | permissionService.deletePermission(CrmBizTypeEnum.CRM_RECEIVABLE.getType(), id); |
| 221 | 232 | |
| 222 | - // 4. 记录操作日志上下文 | |
| 233 | + // 3. 记录操作日志上下文 | |
| 223 | 234 | LogRecordContext.putVariable("receivable", receivable); |
| 235 | + LogRecordContext.putVariable("period", getReceivablePeriod(receivable.getPlanId())); | |
| 224 | 236 | } |
| 225 | 237 | |
| 226 | 238 | @Override |
| ... | ... | @@ -289,4 +301,9 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { |
| 289 | 301 | return receivableMapper.selectReceivablePriceMapByContractId(contractIds); |
| 290 | 302 | } |
| 291 | 303 | |
| 304 | + @Override | |
| 305 | + public Long getReceivableCountByContractId(Long contractId) { | |
| 306 | + return receivableMapper.selectCountByContractId(contractId); | |
| 307 | + } | |
| 308 | + | |
| 292 | 309 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.service.statistics; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; | |
| 4 | + | |
| 5 | +import java.util.List; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * CRM 客户分析 Service 接口 | |
| 9 | + * | |
| 10 | + * @author dhb52 | |
| 11 | + */ | |
| 12 | +public interface CrmStatisticsCustomerService { | |
| 13 | + | |
| 14 | + /** | |
| 15 | + * 总量分析(按日期) | |
| 16 | + * | |
| 17 | + * @param reqVO 请求参数 | |
| 18 | + * @return 统计数据 | |
| 19 | + */ | |
| 20 | + List<CrmStatisticsCustomerSummaryByDateRespVO> getCustomerSummaryByDate(CrmStatisticsCustomerReqVO reqVO); | |
| 21 | + | |
| 22 | + /** | |
| 23 | + * 总量分析(按用户) | |
| 24 | + * | |
| 25 | + * @param reqVO 请求参数 | |
| 26 | + * @return 统计数据 | |
| 27 | + */ | |
| 28 | + List<CrmStatisticsCustomerSummaryByUserRespVO> getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO); | |
| 29 | + | |
| 30 | + /** | |
| 31 | + * 跟进次数分析(按日期) | |
| 32 | + * | |
| 33 | + * @param reqVO 请求参数 | |
| 34 | + * @return 统计数据 | |
| 35 | + */ | |
| 36 | + List<CrmStatisticsFollowUpSummaryByDateRespVO> getFollowUpSummaryByDate(CrmStatisticsCustomerReqVO reqVO); | |
| 37 | + | |
| 38 | + /** | |
| 39 | + * 跟进次数分析(按用户) | |
| 40 | + * | |
| 41 | + * @param reqVO 请求参数 | |
| 42 | + * @return 统计数据 | |
| 43 | + */ | |
| 44 | + List<CrmStatisticsFollowUpSummaryByUserRespVO> getFollowUpSummaryByUser(CrmStatisticsCustomerReqVO reqVO); | |
| 45 | + | |
| 46 | + /** | |
| 47 | + * 客户跟进次数分析(按类型) | |
| 48 | + * | |
| 49 | + * @param reqVO 请求参数 | |
| 50 | + * @return 统计数据 | |
| 51 | + */ | |
| 52 | + List<CrmStatisticsFollowUpSummaryByTypeRespVO> getFollowUpSummaryByType(CrmStatisticsCustomerReqVO reqVO); | |
| 53 | + | |
| 54 | + /** | |
| 55 | + * 获取客户的首次合同、回款信息列表,用于【客户转化率】页面 | |
| 56 | + * | |
| 57 | + * @param reqVO 请求参数 | |
| 58 | + * @return 统计数据 | |
| 59 | + */ | |
| 60 | + List<CrmStatisticsCustomerContractSummaryRespVO> getContractSummary(CrmStatisticsCustomerReqVO reqVO); | |
| 61 | + | |
| 62 | + /** | |
| 63 | + * 公海客户分析(按日期) | |
| 64 | + * | |
| 65 | + * @param reqVO 请求参数 | |
| 66 | + * @return 统计数据 | |
| 67 | + */ | |
| 68 | + List<CrmStatisticsPoolSummaryByDateRespVO> getPoolSummaryByDate(CrmStatisticsCustomerReqVO reqVO); | |
| 69 | + | |
| 70 | + /** | |
| 71 | + * 公海客户分析(按用户) | |
| 72 | + * | |
| 73 | + * @param reqVO 请求参数 | |
| 74 | + * @return 统计数据 | |
| 75 | + */ | |
| 76 | + List<CrmStatisticsPoolSummaryByUserRespVO> getPoolSummaryByUser(CrmStatisticsCustomerReqVO reqVO); | |
| 77 | + | |
| 78 | + /** | |
| 79 | + * 客户成交周期(按日期) | |
| 80 | + * | |
| 81 | + * 成交周期的定义:客户 customer 在创建出来,到合同 contract 第一次成交的时间差 | |
| 82 | + * | |
| 83 | + * @param reqVO 请求参数 | |
| 84 | + * @return 统计数据 | |
| 85 | + */ | |
| 86 | + List<CrmStatisticsCustomerDealCycleByDateRespVO> getCustomerDealCycleByDate(CrmStatisticsCustomerReqVO reqVO); | |
| 87 | + | |
| 88 | + /** | |
| 89 | + * 客户成交周期(按用户) | |
| 90 | + * | |
| 91 | + * @param reqVO 请求参数 | |
| 92 | + * @return 统计数据 | |
| 93 | + */ | |
| 94 | + List<CrmStatisticsCustomerDealCycleByUserRespVO> getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO); | |
| 95 | + | |
| 96 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.service.statistics; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.hutool.core.util.ObjUtil; | |
| 5 | +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; | |
| 6 | +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | |
| 7 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; | |
| 8 | +import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper; | |
| 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 jakarta.annotation.Resource; | |
| 14 | +import org.springframework.stereotype.Service; | |
| 15 | +import org.springframework.validation.annotation.Validated; | |
| 16 | + | |
| 17 | +import java.math.BigDecimal; | |
| 18 | +import java.time.LocalDateTime; | |
| 19 | +import java.util.Collections; | |
| 20 | +import java.util.List; | |
| 21 | +import java.util.Map; | |
| 22 | +import java.util.stream.Stream; | |
| 23 | + | |
| 24 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; | |
| 25 | +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; | |
| 26 | + | |
| 27 | +/** | |
| 28 | + * CRM 客户分析 Service 实现类 | |
| 29 | + * | |
| 30 | + * @author dhb52 | |
| 31 | + */ | |
| 32 | +@Service | |
| 33 | +@Validated | |
| 34 | +public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerService { | |
| 35 | + | |
| 36 | + @Resource | |
| 37 | + private CrmStatisticsCustomerMapper customerMapper; | |
| 38 | + | |
| 39 | + @Resource | |
| 40 | + private AdminUserApi adminUserApi; | |
| 41 | + @Resource | |
| 42 | + private DeptApi deptApi; | |
| 43 | + | |
| 44 | + @Override | |
| 45 | + public List<CrmStatisticsCustomerSummaryByDateRespVO> getCustomerSummaryByDate(CrmStatisticsCustomerReqVO reqVO) { | |
| 46 | + // 1. 获得用户编号数组 | |
| 47 | + reqVO.setUserIds(getUserIds(reqVO)); | |
| 48 | + if (CollUtil.isEmpty(reqVO.getUserIds())) { | |
| 49 | + return Collections.emptyList(); | |
| 50 | + } | |
| 51 | + | |
| 52 | + // 2. 按天统计,获取分项统计数据 | |
| 53 | + List<CrmStatisticsCustomerSummaryByDateRespVO> customerCreateCountList = customerMapper.selectCustomerCreateCountGroupByDate(reqVO); | |
| 54 | + List<CrmStatisticsCustomerSummaryByDateRespVO> customerDealCountList = customerMapper.selectCustomerDealCountGroupByDate(reqVO); | |
| 55 | + | |
| 56 | + // 3. 按照日期间隔,合并数据 | |
| 57 | + List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval()); | |
| 58 | + return convertList(timeRanges, times -> { | |
| 59 | + Integer customerCreateCount = customerCreateCountList.stream() | |
| 60 | + .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime())) | |
| 61 | + .mapToInt(CrmStatisticsCustomerSummaryByDateRespVO::getCustomerCreateCount).sum(); | |
| 62 | + Integer customerDealCount = customerDealCountList.stream() | |
| 63 | + .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime())) | |
| 64 | + .mapToInt(CrmStatisticsCustomerSummaryByDateRespVO::getCustomerDealCount).sum(); | |
| 65 | + return new CrmStatisticsCustomerSummaryByDateRespVO() | |
| 66 | + .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval())) | |
| 67 | + .setCustomerCreateCount(customerCreateCount).setCustomerDealCount(customerDealCount); | |
| 68 | + }); | |
| 69 | + } | |
| 70 | + | |
| 71 | + @Override | |
| 72 | + public List<CrmStatisticsCustomerSummaryByUserRespVO> getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO) { | |
| 73 | + // 1. 获得用户编号数组 | |
| 74 | + reqVO.setUserIds(getUserIds(reqVO)); | |
| 75 | + if (CollUtil.isEmpty(reqVO.getUserIds())) { | |
| 76 | + return Collections.emptyList(); | |
| 77 | + } | |
| 78 | + | |
| 79 | + // 2. 按用户统计,获取分项统计数据 | |
| 80 | + List<CrmStatisticsCustomerSummaryByUserRespVO> customerCreateCountList = customerMapper.selectCustomerCreateCountGroupByUser(reqVO); | |
| 81 | + List<CrmStatisticsCustomerSummaryByUserRespVO> customerDealCountList = customerMapper.selectCustomerDealCountGroupByUser(reqVO); | |
| 82 | + List<CrmStatisticsCustomerSummaryByUserRespVO> contractPriceList = customerMapper.selectContractPriceGroupByUser(reqVO); | |
| 83 | + List<CrmStatisticsCustomerSummaryByUserRespVO> receivablePriceList = customerMapper.selectReceivablePriceGroupByUser(reqVO); | |
| 84 | + | |
| 85 | + // 3.1 按照用户,合并统计数据 | |
| 86 | + List<CrmStatisticsCustomerSummaryByUserRespVO> summaryList = convertList(reqVO.getUserIds(), userId -> { | |
| 87 | + Integer customerCreateCount = customerCreateCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId())) | |
| 88 | + .mapToInt(CrmStatisticsCustomerSummaryByUserRespVO::getCustomerCreateCount).sum(); | |
| 89 | + Integer customerDealCount = customerDealCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId())) | |
| 90 | + .mapToInt(CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount).sum(); | |
| 91 | + BigDecimal contractPrice = contractPriceList.stream().filter(vo -> userId.equals(vo.getOwnerUserId())) | |
| 92 | + .reduce(BigDecimal.ZERO, (sum, vo) -> sum.add(vo.getContractPrice()), BigDecimal::add); | |
| 93 | + BigDecimal receivablePrice = receivablePriceList.stream().filter(vo -> userId.equals(vo.getOwnerUserId())) | |
| 94 | + .reduce(BigDecimal.ZERO, (sum, vo) -> sum.add(vo.getReceivablePrice()), BigDecimal::add); | |
| 95 | + return (CrmStatisticsCustomerSummaryByUserRespVO) new CrmStatisticsCustomerSummaryByUserRespVO() | |
| 96 | + .setCustomerCreateCount(customerCreateCount).setCustomerDealCount(customerDealCount) | |
| 97 | + .setContractPrice(contractPrice).setReceivablePrice(receivablePrice).setOwnerUserId(userId); | |
| 98 | + }); | |
| 99 | + // 3.2 拼接用户信息 | |
| 100 | + appendUserInfo(summaryList); | |
| 101 | + return summaryList; | |
| 102 | + } | |
| 103 | + | |
| 104 | + @Override | |
| 105 | + public List<CrmStatisticsFollowUpSummaryByDateRespVO> getFollowUpSummaryByDate(CrmStatisticsCustomerReqVO reqVO) { | |
| 106 | + // 1. 获得用户编号数组 | |
| 107 | + reqVO.setUserIds(getUserIds(reqVO)); | |
| 108 | + if (CollUtil.isEmpty(reqVO.getUserIds())) { | |
| 109 | + return Collections.emptyList(); | |
| 110 | + } | |
| 111 | + | |
| 112 | + // 2. 按天统计,获取分项统计数据 | |
| 113 | + List<CrmStatisticsFollowUpSummaryByDateRespVO> followUpRecordCountList = customerMapper.selectFollowUpRecordCountGroupByDate(reqVO); | |
| 114 | + List<CrmStatisticsFollowUpSummaryByDateRespVO> followUpCustomerCountList = customerMapper.selectFollowUpCustomerCountGroupByDate(reqVO); | |
| 115 | + | |
| 116 | + // 3. 按照时间间隔,合并统计数据 | |
| 117 | + List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval()); | |
| 118 | + return convertList(timeRanges, times -> { | |
| 119 | + Integer followUpRecordCount = followUpRecordCountList.stream() | |
| 120 | + .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime())) | |
| 121 | + .mapToInt(CrmStatisticsFollowUpSummaryByDateRespVO::getFollowUpRecordCount).sum(); | |
| 122 | + Integer followUpCustomerCount = followUpCustomerCountList.stream() | |
| 123 | + .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime())) | |
| 124 | + .mapToInt(CrmStatisticsFollowUpSummaryByDateRespVO::getFollowUpCustomerCount).sum(); | |
| 125 | + return new CrmStatisticsFollowUpSummaryByDateRespVO() | |
| 126 | + .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval())) | |
| 127 | + .setFollowUpCustomerCount(followUpRecordCount).setFollowUpRecordCount(followUpCustomerCount); | |
| 128 | + }); | |
| 129 | + } | |
| 130 | + | |
| 131 | + @Override | |
| 132 | + public List<CrmStatisticsFollowUpSummaryByUserRespVO> getFollowUpSummaryByUser(CrmStatisticsCustomerReqVO reqVO) { | |
| 133 | + // 1. 获得用户编号数组 | |
| 134 | + reqVO.setUserIds(getUserIds(reqVO)); | |
| 135 | + if (CollUtil.isEmpty(reqVO.getUserIds())) { | |
| 136 | + return Collections.emptyList(); | |
| 137 | + } | |
| 138 | + | |
| 139 | + // 2. 按用户统计,获取分项统计数据 | |
| 140 | + List<CrmStatisticsFollowUpSummaryByUserRespVO> followUpRecordCountList = customerMapper.selectFollowUpRecordCountGroupByUser(reqVO); | |
| 141 | + List<CrmStatisticsFollowUpSummaryByUserRespVO> followUpCustomerCountList = customerMapper.selectFollowUpCustomerCountGroupByUser(reqVO); | |
| 142 | + | |
| 143 | + // 3.1 按照用户,合并统计数据 | |
| 144 | + List<CrmStatisticsFollowUpSummaryByUserRespVO> summaryList = convertList(reqVO.getUserIds(), userId -> { | |
| 145 | + Integer followUpRecordCount = followUpRecordCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId())) | |
| 146 | + .mapToInt(CrmStatisticsFollowUpSummaryByUserRespVO::getFollowUpRecordCount).sum(); | |
| 147 | + Integer followUpCustomerCount = followUpCustomerCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId())) | |
| 148 | + .mapToInt(CrmStatisticsFollowUpSummaryByUserRespVO::getFollowUpCustomerCount).sum(); | |
| 149 | + return (CrmStatisticsFollowUpSummaryByUserRespVO) new CrmStatisticsFollowUpSummaryByUserRespVO() | |
| 150 | + .setFollowUpCustomerCount(followUpRecordCount).setFollowUpRecordCount(followUpCustomerCount).setOwnerUserId(userId); | |
| 151 | + }); | |
| 152 | + // 3.2 拼接用户信息 | |
| 153 | + appendUserInfo(summaryList); | |
| 154 | + return summaryList; | |
| 155 | + } | |
| 156 | + | |
| 157 | + @Override | |
| 158 | + public List<CrmStatisticsFollowUpSummaryByTypeRespVO> getFollowUpSummaryByType(CrmStatisticsCustomerReqVO reqVO) { | |
| 159 | + // 1. 获得用户编号数组 | |
| 160 | + reqVO.setUserIds(getUserIds(reqVO)); | |
| 161 | + if (CollUtil.isEmpty(reqVO.getUserIds())) { | |
| 162 | + return Collections.emptyList(); | |
| 163 | + } | |
| 164 | + | |
| 165 | + // 2. 获得跟进数据 | |
| 166 | + return customerMapper.selectFollowUpRecordCountGroupByType(reqVO); | |
| 167 | + } | |
| 168 | + | |
| 169 | + @Override | |
| 170 | + public List<CrmStatisticsCustomerContractSummaryRespVO> getContractSummary(CrmStatisticsCustomerReqVO reqVO) { | |
| 171 | + // 1. 获得用户编号数组 | |
| 172 | + reqVO.setUserIds(getUserIds(reqVO)); | |
| 173 | + if (CollUtil.isEmpty(reqVO.getUserIds())) { | |
| 174 | + return Collections.emptyList(); | |
| 175 | + } | |
| 176 | + | |
| 177 | + // 2. 按用户统计,获取统计数据 | |
| 178 | + List<CrmStatisticsCustomerContractSummaryRespVO> summaryList = customerMapper.selectContractSummary(reqVO); | |
| 179 | + | |
| 180 | + // 3. 拼接信息 | |
| 181 | + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap( | |
| 182 | + convertSetByFlatMap(summaryList, vo -> Stream.of(NumberUtils.parseLong(vo.getCreator()), vo.getOwnerUserId()))); | |
| 183 | + summaryList.forEach(vo -> { | |
| 184 | + findAndThen(userMap, NumberUtils.parseLong(vo.getCreator()), user -> vo.setCreatorUserName(user.getNickname())); | |
| 185 | + findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname())); | |
| 186 | + }); | |
| 187 | + return summaryList; | |
| 188 | + } | |
| 189 | + | |
| 190 | + @Override | |
| 191 | + public List<CrmStatisticsPoolSummaryByDateRespVO> getPoolSummaryByDate(CrmStatisticsCustomerReqVO reqVO) { | |
| 192 | + // 1. 获得用户编号数组 | |
| 193 | + reqVO.setUserIds(getUserIds(reqVO)); | |
| 194 | + if (CollUtil.isEmpty(reqVO.getUserIds())) { | |
| 195 | + return Collections.emptyList(); | |
| 196 | + } | |
| 197 | + | |
| 198 | + // 2. 按天统计,获取分项统计数据 | |
| 199 | + List<CrmStatisticsPoolSummaryByDateRespVO> customerPutCountList = customerMapper.selectPoolCustomerPutCountByDate(reqVO); | |
| 200 | + List<CrmStatisticsPoolSummaryByDateRespVO> customerTakeCountList = customerMapper.selectPoolCustomerTakeCountByDate(reqVO); | |
| 201 | + | |
| 202 | + // 3. 按照日期间隔,合并数据 | |
| 203 | + List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval()); | |
| 204 | + return convertList(timeRanges, times -> { | |
| 205 | + Integer customerPutCount = customerPutCountList.stream() | |
| 206 | + .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime())) | |
| 207 | + .mapToInt(CrmStatisticsPoolSummaryByDateRespVO::getCustomerPutCount).sum(); | |
| 208 | + Integer customerTakeCount = customerTakeCountList.stream() | |
| 209 | + .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime())) | |
| 210 | + .mapToInt(CrmStatisticsPoolSummaryByDateRespVO::getCustomerTakeCount).sum(); | |
| 211 | + return new CrmStatisticsPoolSummaryByDateRespVO() | |
| 212 | + .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval())) | |
| 213 | + .setCustomerPutCount(customerPutCount).setCustomerTakeCount(customerTakeCount); | |
| 214 | + }); | |
| 215 | + } | |
| 216 | + | |
| 217 | + @Override | |
| 218 | + public List<CrmStatisticsPoolSummaryByUserRespVO> getPoolSummaryByUser(CrmStatisticsCustomerReqVO reqVO) { | |
| 219 | + // 1. 获得用户编号数组 | |
| 220 | + reqVO.setUserIds(getUserIds(reqVO)); | |
| 221 | + if (CollUtil.isEmpty(reqVO.getUserIds())) { | |
| 222 | + return Collections.emptyList(); | |
| 223 | + } | |
| 224 | + | |
| 225 | + // 2. 按用户统计,获取分项统计数据 | |
| 226 | + List<CrmStatisticsPoolSummaryByUserRespVO> customerPutCountList = customerMapper.selectPoolCustomerPutCountByUser(reqVO); | |
| 227 | + List<CrmStatisticsPoolSummaryByUserRespVO> customerTakeCountList = customerMapper.selectPoolCustomerTakeCountByUser(reqVO); | |
| 228 | + | |
| 229 | + // 3.1 按照用户,合并统计数据 | |
| 230 | + List<CrmStatisticsPoolSummaryByUserRespVO> summaryList = convertList(reqVO.getUserIds(), userId -> { | |
| 231 | + Integer customerPutCount = customerPutCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId())) | |
| 232 | + .mapToInt(CrmStatisticsPoolSummaryByUserRespVO::getCustomerPutCount).sum(); | |
| 233 | + Integer customerTakeCount = customerTakeCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId())) | |
| 234 | + .mapToInt(CrmStatisticsPoolSummaryByUserRespVO::getCustomerTakeCount).sum(); | |
| 235 | + return (CrmStatisticsPoolSummaryByUserRespVO) new CrmStatisticsPoolSummaryByUserRespVO() | |
| 236 | + .setCustomerPutCount(customerPutCount).setCustomerTakeCount(customerTakeCount) | |
| 237 | + .setOwnerUserId(userId); | |
| 238 | + }); | |
| 239 | + // 3.2 拼接用户信息 | |
| 240 | + appendUserInfo(summaryList); | |
| 241 | + return summaryList; | |
| 242 | + } | |
| 243 | + | |
| 244 | + @Override | |
| 245 | + public List<CrmStatisticsCustomerDealCycleByDateRespVO> getCustomerDealCycleByDate(CrmStatisticsCustomerReqVO reqVO) { | |
| 246 | + // 1. 获得用户编号数组 | |
| 247 | + reqVO.setUserIds(getUserIds(reqVO)); | |
| 248 | + if (CollUtil.isEmpty(reqVO.getUserIds())) { | |
| 249 | + return Collections.emptyList(); | |
| 250 | + } | |
| 251 | + | |
| 252 | + // 2. 按天统计,获取分项统计数据 | |
| 253 | + List<CrmStatisticsCustomerDealCycleByDateRespVO> customerDealCycleList = customerMapper.selectCustomerDealCycleGroupByDate(reqVO); | |
| 254 | + | |
| 255 | + // 3. 按照日期间隔,合并统计数据 | |
| 256 | + List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval()); | |
| 257 | + return convertList(timeRanges, times -> { | |
| 258 | + Double customerDealCycle = customerDealCycleList.stream() | |
| 259 | + .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime())) | |
| 260 | + .mapToDouble(CrmStatisticsCustomerDealCycleByDateRespVO::getCustomerDealCycle).sum(); | |
| 261 | + return new CrmStatisticsCustomerDealCycleByDateRespVO() | |
| 262 | + .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval())) | |
| 263 | + .setCustomerDealCycle(customerDealCycle); | |
| 264 | + }); | |
| 265 | + } | |
| 266 | + | |
| 267 | + @Override | |
| 268 | + public List<CrmStatisticsCustomerDealCycleByUserRespVO> getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO) { | |
| 269 | + // 1. 获得用户编号数组 | |
| 270 | + reqVO.setUserIds(getUserIds(reqVO)); | |
| 271 | + if (CollUtil.isEmpty(reqVO.getUserIds())) { | |
| 272 | + return Collections.emptyList(); | |
| 273 | + } | |
| 274 | + | |
| 275 | + // 2. 按用户统计,获取分项统计数据 | |
| 276 | + List<CrmStatisticsCustomerDealCycleByUserRespVO> customerDealCycleList = customerMapper.selectCustomerDealCycleGroupByUser(reqVO); | |
| 277 | + List<CrmStatisticsCustomerSummaryByUserRespVO> customerDealCountList = customerMapper.selectCustomerDealCountGroupByUser(reqVO); | |
| 278 | + | |
| 279 | + // 3.1 按照用户,合并统计数据 | |
| 280 | + List<CrmStatisticsCustomerDealCycleByUserRespVO> summaryList = convertList(reqVO.getUserIds(), userId -> { | |
| 281 | + Double customerDealCycle = customerDealCycleList.stream().filter(vo -> userId.equals(vo.getOwnerUserId())) | |
| 282 | + .mapToDouble(CrmStatisticsCustomerDealCycleByUserRespVO::getCustomerDealCycle).sum(); | |
| 283 | + Integer customerDealCount = customerDealCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId())) | |
| 284 | + .mapToInt(CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount).sum(); | |
| 285 | + return (CrmStatisticsCustomerDealCycleByUserRespVO) new CrmStatisticsCustomerDealCycleByUserRespVO() | |
| 286 | + .setCustomerDealCycle(customerDealCycle).setCustomerDealCount(customerDealCount).setOwnerUserId(userId); | |
| 287 | + }); | |
| 288 | + // 3.2 拼接用户信息 | |
| 289 | + appendUserInfo(summaryList); | |
| 290 | + return summaryList; | |
| 291 | + } | |
| 292 | + | |
| 293 | + /** | |
| 294 | + * 拼接用户信息(昵称) | |
| 295 | + * | |
| 296 | + * @param voList 统计数据 | |
| 297 | + */ | |
| 298 | + private <T extends CrmStatisticsCustomerByUserBaseRespVO> void appendUserInfo(List<T> voList) { | |
| 299 | + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap( | |
| 300 | + convertSet(voList, CrmStatisticsCustomerByUserBaseRespVO::getOwnerUserId)); | |
| 301 | + voList.forEach(vo -> findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname()))); | |
| 302 | + } | |
| 303 | + | |
| 304 | + /** | |
| 305 | + * 获取用户编号数组。如果用户编号为空, 则获得部门下的用户编号数组,包括子部门的所有用户编号 | |
| 306 | + * | |
| 307 | + * @param reqVO 请求参数 | |
| 308 | + * @return 用户编号数组 | |
| 309 | + */ | |
| 310 | + private List<Long> getUserIds(CrmStatisticsCustomerReqVO reqVO) { | |
| 311 | + // 情况一:选中某个用户 | |
| 312 | + if (ObjUtil.isNotNull(reqVO.getUserId())) { | |
| 313 | + return List.of(reqVO.getUserId()); | |
| 314 | + } | |
| 315 | + // 情况二:选中某个部门 | |
| 316 | + // 2.1 获得部门列表 | |
| 317 | + List<Long> deptIds = convertList(deptApi.getChildDeptList(reqVO.getDeptId()), DeptRespDTO::getId); | |
| 318 | + deptIds.add(reqVO.getDeptId()); | |
| 319 | + // 2.2 获得用户编号 | |
| 320 | + return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId); | |
| 321 | + } | |
| 322 | + | |
| 323 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPerformanceService.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.service.statistics; | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceReqVO; | |
| 6 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO; | |
| 7 | + | |
| 8 | +import java.util.List; | |
| 9 | + | |
| 10 | +/** | |
| 11 | + * CRM 员工绩效统计 Service 接口 | |
| 12 | + * | |
| 13 | + * @author scholar | |
| 14 | + */ | |
| 15 | +public interface CrmStatisticsPerformanceService { | |
| 16 | + | |
| 17 | + /** | |
| 18 | + * 员工签约合同数量分析 | |
| 19 | + * | |
| 20 | + * @param performanceReqVO 排行参数 | |
| 21 | + * @return 员工签约合同数量排行分析 | |
| 22 | + */ | |
| 23 | + List<CrmStatisticsPerformanceRespVO> getContractCountPerformance(CrmStatisticsPerformanceReqVO performanceReqVO); | |
| 24 | + | |
| 25 | + /** | |
| 26 | + * 员工签约合同金额分析 | |
| 27 | + * | |
| 28 | + * @param performanceReqVO 排行参数 | |
| 29 | + * @return 员工签约合同金额分析 | |
| 30 | + */ | |
| 31 | + List<CrmStatisticsPerformanceRespVO> getContractPricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO); | |
| 32 | + | |
| 33 | + /** | |
| 34 | + * 员工获得回款金额分析 | |
| 35 | + * | |
| 36 | + * @param performanceReqVO 排行参数 | |
| 37 | + * @return 员工获得回款金额分析 | |
| 38 | + */ | |
| 39 | + List<CrmStatisticsPerformanceRespVO> getReceivablePricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO); | |
| 40 | + | |
| 41 | + | |
| 42 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPerformanceServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.service.statistics; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.hutool.core.util.ObjUtil; | |
| 5 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceReqVO; | |
| 6 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO; | |
| 7 | +import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsPerformanceMapper; | |
| 8 | +import cn.iocoder.yudao.module.system.api.dept.DeptApi; | |
| 9 | +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; | |
| 10 | +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | |
| 11 | +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; | |
| 12 | +import org.springframework.stereotype.Service; | |
| 13 | +import org.springframework.validation.annotation.Validated; | |
| 14 | + | |
| 15 | +import jakarta.annotation.Resource; | |
| 16 | +import java.util.Collections; | |
| 17 | +import java.util.List; | |
| 18 | +import java.util.function.Function; | |
| 19 | + | |
| 20 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 21 | + | |
| 22 | +/** | |
| 23 | + * CRM 员工业绩分析 Service 实现类 | |
| 24 | + * | |
| 25 | + * @author scholar | |
| 26 | + */ | |
| 27 | +@Service | |
| 28 | +@Validated | |
| 29 | +public class CrmStatisticsPerformanceServiceImpl implements CrmStatisticsPerformanceService { | |
| 30 | + | |
| 31 | + @Resource | |
| 32 | + private CrmStatisticsPerformanceMapper performanceMapper; | |
| 33 | + | |
| 34 | + @Resource | |
| 35 | + private AdminUserApi adminUserApi; | |
| 36 | + @Resource | |
| 37 | + private DeptApi deptApi; | |
| 38 | + | |
| 39 | + @Override | |
| 40 | + public List<CrmStatisticsPerformanceRespVO> getContractCountPerformance(CrmStatisticsPerformanceReqVO performanceReqVO) { | |
| 41 | + // TODO @scholar:我们可以换个思路实现,减少数据库的计算量; | |
| 42 | + // 比如说,2024 年的合同数据,是不是 2022-12 到 2024-12-31,每个月的统计呢? | |
| 43 | + // 理解之后,我们可以数据 group by 年-月,20222-12 到 2024-12-31 的,然后内存在聚合出 CrmStatisticsPerformanceRespVO 这样 | |
| 44 | + // 这样,我们就可以减少数据库的计算量,提升性能;同时 SQL 也会很简单,开发者理解起来也简单哈; | |
| 45 | + return getPerformance(performanceReqVO, performanceMapper::selectContractCountPerformance); | |
| 46 | + } | |
| 47 | + | |
| 48 | + @Override | |
| 49 | + public List<CrmStatisticsPerformanceRespVO> getContractPricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO) { | |
| 50 | + return getPerformance(performanceReqVO, performanceMapper::selectContractPricePerformance); | |
| 51 | + } | |
| 52 | + | |
| 53 | + @Override | |
| 54 | + public List<CrmStatisticsPerformanceRespVO> getReceivablePricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO) { | |
| 55 | + return getPerformance(performanceReqVO, performanceMapper::selectReceivablePricePerformance); | |
| 56 | + } | |
| 57 | + | |
| 58 | + /** | |
| 59 | + * 获得员工业绩数据 | |
| 60 | + * | |
| 61 | + * @param performanceReqVO 参数 | |
| 62 | + * @param performanceFunction 排行榜方法 | |
| 63 | + * @return 排行版数据 | |
| 64 | + */ | |
| 65 | + private List<CrmStatisticsPerformanceRespVO> getPerformance(CrmStatisticsPerformanceReqVO performanceReqVO, Function<CrmStatisticsPerformanceReqVO, | |
| 66 | + List<CrmStatisticsPerformanceRespVO>> performanceFunction) { | |
| 67 | + | |
| 68 | + // 1. 获得用户编号数组 | |
| 69 | + final List<Long> userIds = getUserIds(performanceReqVO); | |
| 70 | + if (CollUtil.isEmpty(userIds)) { | |
| 71 | + return Collections.emptyList(); | |
| 72 | + } | |
| 73 | + performanceReqVO.setUserIds(userIds); | |
| 74 | + // 2. 获得排行数据 | |
| 75 | + List<CrmStatisticsPerformanceRespVO> performance = performanceFunction.apply(performanceReqVO); | |
| 76 | + if (CollUtil.isEmpty(performance)) { | |
| 77 | + return Collections.emptyList(); | |
| 78 | + } | |
| 79 | + return performance; | |
| 80 | + } | |
| 81 | + | |
| 82 | + /** | |
| 83 | + * 获取用户编号数组。如果用户编号为空, 则获得部门下的用户编号数组,包括子部门的所有用户编号 | |
| 84 | + * | |
| 85 | + * @param reqVO 请求参数 | |
| 86 | + * @return 用户编号数组 | |
| 87 | + */ | |
| 88 | + private List<Long> getUserIds(CrmStatisticsPerformanceReqVO reqVO) { | |
| 89 | + // 情况一:选中某个用户 | |
| 90 | + if (ObjUtil.isNotNull(reqVO.getUserId())) { | |
| 91 | + return List.of(reqVO.getUserId()); | |
| 92 | + } | |
| 93 | + // 情况二:选中某个部门 | |
| 94 | + // 2.1 获得部门列表 | |
| 95 | + final Long deptId = reqVO.getDeptId(); | |
| 96 | + List<Long> deptIds = convertList(deptApi.getChildDeptList(deptId), DeptRespDTO::getId); | |
| 97 | + deptIds.add(deptId); | |
| 98 | + // 2.2 获得用户编号 | |
| 99 | + return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId); | |
| 100 | + } | |
| 101 | + | |
| 102 | +} | |
| 0 | 103 | \ No newline at end of file | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitService.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.service.statistics; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.*; | |
| 4 | + | |
| 5 | +import java.util.List; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * CRM 客户画像 Service 接口 | |
| 9 | + * | |
| 10 | + * @author HUIHUI | |
| 11 | + */ | |
| 12 | +public interface CrmStatisticsPortraitService { | |
| 13 | + | |
| 14 | + /** | |
| 15 | + * 获取客户地区统计数据 | |
| 16 | + * | |
| 17 | + * @param reqVO 请求参数 | |
| 18 | + * @return 统计数据 | |
| 19 | + */ | |
| 20 | + List<CrmStatisticCustomerAreaRespVO> getCustomerSummaryByArea(CrmStatisticsPortraitReqVO reqVO); | |
| 21 | + | |
| 22 | + /** | |
| 23 | + * 获取客户行业统计数据 | |
| 24 | + * | |
| 25 | + * @param reqVO 请求参数 | |
| 26 | + * @return 统计数据 | |
| 27 | + */ | |
| 28 | + List<CrmStatisticCustomerIndustryRespVO> getCustomerSummaryByIndustry(CrmStatisticsPortraitReqVO reqVO); | |
| 29 | + | |
| 30 | + /** | |
| 31 | + * 获取客户级别统计数据 | |
| 32 | + * | |
| 33 | + * @param reqVO 请求参数 | |
| 34 | + * @return 统计数据 | |
| 35 | + */ | |
| 36 | + List<CrmStatisticCustomerLevelRespVO> getCustomerSummaryByLevel(CrmStatisticsPortraitReqVO reqVO); | |
| 37 | + | |
| 38 | + /** | |
| 39 | + * 获取客户来源统计数据 | |
| 40 | + * | |
| 41 | + * @param reqVO 请求参数 | |
| 42 | + * @return 统计数据 | |
| 43 | + */ | |
| 44 | + List<CrmStatisticCustomerSourceRespVO> getCustomerSummaryBySource(CrmStatisticsPortraitReqVO reqVO); | |
| 45 | + | |
| 46 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.service.statistics; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.hutool.core.util.ObjUtil; | |
| 5 | +import cn.iocoder.yudao.framework.ip.core.Area; | |
| 6 | +import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum; | |
| 7 | +import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; | |
| 8 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.*; | |
| 9 | +import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsPortraitMapper; | |
| 10 | +import cn.iocoder.yudao.module.system.api.dept.DeptApi; | |
| 11 | +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; | |
| 12 | +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | |
| 13 | +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; | |
| 14 | +import jakarta.annotation.Resource; | |
| 15 | +import org.springframework.stereotype.Service; | |
| 16 | + | |
| 17 | +import java.util.Collections; | |
| 18 | +import java.util.List; | |
| 19 | +import java.util.Map; | |
| 20 | + | |
| 21 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 22 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; | |
| 23 | +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; | |
| 24 | + | |
| 25 | +/** | |
| 26 | + * CRM 客户画像 Service 实现类 | |
| 27 | + * | |
| 28 | + * @author HUIHUI | |
| 29 | + */ | |
| 30 | +@Service | |
| 31 | +public class CrmStatisticsPortraitServiceImpl implements CrmStatisticsPortraitService { | |
| 32 | + | |
| 33 | + @Resource | |
| 34 | + private CrmStatisticsPortraitMapper portraitMapper; | |
| 35 | + | |
| 36 | + @Resource | |
| 37 | + private AdminUserApi adminUserApi; | |
| 38 | + @Resource | |
| 39 | + private DeptApi deptApi; | |
| 40 | + | |
| 41 | + @Override | |
| 42 | + public List<CrmStatisticCustomerAreaRespVO> getCustomerSummaryByArea(CrmStatisticsPortraitReqVO reqVO) { | |
| 43 | + // 1. 获得用户编号数组 | |
| 44 | + List<Long> userIds = getUserIds(reqVO); | |
| 45 | + if (CollUtil.isEmpty(userIds)) { | |
| 46 | + return Collections.emptyList(); | |
| 47 | + } | |
| 48 | + reqVO.setUserIds(userIds); | |
| 49 | + | |
| 50 | + // 2. 获取客户地区统计数据 | |
| 51 | + List<CrmStatisticCustomerAreaRespVO> list = portraitMapper.selectSummaryListGroupByAreaId(reqVO); | |
| 52 | + if (CollUtil.isEmpty(list)) { | |
| 53 | + return Collections.emptyList(); | |
| 54 | + } | |
| 55 | + | |
| 56 | + // 3. 拼接数据 | |
| 57 | + List<Area> areaList = AreaUtils.getByType(AreaTypeEnum.PROVINCE, area -> area); | |
| 58 | + areaList.add(new Area().setId(null).setName("未知")); // TODO @puhui999:是不是 65 find 的逻辑改下;不用 findAndThen,直接从 areaMap 拿;拿到就设置,不拿到就设置 null 和 未知;这样,58 本行可以删除掉完事了;这样代码更简单和一致 | |
| 59 | + Map<Integer, Area> areaMap = convertMap(areaList, Area::getId); | |
| 60 | + return convertList(list, item -> { | |
| 61 | + Integer parentId = AreaUtils.getParentIdByType(item.getAreaId(), AreaTypeEnum.PROVINCE); | |
| 62 | + if (parentId == null) { // 找不到,归到未知 | |
| 63 | + return item.setAreaId(null).setAreaName("未知"); | |
| 64 | + } | |
| 65 | + findAndThen(areaMap, parentId, area -> item.setAreaId(parentId).setAreaName(area.getName())); | |
| 66 | + return item; | |
| 67 | + }); | |
| 68 | + } | |
| 69 | + | |
| 70 | + @Override | |
| 71 | + public List<CrmStatisticCustomerIndustryRespVO> getCustomerSummaryByIndustry(CrmStatisticsPortraitReqVO reqVO) { | |
| 72 | + // 1. 获得用户编号数组 | |
| 73 | + List<Long> userIds = getUserIds(reqVO); | |
| 74 | + if (CollUtil.isEmpty(userIds)) { | |
| 75 | + return Collections.emptyList(); | |
| 76 | + } | |
| 77 | + reqVO.setUserIds(userIds); | |
| 78 | + | |
| 79 | + // 2. 获取客户行业统计数据 | |
| 80 | + return portraitMapper.selectCustomerIndustryListGroupByIndustryId(reqVO); | |
| 81 | + } | |
| 82 | + | |
| 83 | + @Override | |
| 84 | + public List<CrmStatisticCustomerSourceRespVO> getCustomerSummaryBySource(CrmStatisticsPortraitReqVO reqVO) { | |
| 85 | + // 1. 获得用户编号数组 | |
| 86 | + List<Long> userIds = getUserIds(reqVO); | |
| 87 | + if (CollUtil.isEmpty(userIds)) { | |
| 88 | + return Collections.emptyList(); | |
| 89 | + } | |
| 90 | + reqVO.setUserIds(userIds); | |
| 91 | + | |
| 92 | + // 2. 获取客户行业统计数据 | |
| 93 | + return portraitMapper.selectCustomerSourceListGroupBySource(reqVO); | |
| 94 | + } | |
| 95 | + | |
| 96 | + @Override | |
| 97 | + public List<CrmStatisticCustomerLevelRespVO> getCustomerSummaryByLevel(CrmStatisticsPortraitReqVO reqVO) { | |
| 98 | + // 1. 获得用户编号数组 | |
| 99 | + List<Long> userIds = getUserIds(reqVO); | |
| 100 | + if (CollUtil.isEmpty(userIds)) { | |
| 101 | + return Collections.emptyList(); | |
| 102 | + } | |
| 103 | + reqVO.setUserIds(userIds); | |
| 104 | + | |
| 105 | + // 2. 获取客户级别统计数据 | |
| 106 | + return portraitMapper.selectCustomerLevelListGroupByLevel(reqVO); | |
| 107 | + } | |
| 108 | + | |
| 109 | + /** | |
| 110 | + * 获取用户编号数组。如果用户编号为空, 则获得部门下的用户编号数组,包括子部门的所有用户编号 | |
| 111 | + * | |
| 112 | + * @param reqVO 请求参数 | |
| 113 | + * @return 用户编号数组 | |
| 114 | + */ | |
| 115 | + private List<Long> getUserIds(CrmStatisticsPortraitReqVO reqVO) { | |
| 116 | + // 情况一:选中某个用户 | |
| 117 | + if (ObjUtil.isNotNull(reqVO.getUserId())) { | |
| 118 | + return List.of(reqVO.getUserId()); | |
| 119 | + } | |
| 120 | + // 情况二:选中某个部门 | |
| 121 | + // 2.1 获得部门列表 | |
| 122 | + List<Long> deptIds = convertList(deptApi.getChildDeptList(reqVO.getDeptId()), DeptRespDTO::getId); | |
| 123 | + deptIds.add(reqVO.getDeptId()); | |
| 124 | + // 2.2 获得用户编号 | |
| 125 | + return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId); | |
| 126 | + } | |
| 127 | + | |
| 128 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsRankingService.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsRankService.java
| 1 | 1 | package cn.iocoder.yudao.module.crm.service.statistics; |
| 2 | 2 | |
| 3 | 3 | |
| 4 | -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO; | |
| 5 | -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRankReqVO; | |
| 4 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO; | |
| 5 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankReqVO; | |
| 6 | 6 | |
| 7 | 7 | import java.util.List; |
| 8 | 8 | |
| ... | ... | @@ -11,7 +11,7 @@ import java.util.List; |
| 11 | 11 | * |
| 12 | 12 | * @author anhaohao |
| 13 | 13 | */ |
| 14 | -public interface CrmStatisticsRankingService { | |
| 14 | +public interface CrmStatisticsRankService { | |
| 15 | 15 | |
| 16 | 16 | /** |
| 17 | 17 | * 获得合同金额排行榜 |
| ... | ... | @@ -19,7 +19,7 @@ public interface CrmStatisticsRankingService { |
| 19 | 19 | * @param rankReqVO 排行参数 |
| 20 | 20 | * @return 合同金额排行榜 |
| 21 | 21 | */ |
| 22 | - List<CrmStatisticsRanKRespVO> getContractPriceRank(CrmStatisticsRankReqVO rankReqVO); | |
| 22 | + List<CrmStatisticsRankRespVO> getContractPriceRank(CrmStatisticsRankReqVO rankReqVO); | |
| 23 | 23 | |
| 24 | 24 | /** |
| 25 | 25 | * 获得回款金额排行榜 |
| ... | ... | @@ -27,7 +27,7 @@ public interface CrmStatisticsRankingService { |
| 27 | 27 | * @param rankReqVO 排行参数 |
| 28 | 28 | * @return 回款金额排行榜 |
| 29 | 29 | */ |
| 30 | - List<CrmStatisticsRanKRespVO> getReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO); | |
| 30 | + List<CrmStatisticsRankRespVO> getReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO); | |
| 31 | 31 | |
| 32 | 32 | /** |
| 33 | 33 | * 获得签约合同数量排行榜 |
| ... | ... | @@ -35,7 +35,7 @@ public interface CrmStatisticsRankingService { |
| 35 | 35 | * @param rankReqVO 排行参数 |
| 36 | 36 | * @return 签约合同数量排行榜 |
| 37 | 37 | */ |
| 38 | - List<CrmStatisticsRanKRespVO> getContractCountRank(CrmStatisticsRankReqVO rankReqVO); | |
| 38 | + List<CrmStatisticsRankRespVO> getContractCountRank(CrmStatisticsRankReqVO rankReqVO); | |
| 39 | 39 | |
| 40 | 40 | /** |
| 41 | 41 | * 获得产品销量排行榜 |
| ... | ... | @@ -43,7 +43,7 @@ public interface CrmStatisticsRankingService { |
| 43 | 43 | * @param rankReqVO 排行参数 |
| 44 | 44 | * @return 产品销量排行榜 |
| 45 | 45 | */ |
| 46 | - List<CrmStatisticsRanKRespVO> getProductSalesRank(CrmStatisticsRankReqVO rankReqVO); | |
| 46 | + List<CrmStatisticsRankRespVO> getProductSalesRank(CrmStatisticsRankReqVO rankReqVO); | |
| 47 | 47 | |
| 48 | 48 | /** |
| 49 | 49 | * 获得新增客户数排行榜 |
| ... | ... | @@ -51,7 +51,7 @@ public interface CrmStatisticsRankingService { |
| 51 | 51 | * @param rankReqVO 排行参数 |
| 52 | 52 | * @return 新增客户数排行榜 |
| 53 | 53 | */ |
| 54 | - List<CrmStatisticsRanKRespVO> getCustomerCountRank(CrmStatisticsRankReqVO rankReqVO); | |
| 54 | + List<CrmStatisticsRankRespVO> getCustomerCountRank(CrmStatisticsRankReqVO rankReqVO); | |
| 55 | 55 | |
| 56 | 56 | /** |
| 57 | 57 | * 获得联系人数量排行榜 |
| ... | ... | @@ -59,7 +59,7 @@ public interface CrmStatisticsRankingService { |
| 59 | 59 | * @param rankReqVO 排行参数 |
| 60 | 60 | * @return 联系人数量排行榜 |
| 61 | 61 | */ |
| 62 | - List<CrmStatisticsRanKRespVO> getContactsCountRank(CrmStatisticsRankReqVO rankReqVO); | |
| 62 | + List<CrmStatisticsRankRespVO> getContactsCountRank(CrmStatisticsRankReqVO rankReqVO); | |
| 63 | 63 | |
| 64 | 64 | /** |
| 65 | 65 | * 获得跟进次数排行榜 |
| ... | ... | @@ -67,7 +67,7 @@ public interface CrmStatisticsRankingService { |
| 67 | 67 | * @param rankReqVO 排行参数 |
| 68 | 68 | * @return 跟进次数排行榜 |
| 69 | 69 | */ |
| 70 | - List<CrmStatisticsRanKRespVO> getFollowCountRank(CrmStatisticsRankReqVO rankReqVO); | |
| 70 | + List<CrmStatisticsRankRespVO> getFollowCountRank(CrmStatisticsRankReqVO rankReqVO); | |
| 71 | 71 | |
| 72 | 72 | /** |
| 73 | 73 | * 获得跟进客户数排行榜 |
| ... | ... | @@ -75,6 +75,6 @@ public interface CrmStatisticsRankingService { |
| 75 | 75 | * @param rankReqVO 排行参数 |
| 76 | 76 | * @return 跟进客户数排行榜 |
| 77 | 77 | */ |
| 78 | - List<CrmStatisticsRanKRespVO> getFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO); | |
| 78 | + List<CrmStatisticsRankRespVO> getFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO); | |
| 79 | 79 | |
| 80 | 80 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsRankingServiceImpl.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsRankServiceImpl.java
| ... | ... | @@ -2,9 +2,9 @@ package cn.iocoder.yudao.module.crm.service.statistics; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.util.collection.MapUtils; |
| 5 | -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO; | |
| 6 | -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRankReqVO; | |
| 7 | -import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsRankingMapper; | |
| 5 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankReqVO; | |
| 6 | +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO; | |
| 7 | +import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsRankMapper; | |
| 8 | 8 | import cn.iocoder.yudao.module.system.api.dept.DeptApi; |
| 9 | 9 | import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; |
| 10 | 10 | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| ... | ... | @@ -29,10 +29,10 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. |
| 29 | 29 | */ |
| 30 | 30 | @Service |
| 31 | 31 | @Validated |
| 32 | -public class CrmStatisticsRankingServiceImpl implements CrmStatisticsRankingService { | |
| 32 | +public class CrmStatisticsRankServiceImpl implements CrmStatisticsRankService { | |
| 33 | 33 | |
| 34 | 34 | @Resource |
| 35 | - private CrmStatisticsRankingMapper rankMapper; | |
| 35 | + private CrmStatisticsRankMapper rankMapper; | |
| 36 | 36 | |
| 37 | 37 | @Resource |
| 38 | 38 | private AdminUserApi adminUserApi; |
| ... | ... | @@ -40,64 +40,64 @@ public class CrmStatisticsRankingServiceImpl implements CrmStatisticsRankingServ |
| 40 | 40 | private DeptApi deptApi; |
| 41 | 41 | |
| 42 | 42 | @Override |
| 43 | - public List<CrmStatisticsRanKRespVO> getContractPriceRank(CrmStatisticsRankReqVO rankReqVO) { | |
| 43 | + public List<CrmStatisticsRankRespVO> getContractPriceRank(CrmStatisticsRankReqVO rankReqVO) { | |
| 44 | 44 | return getRank(rankReqVO, rankMapper::selectContractPriceRank); |
| 45 | 45 | } |
| 46 | 46 | |
| 47 | 47 | @Override |
| 48 | - public List<CrmStatisticsRanKRespVO> getReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO) { | |
| 48 | + public List<CrmStatisticsRankRespVO> getReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO) { | |
| 49 | 49 | return getRank(rankReqVO, rankMapper::selectReceivablePriceRank); |
| 50 | 50 | } |
| 51 | 51 | |
| 52 | 52 | @Override |
| 53 | - public List<CrmStatisticsRanKRespVO> getContractCountRank(CrmStatisticsRankReqVO rankReqVO) { | |
| 53 | + public List<CrmStatisticsRankRespVO> getContractCountRank(CrmStatisticsRankReqVO rankReqVO) { | |
| 54 | 54 | return getRank(rankReqVO, rankMapper::selectContractCountRank); |
| 55 | 55 | } |
| 56 | 56 | |
| 57 | 57 | @Override |
| 58 | - public List<CrmStatisticsRanKRespVO> getProductSalesRank(CrmStatisticsRankReqVO rankReqVO) { | |
| 58 | + public List<CrmStatisticsRankRespVO> getProductSalesRank(CrmStatisticsRankReqVO rankReqVO) { | |
| 59 | 59 | return getRank(rankReqVO, rankMapper::selectProductSalesRank); |
| 60 | 60 | } |
| 61 | 61 | |
| 62 | 62 | @Override |
| 63 | - public List<CrmStatisticsRanKRespVO> getCustomerCountRank(CrmStatisticsRankReqVO rankReqVO) { | |
| 63 | + public List<CrmStatisticsRankRespVO> getCustomerCountRank(CrmStatisticsRankReqVO rankReqVO) { | |
| 64 | 64 | return getRank(rankReqVO, rankMapper::selectCustomerCountRank); |
| 65 | 65 | } |
| 66 | 66 | |
| 67 | 67 | @Override |
| 68 | - public List<CrmStatisticsRanKRespVO> getContactsCountRank(CrmStatisticsRankReqVO rankReqVO) { | |
| 68 | + public List<CrmStatisticsRankRespVO> getContactsCountRank(CrmStatisticsRankReqVO rankReqVO) { | |
| 69 | 69 | return getRank(rankReqVO, rankMapper::selectContactsCountRank); |
| 70 | 70 | } |
| 71 | 71 | |
| 72 | 72 | @Override |
| 73 | - public List<CrmStatisticsRanKRespVO> getFollowCountRank(CrmStatisticsRankReqVO rankReqVO) { | |
| 73 | + public List<CrmStatisticsRankRespVO> getFollowCountRank(CrmStatisticsRankReqVO rankReqVO) { | |
| 74 | 74 | return getRank(rankReqVO, rankMapper::selectFollowCountRank); |
| 75 | 75 | } |
| 76 | 76 | |
| 77 | 77 | @Override |
| 78 | - public List<CrmStatisticsRanKRespVO> getFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO) { | |
| 78 | + public List<CrmStatisticsRankRespVO> getFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO) { | |
| 79 | 79 | return getRank(rankReqVO, rankMapper::selectFollowCustomerCountRank); |
| 80 | 80 | } |
| 81 | 81 | |
| 82 | 82 | /** |
| 83 | 83 | * 获得排行版数据 |
| 84 | 84 | * |
| 85 | - * @param rankReqVO 参数 | |
| 85 | + * @param rankReqVO 参数 | |
| 86 | 86 | * @param rankFunction 排行榜方法 |
| 87 | 87 | * @return 排行版数据 |
| 88 | 88 | */ |
| 89 | - private List<CrmStatisticsRanKRespVO> getRank(CrmStatisticsRankReqVO rankReqVO, Function<CrmStatisticsRankReqVO, List<CrmStatisticsRanKRespVO>> rankFunction) { | |
| 89 | + private List<CrmStatisticsRankRespVO> getRank(CrmStatisticsRankReqVO rankReqVO, Function<CrmStatisticsRankReqVO, List<CrmStatisticsRankRespVO>> rankFunction) { | |
| 90 | 90 | // 1. 获得用户编号数组 |
| 91 | 91 | rankReqVO.setUserIds(getUserIds(rankReqVO.getDeptId())); |
| 92 | 92 | if (CollUtil.isEmpty(rankReqVO.getUserIds())) { |
| 93 | 93 | return Collections.emptyList(); |
| 94 | 94 | } |
| 95 | 95 | // 2. 获得排行数据 |
| 96 | - List<CrmStatisticsRanKRespVO> ranks = rankFunction.apply(rankReqVO); | |
| 96 | + List<CrmStatisticsRankRespVO> ranks = rankFunction.apply(rankReqVO); | |
| 97 | 97 | if (CollUtil.isEmpty(ranks)) { |
| 98 | 98 | return Collections.emptyList(); |
| 99 | 99 | } |
| 100 | - ranks.sort(Comparator.comparing(CrmStatisticsRanKRespVO::getCount).reversed()); | |
| 100 | + ranks.sort(Comparator.comparing(CrmStatisticsRankRespVO::getCount).reversed()); | |
| 101 | 101 | // 3. 拼接用户信息 |
| 102 | 102 | appendUserInfo(ranks); |
| 103 | 103 | return ranks; |
| ... | ... | @@ -108,8 +108,8 @@ public class CrmStatisticsRankingServiceImpl implements CrmStatisticsRankingServ |
| 108 | 108 | * |
| 109 | 109 | * @param ranks 排行榜数据 |
| 110 | 110 | */ |
| 111 | - private void appendUserInfo(List<CrmStatisticsRanKRespVO> ranks) { | |
| 112 | - Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(ranks, CrmStatisticsRanKRespVO::getOwnerUserId)); | |
| 111 | + private void appendUserInfo(List<CrmStatisticsRankRespVO> ranks) { | |
| 112 | + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(ranks, CrmStatisticsRankRespVO::getOwnerUserId)); | |
| 113 | 113 | Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); |
| 114 | 114 | ranks.forEach(rank -> MapUtils.findAndThen(userMap, rank.getOwnerUserId(), user -> { |
| 115 | 115 | rank.setNickname(user.getNickname()); | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | |
| 3 | +<mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper"> | |
| 4 | + | |
| 5 | + <select id="selectCustomerCreateCountGroupByDate" | |
| 6 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByDateRespVO"> | |
| 7 | + SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS time, | |
| 8 | + COUNT(*) AS customerCreateCount | |
| 9 | + FROM crm_customer | |
| 10 | + WHERE deleted = 0 | |
| 11 | + AND owner_user_id IN | |
| 12 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 13 | + #{userId} | |
| 14 | + </foreach> | |
| 15 | + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} | |
| 16 | + GROUP BY time | |
| 17 | + </select> | |
| 18 | + | |
| 19 | + <!-- TODO 芋艿:应该不用过滤时间 --> | |
| 20 | + <select id="selectCustomerDealCountGroupByDate" | |
| 21 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByDateRespVO"> | |
| 22 | + SELECT DATE_FORMAT(customer.create_time, '%Y-%m-%d') AS time, | |
| 23 | + COUNT(DISTINCT customer.id) AS customer_deal_count | |
| 24 | + FROM crm_customer AS customer | |
| 25 | + LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id | |
| 26 | + WHERE customer.deleted = 0 AND contract.deleted = 0 | |
| 27 | + AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status} | |
| 28 | + AND customer.owner_user_id IN | |
| 29 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 30 | + #{userId} | |
| 31 | + </foreach> | |
| 32 | + AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} | |
| 33 | + GROUP BY time | |
| 34 | + </select> | |
| 35 | + | |
| 36 | + <select id="selectCustomerCreateCountGroupByUser" | |
| 37 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByUserRespVO"> | |
| 38 | + SELECT owner_user_id, | |
| 39 | + COUNT(*) AS customer_create_count | |
| 40 | + FROM crm_customer | |
| 41 | + WHERE deleted = 0 | |
| 42 | + AND owner_user_id in | |
| 43 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 44 | + #{userId} | |
| 45 | + </foreach> | |
| 46 | + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} | |
| 47 | + GROUP BY owner_user_id | |
| 48 | + </select> | |
| 49 | + | |
| 50 | + <select id="selectCustomerDealCountGroupByUser" | |
| 51 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByUserRespVO"> | |
| 52 | + SELECT customer.owner_user_id, | |
| 53 | + COUNT(DISTINCT customer.id) AS customer_deal_count | |
| 54 | + FROM crm_customer AS customer | |
| 55 | + LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id | |
| 56 | + WHERE customer.deleted = 0 AND contract.deleted = 0 | |
| 57 | + AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status} | |
| 58 | + AND customer.owner_user_id IN | |
| 59 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 60 | + #{userId} | |
| 61 | + </foreach> | |
| 62 | + AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} | |
| 63 | + GROUP BY customer.owner_user_id | |
| 64 | + </select> | |
| 65 | + | |
| 66 | + <select id="selectContractPriceGroupByUser" | |
| 67 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByUserRespVO"> | |
| 68 | + SELECT owner_user_id, | |
| 69 | + IFNULL(SUM(total_price), 0) AS contract_price | |
| 70 | + FROM crm_contract | |
| 71 | + WHERE deleted = 0 | |
| 72 | + AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status} | |
| 73 | + AND owner_user_id in | |
| 74 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 75 | + #{userId} | |
| 76 | + </foreach> | |
| 77 | + AND order_date BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} | |
| 78 | + GROUP BY owner_user_id | |
| 79 | + </select> | |
| 80 | + | |
| 81 | + <select id="selectReceivablePriceGroupByUser" | |
| 82 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerSummaryByUserRespVO"> | |
| 83 | + SELECT owner_user_id, | |
| 84 | + IFNULL(SUM(price), 0) AS receivable_price | |
| 85 | + FROM crm_receivable | |
| 86 | + WHERE deleted = 0 | |
| 87 | + AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status} | |
| 88 | + AND owner_user_id IN | |
| 89 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 90 | + #{userId} | |
| 91 | + </foreach> | |
| 92 | + AND return_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} | |
| 93 | + GROUP BY owner_user_id | |
| 94 | + </select> | |
| 95 | + | |
| 96 | + <select id="selectFollowUpRecordCountGroupByDate" | |
| 97 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowUpSummaryByDateRespVO"> | |
| 98 | + SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS time, | |
| 99 | + COUNT(*) AS follow_up_record_count | |
| 100 | + FROM crm_follow_up_record | |
| 101 | + WHERE creator IN | |
| 102 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 103 | + #{userId} | |
| 104 | + </foreach> | |
| 105 | + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} | |
| 106 | + AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type} | |
| 107 | + GROUP BY time | |
| 108 | + </select> | |
| 109 | + | |
| 110 | + <select id="selectFollowUpCustomerCountGroupByDate" | |
| 111 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowUpSummaryByDateRespVO"> | |
| 112 | + SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS time, | |
| 113 | + COUNT(DISTINCT biz_id) AS follow_up_customer_count | |
| 114 | + FROM crm_follow_up_record | |
| 115 | + WHERE creator IN | |
| 116 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 117 | + #{userId} | |
| 118 | + </foreach> | |
| 119 | + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} | |
| 120 | + AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type} | |
| 121 | + GROUP BY time | |
| 122 | + </select> | |
| 123 | + | |
| 124 | + <select id="selectFollowUpRecordCountGroupByUser" | |
| 125 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowUpSummaryByUserRespVO"> | |
| 126 | + SELECT creator as owner_user_id, | |
| 127 | + COUNT(*) AS follow_up_record_count | |
| 128 | + FROM crm_follow_up_record | |
| 129 | + WHERE creator IN | |
| 130 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 131 | + #{userId} | |
| 132 | + </foreach> | |
| 133 | + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} | |
| 134 | + AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type} | |
| 135 | + GROUP BY creator | |
| 136 | + </select> | |
| 137 | + | |
| 138 | + <select id="selectFollowUpCustomerCountGroupByUser" | |
| 139 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowUpSummaryByUserRespVO"> | |
| 140 | + SELECT | |
| 141 | + creator as owner_user_id, | |
| 142 | + COUNT(DISTINCT biz_id) AS follow_up_customer_count | |
| 143 | + FROM crm_follow_up_record | |
| 144 | + WHERE creator IN | |
| 145 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 146 | + #{userId} | |
| 147 | + </foreach> | |
| 148 | + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} | |
| 149 | + AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type} | |
| 150 | + GROUP BY creator | |
| 151 | + </select> | |
| 152 | + | |
| 153 | + <select id="selectFollowUpRecordCountGroupByType" | |
| 154 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsFollowUpSummaryByTypeRespVO"> | |
| 155 | + SELECT type AS follow_up_type, | |
| 156 | + COUNT(*) AS follow_up_record_count | |
| 157 | + FROM crm_follow_up_record | |
| 158 | + WHERE creator IN | |
| 159 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 160 | + #{userId} | |
| 161 | + </foreach> | |
| 162 | + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} | |
| 163 | + AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type} | |
| 164 | + GROUP BY follow_up_type | |
| 165 | + </select> | |
| 166 | + | |
| 167 | + <select id="selectContractSummary" | |
| 168 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerContractSummaryRespVO"> | |
| 169 | + SELECT customer.name AS customer_name, | |
| 170 | + customer.industry_id, | |
| 171 | + customer.source, | |
| 172 | + customer.owner_user_id, | |
| 173 | + customer.creator, | |
| 174 | + customer.create_time, | |
| 175 | + contract.name AS contract_name, | |
| 176 | + contract.total_price, | |
| 177 | + contract.order_date, | |
| 178 | + IFNULL(receivable.price, 0) AS receivable_price | |
| 179 | + FROM crm_customer AS customer | |
| 180 | + INNER JOIN crm_contract AS contract ON customer.id = contract.customer_id | |
| 181 | + LEFT JOIN crm_receivable AS receivable ON contract.id = receivable.contract_id | |
| 182 | + WHERE customer.deleted = 0 AND contract.deleted = 0 AND receivable.deleted = 0 | |
| 183 | + AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status} | |
| 184 | + AND customer.owner_user_id IN | |
| 185 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 186 | + #{userId} | |
| 187 | + </foreach> | |
| 188 | + AND customer.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} | |
| 189 | + </select> | |
| 190 | + | |
| 191 | + <!-- TIMESTAMPDIFF 用于求差值;AVG 求平均;TRUNCATE 去掉小数点、只保留整数 --> | |
| 192 | + <select id="selectCustomerDealCycleGroupByDate" | |
| 193 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerDealCycleByDateRespVO"> | |
| 194 | + SELECT DATE_FORMAT(contract.order_date, '%Y-%m-%d') AS time, | |
| 195 | + IFNULL(TRUNCATE(AVG(TIMESTAMPDIFF(DAY, customer.create_time, contract.order_date)), 1), 0) AS customer_deal_cycle | |
| 196 | + FROM crm_customer AS customer | |
| 197 | + LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id | |
| 198 | + WHERE customer.deleted = 0 AND contract.deleted = 0 | |
| 199 | + AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status} | |
| 200 | + AND customer.owner_user_id IN | |
| 201 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 202 | + #{userId} | |
| 203 | + </foreach> | |
| 204 | + AND customer.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} | |
| 205 | + GROUP BY time | |
| 206 | + </select> | |
| 207 | + | |
| 208 | + <select id="selectCustomerDealCycleGroupByUser" | |
| 209 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerDealCycleByUserRespVO"> | |
| 210 | + SELECT customer.owner_user_id, | |
| 211 | + IFNULL(TRUNCATE(AVG(TIMESTAMPDIFF(DAY, customer.create_time, contract.order_date)), 1), 0) AS customer_deal_cycle | |
| 212 | + FROM crm_customer AS customer | |
| 213 | + LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id | |
| 214 | + WHERE customer.deleted = 0 AND contract.deleted = 0 | |
| 215 | + AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status} | |
| 216 | + AND customer.owner_user_id IN | |
| 217 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 218 | + #{userId} | |
| 219 | + </foreach> | |
| 220 | + AND customer.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} | |
| 221 | + GROUP BY customer.owner_user_id | |
| 222 | + </select> | |
| 223 | + | |
| 224 | +</mapper> | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsPerformanceMapper.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | |
| 3 | +<mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsPerformanceMapper"> | |
| 4 | + | |
| 5 | + <select id="selectContractCountPerformance" | |
| 6 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO"> | |
| 7 | + SELECT | |
| 8 | + t.time as time, | |
| 9 | + COALESCE(t.currentMonthCount,0) as currentMonthCount, | |
| 10 | + COALESCE(y.lastMonthCount,0) as lastMonthCount, | |
| 11 | + COALESCE(z.lastYearCount,0) as lastYearCount | |
| 12 | + FROM | |
| 13 | + (SELECT | |
| 14 | + COUNT(1) AS currentMonthCount, | |
| 15 | + DATE_FORMAT(order_date, '%Y-%m') AS time | |
| 16 | + FROM crm_contract | |
| 17 | + WHERE deleted = 0 | |
| 18 | + AND audit_status = 20 | |
| 19 | + AND owner_user_id in | |
| 20 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 21 | + #{userId} | |
| 22 | + </foreach> | |
| 23 | + AND DATE_FORMAT(order_date, '%Y') = DATE_FORMAT(#{times[0],javaType=java.time.LocalDateTime},'%Y') | |
| 24 | + GROUP BY time)t | |
| 25 | + LEFT JOIN | |
| 26 | + (SELECT | |
| 27 | + COUNT(1) AS lastMonthCount, | |
| 28 | + DATE_FORMAT(DATE_ADD(order_date,INTERVAL 1 MONTH), '%Y-%m') AS time | |
| 29 | + FROM crm_contract | |
| 30 | + WHERE deleted = 0 | |
| 31 | + AND audit_status = 20 | |
| 32 | + AND owner_user_id in | |
| 33 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 34 | + #{userId} | |
| 35 | + </foreach> | |
| 36 | + AND (DATE_FORMAT(order_date, '%Y') = DATE_FORMAT(#{times[0],javaType=java.time.LocalDateTime},'%Y') | |
| 37 | + or DATE_FORMAT(order_date, '%Y') = DATE_FORMAT(#{times[0],javaType=java.time.LocalDateTime},'%Y')-1) | |
| 38 | + GROUP BY time)y ON t.time = y.time | |
| 39 | + LEFT JOIN | |
| 40 | + (SELECT | |
| 41 | + COUNT(1) AS lastYearCount, | |
| 42 | + DATE_FORMAT(DATE_ADD(order_date,INTERVAL 1 YEAR), '%Y-%m') AS time | |
| 43 | + FROM crm_contract | |
| 44 | + WHERE deleted = 0 | |
| 45 | + AND audit_status = 20 | |
| 46 | + AND owner_user_id in | |
| 47 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 48 | + #{userId} | |
| 49 | + </foreach> | |
| 50 | + AND DATE_FORMAT(order_date, '%Y') = DATE_FORMAT(#{times[0],javaType=java.time.LocalDateTime},'%Y')-1 | |
| 51 | + GROUP BY time)z ON t.time = z.time | |
| 52 | + </select> | |
| 53 | + <select id="selectContractPricePerformance" | |
| 54 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO"> | |
| 55 | + SELECT | |
| 56 | + t.time as time, | |
| 57 | + COALESCE(t.currentMonthCount,0) as currentMonthCount, | |
| 58 | + COALESCE(y.lastMonthCount,0) as lastMonthCount, | |
| 59 | + COALESCE(z.lastYearCount,0) as lastYearCount | |
| 60 | + FROM | |
| 61 | + (SELECT | |
| 62 | + IFNULL(SUM(total_price), 0) AS currentMonthCount, | |
| 63 | + DATE_FORMAT(order_date, '%Y-%m') AS time | |
| 64 | + FROM crm_contract | |
| 65 | + WHERE deleted = 0 | |
| 66 | + AND audit_status = 20 | |
| 67 | + AND owner_user_id in | |
| 68 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 69 | + #{userId} | |
| 70 | + </foreach> | |
| 71 | + AND DATE_FORMAT(order_date, '%Y') = DATE_FORMAT(#{times[0],javaType=java.time.LocalDateTime},'%Y') | |
| 72 | + GROUP BY time)t | |
| 73 | + LEFT JOIN | |
| 74 | + (SELECT | |
| 75 | + IFNULL(SUM(total_price), 0) AS lastMonthCount, | |
| 76 | + DATE_FORMAT(DATE_ADD(order_date,INTERVAL 1 MONTH), '%Y-%m') AS time | |
| 77 | + FROM crm_contract | |
| 78 | + WHERE deleted = 0 | |
| 79 | + AND audit_status = 20 | |
| 80 | + AND owner_user_id in | |
| 81 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 82 | + #{userId} | |
| 83 | + </foreach> | |
| 84 | + AND (DATE_FORMAT(order_date, '%Y') = DATE_FORMAT(#{times[0],javaType=java.time.LocalDateTime},'%Y') | |
| 85 | + or DATE_FORMAT(order_date, '%Y') = DATE_FORMAT(#{times[0],javaType=java.time.LocalDateTime},'%Y')-1) | |
| 86 | + GROUP BY time)y ON t.time = y.time | |
| 87 | + LEFT JOIN | |
| 88 | + (SELECT | |
| 89 | + IFNULL(SUM(total_price), 0) AS lastYearCount, | |
| 90 | + DATE_FORMAT(DATE_ADD(order_date,INTERVAL 1 YEAR), '%Y-%m') AS time | |
| 91 | + FROM crm_contract | |
| 92 | + WHERE deleted = 0 | |
| 93 | + AND audit_status = 20 | |
| 94 | + AND owner_user_id in | |
| 95 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 96 | + #{userId} | |
| 97 | + </foreach> | |
| 98 | + AND DATE_FORMAT(order_date, '%Y') = DATE_FORMAT(#{times[0],javaType=java.time.LocalDateTime},'%Y')-1 | |
| 99 | + GROUP BY time)z ON t.time = z.time | |
| 100 | + </select> | |
| 101 | + | |
| 102 | + <select id="selectReceivablePricePerformance" | |
| 103 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO"> | |
| 104 | + SELECT | |
| 105 | + t.time as time, | |
| 106 | + COALESCE(t.currentMonthCount,0) as currentMonthCount, | |
| 107 | + COALESCE(y.lastMonthCount,0) as lastMonthCount, | |
| 108 | + COALESCE(z.lastYearCount,0) as lastYearCount | |
| 109 | + FROM | |
| 110 | + (SELECT | |
| 111 | + IFNULL(SUM(price), 0) AS currentMonthCount, | |
| 112 | + DATE_FORMAT(return_time, '%Y-%m') AS time | |
| 113 | + FROM crm_receivable | |
| 114 | + WHERE deleted = 0 | |
| 115 | + AND audit_status = 20 | |
| 116 | + AND owner_user_id in | |
| 117 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 118 | + #{userId} | |
| 119 | + </foreach> | |
| 120 | + AND DATE_FORMAT(return_time, '%Y') = DATE_FORMAT(#{times[0],javaType=java.time.LocalDateTime},'%Y') | |
| 121 | + GROUP BY time)t | |
| 122 | + LEFT JOIN | |
| 123 | + (SELECT | |
| 124 | + IFNULL(SUM(price), 0) AS lastMonthCount, | |
| 125 | + DATE_FORMAT(DATE_ADD(return_time,INTERVAL 1 MONTH), '%Y-%m') AS time | |
| 126 | + FROM crm_receivable | |
| 127 | + WHERE deleted = 0 | |
| 128 | + AND audit_status = 20 | |
| 129 | + AND owner_user_id in | |
| 130 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 131 | + #{userId} | |
| 132 | + </foreach> | |
| 133 | + AND (DATE_FORMAT(return_time, '%Y') = DATE_FORMAT(#{times[0],javaType=java.time.LocalDateTime},'%Y') | |
| 134 | + or DATE_FORMAT(return_time, '%Y') = DATE_FORMAT(#{times[0],javaType=java.time.LocalDateTime},'%Y')-1) | |
| 135 | + GROUP BY time)y ON t.time = y.time | |
| 136 | + LEFT JOIN | |
| 137 | + (SELECT | |
| 138 | + IFNULL(SUM(price), 0) AS lastYearCount, | |
| 139 | + DATE_FORMAT(DATE_ADD(return_time,INTERVAL 1 YEAR), '%Y-%m') AS time | |
| 140 | + FROM crm_receivable | |
| 141 | + WHERE deleted = 0 | |
| 142 | + AND audit_status = 20 | |
| 143 | + AND owner_user_id in | |
| 144 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 145 | + #{userId} | |
| 146 | + </foreach> | |
| 147 | + AND DATE_FORMAT(return_time, '%Y') = DATE_FORMAT(#{times[0],javaType=java.time.LocalDateTime},'%Y')-1 | |
| 148 | + GROUP BY time)z ON t.time = z.time | |
| 149 | + </select> | |
| 150 | + | |
| 151 | + | |
| 152 | +</mapper> | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsPortraitMapper.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | |
| 3 | +<mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsPortraitMapper"> | |
| 4 | + | |
| 5 | + <select id="selectSummaryListGroupByAreaId" | |
| 6 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerAreaRespVO"> | |
| 7 | + SELECT area_id, COUNT(*) AS customerCount, SUM(deal_status) AS dealCount | |
| 8 | + FROM crm_customer | |
| 9 | + WHERE deleted = 0 AND area_id IS NOT NULL | |
| 10 | + AND owner_user_id IN | |
| 11 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 12 | + #{userId} | |
| 13 | + </foreach> | |
| 14 | + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} | |
| 15 | + AND #{times[1],javaType=java.time.LocalDateTime} | |
| 16 | + GROUP BY area_id | |
| 17 | + </select> | |
| 18 | + | |
| 19 | + <select id="selectCustomerIndustryListGroupByIndustryId" | |
| 20 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerIndustryRespVO"> | |
| 21 | + SELECT industry_id, COUNT(*) AS customerCount, SUM(deal_status) AS dealCount | |
| 22 | + FROM crm_customer | |
| 23 | + WHERE deleted = 0 AND industry_id IS NOT NULL | |
| 24 | + AND owner_user_id IN | |
| 25 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 26 | + #{userId} | |
| 27 | + </foreach> | |
| 28 | + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} | |
| 29 | + AND #{times[1],javaType=java.time.LocalDateTime} | |
| 30 | + GROUP BY industry_id | |
| 31 | + </select> | |
| 32 | + | |
| 33 | + <select id="selectCustomerSourceListGroupBySource" | |
| 34 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerSourceRespVO"> | |
| 35 | + SELECT source, COUNT(*) AS customerCount, SUM(deal_status) AS dealCount | |
| 36 | + FROM crm_customer | |
| 37 | + WHERE deleted = 0 AND source IS NOT NULL | |
| 38 | + AND owner_user_id IN | |
| 39 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 40 | + #{userId} | |
| 41 | + </foreach> | |
| 42 | + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} | |
| 43 | + AND #{times[1],javaType=java.time.LocalDateTime} | |
| 44 | + GROUP BY source | |
| 45 | + </select> | |
| 46 | + | |
| 47 | + <select id="selectCustomerLevelListGroupByLevel" | |
| 48 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerLevelRespVO"> | |
| 49 | + SELECT level, COUNT(*) AS customerCount, SUM(deal_status) AS dealCount | |
| 50 | + FROM crm_customer | |
| 51 | + WHERE deleted = 0 AND level IS NOT NULL | |
| 52 | + AND owner_user_id IN | |
| 53 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 54 | + #{userId} | |
| 55 | + </foreach> | |
| 56 | + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} | |
| 57 | + AND #{times[1],javaType=java.time.LocalDateTime} | |
| 58 | + GROUP BY level | |
| 59 | + </select> | |
| 60 | + | |
| 61 | +</mapper> | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/bi/CrmBiRankingMapper.xml renamed to yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsRankMapper.xml
| 1 | 1 | <?xml version="1.0" encoding="UTF-8"?> |
| 2 | 2 | <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
| 3 | -<mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsRankingMapper"> | |
| 3 | +<mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsRankMapper"> | |
| 4 | 4 | |
| 5 | 5 | <select id="selectContractPriceRank" |
| 6 | - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO"> | |
| 6 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO"> | |
| 7 | 7 | SELECT IFNULL(SUM(total_price), 0) AS count, owner_user_id |
| 8 | 8 | FROM crm_contract |
| 9 | 9 | WHERE deleted = 0 |
| 10 | - AND audit_status = 20 | |
| 10 | + AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status} | |
| 11 | 11 | and owner_user_id in |
| 12 | - <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 13 | - #{userId} | |
| 14 | - </foreach> | |
| 15 | - AND order_date between #{times[0],javaType=java.time.LocalDateTime} and | |
| 16 | - #{times[1],javaType=java.time.LocalDateTime} | |
| 12 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 13 | + #{userId} | |
| 14 | + </foreach> | |
| 15 | + AND order_date between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime} | |
| 17 | 16 | GROUP BY owner_user_id |
| 18 | 17 | </select> |
| 19 | 18 | |
| 20 | 19 | <select id="selectReceivablePriceRank" |
| 21 | - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO"> | |
| 20 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO"> | |
| 22 | 21 | SELECT IFNULL(SUM(price), 0) AS count, owner_user_id |
| 23 | 22 | FROM crm_receivable |
| 24 | 23 | WHERE deleted = 0 |
| 25 | - AND audit_status = 20 | |
| 24 | + AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status} | |
| 26 | 25 | AND owner_user_id in |
| 27 | - <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 28 | - #{userId} | |
| 29 | - </foreach> | |
| 30 | - AND return_time between #{times[0],javaType=java.time.LocalDateTime} and | |
| 31 | - #{times[1],javaType=java.time.LocalDateTime} | |
| 26 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 27 | + #{userId} | |
| 28 | + </foreach> | |
| 29 | + AND return_time between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime} | |
| 32 | 30 | GROUP BY owner_user_id |
| 33 | 31 | </select> |
| 34 | 32 | |
| 35 | 33 | <select id="selectContractCountRank" |
| 36 | - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO"> | |
| 34 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO"> | |
| 37 | 35 | SELECT COUNT(1) AS count, owner_user_id |
| 38 | 36 | FROM crm_contract |
| 39 | 37 | WHERE deleted = 0 |
| 40 | - AND audit_status = 20 | |
| 38 | + AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status} | |
| 41 | 39 | AND owner_user_id in |
| 42 | - <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 43 | - #{userId} | |
| 44 | - </foreach> | |
| 45 | - AND order_date between #{times[0],javaType=java.time.LocalDateTime} and | |
| 46 | - #{times[1],javaType=java.time.LocalDateTime} | |
| 40 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 41 | + #{userId} | |
| 42 | + </foreach> | |
| 43 | + AND order_date between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime} | |
| 47 | 44 | GROUP BY owner_user_id |
| 48 | 45 | </select> |
| 49 | 46 | |
| 50 | - <!-- TODO 待定 这里是否需要关联 crm_contract_product 表,计算销售额 --> | |
| 51 | 47 | <select id="selectProductSalesRank" |
| 52 | - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO"> | |
| 53 | - SELECT COUNT(1) AS count, owner_user_id | |
| 54 | - FROM crm_contract | |
| 55 | - WHERE deleted = 0 | |
| 56 | - AND audit_status = 20 | |
| 57 | - AND owner_user_id in | |
| 58 | - <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 59 | - #{userId} | |
| 60 | - </foreach> | |
| 61 | - AND order_date between #{times[0],javaType=java.time.LocalDateTime} and | |
| 62 | - #{times[1],javaType=java.time.LocalDateTime} | |
| 63 | - GROUP BY owner_user_id | |
| 48 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO"> | |
| 49 | + SELECT COUNT(product.count) AS count, contract.owner_user_id | |
| 50 | + FROM crm_contract_product product | |
| 51 | + INNER JOIN crm_contract contract ON product.contract_id = contract.id | |
| 52 | + WHERE contract.deleted = 0 AND contract.deleted = 0 | |
| 53 | + AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status} | |
| 54 | + AND contract.owner_user_id in | |
| 55 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 56 | + #{userId} | |
| 57 | + </foreach> | |
| 58 | + AND contract.order_date between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime} | |
| 59 | + GROUP BY contract.owner_user_id | |
| 64 | 60 | </select> |
| 65 | 61 | |
| 66 | 62 | <select id="selectCustomerCountRank" |
| 67 | - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO"> | |
| 63 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO"> | |
| 68 | 64 | SELECT COUNT(1) AS count, owner_user_id |
| 69 | 65 | FROM crm_customer |
| 70 | 66 | WHERE deleted = 0 |
| 71 | 67 | AND owner_user_id in |
| 72 | - <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 73 | - #{userId} | |
| 74 | - </foreach> | |
| 75 | - AND create_time between #{times[0],javaType=java.time.LocalDateTime} and | |
| 76 | - #{times[1],javaType=java.time.LocalDateTime} | |
| 68 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 69 | + #{userId} | |
| 70 | + </foreach> | |
| 71 | + AND create_time between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime} | |
| 77 | 72 | GROUP BY owner_user_id |
| 78 | 73 | </select> |
| 79 | 74 | |
| 80 | 75 | <select id="selectContactsCountRank" |
| 81 | - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO"> | |
| 76 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO"> | |
| 82 | 77 | SELECT COUNT(1) AS count, owner_user_id |
| 83 | 78 | FROM crm_contact |
| 84 | 79 | WHERE deleted = 0 |
| 85 | 80 | AND owner_user_id in |
| 86 | - <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 87 | - #{userId} | |
| 88 | - </foreach> | |
| 89 | - AND create_time between #{times[0],javaType=java.time.LocalDateTime} and | |
| 90 | - #{times[1],javaType=java.time.LocalDateTime} | |
| 81 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 82 | + #{userId} | |
| 83 | + </foreach> | |
| 84 | + AND create_time between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime} | |
| 91 | 85 | GROUP BY owner_user_id |
| 92 | 86 | </select> |
| 93 | 87 | |
| 94 | 88 | <select id="selectFollowCountRank" |
| 95 | - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO"> | |
| 89 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO"> | |
| 96 | 90 | SELECT COUNT(1) AS count, cc.owner_user_id |
| 97 | 91 | FROM crm_follow_up_record AS cfur |
| 98 | 92 | LEFT JOIN crm_contact AS cc ON FIND_IN_SET(cc.id, cfur.contact_ids) |
| 99 | 93 | WHERE cfur.deleted = 0 |
| 100 | 94 | AND cc.deleted = 0 |
| 101 | 95 | AND cc.owner_user_id in |
| 102 | - <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 103 | - #{userId} | |
| 104 | - </foreach> | |
| 105 | - AND cfur.create_time between #{times[0],javaType=java.time.LocalDateTime} and | |
| 106 | - #{times[1],javaType=java.time.LocalDateTime} | |
| 96 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 97 | + #{userId} | |
| 98 | + </foreach> | |
| 99 | + AND cfur.create_time between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime} | |
| 107 | 100 | GROUP BY cc.owner_user_id |
| 108 | 101 | </select> |
| 109 | 102 | |
| 110 | 103 | <select id="selectFollowCustomerCountRank" |
| 111 | - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO"> | |
| 104 | + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO"> | |
| 112 | 105 | SELECT COUNT(DISTINCT cc.id) AS count, cc.owner_user_id |
| 113 | 106 | FROM crm_follow_up_record AS cfur |
| 114 | 107 | LEFT JOIN crm_contact AS cc ON FIND_IN_SET(cc.id, cfur.contact_ids) |
| 115 | 108 | WHERE cfur.deleted = 0 |
| 116 | 109 | AND cc.deleted = 0 |
| 117 | 110 | AND cc.owner_user_id in |
| 118 | - <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 119 | - #{userId} | |
| 120 | - </foreach> | |
| 121 | - AND cfur.create_time between #{times[0],javaType=java.time.LocalDateTime} and | |
| 122 | - #{times[1],javaType=java.time.LocalDateTime} | |
| 111 | + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> | |
| 112 | + #{userId} | |
| 113 | + </foreach> | |
| 114 | + AND cfur.create_time between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime} | |
| 123 | 115 | GROUP BY cc.owner_user_id |
| 124 | 116 | </select> |
| 125 | 117 | ... | ... |
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java
| ... | ... | @@ -61,14 +61,14 @@ public class ErpPurchaseOrderController { |
| 61 | 61 | |
| 62 | 62 | @PostMapping("/create") |
| 63 | 63 | @Operation(summary = "创建采购订单") |
| 64 | - @PreAuthorize("@ss.hasPermission('erp:purchase-create:create')") | |
| 64 | + @PreAuthorize("@ss.hasPermission('erp:purchase-order:create')") | |
| 65 | 65 | public CommonResult<Long> createPurchaseOrder(@Valid @RequestBody ErpPurchaseOrderSaveReqVO createReqVO) { |
| 66 | 66 | return success(purchaseOrderService.createPurchaseOrder(createReqVO)); |
| 67 | 67 | } |
| 68 | 68 | |
| 69 | 69 | @PutMapping("/update") |
| 70 | 70 | @Operation(summary = "更新采购订单") |
| 71 | - @PreAuthorize("@ss.hasPermission('erp:purchase-create:update')") | |
| 71 | + @PreAuthorize("@ss.hasPermission('erp:purchase-order:update')") | |
| 72 | 72 | public CommonResult<Boolean> updatePurchaseOrder(@Valid @RequestBody ErpPurchaseOrderSaveReqVO updateReqVO) { |
| 73 | 73 | purchaseOrderService.updatePurchaseOrder(updateReqVO); |
| 74 | 74 | return success(true); |
| ... | ... | @@ -76,7 +76,7 @@ public class ErpPurchaseOrderController { |
| 76 | 76 | |
| 77 | 77 | @PutMapping("/update-status") |
| 78 | 78 | @Operation(summary = "更新采购订单的状态") |
| 79 | - @PreAuthorize("@ss.hasPermission('erp:purchase-create:update-status')") | |
| 79 | + @PreAuthorize("@ss.hasPermission('erp:purchase-order:update-status')") | |
| 80 | 80 | public CommonResult<Boolean> updatePurchaseOrderStatus(@RequestParam("id") Long id, |
| 81 | 81 | @RequestParam("status") Integer status) { |
| 82 | 82 | purchaseOrderService.updatePurchaseOrderStatus(id, status); |
| ... | ... | @@ -86,7 +86,7 @@ public class ErpPurchaseOrderController { |
| 86 | 86 | @DeleteMapping("/delete") |
| 87 | 87 | @Operation(summary = "删除采购订单") |
| 88 | 88 | @Parameter(name = "ids", description = "编号数组", required = true) |
| 89 | - @PreAuthorize("@ss.hasPermission('erp:purchase-create:delete')") | |
| 89 | + @PreAuthorize("@ss.hasPermission('erp:purchase-order:delete')") | |
| 90 | 90 | public CommonResult<Boolean> deletePurchaseOrder(@RequestParam("ids") List<Long> ids) { |
| 91 | 91 | purchaseOrderService.deletePurchaseOrder(ids); |
| 92 | 92 | return success(true); |
| ... | ... | @@ -95,7 +95,7 @@ public class ErpPurchaseOrderController { |
| 95 | 95 | @GetMapping("/get") |
| 96 | 96 | @Operation(summary = "获得采购订单") |
| 97 | 97 | @Parameter(name = "id", description = "编号", required = true, example = "1024") |
| 98 | - @PreAuthorize("@ss.hasPermission('erp:purchase-create:query')") | |
| 98 | + @PreAuthorize("@ss.hasPermission('erp:purchase-order:query')") | |
| 99 | 99 | public CommonResult<ErpPurchaseOrderRespVO> getPurchaseOrder(@RequestParam("id") Long id) { |
| 100 | 100 | ErpPurchaseOrderDO purchaseOrder = purchaseOrderService.getPurchaseOrder(id); |
| 101 | 101 | if (purchaseOrder == null) { |
| ... | ... | @@ -115,7 +115,7 @@ public class ErpPurchaseOrderController { |
| 115 | 115 | |
| 116 | 116 | @GetMapping("/page") |
| 117 | 117 | @Operation(summary = "获得采购订单分页") |
| 118 | - @PreAuthorize("@ss.hasPermission('erp:purchase-create:query')") | |
| 118 | + @PreAuthorize("@ss.hasPermission('erp:purchase-order:query')") | |
| 119 | 119 | public CommonResult<PageResult<ErpPurchaseOrderRespVO>> getPurchaseOrderPage(@Valid ErpPurchaseOrderPageReqVO pageReqVO) { |
| 120 | 120 | PageResult<ErpPurchaseOrderDO> pageResult = purchaseOrderService.getPurchaseOrderPage(pageReqVO); |
| 121 | 121 | return success(buildPurchaseOrderVOPageResult(pageResult)); |
| ... | ... | @@ -123,7 +123,7 @@ public class ErpPurchaseOrderController { |
| 123 | 123 | |
| 124 | 124 | @GetMapping("/export-excel") |
| 125 | 125 | @Operation(summary = "导出采购订单 Excel") |
| 126 | - @PreAuthorize("@ss.hasPermission('erp:purchase-create:export')") | |
| 126 | + @PreAuthorize("@ss.hasPermission('erp:purchase-order:export')") | |
| 127 | 127 | @ApiAccessLog(operateType = EXPORT) |
| 128 | 128 | public void exportPurchaseOrderExcel(@Valid ErpPurchaseOrderPageReqVO pageReqVO, |
| 129 | 129 | HttpServletResponse response) throws IOException { | ... | ... |
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/list_sub_erp.vue.vm
| ... | ... | @@ -94,7 +94,7 @@ const { t } = useI18n() // 国际化 |
| 94 | 94 | const message = useMessage() // 消息弹窗 |
| 95 | 95 | |
| 96 | 96 | const props = defineProps<{ |
| 97 | - ${subJoinColumn.javaField}: undefined // ${subJoinColumn.columnComment}(主表的关联字段) | |
| 97 | + ${subJoinColumn.javaField}?: number // ${subJoinColumn.columnComment}(主表的关联字段) | |
| 98 | 98 | }>() |
| 99 | 99 | const loading = ref(false) // 列表的加载中 |
| 100 | 100 | const list = ref([]) // 列表的数据 |
| ... | ... | @@ -103,17 +103,20 @@ const total = ref(0) // 列表的总页数 |
| 103 | 103 | const queryParams = reactive({ |
| 104 | 104 | pageNo: 1, |
| 105 | 105 | pageSize: 10, |
| 106 | - ${subJoinColumn.javaField}: undefined | |
| 106 | + ${subJoinColumn.javaField}: undefined as unknown | |
| 107 | 107 | }) |
| 108 | 108 | |
| 109 | 109 | /** 监听主表的关联字段的变化,加载对应的子表数据 */ |
| 110 | 110 | watch( |
| 111 | 111 | () => props.${subJoinColumn.javaField}, |
| 112 | - (val) => { | |
| 112 | + (val: number) => { | |
| 113 | + if (!val) { | |
| 114 | + return | |
| 115 | + } | |
| 113 | 116 | queryParams.${subJoinColumn.javaField} = val |
| 114 | 117 | handleQuery() |
| 115 | 118 | }, |
| 116 | - { immediate: false } | |
| 119 | + { immediate: true, deep: true } | |
| 117 | 120 | ) |
| 118 | 121 | #end |
| 119 | 122 | ... | ... |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java
| ... | ... | @@ -101,7 +101,7 @@ public class AppProductSpuController { |
| 101 | 101 | throw exception(SPU_NOT_EXISTS); |
| 102 | 102 | } |
| 103 | 103 | if (!ProductSpuStatusEnum.isEnable(spu.getStatus())) { |
| 104 | - throw exception(SPU_NOT_ENABLE); | |
| 104 | + throw exception(SPU_NOT_ENABLE, spu.getName()); | |
| 105 | 105 | } |
| 106 | 106 | // 获得商品 SKU |
| 107 | 107 | List<ProductSkuDO> skus = productSkuService.getSkuListBySpuId(spu.getId()); | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStorePageReqVO.java
| ... | ... | @@ -4,14 +4,11 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.validation.InEnum; |
| 5 | 5 | import lombok.*; |
| 6 | 6 | |
| 7 | -import java.time.LocalTime; | |
| 8 | -import java.util.*; | |
| 9 | 7 | import io.swagger.v3.oas.annotations.media.Schema; |
| 10 | 8 | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| 11 | 9 | import org.springframework.format.annotation.DateTimeFormat; |
| 12 | 10 | import java.time.LocalDateTime; |
| 13 | 11 | |
| 14 | -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_HOUR_MINUTE_SECOND; | |
| 15 | 12 | import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; |
| 16 | 13 | |
| 17 | 14 | @Schema(description = "管理后台 - 自提门店分页 Request VO") | ... | ... |