缩小日志事务

This commit is contained in:
zjf committed 2026-06-06 07:44:59 +08:00
1 parent 3557a1f1a4
commit 621e078031
2 files changed
+83 -75

No files matched your search

@@ -3,9 +3,9 @@ package com.bsd.pjcloud.admin.service.impl.stats;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.bsd.pjcloud.admin.api.dto.stats.AppAccessStatsQueryDTO;
import com.bsd.pjcloud.admin.api.dto.stats.AppAccessTrackRequestDTO;
import com.bsd.pjcloud.admin.api.entity.SysDept;
@@ -26,6 +26,7 @@ import com.bsd.pjcloud.common.security.util.SecurityUtils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -51,16 +52,10 @@ public class AppAccessStatsServiceImpl implements AppAccessStatsService {
private final SysDeptMapper sysDeptMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public R<Boolean> track(AppAccessTrackRequestDTO dto, HttpServletRequest request) {
if (!isPageCodeEnabled(dto.getPageCode())) {
return R.failed("pageCode未在埋点字典中启用: " + dto.getPageCode());
}
Long exists = appAccessEventMapper.selectCount(new LambdaQueryWrapper<AppAccessEventEntity>()
.eq(AppAccessEventEntity::getEventId, dto.getEventId()));
if (exists != null && exists > 0) {
return R.ok(Boolean.TRUE);
}
LocalDateTime eventTime = ObjectUtil.defaultIfNull(dto.getEventTime(), LocalDateTime.now());
PjcloudUser loginUser = SecurityUtils.getUser();
@@ -85,7 +80,11 @@ public class AppAccessStatsServiceImpl implements AppAccessStatsService {
entity.setUnitName(orgInfo.unitName);
entity.setDeptId(orgInfo.deptId);
entity.setDeptName(orgInfo.deptName);
appAccessEventMapper.insert(entity);
try {
appAccessEventMapper.insert(entity);
} catch (DuplicateKeyException ex) {
return R.ok(Boolean.TRUE);
}
return R.ok(Boolean.TRUE);
}
@@ -262,34 +261,19 @@ public class AppAccessStatsServiceImpl implements AppAccessStatsService {
}
@Override
@Transactional(rollbackFor = Exception.class)
public void aggregateLastMinute() {
LocalDateTime end = LocalDateTime.now().withSecond(0).withNano(0);
LocalDateTime start = end.minusMinutes(1);
List<Map<String, Object>> rows = queryEventAggRows(start, end);
getSelf().persistMinuteAggregation(start, rows);
}
QueryWrapper<AppAccessEventEntity> queryWrapper = new QueryWrapper<>();
queryWrapper.select(
"tenant_id as tenantId",
"unit_id as unitId",
"max(unit_name) as unitName",
"dept_id as deptId",
"max(dept_name) as deptName",
"page_code as pageCode",
"count(1) as pv",
"count(distinct if(user_id is null, concat('D_', ifnull(device_id,'')), concat('U_', user_id))) as uv",
"count(distinct user_id) as visitUserCount",
"count(1) as trackCount"
);
queryWrapper.ge("event_time", start);
queryWrapper.lt("event_time", end);
queryWrapper.groupBy("tenant_id", "unit_id", "dept_id", "page_code");
List<Map<String, Object>> rows = appAccessEventMapper.selectMaps(queryWrapper);
@Transactional(rollbackFor = Exception.class)
public void persistMinuteAggregation(LocalDateTime start, List<Map<String, Object>> rows) {
appAccessAggMinuteMapper.deletePhysicallyByBucketTime(start);
if (CollUtil.isEmpty(rows)) {
return;
}
for (Map<String, Object> row : rows) {
AppAccessAggMinuteEntity entity = new AppAccessAggMinuteEntity();
entity.setBucketTime(start);
@@ -308,52 +292,63 @@ public class AppAccessStatsServiceImpl implements AppAccessStatsService {
}
@Override
@Transactional(rollbackFor = Exception.class)
public void aggregateRecentDays(int dayCount) {
int safeDayCount = Math.max(dayCount, 1);
LocalDate today = LocalDate.now();
for (int i = 0; i < safeDayCount; i++) {
LocalDate date = today.minusDays(i);
LocalDateTime start = date.atStartOfDay();
LocalDateTime end = start.plusDays(1);
QueryWrapper<AppAccessEventEntity> queryWrapper = new QueryWrapper<>();
queryWrapper.select(
"tenant_id as tenantId",
"unit_id as unitId",
"max(unit_name) as unitName",
"dept_id as deptId",
"max(dept_name) as deptName",
"page_code as pageCode",
"count(1) as pv",
"count(distinct if(user_id is null, concat('D_', ifnull(device_id,'')), concat('U_', user_id))) as uv",
"count(distinct user_id) as visitUserCount",
"count(1) as trackCount"
);
queryWrapper.ge("event_time", start);
queryWrapper.lt("event_time", end);
queryWrapper.groupBy("tenant_id", "unit_id", "dept_id", "page_code");
List<Map<String, Object>> rows = appAccessEventMapper.selectMaps(queryWrapper);
appAccessAggDayMapper.deletePhysicallyByStatDate(date);
for (Map<String, Object> row : rows) {
AppAccessAggDayEntity entity = new AppAccessAggDayEntity();
entity.setStatDate(date);
entity.setTenantId(toLongObj(row.get("tenantId")));
entity.setUnitId(toLongObj(row.get("unitId")));
entity.setUnitName(Objects.toString(row.get("unitName"), null));
entity.setDeptId(toLongObj(row.get("deptId")));
entity.setDeptName(Objects.toString(row.get("deptName"), null));
entity.setPageCode(Objects.toString(row.get("pageCode"), null));
entity.setPv(toLong(row.get("pv")));
entity.setUv(toLong(row.get("uv")));
entity.setVisitUserCount(toLong(row.get("visitUserCount")));
entity.setTrackCount(toLong(row.get("trackCount")));
appAccessAggDayMapper.insert(entity);
try {
List<Map<String, Object>> rows = queryEventAggRows(date.atStartOfDay(), date.plusDays(1).atStartOfDay());
getSelf().persistDayAggregation(date, rows);
} catch (Exception e) {
log.error("日聚合失败 date={}", date, e);
}
}
}
@Transactional(rollbackFor = Exception.class)
public void persistDayAggregation(LocalDate date, List<Map<String, Object>> rows) {
appAccessAggDayMapper.deletePhysicallyByStatDate(date);
if (CollUtil.isEmpty(rows)) {
return;
}
for (Map<String, Object> row : rows) {
AppAccessAggDayEntity entity = new AppAccessAggDayEntity();
entity.setStatDate(date);
entity.setTenantId(toLongObj(row.get("tenantId")));
entity.setUnitId(toLongObj(row.get("unitId")));
entity.setUnitName(Objects.toString(row.get("unitName"), null));
entity.setDeptId(toLongObj(row.get("deptId")));
entity.setDeptName(Objects.toString(row.get("deptName"), null));
entity.setPageCode(Objects.toString(row.get("pageCode"), null));
entity.setPv(toLong(row.get("pv")));
entity.setUv(toLong(row.get("uv")));
entity.setVisitUserCount(toLong(row.get("visitUserCount")));
entity.setTrackCount(toLong(row.get("trackCount")));
appAccessAggDayMapper.insert(entity);
}
}
private List<Map<String, Object>> queryEventAggRows(LocalDateTime start, LocalDateTime end) {
QueryWrapper<AppAccessEventEntity> queryWrapper = new QueryWrapper<>();
queryWrapper.select(
"tenant_id as tenantId",
"unit_id as unitId",
"max(unit_name) as unitName",
"dept_id as deptId",
"max(dept_name) as deptName",
"page_code as pageCode",
"count(1) as pv",
"count(distinct if(user_id is null, concat('D_', ifnull(device_id,'')), concat('U_', user_id))) as uv",
"count(distinct user_id) as visitUserCount",
"count(1) as trackCount"
);
queryWrapper.ge("event_time", start);
queryWrapper.lt("event_time", end);
queryWrapper.groupBy("tenant_id", "unit_id", "dept_id", "page_code");
return appAccessEventMapper.selectMaps(queryWrapper);
}
private boolean isPageCodeEnabled(String pageCode) {
Long count = appAccessPageDictMapper.selectCount(new LambdaQueryWrapper<AppAccessPageDictEntity>()
.eq(AppAccessPageDictEntity::getPageCode, pageCode)
@@ -427,22 +422,35 @@ public class AppAccessStatsServiceImpl implements AppAccessStatsService {
.last("limit 1"));
if (currentSnapshot == null) {
DimUserOrgSnapshotEntity inserted = insertSnapshot(userId, userTenantId, unitDept, dept, eventTime);
return toOrgInfo(inserted);
return getSelf().insertOrgSnapshotTx(userId, userTenantId, unitDept, dept, eventTime);
}
if (isOrgSnapshotOutdated(currentSnapshot, unitDept, dept)) {
DimUserOrgSnapshotEntity update = new DimUserOrgSnapshotEntity();
update.setSnapshotSk(currentSnapshot.getSnapshotSk());
update.setIsCurrent(0);
update.setEffectiveEndTime(eventTime);
dimUserOrgSnapshotMapper.updateById(update);
DimUserOrgSnapshotEntity inserted = insertSnapshot(userId, userTenantId, unitDept, dept, eventTime);
return toOrgInfo(inserted);
return getSelf().rotateOrgSnapshotTx(userId, userTenantId, unitDept, dept, eventTime, currentSnapshot);
}
return toOrgInfo(currentSnapshot);
}
@Transactional(rollbackFor = Exception.class)
public OrgInfo insertOrgSnapshotTx(Long userId, Long tenantId, SysDept unitDept, SysDept dept, LocalDateTime eventTime) {
return toOrgInfo(insertSnapshot(userId, tenantId, unitDept, dept, eventTime));
}
@Transactional(rollbackFor = Exception.class)
public OrgInfo rotateOrgSnapshotTx(Long userId, Long tenantId, SysDept unitDept, SysDept dept, LocalDateTime eventTime,
DimUserOrgSnapshotEntity currentSnapshot) {
DimUserOrgSnapshotEntity update = new DimUserOrgSnapshotEntity();
update.setSnapshotSk(currentSnapshot.getSnapshotSk());
update.setIsCurrent(0);
update.setEffectiveEndTime(eventTime);
dimUserOrgSnapshotMapper.updateById(update);
return toOrgInfo(insertSnapshot(userId, tenantId, unitDept, dept, eventTime));
}
private AppAccessStatsServiceImpl getSelf() {
return SpringUtil.getBean(getClass());
}
private DimUserOrgSnapshotEntity insertSnapshot(Long userId, Long tenantId, SysDept unitDept, SysDept dept, LocalDateTime eventTime) {
DimUserOrgSnapshotEntity snapshot = new DimUserOrgSnapshotEntity();
snapshot.setUserId(userId);
@@ -22,7 +22,7 @@ public class AppAccessStatsAggregateTask {
}
}
@Scheduled(cron = "0 10 * * * ?")
@Scheduled(cron = "0 15 * * * ?")
public void aggregateDay() {
try {
appAccessStatsService.aggregateRecentDays(2);