1038 lines
40 KiB
JavaScript
1038 lines
40 KiB
JavaScript
const ApiClient = {
|
|
// 后端租户标识,所有接口请求都会带到 header 里。
|
|
// 后面如果租户号更换,只需要改这里的默认值,或在 config.js 里配置 CURRENT_CONFIG.TENANT_ID。
|
|
TENANT_ID: '531923',
|
|
// TENANT_ID: '000000',
|
|
|
|
API: {
|
|
authCode: '/api/web/auth/code',
|
|
authLogin: '/api/web/auth/login',
|
|
authLogout: '/api/web/auth/logout',
|
|
authRegister: '/api/web/auth/register',
|
|
authSmsCode: '/api/web/auth/sms/code',
|
|
authWechatOpen: '/api/web/auth/binding/wechat_open',
|
|
authVerifyConfig: '/api/web/auth/verify-config',
|
|
authSlideCaptcha: '/api/web/auth/slide-captcha',
|
|
authSlideCaptchaVerify: '/api/web/auth/slide-captcha/verify',
|
|
authHeartbeat: '/api/web/auth/heartbeat',
|
|
authForgotPasswordSmsCode: '/api/web/auth/forgot-password/sms-code',
|
|
authForgotPasswordReset: '/api/web/auth/forgot-password/reset',
|
|
authSocialLogin: '/api/web/auth/social-login',
|
|
authSocialCallback: '/api/web/auth/social-callback',
|
|
authSocialBind: '/api/web/auth/social-bind',
|
|
authSocialBindPhone: '/api/web/auth/social-bind-phone',
|
|
authSocialBindPhoneSmsCode: '/api/web/auth/social-bind-phone/sms-code',
|
|
appDownloadInfo: '/api/web/app/getDownloadInfo',
|
|
agreement: (type) => `/api/web/agreement/${encodeURIComponent(type || '')}`,
|
|
noticeList: '/api/web/notice/list',
|
|
noticeDetail: (id) => `/api/web/notice/${encodeURIComponent(id || '')}`,
|
|
friendLinks: '/api/web/link/friend',
|
|
otherRecommendLinks: '/api/web/link/other-recommend',
|
|
latestLotteryResult: '/api/web/lottery/result/latest',
|
|
lotteryResultList: '/api/web/lottery/result/list',
|
|
webConfigList: '/api/web/config/list',
|
|
webConfig: '/api/web/config',
|
|
configList: '/api/web/config/list',
|
|
configSite: '/api/web/config/site',
|
|
configCopyright: '/api/web/config/copyright',
|
|
bannerList: '/api/web/banner/list',
|
|
adList: '/api/web/ad/list',
|
|
freeArticleRecommend: '/api/web/free-article/recommend',
|
|
freeArticleList: '/api/web/free-article/list',
|
|
freeArticleLatest: '/api/web/free-article/latest',
|
|
referralShareInfo: '/api/web/referral/share-info',
|
|
referralSummary: '/api/web/referral/summary',
|
|
referralInviteList: '/api/web/referral/invite-list',
|
|
referralRebateList: '/api/web/referral/rebate-list',
|
|
referralBind: '/api/web/referral/bind',
|
|
accountInfo: '/api/web/account/info',
|
|
accountProfile: '/api/web/account/profile',
|
|
accountProfileAvatar: '/api/web/account/profile/avatar',
|
|
expertConfig: '/api/web/expert/config',
|
|
expertApply: '/api/web/expert/apply',
|
|
expertUploadIdCardFront: '/api/web/expert/upload-id-card-front',
|
|
expertUploadIdCardBack: '/api/web/expert/upload-id-card-back',
|
|
homeNav: '/api/web/menu/home-nav',
|
|
menuChildren: (id) => `/api/web/menu/${encodeURIComponent(id)}/children`,
|
|
payArticleExpertProfile: '/api/web/pay-article/expert-profile',
|
|
payArticleExpertList: '/api/web/pay-article/expert-list',
|
|
payArticleExpertRank: '/api/web/pay-article/expert-rank',
|
|
payArticlePublishOptions: (menuId) => `/api/web/pay-article/publish/options?menuId=${encodeURIComponent(menuId || '')}`,
|
|
payArticlePublish: '/api/web/pay-article/publish',
|
|
payArticleLatest: '/api/web/pay-article/latest',
|
|
payArticleRelated: (id) => `/api/web/pay-article/${encodeURIComponent(id || '')}/related`,
|
|
payArticleCollect: (id) => `/api/web/pay-article/article-collect/${encodeURIComponent(id)}`,
|
|
expertCollect: (expertId, accountType = 1) =>
|
|
`/api/web/pay-article/expert-collect?${new URLSearchParams({
|
|
expertId: String(expertId || ''),
|
|
accountType: String(accountType || 1),
|
|
}).toString()}`,
|
|
walletOverview: '/api/web/wallet/overview',
|
|
walletFlowList: '/api/web/wallet/flow/list',
|
|
walletFlowDelete: (id) => `/api/web/wallet/flow/${encodeURIComponent(id)}`,
|
|
walletRechargeConfigList: '/api/web/wallet/recharge/config/list',
|
|
walletRechargeOrder: '/api/web/wallet/recharge/order',
|
|
walletRechargeOrderStatus: (outTradeNo) => `/api/web/wallet/recharge/order/status/${encodeURIComponent(outTradeNo || '')}`,
|
|
walletRechargeNotify: '/api/web/wallet/recharge/notify',
|
|
walletTransferToAccount: '/api/web/wallet/transfer-to-account',
|
|
walletWithdraw: '/api/web/wallet/withdraw',
|
|
walletWithdrawList: '/api/web/wallet/withdraw/list',
|
|
walletPrepurchasePayArticle: '/api/web/wallet/prepurchase/pay-article',
|
|
walletReceiveAccountList: '/api/web/wallet/receive-account/list',
|
|
walletReceiveAccount: '/api/web/wallet/receive-account',
|
|
walletReceiveAccountUpdate: (id) => `/api/web/wallet/receive-account/${encodeURIComponent(id)}`,
|
|
walletReceiveAccountDefault: (id) => `/api/web/wallet/receive-account/${encodeURIComponent(id)}/default`,
|
|
walletReceiveAccountDelete: (id) => `/api/web/wallet/receive-account/${encodeURIComponent(id)}`,
|
|
walletReceiveAccountUploadQrcode: '/api/web/wallet/receive-account/upload-qrcode',
|
|
mineSummary: '/api/web/mine/summary',
|
|
mineFreeCollectList: '/api/web/mine/free-collect/list',
|
|
minePayCollectList: '/api/web/mine/pay-collect/list',
|
|
mineExpertCollectList: '/api/web/mine/expert-collect/list',
|
|
mineSignInList: '/api/web/mine/sign-in/list',
|
|
mineSignInStatus: '/api/web/mine/sign-in/status',
|
|
mineSignIn: '/api/web/mine/sign-in',
|
|
walletPurchasePayArticle: '/api/web/wallet/purchase/pay-article',
|
|
freeArticlePublish: '/api/web/free-article/publish',
|
|
mineFreeArticleList: '/api/web/mine/free-article/list',
|
|
mineFreeArticleDetail: (id) => `/api/web/mine/free-article/${encodeURIComponent(id || '')}`,
|
|
minePayArticleList: '/api/web/mine/pay-article/list',
|
|
minePayArticleDetail: (id) => `/api/web/mine/pay-article/${encodeURIComponent(id)}`,
|
|
minePurchasedPayArticleList: '/api/web/mine/purchased-pay-article/list',
|
|
minePurchasedPayArticleDelete: (sourceType, recordId) => `/api/web/mine/purchased-pay-article/${encodeURIComponent(sourceType)}/${encodeURIComponent(recordId)}`,
|
|
freeArticleDetail: (id) => `/api/web/free-article/${encodeURIComponent(id)}`,
|
|
freeArticleRelated: (id) => `/api/web/free-article/${encodeURIComponent(id)}/related`,
|
|
freeArticleComments: (id) => `/api/web/free-article/${encodeURIComponent(id)}/comments`,
|
|
freeArticleComment: (id) => `/api/web/free-article/${encodeURIComponent(id)}/comment`,
|
|
freeArticleLike: (id) => `/api/web/free-article/${encodeURIComponent(id)}/like`,
|
|
freeArticleCollect: (id) => `/api/web/free-article/${encodeURIComponent(id)}/collect`,
|
|
fc3dFreePredictMenus: '/api/web/fc3d/free-predict/menus',
|
|
fc3dFreePredictList: '/api/web/fc3d/free-predict/list',
|
|
fc3dSchemeList: '/api/web/fc3d/scheme/list',
|
|
fc3dSchemeDetail: (id) => `/api/web/fc3d/scheme/${encodeURIComponent(id)}`,
|
|
walletPurchaseFc3dScheme: '/api/web/wallet/purchase/fc3d-scheme',
|
|
lotteryTrendTools: '/api/web/lottery/result/trend/tools',
|
|
lotteryTrend: '/api/web/lottery/result/trend',
|
|
},
|
|
|
|
get baseUrl() {
|
|
return (window.CURRENT_CONFIG && window.CURRENT_CONFIG.API_BASE_URL) || '';
|
|
},
|
|
|
|
get clientId() {
|
|
return (
|
|
(window.CURRENT_CONFIG && window.CURRENT_CONFIG.CLIENT_ID) ||
|
|
'209ef407810e3856f40870a9f0e769d7'
|
|
);
|
|
},
|
|
|
|
get CLIENT_ID() {
|
|
return this.clientId;
|
|
},
|
|
|
|
get tenantId() {
|
|
return (
|
|
(window.CURRENT_CONFIG && window.CURRENT_CONFIG.TENANT_ID) ||
|
|
this.TENANT_ID
|
|
);
|
|
},
|
|
|
|
request(options) {
|
|
const normalized = this.normalizeLegacyRequest(options || {});
|
|
if (normalized.compose) return normalized.compose();
|
|
|
|
const method = (normalized.method || 'GET').toUpperCase();
|
|
const url = this.buildUrl(normalized.url, method === 'GET' ? normalized.data : null);
|
|
const dedupeKey = this.requestDedupeKey(method, url, normalized);
|
|
const cached = this.getDedupeCache(dedupeKey);
|
|
if (cached) return cached;
|
|
|
|
const controller = new AbortController();
|
|
const timeoutId = setTimeout(() => controller.abort(), normalized.timeout || 10000);
|
|
|
|
const baseHeaders = normalized.publicRequest ? this.publicHeaders() : this.defaultHeaders();
|
|
const config = {
|
|
method,
|
|
headers: {
|
|
...baseHeaders,
|
|
...(normalized.headers || {}),
|
|
},
|
|
signal: controller.signal,
|
|
};
|
|
|
|
if (normalized.data && ['POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) {
|
|
config.body =
|
|
normalized.data instanceof FormData || typeof normalized.data === 'string'
|
|
? normalized.data
|
|
: JSON.stringify(normalized.data);
|
|
if (normalized.data instanceof FormData) delete config.headers['Content-Type'];
|
|
}
|
|
|
|
const promise = fetch(this.baseUrl + url, config)
|
|
.then(async (response) => {
|
|
clearTimeout(timeoutId);
|
|
if (!response.ok) {
|
|
if (response.status === 401) {
|
|
localStorage.removeItem('token');
|
|
NavigationUtil.open('login.html');
|
|
}
|
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
}
|
|
const result = this.normalizeResponse(await this.parseResponse(response), normalized);
|
|
if (this.isAuthExpiredResponse(result)) this.handleAuthExpired();
|
|
return result;
|
|
})
|
|
.catch((error) => {
|
|
clearTimeout(timeoutId);
|
|
if (error.name === 'AbortError') throw new Error('Request timeout');
|
|
throw error;
|
|
});
|
|
return this.setDedupeCache(dedupeKey, promise);
|
|
},
|
|
|
|
requestDedupeKey(method, url, normalized = {}) {
|
|
if (method !== 'GET' || normalized.dedupe === false) return '';
|
|
const token = this.getToken() || '';
|
|
return [method, this.baseUrl + url, token, this.clientId, this.tenantId].join(' ');
|
|
},
|
|
|
|
getDedupeStore() {
|
|
if (!this._dedupeStore) this._dedupeStore = new Map();
|
|
return this._dedupeStore;
|
|
},
|
|
|
|
getDedupeCache(key) {
|
|
if (!key) return null;
|
|
const store = this.getDedupeStore();
|
|
const item = store.get(key);
|
|
if (!item) return null;
|
|
if (item.pending) return item.promise;
|
|
if (Date.now() - item.time > 500) {
|
|
store.delete(key);
|
|
return null;
|
|
}
|
|
return item.promise;
|
|
},
|
|
|
|
setDedupeCache(key, promise) {
|
|
if (!key) return promise;
|
|
const store = this.getDedupeStore();
|
|
store.set(key, { promise, pending: true, time: Date.now() });
|
|
promise
|
|
.then(() => {
|
|
const item = store.get(key);
|
|
if (item?.promise === promise) {
|
|
item.pending = false;
|
|
item.time = Date.now();
|
|
setTimeout(() => {
|
|
if (store.get(key)?.promise === promise) store.delete(key);
|
|
}, 600);
|
|
}
|
|
})
|
|
.catch(() => {
|
|
if (store.get(key)?.promise === promise) store.delete(key);
|
|
});
|
|
return promise;
|
|
},
|
|
|
|
defaultHeaders() {
|
|
const headers = {
|
|
'Content-Type': 'application/json',
|
|
tenantId: this.tenantId,
|
|
};
|
|
const token = this.getToken();
|
|
if (token) {
|
|
headers.Authorization = `Bearer ${token}`;
|
|
headers.clientid = this.clientId;
|
|
}
|
|
return headers;
|
|
},
|
|
|
|
publicHeaders() {
|
|
return {
|
|
'Content-Type': 'application/json',
|
|
tenantId: this.tenantId,
|
|
};
|
|
},
|
|
|
|
isSuccess(res) {
|
|
return !!res && (res.code === 0 || res.code === 200 || res.success === true);
|
|
},
|
|
|
|
isAuthExpiredResponse(result) {
|
|
if (!result || typeof result !== 'object') return false;
|
|
const code = Number(result.code);
|
|
const message = String(result.msg || result.message || result.error || '').toLowerCase();
|
|
return (
|
|
code === 401 ||
|
|
code === 403 ||
|
|
code === 40101 ||
|
|
code === 40102 ||
|
|
message.indexOf('token') >= 0 && (message.indexOf('expired') >= 0 || message.indexOf('invalid') >= 0)
|
|
);
|
|
},
|
|
|
|
getToken() {
|
|
const raw = localStorage.getItem('token');
|
|
if (!raw) return '';
|
|
try {
|
|
const parsed = JSON.parse(raw);
|
|
return parsed.access_token || parsed.accessToken || parsed.token || parsed;
|
|
} catch (e) {
|
|
return raw;
|
|
}
|
|
},
|
|
|
|
token() {
|
|
return this.getToken();
|
|
},
|
|
|
|
clearToken() {
|
|
localStorage.removeItem('token');
|
|
localStorage.removeItem('userInfo');
|
|
localStorage.removeItem('user');
|
|
},
|
|
|
|
getLocalUserInfo() {
|
|
const raw = localStorage.getItem('userInfo') || localStorage.getItem('user') || '';
|
|
if (!raw) return {};
|
|
try {
|
|
return JSON.parse(raw) || {};
|
|
} catch (e) {
|
|
return {};
|
|
}
|
|
},
|
|
|
|
isJwtExpired(token) {
|
|
const parts = String(token || '').split('.');
|
|
if (parts.length < 2) return false;
|
|
try {
|
|
let payloadText = parts[1].replace(/-/g, '+').replace(/_/g, '/');
|
|
while (payloadText.length % 4) payloadText += '=';
|
|
const payload = JSON.parse(decodeURIComponent(escape(window.atob(payloadText))));
|
|
return payload.exp ? payload.exp * 1000 <= Date.now() : false;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
},
|
|
|
|
isTokenExpired() {
|
|
const token = this.token();
|
|
if (!token) return true;
|
|
if (this.isJwtExpired(token)) return true;
|
|
const userInfo = this.getLocalUserInfo();
|
|
const expireTime = Number(userInfo.expireTime || userInfo.expire_time || userInfo.expiresAt || userInfo.expires_at || 0);
|
|
return expireTime ? expireTime <= Date.now() : false;
|
|
},
|
|
|
|
handleAuthExpired() {
|
|
if (typeof window === 'undefined') return;
|
|
this.clearToken();
|
|
const path = window.location.pathname || '';
|
|
if (/\/(login|reg|repwd)\.html$/i.test(path)) return;
|
|
const returnUrl = window.location.pathname + window.location.search;
|
|
const loginPath = '/login.html?redirect=' + encodeURIComponent(returnUrl);
|
|
NavigationUtil.open(
|
|
typeof CommonUtil !== 'undefined' && CommonUtil.siteHref
|
|
? CommonUtil.siteHref(loginPath)
|
|
: ((window.CURRENT_ENV === 'development' ? '/html/login.html?redirect=' : '/login.html?redirect=') + encodeURIComponent(returnUrl)));
|
|
},
|
|
|
|
buildUrl(url, params) {
|
|
if (!params || params instanceof FormData) return url;
|
|
const clean = this.cleanParams(params);
|
|
const query = new URLSearchParams(clean).toString();
|
|
if (!query) return url;
|
|
return url + (url.indexOf('?') >= 0 ? '&' : '?') + query;
|
|
},
|
|
|
|
cleanParams(params, omittedKeys = []) {
|
|
const clean = {};
|
|
const omitted = new Set(omittedKeys || []);
|
|
Object.keys(params || {}).forEach((key) => {
|
|
const value = params[key];
|
|
if (omitted.has(key)) return;
|
|
if (value !== undefined && value !== null && value !== '') clean[key] = value;
|
|
});
|
|
return clean;
|
|
},
|
|
|
|
async parseResponse(response) {
|
|
const contentType = response.headers.get('content-type') || '';
|
|
if (contentType.indexOf('application/json') >= 0) return response.json();
|
|
return response.text();
|
|
},
|
|
|
|
normalizeResponse(result, context = {}) {
|
|
if (!result || typeof result !== 'object') return result;
|
|
const normalized = { ...result };
|
|
|
|
if (normalized.code === 200) {
|
|
normalized.rawCode = 200;
|
|
normalized.code = 0;
|
|
}
|
|
if (typeof normalized.success === 'undefined') {
|
|
normalized.success = normalized.code === 0 || normalized.code === 200;
|
|
}
|
|
if (Array.isArray(normalized.rows)) {
|
|
normalized.data = {
|
|
records: normalized.rows,
|
|
rows: normalized.rows,
|
|
totalRow: normalized.total || 0,
|
|
total: normalized.total || 0,
|
|
};
|
|
}
|
|
if (context.url === '/api/web/pay-article/expert-rank') {
|
|
this.normalizeExpertRankResponse(normalized, context.data || {});
|
|
}
|
|
if (normalized.data && typeof normalized.data === 'object') {
|
|
if (Object.prototype.hasOwnProperty.call(normalized.data, 'captchaEnabled')) {
|
|
normalized.data.id = normalized.data.uuid || 'captcha-disabled';
|
|
normalized.data.image = normalized.data.img || this.transparentGif();
|
|
if (normalized.data.captchaEnabled === false) this.applyCaptchaDisabledState();
|
|
}
|
|
if (normalized.data.access_token && !normalized.data.token) {
|
|
normalized.data.token = normalized.data.access_token;
|
|
}
|
|
}
|
|
return normalized;
|
|
},
|
|
|
|
normalizeExpertRankResponse(result, params = {}) {
|
|
const data = result.data && typeof result.data === 'object' ? result.data : {};
|
|
const source = Array.isArray(data.records)
|
|
? data.records
|
|
: Array.isArray(data.rows)
|
|
? data.rows
|
|
: Array.isArray(result.rows)
|
|
? result.rows
|
|
: [];
|
|
const rows = source.map((item, index) => {
|
|
const successRate =
|
|
item.successRate === undefined || item.successRate === null ? '' : String(item.successRate);
|
|
const hitRate = item.hitRate || (successRate ? `${successRate}%` : '');
|
|
return {
|
|
...item,
|
|
rank: item.rank || index + 1,
|
|
createBy: item.createBy || item.expertId,
|
|
expertId: item.expertId || item.createBy,
|
|
createUserName: item.createUserName || item.nickname || item.expertName,
|
|
nickname: item.nickname || item.createUserName || item.expertName,
|
|
issue: item.issue || item.latestIssue,
|
|
title: item.title || item.latestTitle || item.latestRecommend,
|
|
hitRate,
|
|
};
|
|
});
|
|
|
|
const shouldExpandForLegacyRank =
|
|
params && !params.pageNum && Number(params.pageSize || 0) >= 50;
|
|
const records = shouldExpandForLegacyRank
|
|
? rows.flatMap((item) => {
|
|
const total = Math.max(Number(item.totalCount) || 1, 1);
|
|
const hit = Math.max(Number(item.correctCount) || 0, 0);
|
|
return Array.from({ length: total }, (_, index) => ({
|
|
...item,
|
|
status: index < hit ? '1' : '0',
|
|
}));
|
|
})
|
|
: rows;
|
|
|
|
result.data = {
|
|
...data,
|
|
records,
|
|
rows: records,
|
|
totalRow: data.totalRow || data.total || result.total || rows.length,
|
|
total: data.total || data.totalRow || result.total || rows.length,
|
|
};
|
|
},
|
|
|
|
transparentGif() {
|
|
return 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
|
|
},
|
|
|
|
applyCaptchaDisabledState() {
|
|
if (typeof window === 'undefined' || !window.document) return;
|
|
setTimeout(() => {
|
|
const input = document.getElementById('captchaInput');
|
|
const hidden = document.getElementById('captchaId');
|
|
const box = document.getElementById('captchaContainer');
|
|
if (input) input.value = '0000';
|
|
if (hidden) hidden.value = 'captcha-disabled';
|
|
if (box) box.style.display = 'none';
|
|
}, 0);
|
|
},
|
|
|
|
normalizePageParams(data) {
|
|
const params = { ...(data || {}) };
|
|
if (params.page && !params.pageNum) params.pageNum = params.page;
|
|
if (params.limit && !params.pageSize) params.pageSize = params.limit;
|
|
if (params.pageNo && !params.pageNum) params.pageNum = params.pageNo;
|
|
delete params.page;
|
|
delete params.limit;
|
|
delete params.pageNo;
|
|
return params;
|
|
},
|
|
|
|
pickData(result, fallback) {
|
|
if (!result || typeof result !== 'object') return fallback;
|
|
return result.data === undefined || result.data === null ? fallback : result.data;
|
|
},
|
|
|
|
asArray(value) {
|
|
if (Array.isArray(value)) return value;
|
|
if (!value || typeof value !== 'object') return [];
|
|
if (Array.isArray(value.records)) return value.records;
|
|
if (Array.isArray(value.rows)) return value.rows;
|
|
if (Array.isArray(value.list)) return value.list;
|
|
if (Array.isArray(value.data)) return value.data;
|
|
return [];
|
|
},
|
|
|
|
unwrap(result, fallback) {
|
|
return this.pickData(result, fallback);
|
|
},
|
|
|
|
listFrom(source, keys = ['records', 'rows', 'list', 'data']) {
|
|
if (Array.isArray(source)) return source;
|
|
if (!source || typeof source !== 'object') return [];
|
|
for (const key of keys || []) {
|
|
const nested = this.asArray(source[key]);
|
|
if (nested.length) return nested;
|
|
}
|
|
return [];
|
|
},
|
|
|
|
normalizeLegacyRequest(options) {
|
|
const url = options.url || '';
|
|
const method = (options.method || 'GET').toUpperCase();
|
|
const data = options.data || {};
|
|
|
|
if (url === '/api/web/index') {
|
|
return { compose: () => this.loadHomeIndexCompat() };
|
|
}
|
|
if (url === '/api/web/menu/getFormatList') {
|
|
return { compose: () => this.loadMenuCompat() };
|
|
}
|
|
if (url === '/api/web/link/getList') {
|
|
return { compose: () => this.loadLinksCompat() };
|
|
}
|
|
if (url === '/api/web/QRCode') {
|
|
return { compose: () => Promise.resolve({ code: 0, success: true, data: location.origin + '/html/appdown.html' }) };
|
|
}
|
|
if (url === '/api/web/user/login') {
|
|
return { compose: () => this.loginCompat(data) };
|
|
}
|
|
if (url.indexOf('/api/user/collect/list') === 0) {
|
|
return {
|
|
...options,
|
|
url: '/api/web/mine/free-collect/list',
|
|
method: 'GET',
|
|
data: this.normalizePageParams({
|
|
...data,
|
|
page: this.queryValue(url, 'page') || data.page,
|
|
pageSize: this.queryValue(url, 'pageSize') || data.pageSize,
|
|
}),
|
|
};
|
|
}
|
|
if (url === '/api/web/lotteryResult/getList') {
|
|
return { compose: () => this.requestLegacyLotteryResultCompat(data) };
|
|
}
|
|
if (url === '/api/web/freeArticle/getPageList') {
|
|
return { compose: () => this.requestWithMenu('/api/web/free-article/list', data) };
|
|
}
|
|
if (url === '/api/web/payArticle/getPageList') {
|
|
return { compose: () => this.requestWithMenu('/api/web/pay-article/expert-rank', data) };
|
|
}
|
|
if (url === '/api/web/payArticle/getListByCreateUser') {
|
|
return {
|
|
compose: () => this.loadLegacyExpertDetailCompat(data),
|
|
};
|
|
}
|
|
|
|
const direct = this.mapSimpleLegacyUrl(url, data);
|
|
if (direct) return { ...options, ...direct };
|
|
|
|
if (url.indexOf('/api/web/agreement/getByType') === 0) {
|
|
const type = this.queryValue(url, 'type') || data.type || '1';
|
|
return { ...options, url: `/api/web/agreement/${type}`, method: 'GET', data: null };
|
|
}
|
|
if (url.indexOf('/api/web/notice/getDetail') === 0) {
|
|
const id = this.queryValue(url, 'id') || data.id;
|
|
return { ...options, url: `/api/web/notice/${id}`, method: 'GET', data: null };
|
|
}
|
|
if (url.indexOf('/api/web/freeArticle/getDetail') === 0) {
|
|
const id = this.queryValue(url, 'id') || data.id;
|
|
return { ...options, url: `/api/web/free-article/${id}`, method: 'GET', data: null };
|
|
}
|
|
|
|
return { ...options, method, data };
|
|
},
|
|
|
|
mapSimpleLegacyUrl(url, data) {
|
|
const pageParams = this.normalizePageParams(data);
|
|
const map = {
|
|
'/api/web/user/register': { url: '/api/web/auth/register', method: 'POST', data: this.normalizeAuthPayload(data) },
|
|
'/api/web/user/getCaptcha': { url: '/api/web/auth/code', method: 'GET', data: null },
|
|
'/api/web/user/logout': { url: '/api/web/auth/logout', method: 'POST', data: null },
|
|
'/api/web/user/getCurrentUserDetail': { url: '/api/web/mine/summary', method: 'GET', data: null },
|
|
'/api/web/user/getUserBalance': { url: '/api/web/wallet/overview', method: 'GET', data: null },
|
|
'/api/web/user/signIn': { url: '/api/web/mine/sign-in', method: 'POST', data: null },
|
|
'/api/web/user/signIn/getPageList': { url: '/api/web/mine/sign-in/list', method: 'GET', data: pageParams },
|
|
'/api/web/capitalFlow/getPageList': { url: '/api/web/wallet/flow/list', method: 'GET', data: pageParams },
|
|
'/api/web/payment/getConfigList': { url: '/api/web/wallet/recharge/config/list', method: 'GET', data: null },
|
|
'/api/web/payment/charge/native': {
|
|
url: '/api/web/wallet/recharge/order',
|
|
method: 'POST',
|
|
data: Object.assign({}, data || {}, {
|
|
rechargeId: data && (data.rechargeId || data.configId),
|
|
payType: 'NATIVE',
|
|
returnUrl: typeof window !== 'undefined' ? window.location.href : ''
|
|
})
|
|
},
|
|
'/api/web/freeArticle/getPageListByUser': { url: '/api/web/mine/free-article/list', method: 'GET', data: pageParams },
|
|
'/api/web/freeArticle/push': { url: '/api/web/free-article/publish', method: 'POST', data },
|
|
'/api/web/payArticle/push': {
|
|
url: '/api/web/pay-article/publish',
|
|
method: 'POST',
|
|
data: {
|
|
parentId: data.parentId,
|
|
code: data.code,
|
|
title: data.title,
|
|
predictedCode: data.predictedCode || data.predictCode || data.content,
|
|
issue: data.issue || data.expect,
|
|
},
|
|
},
|
|
'/api/web/payArticle/subscribe': { url: '/api/web/wallet/purchase/pay-article', method: 'POST', data },
|
|
'/api/web/order/getPageListByUser': { url: '/api/web/mine/purchased-pay-article/list', method: 'GET', data: pageParams },
|
|
'/api/web/rebate/getAffInfo': { url: '/api/web/referral/share-info', method: 'GET', data: null },
|
|
'/api/web/rebate/getPageList': { url: '/api/web/referral/rebate-list', method: 'GET', data: pageParams },
|
|
'/api/web/rebate/getWithdrawList': { url: '/api/web/wallet/withdraw/list', method: 'GET', data: pageParams },
|
|
'/api/web/rebate/withdraw': { url: '/api/web/wallet/withdraw', method: 'POST', data },
|
|
'/api/web/rebate/rebateToBalance': { url: '/api/web/wallet/transfer-to-account', method: 'POST', data },
|
|
'/api/web/distribution/getPageList': { url: '/api/web/referral/invite-list', method: 'GET', data: pageParams },
|
|
'/api/web/user/applyExpert': { url: '/api/web/expert/apply', method: 'POST', data },
|
|
'/api/web/user/expert/apply': { url: '/api/web/expert/apply', method: 'POST', data },
|
|
'/api/web/user/updateInfo': { url: '/api/web/account/profile', method: 'PUT', data },
|
|
'/api/web/notice/getPageList': { url: '/api/web/notice/list', method: 'GET', data: pageParams },
|
|
'/api/web/fc3d/freePredict/list': { url: '/api/web/fc3d/free-predict/list', method: 'GET', data: pageParams },
|
|
'/api/web/fc3d/scheme/list': { url: '/api/web/fc3d/scheme/list', method: 'GET', data: pageParams },
|
|
'/api/web/fc3d/scheme/buy': { url: '/api/web/wallet/purchase/fc3d-scheme', method: 'POST', data },
|
|
};
|
|
|
|
if (map[url]) return map[url];
|
|
|
|
const pathMap = [
|
|
[/^\/api\/web\/freeArticle\/like\/(.+)$/, (id) => ({ url: `/api/web/free-article/${id}/like`, method: 'POST', data: null })],
|
|
[/^\/api\/web\/freeArticle\/collect\/(.+)$/, (id) => ({ url: `/api/web/free-article/${id}/collect`, method: 'POST', data: null })],
|
|
[/^\/api\/web\/freeArticleComment\/getPageList$/, () => ({ url: `/api/web/free-article/${data.articleId}/comments`, method: 'GET', data: pageParams })],
|
|
[/^\/api\/web\/freeArticleComment\/push$/, () => ({ url: `/api/web/free-article/${data.articleId}/comment`, method: 'POST', data })],
|
|
[/^\/api\/web\/payArticle\/collect\/(.+)$/, (id) => ({ url: `/api/web/pay-article/article-collect/${id}`, method: 'POST', data: null })],
|
|
[/^\/api\/web\/expert\/collect\/(.+)$/, (id) => ({
|
|
url: `/api/web/pay-article/expert-collect?${new URLSearchParams({
|
|
expertId: id,
|
|
accountType: data.accountType || 1,
|
|
}).toString()}`,
|
|
method: 'POST',
|
|
data: null,
|
|
})],
|
|
];
|
|
|
|
for (const [pattern, build] of pathMap) {
|
|
const match = url.match(pattern);
|
|
if (match) return build(match[1]);
|
|
}
|
|
return null;
|
|
},
|
|
|
|
normalizeAuthPayload(data) {
|
|
const payload = { ...(data || {}) };
|
|
if (!payload.grantType) payload.grantType = 'password';
|
|
if (!payload.deviceType) payload.deviceType = 'WEB';
|
|
if (payload.captcha && !payload.code) payload.code = payload.captcha;
|
|
if (payload.captchaId && payload.captchaId !== 'captcha-disabled' && !payload.uuid) {
|
|
payload.uuid = payload.captchaId;
|
|
}
|
|
if (payload.captchaId === 'captcha-disabled') {
|
|
payload.verifyType = payload.verifyType || 'none';
|
|
delete payload.uuid;
|
|
delete payload.captchaId;
|
|
delete payload.captcha;
|
|
delete payload.code;
|
|
} else if (!payload.verifyType) {
|
|
payload.verifyType = payload.code ? 'captcha' : 'none';
|
|
}
|
|
return payload;
|
|
},
|
|
|
|
async loginCompat(data) {
|
|
const payload = this.normalizeAuthPayload(data);
|
|
const verifyConfig = await this.get('/api/web/auth/verify-config').catch(() => ({ data: null }));
|
|
const verifyType = verifyConfig.data && verifyConfig.data.verifyType;
|
|
if (verifyType === 'slide' || verifyConfig.data?.slideCaptchaEnabled) {
|
|
const slide = await this.get('/api/web/auth/slide-captcha').catch(() => ({ data: null }));
|
|
if (slide.data && slide.data.uuid) {
|
|
payload.verifyType = 'slide';
|
|
payload.slideUuid = slide.data.uuid;
|
|
payload.slideX = slide.data.targetX || slide.data.x || 0;
|
|
delete payload.code;
|
|
delete payload.uuid;
|
|
delete payload.captcha;
|
|
delete payload.captchaId;
|
|
}
|
|
}
|
|
return this.post('/api/web/auth/login', payload);
|
|
},
|
|
|
|
queryValue(url, key) {
|
|
const query = url.split('?')[1];
|
|
if (!query) return '';
|
|
return new URLSearchParams(query).get(key) || '';
|
|
},
|
|
|
|
fetchHomeNav() {
|
|
if (!this._homeNavPromise) {
|
|
this._homeNavPromise = this.get('/api/web/menu/home-nav').catch((error) => {
|
|
this._homeNavPromise = null;
|
|
throw error;
|
|
});
|
|
}
|
|
return this._homeNavPromise;
|
|
},
|
|
|
|
normalizeMenuList(navData, options = {}) {
|
|
return (navData || [])
|
|
.filter((item) => item.type === 'lottery')
|
|
.map((item) => ({
|
|
...item,
|
|
suoxie: item.suoxie || item.code,
|
|
url: item.url || item.path,
|
|
children: options.keepChildren && Array.isArray(item.children) ? item.children : [],
|
|
}));
|
|
},
|
|
|
|
shouldLoadMenuChildren() {
|
|
if (typeof window === 'undefined') return true;
|
|
return /\/(?:cai|caiinfo|fufei)\.html(?:$|\?)/.test(
|
|
window.location.pathname + window.location.search
|
|
);
|
|
},
|
|
|
|
async loadMenuCompat() {
|
|
if (!this._menuCompatPromise) {
|
|
this._menuCompatPromise = (async () => {
|
|
const nav = await this.fetchHomeNav();
|
|
const list = this.normalizeMenuList(nav.data, { keepChildren: true });
|
|
if (!this.shouldLoadMenuChildren()) {
|
|
return { code: 0, success: true, data: list };
|
|
}
|
|
|
|
const missingChildren = list.filter((item) => item.id && !item.children.length);
|
|
|
|
await Promise.all(
|
|
missingChildren.map((item) =>
|
|
this.get(`/api/web/menu/${item.id}/children`)
|
|
.then((res) => {
|
|
item.children = res.data || [];
|
|
})
|
|
.catch(() => {
|
|
item.children = [];
|
|
})
|
|
)
|
|
);
|
|
return { code: 0, success: true, data: list };
|
|
})().catch((error) => {
|
|
this._menuCompatPromise = null;
|
|
throw error;
|
|
});
|
|
}
|
|
return this._menuCompatPromise;
|
|
},
|
|
|
|
async loadHomeMenusCompat() {
|
|
const nav = await this.fetchHomeNav();
|
|
const list = this.normalizeMenuList(nav.data);
|
|
return { code: 0, success: true, data: list };
|
|
},
|
|
|
|
async requestWithMenu(url, data) {
|
|
const params = this.normalizePageParams(data || {});
|
|
if (url === '/api/web/pay-article/expert-rank' && !params.issueCount && params.pageSize) {
|
|
params.issueCount = params.pageSize;
|
|
if (params.pageNum) params.pageSize = params.rowPageSize || 10;
|
|
}
|
|
params.menuId =
|
|
params.menuId ||
|
|
params.parentId ||
|
|
params.typeId ||
|
|
(await this.resolveMenuId(params.code || params.suoxie));
|
|
delete params.parentId;
|
|
delete params.typeId;
|
|
delete params.code;
|
|
delete params.suoxie;
|
|
return this.get(url, params);
|
|
},
|
|
|
|
async requestLegacyLotteryResultCompat(data = {}) {
|
|
const params = this.normalizePageParams(data || {});
|
|
const code = params.code || params.suoxie || params.lotteryCode || '';
|
|
const pageSize = params.pageSize || params.limit || params.count || 100;
|
|
const normalizeResponse = (res) => {
|
|
if (!this.isSuccess(res)) return res;
|
|
const payload = this.pickData(res, {});
|
|
const list = this.asArray(payload);
|
|
return Object.assign({}, res, {
|
|
code: 0,
|
|
success: true,
|
|
data: list,
|
|
rawData: payload,
|
|
rows: list,
|
|
records: list,
|
|
});
|
|
};
|
|
if (code && this.API.lotteryTrend) {
|
|
try {
|
|
const trendRes = await this.get(this.API.lotteryTrend, {
|
|
code,
|
|
pageSize,
|
|
});
|
|
if (this.isSuccess(trendRes) && this.asArray(this.pickData(trendRes, [])).length) {
|
|
return normalizeResponse(trendRes);
|
|
}
|
|
} catch (e) {
|
|
// Fall back to the older result-list endpoint below.
|
|
}
|
|
}
|
|
const listRes = await this.requestWithMenu('/api/web/lottery/result/list', data);
|
|
return normalizeResponse(listRes);
|
|
},
|
|
|
|
async loadLegacyExpertDetailCompat(data = {}) {
|
|
const cacheKey = JSON.stringify(this.normalizePageParams(data));
|
|
if (!this._legacyExpertDetailPromises) this._legacyExpertDetailPromises = {};
|
|
if (this._legacyExpertDetailPromises[cacheKey]) {
|
|
return this._legacyExpertDetailPromises[cacheKey];
|
|
}
|
|
|
|
this._legacyExpertDetailPromises[cacheKey] = this.fetchLegacyExpertDetailCompat(data).catch(
|
|
(error) => {
|
|
delete this._legacyExpertDetailPromises[cacheKey];
|
|
throw error;
|
|
}
|
|
);
|
|
return this._legacyExpertDetailPromises[cacheKey];
|
|
},
|
|
|
|
async fetchLegacyExpertDetailCompat(data = {}) {
|
|
const params = this.normalizePageParams(data);
|
|
const expertId = params.expertId || params.createBy || '';
|
|
const accountType = params.accountType || 1;
|
|
const menuId =
|
|
params.menuId ||
|
|
params.parentId ||
|
|
params.typeId ||
|
|
(await this.resolveMenuId(params.code || params.suoxie));
|
|
const listParams = {
|
|
...params,
|
|
expertId,
|
|
accountType,
|
|
menuId,
|
|
pageNum: params.pageNum || 1,
|
|
pageSize: params.pageSize || 50,
|
|
};
|
|
delete listParams.createBy;
|
|
delete listParams.parentId;
|
|
delete listParams.typeId;
|
|
delete listParams.code;
|
|
delete listParams.suoxie;
|
|
|
|
const [profileRes, listRes] = await Promise.all([
|
|
expertId
|
|
? this.get('/api/web/pay-article/expert-profile', { expertId, accountType }).catch((error) => ({
|
|
code: 500,
|
|
msg: error.message || '获取专家资料失败',
|
|
data: {},
|
|
}))
|
|
: Promise.resolve({ code: 0, data: {} }),
|
|
this.get('/api/web/pay-article/expert-list', listParams),
|
|
]);
|
|
|
|
if (listRes.code !== 0) return listRes;
|
|
const profile = this.pickData(profileRes, {}) || {};
|
|
const listData = this.pickData(listRes, {}) || {};
|
|
const list = this.asArray(listData).map((item) => ({
|
|
...item,
|
|
id: item.id || item.articleId || item.payArticleId,
|
|
issue: item.issue || item.latestIssue || item.expect || item.period,
|
|
title: item.title || item.latestTitle,
|
|
predictedCode:
|
|
item.predictedCode ||
|
|
item.recommendCode ||
|
|
item.latestRecommend ||
|
|
item.recommend ||
|
|
item.content,
|
|
openCode: item.openCode || item.lotteryResult || item.resultCode || item.openResult,
|
|
accountType: item.accountType || accountType,
|
|
}));
|
|
|
|
return {
|
|
...listRes,
|
|
data: {
|
|
...listData,
|
|
...profile,
|
|
expertId,
|
|
createBy: expertId,
|
|
menuId,
|
|
parentId: menuId,
|
|
accountType,
|
|
avatar: profile.avatar || profile.headImg,
|
|
nickname: profile.nickname || profile.nickName || profile.expertName,
|
|
isCollect: profile.isCollect ?? profile.collected ?? profile.expertCollected,
|
|
payArticleList: list,
|
|
records: list,
|
|
rows: list,
|
|
totalRow: listData.totalRow || listData.total || listRes.total || list.length,
|
|
total: listData.total || listData.totalRow || listRes.total || list.length,
|
|
},
|
|
};
|
|
},
|
|
|
|
async resolveMenuId(code) {
|
|
if (!code) return '';
|
|
if (!this._menuIdCache) {
|
|
const nav = await this.fetchHomeNav().catch(() => ({ data: [] }));
|
|
this._menuIdCache = {};
|
|
(nav.data || []).forEach((item) => {
|
|
if (item.code && item.id) this._menuIdCache[item.code] = item.id;
|
|
});
|
|
}
|
|
return this._menuIdCache[code] || '';
|
|
},
|
|
|
|
async loadLinksCompat() {
|
|
const [friend, other] = await Promise.all([
|
|
this.get('/api/web/link/friend').catch(() => ({ data: [] })),
|
|
this.get('/api/web/link/other-recommend').catch(() => ({ data: [] })),
|
|
]);
|
|
const friendList = (friend.data || []).map((item) => ({ ...item, type: 1 }));
|
|
const otherList = (other.data || []).map((item) => ({ ...item, type: 2 }));
|
|
return { code: 0, success: true, data: friendList.concat(otherList) };
|
|
},
|
|
|
|
async loadHomeIndexCompat() {
|
|
const [configList, config, copyright, siteConfig, menus, banners, notices, ads, freeArticles, links, latest] = await Promise.all([
|
|
this.get('/api/web/config/list').catch(() => ({ data: [] })),
|
|
this.get('/api/web/config').catch(() => ({ data: {} })),
|
|
this.get(this.API.configCopyright).catch(() => ({ data: [] })),
|
|
this.get('/api/web/config/site').catch(() => ({ data: {} })),
|
|
this.loadHomeMenusCompat().catch(() => ({ data: [] })),
|
|
this.get('/api/web/banner/list').catch(() => ({ data: [] })),
|
|
this.get('/api/web/notice/list', { count: 6 }).catch(() => ({ data: [] })),
|
|
this.get('/api/web/ad/list', { inTime: true }).catch(() => ({ data: [] })),
|
|
this.get('/api/web/free-article/recommend', { articleCount: 5 }).catch(() => ({ data: [] })),
|
|
this.loadLinksCompat().catch(() => ({ data: [] })),
|
|
this.get('/api/web/lottery/result/latest').catch(() => ({ data: [] })),
|
|
]);
|
|
|
|
const latestMap = {};
|
|
const lotteryList = this.asArray(this.pickData(latest, []));
|
|
lotteryList.forEach((item) => {
|
|
latestMap[item.code] = {
|
|
...item,
|
|
expect: item.expect,
|
|
name: item.expect,
|
|
time: item.time,
|
|
openCode: item.openCode,
|
|
code: item.openCode,
|
|
red: item.redNumbers,
|
|
blue: item.blueNumbers,
|
|
};
|
|
});
|
|
|
|
const articleGroups = this.asArray(this.pickData(freeArticles, []));
|
|
const articleMap = {};
|
|
articleGroups.forEach((group) => {
|
|
if (Array.isArray(group.articles)) {
|
|
articleMap[group.code] = group.articles.map((article) => ({
|
|
...article,
|
|
code: article.code || group.code,
|
|
lotteryName: article.lotteryName || group.lotteryName,
|
|
accountAvatar: article.accountAvatar || article.expertAvatar || article.avatar,
|
|
createUserName: article.createUserName || article.author || article.nickName || article.issuer,
|
|
}));
|
|
return;
|
|
}
|
|
const code = group.code || group.suoxie;
|
|
if (!code) return;
|
|
if (!articleMap[code]) articleMap[code] = [];
|
|
articleMap[code].push({
|
|
...group,
|
|
accountAvatar: group.accountAvatar || group.expertAvatar || group.avatar,
|
|
createUserName: group.createUserName || group.author || group.nickName || group.issuer,
|
|
});
|
|
});
|
|
|
|
const menuList = this.asArray(this.pickData(menus, [])).map((menu) => ({
|
|
...menu,
|
|
suoxie: menu.suoxie || menu.code,
|
|
url: menu.url || menu.path,
|
|
lotteryResult: latestMap[menu.suoxie || menu.code],
|
|
freeArticleList: articleMap[menu.suoxie || menu.code] || [],
|
|
}));
|
|
|
|
return {
|
|
code: 0,
|
|
success: true,
|
|
data: {
|
|
webConfigs: this.asArray(this.pickData(configList, [])).length
|
|
? this.asArray(this.pickData(configList, []))
|
|
: this.configObjectToList(config.data || {}),
|
|
copyrightConfigs: this.asArray(this.pickData(copyright, [])),
|
|
siteConfig: siteConfig.data || {},
|
|
menuList,
|
|
navList: menuList,
|
|
lotteryList,
|
|
freeArticleData: articleGroups,
|
|
bannerList: this.asArray(this.pickData(banners, [])),
|
|
noticeList: this.asArray(this.pickData(notices, [])),
|
|
adList: this.asArray(this.pickData(ads, [])),
|
|
linkList: this.asArray(this.pickData(links, [])),
|
|
},
|
|
};
|
|
},
|
|
|
|
configObjectToList(config) {
|
|
return Object.keys(config || {}).map((key, index) => ({
|
|
type: key,
|
|
content: config[key],
|
|
sort: index,
|
|
}));
|
|
},
|
|
|
|
get(url, params, options = {}) {
|
|
if (typeof options === 'function') {
|
|
return this.request({ url, method: 'GET', data: params || null }).then(options);
|
|
}
|
|
return this.request({ url, method: 'GET', data: params || null, ...options });
|
|
},
|
|
|
|
post(url, data, options = {}) {
|
|
if (typeof options === 'function') {
|
|
return this.request({ url, method: 'POST', data: data || null }).then(options);
|
|
}
|
|
return this.request({ url, method: 'POST', data: data || null, ...options });
|
|
},
|
|
|
|
put(url, data, options = {}) {
|
|
if (typeof options === 'function') {
|
|
return this.request({ url, method: 'PUT', data: data || null }).then(options);
|
|
}
|
|
return this.request({ url, method: 'PUT', data: data || null, ...options });
|
|
},
|
|
|
|
delete(url, data, options = {}) {
|
|
if (typeof options === 'function') {
|
|
return this.request({ url, method: 'DELETE', data: data || null }).then(options);
|
|
}
|
|
return this.request({ url, method: 'DELETE', data: data || null, ...options });
|
|
},
|
|
};
|