增加求片功能

This commit is contained in:
mtvpls
2026-01-12 18:13:14 +08:00
parent 1053f2968e
commit 83b3315fba
9 changed files with 1049 additions and 20 deletions
+33 -1
View File
@@ -117,6 +117,16 @@ export interface IStorage {
// 收藏更新检查相关
getLastFavoriteCheckTime(userName: string): Promise<number>;
setLastFavoriteCheckTime(userName: string, timestamp: number): Promise<void>;
// 求片相关
getAllMovieRequests(): Promise<MovieRequest[]>;
getMovieRequest(requestId: string): Promise<MovieRequest | null>;
createMovieRequest(request: MovieRequest): Promise<void>;
updateMovieRequest(requestId: string, updates: Partial<MovieRequest>): Promise<void>;
deleteMovieRequest(requestId: string): Promise<void>;
getUserMovieRequests(userName: string): Promise<string[]>;
addUserMovieRequest(userName: string, requestId: string): Promise<void>;
removeUserMovieRequest(userName: string, requestId: string): Promise<void>;
}
// 搜索结果数据结构
@@ -195,7 +205,9 @@ export interface EpisodeFilterConfig {
export type NotificationType =
| 'favorite_update' // 收藏更新
| 'system' // 系统通知
| 'announcement'; // 公告
| 'announcement' // 公告
| 'movie_request' // 新求片通知(给管理员)
| 'request_fulfilled'; // 求片已上架通知(给求片用户)
// 通知数据结构
export interface Notification {
@@ -219,3 +231,23 @@ export interface FavoriteUpdateCheck {
new_episodes: number;
}>;
}
// 求片请求数据结构
export interface MovieRequest {
id: string;
tmdbId?: number;
title: string;
year?: string;
mediaType: 'movie' | 'tv';
season?: number; // 季度(仅剧集)
poster?: string;
overview?: string;
requestedBy: string[];
requestCount: number;
status: 'pending' | 'fulfilled';
createdAt: number;
updatedAt: number;
fulfilledAt?: number;
fulfilledSource?: string;
fulfilledId?: string;
}
+48
View File
@@ -1260,6 +1260,54 @@ export class UpstashRedisStorage implements IStorage {
this.client.set(this.lastFavoriteCheckKey(userName), timestamp)
);
}
// ---------- 求片相关 ----------
private movieRequestsKey() {
return 'movie_requests:all';
}
private userMovieRequestsKey(userName: string) {
return `u:${userName}:mr`;
}
async getAllMovieRequests(): Promise<import('./types').MovieRequest[]> {
const data = await withRetry(() => this.client.hgetall(this.movieRequestsKey()));
if (!data) return [];
return Object.values(data) as import('./types').MovieRequest[];
}
async getMovieRequest(requestId: string): Promise<import('./types').MovieRequest | null> {
const val = await withRetry(() => this.client.hget(this.movieRequestsKey(), requestId));
return val ? (val as import('./types').MovieRequest) : null;
}
async createMovieRequest(request: import('./types').MovieRequest): Promise<void> {
await withRetry(() => this.client.hset(this.movieRequestsKey(), { [request.id]: request }));
}
async updateMovieRequest(requestId: string, updates: Partial<import('./types').MovieRequest>): Promise<void> {
const existing = await this.getMovieRequest(requestId);
if (!existing) throw new Error('Movie request not found');
const updated = { ...existing, ...updates };
await withRetry(() => this.client.hset(this.movieRequestsKey(), { [requestId]: updated }));
}
async deleteMovieRequest(requestId: string): Promise<void> {
await withRetry(() => this.client.hdel(this.movieRequestsKey(), requestId));
}
async getUserMovieRequests(userName: string): Promise<string[]> {
const val = await withRetry(() => this.client.smembers(this.userMovieRequestsKey(userName)));
return val ? ensureStringArray(val) : [];
}
async addUserMovieRequest(userName: string, requestId: string): Promise<void> {
await withRetry(() => this.client.sadd(this.userMovieRequestsKey(userName), requestId));
}
async removeUserMovieRequest(userName: string, requestId: string): Promise<void> {
await withRetry(() => this.client.srem(this.userMovieRequestsKey(userName), requestId));
}
}
// 单例 Upstash Redis 客户端