- 实现签到主页面,包含签到按钮、连续天数、今日状态展示 - 实现签到记录页面,包含日历视图和签到历史列表 - 实现个人中心页面,包含用户信息和签到统计 - 后端实现签到、查询状态、查询历史三个接口 - 使用 Supabase 存储签到记录数据 - 采用星空主题设计,深蓝紫渐变背景 + 金色星光强调色 - 完成所有接口测试和前后端匹配验证 - 通过 ESLint 检查和编译验证
149 lines
3.3 KiB
TypeScript
149 lines
3.3 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
import { getSupabaseClient } from '@/storage/database/supabase-client';
|
|
|
|
@Injectable()
|
|
export class SignInService {
|
|
/**
|
|
* 用户签到
|
|
*/
|
|
async signIn(userId: string) {
|
|
const client = getSupabaseClient();
|
|
const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD
|
|
|
|
// 检查今日是否已签到
|
|
const { data: existingRecord } = await client
|
|
.from('sign_in_records')
|
|
.select('*')
|
|
.eq('user_id', userId)
|
|
.eq('sign_date', today)
|
|
.single();
|
|
|
|
if (existingRecord) {
|
|
return {
|
|
code: 400,
|
|
msg: '今日已签到',
|
|
data: null,
|
|
};
|
|
}
|
|
|
|
// 创建签到记录
|
|
const { data, error } = await client
|
|
.from('sign_in_records')
|
|
.insert({
|
|
user_id: userId,
|
|
sign_date: today,
|
|
})
|
|
.select()
|
|
.single();
|
|
|
|
if (error) {
|
|
console.error('签到失败:', error);
|
|
return {
|
|
code: 500,
|
|
msg: '签到失败',
|
|
data: null,
|
|
};
|
|
}
|
|
|
|
return {
|
|
code: 200,
|
|
msg: '签到成功',
|
|
data,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 获取签到状态
|
|
*/
|
|
async getSignInStatus(userId: string) {
|
|
const client = getSupabaseClient();
|
|
const today = new Date().toISOString().split('T')[0];
|
|
|
|
// 检查今日是否已签到
|
|
const { data: todayRecord } = await client
|
|
.from('sign_in_records')
|
|
.select('*')
|
|
.eq('user_id', userId)
|
|
.eq('sign_date', today)
|
|
.single();
|
|
|
|
// 获取所有签到记录
|
|
const { data: allRecords } = await client
|
|
.from('sign_in_records')
|
|
.select('sign_date')
|
|
.eq('user_id', userId)
|
|
.order('sign_date', { ascending: false });
|
|
|
|
const totalDays = allRecords?.length || 0;
|
|
|
|
// 计算连续签到天数
|
|
let continuousDays = 0;
|
|
if (allRecords && allRecords.length > 0) {
|
|
const dates = allRecords.map((r) => r.sign_date).sort().reverse();
|
|
continuousDays = this.calculateContinuousDays(dates, today);
|
|
}
|
|
|
|
return {
|
|
code: 200,
|
|
msg: 'success',
|
|
data: {
|
|
todaySignedIn: !!todayRecord,
|
|
continuousDays,
|
|
totalDays,
|
|
},
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 计算连续签到天数
|
|
*/
|
|
private calculateContinuousDays(dates: string[], today: string): number {
|
|
let continuousDays = 0;
|
|
let checkDate = new Date(today);
|
|
|
|
for (const dateStr of dates) {
|
|
const date = new Date(dateStr);
|
|
const diffTime = checkDate.getTime() - date.getTime();
|
|
const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
|
|
|
|
if (diffDays === 0 || diffDays === 1) {
|
|
continuousDays++;
|
|
checkDate = date;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return continuousDays;
|
|
}
|
|
|
|
/**
|
|
* 获取签到历史记录
|
|
*/
|
|
async getSignInHistory(userId: string, limit: number = 30) {
|
|
const client = getSupabaseClient();
|
|
|
|
const { data, error } = await client
|
|
.from('sign_in_records')
|
|
.select('*')
|
|
.eq('user_id', userId)
|
|
.order('sign_date', { ascending: false })
|
|
.limit(limit);
|
|
|
|
if (error) {
|
|
console.error('获取签到历史失败:', error);
|
|
return {
|
|
code: 500,
|
|
msg: '获取签到历史失败',
|
|
data: null,
|
|
};
|
|
}
|
|
|
|
return {
|
|
code: 200,
|
|
msg: 'success',
|
|
data,
|
|
};
|
|
}
|
|
}
|