feat: 新增连续记账天数统计与热力图展示功能

This commit is contained in:
2026-01-28 10:33:19 +08:00
parent 0ee7c13ed8
commit e0cd9028e7
5 changed files with 556 additions and 4 deletions

View File

@@ -0,0 +1,96 @@
/**
* Streak Service - API calls for user streak management
* 连续记账功能服务
*/
import api from './api';
import type { ApiResponse } from '../types';
/**
* Streak info returned from backend
*/
export interface StreakInfo {
current_streak: number; // 当前连续天数
longest_streak: number; // 最长连续记录
total_record_days: number; // 累计记账天数
has_record_today: boolean; // 今天是否已记账
message: string; // 提示信息
}
/**
* Daily contribution data
*/
export interface DailyContribution {
date: string;
count: number;
}
/**
* Streak info in camelCase for frontend
*/
export interface StreakInfoFormatted {
currentStreak: number;
longestStreak: number;
totalRecordDays: number;
hasRecordToday: boolean;
message: string;
}
/**
* Map streak data from API (snake_case) to frontend (camelCase)
*/
function mapStreakFromApi(data: StreakInfo): StreakInfoFormatted {
return {
currentStreak: data.current_streak,
longestStreak: data.longest_streak,
totalRecordDays: data.total_record_days,
hasRecordToday: data.has_record_today,
message: data.message,
};
}
/**
* Get current user's streak info
* 获取当前用户的连续记账信息
*/
export async function getStreakInfo(): Promise<StreakInfoFormatted> {
const response = await api.get<ApiResponse<StreakInfo>>('/user/streak');
if (!response.data) {
// Return default values if no streak data
return {
currentStreak: 0,
longestStreak: 0,
totalRecordDays: 0,
hasRecordToday: false,
message: '开始记录你的第一笔账吧!',
};
}
return mapStreakFromApi(response.data);
}
/**
* Recalculate streak from transaction history
* 重新计算连续记账天数(基于交易历史)
*/
export async function recalculateStreak(): Promise<StreakInfoFormatted> {
const response = await api.post<ApiResponse<StreakInfo>>('/user/streak/recalculate');
if (!response.data) {
throw new Error(response.error || 'Failed to recalculate streak');
}
return mapStreakFromApi(response.data);
}
/**
* Get daily contribution data for heatmap
* 获取热力图数据
*/
export async function getContributionData(): Promise<DailyContribution[]> {
const response = await api.get<ApiResponse<DailyContribution[]>>('/user/streak/contribution');
return response.data || [];
}
export default {
getStreakInfo,
recalculateStreak,
getContributionData,
};