diff --git a/api/index.js b/api/index.js index 37a9b00..b99ee97 100644 --- a/api/index.js +++ b/api/index.js @@ -1,5 +1,5 @@ export { authApi } from './modules/auth.js'; -export { assetApi, isAssetRealNameCompleted } from './modules/asset.js'; +export { assetApi, hasActiveOrPendingPackage, isAssetRealNameCompleted } from './modules/asset.js'; export { deviceApi } from './modules/device.js'; export { exchangeApi } from './modules/exchange.js'; export { orderApi } from './modules/order.js'; diff --git a/api/modules/asset.js b/api/modules/asset.js index 3d03e34..0dbe581 100644 --- a/api/modules/asset.js +++ b/api/modules/asset.js @@ -27,6 +27,15 @@ export const isAssetRealNameCompleted = (assetInfo = {}) => { return Number(assetInfo.real_name_status) === 1; }; +export const hasActiveOrPendingPackage = async (identifier) => { + const [pendingData, activeData] = await Promise.all([ + assetApi.getPackageHistory(identifier, 1, 1, { status: 0 }), + assetApi.getPackageHistory(identifier, 1, 1, { status: 1 }) + ]); + + return [pendingData, activeData].some(data => Array.isArray(data?.items) && data.items.length > 0); +}; + export const assetApi = { getInfo(identifier) { return request({ diff --git a/openspec/changes/update-july-h5-c-iteration/specs/asset-realname-flow/spec.md b/openspec/changes/update-july-h5-c-iteration/specs/asset-realname-flow/spec.md index 00820e0..7129798 100644 --- a/openspec/changes/update-july-h5-c-iteration/specs/asset-realname-flow/spec.md +++ b/openspec/changes/update-july-h5-c-iteration/specs/asset-realname-flow/spec.md @@ -18,10 +18,17 @@ The H5/C client SHALL use `GET /api/c/v1/asset/info?identifier=...` as the sourc #### Scenario: Real name is required after ordering -- **WHEN** asset information returns `effective_realname_policy = after_order` -- **THEN** the client SHALL allow the policy-defined post-order flow to proceed without applying a before-order block -- **AND** the client SHALL display the returned real-name status -- **AND** selecting the homepage real-name entry SHALL show a confirmation prompt and allow the user to navigate to package ordering instead of entering real-name authentication directly +- **WHEN** the effective real-name policy is `after_order` +- **AND** `/api/c/v1/device/cards` returns no card with completed real-name authentication +- **AND** package history contains neither a pending package (`status = 0`) nor an active package (`status = 1`) +- **THEN** selecting a real-name entry on the homepage or operator-switch page SHALL block direct real-name authentication +- **AND** the client SHALL show a confirmation prompt that can navigate to package ordering + +#### Scenario: Post-order real-name requirement is satisfied + +- **WHEN** the effective real-name policy is `after_order` +- **AND** `/api/c/v1/device/cards` returns at least one authenticated card, or package history contains a pending or active package +- **THEN** the homepage and operator-switch real-name entries SHALL retain the existing real-name authentication flow #### Scenario: Device status is resolved from server output diff --git a/openspec/changes/update-july-h5-c-iteration/tasks.md b/openspec/changes/update-july-h5-c-iteration/tasks.md index 8285d00..fd44341 100644 --- a/openspec/changes/update-july-h5-c-iteration/tasks.md +++ b/openspec/changes/update-july-h5-c-iteration/tasks.md @@ -12,7 +12,7 @@ - [x] 2.2 Drive real-name prompts, status labels, and order gates from `effective_realname_policy`, `realname_required`, and `real_name_status` - [x] 2.3 Remove local card/device real-name policy inference while preserving the existing real-name link flow - [x] 2.4 Replace current-package-only expiry display with `estimated_final_expires_at` and apply backend expiry status/highlight fields -- [x] 2.5 Redirect the homepage real-name entry to package ordering after confirmation when the effective policy is `after_order` +- [x] 2.5 Gate homepage and operator-switch real-name entries for `after_order` using device card real-name states and pending/active package history ## 3. Package purchase and renewal diff --git a/pages/index/index.vue b/pages/index/index.vue index 36bba73..6dec3ed 100644 --- a/pages/index/index.vue +++ b/pages/index/index.vue @@ -136,6 +136,7 @@ import { assetApi, deviceApi, + hasActiveOrPendingPackage, notificationApi } from '@/api/index.js'; import { @@ -197,6 +198,7 @@ let boundPhone = ref(''); let realNameStatus = ref(''); let effectiveRealnamePolicy = ref('none'); + let realnameEntryChecking = ref(false); let wifi_info = reactive({ ssid: '', pwd: '' @@ -715,6 +717,51 @@ }); }; + const shouldRedirectRealnameToPackageOrder = async () => { + if (effectiveRealnamePolicy.value !== 'after_order') return false; + + const identifier = userStore.state.identifier; + let cards = [{ real_name_status: isRealName.value ? 1 : 0 }]; + if (userInfo.isDevice) { + const data = await deviceApi.getCards(identifier); + cards = Array.isArray(data?.cards) ? data.cards : []; + } + + if (cards.some(card => Number(card?.real_name_status) === 1)) return false; + return !(await hasActiveOrPendingPackage(identifier)); + }; + + const openRealnameEntry = async () => { + if (realnameEntryChecking.value) return; + realnameEntryChecking.value = true; + uni.showLoading({ title: '校验中...', mask: true }); + + try { + const shouldRedirect = await shouldRedirectRealnameToPackageOrder(); + uni.hideLoading(); + if (!shouldRedirect) { + uni.navigateTo({ url: '/pages/auth/auth' }); + return; + } + + uni.showModal({ + title: '提示', + content: '当前设备所有卡均未实名,且暂无生效中或待生效套餐。请先订购套餐,再进行实名认证。', + confirmText: '去订购', + cancelText: '取消', + success: ({ confirm }) => { + if (confirm) uni.navigateTo({ url: '/pages/package-order/package-order' }); + } + }); + } catch (error) { + uni.hideLoading(); + console.error('校验实名入口条件失败', error); + uni.showToast({ title: '实名条件校验失败,请稍后重试', icon: 'none' }); + } finally { + realnameEntryChecking.value = false; + } + }; + const enterDetail = (name) => { switch (name) { case 'notifications': @@ -774,23 +821,7 @@ }); break; case 'authentication': - if (effectiveRealnamePolicy.value === 'after_order') { - uni.showModal({ - title: '提示', - content: '当前认证策略为先充值后实名,请先订购套餐后再进行实名认证。是否前往套餐订购页面?', - confirmText: '去订购', - cancelText: '取消', - success: ({ confirm }) => { - if (confirm) { - uni.navigateTo({ url: '/pages/package-order/package-order' }); - } - } - }); - break; - } - uni.navigateTo({ - url: '/pages/auth/auth' - }); + openRealnameEntry(); break; case 'recover': recoverShow.value = true; diff --git a/pages/switch/switch.vue b/pages/switch/switch.vue index 05050ea..c661113 100644 --- a/pages/switch/switch.vue +++ b/pages/switch/switch.vue @@ -57,7 +57,7 @@