修复成绩修正任务与表现评分项回填
修正任务强制读取服务端最新评分表,避免缓存中的旧状态误导进入流程;修正不可编辑时在首页给出明确提示。 表现裁判按稳定 itemCode 和 dimensionCode 映射五个评分维度,恢复已提交加减数量时不再依赖易变的展示名称;增加车轮跳表现裁判回填测试。
This commit is contained in:
parent
f0571b1f03
commit
5480427d65
@ -51,14 +51,17 @@ class JudgeApiRepository {
|
||||
}
|
||||
}
|
||||
|
||||
Future<ApiLoad<JudgeScoreSheet>> loadSheet(String sessionEntryId) async {
|
||||
Future<ApiLoad<JudgeScoreSheet>> 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);
|
||||
|
||||
@ -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 = <String, String>{
|
||||
'MUSIC': '音乐',
|
||||
'QUALITY': '完成质量',
|
||||
'ENTERTAINMENT': '娱乐',
|
||||
'CREATIVITY': '创意',
|
||||
'VARIETY': '多样性',
|
||||
};
|
||||
|
||||
/// Scoring item codes remain stable even when a rule version changes labels.
|
||||
static String? performanceDimension(
|
||||
Iterable<String> 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<String> keys,
|
||||
String source, {
|
||||
|
||||
@ -344,7 +344,7 @@ class _HomePageState extends State<HomePage> {
|
||||
(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<HomePage> {
|
||||
Get.offAllNamed(AppRoutes.login);
|
||||
}
|
||||
|
||||
Future<void> _openTask(int index, _RemoteTask remoteTask) async {
|
||||
Future<void> _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<HomePage> {
|
||||
task: _taskFor(indexedSession.$2, entry),
|
||||
sessionOrder: indexedSession.$1,
|
||||
),
|
||||
correction: true,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -254,6 +254,7 @@ class _ScoringPageState extends State<ScoringPage> with WidgetsBindingObserver {
|
||||
try {
|
||||
final refreshed = await Get.find<JudgeApiRepository>().loadSheet(
|
||||
selected.candidate.sessionEntryId,
|
||||
requireFresh: true,
|
||||
);
|
||||
if (!refreshed.data.isCorrection || !refreshed.data.canEdit) {
|
||||
Get.snackbar('任务暂不可修改', refreshed.data.lockReason ?? '请刷新后重试');
|
||||
|
||||
@ -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 = <String, int>{};
|
||||
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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user