- 清理大量废弃的 barrel 导出文件,移除冗余的中间导出层 - 修复所有相对路径导入错误,统一调整为扁平化模块引用 - 更新多平台 pubspec 版本号与依赖库版本 - 补充后端功能问题管理后台与脚本工具 - 调整部分页面的快捷方式文案适配新功能 - 更新部分翻译覆盖率与API文档
198 lines
6.0 KiB
Dart
198 lines
6.0 KiB
Dart
// ============================================================
|
|
// 闲言APP — 文章创作服务
|
|
// 创建时间: 2026-04-29
|
|
// 更新时间: 2026-04-29
|
|
// 作用: 封装文章创作/列表/详情/管理API调用
|
|
// 上次更新: 初始创建
|
|
// ============================================================
|
|
|
|
import 'package:dio/dio.dart';
|
|
import 'package:xianyan/core/network/api_client.dart';
|
|
import 'package:xianyan/core/network/api_exception.dart';
|
|
import 'package:xianyan/core/network/api_response.dart';
|
|
import 'article_models.dart';
|
|
|
|
class ArticleService {
|
|
ArticleService._();
|
|
static final ApiClient _api = ApiClient.instance;
|
|
static const String _basePath = '/api/article';
|
|
|
|
/// 获取文章列表
|
|
static Future<List<Article>> getArticleList({
|
|
int page = 1,
|
|
int limit = 20,
|
|
String? category,
|
|
String? keyword,
|
|
}) async {
|
|
try {
|
|
final response = await _api.get<Map<String, dynamic>>(
|
|
'$_basePath/list',
|
|
queryParameters: {
|
|
'page': page,
|
|
'limit': limit,
|
|
if (category != null) 'category': category,
|
|
if (keyword != null) 'keyword': keyword,
|
|
},
|
|
);
|
|
final apiResp = ApiResponse<Map<String, dynamic>>.fromJson(
|
|
response.data as Map<String, dynamic>,
|
|
);
|
|
if (!apiResp.isSuccess) {
|
|
throw ApiException(code: apiResp.code, message: apiResp.msg);
|
|
}
|
|
final list = apiResp.data?['list'] as List? ?? [];
|
|
return list
|
|
.map((e) => Article.fromJson(e as Map<String, dynamic>))
|
|
.toList();
|
|
} on DioException catch (e) {
|
|
throw _handleDioError(e);
|
|
}
|
|
}
|
|
|
|
/// 获取文章详情
|
|
static Future<Article> getArticleDetail({required int id}) async {
|
|
try {
|
|
final response = await _api.get<Map<String, dynamic>>(
|
|
'$_basePath/detail',
|
|
queryParameters: {'id': id},
|
|
);
|
|
final apiResp = ApiResponse<Map<String, dynamic>>.fromJson(
|
|
response.data as Map<String, dynamic>,
|
|
);
|
|
if (!apiResp.isSuccess) {
|
|
throw ApiException(code: apiResp.code, message: apiResp.msg);
|
|
}
|
|
return Article.fromJson(apiResp.data ?? {});
|
|
} on DioException catch (e) {
|
|
throw _handleDioError(e);
|
|
}
|
|
}
|
|
|
|
/// 提交/编辑文章
|
|
static Future<Map<String, dynamic>> submitArticle({
|
|
int? id,
|
|
required String title,
|
|
required String content,
|
|
String? summary,
|
|
String? category,
|
|
List<String>? tags,
|
|
}) async {
|
|
try {
|
|
final response = await _api.post<Map<String, dynamic>>(
|
|
'$_basePath/submit',
|
|
data: {
|
|
if (id != null) 'id': id,
|
|
'title': title,
|
|
'content': content,
|
|
if (summary != null) 'summary': summary,
|
|
if (category != null) 'category': category,
|
|
if (tags != null) 'tags': tags,
|
|
},
|
|
);
|
|
final respData = response.data as Map<String, dynamic>;
|
|
final code = respData['code'] as int? ?? 0;
|
|
if (code != 1) {
|
|
throw ApiException(
|
|
code: code,
|
|
message: respData['msg'] as String? ?? '提交失败',
|
|
);
|
|
}
|
|
return respData['data'] as Map<String, dynamic>? ?? {};
|
|
} on DioException catch (e) {
|
|
throw _handleDioError(e);
|
|
}
|
|
}
|
|
|
|
/// 获取我的文章
|
|
static Future<List<Article>> getMyArticles({
|
|
int page = 1,
|
|
int limit = 20,
|
|
}) async {
|
|
try {
|
|
final response = await _api.get<Map<String, dynamic>>(
|
|
'$_basePath/mine',
|
|
queryParameters: {'page': page, 'limit': limit},
|
|
);
|
|
final apiResp = ApiResponse<Map<String, dynamic>>.fromJson(
|
|
response.data as Map<String, dynamic>,
|
|
);
|
|
if (!apiResp.isSuccess) {
|
|
throw ApiException(code: apiResp.code, message: apiResp.msg);
|
|
}
|
|
final list = apiResp.data?['list'] as List? ?? [];
|
|
return list
|
|
.map((e) => Article.fromJson(e as Map<String, dynamic>))
|
|
.toList();
|
|
} on DioException catch (e) {
|
|
throw _handleDioError(e);
|
|
}
|
|
}
|
|
|
|
/// 删除文章
|
|
static Future<bool> deleteArticle({required int id}) async {
|
|
try {
|
|
final response = await _api.post<Map<String, dynamic>>(
|
|
'$_basePath/delete',
|
|
data: {'id': id},
|
|
);
|
|
final respData = response.data as Map<String, dynamic>;
|
|
final code = respData['code'] as int? ?? 0;
|
|
return code == 1;
|
|
} on DioException catch (e) {
|
|
throw _handleDioError(e);
|
|
}
|
|
}
|
|
|
|
/// 获取文章评论
|
|
static Future<List<ArticleComment>> getArticleComments({
|
|
required int articleId,
|
|
int page = 1,
|
|
int limit = 20,
|
|
}) async {
|
|
try {
|
|
final response = await _api.get<Map<String, dynamic>>(
|
|
'$_basePath/comments',
|
|
queryParameters: {
|
|
'article_id': articleId,
|
|
'page': page,
|
|
'limit': limit,
|
|
},
|
|
);
|
|
final apiResp = ApiResponse<Map<String, dynamic>>.fromJson(
|
|
response.data as Map<String, dynamic>,
|
|
);
|
|
if (!apiResp.isSuccess) {
|
|
throw ApiException(code: apiResp.code, message: apiResp.msg);
|
|
}
|
|
final list = apiResp.data?['list'] as List? ?? [];
|
|
return list
|
|
.map((e) => ArticleComment.fromJson(e as Map<String, dynamic>))
|
|
.toList();
|
|
} on DioException catch (e) {
|
|
throw _handleDioError(e);
|
|
}
|
|
}
|
|
|
|
static ApiException _handleDioError(DioException e) {
|
|
switch (e.type) {
|
|
case DioExceptionType.connectionTimeout:
|
|
case DioExceptionType.sendTimeout:
|
|
case DioExceptionType.receiveTimeout:
|
|
return const ApiException(code: -1, message: '连接超时,请检查网络');
|
|
case DioExceptionType.connectionError:
|
|
return const ApiException(code: -2, message: '网络连接失败');
|
|
default:
|
|
if (e.response?.data != null) {
|
|
try {
|
|
final data = e.response!.data as Map<String, dynamic>;
|
|
return ApiException(
|
|
code: data['code'] as int? ?? e.response?.statusCode ?? -5,
|
|
message: data['msg'] as String? ?? '请求失败',
|
|
);
|
|
} catch (_) {}
|
|
}
|
|
return const ApiException(code: -5, message: '未知网络错误');
|
|
}
|
|
}
|
|
}
|