新增打卡模块,包括打卡控制器、服务、模型及数据传输对象,更新应用模块以引入新模块
This commit is contained in:
120
src/checkins/checkins.service.ts
Normal file
120
src/checkins/checkins.service.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import { Injectable, NotFoundException, Logger, ForbiddenException } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/sequelize';
|
||||
import { Checkin, CheckinStatus } from './models/checkin.model';
|
||||
import { CreateCheckinDto, UpdateCheckinDto, CompleteCheckinDto, RemoveCheckinDto, CheckinResponseDto } from './dto/checkin.dto';
|
||||
import { ResponseCode } from '../base.dto';
|
||||
import * as dayjs from 'dayjs';
|
||||
import { Op } from 'sequelize';
|
||||
|
||||
@Injectable()
|
||||
export class CheckinsService {
|
||||
private readonly logger = new Logger(CheckinsService.name);
|
||||
|
||||
constructor(
|
||||
@InjectModel(Checkin)
|
||||
private readonly checkinModel: typeof Checkin,
|
||||
) { }
|
||||
|
||||
async create(dto: CreateCheckinDto): Promise<CheckinResponseDto> {
|
||||
const record = await this.checkinModel.create({
|
||||
userId: dto.userId,
|
||||
workoutId: dto.workoutId || null,
|
||||
planId: dto.planId || null,
|
||||
title: dto.title || null,
|
||||
checkinDate: dto.checkinDate || null,
|
||||
startedAt: dto.startedAt ? new Date(dto.startedAt) : null,
|
||||
notes: dto.notes || null,
|
||||
metrics: dto.metrics || null,
|
||||
status: CheckinStatus.PENDING,
|
||||
});
|
||||
|
||||
return { code: ResponseCode.SUCCESS, message: 'success', data: record.toJSON() };
|
||||
}
|
||||
|
||||
async update(dto: UpdateCheckinDto, userId: string): Promise<CheckinResponseDto> {
|
||||
const record = await this.checkinModel.findByPk(dto.id);
|
||||
if (!record) {
|
||||
throw new NotFoundException('打卡记录不存在');
|
||||
}
|
||||
if (record.userId !== userId) {
|
||||
throw new ForbiddenException('无权操作该打卡记录');
|
||||
}
|
||||
|
||||
if (dto.workoutId !== undefined) record.workoutId = dto.workoutId;
|
||||
if (dto.planId !== undefined) record.planId = dto.planId;
|
||||
if (dto.title !== undefined) record.title = dto.title;
|
||||
if (dto.checkinDate !== undefined) record.checkinDate = dto.checkinDate as any;
|
||||
if (dto.startedAt !== undefined) record.startedAt = dto.startedAt ? new Date(dto.startedAt) : null;
|
||||
if (dto.notes !== undefined) record.notes = dto.notes;
|
||||
if (dto.metrics !== undefined) record.metrics = dto.metrics as any;
|
||||
if (dto.status !== undefined) record.status = dto.status;
|
||||
if (dto.completedAt !== undefined) record.completedAt = dto.completedAt ? new Date(dto.completedAt) : null;
|
||||
if (dto.durationSeconds !== undefined) record.durationSeconds = dto.durationSeconds;
|
||||
|
||||
await record.save();
|
||||
return { code: ResponseCode.SUCCESS, message: 'success', data: record.toJSON() };
|
||||
}
|
||||
|
||||
async complete(dto: CompleteCheckinDto, userId: string): Promise<CheckinResponseDto> {
|
||||
const record = await this.checkinModel.findByPk(dto.id);
|
||||
if (!record) {
|
||||
throw new NotFoundException('打卡记录不存在');
|
||||
}
|
||||
if (record.userId !== userId) {
|
||||
throw new ForbiddenException('无权操作该打卡记录');
|
||||
}
|
||||
|
||||
record.status = CheckinStatus.COMPLETED;
|
||||
record.completedAt = dto.completedAt ? new Date(dto.completedAt) : new Date();
|
||||
if (dto.durationSeconds !== undefined) record.durationSeconds = dto.durationSeconds;
|
||||
if (dto.notes !== undefined) record.notes = dto.notes;
|
||||
if (dto.metrics !== undefined) record.metrics = dto.metrics as any;
|
||||
|
||||
await record.save();
|
||||
return { code: ResponseCode.SUCCESS, message: 'success', data: record.toJSON() };
|
||||
}
|
||||
|
||||
async remove(dto: RemoveCheckinDto, userId: string): Promise<CheckinResponseDto> {
|
||||
const record = await this.checkinModel.findByPk(dto.id);
|
||||
if (!record) {
|
||||
throw new NotFoundException('打卡记录不存在');
|
||||
}
|
||||
if (record.userId !== userId) {
|
||||
throw new ForbiddenException('无权操作该打卡记录');
|
||||
}
|
||||
await record.destroy();
|
||||
return { code: ResponseCode.SUCCESS, message: 'success', data: { id: dto.id } };
|
||||
}
|
||||
|
||||
async getDaily(userId: string, date?: string): Promise<CheckinResponseDto> {
|
||||
const target = date ? dayjs(date) : dayjs();
|
||||
if (!target.isValid()) {
|
||||
return { code: ResponseCode.ERROR, message: '无效日期', data: [] };
|
||||
}
|
||||
const start = target.startOf('day').toDate();
|
||||
const end = target.endOf('day').toDate();
|
||||
|
||||
// 覆盖两类数据:
|
||||
// 1) checkinDate == YYYY-MM-DD(精确日)
|
||||
// 2) startedAt/ completedAt 落在该日范围内(更鲁棒)
|
||||
const rows = await this.checkinModel.findAll({
|
||||
where: {
|
||||
userId,
|
||||
[Op.or]: [
|
||||
{ checkinDate: target.format('YYYY-MM-DD') as any },
|
||||
{
|
||||
[Op.or]: [
|
||||
{ startedAt: { [Op.between]: [start, end] } },
|
||||
{ completedAt: { [Op.between]: [start, end] } },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
order: [['createdAt', 'ASC']],
|
||||
});
|
||||
|
||||
return { code: ResponseCode.SUCCESS, message: 'success', data: rows.map(r => r.toJSON()) };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user