较新较热
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
|
||||
static const tabHome = '主页';
|
||||
static const tabTickets = '票据';
|
||||
static const tabTransfer = '传包';
|
||||
static const tabTransfer = '发包';
|
||||
static const tabSettings = '设置';
|
||||
static const tabAbout = '关于';
|
||||
|
||||
@@ -118,7 +118,7 @@ class AppStrings {
|
||||
static String ticketCooldownNotice(int remaining) =>
|
||||
'登录后需冷却 $ticketCooldownSeconds 秒,剩余 $remaining 秒后可使用功能票。';
|
||||
static String packetCooldownNotice(int remaining) =>
|
||||
'登录后需冷却 $ticketCooldownSeconds 秒,剩余 $remaining 秒后可使用传包。';
|
||||
'登录后需冷却 $ticketCooldownSeconds 秒,剩余 $remaining 秒后可发包。';
|
||||
|
||||
// Settings
|
||||
static const titleServerSettings = 'Title Server 设置';
|
||||
@@ -163,12 +163,12 @@ class AppStrings {
|
||||
static const importFailed = '导入失败:剪贴板内容无效';
|
||||
static const noConfigToExport = '没有可导出的配置';
|
||||
|
||||
// Transfer Package (传包)
|
||||
static const transferTitle = '传包 (UpsertUserAll)';
|
||||
static const transferDesc = '拉取用户数据后,可修改角色槽位、锁定槽位和角色等级,然后传包回服务器。';
|
||||
// Transfer Package (发包)
|
||||
static const transferTitle = '发包 (UpsertUserAll)';
|
||||
static const transferDesc = '拉取用户数据后,可修改角色槽位、锁定槽位和角色等级,然后发包回服务器。';
|
||||
static const transferFetchData = '拉取数据';
|
||||
static const transferFetching = '拉取中...';
|
||||
static const transferNotLoggedIn = '尚未登录游戏服务器,无法传包。';
|
||||
static const transferNotLoggedIn = '尚未登录游戏服务器,无法发包。';
|
||||
static const transferCharaSlot = '角色槽位 (charaSlot)';
|
||||
static const transferCharaLockSlot = '锁定槽位 (charaLockSlot)';
|
||||
static const transferCharacterLevels = '角色等级 (userCharacterList)';
|
||||
@@ -176,10 +176,11 @@ class AppStrings {
|
||||
static const transferCharacterId = '角色ID';
|
||||
static const transferLevel = '等级';
|
||||
static const transferAwakening = '觉醒';
|
||||
static const transferSend = '发送传包';
|
||||
static const transferSend = '发包';
|
||||
static const transferSending = '发送中...';
|
||||
static const transferSuccess = '传包完成';
|
||||
static const transferFailed = '传包失败';
|
||||
static const transferSuccess = '发包完成';
|
||||
static const transferFailed = '发包失败';
|
||||
static const transferUploadingPlaylog = '上传游戏记录...';
|
||||
static const transferSlotLabel = '槽位';
|
||||
static const transferNoData = '⚠️ 未拉取到用户数据,请先点「拉取数据」按钮获取。';
|
||||
static const transferDataLoaded = '数据已就绪,可在下方编辑后发送。';
|
||||
|
||||
@@ -9,7 +9,7 @@ import '../models/upsert_user_all.dart';
|
||||
import '../services/title_api_service.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 {
|
||||
final int userId;
|
||||
@@ -216,14 +216,24 @@ class _TransferPackagePageState extends State<TransferPackagePage> {
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 1: Upload fake playlog
|
||||
setState(() {
|
||||
_step = TransferStep.sending;
|
||||
_step = TransferStep.uploadingPlaylog;
|
||||
_error = null;
|
||||
});
|
||||
|
||||
try {
|
||||
final config = TitleServerConfigHolder().config!;
|
||||
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();
|
||||
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() {
|
||||
final data = _apiData!;
|
||||
final loginDt = widget.loginDateTime ?? DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||
@@ -293,27 +514,29 @@ class _TransferPackagePageState extends State<TransferPackagePage> {
|
||||
final cfg = TitleServerConfigHolder().config;
|
||||
final clientId = ud['lastClientId'] ?? cfg?.clientId ?? '';
|
||||
|
||||
// Build musicData for userMusicDetailList — must NOT be empty.
|
||||
// Matches the playlog entry below (same musicId/level/achievement/…).
|
||||
final musicData = <String, dynamic>{
|
||||
'musicId': 834,
|
||||
'level': 4,
|
||||
'playCount': 1,
|
||||
'achievement': 1000000,
|
||||
'comboStatus': 3,
|
||||
'syncStatus': 0,
|
||||
'deluxscoreMax': 0,
|
||||
'scoreRank': 13,
|
||||
'extNum1': 0,
|
||||
};
|
||||
const musicId = 834;
|
||||
const level = 4;
|
||||
const achievement = 1000000;
|
||||
const comboStatus = 3;
|
||||
const deluxscore = 0;
|
||||
const scoreRank = 13;
|
||||
|
||||
// Build musicDetailList + gamePlaylog + outer playlog (all for 1 track).
|
||||
final musicDetail = _makeMusicDetail(
|
||||
musicId: musicId,
|
||||
level: level,
|
||||
playCount: 1,
|
||||
achievement: achievement,
|
||||
comboStatus: comboStatus,
|
||||
syncStatus: 0,
|
||||
deluxscoreMax: deluxscore,
|
||||
scoreRank: scoreRank,
|
||||
);
|
||||
|
||||
final upsertUserAll = {
|
||||
'userData': [
|
||||
{
|
||||
...ud,
|
||||
'playCount': (ud['playCount'] as int? ?? 0) + 1,
|
||||
'currentPlayCount':
|
||||
(ud['currentPlayCount'] as int? ?? 0) + 1,
|
||||
'lastLoginDate':
|
||||
widget.lastLoginDate ?? ud['lastLoginDate'] ?? '',
|
||||
'banState': userDataJson['banState'] ?? ud['banState'] ?? 0,
|
||||
@@ -323,7 +546,6 @@ class _TransferPackagePageState extends State<TransferPackagePage> {
|
||||
'lastRomVersion': ud['lastRomVersion'] ?? '1.53.00',
|
||||
'lastDataVersion': ud['lastDataVersion'] ?? '1.50.14',
|
||||
'lastPlayDate': _nowStr(),
|
||||
'lastPlayCredit': 1,
|
||||
'lastPlayMode': 0,
|
||||
'lastPlaceId': cfg?.placeId ?? ud['lastPlaceId'] ?? 0,
|
||||
'lastPlaceName':
|
||||
@@ -334,7 +556,7 @@ class _TransferPackagePageState extends State<TransferPackagePage> {
|
||||
'lastAllNetId': ud['lastAllNetId'] ?? 0,
|
||||
'lastCountryCode': ud['lastCountryCode'] ?? 'CHN',
|
||||
'lastClientId': clientId,
|
||||
'dateTime': loginDt,
|
||||
'dateTime': DateTime.now().millisecondsSinceEpoch ~/ 1000,
|
||||
},
|
||||
],
|
||||
'userExtend': [ue],
|
||||
@@ -346,7 +568,7 @@ class _TransferPackagePageState extends State<TransferPackagePage> {
|
||||
'userLoginBonusList': _advanced.parsedUserLoginBonusList('[]'),
|
||||
'userRatingList': [ur],
|
||||
'userItemList': _advanced.parsedUserItemList('[]'),
|
||||
'userMusicDetailList': [musicData],
|
||||
'userMusicDetailList': [musicDetail],
|
||||
'userCourseList': <dynamic>[],
|
||||
'userFriendSeasonRankingList': <dynamic>[],
|
||||
'userChargeList': uc,
|
||||
@@ -409,58 +631,25 @@ class _TransferPackagePageState extends State<TransferPackagePage> {
|
||||
'isNewKaleidxScopeList': '',
|
||||
};
|
||||
|
||||
// Minimal playlog entry — required, server rejects empty playlogList.
|
||||
final rating = ud['playerRating'] as int? ?? 0;
|
||||
final playlog = <String, dynamic>{
|
||||
'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': 834, 'level': 4, 'trackNo': 1,
|
||||
'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': 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,
|
||||
};
|
||||
// Outer playlog matching the fake game log.
|
||||
final playlog = _makePlaylogEntry(
|
||||
charaSlot: charaSlot,
|
||||
musicId: musicId,
|
||||
level: level,
|
||||
trackNo: 1,
|
||||
achievement: achievement,
|
||||
deluxscore: deluxscore,
|
||||
scoreRank: scoreRank,
|
||||
comboStatus: comboStatus,
|
||||
maxCombo: 0,
|
||||
totalCombo: 128,
|
||||
maxSync: 0,
|
||||
totalSync: 0,
|
||||
isClear: true,
|
||||
isAchieveNewRecord: true,
|
||||
isDeluxscoreNewRecord: true,
|
||||
extNum4: 101,
|
||||
);
|
||||
|
||||
return UpsertUserAllPayload(
|
||||
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),
|
||||
if (_error != null && _apiData == null) ...[
|
||||
const SizedBox(height: 12),
|
||||
@@ -828,7 +1019,8 @@ class _TransferPackagePageState extends State<TransferPackagePage> {
|
||||
Widget _buildFetchButton(ThemeData theme) {
|
||||
final hasLogin = widget.loginDateTime != null;
|
||||
final isLoading = _step == TransferStep.fetching;
|
||||
final isSending = _step == TransferStep.sending;
|
||||
final isSending = _step == TransferStep.sending ||
|
||||
_step == TransferStep.uploadingPlaylog;
|
||||
final canFetch = hasLogin && !isLoading && !isSending;
|
||||
|
||||
return SizedBox(
|
||||
@@ -1190,6 +1382,14 @@ class _TransferPackagePageState extends State<TransferPackagePage> {
|
||||
}
|
||||
|
||||
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(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
child: Center(
|
||||
@@ -1198,9 +1398,7 @@ class _TransferPackagePageState extends State<TransferPackagePage> {
|
||||
const CircularProgressIndicator(),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
_step == TransferStep.fetching
|
||||
? AppStrings.transferFetching
|
||||
: AppStrings.transferSending,
|
||||
text,
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
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';
|
||||
}
|
||||
|
||||
// ---- 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.
|
||||
/// Returns a map keyed by the raw API names (e.g. 'GetUserDataApi').
|
||||
|
||||
Reference in new issue
Block a user