新增用户每日活跃记录功能

This commit is contained in:
tianfeng 2026-03-16 19:01:31 +08:00
parent 923c03c155
commit b84935509d
5 changed files with 179 additions and 0 deletions

View File

@ -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)) {

View File

@ -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();
}
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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
);
}
}
}