/// 时间: 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 getRandomPoetry({ String? dynasty, String? tag, }) async { final queryParams = {}; 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 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 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 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 getPoetryByDynasty(String dynasty) async { return getRandomPoetry(dynasty: dynasty); } /// 按标签获取诗词 static Future getPoetryByTag(String tag) async { return getRandomPoetry(tag: tag); } /// 按朝代和标签获取诗词 static Future getPoetryByDynastyAndTag(String dynasty, String tag) async { return getRandomPoetry(dynasty: dynasty, tag: tag); } /// 诗词搜索(GET `searchs.php`:keyword、fields、page、size) static Future 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) { return SearchPoetryResult( total: 0, page: page, limit: limit, list: const [], ); } final listRaw = raw['results']; final list = []; if (listRaw is List) { for (final item in listRaw) { if (item is Map) { list.add(PoetryData.fromJson(item)); } else if (item is Map) { list.add(PoetryData.fromJson(Map.from(item))); } } } // 从 pagination 中获取分页信息 final pagination = raw['pagination'] as Map? ?? {}; 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 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 json) { return PoetryResponse( code: json['code'] ?? 0, message: json['msg'] ?? '', data: json['data'] != null ? PoetryData.fromJson(json['data']) : null, ); } Map 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 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() ?? '', ); return poetryData; } catch (e) { rethrow; } } Map 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 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; }