feat(Materials): add new materials navigation and href functions
- Introduced href functions for accessing materials, including hrefMaterials, hrefMaterialSubject, and hrefMaterialDetail. - Updated AppsScreen to include a new entry for materials, enhancing the app's educational resources section. - Exported material types in index.ts to support the new materials functionality.
This commit is contained in:
289
src/data/repositories/MaterialRepository.ts
Normal file
289
src/data/repositories/MaterialRepository.ts
Normal file
@@ -0,0 +1,289 @@
|
||||
/**
|
||||
* 学习资料 Repository
|
||||
* 提供学习资料的数据访问接口
|
||||
*/
|
||||
import type {
|
||||
MaterialSubject,
|
||||
MaterialFile,
|
||||
MaterialFileType,
|
||||
} from '../../types/material';
|
||||
import { api } from '../../services/api';
|
||||
|
||||
// ==================== 接口定义 ====================
|
||||
|
||||
export interface GetMaterialsParams {
|
||||
subjectId?: string;
|
||||
fileType?: MaterialFileType;
|
||||
keyword?: string;
|
||||
sortBy?: 'createdAt' | 'downloadCount' | 'title';
|
||||
sortOrder?: 'asc' | 'desc';
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
}
|
||||
|
||||
export interface MaterialsResult {
|
||||
materials: MaterialFile[];
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
hasMore: boolean;
|
||||
}
|
||||
|
||||
// ==================== API 响应类型 ====================
|
||||
|
||||
interface SubjectResponse {
|
||||
id: string;
|
||||
name: string;
|
||||
icon: string;
|
||||
color: string;
|
||||
description: string;
|
||||
sort_order: number;
|
||||
is_active: boolean;
|
||||
material_count: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
interface MaterialFileResponse {
|
||||
id: string;
|
||||
subject_id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
file_type: string;
|
||||
file_size: number;
|
||||
file_url: string;
|
||||
file_name: string;
|
||||
download_count: number;
|
||||
author_id: string;
|
||||
tags: string[];
|
||||
status: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
subject?: SubjectResponse;
|
||||
}
|
||||
|
||||
// ==================== 转换函数 ====================
|
||||
|
||||
function toMaterialSubject(res: SubjectResponse): MaterialSubject {
|
||||
return {
|
||||
id: res.id,
|
||||
name: res.name,
|
||||
icon: res.icon,
|
||||
color: res.color,
|
||||
description: res.description,
|
||||
sort_order: res.sort_order,
|
||||
is_active: res.is_active,
|
||||
material_count: res.material_count,
|
||||
created_at: res.created_at,
|
||||
updated_at: res.updated_at,
|
||||
};
|
||||
}
|
||||
|
||||
function toMaterialFile(res: MaterialFileResponse): MaterialFile {
|
||||
return {
|
||||
id: res.id,
|
||||
subject_id: res.subject_id,
|
||||
title: res.title,
|
||||
description: res.description,
|
||||
file_type: res.file_type as MaterialFileType,
|
||||
file_size: res.file_size,
|
||||
file_url: res.file_url,
|
||||
file_name: res.file_name,
|
||||
download_count: res.download_count,
|
||||
author_id: res.author_id,
|
||||
tags: res.tags || [],
|
||||
status: res.status as 'active' | 'inactive',
|
||||
created_at: res.created_at,
|
||||
updated_at: res.updated_at,
|
||||
subject: res.subject ? toMaterialSubject(res.subject) : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
// ==================== Repository 实现 ====================
|
||||
|
||||
/**
|
||||
* 学习资料数据仓库
|
||||
* 使用真实 API
|
||||
*/
|
||||
export class MaterialRepository {
|
||||
private static instance: MaterialRepository;
|
||||
|
||||
private constructor() {}
|
||||
|
||||
static getInstance(): MaterialRepository {
|
||||
if (!MaterialRepository.instance) {
|
||||
MaterialRepository.instance = new MaterialRepository();
|
||||
}
|
||||
return MaterialRepository.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取学科分类列表
|
||||
*/
|
||||
async getSubjects(): Promise<MaterialSubject[]> {
|
||||
try {
|
||||
const response = await api.get<{ list: SubjectResponse[] }>('/materials/subjects');
|
||||
return response.data.list.map(toMaterialSubject);
|
||||
} catch (error) {
|
||||
console.error('获取学科列表失败:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取学科详情
|
||||
*/
|
||||
async getSubjectById(id: string): Promise<MaterialSubject | null> {
|
||||
try {
|
||||
const response = await api.get<SubjectResponse>(`/materials/subjects/${id}`);
|
||||
return toMaterialSubject(response.data);
|
||||
} catch (error) {
|
||||
console.error('获取学科详情失败:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取资料列表
|
||||
*/
|
||||
async getMaterials(params: GetMaterialsParams): Promise<MaterialsResult> {
|
||||
const {
|
||||
subjectId,
|
||||
fileType,
|
||||
keyword,
|
||||
sortBy = 'createdAt',
|
||||
sortOrder = 'desc',
|
||||
page = 1,
|
||||
pageSize = 20,
|
||||
} = params;
|
||||
|
||||
try {
|
||||
const queryParams: Record<string, any> = {
|
||||
page,
|
||||
page_size: pageSize,
|
||||
};
|
||||
|
||||
if (subjectId) {
|
||||
queryParams.subject_id = subjectId;
|
||||
}
|
||||
if (fileType) {
|
||||
queryParams.file_type = fileType;
|
||||
}
|
||||
if (keyword) {
|
||||
queryParams.keyword = keyword;
|
||||
}
|
||||
|
||||
const response = await api.get<{
|
||||
list: MaterialFileResponse[];
|
||||
total: number;
|
||||
page: number;
|
||||
page_size: number;
|
||||
has_more: boolean;
|
||||
}>('/materials', queryParams);
|
||||
|
||||
return {
|
||||
materials: response.data.list.map(toMaterialFile),
|
||||
total: response.data.total,
|
||||
page: response.data.page,
|
||||
pageSize: response.data.page_size,
|
||||
hasMore: response.data.has_more,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('获取资料列表失败:', error);
|
||||
return {
|
||||
materials: [],
|
||||
total: 0,
|
||||
page: 1,
|
||||
pageSize,
|
||||
hasMore: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取资料详情
|
||||
*/
|
||||
async getMaterialById(id: string): Promise<MaterialFile | null> {
|
||||
try {
|
||||
const response = await api.get<MaterialFileResponse>(`/materials/${id}`);
|
||||
return toMaterialFile(response.data);
|
||||
} catch (error) {
|
||||
console.error('获取资料详情失败:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索资料
|
||||
*/
|
||||
async searchMaterials(keyword: string, params?: { fileType?: MaterialFileType }): Promise<MaterialsResult> {
|
||||
return this.getMaterials({
|
||||
keyword,
|
||||
fileType: params?.fileType,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取热门资料
|
||||
*/
|
||||
async getHotMaterials(limit: number = 10): Promise<MaterialFile[]> {
|
||||
try {
|
||||
const response = await api.get<{
|
||||
list: MaterialFileResponse[];
|
||||
total: number;
|
||||
page: number;
|
||||
page_size: number;
|
||||
has_more: boolean;
|
||||
}>('/materials', {
|
||||
page: 1,
|
||||
page_size: limit,
|
||||
sort_by: 'download_count',
|
||||
sort_order: 'desc',
|
||||
});
|
||||
return response.data.list.map(toMaterialFile);
|
||||
} catch (error) {
|
||||
console.error('获取热门资料失败:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最新资料
|
||||
*/
|
||||
async getLatestMaterials(limit: number = 10): Promise<MaterialFile[]> {
|
||||
try {
|
||||
const response = await api.get<{
|
||||
list: MaterialFileResponse[];
|
||||
total: number;
|
||||
page: number;
|
||||
page_size: number;
|
||||
has_more: boolean;
|
||||
}>('/materials', {
|
||||
page: 1,
|
||||
page_size: limit,
|
||||
sort_by: 'created_at',
|
||||
sort_order: 'desc',
|
||||
});
|
||||
return response.data.list.map(toMaterialFile);
|
||||
} catch (error) {
|
||||
console.error('获取最新资料失败:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取资料的下载链接(增加下载次数)
|
||||
*/
|
||||
async downloadMaterial(id: string): Promise<{ file_url: string; file_name: string } | null> {
|
||||
try {
|
||||
const response = await api.get<{ file_url: string; file_name: string }>(`/materials/${id}/download`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('获取下载链接失败:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 导出单例
|
||||
export const materialRepository = MaterialRepository.getInstance();
|
||||
Reference in New Issue
Block a user