2026-04-10 01:49:07 +08:00
|
|
|
|
const { API, getBaseUrl } = require('./constants');
|
2026-03-30 18:08:27 +08:00
|
|
|
|
const { request } = require('./http');
|
|
|
|
|
|
|
2026-04-10 01:49:07 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 上传文件到 /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'
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-31 18:09:37 +08:00
|
|
|
|
function pickString(obj, key) {
|
|
|
|
|
|
return obj && typeof obj[key] === 'string' ? obj[key] : '';
|
2026-03-30 18:08:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-03 17:57:08 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-31 18:09:37 +08:00
|
|
|
|
async function loginWithMobilePassword({ mobile, password }) {
|
2026-03-30 18:08:27 +08:00
|
|
|
|
const loginData = await request({
|
|
|
|
|
|
path: API.wechatLogin,
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
needAuth: false,
|
|
|
|
|
|
data: {
|
2026-03-31 18:09:37 +08:00
|
|
|
|
mobile,
|
|
|
|
|
|
password: password != null ? password : ''
|
2026-03-30 18:08:27 +08:00
|
|
|
|
},
|
|
|
|
|
|
contentType: 'application/json'
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const app = getApp();
|
2026-03-31 18:09:37 +08:00
|
|
|
|
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') : '';
|
2026-03-30 18:08:27 +08:00
|
|
|
|
|
|
|
|
|
|
app.setAuth({
|
|
|
|
|
|
accessToken,
|
2026-03-31 18:09:37 +08:00
|
|
|
|
refreshToken,
|
|
|
|
|
|
openid,
|
|
|
|
|
|
userProfile: userInfo
|
2026-03-30 18:08:27 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-31 23:27:35 +08:00
|
|
|
|
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
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-31 18:09:37 +08:00
|
|
|
|
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 }) {
|
|
|
|
|
|
const data = {
|
|
|
|
|
|
mobile,
|
|
|
|
|
|
password,
|
|
|
|
|
|
verificationCode
|
|
|
|
|
|
};
|
|
|
|
|
|
if (nickName) data.nickName = nickName;
|
|
|
|
|
|
if (avatar) data.avatar = avatar;
|
|
|
|
|
|
|
|
|
|
|
|
return request({
|
|
|
|
|
|
path: API.oauthRegister,
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
needAuth: false,
|
|
|
|
|
|
data,
|
|
|
|
|
|
contentType: 'application/json'
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-02 01:23:15 +08:00
|
|
|
|
async function resetPassword({ mobile, password, verificationCode }) {
|
|
|
|
|
|
return request({
|
|
|
|
|
|
path: API.oauthResetPassword,
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
needAuth: false,
|
2026-04-03 17:57:08 +08:00
|
|
|
|
data: { mobile, newPassword: password, verificationCode },
|
2026-04-02 01:23:15 +08:00
|
|
|
|
contentType: 'application/json'
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-30 18:08:27 +08:00
|
|
|
|
async function fetchMyProfile() {
|
|
|
|
|
|
return request({
|
|
|
|
|
|
path: API.myProfile,
|
|
|
|
|
|
method: 'GET',
|
|
|
|
|
|
needAuth: true
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function fetchMyAuth() {
|
|
|
|
|
|
return request({
|
|
|
|
|
|
path: API.myAuth,
|
|
|
|
|
|
method: 'GET',
|
|
|
|
|
|
needAuth: true
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-02 18:02:17 +08:00
|
|
|
|
async function submitMyAuth(payload) {
|
|
|
|
|
|
// 兼容旧调用方式:submitMyAuth({ name, mobile, idCardNumber, idCardImageUrl, idCardBackImageUrl })
|
|
|
|
|
|
// 新页面会直接把表单对象透传进来(如 authType、passport 字段等),后端若不识别会忽略。
|
|
|
|
|
|
const p = payload && typeof payload === 'object' ? payload : {};
|
2026-03-30 18:08:27 +08:00
|
|
|
|
|
|
|
|
|
|
return request({
|
|
|
|
|
|
path: API.myAuth,
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
needAuth: true,
|
2026-04-02 18:02:17 +08:00
|
|
|
|
data: p,
|
2026-03-30 18:08:27 +08:00
|
|
|
|
contentType: 'application/json'
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function fetchMyRegistrations({ pageNo = 1, pageSize = 10 } = {}) {
|
|
|
|
|
|
return request({
|
|
|
|
|
|
path: API.myRegistrations,
|
|
|
|
|
|
method: 'GET',
|
|
|
|
|
|
needAuth: true,
|
|
|
|
|
|
data: {
|
|
|
|
|
|
PageNo: pageNo,
|
|
|
|
|
|
PageSize: pageSize
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-03 17:57:08 +08:00
|
|
|
|
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;
|
|
|
|
|
|
|
2026-03-31 23:27:35 +08:00
|
|
|
|
return request({
|
|
|
|
|
|
path: API.myPoints,
|
|
|
|
|
|
method: 'GET',
|
|
|
|
|
|
needAuth: true,
|
2026-04-03 17:57:08 +08:00
|
|
|
|
data
|
2026-03-31 23:27:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-08 00:40:29 +08:00
|
|
|
|
async function fetchMyTeams({ pageNo = 1, pageSize = 20 } = {}) {
|
|
|
|
|
|
return request({
|
|
|
|
|
|
path: API.myTeams,
|
|
|
|
|
|
method: 'GET',
|
|
|
|
|
|
needAuth: true,
|
|
|
|
|
|
data: {
|
|
|
|
|
|
PageNo: pageNo,
|
|
|
|
|
|
PageSize: pageSize
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-10 16:10:33 +08:00
|
|
|
|
async function fetchMyTeamDetail(id) {
|
|
|
|
|
|
if (id == null || id === '') throw new Error('缺少团队ID');
|
|
|
|
|
|
return request({
|
|
|
|
|
|
path: `${API.myTeams}/${id}`,
|
|
|
|
|
|
method: 'GET',
|
|
|
|
|
|
needAuth: true
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-08 00:40:29 +08:00
|
|
|
|
async function fetchMyOrders({ pageNo = 1, pageSize = 10 } = {}) {
|
|
|
|
|
|
return request({
|
|
|
|
|
|
path: API.myOrders,
|
|
|
|
|
|
method: 'GET',
|
|
|
|
|
|
needAuth: true,
|
|
|
|
|
|
data: {
|
|
|
|
|
|
PageNo: pageNo,
|
|
|
|
|
|
PageSize: pageSize
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-02 18:02:17 +08:00
|
|
|
|
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
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-30 18:08:27 +08:00
|
|
|
|
module.exports = {
|
2026-03-31 18:09:37 +08:00
|
|
|
|
loginWithMobilePassword,
|
2026-03-31 23:27:35 +08:00
|
|
|
|
loginWithMobileCode,
|
2026-03-31 18:09:37 +08:00
|
|
|
|
sendVerificationCode,
|
|
|
|
|
|
registerWithVerification,
|
2026-04-02 01:23:15 +08:00
|
|
|
|
resetPassword,
|
2026-03-30 18:08:27 +08:00
|
|
|
|
fetchMyProfile,
|
|
|
|
|
|
fetchMyAuth,
|
|
|
|
|
|
submitMyAuth,
|
2026-03-31 23:27:35 +08:00
|
|
|
|
fetchMyRegistrations,
|
2026-04-02 18:02:17 +08:00
|
|
|
|
fetchMyPoints,
|
2026-04-08 00:40:29 +08:00
|
|
|
|
fetchMyTeams,
|
2026-04-10 16:10:33 +08:00
|
|
|
|
fetchMyTeamDetail,
|
2026-04-08 00:40:29 +08:00
|
|
|
|
fetchMyOrders,
|
2026-04-03 17:57:08 +08:00
|
|
|
|
fetchMyCertificates,
|
2026-04-10 01:49:07 +08:00
|
|
|
|
refreshAccessToken,
|
|
|
|
|
|
uploadFile,
|
|
|
|
|
|
ocrIdCard,
|
|
|
|
|
|
ocrPassport
|
2026-03-30 18:08:27 +08:00
|
|
|
|
};
|