From b84935509db24b31c4071da28453767c5611a26d Mon Sep 17 00:00:00 2001 From: tianfeng <769204422@qq.com> Date: Mon, 16 Mar 2026 19:01:31 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=94=A8=E6=88=B7=E6=AF=8F?= =?UTF-8?q?=E6=97=A5=E6=B4=BB=E8=B7=83=E8=AE=B0=E5=BD=95=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/OnlineStatusUploadListener.java | 9 +++ .../cache/key/user/UserDailyActiveKey.java | 25 +++++++ .../user/active/UserDailyActiveLog.java | 60 +++++++++++++++++ .../user/active/UserDailyActiveService.java | 18 +++++ .../impl/UserDailyActiveServiceImpl.java | 67 +++++++++++++++++++ 5 files changed, 179 insertions(+) create mode 100644 rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/cache/key/user/UserDailyActiveKey.java create mode 100644 rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/user/active/UserDailyActiveLog.java create mode 100644 rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/user/active/UserDailyActiveService.java create mode 100644 rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/user/active/impl/UserDailyActiveServiceImpl.java diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/listener/user/OnlineStatusUploadListener.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/listener/user/OnlineStatusUploadListener.java index b9996a65..2fa27946 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/listener/user/OnlineStatusUploadListener.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/listener/user/OnlineStatusUploadListener.java @@ -17,6 +17,7 @@ import com.red.circle.other.infra.database.cache.service.user.CountUserActiveInd import com.red.circle.other.infra.database.mongo.entity.user.status.OnlineUser; import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberService; import com.red.circle.other.infra.database.mongo.service.user.status.OnlineUserService; +import com.red.circle.other.infra.database.mongo.service.user.active.UserDailyActiveService; import com.red.circle.other.infra.database.rds.service.user.user.ExpandService; import com.red.circle.other.infra.database.rds.service.user.user.OnlineTimeService; import com.red.circle.other.inner.enums.user.UserOnlineStatusEnum; @@ -50,6 +51,7 @@ public class OnlineStatusUploadListener implements MessageListener { private final OnlineTimeService onlineTimeService; private final UserProfileGateway userProfileGateway; private final OnlineStatusCommon onlineStatusCommon; + private final UserDailyActiveService userDailyActiveService; private final MessageEventProcess messageEventProcess; private final CountUserActiveIndexService countUserActiveIndexService; @@ -80,6 +82,13 @@ public class OnlineStatusUploadListener implements MessageListener { teamMemberService.updateActiveTimeNow(eventBody.getUserId()); // 用户活跃时间刷新 expandService.updateLastActiveTime(eventBody.getUserId()); + // 记录每日活跃 + userDailyActiveService.record( + eventBody.getUserId(), + Objects.toString(eventBody.getSysOrigin()), + userProfile.getCountryCode(), + expandService.getLanguageStr(eventBody.getUserId()) + ); // 累计在线时长 if (Objects.equals(eventBody.getTask(), Boolean.TRUE)) { diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/cache/key/user/UserDailyActiveKey.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/cache/key/user/UserDailyActiveKey.java new file mode 100644 index 00000000..6b1e01d7 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/cache/key/user/UserDailyActiveKey.java @@ -0,0 +1,25 @@ +package com.red.circle.other.infra.database.cache.key.user; + +import com.red.circle.component.redis.RedisKeys; + +/** + * 用户每日活跃缓存key. + */ +public enum UserDailyActiveKey implements RedisKeys { + + /** + * 用户当日活跃去重标记. key格式: USER_DAILY_ACTIVE:ACTIVE_FLAG:{userId}:{activeDate} + */ + ACTIVE_FLAG; + + @Override + public String businessPrefix() { + return "USER_DAILY_ACTIVE"; + } + + @Override + public String businessKey() { + return this.name(); + } + +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/user/active/UserDailyActiveLog.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/user/active/UserDailyActiveLog.java new file mode 100644 index 00000000..d2046e3c --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/user/active/UserDailyActiveLog.java @@ -0,0 +1,60 @@ +package com.red.circle.other.infra.database.mongo.entity.user.active; + +import java.io.Serial; +import java.io.Serializable; +import java.sql.Timestamp; +import lombok.Data; +import lombok.experimental.Accessors; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.index.CompoundIndex; +import org.springframework.data.mongodb.core.index.CompoundIndexes; +import org.springframework.data.mongodb.core.mapping.Document; + +/** + * 用户每日活跃日志. + */ +@Data +@Accessors(chain = true) +@Document("user_daily_active_log") +@CompoundIndexes({ + @CompoundIndex(name = "idx_uid_date", def = "{'userId': 1, 'activeDate': 1}", unique = true), + @CompoundIndex(name = "idx_date_origin", def = "{'activeDate': 1, 'sysOrigin': 1}"), + @CompoundIndex(name = "idx_date_country", def = "{'activeDate': 1, 'registerCountryCode': 1}") +}) +public class UserDailyActiveLog implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Id + private Long id; + + /** 用户id. */ + private Long userId; + + /** 活跃日期(北京时间 yyyy-MM-dd). */ + private String activeDate; + + /** 来源系统. */ + private String sysOrigin; + + /** 注册国家code. */ + private String registerCountryCode; + + /** 用户语言. */ + private String language; + + /** 当日活跃次数. */ + private Integer activeCount; + + /** 当日首次活跃时间. */ + private Timestamp firstActiveTime; + + /** 当日最后活跃时间. */ + private Timestamp lastActiveTime; + + private Timestamp createTime; + + private Timestamp updateTime; + +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/user/active/UserDailyActiveService.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/user/active/UserDailyActiveService.java new file mode 100644 index 00000000..8e2567c0 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/user/active/UserDailyActiveService.java @@ -0,0 +1,18 @@ +package com.red.circle.other.infra.database.mongo.service.user.active; + +/** + * 用户每日活跃日志服务. + */ +public interface UserDailyActiveService { + + /** + * 记录用户当日活跃. + * + * @param userId 用户id + * @param sysOrigin 来源系统 + * @param registerCountryCode 注册国家code + * @param language 用户语言 + */ + void record(Long userId, String sysOrigin, String registerCountryCode, String language); + +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/user/active/impl/UserDailyActiveServiceImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/user/active/impl/UserDailyActiveServiceImpl.java new file mode 100644 index 00000000..a82dc8aa --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/user/active/impl/UserDailyActiveServiceImpl.java @@ -0,0 +1,67 @@ +package com.red.circle.other.infra.database.mongo.service.user.active.impl; + +import com.red.circle.other.infra.database.cache.key.user.UserDailyActiveKey; +import com.red.circle.other.infra.database.mongo.entity.user.active.UserDailyActiveLog; +import com.red.circle.other.infra.database.mongo.service.user.active.UserDailyActiveService; +import com.red.circle.other.infra.utils.ZonedDateTimeUtils; +import com.red.circle.tool.core.sequence.IdWorkerUtils; +import java.sql.Timestamp; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.concurrent.TimeUnit; +import lombok.RequiredArgsConstructor; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.mongodb.core.query.Update; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Service; + +/** + * 用户每日活跃日志服务. + */ +@Service +@RequiredArgsConstructor +public class UserDailyActiveServiceImpl implements UserDailyActiveService { + + private final MongoTemplate mongoTemplate; + private final StringRedisTemplate stringRedisTemplate; + + @Override + public void record(Long userId, String sysOrigin, String registerCountryCode, String language) { + ZonedDateTime now = ZonedDateTimeUtils.nowAsiaShanghai(); + String activeDate = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); + Timestamp nowTs = Timestamp.from(now.toInstant()); + + String redisKey = UserDailyActiveKey.ACTIVE_FLAG.getKey(userId, activeDate); + boolean isFirst = Boolean.TRUE.equals( + stringRedisTemplate.opsForValue().setIfAbsent(redisKey, "1", 2, TimeUnit.DAYS) + ); + + if (isFirst) { + UserDailyActiveLog log = new UserDailyActiveLog() + .setId(IdWorkerUtils.getId()) + .setUserId(userId) + .setActiveDate(activeDate) + .setSysOrigin(sysOrigin) + .setRegisterCountryCode(registerCountryCode) + .setLanguage(language) + .setActiveCount(1) + .setFirstActiveTime(nowTs) + .setLastActiveTime(nowTs) + .setCreateTime(nowTs) + .setUpdateTime(nowTs); + mongoTemplate.insert(log); + } else { + mongoTemplate.updateFirst( + Query.query(Criteria.where("userId").is(userId).and("activeDate").is(activeDate)), + new Update() + .inc("activeCount", 1) + .set("lastActiveTime", nowTs) + .set("updateTime", nowTs), + UserDailyActiveLog.class + ); + } + } + +}