优化了qq微信切换
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
import { Router } from 'express'
|
||||
|
||||
import {
|
||||
closeClaimSession,
|
||||
confirmClaimRole,
|
||||
createClaimSession,
|
||||
getClaimDetail,
|
||||
getClaimScreenshotPath,
|
||||
getClaimSessionSummary,
|
||||
reloadClaimSession,
|
||||
redeemClaimTask,
|
||||
} from '../services/claim/claim-session-service.js'
|
||||
import { buildNotFoundPayload, buildSuccessPayload, sendRouteError } from '../utils/http.js'
|
||||
@@ -39,6 +41,24 @@ router.get('/:token/session/summary', async (req, res) => {
|
||||
}
|
||||
})
|
||||
|
||||
router.post('/:token/session/refresh', async (req, res) => {
|
||||
try {
|
||||
const data = await reloadClaimSession(req.params.token)
|
||||
res.json(buildSuccessPayload(data, data.session ? (data.session.notice || '后端页面已刷新') : '领取会话已重置'))
|
||||
} catch (error) {
|
||||
sendRouteError(res, error, '刷新领取会话失败', '[claims/:token/session/refresh]')
|
||||
}
|
||||
})
|
||||
|
||||
router.delete('/:token/session', async (req, res) => {
|
||||
try {
|
||||
const data = await closeClaimSession(req.params.token)
|
||||
res.json(buildSuccessPayload(data, '领取会话已关闭'))
|
||||
} catch (error) {
|
||||
sendRouteError(res, error, '关闭领取会话失败', '[claims/:token/session]')
|
||||
}
|
||||
})
|
||||
|
||||
router.post('/:token/confirm-role', async (req, res) => {
|
||||
try {
|
||||
const data = await confirmClaimRole(req.params.token)
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import {
|
||||
closeTencentBrowserSession,
|
||||
createTencentBrowserSession,
|
||||
getTencentBrowserSession,
|
||||
getTencentBrowserSessionSummary,
|
||||
getTencentBrowserSessionScreenshotPath,
|
||||
reloadTencentBrowserSession,
|
||||
redeemTencentBrowserSession,
|
||||
} from '../session/session.js'
|
||||
import { findClaimTokenByToken, getClaimTokenById, updateClaimToken } from '../../repositories/claim-token-repo.js'
|
||||
@@ -24,8 +26,8 @@ const CLAIM_TERMINAL_STATUSES = new Set(['expired', 'closed'])
|
||||
|
||||
export async function getClaimDetail(token, { includeQrImage = true } = {}) {
|
||||
const context = await getClaimContext(token)
|
||||
const session = await loadTaskSession(context.task, { includeQrImage })
|
||||
const syncedTask = session ? await syncTaskWithSession(context.task, session) : context.task
|
||||
const { task, session } = await loadTaskSession(context.task, { includeQrImage })
|
||||
const syncedTask = session ? await syncTaskWithSession(task, session) : task
|
||||
|
||||
return buildClaimDetailPayload({
|
||||
claimToken: context.claimToken,
|
||||
@@ -39,30 +41,54 @@ export async function getClaimDetail(token, { includeQrImage = true } = {}) {
|
||||
export async function createClaimSession(token, payload = {}) {
|
||||
const context = await getClaimContext(token)
|
||||
assertTaskCanProceed(context.task)
|
||||
const requestedLoginType = normalizeClaimLoginType(payload.loginType)
|
||||
const forceRecreate = Boolean(payload.forceRecreate)
|
||||
let task = context.task
|
||||
|
||||
if (context.task.browser_session_id) {
|
||||
const existingSession = await getTencentBrowserSession(context.task.browser_session_id)
|
||||
const syncedTask = await syncTaskWithSession(context.task, existingSession)
|
||||
if (task.browser_session_id) {
|
||||
const existing = await loadTaskSession(task, { includeQrImage: true })
|
||||
task = existing.task
|
||||
|
||||
return buildClaimDetailPayload({
|
||||
claimToken: context.claimToken,
|
||||
task: syncedTask,
|
||||
order: context.order,
|
||||
orderItem: context.orderItem,
|
||||
session: existingSession,
|
||||
})
|
||||
if (existing.session) {
|
||||
const existingLoginType = normalizeClaimLoginType(existing.session.loginType || task.login_type)
|
||||
|
||||
if (!forceRecreate && existingLoginType === requestedLoginType) {
|
||||
const syncedTask = await syncTaskWithSession(task, existing.session)
|
||||
|
||||
return buildClaimDetailPayload({
|
||||
claimToken: context.claimToken,
|
||||
task: syncedTask,
|
||||
order: context.order,
|
||||
orderItem: context.orderItem,
|
||||
session: existing.session,
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
await closeTencentBrowserSession(existing.session.sessionId)
|
||||
} catch (error) {
|
||||
if (!isRecoverableSessionError(error)) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
task = await clearTaskSession(task, {
|
||||
lastError: '',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const session = await createTencentBrowserSession({
|
||||
loginType: payload.loginType,
|
||||
loginType: requestedLoginType,
|
||||
})
|
||||
const updatedTask = await updateTask(context.task.id, {
|
||||
const updatedTask = await updateTask(task.id, {
|
||||
task_status: 'claimed',
|
||||
browser_session_id: session.sessionId,
|
||||
login_type: session.loginType,
|
||||
user_action_status: 'claimed',
|
||||
claimed_at: context.task.claimed_at || nowIso(),
|
||||
claimed_at: task.claimed_at || nowIso(),
|
||||
updated_at: nowIso(),
|
||||
last_error: '',
|
||||
})
|
||||
|
||||
return buildClaimDetailPayload({
|
||||
@@ -76,16 +102,21 @@ export async function createClaimSession(token, payload = {}) {
|
||||
|
||||
export async function getClaimSessionSummary(token) {
|
||||
const context = await getClaimContext(token)
|
||||
const { task, session } = await loadTaskSession(context.task, {
|
||||
includeQrImage: false,
|
||||
})
|
||||
|
||||
if (!context.task.browser_session_id) {
|
||||
throw createHttpError('当前任务还没有创建浏览器会话', {
|
||||
statusCode: 409,
|
||||
errorCode: 'claim_session_not_created',
|
||||
if (!session) {
|
||||
return buildClaimDetailPayload({
|
||||
claimToken: context.claimToken,
|
||||
task,
|
||||
order: context.order,
|
||||
orderItem: context.orderItem,
|
||||
session: null,
|
||||
})
|
||||
}
|
||||
|
||||
const session = await getTencentBrowserSession(context.task.browser_session_id)
|
||||
const syncedTask = await syncTaskWithSession(context.task, session)
|
||||
const syncedTask = await syncTaskWithSession(task, session)
|
||||
|
||||
return buildClaimDetailPayload({
|
||||
claimToken: context.claimToken,
|
||||
@@ -96,6 +127,72 @@ export async function getClaimSessionSummary(token) {
|
||||
})
|
||||
}
|
||||
|
||||
export async function reloadClaimSession(token) {
|
||||
const context = await getClaimContext(token)
|
||||
assertTaskCanProceed(context.task)
|
||||
|
||||
const active = await loadTaskSession(context.task, {
|
||||
includeQrImage: true,
|
||||
})
|
||||
|
||||
if (!active.session) {
|
||||
return buildClaimDetailPayload({
|
||||
claimToken: context.claimToken,
|
||||
task: active.task,
|
||||
order: context.order,
|
||||
orderItem: context.orderItem,
|
||||
session: null,
|
||||
})
|
||||
}
|
||||
|
||||
const session = await reloadTencentBrowserSession(active.session.sessionId)
|
||||
const syncedTask = await syncTaskWithSession(active.task, session)
|
||||
|
||||
return buildClaimDetailPayload({
|
||||
claimToken: context.claimToken,
|
||||
task: syncedTask,
|
||||
order: context.order,
|
||||
orderItem: context.orderItem,
|
||||
session,
|
||||
})
|
||||
}
|
||||
|
||||
export async function closeClaimSession(token) {
|
||||
const context = await getClaimContext(token)
|
||||
assertTaskCanProceed(context.task)
|
||||
let task = context.task
|
||||
|
||||
if (!task.browser_session_id) {
|
||||
return buildClaimDetailPayload({
|
||||
claimToken: context.claimToken,
|
||||
task,
|
||||
order: context.order,
|
||||
orderItem: context.orderItem,
|
||||
session: null,
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
await closeTencentBrowserSession(task.browser_session_id)
|
||||
} catch (error) {
|
||||
if (!isRecoverableSessionError(error)) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
task = await clearTaskSession(task, {
|
||||
lastError: '',
|
||||
})
|
||||
|
||||
return buildClaimDetailPayload({
|
||||
claimToken: context.claimToken,
|
||||
task,
|
||||
order: context.order,
|
||||
orderItem: context.orderItem,
|
||||
session: null,
|
||||
})
|
||||
}
|
||||
|
||||
export async function confirmClaimRole(token) {
|
||||
const context = await getClaimContext(token)
|
||||
assertPublicClaimActionAllowed(context.task, 'confirm')
|
||||
@@ -430,12 +527,35 @@ async function expireClaimContext(claimToken, task) {
|
||||
|
||||
async function loadTaskSession(task, { includeQrImage = false } = {}) {
|
||||
if (!task.browser_session_id) {
|
||||
return null
|
||||
return {
|
||||
task,
|
||||
session: null,
|
||||
}
|
||||
}
|
||||
|
||||
return includeQrImage
|
||||
? getTencentBrowserSession(task.browser_session_id)
|
||||
: getTencentBrowserSessionSummary(task.browser_session_id)
|
||||
try {
|
||||
const session = includeQrImage
|
||||
? await getTencentBrowserSession(task.browser_session_id)
|
||||
: await getTencentBrowserSessionSummary(task.browser_session_id)
|
||||
|
||||
return {
|
||||
task,
|
||||
session,
|
||||
}
|
||||
} catch (error) {
|
||||
if (!isRecoverableSessionError(error)) {
|
||||
throw error
|
||||
}
|
||||
|
||||
const nextTask = await clearTaskSession(task, {
|
||||
lastError: '浏览器会话已失效,请重新初始化登录',
|
||||
})
|
||||
|
||||
return {
|
||||
task: nextTask,
|
||||
session: null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function syncTaskWithSession(task, session) {
|
||||
@@ -550,3 +670,36 @@ function parseTaskState(task) {
|
||||
function isAssistedClaimTask(task) {
|
||||
return String(task?.executor_key || '').trim() === 'tencent_claim_assisted'
|
||||
}
|
||||
|
||||
async function clearTaskSession(task, { lastError = '' } = {}) {
|
||||
const shouldResetClaimProgress = ['link_generated', 'claimed', 'role_confirmed'].includes(String(task.task_status || ''))
|
||||
const nextTaskStatus = shouldResetClaimProgress ? 'link_generated' : task.task_status
|
||||
const nextUserActionStatus = shouldResetClaimProgress ? 'pending_claim' : task.user_action_status
|
||||
const patch = {
|
||||
task_status: nextTaskStatus,
|
||||
user_action_status: nextUserActionStatus,
|
||||
browser_session_id: '',
|
||||
login_type: '',
|
||||
nickname: '',
|
||||
role_id: '',
|
||||
role_name: '',
|
||||
area: '',
|
||||
partition_name: '',
|
||||
artifacts_json: '{}',
|
||||
state_json: '{}',
|
||||
role_confirmed_at: nextTaskStatus === 'link_generated' ? null : task.role_confirmed_at,
|
||||
last_error: String(lastError || ''),
|
||||
updated_at: nowIso(),
|
||||
}
|
||||
|
||||
return updateTask(task.id, patch)
|
||||
}
|
||||
|
||||
function normalizeClaimLoginType(loginType) {
|
||||
return String(loginType || '').trim() === 'wx' ? 'wx' : 'qq'
|
||||
}
|
||||
|
||||
function isRecoverableSessionError(error) {
|
||||
const errorCode = String(error?.errorCode || error?.code || '').trim()
|
||||
return errorCode === 'session_not_found' || errorCode === 'session_closed'
|
||||
}
|
||||
|
||||
@@ -172,6 +172,7 @@ export async function reloadTencentBrowserSession(sessionId) {
|
||||
session.qrImagePath = ''
|
||||
session.qrUpdatedAt = ''
|
||||
await ensureLoginTab(session.page, session.loginType)
|
||||
await captureSessionQr(session)
|
||||
}
|
||||
|
||||
session.notice = credentialReady
|
||||
@@ -247,6 +248,11 @@ export async function refreshTencentBrowserSession(sessionId, { includeQrImage =
|
||||
session.expiresAt = Date.now() + SESSION_TTL_MS
|
||||
|
||||
if (!session.qrImageBase64 || session.status === 'expired') {
|
||||
if (session.status === 'expired') {
|
||||
await reloadActivityPageForPresentation(session.page)
|
||||
await ensureLoginTab(session.page, session.loginType)
|
||||
}
|
||||
|
||||
await captureSessionQr(session)
|
||||
if (session.status === 'expired') {
|
||||
session.notice = '二维码已刷新,请重新扫码'
|
||||
|
||||
@@ -23,6 +23,7 @@ const props = defineProps<{
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
closeSession: []
|
||||
createSession: [loginType: TencentLoginType]
|
||||
openQrPreview: []
|
||||
qrImageLoad: [event: Event]
|
||||
@@ -110,23 +111,33 @@ const emit = defineEmits<{
|
||||
<el-button
|
||||
:loading="props.sessionLoading"
|
||||
class="primary-action"
|
||||
round
|
||||
size="large"
|
||||
type="primary"
|
||||
@click="emit('createSession', props.loginType)"
|
||||
>
|
||||
{{ props.hasSession ? '重新生成二维码' : props.initButtonLabel }}
|
||||
</el-button>
|
||||
<el-button
|
||||
:disabled="!props.hasSession"
|
||||
:loading="props.sessionLoading"
|
||||
class="secondary-action"
|
||||
round
|
||||
size="large"
|
||||
@click="emit('reloadSession')"
|
||||
>
|
||||
刷新后端页面
|
||||
</el-button>
|
||||
|
||||
<div class="secondary-actions">
|
||||
<el-button
|
||||
:disabled="!props.hasSession"
|
||||
:loading="props.sessionLoading"
|
||||
class="secondary-action"
|
||||
size="large"
|
||||
@click="emit('reloadSession')"
|
||||
>
|
||||
刷新后端页面
|
||||
</el-button>
|
||||
<el-button
|
||||
:disabled="!props.hasSession"
|
||||
:loading="props.sessionLoading"
|
||||
class="tertiary-action"
|
||||
size="large"
|
||||
@click="emit('closeSession')"
|
||||
>
|
||||
关闭当前会话
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -334,30 +345,78 @@ const emit = defineEmits<{
|
||||
|
||||
.action-row {
|
||||
margin-top: 16px;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.45fr) minmax(132px, 0.95fr);
|
||||
gap: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.primary-action :deep(span) {
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
.action-row :deep(.el-button) {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
margin: 0;
|
||||
height: 52px;
|
||||
border-radius: 18px;
|
||||
}
|
||||
|
||||
.action-row :deep(.el-button > span) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.secondary-actions {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.primary-action,
|
||||
.secondary-action {
|
||||
.secondary-action,
|
||||
.tertiary-action {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.action-row :deep(.primary-action.el-button) {
|
||||
box-shadow: 0 16px 30px rgba(91, 143, 240, 0.24);
|
||||
}
|
||||
|
||||
.action-row :deep(.primary-action.el-button:not(.is-disabled)) {
|
||||
background: linear-gradient(135deg, #6da9fb 0%, #4f85eb 100%);
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.action-row :deep(.secondary-action.el-button),
|
||||
.action-row :deep(.tertiary-action.el-button) {
|
||||
background: rgba(248, 251, 255, 0.98);
|
||||
border: 1px solid #d9e4f2;
|
||||
color: #4a5e79;
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.action-row :deep(.secondary-action.el-button:hover),
|
||||
.action-row :deep(.secondary-action.el-button:focus-visible) {
|
||||
border-color: #9dc0f6;
|
||||
color: #2859b8;
|
||||
background: #f3f8ff;
|
||||
}
|
||||
|
||||
.action-row :deep(.tertiary-action.el-button:hover),
|
||||
.action-row :deep(.tertiary-action.el-button:focus-visible) {
|
||||
border-color: #efb4b4;
|
||||
color: #b43c3c;
|
||||
background: #fff6f6;
|
||||
}
|
||||
|
||||
.action-row :deep(.secondary-action.el-button.is-disabled),
|
||||
.action-row :deep(.tertiary-action.el-button.is-disabled) {
|
||||
background: #f8fafc;
|
||||
border-color: #e5ebf3;
|
||||
color: #9aa8bc;
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.auth-card {
|
||||
padding: 18px;
|
||||
@@ -372,13 +431,7 @@ const emit = defineEmits<{
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.session-strip,
|
||||
.action-row {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.action-row {
|
||||
.secondary-actions {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,16 @@ import {
|
||||
createClaimSession,
|
||||
fetchClaimDetail,
|
||||
fetchClaimSessionSummary,
|
||||
refreshClaimSession,
|
||||
removeClaimSession,
|
||||
redeemClaim,
|
||||
} from '@/services/claim'
|
||||
import type { ClaimDetailData, ClaimTaskStatus } from '@/types/claim'
|
||||
import type { TencentLoginType } from '@/types/tencent/session'
|
||||
import type {
|
||||
TencentBrowserSessionData,
|
||||
TencentBrowserSessionSummaryData,
|
||||
TencentLoginType,
|
||||
} from '@/types/tencent/session'
|
||||
|
||||
import { notifyTencentActionError } from './tencent/session-errors'
|
||||
|
||||
@@ -163,7 +169,7 @@ export function useClaimPage(token: string) {
|
||||
)
|
||||
|
||||
function applyLoginTypeFromDetail(nextDetail: ClaimDetailData) {
|
||||
loginType.value = syncLoginTypeFromDetail(nextDetail)
|
||||
loginType.value = syncLoginTypeFromDetail(nextDetail, loginType.value)
|
||||
}
|
||||
|
||||
watch(
|
||||
@@ -182,6 +188,10 @@ export function useClaimPage(token: string) {
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
watch(qrImage, () => {
|
||||
qrImageNaturalWidth.value = 0
|
||||
})
|
||||
|
||||
async function loadDetail() {
|
||||
detailLoading.value = true
|
||||
|
||||
@@ -209,7 +219,9 @@ export function useClaimPage(token: string) {
|
||||
|
||||
try {
|
||||
loginType.value = nextLoginType
|
||||
const response = await createClaimSession(token, nextLoginType)
|
||||
const response = await createClaimSession(token, nextLoginType, {
|
||||
forceRecreate: hasSession.value,
|
||||
})
|
||||
|
||||
if (response.code !== 0) {
|
||||
throw new Error(response.msg || '创建领取会话失败')
|
||||
@@ -225,6 +237,75 @@ export function useClaimPage(token: string) {
|
||||
}
|
||||
}
|
||||
|
||||
async function reloadSessionPage() {
|
||||
if (!hasSession.value) {
|
||||
return false
|
||||
}
|
||||
|
||||
sessionLoading.value = true
|
||||
resetPolling()
|
||||
resetPollingWarning()
|
||||
|
||||
try {
|
||||
const response = await refreshClaimSession(token)
|
||||
|
||||
if (response.code !== 0) {
|
||||
throw new Error(response.msg || '刷新后端页面失败')
|
||||
}
|
||||
|
||||
detail.value = mergeClaimDetailData(detail.value, response.data)
|
||||
applyLoginTypeFromDetail(response.data)
|
||||
restartPollingIfNeeded(response.data)
|
||||
return true
|
||||
} catch (error) {
|
||||
notifyTencentActionError(error, '刷新后端页面失败')
|
||||
await refreshSessionSummary({ silent: true })
|
||||
restartPollingIfNeeded()
|
||||
return false
|
||||
} finally {
|
||||
sessionLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function closeSessionFlow({ silent = false } = {}) {
|
||||
if (!hasSession.value) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (!silent) {
|
||||
sessionLoading.value = true
|
||||
}
|
||||
|
||||
resetPolling()
|
||||
resetPollingWarning()
|
||||
|
||||
try {
|
||||
const response = await removeClaimSession(token)
|
||||
|
||||
if (response.code !== 0) {
|
||||
throw new Error(response.msg || '关闭领取会话失败')
|
||||
}
|
||||
|
||||
detail.value = mergeClaimDetailData(detail.value, response.data)
|
||||
applyLoginTypeFromDetail(response.data)
|
||||
|
||||
if (!silent) {
|
||||
showSuccess(response.msg || '领取会话已关闭')
|
||||
}
|
||||
|
||||
return true
|
||||
} catch (error) {
|
||||
if (!silent) {
|
||||
notifyTencentActionError(error, '关闭领取会话失败')
|
||||
}
|
||||
return false
|
||||
} finally {
|
||||
if (!silent) {
|
||||
sessionLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshSessionSummary({ silent = false } = {}) {
|
||||
if (!hasSession.value) {
|
||||
return false
|
||||
@@ -236,14 +317,28 @@ export function useClaimPage(token: string) {
|
||||
}
|
||||
|
||||
try {
|
||||
const currentSession = detail.value?.session || null
|
||||
const response = await fetchClaimSessionSummary(token)
|
||||
|
||||
if (response.code !== 0) {
|
||||
throw new Error(response.msg || '领取会话状态刷新失败')
|
||||
}
|
||||
|
||||
detail.value = mergeClaimDetailData(detail.value, response.data)
|
||||
applyLoginTypeFromDetail(response.data)
|
||||
let nextDetail = mergeClaimDetailData(detail.value, response.data)
|
||||
detail.value = nextDetail
|
||||
applyLoginTypeFromDetail(nextDetail)
|
||||
|
||||
if (shouldRefreshClaimQrImage(currentSession, response.data)) {
|
||||
const fullResponse = await fetchClaimDetail(token)
|
||||
|
||||
if (fullResponse.code !== 0) {
|
||||
throw new Error(fullResponse.msg || '领取二维码刷新失败')
|
||||
}
|
||||
|
||||
nextDetail = mergeClaimDetailData(nextDetail, fullResponse.data)
|
||||
detail.value = nextDetail
|
||||
applyLoginTypeFromDetail(nextDetail)
|
||||
}
|
||||
|
||||
if (silent) {
|
||||
resetPollingWarning()
|
||||
@@ -324,11 +419,12 @@ export function useClaimPage(token: string) {
|
||||
return
|
||||
}
|
||||
|
||||
loginType.value = nextLoginType
|
||||
|
||||
if (hasSession.value) {
|
||||
await createSessionFlow(nextLoginType)
|
||||
return
|
||||
}
|
||||
|
||||
loginType.value = nextLoginType
|
||||
}
|
||||
|
||||
function startPolling() {
|
||||
@@ -461,6 +557,8 @@ export function useClaimPage(token: string) {
|
||||
screenshotUrl,
|
||||
showScreenshot,
|
||||
createSessionFlow,
|
||||
reloadSessionPage,
|
||||
closeSessionFlow,
|
||||
refreshSessionSummary,
|
||||
switchLoginType,
|
||||
confirmRoleNow,
|
||||
@@ -469,9 +567,17 @@ export function useClaimPage(token: string) {
|
||||
}
|
||||
}
|
||||
|
||||
function syncLoginTypeFromDetail(detail: ClaimDetailData) {
|
||||
function syncLoginTypeFromDetail(detail: ClaimDetailData, fallback: TencentLoginType = DEFAULT_LOGIN_TYPE) {
|
||||
const nextLoginType = String(detail.session?.loginType || detail.task.loginType || '').trim()
|
||||
return nextLoginType === 'wx' ? 'wx' : 'qq'
|
||||
if (nextLoginType === 'wx') {
|
||||
return 'wx'
|
||||
}
|
||||
|
||||
if (nextLoginType === 'qq') {
|
||||
return 'qq'
|
||||
}
|
||||
|
||||
return fallback
|
||||
}
|
||||
|
||||
function shouldKeepPolling(detail: ClaimDetailData | null) {
|
||||
@@ -516,17 +622,17 @@ function resolveTaskStatusLabel(taskStatus?: string, sessionStatus?: string) {
|
||||
}
|
||||
|
||||
function mergeClaimDetailData(current: ClaimDetailData | null, next: ClaimDetailData) {
|
||||
if (!current?.session) {
|
||||
return next
|
||||
}
|
||||
|
||||
if (!next.session) {
|
||||
if (next.session === null) {
|
||||
return {
|
||||
...next,
|
||||
session: current.session,
|
||||
session: null,
|
||||
}
|
||||
}
|
||||
|
||||
if (!current?.session) {
|
||||
return next
|
||||
}
|
||||
|
||||
return {
|
||||
...next,
|
||||
session: {
|
||||
@@ -542,3 +648,29 @@ function mergeClaimDetailData(current: ClaimDetailData | null, next: ClaimDetail
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function shouldRefreshClaimQrImage(
|
||||
currentSession: ClaimDetailData['session'],
|
||||
nextDetail: ClaimDetailData,
|
||||
) {
|
||||
if (!currentSession || !nextDetail.session) {
|
||||
return false
|
||||
}
|
||||
|
||||
return shouldRefreshQrImage(currentSession, nextDetail.session)
|
||||
}
|
||||
|
||||
function shouldRefreshQrImage(
|
||||
current: TencentBrowserSessionData | TencentBrowserSessionSummaryData,
|
||||
next: TencentBrowserSessionData | TencentBrowserSessionSummaryData,
|
||||
) {
|
||||
if (!next.artifacts?.hasQrImage) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (!current.qrImageBase64) {
|
||||
return true
|
||||
}
|
||||
|
||||
return Boolean(next.qrUpdatedAt && next.qrUpdatedAt !== current.qrUpdatedAt)
|
||||
}
|
||||
|
||||
@@ -210,6 +210,7 @@ export function useTencentBrowserSessionPage() {
|
||||
return {
|
||||
activityInfo,
|
||||
canRedeem,
|
||||
teardownSession,
|
||||
createSessionFlow,
|
||||
hasSession,
|
||||
initButtonLabel,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { apiGet, apiPost } from '@/lib/http'
|
||||
import { apiDelete, apiGet, apiPost } from '@/lib/http'
|
||||
import type { ClaimDetailData } from '@/types/claim'
|
||||
import type { TencentLoginType } from '@/types/tencent/session'
|
||||
|
||||
@@ -6,14 +6,29 @@ export function fetchClaimDetail(token: string) {
|
||||
return apiGet<ClaimDetailData>(`/api/v1/claim/${token}`)
|
||||
}
|
||||
|
||||
export function createClaimSession(token: string, loginType: TencentLoginType) {
|
||||
return apiPost<ClaimDetailData>(`/api/v1/claim/${token}/session`, { loginType })
|
||||
export function createClaimSession(
|
||||
token: string,
|
||||
loginType: TencentLoginType,
|
||||
options: { forceRecreate?: boolean } = {},
|
||||
) {
|
||||
return apiPost<ClaimDetailData>(`/api/v1/claim/${token}/session`, {
|
||||
loginType,
|
||||
forceRecreate: Boolean(options.forceRecreate),
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchClaimSessionSummary(token: string) {
|
||||
return apiGet<ClaimDetailData>(`/api/v1/claim/${token}/session/summary`)
|
||||
}
|
||||
|
||||
export function refreshClaimSession(token: string) {
|
||||
return apiPost<ClaimDetailData>(`/api/v1/claim/${token}/session/refresh`, {})
|
||||
}
|
||||
|
||||
export function removeClaimSession(token: string) {
|
||||
return apiDelete<ClaimDetailData>(`/api/v1/claim/${token}/session`)
|
||||
}
|
||||
|
||||
export function confirmClaimRole(token: string) {
|
||||
return apiPost<ClaimDetailData>(`/api/v1/claim/${token}/confirm-role`, { confirm: true })
|
||||
}
|
||||
|
||||
@@ -6,10 +6,12 @@ export type TencentLoginType = 'qq' | 'wx'
|
||||
export type TencentBrowserSessionStatus =
|
||||
| 'waiting_scan'
|
||||
| 'scanned'
|
||||
| 'expired'
|
||||
| 'logged_in'
|
||||
| 'ready_to_redeem'
|
||||
| 'redeeming'
|
||||
| 'redeemed'
|
||||
| 'closed'
|
||||
| 'failed'
|
||||
| (string & {})
|
||||
|
||||
|
||||
@@ -43,7 +43,8 @@ const {
|
||||
screenshotUrl,
|
||||
showScreenshot,
|
||||
createSessionFlow,
|
||||
refreshSessionSummary,
|
||||
reloadSessionPage,
|
||||
closeSessionFlow,
|
||||
switchLoginType,
|
||||
confirmRoleNow,
|
||||
redeemNow,
|
||||
@@ -127,9 +128,10 @@ const helperText = computed(() => {
|
||||
:session-loading="sessionLoading"
|
||||
:session-notice="sessionNotice"
|
||||
@create-session="createSessionFlow"
|
||||
@close-session="closeSessionFlow"
|
||||
@open-qr-preview="qrPreviewVisible = true"
|
||||
@qr-image-load="handleQrImageLoad"
|
||||
@reload-session="refreshSessionSummary()"
|
||||
@reload-session="reloadSessionPage"
|
||||
@switch-login-type="switchLoginType"
|
||||
/>
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ const {
|
||||
sessionNotice,
|
||||
statusLabel,
|
||||
switchLoginType,
|
||||
teardownSession,
|
||||
} = useTencentBrowserSessionPage()
|
||||
|
||||
const qrPreviewVisible = ref(false)
|
||||
@@ -109,6 +110,7 @@ const showScreenshot = computed(() => Boolean(screenshotUrl.value))
|
||||
:session-loading="sessionLoading"
|
||||
:session-notice="sessionNotice"
|
||||
@create-session="createSessionFlow"
|
||||
@close-session="teardownSession"
|
||||
@open-qr-preview="qrPreviewVisible = true"
|
||||
@qr-image-load="handleQrImageLoad"
|
||||
@reload-session="reloadSessionPage"
|
||||
|
||||
Reference in New Issue
Block a user