v1.0.1
This commit is contained in:
462
lib/requests/comic_request.dart
Normal file
462
lib/requests/comic_request.dart
Normal file
@@ -0,0 +1,462 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_dmzj/app/app_error.dart';
|
||||
import 'package:flutter_dmzj/app/log.dart';
|
||||
import 'package:flutter_dmzj/models/comic/author_model.dart';
|
||||
import 'package:flutter_dmzj/models/comic/category_comic_model.dart';
|
||||
import 'package:flutter_dmzj/models/comic/category_filter_model.dart';
|
||||
import 'package:flutter_dmzj/models/comic/category_item_model.dart';
|
||||
import 'package:flutter_dmzj/models/comic/chapter_detail_model.dart';
|
||||
import 'package:flutter_dmzj/models/comic/chapter_detail_web_model.dart';
|
||||
import 'package:flutter_dmzj/models/comic/chapter_info.dart';
|
||||
import 'package:flutter_dmzj/models/comic/comic_related_model.dart';
|
||||
import 'package:flutter_dmzj/models/comic/detail_info.dart';
|
||||
import 'package:flutter_dmzj/models/comic/detail_model.dart';
|
||||
import 'package:flutter_dmzj/models/comic/detail_v1_model.dart';
|
||||
import 'package:flutter_dmzj/models/comic/rank_item_model.dart';
|
||||
import 'package:flutter_dmzj/models/comic/recommend_model.dart';
|
||||
import 'package:flutter_dmzj/models/comic/search_item.dart';
|
||||
import 'package:flutter_dmzj/models/comic/search_model.dart';
|
||||
import 'package:flutter_dmzj/models/comic/special_model.dart';
|
||||
import 'package:flutter_dmzj/models/comic/update_item_model.dart';
|
||||
import 'package:flutter_dmzj/models/comic/view_point_model.dart';
|
||||
import 'package:flutter_dmzj/models/comic/web_search_model.dart';
|
||||
import 'package:flutter_dmzj/models/db/download_status.dart';
|
||||
import 'package:flutter_dmzj/requests/common/http_client.dart';
|
||||
import 'package:flutter_dmzj/services/comic_download_service.dart';
|
||||
import 'package:flutter_dmzj/services/user_service.dart';
|
||||
|
||||
import '../models/comic/special_detail_model.dart';
|
||||
|
||||
class ComicRequest {
|
||||
/// 漫画-推荐
|
||||
Future<List<ComicRecommendModel>> recommend() async {
|
||||
var list = <ComicRecommendModel>[];
|
||||
var result = await HttpClient.instance.getJson('/comic/recommend/index');
|
||||
|
||||
for (var item in result) {
|
||||
list.add(ComicRecommendModel.fromJson(item));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// 猜你喜欢
|
||||
Future<List<ComicRecommendItemModel>> refreshRecommend(int categoryId,
|
||||
{int page = 1, int size = 3}) async {
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/comic/recommend/more',
|
||||
queryParameters: {"cateId": categoryId, "size": size, "page": page},
|
||||
);
|
||||
List<ComicRecommendItemModel> list = [];
|
||||
|
||||
for (var item in result["data"]["recommendList"]) {
|
||||
list.add(ComicRecommendItemModel.fromJson(item));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// 首页-我的订阅
|
||||
Future<ComicRecommendModel> recommendSubscribe() async {
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/comic/sub/list',
|
||||
needLogin: true,
|
||||
checkCode: true,
|
||||
queryParameters: {"status": 0, "firstLetter": "", "page": 1, "size": 3},
|
||||
);
|
||||
|
||||
var list = <ComicRecommendItemModel>[];
|
||||
for (var item in result["subList"]) {
|
||||
list.add(ComicRecommendItemModel.fromJson(item));
|
||||
}
|
||||
return ComicRecommendModel(
|
||||
categoryId: 49,
|
||||
title: "我的订阅",
|
||||
sort: 0,
|
||||
data: list,
|
||||
);
|
||||
}
|
||||
|
||||
/// 最近更新
|
||||
Future<List<ComicUpdateItemModel>> latest(
|
||||
{required int type, int page = 1}) async {
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/comic/update/list/$type/$page',
|
||||
needLogin: true,
|
||||
);
|
||||
var list = <ComicUpdateItemModel>[];
|
||||
for (var item in result["data"]) {
|
||||
list.add(ComicUpdateItemModel.fromJson(item));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// 分类
|
||||
Future<List<ComicCategoryItemModel>> categores() async {
|
||||
var list = <ComicCategoryItemModel>[];
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/comic/filter/category',
|
||||
queryParameters: {"source": 1},
|
||||
checkCode: true,
|
||||
);
|
||||
for (var item in result["cateList"]) {
|
||||
list.add(ComicCategoryItemModel.fromJson(item));
|
||||
}
|
||||
// 百合赛高
|
||||
list.add(ComicCategoryItemModel(tagId: 3243, title: "ゆり", cover: ""));
|
||||
return list;
|
||||
}
|
||||
|
||||
/// 分类-筛选
|
||||
Future<List<ComicCategoryFilterModel>> categoryFilter() async {
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/comic/filter/category',
|
||||
queryParameters: {"source": 1},
|
||||
checkCode: true,
|
||||
);
|
||||
// for (var item in result["cateList"]) {
|
||||
// list.add(ComicCategoryFilterModel.fromJson(item));
|
||||
// }
|
||||
var list = <ComicCategoryFilterItemModel>[];
|
||||
for (var item in result["cateList"]) {
|
||||
list.add(ComicCategoryFilterItemModel.fromJson(item));
|
||||
}
|
||||
return [
|
||||
ComicCategoryFilterModel(title: "全部分类", items: list),
|
||||
];
|
||||
}
|
||||
|
||||
/// 分类下漫画
|
||||
/// - [ids] 标签
|
||||
/// - [sort] 排序,0=人气,1=更新
|
||||
/// - [page] 页数,从0开始
|
||||
Future<List<ComicCategoryComicModel>> categoryComic({
|
||||
required int id,
|
||||
int sort = 1,
|
||||
int page = 1,
|
||||
int status = 0,
|
||||
}) async {
|
||||
var list = <ComicCategoryComicModel>[];
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/comic/filter/list',
|
||||
queryParameters: {
|
||||
"theme": id,
|
||||
"status": 0,
|
||||
"sortType": sort,
|
||||
"page": page,
|
||||
"size": 20,
|
||||
},
|
||||
checkCode: true,
|
||||
needLogin: true // 登录可以更多内容
|
||||
);
|
||||
for (var item in result["comicList"]) {
|
||||
list.add(ComicCategoryComicModel.fromJson(item));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// 排行榜
|
||||
Future<List<ComicRankListItemModel>> rank({
|
||||
required int tagId,
|
||||
required byTime,
|
||||
required rankType,
|
||||
int page = 1,
|
||||
}) async {
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/comic/rank/list',
|
||||
queryParameters: {
|
||||
'tag_id': tagId,
|
||||
'by_time': byTime,
|
||||
'rank_type': rankType,
|
||||
'page': page
|
||||
},
|
||||
);
|
||||
var list = <ComicRankListItemModel>[];
|
||||
for (var item in result["data"]) {
|
||||
list.add(ComicRankListItemModel.fromJson(item));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// 排行榜-分类
|
||||
Future<Map<int, String>> rankFilter() async {
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/comic/filter/category',
|
||||
queryParameters: {"source": 1},
|
||||
checkCode: true,
|
||||
);
|
||||
Map<int, String> map = {
|
||||
0: "全部分类",
|
||||
3243: "ゆり"
|
||||
};
|
||||
for (var item in result["cateList"]) {
|
||||
map.addAll({
|
||||
item["tagId"]: item["title"],
|
||||
});
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// 专题
|
||||
Future<List<ComicSpecialModel>> special({int page = 1}) async {
|
||||
var list = <ComicSpecialModel>[];
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/subject/0/$page.json',
|
||||
checkCode: true,
|
||||
);
|
||||
for (var item in result) {
|
||||
list.add(ComicSpecialModel.fromJson(item));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// 专题
|
||||
Future<ComicSpecialDetailModel> specialDetail({required int id}) async {
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/subject/$id.json',
|
||||
checkCode: true,
|
||||
);
|
||||
|
||||
return ComicSpecialDetailModel.fromJson(result);
|
||||
}
|
||||
|
||||
/// 作者详情
|
||||
Future<ComicAuthorModel> authorDetail({required int id}) async {
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/UCenter/author/$id.json',
|
||||
);
|
||||
|
||||
return ComicAuthorModel.fromJson(result);
|
||||
}
|
||||
|
||||
/// 作品相关
|
||||
Future<ComicRelatedModel> related({required int id}) async {
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/v3/comic/related/$id.json',
|
||||
);
|
||||
|
||||
return ComicRelatedModel.fromJson(result);
|
||||
}
|
||||
|
||||
Future<ComicDetailInfo> comicDetail(
|
||||
{required int comicId, bool priorityV1 = false}) async {
|
||||
ComicDetailInfo info;
|
||||
var errorMsg = "";
|
||||
try {
|
||||
if (priorityV1) {
|
||||
info = ComicDetailInfo.fromV1(await comicDetailV1(comicId: comicId),
|
||||
isHide: true);
|
||||
} else {
|
||||
info = ComicDetailInfo.fromV4(await comicDetailV4(comicId: comicId));
|
||||
}
|
||||
} catch (e) {
|
||||
errorMsg += "${priorityV1 ? "V1" : "V4"}:$e";
|
||||
try {
|
||||
if (priorityV1) {
|
||||
info = ComicDetailInfo.fromV4(await comicDetailV4(comicId: comicId));
|
||||
} else {
|
||||
info = ComicDetailInfo.fromV1(await comicDetailV1(comicId: comicId),
|
||||
isHide: e.toString() == "漫画不存在");
|
||||
}
|
||||
} catch (e) {
|
||||
errorMsg += "\n${priorityV1 ? "V4" : "V1"}:$e";
|
||||
throw AppError("ComicID:$comicId\n无法读取漫画信息,可能需要登录或有等级限制\n$errorMsg");
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
/// 漫画详情
|
||||
Future<ComicDetailModel> comicDetailV4({
|
||||
required int comicId,
|
||||
}) async {
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/comic/detail/$comicId',
|
||||
needLogin: true,
|
||||
checkCode: true,
|
||||
);
|
||||
|
||||
return ComicDetailModel.fromJson(result);
|
||||
}
|
||||
|
||||
/// 漫画详情
|
||||
Future<ComicDetailV1Model> comicDetailV1({
|
||||
required int comicId,
|
||||
}) async {
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/dynamic/comicinfo/$comicId.json',
|
||||
baseUrl: "https://api.dmzj.com",
|
||||
needLogin: true,
|
||||
);
|
||||
var data = json.decode(result);
|
||||
if (data["result"] != 1) {
|
||||
throw AppError(data["msg"]);
|
||||
}
|
||||
if (data["data"]?["info"]?["id"] == null) {
|
||||
throw AppError("无法读取漫画信息");
|
||||
}
|
||||
return ComicDetailV1Model.fromJson(data["data"]);
|
||||
}
|
||||
|
||||
/// 漫画搜索
|
||||
/// - [page] 页数从0开始
|
||||
/// - [keyword] 关键字
|
||||
Future<List<SearchComicItem>> search(
|
||||
{required String keyword, int page = 1}) async {
|
||||
var list = <ComicSearchModel>[];
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/search/index',
|
||||
queryParameters: {
|
||||
"keyword": keyword,
|
||||
"page": page,
|
||||
"size": 20,
|
||||
},
|
||||
checkCode: true,
|
||||
);
|
||||
for (var item in result["list"]) {
|
||||
list.add(ComicSearchModel.fromJson(item));
|
||||
}
|
||||
return list.map((e) => SearchComicItem.fromApi(e)).toList();
|
||||
}
|
||||
|
||||
/// 漫画搜索热词
|
||||
Future<Map<int, String>> searchHotWord() async {
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/search/hot/0.json',
|
||||
);
|
||||
Map<int, String> map = {};
|
||||
for (var item in result) {
|
||||
map.addAll({
|
||||
item["id"]: item["name"],
|
||||
});
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// 章节详情
|
||||
Future<ComicChapterDetail> chapterDetail(
|
||||
{required int comicId,
|
||||
required int chapterId,
|
||||
required bool useHD}) async {
|
||||
ComicChapterDetail info;
|
||||
|
||||
try {
|
||||
//查询本地是否存在
|
||||
var localInfo =
|
||||
ComicDownloadService.instance.box.get("${comicId}_$chapterId");
|
||||
if (localInfo != null && localInfo.status == DownloadStatus.complete) {
|
||||
return ComicChapterDetail.fromDownload(localInfo);
|
||||
}
|
||||
|
||||
var v4 = await chapterDetailV4(comicId: comicId, chapterId: chapterId);
|
||||
info = ComicChapterDetail.fromV4(v4, useHD);
|
||||
} catch (e) {
|
||||
Log.logPrint(e);
|
||||
try {
|
||||
var v1 = await chapterDetailWeb(comicId: comicId, chapterId: chapterId);
|
||||
info = ComicChapterDetail.fromWebApi(v1);
|
||||
} catch (e) {
|
||||
Log.logPrint(e);
|
||||
|
||||
throw AppError("ComicID:$comicId ChapterID:$chapterId\n无法读取章节信息");
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
/// 章节详情-V4
|
||||
Future<ComicChapterDetailModel> chapterDetailV4(
|
||||
{required int comicId, required int chapterId}) async {
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/comic/chapter/$comicId/$chapterId',
|
||||
needLogin: true,
|
||||
checkCode: true,
|
||||
);
|
||||
|
||||
var data = ComicChapterDetailModel.fromJson(result["data"]);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/// 章节详情-WebAPI
|
||||
Future<ComicChapterDetailWebModel> chapterDetailWeb(
|
||||
{required int comicId, required int chapterId}) async {
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/chapinfo/$comicId/$chapterId.html',
|
||||
baseUrl: "https://m.idmzj.com",
|
||||
needLogin: true,
|
||||
);
|
||||
if (result.toString().startsWith("{")) {
|
||||
var data = json.decode(result);
|
||||
return ComicChapterDetailWebModel.fromJson(data);
|
||||
} else {
|
||||
throw AppError(result);
|
||||
}
|
||||
}
|
||||
|
||||
/// 观点、吐槽
|
||||
Future<List<ComicViewPointModel>> viewPoints(
|
||||
{required int comicId, required int chapterId}) async {
|
||||
var list = <ComicViewPointModel>[];
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/viewPoint/0/$comicId/$chapterId.json',
|
||||
);
|
||||
for (var item in result) {
|
||||
list.add(ComicViewPointModel.fromJson(item));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// 点赞观点、吐槽
|
||||
Future<bool> likeViewPoint({required int comicId, required int id}) async {
|
||||
await HttpClient.instance.postJson(
|
||||
'/viewPoint/praise',
|
||||
checkCode: true,
|
||||
data: {
|
||||
"sub_type": comicId,
|
||||
"uid": UserService.instance.userId,
|
||||
"vote_id": id,
|
||||
},
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// 点赞观点、吐槽
|
||||
Future<bool> sendViewPoint(
|
||||
{required int comicId,
|
||||
required int chapterId,
|
||||
required String content,
|
||||
required int page}) async {
|
||||
await HttpClient.instance.postJson(
|
||||
'/viewPoint/addv2',
|
||||
checkCode: true,
|
||||
data: {
|
||||
"sub_type": comicId,
|
||||
"uid": UserService.instance.userId,
|
||||
"dmzj_token": UserService.instance.dmzjToken,
|
||||
"page": page,
|
||||
"type": 0,
|
||||
"third_type": chapterId,
|
||||
"content": content,
|
||||
},
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// 漫画搜索-Web接口
|
||||
/// - [keyword] 关键字
|
||||
Future<List<SearchComicItem>> searchWeb({required String keyword}) async {
|
||||
var list = <ComicWebSearchModel>[];
|
||||
var result = await HttpClient.instance.getText(
|
||||
'http://sacg.idmzj.com/comicsum/search.php',
|
||||
baseUrl: "",
|
||||
queryParameters: {
|
||||
"s": keyword,
|
||||
},
|
||||
);
|
||||
var data = jsonDecode(result.substring(20, result.lastIndexOf(';')));
|
||||
for (var item in data) {
|
||||
list.add(ComicWebSearchModel.fromJson(item));
|
||||
}
|
||||
return list.map((e) => SearchComicItem.fromWeb(e)).toList();
|
||||
}
|
||||
}
|
||||
169
lib/requests/comment_request.dart
Normal file
169
lib/requests/comment_request.dart
Normal file
@@ -0,0 +1,169 @@
|
||||
import 'package:flutter_dmzj/app/app_error.dart';
|
||||
import 'package:flutter_dmzj/models/comment/comment_item.dart';
|
||||
import 'package:flutter_dmzj/models/comment/user_comment_item.dart';
|
||||
import 'package:flutter_dmzj/requests/common/api.dart';
|
||||
import 'package:flutter_dmzj/requests/common/http_client.dart';
|
||||
import 'package:flutter_dmzj/services/user_service.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:html_unescape/html_unescape.dart';
|
||||
|
||||
class CommentRequest {
|
||||
var unescape = HtmlUnescape();
|
||||
|
||||
/// 读取最新的评论
|
||||
/// - [type] 类型
|
||||
/// - [objId] ID
|
||||
/// - [page] 页数
|
||||
/// - [pageSize] 每页数量
|
||||
Future<List<CommentItem>> getComment({
|
||||
required int type,
|
||||
required int objId,
|
||||
int sort = 1,
|
||||
int page = 1,
|
||||
int pageSize = 30,
|
||||
}) async {
|
||||
List<CommentItem> ls = [];
|
||||
Map result = await HttpClient.instance.getJson(
|
||||
'/comment/list',
|
||||
baseUrl: Api.BASE_URL,
|
||||
queryParameters: {
|
||||
"type": type,
|
||||
"objId": objId,
|
||||
"sort": sort,
|
||||
"page": page - 1,
|
||||
"size": pageSize,
|
||||
},
|
||||
);
|
||||
if (result["errno"] != 0) {
|
||||
throw AppError(result["errmsg"].toString());
|
||||
}
|
||||
if (result["data"]["commentIdList"] == null) {
|
||||
return [];
|
||||
}
|
||||
var ids = result["data"]["commentIdList"];
|
||||
var comments = result["data"]["commentList"];
|
||||
for (String id in ids) {
|
||||
var idSplit = id.split(",");
|
||||
var item = _parseLatestCommentItem(comments, idSplit.first, type);
|
||||
if (idSplit.length > 1) {
|
||||
item.parents = [];
|
||||
for (var id2 in idSplit.skip(1)) {
|
||||
item.parents.insert(0, _parseLatestCommentItem(comments, id2, type));
|
||||
}
|
||||
}
|
||||
if (item.id != 0) {
|
||||
ls.add(item);
|
||||
}
|
||||
}
|
||||
return ls;
|
||||
}
|
||||
|
||||
CommentItem _parseLatestCommentItem(Map comments, String id, int type) {
|
||||
if (!comments.containsKey(id)) {
|
||||
return CommentItem.createEmpty();
|
||||
}
|
||||
var item = comments[id];
|
||||
//返回的类型非常随机,有时候是int,有时候是string,所以使用int.tryParse
|
||||
return CommentItem(
|
||||
type: type,
|
||||
id: int.tryParse(item["id"].toString()) ?? 0,
|
||||
objId: int.tryParse(item["obj_id"].toString()) ?? 0,
|
||||
content: unescape.convert(item["content"].toString()),
|
||||
photo: item["photo"].toString(),
|
||||
createTime: int.tryParse(item["create_time"].toString()) ?? 0,
|
||||
images: item["upload_images"]
|
||||
.toString()
|
||||
.split(",")
|
||||
.where((x) => x.isNotEmpty)
|
||||
.toList(),
|
||||
likeAmount: (int.tryParse(item["like_amount"].toString()) ?? 0).obs,
|
||||
nickname: item["nickname"].toString(),
|
||||
replyAmount: int.tryParse(item["reply_amount"].toString()) ?? 0,
|
||||
gender: int.tryParse(item["sex"].toString()) ?? 0,
|
||||
userId: int.tryParse(item["sender_uid"].toString()) ?? 0,
|
||||
originId: int.tryParse(item["origin_comment_id"].toString()) ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
/// 发表评论
|
||||
/// - [objId] ID
|
||||
/// - [type] 类型 ,见AppConstant
|
||||
/// - [content] 内容
|
||||
/// - [toCommentId] 回复评论ID
|
||||
/// - [originCommentId] 原始评论ID
|
||||
/// - [toUid] 回复用户
|
||||
Future<bool> sendComment({
|
||||
required int objId,
|
||||
required int type,
|
||||
required String content,
|
||||
String toCommentId = "0",
|
||||
String originCommentId = "0",
|
||||
String toUid = "0",
|
||||
}) async {
|
||||
var result = await HttpClient.instance.postJson(
|
||||
"/v1/$type/new/add/app",
|
||||
baseUrl: Api.BASE_URL,
|
||||
data: {
|
||||
"obj_id": objId,
|
||||
"to_comment_id": toCommentId,
|
||||
"origin_comment_id": originCommentId,
|
||||
"to_uid": toUid,
|
||||
"sender_terminal": 1,
|
||||
"content": content,
|
||||
"dmzj_token": UserService.instance.dmzjToken,
|
||||
"_debug": 0
|
||||
},
|
||||
);
|
||||
if (result["code"] != 0) {
|
||||
throw AppError(result["msg"].toString());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// 评论点赞
|
||||
Future<bool> likeComment({
|
||||
required int commentId,
|
||||
required int objId,
|
||||
required int type,
|
||||
}) async {
|
||||
await HttpClient.instance.getJson(
|
||||
"/v1/$type/like/$commentId",
|
||||
baseUrl: Api.BASE_URL,
|
||||
queryParameters: {
|
||||
"comment_id": commentId,
|
||||
"obj_id": objId,
|
||||
"type": type,
|
||||
},
|
||||
needLogin: true,
|
||||
withDefaultParameter: true,
|
||||
checkCode: true,
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// 读取用户的评论
|
||||
/// - [type] 类型 0=漫画,1=轻小说,2=新闻
|
||||
/// - [uid] 用户ID
|
||||
/// - [page] 页数,从0开始
|
||||
Future<List<UserCommentItem>> getUserComment({
|
||||
required int type,
|
||||
required int uid,
|
||||
int page = 0,
|
||||
}) async {
|
||||
List<UserCommentItem> ls = [];
|
||||
var result = await HttpClient.instance.getJson(
|
||||
type == 1
|
||||
? '/comment/owner/1/$uid/$page.json'
|
||||
: '/v3/old/comment/owner/$type/$uid/$page.json',
|
||||
baseUrl: Api.BASE_URL,
|
||||
withDefaultParameter: true,
|
||||
needLogin: true,
|
||||
);
|
||||
for (var item in result) {
|
||||
ls.add(UserCommentItem.fromJson(item));
|
||||
}
|
||||
|
||||
return ls;
|
||||
}
|
||||
}
|
||||
81
lib/requests/common/api.dart
Normal file
81
lib/requests/common/api.dart
Normal file
@@ -0,0 +1,81 @@
|
||||
// ignore_for_file: constant_identifier_names
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:crypton/crypton.dart';
|
||||
import 'package:flutter_dmzj/app/app_error.dart';
|
||||
import 'package:flutter_dmzj/services/user_service.dart';
|
||||
|
||||
class Api {
|
||||
static const String DMZJ_DOMAIN_NAME = "dmzj.com";
|
||||
static const String IDMZJ_DOMAIN_NAME = "idmzj.com";
|
||||
static const String MUWAI_DOMAIN_NAME = "muwai.com";
|
||||
static const String DOMAIN_NAME = "zaimanhua.com";
|
||||
|
||||
/// V3接口,无加密
|
||||
static const String BASE_URL = "https://v4api.zaimanhua.com/app/v1";
|
||||
|
||||
/// 用户
|
||||
static const String BASE_URL_USER = "https://account-api.zaimanhua.com/v1";
|
||||
|
||||
/// Interface
|
||||
static const String BASE_URL_INTERFACE =
|
||||
"http://nninterface.$IDMZJ_DOMAIN_NAME";
|
||||
|
||||
/// V4 API的密钥
|
||||
static const V4_PRIVATE_KEY =
|
||||
"MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAK8nNR1lTnIfIes6oRWJNj3mB6OssDGx0uGMpgpbVCpf6+VwnuI2stmhZNoQcM417Iz7WqlPzbUmu9R4dEKmLGEEqOhOdVaeh9Xk2IPPjqIu5TbkLZRxkY3dJM1htbz57d/roesJLkZXqssfG5EJauNc+RcABTfLb4IiFjSMlTsnAgMBAAECgYEAiz/pi2hKOJKlvcTL4jpHJGjn8+lL3wZX+LeAHkXDoTjHa47g0knYYQteCbv+YwMeAGupBWiLy5RyyhXFoGNKbbnvftMYK56hH+iqxjtDLnjSDKWnhcB7089sNKaEM9Ilil6uxWMrMMBH9v2PLdYsqMBHqPutKu/SigeGPeiB7VECQQDizVlNv67go99QAIv2n/ga4e0wLizVuaNBXE88AdOnaZ0LOTeniVEqvPtgUk63zbjl0P/pzQzyjitwe6HoCAIpAkEAxbOtnCm1uKEp5HsNaXEJTwE7WQf7PrLD4+BpGtNKkgja6f6F4ld4QZ2TQ6qvsCizSGJrjOpNdjVGJ7bgYMcczwJBALvJWPLmDi7ToFfGTB0EsNHZVKE66kZ/8Stx+ezueke4S556XplqOflQBjbnj2PigwBN/0afT+QZUOBOjWzoDJkCQClzo+oDQMvGVs9GEajS/32mJ3hiWQZrWvEzgzYRqSf3XVcEe7PaXSd8z3y3lACeeACsShqQoc8wGlaHXIJOHTcCQQCZw5127ZGs8ZDTSrogrH73Kw/HvX55wGAeirKYcv28eauveCG7iyFR0PFB/P/EDZnyb+ifvyEFlucPUI0+Y87F";
|
||||
static Uint8List decryptV4(String text) {
|
||||
try {
|
||||
RSAKeypair rsaKeypair =
|
||||
RSAKeypair(RSAPrivateKey.fromString(V4_PRIVATE_KEY));
|
||||
var decrypted = rsaKeypair.privateKey.decryptData(base64.decode(text));
|
||||
return decrypted;
|
||||
} catch (e) {
|
||||
throw AppError('返回数据解密失败');
|
||||
}
|
||||
}
|
||||
|
||||
/// 签名
|
||||
static String sign(String content, String mode) {
|
||||
var utf8Content = utf8.encode(mode + content);
|
||||
|
||||
return md5.convert(utf8Content).toString();
|
||||
}
|
||||
|
||||
static const String VERSION = "3.8.2";
|
||||
static String get timeStamp =>
|
||||
(DateTime.now().millisecondsSinceEpoch / 1000).toStringAsFixed(0);
|
||||
|
||||
/// 默认的参数
|
||||
static Map<String, dynamic> getDefaultParameter({bool withUid = false}) {
|
||||
var map = <String, dynamic>{
|
||||
"channel": "android",
|
||||
//"version": VERSION,
|
||||
"timestamp": timeStamp
|
||||
};
|
||||
if (withUid && UserService.instance.logined.value) {
|
||||
map.addAll({"uid": UserService.instance.userId});
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// 小说正文链接
|
||||
static String getNovelContentUrl(
|
||||
{required int volumeId, required int chapterId}) {
|
||||
// var path = "/lnovel/${volumeId}_$chapterId.txt";
|
||||
// var ts = (DateTime.now().millisecondsSinceEpoch / 1000).toStringAsFixed(0);
|
||||
// var key =
|
||||
// "IBAAKCAQEAsUAdKtXNt8cdrcTXLsaFKj9bSK1nEOAROGn2KJXlEVekcPssKUxSN8dsfba51kmHM";
|
||||
// key += path;
|
||||
// key += ts;
|
||||
// key = md5.convert(utf8.encode(key)).toString().toLowerCase();
|
||||
|
||||
// return "http://jurisdiction.idmzj.com$path?t=$ts&k=$key";
|
||||
|
||||
//https://v4api.zaimanhua.com/app/v1/novel/download/chapter?volumeId=12458&chapterId=127221
|
||||
return "$BASE_URL/novel/download/chapter?volumeId=$volumeId&chapterId=$chapterId";
|
||||
}
|
||||
}
|
||||
53
lib/requests/common/custom_interceptor.dart
Normal file
53
lib/requests/common/custom_interceptor.dart
Normal file
@@ -0,0 +1,53 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_dmzj/app/log.dart';
|
||||
|
||||
class CustomInterceptor extends Interceptor {
|
||||
@override
|
||||
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
|
||||
options.extra["ts"] = DateTime.now().millisecondsSinceEpoch;
|
||||
super.onRequest(options, handler);
|
||||
}
|
||||
|
||||
@override
|
||||
void onError(DioException err, ErrorInterceptorHandler handler) {
|
||||
var time =
|
||||
DateTime.now().millisecondsSinceEpoch - err.requestOptions.extra["ts"];
|
||||
Log.e('''【HTTP请求错误】 耗时:${time}ms
|
||||
Request Method:${err.requestOptions.method}
|
||||
Response Code:${err.response?.statusCode}
|
||||
Request URL:${err.requestOptions.uri}
|
||||
Request Query:${err.requestOptions.queryParameters}
|
||||
Request Data:${err.requestOptions.data}
|
||||
Request Headers:${err.requestOptions.headers}
|
||||
Response Headers:${err.response?.headers.map}
|
||||
Response Data:${err.response?.data}''', err.stackTrace);
|
||||
super.onError(err, handler);
|
||||
}
|
||||
|
||||
@override
|
||||
void onResponse(Response response, ResponseInterceptorHandler handler) {
|
||||
var time = DateTime.now().millisecondsSinceEpoch -
|
||||
response.requestOptions.extra["ts"];
|
||||
if (response.requestOptions.uri.toString().contains(".txt")) {
|
||||
Log.i(
|
||||
'''【HTTP请求响应】 耗时:${time}ms
|
||||
Request Method:${response.requestOptions.method}
|
||||
Request Code:${response.statusCode}
|
||||
Request URL:${response.requestOptions.uri}''',
|
||||
);
|
||||
return super.onResponse(response, handler);
|
||||
}
|
||||
Log.i(
|
||||
'''【HTTP请求响应】 耗时:${time}ms
|
||||
Request Method:${response.requestOptions.method}
|
||||
Request Code:${response.statusCode}
|
||||
Request URL:${response.requestOptions.uri}
|
||||
Request Query:${response.requestOptions.queryParameters}
|
||||
Request Data:${response.requestOptions.data}
|
||||
Request Headers:${response.requestOptions.headers}
|
||||
Response Headers:${response.headers.map}
|
||||
Response Data:${response.data}''',
|
||||
);
|
||||
super.onResponse(response, handler);
|
||||
}
|
||||
}
|
||||
260
lib/requests/common/http_client.dart
Normal file
260
lib/requests/common/http_client.dart
Normal file
@@ -0,0 +1,260 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_dmzj/app/app_error.dart';
|
||||
import 'package:flutter_dmzj/requests/common/api.dart';
|
||||
import 'package:flutter_dmzj/requests/common/custom_interceptor.dart';
|
||||
import 'package:flutter_dmzj/services/user_service.dart';
|
||||
|
||||
class HttpClient {
|
||||
static HttpClient? _httpUtil;
|
||||
|
||||
static HttpClient get instance {
|
||||
_httpUtil ??= HttpClient();
|
||||
return _httpUtil!;
|
||||
}
|
||||
|
||||
late Dio dio;
|
||||
HttpClient() {
|
||||
dio = Dio(
|
||||
BaseOptions(
|
||||
connectTimeout: const Duration(seconds: 20),
|
||||
receiveTimeout: const Duration(seconds: 20),
|
||||
sendTimeout: const Duration(seconds: 20),
|
||||
),
|
||||
);
|
||||
dio.interceptors.add(CustomInterceptor());
|
||||
}
|
||||
|
||||
/// Get请求
|
||||
/// * [path] 请求链接
|
||||
/// * [queryParameters] 请求参数
|
||||
/// * [cancel] 任务取消Token
|
||||
/// * [needLogin] 是否需要登录
|
||||
/// * [withDefaultParameter] 是否需要带上一些默认参数
|
||||
/// * [responseType] 返回的类型
|
||||
Future<dynamic> get(
|
||||
String path, {
|
||||
Map<String, dynamic>? queryParameters,
|
||||
String baseUrl = Api.BASE_URL,
|
||||
CancelToken? cancel,
|
||||
bool withDefaultParameter = true,
|
||||
bool needLogin = false,
|
||||
ResponseType responseType = ResponseType.json,
|
||||
bool checkCode = false,
|
||||
}) async {
|
||||
Map<String, dynamic> header = {};
|
||||
queryParameters ??= <String, dynamic>{};
|
||||
var query = Api.getDefaultParameter(withUid: needLogin);
|
||||
if (withDefaultParameter) {
|
||||
queryParameters.addAll(query);
|
||||
}
|
||||
if (needLogin) {
|
||||
if (UserService.instance.logined.value) {
|
||||
header['Authorization'] = 'Bearer ${UserService.instance.dmzjToken}';
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
var result = await dio.get(
|
||||
baseUrl + path,
|
||||
queryParameters: queryParameters,
|
||||
options: Options(
|
||||
responseType: responseType,
|
||||
headers: header,
|
||||
),
|
||||
cancelToken: cancel,
|
||||
);
|
||||
if (checkCode && result.data is Map) {
|
||||
var data = result.data as Map;
|
||||
if (data['errno'] == 0) {
|
||||
return result.data['data'];
|
||||
} else {
|
||||
throw AppError(
|
||||
result.data['errmsg'].toString(),
|
||||
code: int.tryParse(result.data['errno'].toString()) ?? 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
return result.data;
|
||||
} on DioException catch (e) {
|
||||
if (e.type == DioExceptionType.cancel) {
|
||||
rethrow;
|
||||
}
|
||||
if (e.type == DioExceptionType.badResponse) {
|
||||
return throw AppError("请求失败:${e.response?.statusCode ?? -1}");
|
||||
}
|
||||
throw AppError("请求失败,请检查网络");
|
||||
}
|
||||
}
|
||||
|
||||
/// Get 请求,返回JSON
|
||||
/// * [path] 请求链接
|
||||
/// * [queryParameters] 请求参数
|
||||
/// * [cancel] 任务取消Token
|
||||
/// * [needLogin] 是否需要登录
|
||||
/// * [withDefaultParameter] 是否需要带上一些默认参数
|
||||
Future<dynamic> getJson(
|
||||
String path, {
|
||||
Map<String, dynamic>? queryParameters,
|
||||
String baseUrl = Api.BASE_URL,
|
||||
CancelToken? cancel,
|
||||
bool withDefaultParameter = true,
|
||||
bool needLogin = false,
|
||||
bool checkCode = false,
|
||||
}) async {
|
||||
var result = await get(
|
||||
path,
|
||||
queryParameters: queryParameters,
|
||||
baseUrl: baseUrl,
|
||||
cancel: cancel,
|
||||
withDefaultParameter: withDefaultParameter,
|
||||
needLogin: needLogin,
|
||||
responseType: ResponseType.json,
|
||||
checkCode: checkCode,
|
||||
);
|
||||
if (result is Map || result is List) {
|
||||
return result;
|
||||
} else if (result is String) {
|
||||
return jsonDecode(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Get 请求,返回Text
|
||||
/// * [path] 请求链接
|
||||
/// * [queryParameters] 请求参数
|
||||
/// * [cancel] 任务取消Token
|
||||
/// * [needLogin] 是否需要登录
|
||||
/// * [withDefaultParameter] 是否需要带上一些默认参数
|
||||
Future<dynamic> getText(
|
||||
String path, {
|
||||
Map<String, dynamic>? queryParameters,
|
||||
String baseUrl = Api.BASE_URL,
|
||||
CancelToken? cancel,
|
||||
bool withDefaultParameter = true,
|
||||
bool needLogin = false,
|
||||
}) async {
|
||||
return await get(
|
||||
path,
|
||||
queryParameters: queryParameters,
|
||||
baseUrl: baseUrl,
|
||||
cancel: cancel,
|
||||
withDefaultParameter: withDefaultParameter,
|
||||
needLogin: needLogin,
|
||||
responseType: ResponseType.plain,
|
||||
);
|
||||
}
|
||||
|
||||
/// Get 请求,返回解密后Bytes
|
||||
/// * [path] 请求链接
|
||||
/// * [queryParameters] 请求参数
|
||||
/// * [cancel] 任务取消Token
|
||||
/// * [needLogin] 是否需要登录
|
||||
/// * [withDefaultParameter] 是否需要带上一些默认参数
|
||||
Future<Uint8List> getEncryptV4(
|
||||
String path, {
|
||||
Map<String, dynamic>? queryParameters,
|
||||
String baseUrl = Api.BASE_URL,
|
||||
CancelToken? cancel,
|
||||
bool withDefaultParameter = true,
|
||||
bool needLogin = false,
|
||||
}) async {
|
||||
var result = await get(
|
||||
path,
|
||||
queryParameters: queryParameters,
|
||||
baseUrl: baseUrl,
|
||||
cancel: cancel,
|
||||
withDefaultParameter: withDefaultParameter,
|
||||
needLogin: needLogin,
|
||||
responseType: ResponseType.plain,
|
||||
);
|
||||
var resultBytes = Api.decryptV4(result);
|
||||
return resultBytes;
|
||||
}
|
||||
|
||||
/// Get 请求,返回byte
|
||||
/// * [path] 请求链接
|
||||
/// * [queryParameters] 请求参数
|
||||
/// * [cancel] 任务取消Token
|
||||
/// * [needLogin] 是否需要登录
|
||||
/// * [withDefaultParameter] 是否需要带上一些默认参数
|
||||
Future<dynamic> getBytes(
|
||||
String path, {
|
||||
Map<String, dynamic>? queryParameters,
|
||||
String baseUrl = Api.BASE_URL,
|
||||
CancelToken? cancel,
|
||||
bool withDefaultParameter = true,
|
||||
bool needLogin = false,
|
||||
}) async {
|
||||
return await get(
|
||||
path,
|
||||
queryParameters: queryParameters,
|
||||
baseUrl: baseUrl,
|
||||
cancel: cancel,
|
||||
withDefaultParameter: withDefaultParameter,
|
||||
needLogin: needLogin,
|
||||
responseType: ResponseType.bytes,
|
||||
);
|
||||
}
|
||||
|
||||
/// Post请求,返回Map
|
||||
/// * [path] 请求链接
|
||||
/// * [data] 发送数据
|
||||
/// * [queryParameters] 请求参数
|
||||
/// * [cancel] 任务取消Token
|
||||
Future<dynamic> postJson(
|
||||
String path, {
|
||||
Map<String, dynamic>? queryParameters,
|
||||
Map<String, dynamic>? data,
|
||||
String baseUrl = Api.BASE_URL,
|
||||
CancelToken? cancel,
|
||||
bool formUrlEncoded = false,
|
||||
bool checkCode = false,
|
||||
bool needLogin = false,
|
||||
}) async {
|
||||
Map<String, dynamic> header = {};
|
||||
queryParameters ??= {};
|
||||
if (needLogin) {
|
||||
if (UserService.instance.logined.value) {
|
||||
header['Authorization'] = 'Bearer ${UserService.instance.dmzjToken}';
|
||||
}
|
||||
}
|
||||
try {
|
||||
var result = await dio.post(
|
||||
baseUrl + path,
|
||||
queryParameters: queryParameters,
|
||||
data: data,
|
||||
options: Options(
|
||||
responseType: ResponseType.json,
|
||||
headers: header,
|
||||
contentType:
|
||||
formUrlEncoded ? Headers.formUrlEncodedContentType : null,
|
||||
),
|
||||
cancelToken: cancel,
|
||||
);
|
||||
var jsonMap = result.data;
|
||||
if (jsonMap is String) {
|
||||
jsonMap = jsonDecode(jsonMap);
|
||||
}
|
||||
if (checkCode) {
|
||||
var data = result.data as Map;
|
||||
if (data['errno'] == 0) {
|
||||
return result.data['data'];
|
||||
} else {
|
||||
throw AppError(
|
||||
result.data['errmsg'].toString(),
|
||||
code: int.tryParse(result.data['errno'].toString()) ?? 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
return result.data;
|
||||
} on DioException catch (e) {
|
||||
if (e.type == DioExceptionType.badResponse) {
|
||||
return throw AppError("请求失败:状态码:${e.response?.statusCode ?? -1}");
|
||||
}
|
||||
throw AppError("请求失败,请检查网络");
|
||||
}
|
||||
}
|
||||
}
|
||||
41
lib/requests/common_request.dart
Normal file
41
lib/requests/common_request.dart
Normal file
@@ -0,0 +1,41 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_dmzj/models/version_model.dart';
|
||||
|
||||
/// 通用的请求
|
||||
class CommonRequest {
|
||||
Future<VersionModel> checkUpdate() async {
|
||||
try {
|
||||
return await checkUpdateGitMirror();
|
||||
} catch (e) {
|
||||
return await checkUpdateJsDelivr();
|
||||
}
|
||||
}
|
||||
|
||||
/// 检查更新
|
||||
Future<VersionModel> checkUpdateGitMirror() async {
|
||||
var result = await Dio().get(
|
||||
"https://raw.gitmirror.com/xiaoyaocz/flutter_dmzj/zaimanhua/document/app_version.json",
|
||||
queryParameters: {
|
||||
"ts": DateTime.now().millisecondsSinceEpoch,
|
||||
},
|
||||
options: Options(
|
||||
responseType: ResponseType.json,
|
||||
),
|
||||
);
|
||||
return VersionModel.fromJson(result.data);
|
||||
}
|
||||
|
||||
/// 检查更新
|
||||
Future<VersionModel> checkUpdateJsDelivr() async {
|
||||
var result = await Dio().get(
|
||||
"https://cdn.jsdelivr.net/gh/xiaoyaocz/flutter_dmzj@zaimanhua/document/app_version.json",
|
||||
queryParameters: {
|
||||
"ts": DateTime.now().millisecondsSinceEpoch,
|
||||
},
|
||||
options: Options(
|
||||
responseType: ResponseType.json,
|
||||
),
|
||||
);
|
||||
return VersionModel.fromJson(result.data);
|
||||
}
|
||||
}
|
||||
134
lib/requests/news_request.dart
Normal file
134
lib/requests/news_request.dart
Normal file
@@ -0,0 +1,134 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_dmzj/app/app_error.dart';
|
||||
import 'package:flutter_dmzj/models/news/news_banner_model.dart';
|
||||
import 'package:flutter_dmzj/models/news/news_list_item_model.dart';
|
||||
import 'package:flutter_dmzj/models/news/news_stat_model.dart';
|
||||
import 'package:flutter_dmzj/models/news/news_tag_model.dart';
|
||||
import 'package:flutter_dmzj/requests/common/api.dart';
|
||||
import 'package:flutter_dmzj/requests/common/http_client.dart';
|
||||
import 'package:flutter_dmzj/services/user_service.dart';
|
||||
|
||||
class NewsRequest {
|
||||
/// 新闻分类
|
||||
Future<List<NewsTagModel>> category() async {
|
||||
var list = <NewsTagModel>[];
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/news/category',
|
||||
);
|
||||
for (var item in result["data"]["cateList"]) {
|
||||
list.add(NewsTagModel.fromJson(item));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// 新闻Banner
|
||||
Future<List<NewsBannerModel>> banner() async {
|
||||
var list = <NewsBannerModel>[];
|
||||
var result = await HttpClient.instance.getJson('/news/recommend');
|
||||
for (var item in result["data"]["recommendList"]) {
|
||||
list.add(NewsBannerModel.fromJson(item));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// 读取新闻列表
|
||||
/// - [id] 新闻分类ID
|
||||
/// - [page] 页数,从1开始
|
||||
Future<List<NewsListItemModel>> getNewsList(int id, int page) async {
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/news/list/$id/${id == 0 ? 2 : 3}/$page',
|
||||
);
|
||||
|
||||
if (result["errno"] != 0) {
|
||||
throw AppError(result["errmsg"]);
|
||||
}
|
||||
var list = <NewsListItemModel>[];
|
||||
for (var item in result["data"]["newsList"]) {
|
||||
list.add(NewsListItemModel.fromJson(item));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// 新闻数据
|
||||
/// - [newsId] 新闻ID
|
||||
Future<NewsStatModel> stat(int newsId) async {
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/v3/article/total/$newsId.json',
|
||||
checkCode: true,
|
||||
);
|
||||
|
||||
return NewsStatModel.fromJson(result);
|
||||
}
|
||||
|
||||
/// 新闻点赞
|
||||
/// - [newsId] 新闻ID
|
||||
Future<bool> like(int newsId) async {
|
||||
await HttpClient.instance.getJson(
|
||||
'/article/mood/$newsId',
|
||||
checkCode: true,
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// 新闻检查收藏
|
||||
/// - [newsId] 新闻ID
|
||||
Future<bool> checkCollect(int newsId) async {
|
||||
var uid = UserService.instance.userId;
|
||||
var par = {"uid": int.parse(uid), "sub_id": newsId};
|
||||
var parJson = jsonEncode(par);
|
||||
var sign = Api.sign(parJson, 'app_news_sub');
|
||||
|
||||
var result = await HttpClient.instance.postJson(
|
||||
'/api/news/subscribe/check',
|
||||
baseUrl: Api.BASE_URL_INTERFACE,
|
||||
data: {
|
||||
"parm": parJson,
|
||||
"sign": sign,
|
||||
},
|
||||
);
|
||||
|
||||
return json.decode(result)["result"] == 809;
|
||||
}
|
||||
|
||||
/// 新闻收藏
|
||||
/// - [newsId] 新闻ID
|
||||
Future<bool> collect(int newsId) async {
|
||||
var uid = UserService.instance.userId;
|
||||
var par = {"uid": int.parse(uid), "sub_id": newsId};
|
||||
var parJson = jsonEncode(par);
|
||||
var sign = Api.sign(parJson, 'app_news_sub');
|
||||
|
||||
var result = await HttpClient.instance.postJson(
|
||||
'/api/news/subscribe/add',
|
||||
baseUrl: Api.BASE_URL_INTERFACE,
|
||||
data: {
|
||||
"parm": parJson,
|
||||
"sign": sign,
|
||||
},
|
||||
);
|
||||
|
||||
return json.decode(result)["result"] == 1000;
|
||||
}
|
||||
|
||||
/// 移除收藏
|
||||
/// - [newsId] 新闻ID
|
||||
Future<bool> delCollect(int newsId) async {
|
||||
var uid = UserService.instance.userId;
|
||||
var par = {"uid": int.parse(uid), "sub_id": newsId};
|
||||
var parJson = jsonEncode(par);
|
||||
var sign = Api.sign(parJson, 'app_news_sub');
|
||||
|
||||
var result = await HttpClient.instance.postJson(
|
||||
'/api/news/subscribe/del',
|
||||
baseUrl: Api.BASE_URL_INTERFACE,
|
||||
data: {
|
||||
"parm": parJson,
|
||||
"sign": sign,
|
||||
},
|
||||
);
|
||||
|
||||
return json.decode(result)["result"] == 1000;
|
||||
}
|
||||
}
|
||||
240
lib/requests/novel_request.dart
Normal file
240
lib/requests/novel_request.dart
Normal file
@@ -0,0 +1,240 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_dmzj/models/novel/category_filter_model.dart';
|
||||
import 'package:flutter_dmzj/models/novel/category_model.dart';
|
||||
import 'package:flutter_dmzj/models/novel/category_novel_model.dart';
|
||||
import 'package:flutter_dmzj/models/novel/detail_model.dart';
|
||||
import 'package:flutter_dmzj/models/novel/latest_model.dart';
|
||||
import 'package:flutter_dmzj/models/novel/rank_model.dart';
|
||||
import 'package:flutter_dmzj/models/novel/recommend_model.dart';
|
||||
import 'package:flutter_dmzj/models/novel/search_model.dart';
|
||||
import 'package:flutter_dmzj/models/novel/volume_detail_model.dart';
|
||||
import 'package:flutter_dmzj/requests/common/api.dart';
|
||||
import 'package:flutter_dmzj/requests/common/http_client.dart';
|
||||
import 'package:flutter_dmzj/services/local_storage_service.dart';
|
||||
|
||||
class NovelRequest {
|
||||
/// 轻小说-推荐
|
||||
Future<List<NovelRecommendModel>> recommend() async {
|
||||
var list = <NovelRecommendModel>[];
|
||||
var result =
|
||||
await HttpClient.instance.getJson('/novel/recommend', checkCode: true);
|
||||
for (var item in result["recommendList"]) {
|
||||
list.add(NovelRecommendModel.fromJson(item));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// 轻小说-更新
|
||||
/// - [page] 页数从0开始
|
||||
Future<List<NovelLatestModel>> latest({int page = 1}) async {
|
||||
var list = <NovelLatestModel>[];
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/novel/filter/list',
|
||||
queryParameters: {
|
||||
//status=0&sortType=1&page=1&size=20&tagId=0
|
||||
"status": 0,
|
||||
"sortType": 1,
|
||||
"page": page,
|
||||
"size": 20,
|
||||
},
|
||||
checkCode: true,
|
||||
);
|
||||
for (var item in result["novelList"]) {
|
||||
list.add(NovelLatestModel.fromJson(item));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// 轻小说-分类
|
||||
Future<List<NovelCategoryModel>> categores() async {
|
||||
var list = <NovelCategoryModel>[];
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/comic/filter/category',
|
||||
queryParameters: {
|
||||
"source": 2,
|
||||
},
|
||||
checkCode: true,
|
||||
);
|
||||
for (var item in result["cateList"]) {
|
||||
list.add(NovelCategoryModel.fromJson(item));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// 分类-筛选
|
||||
Future<List<NovelCategoryFilterModel>> categoryFilter() async {
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/comic/filter/category',
|
||||
queryParameters: {"source": 2},
|
||||
checkCode: true,
|
||||
);
|
||||
var list = <NovelCategoryFilterItemModel>[];
|
||||
for (var item in result["cateList"]) {
|
||||
list.add(NovelCategoryFilterItemModel.fromJson(item));
|
||||
}
|
||||
return [
|
||||
NovelCategoryFilterModel(title: "题材", items: list),
|
||||
];
|
||||
}
|
||||
|
||||
/// 分类下漫画
|
||||
/// - [cateId] 分类
|
||||
/// - [sort] 排序,0=人气,1=更新
|
||||
/// - [page] 页数,从0开始
|
||||
Future<List<NovelCategoryNovelModel>> categoryNovel({
|
||||
int cateId = 0,
|
||||
int status = 0,
|
||||
int sort = 0,
|
||||
int page = 1,
|
||||
}) async {
|
||||
var list = <NovelCategoryNovelModel>[];
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/novel/filter/list',
|
||||
queryParameters: {
|
||||
"tagId": cateId,
|
||||
"status": 0,
|
||||
"sortType": sort,
|
||||
"page": page,
|
||||
"size": 20,
|
||||
},
|
||||
checkCode: true,
|
||||
);
|
||||
for (var item in result["novelList"]) {
|
||||
list.add(NovelCategoryNovelModel.fromJson(item));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// 排行榜
|
||||
Future<List<NovelRankModel>> rank({
|
||||
required int tagId,
|
||||
required sort,
|
||||
int page = 0,
|
||||
}) async {
|
||||
var list = <NovelRankModel>[];
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/novel/rank/$sort/$tagId/$page.json',
|
||||
);
|
||||
for (var item in result) {
|
||||
list.add(NovelRankModel.fromJson(item));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// 排行榜-分类
|
||||
Future<Map<int, String>> rankFilter() async {
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/comic/filter/category',
|
||||
queryParameters: {
|
||||
"source": 2,
|
||||
},
|
||||
checkCode: true,
|
||||
);
|
||||
Map<int, String> map = {};
|
||||
for (var item in result["cateList"]) {
|
||||
map.addAll({
|
||||
item["tagId"]: item["title"],
|
||||
});
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// 轻小说搜索
|
||||
/// - [page] 页数从0开始
|
||||
/// - [keyword] 关键字
|
||||
Future<List<NovelSearchModel>> search(
|
||||
{required String keyword, int page = 1}) async {
|
||||
var list = <NovelSearchModel>[];
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/search/index',
|
||||
queryParameters: {
|
||||
"keyword": keyword,
|
||||
"page": page,
|
||||
"size": 20,
|
||||
"source": 1,
|
||||
},
|
||||
checkCode: true,
|
||||
);
|
||||
for (var item in result["list"]) {
|
||||
list.add(NovelSearchModel.fromJson(item));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// 小说搜索热词
|
||||
Future<Map<int, String>> searchHotWord() async {
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/search/hot/1.json',
|
||||
);
|
||||
Map<int, String> map = {};
|
||||
for (var item in result) {
|
||||
map.addAll({
|
||||
item["id"]: item["name"],
|
||||
});
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// 小说详情
|
||||
Future<NovelDetailModel> novelDetail({
|
||||
required int novelId,
|
||||
}) async {
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/novel/detail/$novelId',
|
||||
needLogin: true,
|
||||
checkCode: true,
|
||||
);
|
||||
var data = NovelDetailModel.fromJson(result);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/// 小说章节
|
||||
Future<List<NovelVolumeDetailModel>> novelChapter({
|
||||
required int novelId,
|
||||
}) async {
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/novel/chapter/$novelId',
|
||||
needLogin: true,
|
||||
checkCode: true,
|
||||
);
|
||||
var list = <NovelVolumeDetailModel>[];
|
||||
for (var item in result["data"]) {
|
||||
list.add(NovelVolumeDetailModel.fromJson(item));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// 小说正文内容
|
||||
/// - [volumeId] 卷ID
|
||||
/// - [chapterId] 章节ID
|
||||
/// - [cancel] 取消Token
|
||||
/// - [cache] 是否缓存
|
||||
Future<String> novelContent({
|
||||
required int volumeId,
|
||||
required int chapterId,
|
||||
CancelToken? cancel,
|
||||
bool cache = true,
|
||||
}) async {
|
||||
var localContent = await LocalStorageService.instance
|
||||
.getNovelContent(volumeId: volumeId, chapterId: chapterId);
|
||||
if (localContent != null) {
|
||||
return localContent;
|
||||
}
|
||||
var result = await HttpClient.instance.getText(
|
||||
Api.getNovelContentUrl(volumeId: volumeId, chapterId: chapterId),
|
||||
baseUrl: "",
|
||||
withDefaultParameter: false,
|
||||
cancel: cancel,
|
||||
);
|
||||
if (cache) {
|
||||
await LocalStorageService.instance.saveNovelContent(
|
||||
volumeId: volumeId,
|
||||
chapterId: chapterId,
|
||||
content: result,
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
349
lib/requests/user_request.dart
Normal file
349
lib/requests/user_request.dart
Normal file
@@ -0,0 +1,349 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:flutter_dmzj/app/app_constant.dart';
|
||||
import 'package:flutter_dmzj/app/app_error.dart';
|
||||
import 'package:flutter_dmzj/models/user/comic_history_model.dart';
|
||||
import 'package:flutter_dmzj/models/user/bind_status_model.dart';
|
||||
import 'package:flutter_dmzj/models/user/login_result_model.dart';
|
||||
import 'package:flutter_dmzj/models/user/novel_history_model.dart';
|
||||
import 'package:flutter_dmzj/models/user/subscribe_comic_model.dart';
|
||||
import 'package:flutter_dmzj/models/user/subscribe_news_model.dart';
|
||||
import 'package:flutter_dmzj/models/user/subscribe_novel_model.dart';
|
||||
import 'package:flutter_dmzj/models/user/user_profile_model.dart';
|
||||
import 'package:flutter_dmzj/requests/common/api.dart';
|
||||
import 'package:flutter_dmzj/requests/common/http_client.dart';
|
||||
import 'package:flutter_dmzj/services/db_service.dart';
|
||||
import 'package:flutter_dmzj/services/user_service.dart';
|
||||
|
||||
class UserRequest {
|
||||
/// 登录
|
||||
/// - [nickname] 用户名
|
||||
/// - [password] 密码
|
||||
Future<LoginResultModel> login(
|
||||
{required String nickname, required String password}) async {
|
||||
var pwd = md5.convert(utf8.encode(password)).toString().toLowerCase();
|
||||
|
||||
Map<String, dynamic> data = {
|
||||
"username": nickname,
|
||||
"passwd": pwd,
|
||||
};
|
||||
|
||||
var result = await HttpClient.instance.postJson(
|
||||
"/login/passwd",
|
||||
baseUrl: Api.BASE_URL_USER,
|
||||
data: data,
|
||||
formUrlEncoded: true,
|
||||
checkCode: true,
|
||||
);
|
||||
|
||||
return LoginResultModel.fromJson(result["user"]);
|
||||
}
|
||||
|
||||
/// 用户资料
|
||||
Future<UserProfileModel> userProfile() async {
|
||||
var result = await HttpClient.instance.getJson(
|
||||
"/UCenter/comicsv2/${UserService.instance.userId}.json",
|
||||
baseUrl: Api.BASE_URL,
|
||||
queryParameters: {
|
||||
"dmzj_token": UserService.instance.dmzjToken,
|
||||
},
|
||||
withDefaultParameter: true,
|
||||
);
|
||||
|
||||
return UserProfileModel.fromJson(result);
|
||||
}
|
||||
|
||||
/// 获取绑定手机、设置密码状态
|
||||
Future<UserBindStatusModel> isBindTelPwd() async {
|
||||
var result = await HttpClient.instance.getJson(
|
||||
"/account/isbindtelpwd",
|
||||
baseUrl: Api.BASE_URL,
|
||||
queryParameters: {
|
||||
"dmzj_token": UserService.instance.dmzjToken,
|
||||
},
|
||||
withDefaultParameter: true,
|
||||
checkCode: true,
|
||||
);
|
||||
|
||||
return UserBindStatusModel.fromJson(result);
|
||||
}
|
||||
|
||||
/// 我的漫画订阅
|
||||
/// - [page] 页数从0开始
|
||||
/// - [subType] 全部=1,未读=2,已读=3,完结=4
|
||||
/// - [letter] all=全部
|
||||
Future<List<UserSubscribeComicItemModel>> comicSubscribes(
|
||||
{required int subType, int page = 1, String letter = ""}) async {
|
||||
var list = <UserSubscribeComicItemModel>[];
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/comic/sub/list',
|
||||
queryParameters: {
|
||||
//uid=$uid&sub_type=$subType&letter=$letter&dmzj_token=$token&page=$page&type=$type
|
||||
"status": subType,
|
||||
"firstLetter": letter,
|
||||
"page": page,
|
||||
"size": 20
|
||||
},
|
||||
needLogin: true,
|
||||
checkCode: true,
|
||||
);
|
||||
for (var item in result["subList"]) {
|
||||
list.add(UserSubscribeComicItemModel.fromJson(item));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// 我的小说订阅
|
||||
/// - [page] 页数从0开始
|
||||
/// - [subType] 全部=1,未读=2,已读=3,完结=4
|
||||
/// - [letter] all=全部
|
||||
Future<List<UserSubscribeNovelModel>> novelSubscribes(
|
||||
{required int subType, int page = 0, String letter = "all"}) async {
|
||||
var list = <UserSubscribeNovelModel>[];
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/novel/sub/list',
|
||||
queryParameters: {
|
||||
//uid=$uid&sub_type=$subType&letter=$letter&dmzj_token=$token&page=$page&type=$type
|
||||
"status": subType,
|
||||
"firstLetter": letter,
|
||||
"page": page,
|
||||
"size": 20
|
||||
},
|
||||
needLogin: true,
|
||||
checkCode: true,
|
||||
);
|
||||
for (var item in result["subList"]) {
|
||||
list.add(UserSubscribeNovelModel.fromJson(item));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// 我的新闻收藏
|
||||
/// - [page] 页数从0开始
|
||||
Future<List<UserSubscribeNewsModel>> newsSubscribes({int page = 1}) async {
|
||||
var uid = UserService.instance.userId;
|
||||
var par = {"uid": int.parse(uid), "page": page};
|
||||
var parJson = jsonEncode(par);
|
||||
var sign = Api.sign(parJson, 'app_news_sub');
|
||||
|
||||
var result = await HttpClient.instance.postJson(
|
||||
'/api/news/getSubscribe',
|
||||
baseUrl: Api.BASE_URL_INTERFACE,
|
||||
data: {
|
||||
"parm": parJson,
|
||||
"sign": sign,
|
||||
},
|
||||
);
|
||||
var data = json.decode(result);
|
||||
if (data["result"] != 1000) {
|
||||
throw AppError(data["msg"]);
|
||||
}
|
||||
var list = <UserSubscribeNewsModel>[];
|
||||
for (var item in data["data"]) {
|
||||
list.add(UserSubscribeNewsModel.fromJson(item));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// 添加订阅
|
||||
/// - [type] 类型,对应AppConstant
|
||||
Future<bool> addSubscribe({required List<int> ids, required int type}) async {
|
||||
var requestUrl = "/comic/sub/add";
|
||||
var requestQuery = <String, dynamic>{};
|
||||
if (type == AppConstant.kTypeComic) {
|
||||
requestUrl = "/comic/sub/add";
|
||||
requestQuery = {
|
||||
"comic_id": ids.join(","),
|
||||
};
|
||||
} else if (type == AppConstant.kTypeNovel) {
|
||||
requestUrl = "/novel/sub/add";
|
||||
requestQuery = {
|
||||
"novel_id": ids.join(","),
|
||||
};
|
||||
}
|
||||
|
||||
await HttpClient.instance.getJson(
|
||||
requestUrl,
|
||||
queryParameters: requestQuery,
|
||||
needLogin: true,
|
||||
checkCode: true,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// 更新订阅的阅读状态
|
||||
/// - [type] 类型,对应AppConstant
|
||||
Future<bool> subscribeRead({required int id, required int type}) async {
|
||||
var typeStr = "mh";
|
||||
if (type == AppConstant.kTypeComic) {
|
||||
typeStr = "mh";
|
||||
} else if (type == AppConstant.kTypeNovel) {
|
||||
typeStr = "xs";
|
||||
}
|
||||
|
||||
await HttpClient.instance.getJson(
|
||||
'/subscribe/read',
|
||||
queryParameters: {
|
||||
"obj_id": id,
|
||||
"type": typeStr,
|
||||
},
|
||||
withDefaultParameter: true,
|
||||
needLogin: true,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// 取消订阅
|
||||
/// - [type] 类型,对应AppConstant
|
||||
Future<bool> removeSubscribe(
|
||||
{required List<int> ids, required int type}) async {
|
||||
var requestUrl = "/comic/sub/del";
|
||||
var requestQuery = <String, dynamic>{};
|
||||
if (type == AppConstant.kTypeComic) {
|
||||
requestUrl = "/comic/sub/del";
|
||||
requestQuery = {
|
||||
"comic_id": ids.join(","),
|
||||
};
|
||||
} else if (type == AppConstant.kTypeNovel) {
|
||||
requestUrl = "/novel/sub/del";
|
||||
requestQuery = {
|
||||
"novel_id": ids.join(","),
|
||||
};
|
||||
}
|
||||
|
||||
await HttpClient.instance.getJson(
|
||||
requestUrl,
|
||||
queryParameters: requestQuery,
|
||||
needLogin: true,
|
||||
checkCode: true,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// 查询订阅状态
|
||||
/// - [objId] 漫画ID或小说ID
|
||||
/// - [type] 类型,对应AppConstant
|
||||
Future<bool> checkSubscribeStatus(
|
||||
{required int objId, required int type}) async {
|
||||
var typeId = 1;
|
||||
if (type == AppConstant.kTypeComic) {
|
||||
typeId = 1;
|
||||
} else if (type == AppConstant.kTypeNovel) {
|
||||
typeId = 2;
|
||||
}
|
||||
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/comic/sub/checkIsSub',
|
||||
checkCode: true,
|
||||
queryParameters: {
|
||||
"objId": objId,
|
||||
"source": typeId,
|
||||
},
|
||||
needLogin: true,
|
||||
);
|
||||
return result["isSub"];
|
||||
}
|
||||
|
||||
/// 漫画阅读记录
|
||||
/// - [page] 页数从0开始,接口并没有分页
|
||||
Future<List<UserComicHistoryModel>> comicHistory({int page = 0}) async {
|
||||
var list = <UserComicHistoryModel>[];
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/api/getReInfo/comic/${UserService.instance.userId}/$page',
|
||||
queryParameters: {},
|
||||
baseUrl: Api.BASE_URL_INTERFACE,
|
||||
);
|
||||
var data = json.decode(result);
|
||||
for (var item in data) {
|
||||
list.add(UserComicHistoryModel.fromJson(item));
|
||||
}
|
||||
//远程与本地同步
|
||||
DBService.instance.syncRemoteComicHistory(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
/// 小说阅读记录
|
||||
/// - [page] 页数从0开始,接口并没有分页
|
||||
Future<List<UserNovelHistoryModel>> novelHistory({int page = 0}) async {
|
||||
var list = <UserNovelHistoryModel>[];
|
||||
var result = await HttpClient.instance.getJson(
|
||||
'/api/getReInfo/novel/${UserService.instance.userId}/$page',
|
||||
queryParameters: {},
|
||||
baseUrl: Api.BASE_URL_INTERFACE,
|
||||
);
|
||||
var data = json.decode(result);
|
||||
for (var item in data) {
|
||||
list.add(UserNovelHistoryModel.fromJson(item));
|
||||
}
|
||||
//远程与本地同步
|
||||
DBService.instance.syncRemoteNovelHistory(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
/// 上传漫画记录
|
||||
Future<bool> uploadComicHistory({
|
||||
required int comicId,
|
||||
required int chapterId,
|
||||
required int page,
|
||||
required DateTime time,
|
||||
}) async {
|
||||
var data = {
|
||||
comicId.toString(): chapterId.toString(),
|
||||
"comicId": comicId.toString(),
|
||||
"chapterId": chapterId.toString(),
|
||||
"page": page,
|
||||
"time": (time.millisecondsSinceEpoch ~/ 1000).toString()
|
||||
};
|
||||
await HttpClient.instance.getJson(
|
||||
"/api/record/getRe",
|
||||
baseUrl: Api.BASE_URL_INTERFACE,
|
||||
queryParameters: {
|
||||
"st": "comic",
|
||||
"uid": UserService.instance.userId,
|
||||
"callback": "record_jsonpCallback",
|
||||
"type": 3,
|
||||
"json": "[${json.encode(data)}]",
|
||||
},
|
||||
withDefaultParameter: true,
|
||||
checkCode: true,
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// 上传小说记录
|
||||
Future<bool> uploadNovelHistory({
|
||||
required int novelId,
|
||||
required int chapterId,
|
||||
required int volumeId,
|
||||
required int page,
|
||||
required int total,
|
||||
required DateTime time,
|
||||
}) async {
|
||||
var data = {
|
||||
novelId.toString(): chapterId.toString(),
|
||||
"lnovel_id": novelId.toString(),
|
||||
"volume_id": volumeId.toString(),
|
||||
"chapterId": chapterId.toString(),
|
||||
"total_num": total,
|
||||
"page": page,
|
||||
"time": (time.millisecondsSinceEpoch ~/ 1000).toString()
|
||||
};
|
||||
await HttpClient.instance.getJson(
|
||||
"/api/record/getRe",
|
||||
baseUrl: Api.BASE_URL_INTERFACE,
|
||||
queryParameters: {
|
||||
"st": "novel",
|
||||
"uid": UserService.instance.userId,
|
||||
"callback": "record_jsonpCallback",
|
||||
"type": 3,
|
||||
"json": "[${json.encode(data)}]",
|
||||
},
|
||||
withDefaultParameter: true,
|
||||
checkCode: true,
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user