Explorar el Código

完成接口竞态问题

yuanmingze hace 2 días
padre
commit
bb63a963cc
Se han modificado 2 ficheros con 25 adiciones y 67 borrados
  1. 11 62
      src/hooks/useInvoice.ts
  2. 14 5
      src/views/invoice-information/index.vue

+ 11 - 62
src/hooks/useInvoice.ts

@@ -1,4 +1,4 @@
-import { ref, reactive, computed } from 'vue'
+import { ref, reactive } from 'vue'
 import { useRouter } from 'vue-router'
 import { showToast, showSuccessToast } from 'vant'
 import { getStatusApi, submitInvoiceApplyApi } from '@/services/modules/invoiceInformation'
@@ -35,15 +35,6 @@ export function useInvoice() {
   const isLoading = ref(false)
   const statusMap = ref<ToStatus>({})
 
-  // get-status 接口是否允许继续流程
-  const statusReady = ref(false)
-
-  /**
-   * 只表示 get-face-auth-result 接口是否已成功返回
-   * 仅用于 btnDisabled 判断
-   */
-  const faceAuthReady = ref(false)
-
   /**
    * 是否需要跳转人脸认证页
    * REQUIRED_AUTHENTICATION => true
@@ -57,57 +48,31 @@ export function useInvoice() {
    */
   const faceAuthResult = ref(false)
 
-  /* ------------------ UI 控制层 ------------------ */
-
-  const btnDisabled = computed(() => {
-    return !(statusReady.value && faceAuthReady.value)
-  })
-
   /* ------------------ 请求参数 ------------------ */
 
   const params = reactive<PushRecordIdRequest>({
     pushRecordId: userStore.pushRecordId,
   })
 
-  /* ------------------ 常量映射 ------------------ */
-
-  const isSubmitStatusMap = ['PENDING', 'FINISHED', 'RED_INK_ENTRY', 'NOT_APPROVED', 'DISCARD']
-
-  const isSubmitEventStatusMap = [
-    'SUBMITTED',
-    'PENDING_PAYMENT',
-    'PAID',
-    'PENDING_FINAL_INVOICING',
-    'INVOICE_FAILED',
-    'FINAL_INVOICE_SUBMITTED',
-    'INVOICE_DOWNLOADABLE',
-  ]
-
   /* ============================================================
    * 状态获取层
    * ============================================================ */
 
-  const getStatus = async () => {
-    if (!params.pushRecordId) return
+  const getStatus = async (): Promise<boolean> => {
+    if (!params.pushRecordId) return false
 
     isLoading.value = true
 
     try {
       const res = await getStatusApi(params)
-      if (res.code !== 0) return
+      if (res.code !== 0 || !res.data) return false
 
       statusMap.value = res.data as ToStatus
-
-      const { invoiceStatus, eventStatus } = res.data
-
-      const isInvoiceSubmitted = !!invoiceStatus && isSubmitStatusMap.includes(invoiceStatus)
-      const isEventSubmitted = !!eventStatus && isSubmitEventStatusMap.includes(eventStatus)
-
-      // 只有未提交状态才允许继续
-      statusReady.value = !(isInvoiceSubmitted && isEventSubmitted)
+      return true
     } catch (err) {
       console.error('获取状态失败', err)
       showToast('获取开票状态失败,请稍后重试')
+      return false
     } finally {
       isLoading.value = false
     }
@@ -117,28 +82,27 @@ export function useInvoice() {
    * 人脸认证状态层
    * ============================================================ */
 
-  const getFaceAuthResult = async () => {
-    if (!params.pushRecordId) return
+  const getFaceAuthResult = async (): Promise<boolean> => {
+    if (!params.pushRecordId) return false
 
     try {
       const res = await getFaceAuthResultApi(params)
 
-      if (res.code !== 0 || !res.data?.success) return
+      if (res.code !== 0 || !res.data?.success) return false
 
       const data = res.data as FaceAuthResultData
       const rzzt = data.rzzt
 
-      // 接口成功返回后,按钮可以进入下一步流程
-      faceAuthReady.value = true
-
       // 需要做人脸:按钮可点,但点击后应跳人脸页
       needFaceAuth.value = rzzt === 'REQUIRED_AUTHENTICATION'
 
       // 无需认证:视为人脸流程已通过,可以直接提交
       faceAuthResult.value = rzzt === 'NO_REQUIRED_AUTHENTICATION'
+      return true
     } catch (err: any) {
       console.error('获取认证结果失败', err)
       showToast(err?.message || '获取认证结果失败,请稍后重试')
+      return false
     }
   }
 
@@ -175,15 +139,6 @@ export function useInvoice() {
     }
   }
 
-  /* ============================================================
-   * UI 场景提交
-   * ============================================================ */
-
-  const submitInvoiceWithGuard = async () => {
-    if (btnDisabled.value) return false
-    return await doSubmitInvoice()
-  }
-
   /* ============================================================
    * 流程场景提交
    * ============================================================ */
@@ -209,17 +164,11 @@ export function useInvoice() {
      */
     faceAuthResult,
 
-    /**
-     * 按钮禁用状态
-     */
-    btnDisabled,
-
     /* 状态获取 */
     getStatus,
     getFaceAuthResult,
 
     /* 提交 */
-    submitInvoiceWithGuard,
     submitInvoiceForce,
   }
 }

+ 14 - 5
src/views/invoice-information/index.vue

@@ -113,7 +113,7 @@
 
       <van-button
         type="primary"
-        :disabled="loading"
+        :disabled="loading || !flowStatusReady"
         round
         class="next-btn btn"
         @click="handleNextDebounced"
@@ -165,6 +165,7 @@ const {
 const userStore = useUserStore()
 const router = useRouter()
 const loading = ref(true)
+const flowStatusReady = ref(false)
 const invoiceInfo = ref<any>({})
 const activeStep = ref(1)
 
@@ -191,11 +192,11 @@ const getConfirmInvoiceInfo = async () => {
 
 /**
  * 下一步逻辑
- * 按钮只受开票信息加载状态限制,其他状态用于决定后续流程
+ * 等待开票状态及人脸认证状态接口返回后,再决定后续流程
  */
 const handleNext = async () => {
-  // 当前页面只等待开票信息加载完成,不受开票状态及人脸状态接口就绪状态限制
-  if (loading.value) return
+  // 点击处理层再次校验,避免接口返回前的竞态点击绕过按钮禁用态
+  if (loading.value || !flowStatusReady.value) return
 
   // 未上传身份证,跳转上传页
   if (!statusMap.value.isIdImgReady) {
@@ -262,7 +263,15 @@ const toDetail = () => {
 
 /** 生命周期 */
 onMounted(async () => {
-  await Promise.all([getStatus(), getConfirmInvoiceInfo(), getFaceAuthResult()])
+  const getFlowStatus = async () => {
+    const [hasStatusData, hasFaceAuthData] = await Promise.all([
+      getStatus(),
+      getFaceAuthResult(),
+    ])
+    flowStatusReady.value = hasStatusData && hasFaceAuthData
+  }
+
+  await Promise.all([getConfirmInvoiceInfo(), getFlowStatus()])
 })
 </script>