- 新增参赛者、裁判、检录员角色识别及角色化首页与个人中心 - 新增裁判注册、身份认证提示、赛事执裁报名及报名记录查询 - 新增检录工作台,支持检录任务、轮次状态与运动员检录操作 - 新增赛事成绩查询页面及成绩详情展示 - 封装成绩、检录和裁判报名接口,补充请求参数清理逻辑 - 新增检录 Tab 图标、页面路由及本地开发配置 - 新增 Git 忽略规则,排除依赖、构建缓存、日志和本地工具文件
443 lines
10 KiB
JavaScript
443 lines
10 KiB
JavaScript
const { API, getBaseUrl } = require('./constants');
|
||
const { request } = require('./http');
|
||
|
||
/**
|
||
* 上传文件到 /api/file/upload,返回文件 URL 字符串
|
||
*/
|
||
function uploadFile(filePath) {
|
||
return new Promise((resolve, reject) => {
|
||
const app = getApp();
|
||
const baseUrl = getBaseUrl();
|
||
const accessToken =
|
||
(app && app.globalData && app.globalData.accessToken) ||
|
||
wx.getStorageSync('accessToken') ||
|
||
'';
|
||
|
||
wx.uploadFile({
|
||
url: baseUrl + API.fileUpload,
|
||
filePath,
|
||
name: 'file',
|
||
header: accessToken ? { Authorization: `Bearer ${accessToken}` } : {},
|
||
success(res) {
|
||
try {
|
||
const body = typeof res.data === 'string' ? JSON.parse(res.data) : res.data;
|
||
// 接口返回数组 [{name, url}] 或 RESTfulResult 包装
|
||
let url = '';
|
||
if (Array.isArray(body) && body[0] && body[0].url) {
|
||
url = body[0].url;
|
||
} else if (body && body.data && Array.isArray(body.data) && body.data[0]) {
|
||
url = body.data[0].url || '';
|
||
} else if (body && body.data && typeof body.data === 'string') {
|
||
url = body.data;
|
||
}
|
||
if (url) resolve(url);
|
||
else reject(new Error('上传失败:未获取到文件URL'));
|
||
} catch (e) {
|
||
reject(new Error('上传响应解析失败'));
|
||
}
|
||
},
|
||
fail(err) {
|
||
reject(new Error((err && err.errMsg) || '文件上传失败'));
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
/**
|
||
* OCR 识别身份证,返回 IdCardRecognitionResult
|
||
* { success, name, gender, idCardNumber, birthDate }
|
||
*/
|
||
async function ocrIdCard(imageUrl) {
|
||
return request({
|
||
path: API.ocrIdCard,
|
||
method: 'POST',
|
||
needAuth: true,
|
||
data: { imageUrl },
|
||
contentType: 'application/json'
|
||
});
|
||
}
|
||
|
||
/**
|
||
* OCR 识别护照,返回 PassportRecognitionResult
|
||
* { success, name, gender, passportNumber, nationality, birthDate, issueDate, expiryDate, ... }
|
||
*/
|
||
async function ocrPassport(imageUrl) {
|
||
return request({
|
||
path: API.ocrPassport,
|
||
method: 'POST',
|
||
needAuth: true,
|
||
data: { imageUrl },
|
||
contentType: 'application/json'
|
||
});
|
||
}
|
||
|
||
/**
|
||
* OCR 识别户口本,返回 HouseholdRecognitionResult
|
||
* { success, name, gender, birthDate, idCardNumber, relation, ethnicity, ... }
|
||
*/
|
||
async function ocrHousehold(imageUrl) {
|
||
return request({
|
||
path: API.ocrHousehold,
|
||
method: 'POST',
|
||
needAuth: true,
|
||
data: { imageUrl },
|
||
contentType: 'application/json'
|
||
});
|
||
}
|
||
|
||
function pickString(obj, key) {
|
||
return obj && typeof obj[key] === 'string' ? obj[key] : '';
|
||
}
|
||
|
||
let refreshPromise = null;
|
||
|
||
async function refreshAccessToken() {
|
||
if (refreshPromise) return refreshPromise;
|
||
|
||
const app = getApp();
|
||
const storedRefreshToken = wx.getStorageSync('refreshToken') || '';
|
||
const globalRefreshToken = app && app.globalData ? app.globalData.refreshToken : '';
|
||
const refreshToken = typeof globalRefreshToken === 'string' && globalRefreshToken.length > 0 ? globalRefreshToken : storedRefreshToken;
|
||
|
||
if (!refreshToken) throw new Error('refreshAccessToken: missing refreshToken');
|
||
|
||
refreshPromise = (async () => {
|
||
const tokenData = await request({
|
||
path: API.oauthRefreshToken,
|
||
method: 'POST',
|
||
needAuth: false,
|
||
data: { refreshToken },
|
||
contentType: 'application/json'
|
||
});
|
||
|
||
const accessToken = pickString(tokenData, 'accessToken');
|
||
const nextRefreshToken = pickString(tokenData, 'refreshToken') || refreshToken;
|
||
|
||
if (!accessToken) throw new Error('refreshAccessToken: missing accessToken');
|
||
|
||
app.setAuth({
|
||
accessToken,
|
||
refreshToken: nextRefreshToken
|
||
});
|
||
|
||
return {
|
||
accessToken,
|
||
refreshToken: nextRefreshToken,
|
||
tokenData
|
||
};
|
||
})().finally(() => {
|
||
refreshPromise = null;
|
||
});
|
||
|
||
return refreshPromise;
|
||
}
|
||
|
||
async function loginWithMobilePassword({ mobile, password }) {
|
||
const loginData = await request({
|
||
path: API.wechatLogin,
|
||
method: 'POST',
|
||
needAuth: false,
|
||
data: {
|
||
mobile,
|
||
password: password != null ? password : ''
|
||
},
|
||
contentType: 'application/json'
|
||
});
|
||
|
||
const app = getApp();
|
||
const accessToken = pickString(loginData, 'accessToken');
|
||
const refreshToken = pickString(loginData, 'refreshToken');
|
||
const userInfo =
|
||
loginData && loginData.userInfo && typeof loginData.userInfo === 'object'
|
||
? loginData.userInfo
|
||
: null;
|
||
const openid = userInfo ? pickString(userInfo, 'openid') : '';
|
||
|
||
app.setAuth({
|
||
accessToken,
|
||
refreshToken,
|
||
openid,
|
||
userProfile: userInfo
|
||
});
|
||
|
||
let myProfile = null;
|
||
try {
|
||
myProfile = await request({
|
||
path: API.myProfile,
|
||
method: 'GET',
|
||
needAuth: true
|
||
});
|
||
} catch (e) {
|
||
myProfile = null;
|
||
}
|
||
|
||
if (myProfile && typeof myProfile === 'object') {
|
||
app.setAuth({
|
||
userProfile: myProfile
|
||
});
|
||
}
|
||
|
||
return {
|
||
loginData,
|
||
myProfile
|
||
};
|
||
}
|
||
|
||
async function loginWithMobileCode({ mobile, verificationCode }) {
|
||
const loginData = await request({
|
||
path: API.loginByCode,
|
||
method: 'POST',
|
||
needAuth: false,
|
||
data: {
|
||
mobile,
|
||
verificationCode
|
||
},
|
||
contentType: 'application/json'
|
||
});
|
||
|
||
const app = getApp();
|
||
const accessToken = pickString(loginData, 'accessToken');
|
||
const refreshToken = pickString(loginData, 'refreshToken');
|
||
const userInfo =
|
||
loginData && loginData.userInfo && typeof loginData.userInfo === 'object'
|
||
? loginData.userInfo
|
||
: null;
|
||
const openid = userInfo ? pickString(userInfo, 'openid') : '';
|
||
|
||
app.setAuth({
|
||
accessToken,
|
||
refreshToken,
|
||
openid,
|
||
userProfile: userInfo
|
||
});
|
||
|
||
let myProfile = null;
|
||
try {
|
||
myProfile = await request({
|
||
path: API.myProfile,
|
||
method: 'GET',
|
||
needAuth: true
|
||
});
|
||
} catch (e) {
|
||
myProfile = null;
|
||
}
|
||
|
||
if (myProfile && typeof myProfile === 'object') {
|
||
app.setAuth({
|
||
userProfile: myProfile
|
||
});
|
||
}
|
||
|
||
return {
|
||
loginData,
|
||
myProfile
|
||
};
|
||
}
|
||
|
||
async function sendVerificationCode(phone) {
|
||
return request({
|
||
path: API.sendVerificationCode,
|
||
method: 'POST',
|
||
needAuth: false,
|
||
data: { phone },
|
||
contentType: 'application/json'
|
||
});
|
||
}
|
||
|
||
async function registerWithVerification({ mobile, password, verificationCode, nickName, avatar, role }) {
|
||
const data = {
|
||
mobile,
|
||
password,
|
||
verificationCode
|
||
};
|
||
if (nickName) data.nickName = nickName;
|
||
if (avatar) data.avatar = avatar;
|
||
if (role) {
|
||
// 同时保留语义值与数值枚举,联调时可按后端契约删去其中一个字段。
|
||
const roleTypeMap = { participant: 1, referee: 2 };
|
||
data.role = role;
|
||
data.roleType = roleTypeMap[role] || 1;
|
||
}
|
||
|
||
return request({
|
||
path: API.oauthRegister,
|
||
method: 'POST',
|
||
needAuth: false,
|
||
data,
|
||
contentType: 'application/json'
|
||
});
|
||
}
|
||
|
||
async function resetPassword({ mobile, password, verificationCode }) {
|
||
return request({
|
||
path: API.oauthResetPassword,
|
||
method: 'POST',
|
||
needAuth: false,
|
||
data: { mobile, newPassword: password, verificationCode },
|
||
contentType: 'application/json'
|
||
});
|
||
}
|
||
|
||
async function fetchMyProfile() {
|
||
return request({
|
||
path: API.myProfile,
|
||
method: 'GET',
|
||
needAuth: true
|
||
});
|
||
}
|
||
|
||
async function updateProfile(data) {
|
||
return request({
|
||
path: API.updateProfile,
|
||
method: 'POST',
|
||
needAuth: true,
|
||
data: data,
|
||
contentType: 'application/json'
|
||
});
|
||
}
|
||
|
||
async function fetchMyAuth() {
|
||
return request({
|
||
path: API.myAuth,
|
||
method: 'GET',
|
||
needAuth: true
|
||
});
|
||
}
|
||
|
||
async function submitMyAuth(payload) {
|
||
// 兼容旧调用方式:submitMyAuth({ name, mobile, idCardNumber, idCardImageUrl, idCardBackImageUrl })
|
||
// 新页面会直接把表单对象透传进来(如 authType、passport 字段等),后端若不识别会忽略。
|
||
const p = payload && typeof payload === 'object' ? payload : {};
|
||
|
||
return request({
|
||
path: API.myAuth,
|
||
method: 'POST',
|
||
needAuth: true,
|
||
data: p,
|
||
contentType: 'application/json'
|
||
});
|
||
}
|
||
|
||
async function fetchMyRegistrations({ pageNo = 1, pageSize = 10 } = {}) {
|
||
return request({
|
||
path: API.myRegistrations,
|
||
method: 'GET',
|
||
needAuth: true,
|
||
data: {
|
||
PageNo: pageNo,
|
||
PageSize: pageSize
|
||
}
|
||
});
|
||
}
|
||
|
||
async function fetchMyPoints({
|
||
pageNo = 1,
|
||
pageSize = 10,
|
||
// 新接口参数(按 swagger):1=获取,2=消耗,不传查全部
|
||
type,
|
||
// 单位认证ID筛选,不传查全部
|
||
merchantCertificationId,
|
||
// 兼容旧调用方:也可能传 Type / MerchantCertificationId
|
||
Type,
|
||
MerchantCertificationId
|
||
} = {}) {
|
||
const t = type != null ? type : Type;
|
||
const mcId = merchantCertificationId != null ? merchantCertificationId : MerchantCertificationId;
|
||
|
||
const data = {
|
||
PageNo: pageNo,
|
||
PageSize: pageSize
|
||
};
|
||
if (t != null && t !== '') data.Type = t;
|
||
if (mcId != null && mcId !== '') data.MerchantCertificationId = mcId;
|
||
|
||
return request({
|
||
path: API.myPoints,
|
||
method: 'GET',
|
||
needAuth: true,
|
||
data
|
||
});
|
||
}
|
||
|
||
async function fetchMyTeams({ pageNo = 1, pageSize = 20 } = {}) {
|
||
return request({
|
||
path: API.myTeams,
|
||
method: 'GET',
|
||
needAuth: true,
|
||
data: {
|
||
PageNo: pageNo,
|
||
PageSize: pageSize
|
||
}
|
||
});
|
||
}
|
||
|
||
async function fetchMyTeamDetail(id) {
|
||
if (id == null || id === '') throw new Error('缺少团队ID');
|
||
return request({
|
||
path: `${API.myTeams}/${id}`,
|
||
method: 'GET',
|
||
needAuth: true
|
||
});
|
||
}
|
||
|
||
async function fetchMyOrders({ pageNo = 1, pageSize = 10 } = {}) {
|
||
return request({
|
||
path: API.myOrders,
|
||
method: 'GET',
|
||
needAuth: true,
|
||
data: {
|
||
PageNo: pageNo,
|
||
PageSize: pageSize
|
||
}
|
||
});
|
||
}
|
||
|
||
async function fetchMyOrderDetail(id) {
|
||
if (id == null || id === '') throw new Error('缺少订单ID');
|
||
return request({
|
||
path: `${API.myOrders}/${id}`,
|
||
method: 'GET',
|
||
needAuth: true
|
||
});
|
||
}
|
||
|
||
async function fetchMyCertificates({ pageNo = 1, pageSize = 10, type } = {}) {
|
||
const data = {
|
||
PageNo: pageNo,
|
||
PageSize: pageSize
|
||
};
|
||
// type: 1=个人 2=团队 (若后端支持)
|
||
if (type != null) data.Type = type;
|
||
|
||
return request({
|
||
path: API.myCertificates,
|
||
method: 'GET',
|
||
needAuth: true,
|
||
data
|
||
});
|
||
}
|
||
|
||
module.exports = {
|
||
loginWithMobilePassword,
|
||
loginWithMobileCode,
|
||
sendVerificationCode,
|
||
registerWithVerification,
|
||
resetPassword,
|
||
fetchMyProfile,
|
||
updateProfile,
|
||
fetchMyAuth,
|
||
submitMyAuth,
|
||
fetchMyRegistrations,
|
||
fetchMyPoints,
|
||
fetchMyTeams,
|
||
fetchMyTeamDetail,
|
||
fetchMyOrders,
|
||
fetchMyOrderDetail,
|
||
fetchMyCertificates,
|
||
refreshAccessToken,
|
||
uploadFile,
|
||
ocrIdCard,
|
||
ocrPassport,
|
||
ocrHousehold
|
||
};
|