fix(client): 兼容后端 Long 字段字符串化(id 与计数)

后端 J1 起 Long/long 一律以字符串下发(含雪花 id 与 viewCount/expiresIn)。
客户端 id 方向本来就是 (json['id'] ?? '').toString(),不受影响;会被 as num? 打挂的
是 Long 计数,这里加统一兼容解析并替换 3 处:

- lib/core/data/models/json_numbers.dart(新增,接受数字或数字字符串)
- lib/core/data/models/auth_session.dart(expiresIn,后端 long)
- lib/core/data/models/drama.dart(viewCount,后端 Long ×2)
- test/core/json_id_parsing_test.dart(新增 8 条回归用例)

其余 as num?(durationSec/unlockCoin/number/totalEpisodes/freeEpisodes/code)对应后端
Integer/int,仍是数字,未改动;未碰任何业务逻辑与 UI。
This commit is contained in:
Vicky committed 2026-10-16 16:44:02 +08:00
1 parent 61454e36b1
commit 94ff9f1177
4 files changed
+125 -3

No files matched your search

+103
View File
@@ -0,0 +1,103 @@
// Author: fruit.com.cn
import 'package:flutter_test/flutter_test.dart';
import 'package:fruitshorts/core/data/models/auth_session.dart';
import 'package:fruitshorts/core/data/models/drama.dart';
import 'package:fruitshorts/core/data/models/json_numbers.dart';
import 'package:fruitshorts/core/data/models/play_ticket.dart';
/// 后端契约(FruitServer/docs/api-phase1.md §0):`Long`/`long` 字段以字符串下发。
/// 这里钉住客户端在"收到字符串形式的 id / 计数"时照样能解析,不抛异常。
void main() {
group('jsonToInt', () {
test('should_parse_int_when_value_is_number', () {
expect(jsonToInt(12345), 12345);
expect(jsonToInt(12.9), 12);
});
test('should_parse_int_when_value_is_numeric_string', () {
expect(jsonToInt('2111012753262120962'), 2111012753262120962);
expect(jsonToInt(' 42 '), 42);
});
test('should_fall_back_when_value_is_missing_or_invalid', () {
expect(jsonToInt(null), 0);
expect(jsonToInt(''), 0);
expect(jsonToInt('abc'), 0);
expect(jsonToInt(null, fallback: 7), 7);
});
});
group('drama model', () {
test('should_parse_string_ids_when_backend_sends_ids_as_string', () {
final drama = Drama.fromCardJson({
'id': '2111012753262120962',
'title': '闪婚后,傅先生每天都在撒糖',
'totalEpisodes': 72,
'viewCount': '3100000',
'isHot': true,
});
expect(drama.id, '2111012753262120962');
expect(drama.title, '闪婚后,傅先生每天都在撒糖');
expect(drama.totalEpisodes, 72);
// viewCount 后端是 Long → 字符串
expect(drama.viewCount, 3100000);
});
test('should_parse_view_count_when_backend_sends_number', () {
final drama = Drama.fromCardJson({'id': 5, 'title': 'T', 'viewCount': 1200});
expect(drama.id, '5');
expect(drama.viewCount, 1200);
});
test('should_parse_detail_payload_when_ids_are_strings', () {
final drama = Drama.fromDetailJson({
'id': '2111012753262120962',
'title': 'T',
'totalEpisodes': 12,
'freeEpisodes': 3,
'viewCount': '99',
'episodes': [
{'id': '2111012753262120963', 'number': 1, 'title': 'E1', 'isFree': true},
],
});
expect(drama.id, '2111012753262120962');
expect(drama.freeEpisodes, 3);
expect(drama.viewCount, 99);
expect(drama.episodes.first.id, '2111012753262120963');
expect(drama.episodes.first.number, 1);
});
});
group('auth session', () {
test('should_parse_expires_in_when_backend_sends_string', () {
final session = AuthSession.fromJson({
'accessToken': 'a',
'refreshToken': 'r',
'expiresIn': '7200',
'user': {'uid': 'u-1', 'nickname': 'n'},
});
expect(session.expiresIn, 7200);
expect(session.user.uid, 'u-1');
});
});
group('play ticket', () {
test('should_parse_episode_id_when_backend_sends_string', () {
final ticket = PlayTicket.fromJson({
'episodeId': '2111012753262120963',
'accessible': false,
'accessType': PlayTicket.coinRequired,
'unlockCoin': 30,
});
expect(ticket.episodeId, '2111012753262120963');
expect(ticket.unlockCoin, 30);
expect(ticket.needsUnlock, isTrue);
});
});
}