From 5480427d653ae331ab5d47d9807142e4846b7e1f Mon Sep 17 00:00:00 2001 From: ltx <1006158920@qq.com> Date: Mon, 12 Oct 2026 16:43:14 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=88=90=E7=BB=A9=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=E4=BB=BB=E5=8A=A1=E4=B8=8E=E8=A1=A8=E7=8E=B0=E8=AF=84?= =?UTF-8?q?=E5=88=86=E9=A1=B9=E5=9B=9E=E5=A1=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修正任务强制读取服务端最新评分表,避免缓存中的旧状态误导进入流程;修正不可编辑时在首页给出明确提示。 表现裁判按稳定 itemCode 和 dimensionCode 映射五个评分维度,恢复已提交加减数量时不再依赖易变的展示名称;增加车轮跳表现裁判回填测试。 --- lib/core/network/judge_api_repository.dart | 7 +++-- lib/core/network/score_item_key_matcher.dart | 24 ++++++++++++++++ lib/modules/home/views/home_page.dart | 18 ++++++++++-- .../controllers/scoring_controller.dart | 7 ++++- lib/modules/scoring/views/scoring_page.dart | 1 + test/score_item_key_matcher_test.dart | 28 +++++++++++++++++++ 6 files changed, 79 insertions(+), 6 deletions(-) diff --git a/lib/core/network/judge_api_repository.dart b/lib/core/network/judge_api_repository.dart index 588b323..1f5946d 100644 --- a/lib/core/network/judge_api_repository.dart +++ b/lib/core/network/judge_api_repository.dart @@ -51,14 +51,17 @@ class JudgeApiRepository { } } - Future> loadSheet(String sessionEntryId) async { + Future> loadSheet( + String sessionEntryId, { + bool requireFresh = false, + }) async { final key = _key('sheet-$sessionEntryId'); try { final data = await _client.getSheet(sessionEntryId); await _putJson(key, data.toJson()); return ApiLoad(data, fromCache: false); } on ApiException catch (error) { - if (error.isUnauthorized) rethrow; + if (error.isUnauthorized || requireFresh) rethrow; final source = _storage.settings.get(key) as String?; if (source != null) { return ApiLoad(JudgeScoreSheet.decode(source), fromCache: true); diff --git a/lib/core/network/score_item_key_matcher.dart b/lib/core/network/score_item_key_matcher.dart index 7bd055c..36f98bf 100644 --- a/lib/core/network/score_item_key_matcher.dart +++ b/lib/core/network/score_item_key_matcher.dart @@ -1,6 +1,30 @@ /// Maps server labels to the existing scoring controls without allowing a /// shorter label (e.g. 违例) to shadow a more specific one (e.g. 空间违例). abstract final class ScoreItemKeyMatcher { + static const _performanceDimensions = { + 'MUSIC': '音乐', + 'QUALITY': '完成质量', + 'ENTERTAINMENT': '娱乐', + 'CREATIVITY': '创意', + 'VARIETY': '多样性', + }; + + /// Scoring item codes remain stable even when a rule version changes labels. + static String? performanceDimension( + Iterable keys, { + String? dimensionCode, + required String itemCode, + required String label, + }) { + final code = (dimensionCode?.trim().isNotEmpty == true + ? dimensionCode!.trim() + : itemCode.split('_').first) + .toUpperCase(); + final key = _performanceDimensions[code]; + if (key != null && keys.contains(key)) return key; + return match(keys, label); + } + static String? match( Iterable keys, String source, { diff --git a/lib/modules/home/views/home_page.dart b/lib/modules/home/views/home_page.dart index 413def2..9a37631 100644 --- a/lib/modules/home/views/home_page.dart +++ b/lib/modules/home/views/home_page.dart @@ -344,7 +344,7 @@ class _HomePageState extends State { (item) => item.entry.sessionEntryId == entryId, ); if (index >= 0) { - await _openTask(index, _remoteTasks[index]); + await _openTask(index, _remoteTasks[index], correction: true); } else { await _openCorrectionTask(entryId); } @@ -420,12 +420,23 @@ class _HomePageState extends State { Get.offAllNamed(AppRoutes.login); } - Future _openTask(int index, _RemoteTask remoteTask) async { + Future _openTask( + int index, + _RemoteTask remoteTask, { + bool correction = false, + }) async { setState(() => _openingEntryId = remoteTask.entry.sessionEntryId); try { - final sheetResult = await _api.loadSheet(remoteTask.entry.sessionEntryId); + final sheetResult = await _api.loadSheet( + remoteTask.entry.sessionEntryId, + requireFresh: correction || remoteTask.entry.scoreStatus == 3, + ); final session = _auth.session!; final sheet = sheetResult.data; + if (correction && (!sheet.isCorrection || !sheet.canEdit)) { + Get.snackbar('修正任务暂不可用', sheet.lockReason ?? '请刷新任务后重试'); + return; + } final assignmentChanged = !RemoteScoringAdapter.sheetRoleMatchesSession( sheet, remoteTask.session, @@ -522,6 +533,7 @@ class _HomePageState extends State { task: _taskFor(indexedSession.$2, entry), sessionOrder: indexedSession.$1, ), + correction: true, ); return; } diff --git a/lib/modules/scoring/controllers/scoring_controller.dart b/lib/modules/scoring/controllers/scoring_controller.dart index bee8a20..3bcb29b 100644 --- a/lib/modules/scoring/controllers/scoring_controller.dart +++ b/lib/modules/scoring/controllers/scoring_controller.dart @@ -408,7 +408,12 @@ class ScoringController extends GetxController { if (profile.role == ScoreRole.presentation) { final subject = '${item.subjectName ?? ''}${item.dimensionCode ?? ''}${item.name}'; - final key = _matchingKey(performanceItems.keys, subject); + final key = ScoreItemKeyMatcher.performanceDimension( + performanceItems.keys, + dimensionCode: item.dimensionCode, + itemCode: item.code, + label: subject, + ); if (item.isFailureCount) { performanceErrorCount.value = count; } else if (key != null && item.adjustSign > 0) { diff --git a/lib/modules/scoring/views/scoring_page.dart b/lib/modules/scoring/views/scoring_page.dart index 3d98199..2f5b703 100644 --- a/lib/modules/scoring/views/scoring_page.dart +++ b/lib/modules/scoring/views/scoring_page.dart @@ -254,6 +254,7 @@ class _ScoringPageState extends State with WidgetsBindingObserver { try { final refreshed = await Get.find().loadSheet( selected.candidate.sessionEntryId, + requireFresh: true, ); if (!refreshed.data.isCorrection || !refreshed.data.canEdit) { Get.snackbar('任务暂不可修改', refreshed.data.lockReason ?? '请刷新后重试'); diff --git a/test/score_item_key_matcher_test.dart b/test/score_item_key_matcher_test.dart index 121699e..ce22db0 100644 --- a/test/score_item_key_matcher_test.dart +++ b/test/score_item_key_matcher_test.dart @@ -6,6 +6,34 @@ import 'package:yd_pattern_scoring_system/core/models/prescribed_execution_rule. import 'package:yd_pattern_scoring_system/core/network/score_item_key_matcher.dart'; void main() { + test('车轮跳表现裁判修正时按稳定编码回填已提交的五项加减数量', () { + const dimensions = ['音乐', '完成质量', '娱乐', '创意', '多样性']; + const codes = { + 'MUSIC_PLUS': '音乐', + 'MUSIC_MINUS': '音乐', + 'QUALITY_PLUS': '完成质量', + 'QUALITY_MINUS': '完成质量', + 'ENTERTAINMENT_PLUS': '娱乐', + 'ENTERTAINMENT_MINUS': '娱乐', + 'CREATIVITY_PLUS': '创意', + 'CREATIVITY_MINUS': '创意', + 'VARIETY_PLUS': '多样性', + 'VARIETY_MINUS': '多样性', + }; + final restored = {}; + for (final entry in codes.entries) { + final dimension = ScoreItemKeyMatcher.performanceDimension( + dimensions, + itemCode: entry.key, + label: '历史评分项', + ); + expect(dimension, entry.value); + restored[entry.key] = dimension == null ? 0 : 2; + } + expect(restored.length, 10); + expect(restored.values, everyElement(2)); + }); + test('规定赛完成质量项按稳定编码匹配平板控件', () { const keys = ['动作准确性', '动作熟练性', '动作合拍性']; const aliases = PrescribedExecutionRuleCatalog.serverItemAliases;