feat: 实现微博签到小程序功能
- 实现签到主页面,包含签到按钮、连续天数、今日状态展示 - 实现签到记录页面,包含日历视图和签到历史列表 - 实现个人中心页面,包含用户信息和签到统计 - 后端实现签到、查询状态、查询历史三个接口 - 使用 Supabase 存储签到记录数据 - 采用星空主题设计,深蓝紫渐变背景 + 金色星光强调色 - 完成所有接口测试和前后端匹配验证 - 通过 ESLint 检查和编译验证
This commit is contained in:
148
server/src/signin/signin.service.ts
Normal file
148
server/src/signin/signin.service.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user