Преглед изворни кода

```
refactor(infrastructure): 重构用户活跃记录功能,替换原有日活统计逻辑将原有的 DailyActiveUsers 相关类重构为 UserActivity,支持更灵活的访问次数统计。
新增访问计数字段 visitCount 和统计日期 countDate,优化插入与更新策略。同时修改了 DeviceGatewayImpl 与 WebStatsGatewayImpl 中相关服务引用及调用逻辑。
DTO 层同步调整,使用 userId 和 countDate 替代原有 phone 字段进行数据展示。
```

hxd пре 1 месец
родитељ
комит
55f0a17ce5

+ 7 - 2
portal-service-common/src/main/java/com/hfln/portal/common/dto/data/user/UserDailyActiveDTO.java

@@ -3,13 +3,18 @@ package com.hfln.portal.common.dto.data.user;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
+import java.time.LocalDate;
+
 @Data
 public class UserDailyActiveDTO {
 
-    @Schema(description = "用户手机号")
-    private String phone;
+    @Schema(description = "用户id")
+    private Long userId;
 
     @Schema(description = "用户访问总次数")
     private Integer visitCount;
 
+    @Schema(description = "日期")
+    private LocalDate countDate;
+
 }

+ 2 - 2
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/gateway/impl/DeviceGatewayImpl.java

