Initial commit: Flutter 无书应用项目
This commit is contained in:
411
lib/services/API_DOCUMENTATION.md
Normal file
411
lib/services/API_DOCUMENTATION.md
Normal file
@@ -0,0 +1,411 @@
|
||||
# 诗词API接口文档
|
||||
|
||||
## 概述
|
||||
|
||||
本文档描述了诗词应用的所有API接口,包括诗词推荐、搜索、点赞、统计等功能。
|
||||
|
||||
**基础URL**: `https://yy.vogov.cn/api/`
|
||||
|
||||
---
|
||||
|
||||
## 1. 诗词推荐接口 (`/pms.php`)
|
||||
|
||||
### 接口说明
|
||||
提供诗词随机推荐、获取指定站点信息及点赞功能,支持朝代、标签多条件筛选。
|
||||
|
||||
**请求方式**: GET / POST 均可
|
||||
|
||||
**重要**: 只要接口成功返回 `data` 字段,即会自动增加该站点的浏览次数(日、月、总)。
|
||||
|
||||
### 请求参数
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `lid` | int | 否 | 点赞ID。传入时直接对该站点执行点赞/取消点赞,返回更新后的信息,并增加浏览次数统计。此时忽略其他所有参数。 |
|
||||
| `id` | int | 否 | 站点ID。返回该站点的完整信息(不点赞),可与 `like` 组合实现同时点赞。 |
|
||||
| `like` | 标志 | 否 | 无值参数。若存在,则对本次请求返回的站点(通过 `id` 指定或随机推荐)执行点赞/取消点赞。可与 `id`、`dyn`、`tag` 组合。 |
|
||||
| `dyn` | string | 否 | 朝代,多个用英文逗号分隔,例如 `宋代,唐代`。条件为"或",即满足任一朝代。 |
|
||||
| `tag` | string | 否 | 标签,多个用英文逗号分隔,例如 `友情,柳树`。条件为"或",即满足任一标签。 |
|
||||
|
||||
### 优先级说明
|
||||
- `lid` > `id` > 随机推荐。若同时提供 `lid` 和 `like`,`like` 无效(以 `lid` 为准)。
|
||||
- 若不提供任何参数,则从全站随机返回一条。
|
||||
- 若同时提供 `dyn` 和 `tag`,则取交集(必须同时满足朝代和标签条件)。
|
||||
|
||||
### 返回格式
|
||||
|
||||
始终返回 JSON 格式,包含以下字段:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `code` | int | 状态码:0 成功,非0 失败 |
|
||||
| `msg` | string | 提示信息(如"点赞成功"、"取消点赞"等) |
|
||||
| `data` | object | 站点完整信息 |
|
||||
|
||||
### data 对象字段说明
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `id` | int | 站点ID |
|
||||
| `name` | string | 诗词名称/诗句 |
|
||||
| `alias` | string | 朝代 |
|
||||
| `keywords` | string | 标签(多个用逗号分隔) |
|
||||
| `introduce` | string | 译文/介绍 |
|
||||
| `drtime` | string | 原文 |
|
||||
| `like` | int | 点赞数 |
|
||||
| `url` | string | 诗人和<标题> |
|
||||
| `tui` | int | 是否推荐(1是0否) |
|
||||
| `star` | int | 星级 |
|
||||
| `hits_total` | int | 总浏览数 |
|
||||
| `hits_month` | int | 月浏览数 |
|
||||
| `hits_day` | int | 日浏览数 |
|
||||
| `date` | string | 最后统计日期(Y-m-d) |
|
||||
| `datem` | string | 最后统计月份(Y-m) |
|
||||
| `time` | string | 收录时间 |
|
||||
| `create_time` | string | 创建时间 |
|
||||
| `update_time` | string | 更新时间 |
|
||||
| …… | | 其他数据库字段均返回 |
|
||||
|
||||
### 示例请求
|
||||
|
||||
#### 1. 随机推荐(无参数)
|
||||
```
|
||||
GET /pms.php
|
||||
```
|
||||
|
||||
#### 2. 随机推荐并点赞
|
||||
```
|
||||
GET /pms.php?like
|
||||
```
|
||||
|
||||
#### 3. 获取指定站点信息(id=5)
|
||||
```
|
||||
GET /pms.php?id=5
|
||||
```
|
||||
|
||||
#### 4. 获取指定站点信息并点赞(id=5 且 like)
|
||||
```
|
||||
GET /pms.php?id=5&like
|
||||
```
|
||||
|
||||
#### 5. 按朝代筛选(宋代或唐代)并随机推荐
|
||||
```
|
||||
GET /pms.php?dyn=宋代,唐代
|
||||
```
|
||||
|
||||
#### 6. 按朝代筛选并点赞(like 标志)
|
||||
```
|
||||
GET /pms.php?like&dyn=宋代
|
||||
```
|
||||
|
||||
#### 7. 按标签筛选(友情)并点赞
|
||||
```
|
||||
GET /pms.php?like&tag=友情
|
||||
```
|
||||
|
||||
#### 8. 同时筛选(朝代=宋代 且 标签=友情)并点赞
|
||||
```
|
||||
GET /pms.php?like&dyn=宋代&tag=友情
|
||||
```
|
||||
|
||||
#### 9. 直接点赞指定站点(lid=5)
|
||||
```
|
||||
POST /pms.php?lid=5
|
||||
```
|
||||
|
||||
### 返回示例
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "点赞成功",
|
||||
"data": {
|
||||
"id": 15572,
|
||||
"name": "良将不怯死以苟免,烈士不毁节以求生",
|
||||
"alias": "暂无朝代",
|
||||
"keywords": "三国志",
|
||||
"introduce": "优秀的将领不会畏惧死亡而苟且偷生,忠烈之士不会毁坏名节而求得生存。",
|
||||
"drtime": "(赏析)此句强调了人的尊严和信仰是不能被牺牲和放弃...",
|
||||
"like": 1,
|
||||
"tui": 0,
|
||||
"star": 4,
|
||||
"hits_total": 156,
|
||||
"hits_month": 12,
|
||||
"hits_day": 3,
|
||||
"date": "2026-03-21",
|
||||
"datem": "2026-03",
|
||||
"create_time": "2026-03-13 06:38:24",
|
||||
"update_time": "2026-03-13 21:03:58"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 注意事项
|
||||
|
||||
1. **浏览次数统计**:只要接口成功返回 data,该站点的日、月、总浏览次数均会 +1。
|
||||
2. **朝代对应**:数据库字段 alias,标签对应 keywords(支持模糊匹配)。
|
||||
3. **多个值分隔**:用英文逗号分隔,不要混用中文逗号。
|
||||
4. **点赞/取消点赞**:基于 IP 进行限制,同一 IP 对同一站点切换状态。
|
||||
5. **like 是无值参数**:只需在 URL 中包含 &like 或 ?like 即可,无需赋值。
|
||||
6. **频率限制**:每个 IP 每秒最多请求 2 次,超出将返回 `{"code":429, "msg":"请求过于频繁,请稍后再试"}`。
|
||||
7. **接口未做严格的频率限制**,但建议合理调用。
|
||||
|
||||
---
|
||||
|
||||
## 2. 搜索接口 (`/searchs.php`)
|
||||
|
||||
**Flutter 客户端**:`lib/utils/http/poetry_api.dart` 中 `PoetryApi.searchPoetry` 已对接本接口;全站搜索 UI 为 `lib/views/active/active_search_page.dart`。
|
||||
|
||||
### 接口说明
|
||||
提供诗词内容搜索功能,支持模糊搜索和精确搜索,多关键字搜索,以及自定义搜索字段。
|
||||
|
||||
**请求方式**: GET / POST 均可
|
||||
|
||||
### 请求参数
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `q` | string | 否 | 搜索关键词 |
|
||||
| `field` | string | 否 | 搜索字段:name(标题), keywords(标签), introduce(译文) |
|
||||
| `page` | int | 否 | 页码,默认1 |
|
||||
| `limit` | int | 否 | 每页数量,默认20 |
|
||||
|
||||
### 返回格式
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "搜索成功",
|
||||
"data": {
|
||||
"total": 156,
|
||||
"page": 1,
|
||||
"limit": 20,
|
||||
"list": [
|
||||
{
|
||||
"id": 123,
|
||||
"name": "诗词标题",
|
||||
"alias": "唐代",
|
||||
"keywords": "友情,离别",
|
||||
"introduce": "译文内容...",
|
||||
"like": 25,
|
||||
"hits_total": 1024
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 示例请求
|
||||
|
||||
#### 1. 基础搜索
|
||||
```
|
||||
GET /searchs.php?q=李白
|
||||
```
|
||||
|
||||
#### 2. 按标题搜索
|
||||
```
|
||||
GET /searchs.php?q=李白&field=name
|
||||
```
|
||||
|
||||
#### 3. 分页搜索
|
||||
```
|
||||
GET /searchs.php?q=爱情&page=2&limit=10
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 朝代列表接口 (`/dyn.php`)
|
||||
|
||||
### 接口说明
|
||||
获取所有可用的朝代列表。
|
||||
|
||||
**请求方式**: GET
|
||||
|
||||
### 返回格式
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"total": 8,
|
||||
"dynasties": [
|
||||
"先秦",
|
||||
"汉代",
|
||||
"唐代",
|
||||
"宋代",
|
||||
"元代",
|
||||
"明代",
|
||||
"清代",
|
||||
"暂无朝代"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 排行榜接口 (`/apilist.php`)
|
||||
|
||||
### 接口说明
|
||||
获取诗词排行榜信息,包括点赞排行、浏览排行等。
|
||||
|
||||
**请求方式**: GET
|
||||
|
||||
### 请求参数
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `type` | string | 否 | 排行类型:like(点赞), hits(浏览) |
|
||||
| `period` | string | 否 | 时间周期:day(日), month(月), total(总) |
|
||||
| `limit` | int | 否 | 返回数量,默认10 |
|
||||
|
||||
### 返回格式
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"top_like": [
|
||||
{"id": 123, "name": "诗词标题", "like": 156}
|
||||
],
|
||||
"top_hits_day": [
|
||||
{"id": 456, "name": "诗词标题", "hits_day": 89}
|
||||
],
|
||||
"top_hits_month": [
|
||||
{"id": 789, "name": "诗词标题", "hits_month": 456}
|
||||
],
|
||||
"top_hits_total": [
|
||||
{"id": 101, "name": "诗词标题", "hits_total": 2341}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 网站统计接口 (`/stat.php`)
|
||||
|
||||
### 接口说明
|
||||
获取网站整体统计信息。
|
||||
|
||||
**请求方式**: GET
|
||||
|
||||
### 返回格式
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "网站统计",
|
||||
"data": {
|
||||
"build_time": "2026-03-13 06:38:24",
|
||||
"category": 25,
|
||||
"site": 1568,
|
||||
"apply": 45,
|
||||
"apply_reject": 12,
|
||||
"article": 89,
|
||||
"article_category": 8,
|
||||
"notice": 6,
|
||||
"link": 23,
|
||||
"top_hits_day": [
|
||||
{"id": 123, "name": "诗词标题", "hits_day": 156}
|
||||
],
|
||||
"top_hits_month": [
|
||||
{"id": 456, "name": "诗词标题", "hits_month": 892}
|
||||
],
|
||||
"top_hits_total": [
|
||||
{"id": 789, "name": "诗词标题", "hits_total": 3456}
|
||||
],
|
||||
"top_like": [
|
||||
{"id": 101, "name": "诗词标题", "like": 234}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 通用说明
|
||||
|
||||
### 错误码
|
||||
|
||||
| 错误码 | 说明 |
|
||||
|------|------|
|
||||
| 0 | 成功 |
|
||||
| -1 | 数据不存在或参数错误 |
|
||||
| 429 | 请求过于频繁 |
|
||||
| 500 | 服务器内部错误 |
|
||||
|
||||
### 通用响应格式
|
||||
|
||||
所有接口都遵循统一的响应格式:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0, // 状态码
|
||||
"msg": "success", // 提示信息
|
||||
"data": {} // 具体数据,成功时返回
|
||||
}
|
||||
```
|
||||
|
||||
### 开发建议
|
||||
|
||||
1. **错误处理**:检查 `code` 字段,非0时处理错误信息
|
||||
2. **频率控制**:避免短时间内大量请求,建议每秒不超过2次
|
||||
3. **缓存策略**:对于不常变化的数据(如朝代列表),建议本地缓存
|
||||
4. **分页处理**:搜索接口支持分页,合理设置每页数量
|
||||
5. **字段验证**:传入参数前进行基本验证
|
||||
|
||||
---
|
||||
|
||||
## Flutter客户端使用示例
|
||||
|
||||
### 使用HttpClient
|
||||
|
||||
```dart
|
||||
import 'package:your_app/utils/http/http_client.dart';
|
||||
import 'package:your_app/utils/http/poetry_api.dart';
|
||||
|
||||
// 获取随机诗词
|
||||
try {
|
||||
final response = await PoetryApi.getRandomPoetry();
|
||||
print('诗词标题: ${response.data?.name}');
|
||||
print('朝代: ${response.data?.alias}');
|
||||
} catch (e) {
|
||||
print('获取失败: $e');
|
||||
}
|
||||
|
||||
// 按朝代获取
|
||||
final response = await PoetryApi.getRandomPoetry(dynasty: '唐代');
|
||||
|
||||
// 搜索诗词
|
||||
final searchResponse = await PoetryApi.searchPoetry('李白');
|
||||
|
||||
// 点赞
|
||||
if (poetryData?.id != null) {
|
||||
await PoetryApi.toggleLike(poetryData!.id);
|
||||
}
|
||||
```
|
||||
|
||||
### 网络权限配置
|
||||
|
||||
确保在 `AndroidManifest.xml` 和 `module.json5` 中配置了网络权限:
|
||||
|
||||
**Android**:
|
||||
```xml
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
```
|
||||
|
||||
**鸿蒙**:
|
||||
```json
|
||||
"requestPermissions": [
|
||||
{"name": "ohos.permission.INTERNET"},
|
||||
{"name": "ohos.permission.GET_NETWORK_INFO"}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*最后更新时间: 2026-03-21*
|
||||
*API版本: v1.0*
|
||||
188
lib/services/network_listener_service.dart
Normal file
188
lib/services/network_listener_service.dart
Normal file
@@ -0,0 +1,188 @@
|
||||
/// 时间: 2026-03-22
|
||||
/// 功能: 网络监听服务
|
||||
/// 介绍: 监听网络操作状态,提供全局网络状态管理和事件通知
|
||||
/// 最新变化: 新增 NetworkEventType.search 与 sendSearchEvent,供诗词搜索页通知
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
/// 网络状态枚举
|
||||
enum NetworkStatus {
|
||||
idle, // 空闲
|
||||
loading, // 加载中
|
||||
success, // 成功
|
||||
error, // 错误
|
||||
}
|
||||
|
||||
/// 网络事件类型
|
||||
enum NetworkEventType {
|
||||
like, // 点赞操作
|
||||
unlike, // 取消点赞
|
||||
refresh, // 刷新操作
|
||||
load, // 加载操作
|
||||
search, // 诗词搜索完成
|
||||
noteUpdate, // 笔记更新(新建/编辑/删除)
|
||||
}
|
||||
|
||||
/// 网络事件数据
|
||||
class NetworkEvent {
|
||||
final NetworkEventType type;
|
||||
final String? data;
|
||||
final DateTime timestamp;
|
||||
final String? errorMessage;
|
||||
|
||||
NetworkEvent({
|
||||
required this.type,
|
||||
this.data,
|
||||
DateTime? timestamp,
|
||||
this.errorMessage,
|
||||
}) : timestamp = timestamp ?? DateTime.now();
|
||||
}
|
||||
|
||||
/// 网络监听服务
|
||||
class NetworkListenerService {
|
||||
static final NetworkListenerService _instance = NetworkListenerService._internal();
|
||||
factory NetworkListenerService() => _instance;
|
||||
NetworkListenerService._internal();
|
||||
|
||||
// 状态管理
|
||||
NetworkStatus _status = NetworkStatus.idle;
|
||||
final Map<String, bool> _loadingStates = {};
|
||||
|
||||
// 事件流控制器
|
||||
final StreamController<NetworkEvent> _eventController =
|
||||
StreamController<NetworkEvent>.broadcast();
|
||||
|
||||
// 状态流控制器
|
||||
final StreamController<NetworkStatus> _statusController =
|
||||
StreamController<NetworkStatus>.broadcast();
|
||||
|
||||
// 公开流
|
||||
Stream<NetworkEvent> get eventStream => _eventController.stream;
|
||||
Stream<NetworkStatus> get statusStream => _statusController.stream;
|
||||
NetworkStatus get currentStatus => _status;
|
||||
|
||||
/// 获取加载状态
|
||||
bool isLoading(String key) => _loadingStates[key] ?? false;
|
||||
|
||||
/// 开始网络操作
|
||||
void startLoading(String key) {
|
||||
_loadingStates[key] = true;
|
||||
_updateStatus(NetworkStatus.loading);
|
||||
|
||||
if (kDebugMode) {
|
||||
print('NetworkListener: 开始加载 - $key');
|
||||
}
|
||||
}
|
||||
|
||||
/// 结束网络操作
|
||||
void endLoading(String key) {
|
||||
_loadingStates.remove(key);
|
||||
|
||||
if (_loadingStates.isEmpty) {
|
||||
_updateStatus(NetworkStatus.idle);
|
||||
}
|
||||
|
||||
if (kDebugMode) {
|
||||
print('NetworkListener: 结束加载 - $key');
|
||||
}
|
||||
}
|
||||
|
||||
/// 发送成功事件
|
||||
void sendSuccessEvent(NetworkEventType type, {String? data}) {
|
||||
_updateStatus(NetworkStatus.success);
|
||||
_eventController.add(NetworkEvent(type: type, data: data));
|
||||
|
||||
if (kDebugMode) {
|
||||
print('NetworkListener: 成功事件 - $type, 数据: $data');
|
||||
}
|
||||
}
|
||||
|
||||
/// 发送错误事件
|
||||
void sendErrorEvent(NetworkEventType type, {String? errorMessage}) {
|
||||
_updateStatus(NetworkStatus.error);
|
||||
_eventController.add(NetworkEvent(
|
||||
type: type,
|
||||
errorMessage: errorMessage,
|
||||
));
|
||||
|
||||
if (kDebugMode) {
|
||||
print('NetworkListener: 错误事件 - $type, 错误: $errorMessage');
|
||||
}
|
||||
}
|
||||
|
||||
/// 发送点赞事件
|
||||
void sendLikeEvent(String poetryId, bool isLiked) {
|
||||
final eventType = isLiked ? NetworkEventType.like : NetworkEventType.unlike;
|
||||
sendSuccessEvent(eventType, data: poetryId);
|
||||
}
|
||||
|
||||
/// 发送刷新事件
|
||||
void sendRefreshEvent() {
|
||||
sendSuccessEvent(NetworkEventType.refresh);
|
||||
}
|
||||
|
||||
/// 发送搜索完成事件(关键词可为空表示仅清空结果)
|
||||
void sendSearchEvent({String? keyword}) {
|
||||
sendSuccessEvent(NetworkEventType.search, data: keyword);
|
||||
}
|
||||
|
||||
/// 更新状态
|
||||
void _updateStatus(NetworkStatus newStatus) {
|
||||
if (_status != newStatus) {
|
||||
_status = newStatus;
|
||||
_statusController.add(_status);
|
||||
|
||||
if (kDebugMode) {
|
||||
print('NetworkListener: 状态更新 - $newStatus');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 清理资源
|
||||
void dispose() {
|
||||
_eventController.close();
|
||||
_statusController.close();
|
||||
_loadingStates.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// 网络监听混入类
|
||||
/// 方便其他类使用网络监听功能
|
||||
mixin NetworkListenerMixin {
|
||||
final NetworkListenerService _networkService = NetworkListenerService();
|
||||
|
||||
Stream<NetworkEvent> get networkEvents => _networkService.eventStream;
|
||||
Stream<NetworkStatus> get networkStatus => _networkService.statusStream;
|
||||
NetworkStatus get currentNetworkStatus => _networkService.currentStatus;
|
||||
|
||||
/// 开始网络加载
|
||||
void startNetworkLoading(String key) {
|
||||
_networkService.startLoading(key);
|
||||
}
|
||||
|
||||
/// 结束网络加载
|
||||
void endNetworkLoading(String key) {
|
||||
_networkService.endLoading(key);
|
||||
}
|
||||
|
||||
/// 发送点赞事件
|
||||
void sendLikeEvent(String poetryId, bool isLiked) {
|
||||
_networkService.sendLikeEvent(poetryId, isLiked);
|
||||
}
|
||||
|
||||
/// 发送刷新事件
|
||||
void sendRefreshEvent() {
|
||||
_networkService.sendRefreshEvent();
|
||||
}
|
||||
|
||||
/// 发送搜索完成事件
|
||||
void sendSearchEvent({String? keyword}) {
|
||||
_networkService.sendSearchEvent(keyword: keyword);
|
||||
}
|
||||
|
||||
/// 检查是否正在加载
|
||||
bool isNetworkLoading(String key) {
|
||||
return _networkService.isLoading(key);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user