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,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|