@@ -102,7 +102,7 @@ public class DeviceGatewayImpl implements DeviceGateway {
     private TblOprLogService logService;
 
     @Autowired
-    private DailyActiveUsersService dailyActiveUsersService;
+    private UserActivityService userActivityService;
 
     @Autowired
     private org.springframework.data.redis.core.RedisTemplate<String, Object> redisTemplate;
@@ -185,7 +185,7 @@ public class DeviceGatewayImpl implements DeviceGateway {
         /*
          *  调取首页记录日活次数
          */
-        dailyActiveUsersService.insertDailyActiveUsers(userId);
+        userActivityService.insertUserActivity(userId);
 
         return homeInfoDTO;
     }

+ 18 - 101
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/gateway/impl/WebStatsGatewayImpl.java

@@ -1,8 +1,6 @@
 package com.hfln.portal.infrastructure.gateway.impl;
 
 import cn.dev33.satoken.stp.StpUtil;
-import com.alibaba.excel.util.StringUtils;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.hfln.portal.common.constant.UserConstants;
 import com.hfln.portal.common.dto.data.device.OnoffDTO;
@@ -32,13 +30,15 @@ import org.springframework.stereotype.Service;
 import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import java.time.temporal.ChronoUnit;
-import java.util.*;
-import java.util.stream.Collectors;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
 
 @Slf4j
 @Service
 public class WebStatsGatewayImpl implements WebStatsGateway {
 
+
     @Autowired
     private DevInfoService devInfoService;
 
@@ -46,15 +46,9 @@ public class WebStatsGatewayImpl implements WebStatsGateway {
     private EventListService eventListService;
 
     @Autowired
-    private DailyActiveUsersService dailyActiveUsersService;
-
-    @Autowired
     private EventsService eventsService;
 
     @Autowired
-    private UserService userService;
-
-    @Autowired
     private TblTenantService tblTenantService;
 
     @Autowired
@@ -63,6 +57,9 @@ public class WebStatsGatewayImpl implements WebStatsGateway {
     @Autowired
     private DevOnOffInfoService devOnOffInfoService;
 
+    @Autowired
+    private UserActivityService userActivityService;
+
 
     @Override
     public PageRecord<EventListDTO> fallQuery(EventListParams params) {
@@ -105,102 +102,22 @@ public class WebStatsGatewayImpl implements WebStatsGateway {
 
     @Override
     public PageRecord<UserDailyActiveDTO> queryUserDailyActive(UserDailyActiveParams params) {
+        // 手机号不为空,查询单个用户
+        log.info("查询指定用户日活 phone{}", params.getPhone());
+        if (params.getPhone() != null){
+            Page<UserActivity> userActivityPage = userActivityService.queryUserActivity(params);
 
-        PageRecord<UserDailyActiveDTO> pageRecord = new PageRecord<>();
+            List<UserDailyActiveDTO> dtoList = CopyUtils.copyList(userActivityPage.getRecords(), UserDailyActiveDTO.class);
 
-        LambdaQueryWrapper<DailyActiveUsers> queryWrapper = new LambdaQueryWrapper<>();
-        if (Objects.nonNull(params.getCreateTimeStart())) {
-            queryWrapper.ge(DailyActiveUsers::getCreateTime, params.getCreateTimeStart());
-        }
-        if (Objects.nonNull(params.getCreateTimeEnd())) {
-            queryWrapper.lt(DailyActiveUsers::getCreateTime, params.getCreateTimeEnd().plusDays(1));
-        }
-        queryWrapper.orderByDesc(DailyActiveUsers::getCreateTime);
-
-        // ------------------------
-        // 手机号不为空 → 查询指定用户
-        // ------------------------
-        if (StringUtils.isNotBlank(params.getPhone())) {
-            final String phone = params.getPhone();
-            UserInfo userInfo = userService.lambdaQuery()
-                    .eq(UserInfo::getPhone, phone)
-                    .one();
-
-            if (userInfo == null) {
-                pageRecord.setRows(Collections.emptyList());
-                pageRecord.setTotal(0L);
-                pageRecord.setPageNum(params.getPageNo());
-                pageRecord.setPageSize(params.getPageSize());
-                pageRecord.setTotalPageNum(0);
-                return pageRecord;
-            }
-
-            queryWrapper.eq(DailyActiveUsers::getUserId, userInfo.getUserId());
-            List<DailyActiveUsers> userRecords = dailyActiveUsersService.list(queryWrapper);
-
-            UserDailyActiveDTO dto = new UserDailyActiveDTO();
-            dto.setPhone(phone);
-            dto.setVisitCount(userRecords.size());
-
-            pageRecord.setRows(Collections.singletonList(dto));
-            pageRecord.setTotal(userRecords.isEmpty() ? 0L : 1L);
-            pageRecord.setPageNum(params.getPageNo());
-            pageRecord.setPageSize(params.getPageSize());
-            pageRecord.setTotalPageNum(1);
-            return pageRecord;
+            return CopyUtils.copyPage(userActivityPage, dtoList);
         }
+        // 手机号为空,查询所有用户
+        log.info("查询所有用户日活---------------------------");
+        Page<UserActivity> userActivityPage = userActivityService.queryAllUserActivity(params);
 
-        // ------------------------
-        // 手机号为空 → 查询所有用户
-        // ------------------------
-        List<DailyActiveUsers> allRecords = dailyActiveUsersService.list(queryWrapper);
-        if (allRecords.isEmpty()) {
-            pageRecord.setRows(Collections.emptyList());
-            pageRecord.setTotal(0L);
-            pageRecord.setPageNum(params.getPageNo());
-            pageRecord.setPageSize(params.getPageSize());
-            pageRecord.setTotalPageNum(0);
-            return pageRecord;
-        }
+        List<UserDailyActiveDTO> dtoList = CopyUtils.copyList(userActivityPage.getRecords(), UserDailyActiveDTO.class);
 
-        // 批量查询用户信息
-        Set<Long> userIds = allRecords.stream()
-                .map(DailyActiveUsers::getUserId)
-                .collect(Collectors.toSet());
-        List<UserInfo> userInfos = userService.listByIds(userIds);
-        Map<Long, String> userIdPhoneMap = userInfos.stream()
-                .collect(Collectors.toMap(UserInfo::getUserId, UserInfo::getPhone));
-
-        // 聚合访问次数
-        Map<Long, Long> visitCountMap = allRecords.stream()
-                .collect(Collectors.groupingBy(DailyActiveUsers::getUserId, Collectors.counting()));
-
-        // 转 DTO 列表
-        List<UserDailyActiveDTO> dtoList = visitCountMap.entrySet().stream()
-                .map(entry -> {
-                    UserDailyActiveDTO dto = new UserDailyActiveDTO();
-                    dto.setPhone(userIdPhoneMap.get(entry.getKey()));
-                    dto.setVisitCount(entry.getValue().intValue());
-                    return dto;
-                }).collect(Collectors.toList());
-
-        // ------------------------
-        // Java 层分页
-        // ------------------------
-        int pageNo = params.getPageNo();
-        int pageSize = params.getPageSize();
-        int total = dtoList.size();
-        int start = (pageNo - 1) * pageSize;
-        int end = Math.min(start + pageSize, total);
-        List<UserDailyActiveDTO> pageList = start >= total ? Collections.emptyList() : dtoList.subList(start, end);
-
-        pageRecord.setRows(pageList);
-        pageRecord.setTotal((long) total);
-        pageRecord.setPageNum(pageNo);
-        pageRecord.setPageSize(pageSize);
-        pageRecord.setTotalPageNum((total + pageSize - 1) / pageSize);
-
-        return pageRecord;
+        return CopyUtils.copyPage(userActivityPage, dtoList);
     }
 
     @Override

+ 0 - 7
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/mapper/DailyActiveUsersMapper.java

@@ -1,7 +0,0 @@
-package com.hfln.portal.infrastructure.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.hfln.portal.infrastructure.po.DailyActiveUsers;
-
-public interface DailyActiveUsersMapper extends BaseMapper<DailyActiveUsers> {
-}

+ 12 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/mapper/UserActivityMapper.java

@@ -0,0 +1,12 @@
+package com.hfln.portal.infrastructure.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hfln.portal.infrastructure.po.UserActivity;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 用户活跃表 Mapper 接口
+ */
+@Mapper
+public interface UserActivityMapper extends BaseMapper<UserActivity> {
+}

+ 16 - 4
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/po/DailyActiveUsers.java → portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/po/UserActivity.java

@@ -7,22 +7,34 @@ import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
+import java.time.LocalDate;
+
 /**
- * 每日活跃用户表
+ * 用户活跃
  */
 @Data
 @EqualsAndHashCode(callSuper = true)
-@TableName("daily_active_users")
-public class DailyActiveUsers extends BasePO {
+@TableName("user_activity")
+public class UserActivity extends BasePO{
 
     /**
      * 主键ID,数据库自增策略
      */
     @TableId(type = IdType.ASSIGN_ID)
-    private Long dailyActiveId;
+    private Long activityId;
 
     /**
      * 用户ID
      */
     private Long userId;
+
+    /**
+     * 当日访问次数
+     */
+    private Integer visitCount;
+
+    /**
+     * 统计日期 yyyy-MM-dd
+     */
+    private LocalDate countDate;
 }

+ 0 - 9
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/DailyActiveUsersService.java

@@ -1,9 +0,0 @@
-package com.hfln.portal.infrastructure.service;
-
-import com.baomidou.mybatisplus.extension.service.IService;
-import com.hfln.portal.infrastructure.po.DailyActiveUsers;
-
-public interface DailyActiveUsersService extends IService<DailyActiveUsers> {
-
-    void insertDailyActiveUsers(Long userId);
-}

+ 24 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/UserActivityService.java

@@ -0,0 +1,24 @@
+package com.hfln.portal.infrastructure.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.hfln.portal.common.request.user.UserDailyActiveParams;
+import com.hfln.portal.infrastructure.po.UserActivity;
+
+import java.time.LocalDate;
+
+public interface UserActivityService extends IService<UserActivity> {
+    void insertUserActivity (Long userId);
+
+    UserActivity queryOneByUserId(Long UserId, LocalDate countDate);
+
+    /*
+     * 查询单个用户
+     */
+    Page<UserActivity> queryUserActivity(UserDailyActiveParams params);
+
+    /*
+     * 查询所有用户
+     */
+    Page<UserActivity> queryAllUserActivity(UserDailyActiveParams params);
+}

+ 0 - 45
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/impl/DailyActiveUsersServiceImpl.java

@@ -1,45 +0,0 @@
-package com.hfln.portal.infrastructure.service.impl;
-
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.hfln.portal.infrastructure.mapper.DailyActiveUsersMapper;
-import com.hfln.portal.infrastructure.po.BasePO;
-import com.hfln.portal.infrastructure.po.DailyActiveUsers;
-import com.hfln.portal.infrastructure.service.DailyActiveUsersService;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-
-import java.time.LocalDateTime;
-
-
-@Service
-@Slf4j
-public class DailyActiveUsersServiceImpl extends ServiceImpl<DailyActiveUsersMapper, DailyActiveUsers> implements DailyActiveUsersService {
-
-
-    /**
-     * 小程序用户登录首页,便往日活表插一条对应记录
-     */
-    @Override
-    public void insertDailyActiveUsers(Long userId) {
-        try {
-            LocalDateTime threshold = LocalDateTime.now().minusMinutes(10);
-
-            boolean exists = this.lambdaQuery()
-                    .eq(DailyActiveUsers::getUserId, userId)
-                    .ge(DailyActiveUsers::getCreateTime, threshold)
-                    .eq(DailyActiveUsers::getIsDeleted, BasePO.DeleteFlag.NOT_DELETED)
-                    .exists();   // ✅ 直接判断是否存在
-
-            if (exists) {
-                return;
-            }
-
-            DailyActiveUsers record = new DailyActiveUsers();
-            record.setUserId(userId);
-            this.save(record);
-
-        } catch (Exception e) {
-            log.warn("记录日活失败, userId={}", userId, e);
-        }
-    }
-}

+ 108 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/impl/UserActivityServiceImpl.java

@@ -0,0 +1,108 @@
+package com.hfln.portal.infrastructure.service.impl;
+
+import cn.hfln.framework.extension.BizException;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.hfln.portal.common.request.user.UserDailyActiveParams;
+import com.hfln.portal.domain.exception.ErrorEnum;
+import com.hfln.portal.infrastructure.mapper.UserActivityMapper;
+import com.hfln.portal.infrastructure.po.UserActivity;
+import com.hfln.portal.infrastructure.po.UserInfo;
+import com.hfln.portal.infrastructure.service.UserActivityService;
+import com.hfln.portal.infrastructure.service.UserService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.time.Duration;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+
+@Service
+@Slf4j
+public class UserActivityServiceImpl extends ServiceImpl<UserActivityMapper, UserActivity> implements UserActivityService {
+
+    private final UserService userService;
+
+    public UserActivityServiceImpl(UserService userService) {
+        this.userService = userService;
+    }
+
+    @Override
+    public UserActivity queryOneByUserId(Long userId, LocalDate countDate) {
+
+        LambdaQueryWrapper<UserActivity> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(UserActivity::getUserId, userId)
+                .eq(UserActivity::getCountDate, countDate);
+        return this.baseMapper.selectOne(wrapper);
+    }
+
+    @Override
+    public void insertUserActivity(Long userId){
+        LocalDate today = LocalDate.now();
+        // 查询今日是否有访问记录
+        UserActivity userActivity = this.queryOneByUserId(userId, today);
+        if (userActivity == null) {
+            // 插入今日的访问记录
+            UserActivity insertUserActivity = new UserActivity();
+            insertUserActivity.setUserId(userId);
+            insertUserActivity.setCountDate(today);
+            insertUserActivity.setVisitCount(1);
+            this.baseMapper.insert(insertUserActivity);
+        } else {
+            // 已存在今日记录,判断是否超过 10 分钟
+            LocalDateTime lastUpdate = userActivity.getUpdateTime();
+            LocalDateTime now = LocalDateTime.now();
+
+            if (Duration.between(lastUpdate, now).toMinutes() >= 10) {
+                LambdaUpdateWrapper<UserActivity> wrapper = new LambdaUpdateWrapper<>();
+                wrapper.eq(UserActivity::getUserId, userId)
+                        .eq(UserActivity::getCountDate, today)
+                        .set(UserActivity::getVisitCount, userActivity.getVisitCount() + 1)
+                        .set(UserActivity::getUpdateTime, now);
+                this.update(wrapper);
+            }
+        }
+        // 否则: 10分钟内,不做任何更新,算作一次访问
+    }
+
+    @Override
+    public Page<UserActivity> queryUserActivity(UserDailyActiveParams params) {
+        UserInfo userInfo = userService.queryByPhone(params.getPhone());
+        if (userInfo == null){
+            throw new BizException(ErrorEnum.USER_IS_NOT_EXIST.getErrorCode(), ErrorEnum.USER_IS_NOT_EXIST.getErrorMessage());
+        }
+
+        Page<UserActivity> page = new Page<>(params.getPageNo(), params.getPageSize());
+
+        LambdaQueryWrapper<UserActivity> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(UserActivity::getUserId, userInfo.getUserId());
+        if (params.getCreateTimeStart() != null){
+            queryWrapper.ge(UserActivity::getCountDate, params.getCreateTimeStart());
+        }
+        if (params.getCreateTimeEnd() != null){
+            queryWrapper.le(UserActivity::getCountDate, params.getCreateTimeEnd());
+        }
+        queryWrapper.orderByDesc(UserActivity::getCountDate);
+
+        return this.baseMapper.selectPage(page, queryWrapper);
+    }
+
+    @Override
+    public Page<UserActivity> queryAllUserActivity(UserDailyActiveParams params) {
+        Page<UserActivity> page = new Page<>(params.getPageNo(), params.getPageSize());
+
+        LambdaQueryWrapper<UserActivity> queryWrapper = new LambdaQueryWrapper<>();
+        if (params.getCreateTimeStart() != null) {
+            queryWrapper.ge(UserActivity::getCountDate, params.getCreateTimeStart());
+        }
+        if (params.getCreateTimeEnd() != null) {
+            queryWrapper.le(UserActivity::getCountDate, params.getCreateTimeEnd());
+        }
+        queryWrapper.orderByDesc(UserActivity::getCountDate);
+
+        return this.baseMapper.selectPage(page, queryWrapper);
+    }
+}