feat: 生成活动接口
This commit is contained in:
137
src/users/services/user-activity.service.ts
Normal file
137
src/users/services/user-activity.service.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/sequelize';
|
||||
import { UserActivity, ActivityType, ActivityLevel } from '../models/user-activity.model';
|
||||
import { CreateUserActivityDto, UserActivitySummaryDto } from '../dto/user-activity.dto';
|
||||
import { Op } from 'sequelize';
|
||||
import * as dayjs from 'dayjs';
|
||||
|
||||
@Injectable()
|
||||
export class UserActivityService {
|
||||
private readonly logger = new Logger(UserActivityService.name);
|
||||
|
||||
constructor(
|
||||
@InjectModel(UserActivity)
|
||||
private userActivityModel: typeof UserActivity,
|
||||
) { }
|
||||
|
||||
/**
|
||||
* 记录用户活跃
|
||||
*/
|
||||
async recordActivity(userId: string, activityDto: CreateUserActivityDto): Promise<UserActivity> {
|
||||
try {
|
||||
// 使用 upsert 避免重复记录同一天同一类型的活跃
|
||||
const [activity, created] = await this.userActivityModel.upsert({
|
||||
userId,
|
||||
...activityDto,
|
||||
});
|
||||
|
||||
this.logger.log(`记录用户活跃 - 用户: ${userId}, 类型: ${activityDto.activityType}, 日期: ${activityDto.activityDate}, 是否新建: ${created}`);
|
||||
return activity;
|
||||
} catch (error) {
|
||||
this.logger.error(`记录用户活跃失败: ${error.message}`, error.stack);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查今日是否有登录记录,如果没有则创建
|
||||
*/
|
||||
async checkAndRecordTodayLogin(userId: string): Promise<void> {
|
||||
const today = dayjs().format('YYYY-MM-DD'); // YYYY-MM-DD 格式
|
||||
|
||||
try {
|
||||
const existingLogin = await this.userActivityModel.findOne({
|
||||
where: {
|
||||
userId,
|
||||
activityDate: today,
|
||||
activityType: ActivityType.LOGIN,
|
||||
},
|
||||
});
|
||||
|
||||
if (!existingLogin) {
|
||||
await this.recordActivity(userId, {
|
||||
activityType: ActivityType.LOGIN,
|
||||
activityDate: today,
|
||||
level: ActivityLevel.LOW, // 登录默认为低活跃
|
||||
remark: '用户拉取profile接口自动记录',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(`检查并记录今日登录失败: ${error.message}`, error.stack);
|
||||
// 不抛出错误,避免影响主要业务流程
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户最近六个月的活跃情况
|
||||
*/
|
||||
async getUserActivityHistory(userId: string): Promise<UserActivitySummaryDto[]> {
|
||||
const startDate = dayjs().subtract(6, 'month').format('YYYY-MM-DD');
|
||||
const today = dayjs().format('YYYY-MM-DD');
|
||||
|
||||
try {
|
||||
// 获取用户在指定时间范围内的活跃记录,按日期分组,取每日最高活跃等级
|
||||
const activities = await this.userActivityModel.findAll({
|
||||
where: {
|
||||
userId,
|
||||
activityDate: {
|
||||
[Op.gte]: startDate,
|
||||
[Op.lte]: today,
|
||||
},
|
||||
},
|
||||
attributes: [
|
||||
'activityDate',
|
||||
[this.userActivityModel.sequelize!.fn('MAX', this.userActivityModel.sequelize!.col('level')), 'maxLevel'],
|
||||
],
|
||||
group: ['activityDate'],
|
||||
order: [['activityDate', 'ASC']],
|
||||
raw: true,
|
||||
});
|
||||
|
||||
// 生成完整的日期范围(最近6个月的每一天)
|
||||
const result: UserActivitySummaryDto[] = [];
|
||||
let cursor = dayjs(startDate, 'YYYY-MM-DD');
|
||||
const end = dayjs(today, 'YYYY-MM-DD');
|
||||
|
||||
while (!cursor.isAfter(end)) {
|
||||
const dateStr = cursor.format('YYYY-MM-DD');
|
||||
const activity = activities.find((a: any) => a.activityDate === dateStr);
|
||||
|
||||
result.push({
|
||||
date: dateStr,
|
||||
level: activity ? parseInt((activity as any).maxLevel) : ActivityLevel.NONE, // 无活跃
|
||||
});
|
||||
|
||||
cursor = cursor.add(1, 'day');
|
||||
}
|
||||
|
||||
this.logger.log(`获取用户活跃历史 - 用户: ${userId}, 记录数: ${result.length}`);
|
||||
return result;
|
||||
} catch (error) {
|
||||
this.logger.error(`获取用户活跃历史失败: ${error.message}`, error.stack);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户某日的活跃等级(如果新等级更高)
|
||||
*/
|
||||
async updateActivityLevel(userId: string, activityDate: string, activityType: ActivityType, newLevel: ActivityLevel): Promise<void> {
|
||||
try {
|
||||
const activity = await this.userActivityModel.findOne({
|
||||
where: {
|
||||
userId,
|
||||
activityDate,
|
||||
activityType,
|
||||
},
|
||||
});
|
||||
|
||||
if (activity && activity.level < newLevel) {
|
||||
await activity.update({ level: newLevel });
|
||||
this.logger.log(`更新用户活跃等级 - 用户: ${userId}, 日期: ${activityDate}, 类型: ${activityType}, 新等级: ${newLevel}`);
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(`更新用户活跃等级失败: ${error.message}`, error.stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user