feat: migrate app routes and business modules
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
import { ref } from "vue";
|
||||
import {
|
||||
createRequestController,
|
||||
isRequestCancelled
|
||||
} from "@/services/api/request-controller.js";
|
||||
import { authApi } from "@/services/api/auth-service.js";
|
||||
import { createAuthSmsCooldown } from "@/utils/auth/sms-cooldown.js";
|
||||
import {
|
||||
createTacRenderContext,
|
||||
isAuthPhone,
|
||||
isSmsDeliveryOutcomeUnknown,
|
||||
normalizeCaptchaRequirement,
|
||||
normalizeTacSuccess,
|
||||
} from "@/utils/auth/verification.js";
|
||||
import { runtimeConfig } from "@/utils/runtime-config.js";
|
||||
|
||||
export const useSmsVerification = ({
|
||||
operationCode,
|
||||
requestIdPrefix,
|
||||
phone,
|
||||
isActive,
|
||||
showFeedback,
|
||||
getErrorMessage = (error, fallback) => error?.message || fallback,
|
||||
}) => {
|
||||
if (!phone || typeof phone !== "object" || !("value" in phone)) {
|
||||
throw new TypeError("phone must be a Vue ref");
|
||||
}
|
||||
if (
|
||||
typeof isActive !== "function" ||
|
||||
typeof showFeedback !== "function" ||
|
||||
typeof getErrorMessage !== "function"
|
||||
) {
|
||||
throw new TypeError("isActive, showFeedback and getErrorMessage must be functions");
|
||||
}
|
||||
|
||||
const tacVisible = ref(false);
|
||||
const tacContext = ref(null);
|
||||
const sendingCode = ref(false);
|
||||
const cooldownSeconds = ref(0);
|
||||
const sentPhone = ref("");
|
||||
const captchaRequirementRequestController = createRequestController();
|
||||
const smsDeliveryRequestController = createRequestController();
|
||||
const smsCooldown = createAuthSmsCooldown({
|
||||
operationCode,
|
||||
onChange: (seconds) => {
|
||||
cooldownSeconds.value = seconds;
|
||||
},
|
||||
});
|
||||
let requestSequence = 0;
|
||||
let disposed = false;
|
||||
|
||||
const canCommit = () => !disposed && isActive();
|
||||
|
||||
const closeTac = () => {
|
||||
tacVisible.value = false;
|
||||
tacContext.value = null;
|
||||
};
|
||||
|
||||
const markSmsSent = (requestedPhone, message = "验证码已发送") => {
|
||||
if (!canCommit()) return;
|
||||
sentPhone.value = requestedPhone;
|
||||
smsCooldown.start();
|
||||
showFeedback(message);
|
||||
};
|
||||
|
||||
const requestCode = async () => {
|
||||
if (sendingCode.value || cooldownSeconds.value > 0) return false;
|
||||
if (!isAuthPhone(phone.value)) return false;
|
||||
|
||||
const requestedPhone = phone.value;
|
||||
let smsDeliveryStarted = false;
|
||||
sendingCode.value = true;
|
||||
try {
|
||||
const requirementResponse = await authApi.getCaptchaRequirement(
|
||||
{ operationCode, subject: requestedPhone },
|
||||
{ requestController: captchaRequirementRequestController },
|
||||
);
|
||||
if (!canCommit()) return false;
|
||||
if (phone.value !== requestedPhone) {
|
||||
throw new Error("手机号已变化,请重新获取验证码");
|
||||
}
|
||||
const requirement = normalizeCaptchaRequirement(requirementResponse);
|
||||
if (!requirement.required) {
|
||||
smsDeliveryStarted = true;
|
||||
await authApi.sendSmsCode(
|
||||
{ operationCode, phone: requestedPhone },
|
||||
{ requestController: smsDeliveryRequestController },
|
||||
);
|
||||
markSmsSent(requestedPhone);
|
||||
return true;
|
||||
}
|
||||
|
||||
requestSequence += 1;
|
||||
tacContext.value = createTacRenderContext({
|
||||
requestId: `${requestIdPrefix}-${requestSequence}`,
|
||||
baseUrl: runtimeConfig.baseUrl,
|
||||
clientId: runtimeConfig.clientId,
|
||||
tenantId: runtimeConfig.tenantId,
|
||||
operationCode,
|
||||
subject: requestedPhone,
|
||||
requirement,
|
||||
});
|
||||
tacVisible.value = true;
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (canCommit()) {
|
||||
if (smsDeliveryStarted && isSmsDeliveryOutcomeUnknown(error)) {
|
||||
markSmsSent(
|
||||
requestedPhone,
|
||||
"发送结果未知,如收到短信可直接填写;60 秒后可重试",
|
||||
);
|
||||
} else if (!isRequestCancelled(error)) {
|
||||
showFeedback(getErrorMessage(error, "安全验证暂不可用"));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} finally {
|
||||
if (canCommit()) sendingCode.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const completeTac = async (result) => {
|
||||
const expectedContext = tacContext.value;
|
||||
if (!expectedContext) return false;
|
||||
try {
|
||||
const ticket = normalizeTacSuccess(result, expectedContext.requestId);
|
||||
if (phone.value !== expectedContext.subject) {
|
||||
throw new Error("手机号已变化,请重新验证");
|
||||
}
|
||||
closeTac();
|
||||
sendingCode.value = true;
|
||||
await authApi.sendSmsCode(
|
||||
{
|
||||
operationCode,
|
||||
phone: expectedContext.subject,
|
||||
validToken: ticket.validToken,
|
||||
},
|
||||
{ requestController: smsDeliveryRequestController },
|
||||
);
|
||||
markSmsSent(expectedContext.subject);
|
||||
return true;
|
||||
} catch (error) {
|
||||
closeTac();
|
||||
if (canCommit()) {
|
||||
if (isSmsDeliveryOutcomeUnknown(error)) {
|
||||
markSmsSent(
|
||||
expectedContext.subject,
|
||||
"发送结果未知,如收到短信可直接填写;60 秒后可重试",
|
||||
);
|
||||
} else if (!isRequestCancelled(error)) {
|
||||
showFeedback(getErrorMessage(error, "验证码发送失败"));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} finally {
|
||||
if (canCommit()) sendingCode.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleTacFailure = ({ message } = {}) =>
|
||||
showFeedback(message || "行为验证未通过,请重试");
|
||||
|
||||
const handleTacError = ({ message } = {}) => {
|
||||
closeTac();
|
||||
showFeedback(message || "安全验证暂不可用");
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
captchaRequirementRequestController.abort();
|
||||
smsDeliveryRequestController.abort();
|
||||
sendingCode.value = false;
|
||||
};
|
||||
|
||||
const dispose = () => {
|
||||
disposed = true;
|
||||
cancel();
|
||||
closeTac();
|
||||
smsCooldown.dispose();
|
||||
};
|
||||
|
||||
return {
|
||||
tacVisible,
|
||||
tacContext,
|
||||
sendingCode,
|
||||
cooldownSeconds,
|
||||
sentPhone,
|
||||
requestCode,
|
||||
completeTac,
|
||||
closeTac,
|
||||
handleTacFailure,
|
||||
handleTacError,
|
||||
cancel,
|
||||
dispose,
|
||||
syncCooldown: smsCooldown.sync,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user