diff --git a/lib/core/network/remote_scoring_adapter.dart b/lib/core/network/remote_scoring_adapter.dart index a49752c..f923890 100644 --- a/lib/core/network/remote_scoring_adapter.dart +++ b/lib/core/network/remote_scoring_adapter.dart @@ -66,6 +66,19 @@ abstract final class RemoteScoringAdapter { sheet.header.judgeRole.trim().toUpperCase() == session.judgeRole.trim().toUpperCase(); + /// Returns the business session number shown on the judge task card. + /// + /// [JudgeEntry.playOrder] is the entry order inside an EventSession. The + /// arrangement order identifies the first business session represented by + /// that EventSession, so both values are needed for a stable display number. + static int sessionDisplayOrder(JudgeSession session, JudgeEntry entry) { + final playOrder = entry.playOrder > 0 ? entry.playOrder : 1; + final arrangementOrder = session.arrangementOrder; + return arrangementOrder > 0 && arrangementOrder < 0x7fffffff + ? arrangementOrder + playOrder - 1 + : playOrder; + } + static JudgeAccount accountForSession( JudgeSession session, { required String userId, diff --git a/lib/modules/home/views/home_page.dart b/lib/modules/home/views/home_page.dart index 9a37631..1f9dfd8 100644 --- a/lib/modules/home/views/home_page.dart +++ b/lib/modules/home/views/home_page.dart @@ -44,7 +44,6 @@ class _HomePageState extends State { late final Worker _syncStatusWorker; Timer? _messageTimer; int _unreadMessages = 0; - int _correctionTaskCount = 0; @override void initState() { @@ -56,9 +55,6 @@ class _HomePageState extends State { _loadUnreadMessages(); _messageTimer = Timer.periodic(const Duration(seconds: 30), (_) { _loadUnreadMessages(); - if (_allSessions.isNotEmpty) { - _loadCorrectionTaskCount(_allSessions); - } }); } @@ -122,7 +118,6 @@ class _HomePageState extends State { sessionsResult.fromCache || batches.any((item) => item.fromCache); _loading = false; }); - unawaited(_loadCorrectionTaskCount(sessionsResult.data)); } on ApiException catch (error) { if (!mounted) return; setState(() { @@ -172,9 +167,7 @@ class _HomePageState extends State { userName: userName, roleSummary: roleSummary, unreadMessages: _unreadMessages, - correctionTaskCount: _correctionTaskCount, onMessages: _openMessages, - onCorrections: _openCorrections, onResults: chiefSessions.isNotEmpty ? _openResults : null, onLogout: _logout, ), @@ -209,6 +202,11 @@ class _HomePageState extends State { padding: const EdgeInsets.only(bottom: 16), child: _TaskCard( task: entry.$2, + sessionDisplayOrder: RemoteScoringAdapter + .sessionDisplayOrder( + _remoteTasks[entry.$1].session, + _remoteTasks[entry.$1].entry, + ), roleLabel: RemoteScoringAdapter.roleLabelForSession( _remoteTasks[entry.$1].session, @@ -332,41 +330,6 @@ class _HomePageState extends State { if (mounted) setState(() {}); } - Future _openCorrections() async { - final entryId = await Get.toNamed( - AppRoutes.corrections, - arguments: _sessions, - ); - if (!mounted) return; - await _loadTasks(); - if (!mounted || entryId is! String) return; - final index = _remoteTasks.indexWhere( - (item) => item.entry.sessionEntryId == entryId, - ); - if (index >= 0) { - await _openTask(index, _remoteTasks[index], correction: true); - } else { - await _openCorrectionTask(entryId); - } - } - - Future _loadCorrectionTaskCount(List sessions) async { - try { - final batches = await Future.wait( - sessions.map( - (session) => _api.loadCorrectionCandidates(session.sessionId), - ), - ); - final count = batches - .expand((items) => items) - .where((item) => item.isAssignedTask) - .length; - if (mounted) setState(() => _correctionTaskCount = count); - } on ApiException { - // Badge refresh failure must not block the scoring task list. - } - } - Future _openResults() async { await Get.toNamed(AppRoutes.results, arguments: _chiefSessions); } @@ -696,9 +659,7 @@ class _TopBar extends StatelessWidget { required this.userName, required this.roleSummary, required this.unreadMessages, - required this.correctionTaskCount, required this.onMessages, - required this.onCorrections, required this.onResults, required this.onLogout, }); @@ -707,9 +668,7 @@ class _TopBar extends StatelessWidget { final String userName; final String roleSummary; final int unreadMessages; - final int correctionTaskCount; final VoidCallback onMessages; - final VoidCallback onCorrections; final VoidCallback? onResults; final VoidCallback onLogout; @@ -762,19 +721,7 @@ class _TopBar extends StatelessWidget { ), ), const SizedBox(width: 18), - Badge( - isLabelVisible: correctionTaskCount > 0, - label: Text( - correctionTaskCount > 99 ? '99+' : '$correctionTaskCount', - ), - child: IconButton.outlined( - onPressed: onCorrections, - tooltip: '成绩修正任务', - icon: const Icon(Icons.edit_note_rounded, size: 21), - ), - ), if (onResults != null) ...[ - const SizedBox(width: 8), IconButton.outlined( onPressed: onResults, tooltip: '场次成绩', @@ -944,6 +891,7 @@ class _PageHeading extends StatelessWidget { class _TaskCard extends StatelessWidget { const _TaskCard({ required this.task, + required this.sessionDisplayOrder, required this.roleLabel, required this.status, required this.isCurrent, @@ -951,6 +899,7 @@ class _TaskCard extends StatelessWidget { required this.onTap, }); final ScoringTask task; + final int sessionDisplayOrder; final String roleLabel; final String status; final bool isCurrent; @@ -1050,7 +999,7 @@ class _TaskCard extends StatelessWidget { ), const SizedBox(height: 3), Text( - '#${task.displayOrder}', + '#${sessionDisplayOrder.toString().padLeft(2, '0')}', style: TextStyle( color: color, fontSize: 14, diff --git a/test/remote_scoring_adapter_test.dart b/test/remote_scoring_adapter_test.dart index cf2e957..7bbdcf4 100644 --- a/test/remote_scoring_adapter_test.dart +++ b/test/remote_scoring_adapter_test.dart @@ -220,6 +220,63 @@ void main() { expect(task.session, isNot(contains(task.project))); expect(task.session, isNot(contains(task.groupName))); }); + + test('任务卡场次号由编排起始场次和出场顺序计算', () { + final firstSession = _session( + id: 'session-1', + role: 'DJ1', + roleType: 'DJ', + arrangementOrder: 1, + ); + final secondSession = _session( + id: 'session-2', + role: 'DJ1', + roleType: 'DJ', + arrangementOrder: 2, + ); + const firstEntry = JudgeEntry( + sessionEntryId: 'entry-1', + entryNo: '01', + playOrder: 1, + participantName: '测试队', + groupName: '青年组', + entryStatus: 0, + isRematch: false, + scoreStatus: 0, + scoreStatusName: '待评分', + isDisqualified: false, + scoreSheetId: '', + ); + const secondEntryInSameSession = JudgeEntry( + sessionEntryId: 'entry-2', + entryNo: '02', + playOrder: 2, + participantName: '测试队', + groupName: '青年组', + entryStatus: 0, + isRematch: false, + scoreStatus: 0, + scoreStatusName: '待评分', + isDisqualified: false, + scoreSheetId: '', + ); + + expect( + RemoteScoringAdapter.sessionDisplayOrder(firstSession, firstEntry), + 1, + ); + expect( + RemoteScoringAdapter.sessionDisplayOrder(secondSession, firstEntry), + 2, + ); + expect( + RemoteScoringAdapter.sessionDisplayOrder( + firstSession, + secondEntryInSameSession, + ), + 2, + ); + }); } JudgeSession _session({ @@ -227,6 +284,7 @@ JudgeSession _session({ required String role, required String roleType, String roleName = '', + int arrangementOrder = 0, }) => JudgeSession( sessionId: id, eventId: 'event-1', @@ -244,6 +302,7 @@ JudgeSession _session({ judgeRoleName: roleName, entryCount: 2, scoredCount: 0, + arrangementOrder: arrangementOrder, ); JudgeScoreSheet _sheet({