较新较热
This commit is contained in:
1 parent
2bf8030c37
commit
cfff0b8b06
3 files changed
+303
-88
No files matched your search
+10
-9
@@ -4,7 +4,7 @@ class AppStrings {
|
|||||||
// Tab
|
// Tab
|
||||||
static const tabHome = '主页';
|
static const tabHome = '主页';
|
||||||
static const tabTickets = '票据';
|
static const tabTickets = '票据';
|
||||||
static const tabTransfer = '传包';
|
static const tabTransfer = '发包';
|
||||||
static const tabSettings = '设置';
|
static const tabSettings = '设置';
|
||||||
static const tabAbout = '关于';
|
static const tabAbout = '关于';
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ class AppStrings {
|
|||||||
static String ticketCooldownNotice(int remaining) =>
|
static String ticketCooldownNotice(int remaining) =>
|
||||||
'登录后需冷却 $ticketCooldownSeconds 秒,剩余 $remaining 秒后可使用功能票。';
|
'登录后需冷却 $ticketCooldownSeconds 秒,剩余 $remaining 秒后可使用功能票。';
|
||||||
static String packetCooldownNotice(int remaining) =>
|
static String packetCooldownNotice(int remaining) =>
|
||||||
'登录后需冷却 $ticketCooldownSeconds 秒,剩余 $remaining 秒后可使用传包。';
|
'登录后需冷却 $ticketCooldownSeconds 秒,剩余 $remaining 秒后可发包。';
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
static const titleServerSettings = 'Title Server 设置';
|
static const titleServerSettings = 'Title Server 设置';
|
||||||
@@ -163,12 +163,12 @@ class AppStrings {
|
|||||||
static const importFailed = '导入失败:剪贴板内容无效';
|
static const importFailed = '导入失败:剪贴板内容无效';
|
||||||
static const noConfigToExport = '没有可导出的配置';
|
static const noConfigToExport = '没有可导出的配置';
|
||||||
|
|
||||||
// Transfer Package (传包)
|
// Transfer Package (发包)
|
||||||
static const transferTitle = '传包 (UpsertUserAll)';
|
static const transferTitle = '发包 (UpsertUserAll)';
|
||||||
static const transferDesc = '拉取用户数据后,可修改角色槽位、锁定槽位和角色等级,然后传包回服务器。';
|
static const transferDesc = '拉取用户数据后,可修改角色槽位、锁定槽位和角色等级,然后发包回服务器。';
|
||||||
static const transferFetchData = '拉取数据';
|
static const transferFetchData = '拉取数据';
|
||||||
static const transferFetching = '拉取中...';
|
static const transferFetching = '拉取中...';
|
||||||
static const transferNotLoggedIn = '尚未登录游戏服务器,无法传包。';
|
static const transferNotLoggedIn = '尚未登录游戏服务器,无法发包。';
|
||||||
static const transferCharaSlot = '角色槽位 (charaSlot)';
|
static const transferCharaSlot = '角色槽位 (charaSlot)';
|
||||||
static const transferCharaLockSlot = '锁定槽位 (charaLockSlot)';
|
static const transferCharaLockSlot = '锁定槽位 (charaLockSlot)';
|
||||||
static const transferCharacterLevels = '角色等级 (userCharacterList)';
|
static const transferCharacterLevels = '角色等级 (userCharacterList)';
|
||||||
@@ -176,10 +176,11 @@ class AppStrings {
|
|||||||
static const transferCharacterId = '角色ID';
|
static const transferCharacterId = '角色ID';
|
||||||
static const transferLevel = '等级';
|
static const transferLevel = '等级';
|
||||||
static const transferAwakening = '觉醒';
|
static const transferAwakening = '觉醒';
|
||||||
static const transferSend = '发送传包';
|
static const transferSend = '发包';
|
||||||
static const transferSending = '发送中...';
|
static const transferSending = '发送中...';
|
||||||
static const transferSuccess = '传包完成';
|
static const transferSuccess = '发包完成';
|
||||||
static const transferFailed = '传包失败';
|
static const transferFailed = '发包失败';
|
||||||
|
static const transferUploadingPlaylog = '上传游戏记录...';
|
||||||
static const transferSlotLabel = '槽位';
|
static const transferSlotLabel = '槽位';
|
||||||
static const transferNoData = '⚠️ 未拉取到用户数据,请先点「拉取数据」按钮获取。';
|
static const transferNoData = '⚠️ 未拉取到用户数据,请先点「拉取数据」按钮获取。';
|
||||||
static const transferDataLoaded = '数据已就绪,可在下方编辑后发送。';
|
static const transferDataLoaded = '数据已就绪,可在下方编辑后发送。';
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import '../models/upsert_user_all.dart';
|
|||||||
import '../services/title_api_service.dart';
|
import '../services/title_api_service.dart';
|
||||||
import 'transfer_advanced_page.dart';
|
import 'transfer_advanced_page.dart';
|
||||||
|
|
||||||
enum TransferStep { idle, fetching, ready, sending, done, failed }
|
enum TransferStep { idle, fetching, ready, uploadingPlaylog, sending, done, failed }
|
||||||
|
|
||||||
class TransferPackagePage extends StatefulWidget {
|
class TransferPackagePage extends StatefulWidget {
|
||||||
final int userId;
|
final int userId;
|
||||||
@@ -216,14 +216,24 @@ class _TransferPackagePageState extends State<TransferPackagePage> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Step 1: Upload fake playlog
|
||||||
setState(() {
|
setState(() {
|
||||||
_step = TransferStep.sending;
|
_step = TransferStep.uploadingPlaylog;
|
||||||
_error = null;
|
_error = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final config = TitleServerConfigHolder().config!;
|
final config = TitleServerConfigHolder().config!;
|
||||||
final service = TitleApiService(config, cookies: widget.cookies);
|
final service = TitleApiService(config, cookies: widget.cookies);
|
||||||
|
|
||||||
|
final playlog = _buildFakePlaylog();
|
||||||
|
await service.uploadUserPlaylog(playlog, widget.userId);
|
||||||
|
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
|
// Step 2: UpsertUserAll
|
||||||
|
setState(() => _step = TransferStep.sending);
|
||||||
|
|
||||||
final payload = _buildPayload();
|
final payload = _buildPayload();
|
||||||
await service.upsertUserAll(payload.toJson(), widget.userId);
|
await service.upsertUserAll(payload.toJson(), widget.userId);
|
||||||
|
|
||||||
@@ -244,6 +254,217 @@ class _TransferPackagePageState extends State<TransferPackagePage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── Fake game log (UploadUserPlaylog) ─────────────────────────────────
|
||||||
|
|
||||||
|
/// Look up level + awakening for a character ID from the editable list.
|
||||||
|
(int level, int awakening) _charLevel(int charId) {
|
||||||
|
for (final e in _charEntries) {
|
||||||
|
if (e.characterId == charId) return (e.level, e.awakening);
|
||||||
|
}
|
||||||
|
return (1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build a playlog entry with character levels from _charEntries.
|
||||||
|
Map<String, dynamic> _makePlaylogEntry({
|
||||||
|
required List<int> charaSlot,
|
||||||
|
required int musicId,
|
||||||
|
required int level,
|
||||||
|
required int trackNo,
|
||||||
|
required int achievement,
|
||||||
|
required int deluxscore,
|
||||||
|
required int scoreRank,
|
||||||
|
required int comboStatus,
|
||||||
|
required int maxCombo,
|
||||||
|
required int totalCombo,
|
||||||
|
required int maxSync,
|
||||||
|
required int totalSync,
|
||||||
|
required bool isClear,
|
||||||
|
required bool isAchieveNewRecord,
|
||||||
|
required bool isDeluxscoreNewRecord,
|
||||||
|
required int extNum4,
|
||||||
|
}) {
|
||||||
|
final rating = _apiRating;
|
||||||
|
final cfg = TitleServerConfigHolder().config;
|
||||||
|
final loginDt = widget.loginDateTime ??
|
||||||
|
DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||||
|
|
||||||
|
final c1 = _charLevel(charaSlot.isNotEmpty ? charaSlot[0] : 0);
|
||||||
|
final c2 = _charLevel(charaSlot.length > 1 ? charaSlot[1] : 0);
|
||||||
|
final c3 = _charLevel(charaSlot.length > 2 ? charaSlot[2] : 0);
|
||||||
|
final c4 = _charLevel(charaSlot.length > 3 ? charaSlot[3] : 0);
|
||||||
|
final c5 = _charLevel(charaSlot.length > 4 ? charaSlot[4] : 0);
|
||||||
|
|
||||||
|
return {
|
||||||
|
'userId': 0,
|
||||||
|
'orderId': 0,
|
||||||
|
'playlogId': widget.playlogId ?? 0,
|
||||||
|
'version': 1053000,
|
||||||
|
'placeId': cfg?.placeId ?? 0,
|
||||||
|
'placeName': cfg?.placeName ?? '',
|
||||||
|
'loginDate': loginDt,
|
||||||
|
'playDate': _nowStr().split(' ')[0],
|
||||||
|
'userPlayDate': _nowStr(),
|
||||||
|
'type': 0,
|
||||||
|
'musicId': musicId,
|
||||||
|
'level': level,
|
||||||
|
'trackNo': trackNo,
|
||||||
|
'vsMode': 0,
|
||||||
|
'vsUserName': '',
|
||||||
|
'vsStatus': 0,
|
||||||
|
'vsUserRating': 0,
|
||||||
|
'vsUserAchievement': 0,
|
||||||
|
'vsUserGradeRank': 0,
|
||||||
|
'vsRank': 0,
|
||||||
|
'playerNum': 1,
|
||||||
|
'playedUserId1': 0,
|
||||||
|
'playedUserName1': '',
|
||||||
|
'playedMusicLevel1': 0,
|
||||||
|
'playedUserId2': 0,
|
||||||
|
'playedUserName2': '',
|
||||||
|
'playedMusicLevel2': 0,
|
||||||
|
'playedUserId3': 0,
|
||||||
|
'playedUserName3': '',
|
||||||
|
'playedMusicLevel3': 0,
|
||||||
|
'characterId1': charaSlot.isNotEmpty ? charaSlot[0] : 0,
|
||||||
|
'characterLevel1': c1.$1,
|
||||||
|
'characterAwakening1': c1.$2,
|
||||||
|
'characterId2': charaSlot.length > 1 ? charaSlot[1] : 0,
|
||||||
|
'characterLevel2': c2.$1,
|
||||||
|
'characterAwakening2': c2.$2,
|
||||||
|
'characterId3': charaSlot.length > 2 ? charaSlot[2] : 0,
|
||||||
|
'characterLevel3': c3.$1,
|
||||||
|
'characterAwakening3': c3.$2,
|
||||||
|
'characterId4': charaSlot.length > 3 ? charaSlot[3] : 0,
|
||||||
|
'characterLevel4': c4.$1,
|
||||||
|
'characterAwakening4': c4.$2,
|
||||||
|
'characterId5': charaSlot.length > 4 ? charaSlot[4] : 0,
|
||||||
|
'characterLevel5': c5.$1,
|
||||||
|
'characterAwakening5': c5.$2,
|
||||||
|
'achievement': achievement,
|
||||||
|
'deluxscore': deluxscore,
|
||||||
|
'scoreRank': scoreRank,
|
||||||
|
'maxCombo': maxCombo,
|
||||||
|
'totalCombo': totalCombo,
|
||||||
|
'maxSync': maxSync,
|
||||||
|
'totalSync': totalSync,
|
||||||
|
'tapCriticalPerfect': 101,
|
||||||
|
'tapPerfect': 0,
|
||||||
|
'tapGreat': 0,
|
||||||
|
'tapGood': 0,
|
||||||
|
'tapMiss': 0,
|
||||||
|
'holdCriticalPerfect': 9,
|
||||||
|
'holdPerfect': 0,
|
||||||
|
'holdGreat': 0,
|
||||||
|
'holdGood': 0,
|
||||||
|
'holdMiss': 0,
|
||||||
|
'slideCriticalPerfect': 4,
|
||||||
|
'slidePerfect': 0,
|
||||||
|
'slideGreat': 0,
|
||||||
|
'slideGood': 0,
|
||||||
|
'slideMiss': 0,
|
||||||
|
'touchCriticalPerfect': 0,
|
||||||
|
'touchPerfect': 0,
|
||||||
|
'touchGreat': 0,
|
||||||
|
'touchGood': 0,
|
||||||
|
'touchMiss': 0,
|
||||||
|
'breakCriticalPerfect': 1,
|
||||||
|
'breakPerfect': 0,
|
||||||
|
'breakGreat': 0,
|
||||||
|
'breakGood': 0,
|
||||||
|
'breakMiss': 0,
|
||||||
|
'isTap': true,
|
||||||
|
'isHold': true,
|
||||||
|
'isSlide': true,
|
||||||
|
'isTouch': false,
|
||||||
|
'isBreak': true,
|
||||||
|
'isCriticalDisp': true,
|
||||||
|
'isFastLateDisp': true,
|
||||||
|
'fastCount': 0,
|
||||||
|
'lateCount': 0,
|
||||||
|
'isAchieveNewRecord': isAchieveNewRecord,
|
||||||
|
'isDeluxscoreNewRecord': isDeluxscoreNewRecord,
|
||||||
|
'comboStatus': comboStatus,
|
||||||
|
'syncStatus': 0,
|
||||||
|
'isClear': isClear,
|
||||||
|
'beforeRating': rating,
|
||||||
|
'afterRating': rating,
|
||||||
|
'beforeGrade': 0,
|
||||||
|
'afterGrade': 0,
|
||||||
|
'afterGradeRank': 0,
|
||||||
|
'beforeDeluxRating': rating,
|
||||||
|
'afterDeluxRating': rating,
|
||||||
|
'isPlayTutorial': false,
|
||||||
|
'isEventMode': false,
|
||||||
|
'isFreedomMode': false,
|
||||||
|
'playMode': 0,
|
||||||
|
'isNewFree': false,
|
||||||
|
'trialPlayAchievement': -1,
|
||||||
|
'extNum1': 0,
|
||||||
|
'extNum2': 0,
|
||||||
|
'extNum4': extNum4,
|
||||||
|
'extBool1': false,
|
||||||
|
'extBool2': false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build musicDetail entry matching a playlog.
|
||||||
|
Map<String, dynamic> _makeMusicDetail({
|
||||||
|
required int musicId,
|
||||||
|
required int level,
|
||||||
|
required int playCount,
|
||||||
|
required int achievement,
|
||||||
|
required int comboStatus,
|
||||||
|
required int syncStatus,
|
||||||
|
required int deluxscoreMax,
|
||||||
|
required int scoreRank,
|
||||||
|
}) {
|
||||||
|
return {
|
||||||
|
'musicId': musicId,
|
||||||
|
'level': level,
|
||||||
|
'playCount': playCount,
|
||||||
|
'achievement': achievement,
|
||||||
|
'comboStatus': comboStatus,
|
||||||
|
'syncStatus': syncStatus,
|
||||||
|
'deluxscoreMax': deluxscoreMax,
|
||||||
|
'scoreRank': scoreRank,
|
||||||
|
'extNum1': 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
int get _apiRating {
|
||||||
|
final data = _apiData;
|
||||||
|
if (data == null) return 0;
|
||||||
|
final userDataJson = data['GetUserDataApi']!;
|
||||||
|
final ud =
|
||||||
|
(userDataJson['userData'] as Map<String, dynamic>?) ?? const {};
|
||||||
|
return ud['playerRating'] as int? ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> _buildFakePlaylog() {
|
||||||
|
final charaSlot =
|
||||||
|
_slotCtrls.map((c) => int.tryParse(c.text) ?? 0).toList();
|
||||||
|
return _makePlaylogEntry(
|
||||||
|
charaSlot: charaSlot,
|
||||||
|
musicId: 834,
|
||||||
|
level: 4,
|
||||||
|
trackNo: 1,
|
||||||
|
achievement: 1000000,
|
||||||
|
deluxscore: 0,
|
||||||
|
scoreRank: 13,
|
||||||
|
comboStatus: 3,
|
||||||
|
maxCombo: 0,
|
||||||
|
totalCombo: 128,
|
||||||
|
maxSync: 0,
|
||||||
|
totalSync: 0,
|
||||||
|
isClear: true,
|
||||||
|
isAchieveNewRecord: true,
|
||||||
|
isDeluxscoreNewRecord: true,
|
||||||
|
extNum4: 101,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Build UpsertUserAll payload ──────────────────────────────────────
|
||||||
|
|
||||||
UpsertUserAllPayload _buildPayload() {
|
UpsertUserAllPayload _buildPayload() {
|
||||||
final data = _apiData!;
|
final data = _apiData!;
|
||||||
final loginDt = widget.loginDateTime ?? DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
final loginDt = widget.loginDateTime ?? DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||||
@@ -293,27 +514,29 @@ class _TransferPackagePageState extends State<TransferPackagePage> {
|
|||||||
final cfg = TitleServerConfigHolder().config;
|
final cfg = TitleServerConfigHolder().config;
|
||||||
final clientId = ud['lastClientId'] ?? cfg?.clientId ?? '';
|
final clientId = ud['lastClientId'] ?? cfg?.clientId ?? '';
|
||||||
|
|
||||||
// Build musicData for userMusicDetailList — must NOT be empty.
|
const musicId = 834;
|
||||||
// Matches the playlog entry below (same musicId/level/achievement/…).
|
const level = 4;
|
||||||
final musicData = <String, dynamic>{
|
const achievement = 1000000;
|
||||||
'musicId': 834,
|
const comboStatus = 3;
|
||||||
'level': 4,
|
const deluxscore = 0;
|
||||||
'playCount': 1,
|
const scoreRank = 13;
|
||||||
'achievement': 1000000,
|
|
||||||
'comboStatus': 3,
|
// Build musicDetailList + gamePlaylog + outer playlog (all for 1 track).
|
||||||
'syncStatus': 0,
|
final musicDetail = _makeMusicDetail(
|
||||||
'deluxscoreMax': 0,
|
musicId: musicId,
|
||||||
'scoreRank': 13,
|
level: level,
|
||||||
'extNum1': 0,
|
playCount: 1,
|
||||||
};
|
achievement: achievement,
|
||||||
|
comboStatus: comboStatus,
|
||||||
|
syncStatus: 0,
|
||||||
|
deluxscoreMax: deluxscore,
|
||||||
|
scoreRank: scoreRank,
|
||||||
|
);
|
||||||
|
|
||||||
final upsertUserAll = {
|
final upsertUserAll = {
|
||||||
'userData': [
|
'userData': [
|
||||||
{
|
{
|
||||||
...ud,
|
...ud,
|
||||||
'playCount': (ud['playCount'] as int? ?? 0) + 1,
|
|
||||||
'currentPlayCount':
|
|
||||||
(ud['currentPlayCount'] as int? ?? 0) + 1,
|
|
||||||
'lastLoginDate':
|
'lastLoginDate':
|
||||||
widget.lastLoginDate ?? ud['lastLoginDate'] ?? '',
|
widget.lastLoginDate ?? ud['lastLoginDate'] ?? '',
|
||||||
'banState': userDataJson['banState'] ?? ud['banState'] ?? 0,
|
'banState': userDataJson['banState'] ?? ud['banState'] ?? 0,
|
||||||
@@ -323,7 +546,6 @@ class _TransferPackagePageState extends State<TransferPackagePage> {
|
|||||||
'lastRomVersion': ud['lastRomVersion'] ?? '1.53.00',
|
'lastRomVersion': ud['lastRomVersion'] ?? '1.53.00',
|
||||||
'lastDataVersion': ud['lastDataVersion'] ?? '1.50.14',
|
'lastDataVersion': ud['lastDataVersion'] ?? '1.50.14',
|
||||||
'lastPlayDate': _nowStr(),
|
'lastPlayDate': _nowStr(),
|
||||||
'lastPlayCredit': 1,
|
|
||||||
'lastPlayMode': 0,
|
'lastPlayMode': 0,
|
||||||
'lastPlaceId': cfg?.placeId ?? ud['lastPlaceId'] ?? 0,
|
'lastPlaceId': cfg?.placeId ?? ud['lastPlaceId'] ?? 0,
|
||||||
'lastPlaceName':
|
'lastPlaceName':
|
||||||
@@ -334,7 +556,7 @@ class _TransferPackagePageState extends State<TransferPackagePage> {
|
|||||||
'lastAllNetId': ud['lastAllNetId'] ?? 0,
|
'lastAllNetId': ud['lastAllNetId'] ?? 0,
|
||||||
'lastCountryCode': ud['lastCountryCode'] ?? 'CHN',
|
'lastCountryCode': ud['lastCountryCode'] ?? 'CHN',
|
||||||
'lastClientId': clientId,
|
'lastClientId': clientId,
|
||||||
'dateTime': loginDt,
|
'dateTime': DateTime.now().millisecondsSinceEpoch ~/ 1000,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'userExtend': [ue],
|
'userExtend': [ue],
|
||||||
@@ -346,7 +568,7 @@ class _TransferPackagePageState extends State<TransferPackagePage> {
|
|||||||
'userLoginBonusList': _advanced.parsedUserLoginBonusList('[]'),
|
'userLoginBonusList': _advanced.parsedUserLoginBonusList('[]'),
|
||||||
'userRatingList': [ur],
|
'userRatingList': [ur],
|
||||||
'userItemList': _advanced.parsedUserItemList('[]'),
|
'userItemList': _advanced.parsedUserItemList('[]'),
|
||||||
'userMusicDetailList': [musicData],
|
'userMusicDetailList': [musicDetail],
|
||||||
'userCourseList': <dynamic>[],
|
'userCourseList': <dynamic>[],
|
||||||
'userFriendSeasonRankingList': <dynamic>[],
|
'userFriendSeasonRankingList': <dynamic>[],
|
||||||
'userChargeList': uc,
|
'userChargeList': uc,
|
||||||
@@ -409,58 +631,25 @@ class _TransferPackagePageState extends State<TransferPackagePage> {
|
|||||||
'isNewKaleidxScopeList': '',
|
'isNewKaleidxScopeList': '',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Minimal playlog entry — required, server rejects empty playlogList.
|
// Outer playlog matching the fake game log.
|
||||||
final rating = ud['playerRating'] as int? ?? 0;
|
final playlog = _makePlaylogEntry(
|
||||||
final playlog = <String, dynamic>{
|
charaSlot: charaSlot,
|
||||||
'userId': 0, 'orderId': 0,
|
musicId: musicId,
|
||||||
'playlogId': widget.playlogId ?? 0, 'version': 1053000,
|
level: level,
|
||||||
'placeId': cfg?.placeId ?? 0,
|
trackNo: 1,
|
||||||
'placeName': cfg?.placeName ?? '',
|
achievement: achievement,
|
||||||
'loginDate': loginDt,
|
deluxscore: deluxscore,
|
||||||
'playDate': _nowStr().split(' ')[0],
|
scoreRank: scoreRank,
|
||||||
'userPlayDate': _nowStr(), 'type': 0,
|
comboStatus: comboStatus,
|
||||||
'musicId': 834, 'level': 4, 'trackNo': 1,
|
maxCombo: 0,
|
||||||
'vsMode': 0, 'vsUserName': '', 'vsStatus': 0,
|
totalCombo: 128,
|
||||||
'vsUserRating': 0, 'vsUserAchievement': 0, 'vsUserGradeRank': 0,
|
maxSync: 0,
|
||||||
'vsRank': 0, 'playerNum': 1,
|
totalSync: 0,
|
||||||
'playedUserId1': 0, 'playedUserName1': '', 'playedMusicLevel1': 0,
|
isClear: true,
|
||||||
'playedUserId2': 0, 'playedUserName2': '', 'playedMusicLevel2': 0,
|
isAchieveNewRecord: true,
|
||||||
'playedUserId3': 0, 'playedUserName3': '', 'playedMusicLevel3': 0,
|
isDeluxscoreNewRecord: true,
|
||||||
'characterId1': charaSlot.isNotEmpty ? charaSlot[0] : 0,
|
extNum4: 101,
|
||||||
'characterLevel1': 1, 'characterAwakening1': 0,
|
);
|
||||||
'characterId2': charaSlot.length > 1 ? charaSlot[1] : 0,
|
|
||||||
'characterLevel2': 1, 'characterAwakening2': 0,
|
|
||||||
'characterId3': charaSlot.length > 2 ? charaSlot[2] : 0,
|
|
||||||
'characterLevel3': 1, 'characterAwakening3': 0,
|
|
||||||
'characterId4': charaSlot.length > 3 ? charaSlot[3] : 0,
|
|
||||||
'characterLevel4': 1, 'characterAwakening4': 0,
|
|
||||||
'characterId5': charaSlot.length > 4 ? charaSlot[4] : 0,
|
|
||||||
'characterLevel5': 1, 'characterAwakening5': 0,
|
|
||||||
'achievement': 1000000, 'deluxscore': 0, 'scoreRank': 13,
|
|
||||||
'maxCombo': 0, 'totalCombo': 128, 'maxSync': 0, 'totalSync': 0,
|
|
||||||
'tapCriticalPerfect': 101, 'tapPerfect': 0, 'tapGreat': 0,
|
|
||||||
'tapGood': 0, 'tapMiss': 0,
|
|
||||||
'holdCriticalPerfect': 9, 'holdPerfect': 0, 'holdGreat': 0,
|
|
||||||
'holdGood': 0, 'holdMiss': 0,
|
|
||||||
'slideCriticalPerfect': 4, 'slidePerfect': 0, 'slideGreat': 0,
|
|
||||||
'slideGood': 0, 'slideMiss': 0,
|
|
||||||
'touchCriticalPerfect': 0, 'touchPerfect': 0, 'touchGreat': 0,
|
|
||||||
'touchGood': 0, 'touchMiss': 0,
|
|
||||||
'breakCriticalPerfect': 1, 'breakPerfect': 0, 'breakGreat': 0,
|
|
||||||
'breakGood': 0, 'breakMiss': 0,
|
|
||||||
'isTap': true, 'isHold': true, 'isSlide': true, 'isTouch': false,
|
|
||||||
'isBreak': true, 'isCriticalDisp': true, 'isFastLateDisp': true,
|
|
||||||
'fastCount': 0, 'lateCount': 0,
|
|
||||||
'isAchieveNewRecord': false, 'isDeluxscoreNewRecord': false,
|
|
||||||
'comboStatus': 3, 'syncStatus': 0, 'isClear': true,
|
|
||||||
'beforeRating': rating, 'afterRating': rating,
|
|
||||||
'beforeGrade': 0, 'afterGrade': 0, 'afterGradeRank': 0,
|
|
||||||
'beforeDeluxRating': rating, 'afterDeluxRating': rating,
|
|
||||||
'isPlayTutorial': false, 'isEventMode': false, 'isFreedomMode': false,
|
|
||||||
'playMode': 0, 'isNewFree': false, 'trialPlayAchievement': -1,
|
|
||||||
'extNum1': 0, 'extNum2': 0, 'extNum4': 101,
|
|
||||||
'extBool1': false, 'extBool2': false,
|
|
||||||
};
|
|
||||||
|
|
||||||
return UpsertUserAllPayload(
|
return UpsertUserAllPayload(
|
||||||
userId: widget.userId,
|
userId: widget.userId,
|
||||||
@@ -713,7 +902,9 @@ class _TransferPackagePageState extends State<TransferPackagePage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
if (_step == TransferStep.fetching || _step == TransferStep.sending)
|
if (_step == TransferStep.fetching ||
|
||||||
|
_step == TransferStep.sending ||
|
||||||
|
_step == TransferStep.uploadingPlaylog)
|
||||||
_buildProgress(theme),
|
_buildProgress(theme),
|
||||||
if (_error != null && _apiData == null) ...[
|
if (_error != null && _apiData == null) ...[
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
@@ -828,7 +1019,8 @@ class _TransferPackagePageState extends State<TransferPackagePage> {
|
|||||||
Widget _buildFetchButton(ThemeData theme) {
|
Widget _buildFetchButton(ThemeData theme) {
|
||||||
final hasLogin = widget.loginDateTime != null;
|
final hasLogin = widget.loginDateTime != null;
|
||||||
final isLoading = _step == TransferStep.fetching;
|
final isLoading = _step == TransferStep.fetching;
|
||||||
final isSending = _step == TransferStep.sending;
|
final isSending = _step == TransferStep.sending ||
|
||||||
|
_step == TransferStep.uploadingPlaylog;
|
||||||
final canFetch = hasLogin && !isLoading && !isSending;
|
final canFetch = hasLogin && !isLoading && !isSending;
|
||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
@@ -1190,6 +1382,14 @@ class _TransferPackagePageState extends State<TransferPackagePage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildProgress(ThemeData theme) {
|
Widget _buildProgress(ThemeData theme) {
|
||||||
|
String text;
|
||||||
|
if (_step == TransferStep.fetching) {
|
||||||
|
text = AppStrings.transferFetching;
|
||||||
|
} else if (_step == TransferStep.uploadingPlaylog) {
|
||||||
|
text = AppStrings.transferUploadingPlaylog;
|
||||||
|
} else {
|
||||||
|
text = AppStrings.transferSending;
|
||||||
|
}
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
child: Center(
|
child: Center(
|
||||||
@@ -1198,9 +1398,7 @@ class _TransferPackagePageState extends State<TransferPackagePage> {
|
|||||||
const CircularProgressIndicator(),
|
const CircularProgressIndicator(),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
Text(
|
Text(
|
||||||
_step == TransferStep.fetching
|
text,
|
||||||
? AppStrings.transferFetching
|
|
||||||
: AppStrings.transferSending,
|
|
||||||
style: theme.textTheme.bodyMedium?.copyWith(
|
style: theme.textTheme.bodyMedium?.copyWith(
|
||||||
color: theme.colorScheme.onSurfaceVariant,
|
color: theme.colorScheme.onSurfaceVariant,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -511,7 +511,23 @@ class TitleApiService {
|
|||||||
'${dt.hour.toString().padLeft(2, '0')}:${dt.minute.toString().padLeft(2, '0')}:${dt.second.toString().padLeft(2, '0')}.0';
|
'${dt.hour.toString().padLeft(2, '0')}:${dt.minute.toString().padLeft(2, '0')}:${dt.second.toString().padLeft(2, '0')}.0';
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- UpsertUserAll (传包) ----
|
// ---- UpsertUserAll (发包) ----
|
||||||
|
|
||||||
|
/// Upload a single playlog record (fake game log).
|
||||||
|
Future<void> uploadUserPlaylog(
|
||||||
|
Map<String, dynamic> userPlaylog,
|
||||||
|
int userId,
|
||||||
|
) async {
|
||||||
|
const apiName = 'UploadUserPlaylogApi';
|
||||||
|
final json = await _callApi(apiName, {
|
||||||
|
'userId': userId,
|
||||||
|
'userPlaylog': userPlaylog,
|
||||||
|
}, userId);
|
||||||
|
final returnCode = json['returnCode'] as int? ?? -1;
|
||||||
|
if (returnCode != 1) {
|
||||||
|
throw TitleApiException('UploadUserPlaylogApi returnCode=$returnCode');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Fetch all user data needed to build an UpsertUserAll payload.
|
/// Fetch all user data needed to build an UpsertUserAll payload.
|
||||||
/// Returns a map keyed by the raw API names (e.g. 'GetUserDataApi').
|
/// Returns a map keyed by the raw API names (e.g. 'GetUserDataApi').
|
||||||
|
|||||||
Reference in new issue
Block a user