221 lines
6.6 KiB
JavaScript
221 lines
6.6 KiB
JavaScript
/**
|
||
* 授权流程服务
|
||
* 处理多个 H5 授权页面的逐个加载、轮询检测、超时控制和最终跳转
|
||
*/
|
||
|
||
import { ApiClient } from '../core/api.js';
|
||
import { API_CONFIG } from '../config/index.js';
|
||
|
||
// 授权状态常量
|
||
const AUTH_STATUS = {
|
||
WAITING: 1, // 等待授权
|
||
CALLBACK: 2, // 已回调
|
||
APPLY_OK: 3, // 进件成功
|
||
APPLY_FAIL: 4 // 进件失败
|
||
};
|
||
|
||
export class AuthFlowService {
|
||
// 配置常量
|
||
static POLL_INTERVAL = 3000; // 轮询间隔:3秒
|
||
static POLL_TIMEOUT = 10 * 60 * 1000; // 超时时间:10分钟
|
||
static COUNTDOWN_SECONDS = 5; // 倒计时:5秒
|
||
|
||
/**
|
||
* 检查单个 API 的授权状态
|
||
* @param {number} formdataid - 表单数据ID
|
||
* @param {string} apicode - API编码
|
||
* @returns {Promise<Object>} - 状态信息 { apicode, apiname, status, h5url }
|
||
*/
|
||
static async checkAuthStatus(formdataid, apicode) {
|
||
try {
|
||
const response = await ApiClient.xpost(API_CONFIG.ENDPOINTS.CHECK_AUTH_STATUS, {
|
||
formdataid,
|
||
apicode
|
||
});
|
||
|
||
if (response.retcode === 0 && response.result) {
|
||
return response.result;
|
||
}
|
||
|
||
console.warn('[AuthFlowService] 检查授权状态失败:', response.retinfo);
|
||
return null;
|
||
} catch (error) {
|
||
console.error('[AuthFlowService] 检查授权状态出错:', error);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 等待授权完成(轮询)
|
||
* @param {number} formdataid - 表单数据ID
|
||
* @param {string} apicode - API编码
|
||
* @param {Function} onStatusChange - 状态变化回调
|
||
* @returns {Promise<Object>} - { success: boolean, status: number, timeout: boolean }
|
||
*/
|
||
static async waitForAuth(formdataid, apicode, onStatusChange) {
|
||
const startTime = Date.now();
|
||
|
||
while (true) {
|
||
// 检查超时
|
||
if (Date.now() - startTime > this.POLL_TIMEOUT) {
|
||
console.log('[AuthFlowService] 轮询超时:', apicode);
|
||
return { success: false, status: AUTH_STATUS.WAITING, timeout: true };
|
||
}
|
||
|
||
// 查询状态
|
||
const result = await this.checkAuthStatus(formdataid, apicode);
|
||
|
||
if (result) {
|
||
// 通知状态变化
|
||
if (onStatusChange) {
|
||
onStatusChange(result);
|
||
}
|
||
|
||
// 状态 >= 2 表示已回调/成功/失败,可以进入下一个
|
||
if (result.status >= AUTH_STATUS.CALLBACK) {
|
||
return {
|
||
success: result.status === AUTH_STATUS.CALLBACK || result.status === AUTH_STATUS.APPLY_OK,
|
||
status: result.status,
|
||
timeout: false
|
||
};
|
||
}
|
||
}
|
||
|
||
// 等待下次轮询
|
||
await this.sleep(this.POLL_INTERVAL);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 执行授权流程
|
||
* @param {number} formdataid - 表单数据ID
|
||
* @param {Array} h5Urls - H5 URL 列表 [{ apicode, apiname, h5url }]
|
||
* @param {Object} callbacks - 回调函数集合
|
||
* - onStart(item, index) - 开始处理某个产品
|
||
* - onProgress(item, index, status) - 状态更新
|
||
* - onComplete(item, index, result) - 单个产品完成
|
||
* - onAllComplete() - 全部完成
|
||
* - onIframeChange(url) - iframe 需要切换 URL
|
||
* @returns {Promise<void>}
|
||
*/
|
||
static async startAuthFlow(formdataid, h5Urls, callbacks = {}) {
|
||
const { onStart, onProgress, onComplete, onAllComplete, onIframeChange } = callbacks;
|
||
|
||
console.log('[AuthFlowService] 开始授权流程, formdataid:', formdataid, 'h5Urls:', h5Urls);
|
||
|
||
for (let i = 0; i < h5Urls.length; i++) {
|
||
const item = h5Urls[i];
|
||
console.log('[AuthFlowService] 处理第', i + 1, '个产品:', item.apiname);
|
||
|
||
// 通知开始处理
|
||
if (onStart) {
|
||
onStart(item, i);
|
||
}
|
||
|
||
// 切换 iframe
|
||
if (onIframeChange && item.h5url) {
|
||
onIframeChange(item.h5url);
|
||
}
|
||
|
||
// 轮询等待授权完成
|
||
const result = await this.waitForAuth(formdataid, item.apicode, (status) => {
|
||
if (onProgress) {
|
||
onProgress(item, i, status);
|
||
}
|
||
});
|
||
|
||
console.log('[AuthFlowService] 产品授权结果:', item.apiname, result);
|
||
|
||
// 通知单个完成
|
||
if (onComplete) {
|
||
onComplete(item, i, result);
|
||
}
|
||
}
|
||
|
||
// 全部完成
|
||
console.log('[AuthFlowService] 全部授权流程完成');
|
||
if (onAllComplete) {
|
||
onAllComplete();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 开始倒计时
|
||
* @param {number} seconds - 倒计时秒数
|
||
* @param {Function} onTick - 每秒回调,参数为剩余秒数
|
||
* @param {Function} onComplete - 倒计时完成回调
|
||
* @returns {Function} - 取消函数
|
||
*/
|
||
static startCountdown(seconds, onTick, onComplete) {
|
||
let remaining = seconds;
|
||
let cancelled = false;
|
||
|
||
const tick = () => {
|
||
if (cancelled) return;
|
||
|
||
if (onTick) {
|
||
onTick(remaining);
|
||
}
|
||
|
||
if (remaining <= 0) {
|
||
if (onComplete) {
|
||
onComplete();
|
||
}
|
||
return;
|
||
}
|
||
|
||
remaining--;
|
||
setTimeout(tick, 1000);
|
||
};
|
||
|
||
tick();
|
||
|
||
// 返回取消函数
|
||
return () => {
|
||
cancelled = true;
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 跳转到目标页面
|
||
* @param {string} url - 目标 URL
|
||
*/
|
||
static redirect(url) {
|
||
if (url) {
|
||
window.location.href = url;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 辅助函数:延时
|
||
* @param {number} ms - 毫秒
|
||
* @returns {Promise}
|
||
*/
|
||
static sleep(ms) {
|
||
return new Promise(resolve => setTimeout(resolve, ms));
|
||
}
|
||
|
||
/**
|
||
* 获取状态文本
|
||
* @param {number} status - 状态码
|
||
* @returns {string}
|
||
*/
|
||
static getStatusText(status) {
|
||
switch (status) {
|
||
case AUTH_STATUS.WAITING:
|
||
return '等待授权';
|
||
case AUTH_STATUS.CALLBACK:
|
||
return '已完成';
|
||
case AUTH_STATUS.APPLY_OK:
|
||
return '授权成功';
|
||
case AUTH_STATUS.APPLY_FAIL:
|
||
return '授权失败';
|
||
default:
|
||
return '未知状态';
|
||
}
|
||
}
|
||
}
|
||
|
||
// 导出状态常量
|
||
export { AUTH_STATUS };
|