feat(water-records): 新增喝水记录功能模块

新增完整的喝水记录管理功能,支持用户记录每日喝水情况、设置目标和查看统计信息。功能包括:

- 创建、查询、更新和删除喝水记录
- 设置和管理每日喝水目标
- 获取今日喝水统计和完成率分析
- 支持分页查询和日期范围筛选
- 完整的数据验证和错误处理机制

该模块已从用户模块中独立出来,提供REST API接口,包含数据库迁移脚本和详细文档。
This commit is contained in:
richarjiang
2025-09-01 11:02:13 +08:00
parent 0488fe62a1
commit 2c2e964199
13 changed files with 1131 additions and 0 deletions

View File

@@ -0,0 +1,272 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNumber, IsOptional, IsEnum, Min, Max, IsString, MaxLength, IsDateString } from 'class-validator';
import { Transform } from 'class-transformer';
import { WaterRecordSource } from '../models/user-water-history.model';
/**
* 创建喝水记录请求DTO
*/
export class CreateWaterRecordDto {
@ApiProperty({
description: '喝水量(毫升)',
example: 250,
minimum: 1,
maximum: 5000,
})
@IsNumber({}, { message: '喝水量必须是数字' })
@Min(1, { message: '喝水量不能小于1毫升' })
@Max(5000, { message: '喝水量不能大于5000毫升' })
amount: number;
@ApiProperty({
description: '记录时间',
example: '2023-12-01T10:00:00.000Z',
required: false,
})
@IsOptional()
@IsDateString({}, { message: '记录时间格式不正确' })
recordedAt?: Date;
@ApiProperty({
description: '记录来源',
enum: WaterRecordSource,
example: WaterRecordSource.Manual,
required: false,
})
@IsOptional()
@IsEnum(WaterRecordSource, { message: '记录来源必须是有效值' })
source?: WaterRecordSource;
@ApiProperty({
description: '备注',
example: '早晨第一杯水',
required: false,
maxLength: 100,
})
@IsOptional()
@IsString({ message: '备注必须是字符串' })
@MaxLength(100, { message: '备注长度不能超过100个字符' })
note?: string;
}
/**
* 更新喝水记录请求DTO
*/
export class UpdateWaterRecordDto {
@ApiProperty({
description: '喝水量(毫升)',
example: 250,
minimum: 1,
maximum: 5000,
required: false,
})
@IsOptional()
@IsNumber({}, { message: '喝水量必须是数字' })
@Min(1, { message: '喝水量不能小于1毫升' })
@Max(5000, { message: '喝水量不能大于5000毫升' })
amount?: number;
@ApiProperty({
description: '记录时间',
example: '2023-12-01T10:00:00.000Z',
required: false,
})
@IsOptional()
@IsDateString({}, { message: '记录时间格式不正确' })
recordedAt?: Date;
@ApiProperty({
description: '记录来源',
enum: WaterRecordSource,
example: WaterRecordSource.Manual,
required: false,
})
@IsOptional()
@IsEnum(WaterRecordSource, { message: '记录来源必须是有效值' })
source?: WaterRecordSource;
@ApiProperty({
description: '备注',
example: '早晨第一杯水',
required: false,
maxLength: 100,
})
@IsOptional()
@IsString({ message: '备注必须是字符串' })
@MaxLength(100, { message: '备注长度不能超过100个字符' })
note?: string;
}
/**
* 获取喝水记录查询DTO
*/
export class GetWaterRecordsQueryDto {
@ApiProperty({
description: '开始日期 (YYYY-MM-DD)',
example: '2023-12-01',
required: false,
})
@IsOptional()
@IsString()
startDate?: string;
@ApiProperty({
description: '结束日期 (YYYY-MM-DD)',
example: '2023-12-31',
required: false,
})
@IsOptional()
@IsString()
endDate?: string;
@ApiProperty({
description: '页码默认1',
example: 1,
required: false,
})
@IsOptional()
@Transform(({ value }) => parseInt(value))
@IsNumber({}, { message: '页码必须是数字' })
@Min(1, { message: '页码不能小于1' })
page?: number;
@ApiProperty({
description: '每页数量默认20',
example: 20,
required: false,
})
@IsOptional()
@Transform(({ value }) => parseInt(value))
@IsNumber({}, { message: '每页数量必须是数字' })
@Min(1, { message: '每页数量不能小于1' })
@Max(100, { message: '每页数量不能大于100' })
limit?: number;
}
/**
* 更新喝水目标请求DTO
*/
export class UpdateWaterGoalDto {
@ApiProperty({
description: '每日喝水目标(毫升)',
example: 2000,
minimum: 500,
maximum: 10000,
})
@IsNumber({}, { message: '喝水目标必须是数字' })
@Min(500, { message: '喝水目标不能小于500毫升' })
@Max(10000, { message: '喝水目标不能大于10000毫升' })
dailyWaterGoal: number;
}
/**
* 基础响应DTO
*/
export class BaseResponseDto {
@ApiProperty({ description: '是否成功', example: true })
success: boolean;
@ApiProperty({ description: '响应消息', example: '操作成功' })
message: string;
}
/**
* 喝水记录数据DTO
*/
export class WaterRecordDataDto {
@ApiProperty({ description: '记录ID', example: 1 })
id: number;
@ApiProperty({ description: '喝水量(毫升)', example: 250 })
amount: number;
@ApiProperty({ description: '记录时间', example: '2023-12-01T10:00:00.000Z' })
recordedAt: Date;
@ApiProperty({ description: '备注', example: '早晨第一杯水', nullable: true })
note: string | null;
@ApiProperty({ description: '创建时间', example: '2023-12-01T10:00:00.000Z' })
createdAt: Date;
@ApiProperty({ description: '更新时间', example: '2023-12-01T10:00:00.000Z' })
updatedAt: Date;
}
/**
* 喝水记录响应DTO
*/
export class WaterRecordResponseDto extends BaseResponseDto {
@ApiProperty({ description: '喝水记录数据', type: WaterRecordDataDto })
data: WaterRecordDataDto;
}
/**
* 分页信息DTO
*/
export class PaginationDto {
@ApiProperty({ description: '当前页码', example: 1 })
page: number;
@ApiProperty({ description: '每页数量', example: 20 })
limit: number;
@ApiProperty({ description: '总记录数', example: 100 })
total: number;
@ApiProperty({ description: '总页数', example: 5 })
totalPages: number;
}
/**
* 喝水记录列表响应DTO
*/
export class WaterRecordsListResponseDto extends BaseResponseDto {
data: {
records: WaterRecordDataDto[];
pagination: PaginationDto;
};
}
/**
* 喝水目标响应DTO
*/
export class WaterGoalResponseDto extends BaseResponseDto {
@ApiProperty({
description: '喝水目标数据',
example: { dailyWaterGoal: 2000 },
})
data: {
dailyWaterGoal: number;
};
}
/**
* 今日喝水统计响应DTO
*/
export class TodayWaterStatsResponseDto extends BaseResponseDto {
@ApiProperty({
description: '今日喝水统计',
example: {
date: '2023-12-01',
totalAmount: 1500,
dailyGoal: 2000,
completionRate: 75.0,
recordCount: 6,
records: [],
},
})
data: {
date: string;
totalAmount: number;
dailyGoal: number;
completionRate: number;
recordCount: number;
records: {
id: number;
amount: number;
recordedAt: Date;
note: string | null;
}[];
};
}