Initial commit: Flutter 无书应用项目
This commit is contained in:
350
lib/utils/http/poetry_api.dart
Normal file
350
lib/utils/http/poetry_api.dart
Normal file
@@ -0,0 +1,350 @@
|
||||
/// 时间: 2026-03-22
|
||||
/// 功能: 诗词API服务
|
||||
/// 介绍: 专门处理诗词相关的API请求,包括获取诗词、点赞、搜索等功能
|
||||
/// 最新变化: 新增 search.php 搜索接口(与 API_DOCUMENTATION.md 一致)
|
||||
|
||||
import 'http_client.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class PoetryApi {
|
||||
static const String _endpoint = 'pms.php';
|
||||
/// 全文搜索(见 lib/services/API_DOCUMENTATION.md 第二节)
|
||||
static const String _searchEndpoint = 'searchs.php';
|
||||
|
||||
/// 获取随机诗词
|
||||
static Future<PoetryResponse> getRandomPoetry({
|
||||
String? dynasty,
|
||||
String? tag,
|
||||
}) async {
|
||||
final queryParams = <String, dynamic>{};
|
||||
|
||||
if (dynasty != null && dynasty.isNotEmpty) {
|
||||
queryParams['dyn'] = dynasty;
|
||||
}
|
||||
|
||||
if (tag != null && tag.isNotEmpty) {
|
||||
queryParams['tag'] = tag;
|
||||
}
|
||||
|
||||
final response = await HttpClient.get(_endpoint, queryParameters: queryParams);
|
||||
|
||||
if (!response.isSuccess) {
|
||||
throw HttpException('获取诗词失败1: ${response.message}');
|
||||
}
|
||||
|
||||
final jsonData = response.jsonData;
|
||||
if (jsonData['code'] != 0) {
|
||||
throw HttpException(jsonData['msg'] ?? '获取诗词失败2');
|
||||
}
|
||||
|
||||
return PoetryResponse.fromJson(jsonData);
|
||||
}
|
||||
|
||||
/// 获取指定ID的诗词
|
||||
static Future<PoetryResponse> getPoetryById(int id) async {
|
||||
final response = await HttpClient.get(
|
||||
_endpoint,
|
||||
queryParameters: {'id': id.toString()},
|
||||
);
|
||||
|
||||
if (!response.isSuccess) {
|
||||
throw HttpException('获取诗词失败3: ${response.message}');
|
||||
}
|
||||
|
||||
final jsonData = response.jsonData;
|
||||
if (jsonData['code'] != 0) {
|
||||
throw HttpException(jsonData['msg'] ?? '获取诗词失败4');
|
||||
}
|
||||
|
||||
return PoetryResponse.fromJson(jsonData);
|
||||
}
|
||||
|
||||
/// 点赞或取消点赞诗词
|
||||
static Future<PoetryResponse> toggleLike(int id) async {
|
||||
final response = await HttpClient.get(
|
||||
_endpoint,
|
||||
queryParameters: {
|
||||
'id': id.toString(),
|
||||
'like': '', // 无值参数
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.isSuccess) {
|
||||
throw HttpException('点赞失败: ${response.message}');
|
||||
}
|
||||
|
||||
final jsonData = response.jsonData;
|
||||
if (jsonData['code'] != 0) {
|
||||
throw HttpException(jsonData['msg'] ?? '点赞失败');
|
||||
}
|
||||
|
||||
return PoetryResponse.fromJson(jsonData);
|
||||
}
|
||||
|
||||
/// 直接点赞指定诗词(通过lid参数)
|
||||
static Future<PoetryResponse> likePoetry(int lid) async {
|
||||
final response = await HttpClient.get(
|
||||
_endpoint,
|
||||
queryParameters: {'lid': lid.toString()},
|
||||
);
|
||||
|
||||
if (!response.isSuccess) {
|
||||
throw HttpException('点赞失败: ${response.message}');
|
||||
}
|
||||
|
||||
final jsonData = response.jsonData;
|
||||
if (jsonData['code'] != 0) {
|
||||
throw HttpException(jsonData['msg'] ?? '点赞失败');
|
||||
}
|
||||
|
||||
return PoetryResponse.fromJson(jsonData);
|
||||
}
|
||||
|
||||
/// 按朝代获取诗词
|
||||
static Future<PoetryResponse> getPoetryByDynasty(String dynasty) async {
|
||||
return getRandomPoetry(dynasty: dynasty);
|
||||
}
|
||||
|
||||
/// 按标签获取诗词
|
||||
static Future<PoetryResponse> getPoetryByTag(String tag) async {
|
||||
return getRandomPoetry(tag: tag);
|
||||
}
|
||||
|
||||
/// 按朝代和标签获取诗词
|
||||
static Future<PoetryResponse> getPoetryByDynastyAndTag(String dynasty, String tag) async {
|
||||
return getRandomPoetry(dynasty: dynasty, tag: tag);
|
||||
}
|
||||
|
||||
/// 诗词搜索(GET `searchs.php`:keyword、fields、page、size)
|
||||
static Future<SearchPoetryResult> searchPoetry({
|
||||
required String q,
|
||||
String field = '',
|
||||
int page = 1,
|
||||
int limit = 20,
|
||||
}) async {
|
||||
final keyword = q.trim();
|
||||
|
||||
final response = await HttpClient.get(
|
||||
_searchEndpoint,
|
||||
queryParameters: {
|
||||
'keyword': keyword,
|
||||
if (field.isNotEmpty) 'fields': field,
|
||||
'page': page.toString(),
|
||||
'size': limit.toString(),
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.isSuccess) {
|
||||
throw HttpException('搜索失败: ${response.message}');
|
||||
}
|
||||
|
||||
final jsonData = response.jsonData;
|
||||
if (jsonData['code'] != 0) {
|
||||
throw HttpException(jsonData['msg']?.toString() ?? '搜索失败');
|
||||
}
|
||||
|
||||
final raw = jsonData['data'];
|
||||
if (raw is! Map<String, dynamic>) {
|
||||
return SearchPoetryResult(
|
||||
total: 0,
|
||||
page: page,
|
||||
limit: limit,
|
||||
list: const [],
|
||||
);
|
||||
}
|
||||
|
||||
final listRaw = raw['results'];
|
||||
final list = <PoetryData>[];
|
||||
if (listRaw is List) {
|
||||
for (final item in listRaw) {
|
||||
if (item is Map<String, dynamic>) {
|
||||
list.add(PoetryData.fromJson(item));
|
||||
} else if (item is Map) {
|
||||
list.add(PoetryData.fromJson(Map<String, dynamic>.from(item)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 从 pagination 中获取分页信息
|
||||
final pagination = raw['pagination'] as Map<String, dynamic>? ?? {};
|
||||
final totalCount = int.tryParse(pagination['total_count']?.toString() ?? '0') ?? 0;
|
||||
final currentPage = int.tryParse(pagination['current_page']?.toString() ?? '$page') ?? page;
|
||||
final pageSize = int.tryParse(pagination['page_size']?.toString() ?? '$limit') ?? limit;
|
||||
|
||||
return SearchPoetryResult(
|
||||
total: totalCount,
|
||||
page: currentPage,
|
||||
limit: pageSize,
|
||||
list: list,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 搜索结果分页数据(对应 searchs.php 返回 data)
|
||||
class SearchPoetryResult {
|
||||
final int total;
|
||||
final int page;
|
||||
final int limit;
|
||||
final List<PoetryData> list;
|
||||
|
||||
const SearchPoetryResult({
|
||||
required this.total,
|
||||
required this.page,
|
||||
required this.limit,
|
||||
required this.list,
|
||||
});
|
||||
|
||||
bool get hasNextPage => page * limit < total;
|
||||
}
|
||||
|
||||
/// 诗词响应数据模型
|
||||
class PoetryResponse {
|
||||
final int code;
|
||||
final String message;
|
||||
final PoetryData? data;
|
||||
|
||||
PoetryResponse({
|
||||
required this.code,
|
||||
required this.message,
|
||||
this.data,
|
||||
});
|
||||
|
||||
factory PoetryResponse.fromJson(Map<String, dynamic> json) {
|
||||
return PoetryResponse(
|
||||
code: json['code'] ?? 0,
|
||||
message: json['msg'] ?? '',
|
||||
data: json['data'] != null ? PoetryData.fromJson(json['data']) : null,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'code': code,
|
||||
'msg': message,
|
||||
'data': data?.toJson(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// 诗词数据模型
|
||||
class PoetryData {
|
||||
final int id;
|
||||
final String name;// 精选诗句
|
||||
final String alias; // 朝代
|
||||
final String keywords; // 标签
|
||||
final String introduce; // 译文/介绍
|
||||
final String drtime; // 原文
|
||||
final int like; // 点赞数
|
||||
final String url; // 诗人和<标题>
|
||||
final int tui; // 是否推荐
|
||||
final int star; // 星级
|
||||
final int hitsTotal; // 总浏览数
|
||||
final int hitsMonth; // 月浏览数
|
||||
final int hitsDay; // 日浏览数
|
||||
final String date; // 最后统计日期
|
||||
final String datem; // 最后统计月份
|
||||
final String time; // 收录时间
|
||||
final String createTime; // 创建时间
|
||||
final String updateTime; // 更新时间
|
||||
|
||||
PoetryData({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.alias,
|
||||
required this.keywords,
|
||||
required this.introduce,
|
||||
required this.drtime,
|
||||
required this.like,
|
||||
required this.url,
|
||||
required this.tui,
|
||||
required this.star,
|
||||
required this.hitsTotal,
|
||||
required this.hitsMonth,
|
||||
required this.hitsDay,
|
||||
required this.date,
|
||||
required this.datem,
|
||||
required this.time,
|
||||
required this.createTime,
|
||||
required this.updateTime,
|
||||
});
|
||||
|
||||
factory PoetryData.fromJson(Map<String, dynamic> json) {
|
||||
// 添加调试信息
|
||||
if (kDebugMode) {
|
||||
print('PoetryData.fromJson: 输入JSON = $json');
|
||||
}
|
||||
|
||||
try {
|
||||
final poetryData = PoetryData(
|
||||
id: int.tryParse(json['id'].toString()) ?? 0,
|
||||
name: json['name']?.toString() ?? '',
|
||||
alias: json['alias']?.toString() ?? '',
|
||||
keywords: json['keywords']?.toString() ?? '',
|
||||
introduce: json['introduce']?.toString() ?? '',
|
||||
drtime: json['drtime']?.toString() ?? '',
|
||||
like: int.tryParse(json['like'].toString()) ?? 0,
|
||||
url: json['url']?.toString() ?? '',
|
||||
tui: int.tryParse(json['tui'].toString()) ?? 0,
|
||||
star: int.tryParse(json['star'].toString()) ?? 0,
|
||||
hitsTotal: int.tryParse(json['hits_total'].toString()) ?? 0,
|
||||
hitsMonth: int.tryParse(json['hits_month'].toString()) ?? 0,
|
||||
hitsDay: int.tryParse(json['hits_day'].toString()) ?? 0,
|
||||
date: json['date']?.toString() ?? '',
|
||||
datem: json['datem']?.toString() ?? '',
|
||||
time: json['time']?.toString() ?? '',
|
||||
createTime: json['create_time']?.toString() ?? '',
|
||||
updateTime: json['update_time']?.toString() ?? '',
|
||||
);
|
||||
|
||||
if (kDebugMode) {
|
||||
print('PoetryData.fromJson: 解析成功');
|
||||
}
|
||||
|
||||
return poetryData;
|
||||
} catch (e) {
|
||||
if (kDebugMode) {
|
||||
print('PoetryData.fromJson: 解析失败 - $e');
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'name': name,
|
||||
'alias': alias,
|
||||
'keywords': keywords,
|
||||
'introduce': introduce,
|
||||
'drtime': drtime,
|
||||
'like': like,
|
||||
'url': url,
|
||||
'tui': tui,
|
||||
'star': star,
|
||||
'hits_total': hitsTotal,
|
||||
'hits_month': hitsMonth,
|
||||
'hits_day': hitsDay,
|
||||
'date': date,
|
||||
'datem': datem,
|
||||
'time': time,
|
||||
'create_time': createTime,
|
||||
'update_time': updateTime,
|
||||
};
|
||||
}
|
||||
|
||||
/// 获取标签列表
|
||||
List<String> get keywordList {
|
||||
if (keywords.isEmpty) return [];
|
||||
return keywords.split(',').map((k) => k.trim()).where((k) => k.isNotEmpty).toList();
|
||||
}
|
||||
|
||||
/// 生成星级显示
|
||||
String get starDisplay {
|
||||
if (star <= 0) return '';
|
||||
if (star >= 5) return '🌟';
|
||||
return '⭐';
|
||||
}
|
||||
|
||||
/// 是否为推荐内容
|
||||
bool get isRecommended => tui == 1;
|
||||
}
|
||||
Reference in New Issue
Block a user