v0.0.18
CI / build-deploy (pull_request) Failing after 8s
CI / notify-on-failure (pull_request) Successful in 0s

This commit is contained in:
Timi
2026-08-03 19:57:44 +08:00
parent 65161a7fad
commit c49686c411
7 changed files with 208 additions and 1 deletions
@@ -10,6 +10,7 @@ import com.imyeyu.api.modules.gao.entity.GaoRegistrationEventRecord;
import com.imyeyu.api.modules.gao.service.GaoRegistrationEventRecordService; import com.imyeyu.api.modules.gao.service.GaoRegistrationEventRecordService;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventPeriodStatView; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventPeriodStatView;
import com.imyeyu.api.modules.gao.vo.GaoCustomerRegisterStatView; import com.imyeyu.api.modules.gao.vo.GaoCustomerRegisterStatView;
import com.imyeyu.api.modules.gao.vo.GaoCustomerRegistrationRankView;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCreateReq; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCreateReq;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCalendarView; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCalendarView;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordDailyStatView; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordDailyStatView;
@@ -171,6 +172,15 @@ public class GaoRegistrationEventRecordController {
return service.statAllCustomerByRange(req); return service.statAllCustomerByRange(req);
} }
@AOPLog
@RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.STAT_READ)
@PostMapping("/customer/registration-rank")
/// 按时间范围查询客户登记排行榜
public List<GaoCustomerRegistrationRankView> customerRegistrationRank(@RequestBody(required = false) GaoRegistrationEventRecordRangeReq req) {
return service.listCustomerRegistrationRank(req);
}
@AOPLog @AOPLog
@RequestRateLimit @RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.STAT_READ) @RequireGaoPermission(GaoPermissionCode.STAT_READ)
@@ -3,6 +3,7 @@ package com.imyeyu.api.modules.gao.mapper;
import com.imyeyu.api.modules.gao.entity.GaoRegistrationEventRecord; import com.imyeyu.api.modules.gao.entity.GaoRegistrationEventRecord;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventPeriodStatView; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventPeriodStatView;
import com.imyeyu.api.modules.gao.vo.GaoCustomerRegisterStatView; import com.imyeyu.api.modules.gao.vo.GaoCustomerRegisterStatView;
import com.imyeyu.api.modules.gao.vo.GaoCustomerRegistrationDayStatView;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCalendarView; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCalendarView;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordDailyStatView; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordDailyStatView;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordListItemView; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordListItemView;
@@ -77,6 +78,12 @@ public interface GaoRegistrationEventRecordMapper extends BaseMapper<GaoRegistra
/// @return 统计结果 /// @return 统计结果
List<GaoCustomerRegisterStatView> statAllCustomerByRange(@Param("beginRegisteredAt") Long beginRegisteredAt, @Param("endRegisteredAt") Long endRegisteredAt); List<GaoCustomerRegisterStatView> statAllCustomerByRange(@Param("beginRegisteredAt") Long beginRegisteredAt, @Param("endRegisteredAt") Long endRegisteredAt);
/// 按时间范围统计全部客户每日登记
///
/// @param req 范围查询请求
/// @return 每个有登记客户的每日统计
List<GaoCustomerRegistrationDayStatView> statCustomerRegistrationDay(@Param("req") GaoRegistrationEventRecordRangeReq req);
/// 统计单个客户单个登记事件的每日登记事件记录数 /// 统计单个客户单个登记事件的每日登记事件记录数
/// ///
/// @param customerId 客户 ID /// @param customerId 客户 ID
@@ -3,6 +3,7 @@ package com.imyeyu.api.modules.gao.service;
import com.imyeyu.api.modules.gao.entity.GaoRegistrationEventRecord; import com.imyeyu.api.modules.gao.entity.GaoRegistrationEventRecord;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventPeriodStatView; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventPeriodStatView;
import com.imyeyu.api.modules.gao.vo.GaoCustomerRegisterStatView; import com.imyeyu.api.modules.gao.vo.GaoCustomerRegisterStatView;
import com.imyeyu.api.modules.gao.vo.GaoCustomerRegistrationRankView;
import com.imyeyu.api.modules.gao.vo.GaoQuickRegistrationEventRecordReq; import com.imyeyu.api.modules.gao.vo.GaoQuickRegistrationEventRecordReq;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCalendarView; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCalendarView;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCreateReq; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCreateReq;
@@ -100,6 +101,12 @@ public interface GaoRegistrationEventRecordService extends BaseService<GaoRegist
/// @return 统计结果 /// @return 统计结果
List<GaoCustomerRegisterStatView> statAllCustomerByRange(GaoRegistrationEventRecordStatReq req); List<GaoCustomerRegisterStatView> statAllCustomerByRange(GaoRegistrationEventRecordStatReq req);
/// 按时间范围查询客户登记排行榜
///
/// @param req 范围查询请求,不传时间为全部时间
/// @return 客户登记排行榜
List<GaoCustomerRegistrationRankView> listCustomerRegistrationRank(GaoRegistrationEventRecordRangeReq req);
/// 按时间段统计单个客户单个登记事件每日趋势 /// 按时间段统计单个客户单个登记事件每日趋势
/// ///
/// @param req 趋势统计请求 /// @param req 趋势统计请求
@@ -17,6 +17,8 @@ import com.imyeyu.api.modules.gao.service.GaoRegistrationEventService;
import com.imyeyu.api.modules.gao.service.GaoRegistrationEventRecordService; import com.imyeyu.api.modules.gao.service.GaoRegistrationEventRecordService;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventPeriodStatView; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventPeriodStatView;
import com.imyeyu.api.modules.gao.vo.GaoCustomerRegisterStatView; import com.imyeyu.api.modules.gao.vo.GaoCustomerRegisterStatView;
import com.imyeyu.api.modules.gao.vo.GaoCustomerRegistrationDayStatView;
import com.imyeyu.api.modules.gao.vo.GaoCustomerRegistrationRankView;
import com.imyeyu.api.modules.gao.vo.GaoQuickRegistrationEventRecordReq; import com.imyeyu.api.modules.gao.vo.GaoQuickRegistrationEventRecordReq;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCalendarView; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCalendarView;
import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCreateReq; import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCreateReq;
@@ -62,6 +64,7 @@ import java.time.temporal.ChronoUnit;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -80,7 +83,7 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor @RequiredArgsConstructor
public class GaoRegistrationEventRecordServiceImplement extends AbstractEntityService<GaoRegistrationEventRecord, String> implements GaoRegistrationEventRecordService { public class GaoRegistrationEventRecordServiceImplement extends AbstractEntityService<GaoRegistrationEventRecord, String> implements GaoRegistrationEventRecordService {
private static final DateTimeFormatter DAY_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd"); private static final DateTimeFormatter DAY_FORMATTER = DateTimeFormatter.BASIC_ISO_DATE;
private static final DateTimeFormatter EXPORT_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault()); private static final DateTimeFormatter EXPORT_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault());
private final GaoRegistrationEventRecordMapper mapper; private final GaoRegistrationEventRecordMapper mapper;
@@ -379,6 +382,29 @@ public class GaoRegistrationEventRecordServiceImplement extends AbstractEntitySe
return mapper.statAllCustomerByRange(req.getBeginRegisteredAt(), req.getEndRegisteredAt()); return mapper.statAllCustomerByRange(req.getBeginRegisteredAt(), req.getEndRegisteredAt());
} }
@Override
public List<GaoCustomerRegistrationRankView> listCustomerRegistrationRank(GaoRegistrationEventRecordRangeReq req) {
GaoRegistrationEventRecordRangeReq rangeReq = TimiJava.defaultIfNull(req, new GaoRegistrationEventRecordRangeReq());
if (rangeReq.getBeginRegisteredAt() != null || rangeReq.getEndRegisteredAt() != null) {
checkStatRange(rangeReq.getBeginRegisteredAt(), rangeReq.getEndRegisteredAt());
}
List<GaoCustomerRegistrationDayStatView> dayStatList = mapper.statCustomerRegistrationDay(rangeReq);
Map<String, List<GaoCustomerRegistrationDayStatView>> customerDayStatMap = dayStatList.stream()
.collect(Collectors.groupingBy(GaoCustomerRegistrationDayStatView::getCustomerId));
List<GaoCustomerRegistrationRankView> rankList = new ArrayList<>();
for (Map.Entry<String, List<GaoCustomerRegistrationDayStatView>> entry : customerDayStatMap.entrySet()) {
rankList.add(buildCustomerRegistrationRank(entry.getValue()));
}
rankList.sort(Comparator
.comparing(GaoCustomerRegistrationRankView::getTotalRegistrationDays, Comparator.nullsLast(Comparator.reverseOrder()))
.thenComparing(GaoCustomerRegistrationRankView::getTotalRegistrationCount, Comparator.nullsLast(Comparator.reverseOrder()))
.thenComparing(GaoCustomerRegistrationRankView::getLatestRegisteredAt, Comparator.nullsLast(Comparator.reverseOrder())));
for (int index = 0; index < rankList.size(); index++) {
rankList.get(index).setRank(index + 1);
}
return rankList;
}
@Override @Override
public List<GaoRegistrationEventRecordDailyStatView> statDailyByCustomerAndRegistrationEvent(GaoRegistrationEventRecordTrendStatReq req) { public List<GaoRegistrationEventRecordDailyStatView> statDailyByCustomerAndRegistrationEvent(GaoRegistrationEventRecordTrendStatReq req) {
TimiException.required(req, "not found req"); TimiException.required(req, "not found req");
@@ -555,6 +581,43 @@ public class GaoRegistrationEventRecordServiceImplement extends AbstractEntitySe
return list; return list;
} }
private GaoCustomerRegistrationRankView buildCustomerRegistrationRank(List<GaoCustomerRegistrationDayStatView> dayStatList) {
List<GaoCustomerRegistrationDayStatView> sortedDayStatList = dayStatList.stream()
.sorted(Comparator.comparing(GaoCustomerRegistrationDayStatView::getDateValue, Comparator.nullsLast(Comparator.reverseOrder())))
.toList();
GaoCustomerRegistrationDayStatView first = sortedDayStatList.get(0);
GaoCustomerRegistrationRankView view = new GaoCustomerRegistrationRankView();
view.setCustomerId(first.getCustomerId());
view.setCustomerCode(first.getCustomerCode());
view.setCustomerName(first.getCustomerName());
view.setCustomerPhotoAttachmentId(first.getCustomerPhotoAttachmentId());
view.setTotalRegistrationDays((long) sortedDayStatList.size());
view.setTotalRegistrationCount(sortedDayStatList.stream().mapToLong(item -> TimiJava.defaultIfNull(item.getDailyCount(), 0L)).sum());
view.setLatestRegisteredAt(sortedDayStatList.stream().map(GaoCustomerRegistrationDayStatView::getLatestRegisteredAt).filter(Objects::nonNull).max(Long::compareTo).orElse(null));
view.setRecentContinuousRegistrationDays(countRecentContinuousRegistrationDays(sortedDayStatList));
return view;
}
private long countRecentContinuousRegistrationDays(List<GaoCustomerRegistrationDayStatView> sortedDayStatList) {
if (sortedDayStatList.isEmpty() || sortedDayStatList.get(0).getDateValue() == null) {
return 0;
}
LocalDate expectedDate = LocalDate.parse(String.valueOf(sortedDayStatList.get(0).getDateValue()), DAY_FORMATTER);
long count = 0;
for (GaoCustomerRegistrationDayStatView item : sortedDayStatList) {
if (item.getDateValue() == null) {
continue;
}
LocalDate date = LocalDate.parse(String.valueOf(item.getDateValue()), DAY_FORMATTER);
if (!date.equals(expectedDate)) {
break;
}
count++;
expectedDate = expectedDate.minusDays(1);
}
return count;
}
private void loadTransient(List<GaoRegistrationEventRecord> recordList) { private void loadTransient(List<GaoRegistrationEventRecord> recordList) {
if (recordList == null || recordList.isEmpty()) { if (recordList == null || recordList.isEmpty()) {
return; return;
@@ -0,0 +1,33 @@
package com.imyeyu.api.modules.gao.vo;
import lombok.Data;
///
/// 客户登记日期统计视图
///
/// @author Codex
/// @since 2026-08-03
@Data
public class GaoCustomerRegistrationDayStatView {
/// 客户 ID
private String customerId;
/// 客户编码
private String customerCode;
/// 客户姓名
private String customerName;
/// 客户头像附件 ID
private String customerPhotoAttachmentId;
/// 登记日期,格式 yyyyMMdd
private Integer dateValue;
/// 当日登记次数
private Long dailyCount;
/// 当日最后登记时间
private Long latestRegisteredAt;
}
@@ -0,0 +1,39 @@
package com.imyeyu.api.modules.gao.vo;
import lombok.Data;
///
/// 客户登记排行榜视图
///
/// @author Codex
/// @since 2026-08-03
@Data
public class GaoCustomerRegistrationRankView {
/// 排名
private Integer rank;
/// 客户 ID
private String customerId;
/// 客户编码
private String customerCode;
/// 客户姓名
private String customerName;
/// 客户头像附件 ID
private String customerPhotoAttachmentId;
/// 累计登记天数
private Long totalRegistrationDays;
/// 累计登记次数
private Long totalRegistrationCount;
/// 最近连续登记天数
private Long recentContinuousRegistrationDays;
/// 最后登记时间
private Long latestRegisteredAt;
}
@@ -143,6 +143,54 @@
ORDER BY `latestRegisteredAt` DESC ORDER BY `latestRegisteredAt` DESC
</select> </select>
<select id="statCustomerRegistrationDay" resultType="com.imyeyu.api.modules.gao.vo.GaoCustomerRegistrationDayStatView">
SELECT
r.`customer_id` AS `customerId`,
c.`code` AS `customerCode`,
c.`name` AS `customerName`,
(
SELECT a.`id`
FROM `attachment` a
WHERE
a.`biz_type` = 'GAO_CUSTOMER'
AND a.`biz_id` = c.`id`
AND a.`attach_type` = 'PHOTO'
AND a.`deleted_at` IS NULL
AND (a.`destroy_at` IS NULL OR UNIX_TIMESTAMP() &lt; a.`destroy_at`)
ORDER BY a.`created_at` ASC
LIMIT 1
) AS `customerPhotoAttachmentId`,
CAST(DATE_FORMAT(FROM_UNIXTIME(r.`registered_at` / 1000), '%Y%m%d') AS UNSIGNED) AS `dateValue`,
COUNT(1) AS `dailyCount`,
MAX(r.`registered_at`) AS `latestRegisteredAt`
FROM `gao_registration_event_record` r
LEFT JOIN `gao_customer` c ON r.`customer_id` = c.`id`
WHERE
r.`deleted_at` IS NULL
AND c.`deleted_at` IS NULL
<if test="req.beginRegisteredAt != null">
AND r.`registered_at` &gt;= #{req.beginRegisteredAt}
</if>
<if test="req.endRegisteredAt != null">
AND r.`registered_at` &lt;= #{req.endRegisteredAt}
</if>
<if test="req.registrationEventId != null and req.registrationEventId != ''">
AND r.`registration_event_id` = #{req.registrationEventId}
</if>
<if test="req.registrationEventIdList != null and req.registrationEventIdList.size() > 0">
AND r.`registration_event_id` IN
<foreach collection="req.registrationEventIdList" item="registrationEventId" open="(" separator="," close=")">
#{registrationEventId}
</foreach>
</if>
GROUP BY
r.`customer_id`,
c.`code`,
c.`name`,
DATE_FORMAT(FROM_UNIXTIME(r.`registered_at` / 1000), '%Y%m%d')
ORDER BY r.`customer_id` ASC, `dateValue` DESC
</select>
<select id="statDailyCountByCustomerIdAndRegistrationEventIdRange" resultType="com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordDailyStatView"> <select id="statDailyCountByCustomerIdAndRegistrationEventIdRange" resultType="com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordDailyStatView">
SELECT SELECT
CAST(DATE_FORMAT(FROM_UNIXTIME(`registered_at` / 1000), '%Y%m%d') AS UNSIGNED) AS `dateValue`, CAST(DATE_FORMAT(FROM_UNIXTIME(`registered_at` / 1000), '%Y%m%d') AS UNSIGNED) AS `dateValue`,