167 lines
5.5 KiB
JavaScript
167 lines
5.5 KiB
JavaScript
const { API } = require('./constants');
|
||
const { request } = require('./http');
|
||
const { normalizeCheckinProgress } = require('./checkin-progress');
|
||
|
||
function fetchScoreEvents() {
|
||
return request({ path: API.scoreEvents, method: 'GET', needAuth: false });
|
||
}
|
||
|
||
function fetchScoreFilters(eventId) {
|
||
return request({ path: API.scoreFilters, method: 'GET', data: { eventId }, needAuth: false });
|
||
}
|
||
|
||
function queryScores({ eventId, projectId, groupName, keyword } = {}) {
|
||
return request({ path: API.scoreQuery, method: 'GET', data: { eventId, projectId, groupName, keyword }, needAuth: false });
|
||
}
|
||
|
||
function isEmptyValue(value) {
|
||
return value === undefined || value === null || value === '';
|
||
}
|
||
|
||
// 参数校验一律以 reject 返回,不能同步 throw:
|
||
// 调用方普遍写成 fn().then().catch().finally(),同步抛出会绕过 catch/finally,
|
||
// 造成 loading 遮罩不消失、按钮 loading 锁死。
|
||
function rejectWith(message) {
|
||
return Promise.reject(new Error(message));
|
||
}
|
||
|
||
function withProjectScope(data, eventProjectId) {
|
||
if (!isEmptyValue(eventProjectId)) data.eventProjectId = eventProjectId;
|
||
return data;
|
||
}
|
||
|
||
function fetchCurrentCheckin({ eventProjectId } = {}) {
|
||
return request({ path: API.checkinContext, method: 'GET', data: withProjectScope({}, eventProjectId), needAuth: true });
|
||
}
|
||
|
||
function updateCheckinStatus({ unitKey, eventProjectId, checked }) {
|
||
return request({ path: API.checkinStatus, method: 'POST', data: withProjectScope({ unitKey, checked }, eventProjectId), needAuth: true });
|
||
}
|
||
|
||
function startCheckin({ eventProjectId } = {}) {
|
||
if (isEmptyValue(eventProjectId)) return rejectWith('请选择检录项目');
|
||
return request({
|
||
path: API.checkinStart,
|
||
method: 'POST',
|
||
data: { eventProjectId },
|
||
needAuth: true
|
||
});
|
||
}
|
||
|
||
function scanCheckin({ code, eventProjectId } = {}) {
|
||
if (isEmptyValue(code)) return rejectWith('未识别到有效检录码');
|
||
const normalizedCode = String(code).trim();
|
||
if (!normalizedCode) return rejectWith('未识别到有效检录码');
|
||
return request({
|
||
path: API.checkinScan,
|
||
method: 'POST',
|
||
data: withProjectScope({ code: normalizedCode }, eventProjectId),
|
||
needAuth: true
|
||
});
|
||
}
|
||
|
||
function markCheckinOnStage({ unitKey, eventProjectId } = {}) {
|
||
if (isEmptyValue(unitKey)) return rejectWith('缺少参赛单位标识');
|
||
return request({
|
||
path: API.checkinOnStage,
|
||
method: 'POST',
|
||
data: withProjectScope({ unitKey }, eventProjectId),
|
||
needAuth: true
|
||
});
|
||
}
|
||
|
||
function fetchCheckinProgress(eventId) {
|
||
if (isEmptyValue(eventId)) return rejectWith('缺少赛事ID');
|
||
return request({ path: API.checkinProgress, method: 'GET', data: { eventId }, needAuth: true })
|
||
.then(normalizeCheckinProgress);
|
||
}
|
||
|
||
function fetchRefereeProfileOptions() {
|
||
return request({ path: API.refereeProfileOptions, method: 'GET', needAuth: true })
|
||
.then((res) => (res && typeof res === 'object' ? res : {}));
|
||
}
|
||
|
||
function fetchRefereeProfile() {
|
||
// 裁判未提交资料时后端返回 data:null,统一归一为空对象,避免调用方读字段时抛错
|
||
return request({ path: API.refereeProfile, method: 'GET', needAuth: true })
|
||
.then((res) => (res && typeof res === 'object' ? res : {}));
|
||
}
|
||
|
||
function resolveRefereeRegistrationCode(code) {
|
||
if (isEmptyValue(code)) return rejectWith('裁判报名码无效');
|
||
return request({ path: API.refereeRegistrationCode(String(code).trim()), method: 'GET', needAuth: false });
|
||
}
|
||
|
||
// 裁判角色是 [Flags] 位枚举(1-主裁 2-表现 4-难度 8-创意编排 16-完成质量)。
|
||
// 接口文档在 RefereeProfileSubmitInput/RefereeProfileDto 两处误写为 int 数组,
|
||
// 后端实际按单个 RefereeRole 反序列化,因此提交前合并为位标志,回显时再拆开。
|
||
function combineRefereeRoles(roles) {
|
||
const list = Array.isArray(roles) ? roles : [roles];
|
||
return list.reduce((mask, item) => {
|
||
const value = Number(item);
|
||
return Number.isFinite(value) && value > 0 ? mask | value : mask;
|
||
}, 0);
|
||
}
|
||
|
||
function splitRefereeRoles(value) {
|
||
if (Array.isArray(value)) return value.map(Number).filter((item) => Number.isFinite(item) && item > 0);
|
||
const mask = Number(value);
|
||
if (!Number.isFinite(mask) || mask <= 0) return [];
|
||
const result = [];
|
||
for (let bit = 1; bit <= mask; bit *= 2) {
|
||
if (mask & bit) result.push(bit);
|
||
}
|
||
return result;
|
||
}
|
||
|
||
function submitRefereeProfile(payload) {
|
||
const data = Object.assign({}, payload || {});
|
||
data.roles = combineRefereeRoles(data.roles);
|
||
return request({
|
||
path: API.refereeProfileSubmit,
|
||
method: 'POST',
|
||
data,
|
||
needAuth: true
|
||
});
|
||
}
|
||
|
||
function fetchRefereeQuota(eventId) {
|
||
if (isEmptyValue(eventId)) return rejectWith('缺少赛事ID');
|
||
return request({ path: API.refereeApplicationQuota, method: 'GET', data: { eventId }, needAuth: true });
|
||
}
|
||
|
||
function fetchRefereeApplications() {
|
||
return request({ path: API.refereeApplications, method: 'GET', needAuth: true });
|
||
}
|
||
|
||
function applyForReferee(event) {
|
||
const payload = event && typeof event === 'object' ? event : {};
|
||
if (isEmptyValue(payload.eventId)) return rejectWith('缺少赛事ID');
|
||
return request({
|
||
path: API.refereeApply,
|
||
method: 'POST',
|
||
data: { eventId: payload.eventId },
|
||
needAuth: true
|
||
});
|
||
}
|
||
|
||
module.exports = {
|
||
fetchScoreEvents,
|
||
fetchScoreFilters,
|
||
queryScores,
|
||
fetchCurrentCheckin,
|
||
updateCheckinStatus,
|
||
startCheckin,
|
||
scanCheckin,
|
||
markCheckinOnStage,
|
||
fetchCheckinProgress,
|
||
fetchRefereeProfileOptions,
|
||
fetchRefereeProfile,
|
||
resolveRefereeRegistrationCode,
|
||
submitRefereeProfile,
|
||
splitRefereeRoles,
|
||
fetchRefereeQuota,
|
||
fetchRefereeApplications,
|
||
applyForReferee
|
||
};
|