更新 devtools_options.yaml 以启用 riverpod 和 shared_preferences 扩展;更新 AuthApi 枚举以添加 playerRegistrationList API;重构 Auth 页面以在用户已登录时直接导航到扫码页面;重构 EventInfoPage 以使用新的状态管理和加载赛事报名信息;更新扫码页面以导航到 EventInfoPage。

This commit is contained in:
2026-07-09 17:38:56 +08:00
parent 6138eddc16
commit 1c909277ff
11 changed files with 646 additions and 137 deletions
@@ -0,0 +1,356 @@
import 'package:recording_tool/features/recording/model/model_recording_context.dart';
class PlayerRegistrationListReq {
const PlayerRegistrationListReq({required this.userId, this.status = ''});
final String userId;
final String status;
Map<String, dynamic> toFormDataMap() {
return {'userId': userId, 'status': status};
}
}
class StreamKeyReq {
const StreamKeyReq({
required this.eventId,
required this.itemId,
required this.userId,
});
final String eventId;
final String itemId;
final String userId;
Map<String, dynamic> toFormDataMap() {
return {'eventId': eventId, 'itemId': itemId, 'userId': userId};
}
}
class EventProfile {
const EventProfile({
required this.name,
required this.phone,
required this.eventTitle,
this.avatarUrl = '',
this.avatarLabel = '头像',
});
final String name;
final String phone;
final String eventTitle;
final String avatarUrl;
final String avatarLabel;
}
class EventRegistrationList {
const EventRegistrationList({
required this.userId,
required this.name,
required this.avatar,
required this.total,
required this.items,
});
final String userId;
final String name;
final String avatar;
final int total;
final List<EventRegistrationItem> items;
factory EventRegistrationList.fromJson(dynamic json) {
final map = _extractObject(json);
final rawItems = _extractList(json);
final userId = _readString(map, const ['userId']);
final name = _readString(map, const [
'name',
'playerName',
'userName',
'realName',
]);
final avatar = _readString(map, const ['avatar', 'avatarUrl']);
return EventRegistrationList(
userId: userId,
name: name,
avatar: avatar,
total: _readInt(map, const ['total'], fallback: rawItems.length),
items: rawItems
.whereType<Map>()
.map(
(item) => EventRegistrationItem.fromJson(
item,
userId: userId,
playerName: name,
),
)
.toList(),
);
}
EventProfile toProfile() {
return EventProfile(
name: name,
phone: '',
eventTitle: items.isEmpty ? '赛事信息' : items.first.eventTitle,
avatarUrl: avatar,
);
}
}
class EventRegistrationItem {
const EventRegistrationItem({
required this.eventId,
required this.itemId,
required this.userId,
required this.eventTitle,
required this.name,
required this.group,
required this.venue,
required this.time,
required this.playerName,
required this.playerPhone,
this.avatarLabel = '头像',
this.matchStartTime = '',
this.matchEndTime = '',
this.completed = false,
this.laneNo,
this.status,
this.rawData = const {},
});
final String eventId;
final String itemId;
final String userId;
final String eventTitle;
final String name;
final String group;
final String venue;
final String time;
final String playerName;
final String playerPhone;
final String avatarLabel;
final String matchStartTime;
final String matchEndTime;
final bool completed;
final String? laneNo;
final String? status;
final Map<String, dynamic> rawData;
factory EventRegistrationItem.fromJson(
Map<dynamic, dynamic> json, {
String userId = '',
String playerName = '',
}) {
final map = Map<String, dynamic>.from(json);
final startTime = _readString(map, const ['matchStartTime', 'startTime']);
final endTime = _readString(map, const ['matchEndTime', 'endTime']);
final completed = _readBool(map, const ['completed']);
return EventRegistrationItem(
eventId: _readString(map, const ['eventId', 'eventsId', 'eventID']),
itemId: _readString(map, const ['itemId', 'eventItemId', 'itemID']),
userId: _readString(map, const [
'userId',
'playerId',
'registrationId',
], fallback: userId),
eventTitle: _readString(map, const [
'eventTitle',
'eventName',
'competitionName',
'matchTitle',
], fallback: '赛事信息'),
name: _readString(map, const [
'name',
'itemName',
'eventItemName',
'matchName',
'projectName',
], fallback: '未命名项目'),
group: _readString(map, const [
'group',
'groupName',
'categoryName',
'levelName',
]),
venue: _readString(map, const [
'matchPlace',
'venue',
'venueName',
'siteName',
'fieldName',
'placeName',
]),
time: _readScheduleTime(map, startTime, endTime),
playerName: _readString(map, const [
'playerName',
'userName',
'nameCn',
'realName',
'athleteName',
], fallback: playerName),
playerPhone: _readString(map, const [
'playerPhone',
'phone',
'mobile',
'telephone',
'userPhone',
]),
laneNo: _readNullableString(map, const [
'laneNo',
'laneNumber',
'trackNo',
'number',
'serialNo',
]),
matchStartTime: startTime,
matchEndTime: endTime,
completed: completed,
status: completed ? '已完成' : null,
rawData: map,
);
}
EventProfile toProfile() {
return EventProfile(
avatarLabel: avatarLabel,
name: playerName,
phone: playerPhone,
eventTitle: eventTitle,
);
}
RecordingContext toRecordingContext({EventProfile? profile}) {
return RecordingContext(
eventTitle: eventTitle,
matchName: name,
group: group,
venue: venue,
time: time,
playerName: profile?.name ?? playerName,
playerPhone: profile?.phone ?? playerPhone,
laneNo: laneNo,
status: status,
);
}
}
class StreamKeyResponse {
const StreamKeyResponse({required this.rawData});
final Map<String, dynamic> rawData;
factory StreamKeyResponse.fromJson(dynamic json) {
if (json is Map) {
return StreamKeyResponse(rawData: Map<String, dynamic>.from(json));
}
return StreamKeyResponse(rawData: {'value': json});
}
@override
String toString() => rawData.toString();
}
List<dynamic> _extractList(dynamic json) {
if (json is List) return json;
if (json is Map) {
for (final key in const ['records', 'items', 'rows', 'list', 'data']) {
final value = json[key];
if (value is List) return value;
if (value is Map) {
final nested = _extractList(value);
if (nested.isNotEmpty) return nested;
}
}
}
return const [];
}
Map<String, dynamic> _extractObject(dynamic json) {
if (json is Map) {
final map = Map<String, dynamic>.from(json);
final data = map['data'];
if (data is Map) return Map<String, dynamic>.from(data);
return map;
}
return const {};
}
String _readString(
Map<String, dynamic> map,
List<String> keys, {
String fallback = '',
}) {
return _readNullableString(map, keys) ?? fallback;
}
String _readScheduleTime(
Map<String, dynamic> map,
String startTime,
String endTime,
) {
if (startTime.isNotEmpty && endTime.isNotEmpty) {
return '$startTime-$endTime';
}
if (startTime.isNotEmpty) return startTime;
if (endTime.isNotEmpty) return endTime;
return _readString(map, const [
'time',
'competitionTime',
'matchTime',
'scheduleTime',
]);
}
int _readInt(Map<String, dynamic> map, List<String> keys, {int fallback = 0}) {
for (final key in keys) {
final value = _findValue(map, key);
if (value is int) return value;
if (value is num) return value.toInt();
if (value is String) {
final parsed = int.tryParse(value.trim());
if (parsed != null) return parsed;
}
}
return fallback;
}
bool _readBool(Map<String, dynamic> map, List<String> keys) {
for (final key in keys) {
final value = _findValue(map, key);
if (value is bool) return value;
if (value is num) return value != 0;
if (value is String) {
final text = value.trim().toLowerCase();
if (text == 'true' || text == '1') return true;
if (text == 'false' || text == '0') return false;
}
}
return false;
}
String? _readNullableString(Map<String, dynamic> map, List<String> keys) {
for (final key in keys) {
final value = _findValue(map, key);
if (value == null) continue;
final text = value.toString().trim();
if (text.isNotEmpty) return text;
}
return null;
}
dynamic _findValue(dynamic value, String key) {
if (value is Map) {
if (value.containsKey(key)) return value[key];
for (final child in value.values) {
final found = _findValue(child, key);
if (found != null) return found;
}
}
if (value is List) {
for (final child in value) {
final found = _findValue(child, key);
if (found != null) return found;
}
}
return null;
}