feat: 新增心情打卡功能模块

实现心情打卡的完整功能,包括数据库表设计、API接口、业务逻辑和文档说明。支持记录多种心情类型、强度评分和统计分析功能。
This commit is contained in:
richarjiang
2025-08-21 15:20:05 +08:00
parent 513d6e071d
commit f26d8e64c6
9 changed files with 888 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
-- 心情打卡表
CREATE TABLE IF NOT EXISTS `t_mood_checkins` (
`id` varchar(36) NOT NULL COMMENT '主键ID',
`user_id` varchar(255) NOT NULL COMMENT '用户ID',
`mood_type` enum('happy','excited','thrilled','calm','anxious','sad','lonely','wronged','angry','tired') NOT NULL COMMENT '心情类型:开心、心动、兴奋、平静、焦虑、难过、孤独、委屈、生气、心累',
`intensity` int NOT NULL DEFAULT '5' COMMENT '心情强度1-10',
`description` text COMMENT '心情描述',
`checkin_date` date NOT NULL COMMENT '打卡日期YYYY-MM-DD',
`metadata` json DEFAULT NULL COMMENT '扩展数据(标签、触发事件等)',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否删除',
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_checkin_date` (`checkin_date`),
KEY `idx_mood_type` (`mood_type`),
KEY `idx_user_date` (`user_id`, `checkin_date`),
KEY `idx_deleted` (`deleted`),
CONSTRAINT `fk_mood_checkins_user_id` FOREIGN KEY (`user_id`) REFERENCES `t_users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='心情打卡表';
-- 添加索引以优化查询性能
CREATE INDEX `idx_user_mood_date` ON `t_mood_checkins` (`user_id`, `mood_type`, `checkin_date`);
CREATE INDEX `idx_intensity` ON `t_mood_checkins` (`intensity`);

View File

@@ -13,6 +13,7 @@ import { RecommendationsModule } from './recommendations/recommendations.module'
import { ActivityLogsModule } from './activity-logs/activity-logs.module';
import { ExercisesModule } from './exercises/exercises.module';
import { WorkoutsModule } from './workouts/workouts.module';
import { MoodCheckinsModule } from './mood-checkins/mood-checkins.module';
@Module({
imports: [
@@ -31,6 +32,7 @@ import { WorkoutsModule } from './workouts/workouts.module';
ActivityLogsModule,
ExercisesModule,
WorkoutsModule,
MoodCheckinsModule,
],
controllers: [AppController],
providers: [AppService],

145
src/mood-checkins/README.md Normal file
View File

@@ -0,0 +1,145 @@
# 心情打卡功能
## 功能概述
心情打卡功能允许用户记录每日的情绪状态,包括喜怒哀乐四种基本情绪类型,并可以添加强度评分和详细描述。
## 功能特性
### 1. 心情类型
- **开心 (happy)**: 愉悦、满足的情绪
- **心动 (excited)**: 因喜欢而心跳加速的情绪
- **兴奋 (thrilled)**: 激动、充满活力的情绪
- **平静 (calm)**: 宁静、安详的情绪
- **焦虑 (anxious)**: 紧张、担忧的情绪
- **难过 (sad)**: 悲伤、失落的情绪
- **孤独 (lonely)**: 寂寞、孤单的情绪
- **委屈 (wronged)**: 受到不公正对待的情绪
- **生气 (angry)**: 愤怒、恼火的情绪
- **心累 (tired)**: 精神疲惫、倦怠的情绪
### 2. 核心功能
- ✅ 创建心情打卡记录
- ✅ 更新已有心情记录
- ✅ 删除心情记录(软删除)
- ✅ 查看每日心情记录
- ✅ 查看历史心情记录
- ✅ 心情统计分析
### 3. 数据字段
- `moodType`: 心情类型(必填)
- `intensity`: 心情强度 1-10必填默认5
- `description`: 心情描述(可选)
- `checkinDate`: 打卡日期(可选,默认当天)
- `metadata`: 扩展数据,如标签、触发事件等(可选)
## API 接口
### 创建心情打卡
```http
POST /mood-checkins
```
### 更新心情打卡
```http
PUT /mood-checkins
```
### 删除心情打卡
```http
DELETE /mood-checkins
```
### 获取每日心情
```http
GET /mood-checkins/daily?date=2025-08-21
```
### 获取心情历史
```http
GET /mood-checkins/history?startDate=2025-08-01&endDate=2025-08-31&moodType=joy
```
### 获取心情统计
```http
GET /mood-checkins/statistics?startDate=2025-08-01&endDate=2025-08-31
```
## 数据库设计
### 表结构
- 表名: `t_mood_checkins`
- 主键: `id` (UUID)
- 外键: `user_id` (关联用户表)
- 索引: 用户ID、打卡日期、心情类型等
### 约束条件
- 同一用户同一天同一心情类型只能有一条记录(自动更新)
- 心情强度范围: 1-10
- 支持软删除
## 业务逻辑
### 1. 创建逻辑
- 检查当天是否已有相同心情类型的记录
- 如果存在则更新,不存在则创建新记录
- 记录活动日志
### 2. 统计功能
- 总打卡次数
- 平均心情强度
- 各心情类型分布
- 最频繁的心情类型
### 3. 权限控制
- 用户只能操作自己的心情记录
- 需要JWT认证
- 支持活动日志记录
## 使用示例
### 创建心情打卡
```json
{
"moodType": "joy",
"intensity": 8,
"description": "今天工作顺利,心情很好",
"metadata": {
"tags": ["工作", "成就感"],
"trigger": "完成重要项目"
}
}
```
### 统计数据响应
```json
{
"code": 200,
"message": "success",
"data": {
"totalCheckins": 30,
"averageIntensity": 6.8,
"moodDistribution": {
"happy": 8,
"excited": 5,
"thrilled": 4,
"calm": 6,
"anxious": 3,
"sad": 2,
"lonely": 1,
"wronged": 0,
"angry": 1,
"tired": 0
},
"mostFrequentMood": "happy"
}
}
```
## 扩展功能建议
1. **心情趋势分析**: 基于历史数据分析心情变化趋势
2. **心情提醒**: 定时提醒用户进行心情打卡
3. **心情分享**: 允许用户分享心情状态
4. **心情建议**: 基于心情状态提供改善建议
5. **数据导出**: 支持心情数据导出功能

View File

@@ -0,0 +1,133 @@
import { ApiProperty, PartialType } from '@nestjs/swagger';
import { IsString, IsOptional, IsEnum, IsInt, Min, Max, IsObject, IsDateString } from 'class-validator';
import { MoodType } from '../models/mood-checkin.model';
import { ResponseCode } from '../../base.dto';
export class CreateMoodCheckinDto {
// 由后端从登录态注入
@IsOptional()
@IsString()
userId?: string;
@ApiProperty({
enum: MoodType,
description: '心情类型:开心、心动、兴奋、平静、焦虑、难过、孤独、委屈、生气、心累',
example: MoodType.HAPPY
})
@IsEnum(MoodType)
moodType: MoodType;
@ApiProperty({
description: '心情强度1-10',
minimum: 1,
maximum: 10,
example: 7
})
@IsInt()
@Min(1)
@Max(10)
intensity: number;
@ApiProperty({
description: '心情描述',
required: false,
example: '今天工作顺利,心情很好'
})
@IsOptional()
@IsString()
description?: string;
@ApiProperty({
description: '打卡日期(YYYY-MM-DD)',
required: false,
example: '2025-08-21'
})
@IsOptional()
@IsDateString()
checkinDate?: string;
@ApiProperty({
description: '扩展数据',
required: false,
example: { tags: ['工作', '运动'], trigger: '完成重要项目' }
})
@IsOptional()
@IsObject()
metadata?: Record<string, any>;
}
export class UpdateMoodCheckinDto extends PartialType(CreateMoodCheckinDto) {
@ApiProperty({ description: '心情打卡ID' })
@IsString()
id: string;
// 由后端从登录态注入
@IsOptional()
@IsString()
userId?: string;
}
export class RemoveMoodCheckinDto {
@ApiProperty({ description: '心情打卡ID' })
@IsString()
id: string;
// 由后端从登录态注入
@IsOptional()
@IsString()
userId?: string;
}
export class MoodCheckinResponseDto<T = any> {
@ApiProperty({ description: '状态码', example: ResponseCode.SUCCESS })
code: ResponseCode;
@ApiProperty({ description: '消息', example: 'success' })
message: string;
@ApiProperty({ description: '数据' })
data: T;
}
export class GetMoodCheckinsQueryDto {
@ApiProperty({
description: '日期(YYYY-MM-DD),不传则默认今天',
required: false,
example: '2025-08-21'
})
@IsOptional()
@IsString()
date?: string;
}
export class GetMoodHistoryQueryDto {
@ApiProperty({
description: '开始日期(YYYY-MM-DD)',
example: '2025-08-01'
})
@IsString()
startDate: string;
@ApiProperty({
description: '结束日期(YYYY-MM-DD)',
example: '2025-08-31'
})
@IsString()
endDate: string;
@ApiProperty({
description: '心情类型过滤:开心、心动、兴奋、平静、焦虑、难过、孤独、委屈、生气、心累',
enum: MoodType,
required: false
})
@IsOptional()
@IsEnum(MoodType)
moodType?: MoodType;
}
export interface MoodStatistics {
totalCheckins: number;
averageIntensity: number;
moodDistribution: Record<MoodType, number>;
mostFrequentMood: MoodType | null;
}

View File

@@ -0,0 +1,97 @@
import { Column, Model, Table, DataType, ForeignKey, BelongsTo } from 'sequelize-typescript';
import { User } from '../../users/models/user.model';
export enum MoodType {
HAPPY = 'happy', // 开心
EXCITED = 'excited', // 心动
THRILLED = 'thrilled', // 兴奋
CALM = 'calm', // 平静
ANXIOUS = 'anxious', // 焦虑
SAD = 'sad', // 难过
LONELY = 'lonely', // 孤独
WRONGED = 'wronged', // 委屈
ANGRY = 'angry', // 生气
TIRED = 'tired' // 心累
}
@Table({
tableName: 't_mood_checkins',
underscored: true,
})
export class MoodCheckin extends Model {
@Column({
type: DataType.UUID,
defaultValue: DataType.UUIDV4,
primaryKey: true,
})
declare id: string;
@ForeignKey(() => User)
@Column({
type: DataType.STRING,
allowNull: false,
comment: '用户ID',
})
declare userId: string;
@BelongsTo(() => User)
declare user: User;
@Column({
type: DataType.ENUM(...Object.values(MoodType)),
allowNull: false,
comment: '心情类型:喜怒哀乐',
})
declare moodType: MoodType;
@Column({
type: DataType.INTEGER,
allowNull: false,
defaultValue: 5,
comment: '心情强度1-10',
validate: {
min: 1,
max: 10,
},
})
declare intensity: number;
@Column({
type: DataType.TEXT,
allowNull: true,
comment: '心情描述',
})
declare description: string | null;
@Column({
type: DataType.DATEONLY,
allowNull: false,
comment: '打卡日期YYYY-MM-DD',
})
declare checkinDate: string;
@Column({
type: DataType.JSON,
allowNull: true,
comment: '扩展数据(标签、触发事件等)',
})
declare metadata: Record<string, any> | null;
@Column({
type: DataType.DATE,
defaultValue: DataType.NOW,
})
declare createdAt: Date;
@Column({
type: DataType.DATE,
defaultValue: DataType.NOW,
})
declare updatedAt: Date;
@Column({
type: DataType.BOOLEAN,
defaultValue: false,
})
declare deleted: boolean;
}

View File

@@ -0,0 +1,106 @@
import { Controller, Get, Post, Put, Delete, Body, Query, UseGuards, Logger } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth } from '@nestjs/swagger';
import { MoodCheckinsService } from './mood-checkins.service';
import {
CreateMoodCheckinDto,
UpdateMoodCheckinDto,
RemoveMoodCheckinDto,
MoodCheckinResponseDto,
GetMoodCheckinsQueryDto,
GetMoodHistoryQueryDto,
MoodStatistics
} from './dto/mood-checkin.dto';
import { JwtAuthGuard } from '../common/guards/jwt-auth.guard';
import { CurrentUser } from '../common/decorators/current-user.decorator';
@ApiTags('心情打卡')
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@Controller('mood-checkins')
export class MoodCheckinsController {
private readonly logger = new Logger(MoodCheckinsController.name);
constructor(private readonly moodCheckinsService: MoodCheckinsService) { }
@Post()
@ApiOperation({ summary: '创建心情打卡' })
@ApiResponse({ status: 201, description: '心情打卡创建成功', type: MoodCheckinResponseDto })
@ApiResponse({ status: 400, description: '请求参数错误' })
@ApiResponse({ status: 401, description: '未授权' })
async create(
@Body() createMoodCheckinDto: CreateMoodCheckinDto,
@CurrentUser('id') userId: string,
): Promise<MoodCheckinResponseDto> {
this.logger.log(`用户 ${userId} 创建心情打卡: ${JSON.stringify(createMoodCheckinDto)}`);
return this.moodCheckinsService.create(createMoodCheckinDto, userId);
}
@Put()
@ApiOperation({ summary: '更新心情打卡' })
@ApiResponse({ status: 200, description: '心情打卡更新成功', type: MoodCheckinResponseDto })
@ApiResponse({ status: 400, description: '请求参数错误' })
@ApiResponse({ status: 401, description: '未授权' })
@ApiResponse({ status: 403, description: '无权操作' })
@ApiResponse({ status: 404, description: '记录不存在' })
async update(
@Body() updateMoodCheckinDto: UpdateMoodCheckinDto,
@CurrentUser('id') userId: string,
): Promise<MoodCheckinResponseDto> {
this.logger.log(`用户 ${userId} 更新心情打卡: ${JSON.stringify(updateMoodCheckinDto)}`);
return this.moodCheckinsService.update(updateMoodCheckinDto, userId);
}
@Delete()
@ApiOperation({ summary: '删除心情打卡' })
@ApiResponse({ status: 200, description: '心情打卡删除成功', type: MoodCheckinResponseDto })
@ApiResponse({ status: 401, description: '未授权' })
@ApiResponse({ status: 403, description: '无权操作' })
@ApiResponse({ status: 404, description: '记录不存在' })
async remove(
@Body() removeMoodCheckinDto: RemoveMoodCheckinDto,
@CurrentUser('id') userId: string,
): Promise<MoodCheckinResponseDto> {
this.logger.log(`用户 ${userId} 删除心情打卡: ${removeMoodCheckinDto.id}`);
return this.moodCheckinsService.remove(removeMoodCheckinDto, userId);
}
@Get('daily')
@ApiOperation({ summary: '获取每日心情打卡' })
@ApiResponse({ status: 200, description: '获取成功', type: MoodCheckinResponseDto })
@ApiResponse({ status: 400, description: '日期格式错误' })
@ApiResponse({ status: 401, description: '未授权' })
async getDaily(
@Query() query: GetMoodCheckinsQueryDto,
@CurrentUser('id') userId: string,
): Promise<MoodCheckinResponseDto> {
this.logger.log(`用户 ${userId} 获取每日心情打卡: ${query.date || '今天'}`);
return this.moodCheckinsService.getDaily(userId, query.date);
}
@Get('history')
@ApiOperation({ summary: '获取心情打卡历史' })
@ApiResponse({ status: 200, description: '获取成功', type: MoodCheckinResponseDto })
@ApiResponse({ status: 400, description: '日期范围错误' })
@ApiResponse({ status: 401, description: '未授权' })
async getHistory(
@Query() query: GetMoodHistoryQueryDto,
@CurrentUser('id') userId: string,
): Promise<MoodCheckinResponseDto> {
this.logger.log(`用户 ${userId} 获取心情打卡历史: ${query.startDate} - ${query.endDate}`);
return this.moodCheckinsService.getHistory(userId, query);
}
@Get('statistics')
@ApiOperation({ summary: '获取心情统计数据' })
@ApiResponse({ status: 200, description: '获取成功', type: MoodCheckinResponseDto })
@ApiResponse({ status: 400, description: '日期范围错误' })
@ApiResponse({ status: 401, description: '未授权' })
async getStatistics(
@Query('startDate') startDate: string,
@Query('endDate') endDate: string,
@CurrentUser('id') userId: string,
): Promise<MoodCheckinResponseDto<MoodStatistics>> {
this.logger.log(`用户 ${userId} 获取心情统计: ${startDate} - ${endDate}`);
return this.moodCheckinsService.getStatistics(userId, startDate, endDate);
}
}

View File

@@ -0,0 +1,15 @@
import { Module } from '@nestjs/common';
import { SequelizeModule } from '@nestjs/sequelize';
import { MoodCheckinsService } from './mood-checkins.service';
import { MoodCheckinsController } from './mood-checkins.controller';
import { MoodCheckin } from './models/mood-checkin.model';
import { UsersModule } from '../users/users.module';
import { ActivityLogsModule } from '../activity-logs/activity-logs.module';
@Module({
imports: [SequelizeModule.forFeature([MoodCheckin]), UsersModule, ActivityLogsModule],
providers: [MoodCheckinsService],
controllers: [MoodCheckinsController],
exports: [MoodCheckinsService],
})
export class MoodCheckinsModule { }

View File

@@ -0,0 +1,272 @@
import { Injectable, NotFoundException, Logger, ForbiddenException, BadRequestException } from '@nestjs/common';
import { InjectModel } from '@nestjs/sequelize';
import { MoodCheckin, MoodType } from './models/mood-checkin.model';
import {
CreateMoodCheckinDto,
UpdateMoodCheckinDto,
RemoveMoodCheckinDto,
MoodCheckinResponseDto,
GetMoodHistoryQueryDto,
MoodStatistics
} from './dto/mood-checkin.dto';
import { ResponseCode } from '../base.dto';
import * as dayjs from 'dayjs';
import { Op } from 'sequelize';
import { ActivityLogsService } from '../activity-logs/activity-logs.service';
import { ActivityActionType, ActivityEntityType } from '../activity-logs/models/activity-log.model';
@Injectable()
export class MoodCheckinsService {
private readonly logger = new Logger(MoodCheckinsService.name);
constructor(
@InjectModel(MoodCheckin)
private readonly moodCheckinModel: typeof MoodCheckin,
private readonly activityLogsService: ActivityLogsService,
) { }
async create(dto: CreateMoodCheckinDto, userId: string): Promise<MoodCheckinResponseDto> {
const checkinDate = dto.checkinDate || dayjs().format('YYYY-MM-DD');
// 检查当天是否已有相同心情类型的记录
const existingRecord = await this.moodCheckinModel.findOne({
where: {
userId,
moodType: dto.moodType,
checkinDate,
deleted: false,
},
});
if (existingRecord) {
// 如果存在,则更新现有记录
return this.update({
...dto,
id: existingRecord.id,
checkinDate,
}, userId);
}
const record = await this.moodCheckinModel.create({
userId,
moodType: dto.moodType,
intensity: dto.intensity,
description: dto.description || null,
checkinDate,
metadata: dto.metadata || null,
});
await this.activityLogsService.record({
userId,
entityType: ActivityEntityType.CHECKIN,
action: ActivityActionType.CREATE,
entityId: record.id,
changes: record.toJSON(),
});
return {
code: ResponseCode.SUCCESS,
message: '心情打卡成功',
data: record.toJSON()
};
}
async update(dto: UpdateMoodCheckinDto, userId: string): Promise<MoodCheckinResponseDto> {
const record = await this.moodCheckinModel.findOne({
where: {
id: dto.id,
deleted: false,
},
});
if (!record) {
throw new NotFoundException('心情打卡记录不存在');
}
if (record.userId !== userId) {
throw new ForbiddenException('无权操作该心情打卡记录');
}
const changes: Record<string, any> = {};
if (dto.moodType !== undefined) {
record.moodType = dto.moodType;
changes.moodType = dto.moodType;
}
if (dto.intensity !== undefined) {
record.intensity = dto.intensity;
changes.intensity = dto.intensity;
}
if (dto.description !== undefined) {
record.description = dto.description;
changes.description = dto.description;
}
if (dto.checkinDate !== undefined) {
record.checkinDate = dto.checkinDate;
changes.checkinDate = dto.checkinDate;
}
if (dto.metadata !== undefined) {
record.metadata = dto.metadata as any;
changes.metadata = dto.metadata;
}
await record.save();
await this.activityLogsService.record({
userId: record.userId,
entityType: ActivityEntityType.CHECKIN,
action: ActivityActionType.UPDATE,
entityId: record.id,
changes,
});
return {
code: ResponseCode.SUCCESS,
message: '心情打卡更新成功',
data: record.toJSON()
};
}
async remove(dto: RemoveMoodCheckinDto, userId: string): Promise<MoodCheckinResponseDto> {
const record = await this.moodCheckinModel.findByPk(dto.id);
if (!record) {
throw new NotFoundException('心情打卡记录不存在');
}
if (record.userId !== userId) {
throw new ForbiddenException('无权操作该心情打卡记录');
}
record.deleted = true;
await record.save();
await this.activityLogsService.record({
userId: record.userId,
entityType: ActivityEntityType.CHECKIN,
action: ActivityActionType.DELETE,
entityId: record.id,
changes: null,
});
return {
code: ResponseCode.SUCCESS,
message: '心情打卡删除成功',
data: { id: dto.id }
};
}
async getDaily(userId: string, date?: string): Promise<MoodCheckinResponseDto> {
const targetDate = date || dayjs().format('YYYY-MM-DD');
if (!dayjs(targetDate, 'YYYY-MM-DD').isValid()) {
throw new BadRequestException('无效日期格式');
}
const records = await this.moodCheckinModel.findAll({
where: {
userId,
checkinDate: targetDate,
deleted: false,
},
order: [['createdAt', 'ASC']],
});
return {
code: ResponseCode.SUCCESS,
message: 'success',
data: records.map(r => r.toJSON())
};
}
async getHistory(userId: string, query: GetMoodHistoryQueryDto): Promise<MoodCheckinResponseDto> {
const start = dayjs(query.startDate, 'YYYY-MM-DD');
const end = dayjs(query.endDate, 'YYYY-MM-DD');
if (!start.isValid() || !end.isValid() || end.isBefore(start)) {
throw new BadRequestException('无效日期范围');
}
const whereCondition: any = {
userId,
checkinDate: {
[Op.between]: [start.format('YYYY-MM-DD'), end.format('YYYY-MM-DD')],
},
deleted: false,
};
if (query.moodType) {
whereCondition.moodType = query.moodType;
}
const records = await this.moodCheckinModel.findAll({
where: whereCondition,
order: [['checkinDate', 'DESC'], ['createdAt', 'DESC']],
});
return {
code: ResponseCode.SUCCESS,
message: 'success',
data: records.map(r => r.toJSON())
};
}
async getStatistics(userId: string, startDate: string, endDate: string): Promise<MoodCheckinResponseDto<MoodStatistics>> {
const start = dayjs(startDate, 'YYYY-MM-DD');
const end = dayjs(endDate, 'YYYY-MM-DD');
if (!start.isValid() || !end.isValid() || end.isBefore(start)) {
throw new BadRequestException('无效日期范围');
}
const records = await this.moodCheckinModel.findAll({
where: {
userId,
checkinDate: {
[Op.between]: [start.format('YYYY-MM-DD'), end.format('YYYY-MM-DD')],
},
deleted: false,
},
});
const totalCheckins = records.length;
const averageIntensity = totalCheckins > 0
? records.reduce((sum, record) => sum + record.intensity, 0) / totalCheckins
: 0;
const moodDistribution: Record<MoodType, number> = {
[MoodType.HAPPY]: 0,
[MoodType.EXCITED]: 0,
[MoodType.THRILLED]: 0,
[MoodType.CALM]: 0,
[MoodType.ANXIOUS]: 0,
[MoodType.SAD]: 0,
[MoodType.LONELY]: 0,
[MoodType.WRONGED]: 0,
[MoodType.ANGRY]: 0,
[MoodType.TIRED]: 0,
};
records.forEach(record => {
moodDistribution[record.moodType]++;
});
const mostFrequentMood = totalCheckins > 0
? Object.entries(moodDistribution).reduce((a, b) =>
moodDistribution[a[0] as MoodType] > moodDistribution[b[0] as MoodType] ? a : b
)[0] as MoodType
: null;
const statistics: MoodStatistics = {
totalCheckins,
averageIntensity: Math.round(averageIntensity * 100) / 100,
moodDistribution,
mostFrequentMood,
};
return {
code: ResponseCode.SUCCESS,
message: 'success',
data: statistics
};
}
}

94
test-mood-checkins.http Normal file
View File

@@ -0,0 +1,94 @@
### 心情打卡功能测试
### 1. 创建心情打卡 - 开心
POST http://localhost:3000/mood-checkins
Authorization: Bearer YOUR_JWT_TOKEN
Content-Type: application/json
{
"moodType": "happy",
"intensity": 8,
"description": "今天完成了重要的项目,心情很好!",
"checkinDate": "2025-08-21",
"metadata": {
"tags": ["工作", "成就感"],
"trigger": "完成项目里程碑"
}
}
### 2. 创建心情打卡 - 焦虑
POST http://localhost:3000/mood-checkins
Authorization: Bearer YOUR_JWT_TOKEN
Content-Type: application/json
{
"moodType": "anxious",
"intensity": 6,
"description": "明天有重要的会议,感到有些紧张",
"checkinDate": "2025-08-21",
"metadata": {
"tags": ["工作", "压力"],
"trigger": "重要会议前的紧张"
}
}
### 3. 创建心情打卡 - 心动
POST http://localhost:3000/mood-checkins
Authorization: Bearer YOUR_JWT_TOKEN
Content-Type: application/json
{
"moodType": "excited",
"intensity": 9,
"description": "看到喜欢的人发来消息,心跳加速",
"checkinDate": "2025-08-21",
"metadata": {
"tags": ["感情", "甜蜜"],
"trigger": "收到心仪对象的消息"
}
}
### 3. 获取今日心情打卡
GET http://localhost:3000/mood-checkins/daily
Authorization: Bearer YOUR_JWT_TOKEN
### 4. 获取指定日期心情打卡
GET http://localhost:3000/mood-checkins/daily?date=2025-08-21
Authorization: Bearer YOUR_JWT_TOKEN
### 5. 获取心情打卡历史
GET http://localhost:3000/mood-checkins/history?startDate=2025-08-01&endDate=2025-08-31
Authorization: Bearer YOUR_JWT_TOKEN
### 6. 获取心情打卡历史(按类型过滤)
GET http://localhost:3000/mood-checkins/history?startDate=2025-08-01&endDate=2025-08-31&moodType=happy
Authorization: Bearer YOUR_JWT_TOKEN
### 7. 获取心情统计数据
GET http://localhost:3000/mood-checkins/statistics?startDate=2025-08-01&endDate=2025-08-31
Authorization: Bearer YOUR_JWT_TOKEN
### 8. 更新心情打卡
PUT http://localhost:3000/mood-checkins
Authorization: Bearer YOUR_JWT_TOKEN
Content-Type: application/json
{
"id": "MOOD_CHECKIN_ID",
"moodType": "thrilled",
"intensity": 9,
"description": "更新后的心情描述 - 非常兴奋!",
"metadata": {
"tags": ["更新", "兴奋"],
"trigger": "收到好消息"
}
}
### 9. 删除心情打卡
DELETE http://localhost:3000/mood-checkins
Authorization: Bearer YOUR_JWT_TOKEN
Content-Type: application/json
{
"id": "MOOD_CHECKIN_ID"
}