新增AI教练模块,包括控制器、服务、模型及数据传输对象,更新应用模块以引入新模块,同时在打卡模块中添加按时间范围返回每日打卡状态的功能

This commit is contained in:
richarjiang
2025-08-14 09:12:44 +08:00
parent 866143d3ad
commit d1a6e3d42e
15 changed files with 556 additions and 5 deletions

View File

@@ -1,7 +1,7 @@
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 { CreateCheckinDto, UpdateCheckinDto, CompleteCheckinDto, RemoveCheckinDto, CheckinResponseDto, GetDailyStatusRangeQueryDto, DailyStatusItem } from './dto/checkin.dto';
import { ResponseCode } from '../base.dto';
import * as dayjs from 'dayjs';
import { Op } from 'sequelize';
@@ -115,6 +115,62 @@ export class CheckinsService {
return { code: ResponseCode.SUCCESS, message: 'success', data: rows.map(r => r.toJSON()) };
}
// 按时间范围返回每天是否打卡(任一记录满足视为已打卡)
async getDailyStatusRange(userId: string, query: GetDailyStatusRangeQueryDto): Promise<CheckinResponseDto<DailyStatusItem[]>> {
const start = dayjs(query.startDate, 'YYYY-MM-DD');
const end = dayjs(query.endDate, 'YYYY-MM-DD');
if (!start.isValid() || !end.isValid() || end.isBefore(start)) {
return { code: ResponseCode.ERROR, message: '无效日期范围', data: [] };
}
// 查询范围内所有打卡覆盖checkinDate与时间戳
const startTs = start.startOf('day').toDate();
const endTs = end.endOf('day').toDate();
const rows = await this.checkinModel.findAll({
where: {
userId,
[Op.or]: [
{
checkinDate: {
[Op.between]: [start.format('YYYY-MM-DD') as any, end.format('YYYY-MM-DD') as any],
} as any,
},
{
[Op.or]: [
{ startedAt: { [Op.between]: [startTs, endTs] } },
{ completedAt: { [Op.between]: [startTs, endTs] } },
],
},
],
},
attributes: ['checkinDate', 'startedAt', 'completedAt'],
});
const set = new Set<string>();
for (const r of rows) {
if (r.checkinDate) {
set.add(r.checkinDate);
}
if (r.startedAt) {
set.add(dayjs(r.startedAt).format('YYYY-MM-DD'));
}
if (r.completedAt) {
set.add(dayjs(r.completedAt).format('YYYY-MM-DD'));
}
}
const result: DailyStatusItem[] = [];
let cur = start.clone();
while (!cur.isAfter(end)) {
const d = cur.format('YYYY-MM-DD');
result.push({ date: d, checkedIn: set.has(d) });
cur = cur.add(1, 'day');
}
return { code: ResponseCode.SUCCESS, message: 'success', data: result };
}
}