Selaa lähdekoodia

访客登记与后端的对接

zmj 14 tuntia sitten
vanhempi
sitoutus
c3ffd04bf7

+ 1 - 0
.env.production

@@ -6,6 +6,7 @@ VITE_APP_ENV = 'production'
 
 # 若依管理系统/生产环境
 VITE_APP_BASE_API = '/prod-api'
+VITE_API_BASE_URL = '/prod-api'
 
 # 是否在打包时开启压缩,支持 gzip 和 brotli
 VITE_BUILD_COMPRESS = gzip

+ 20 - 14
src/api/screen.js

@@ -233,8 +233,8 @@ export async function readIdCard() {
   try {
     return await http.post('/robot-ops/id-card/read')
   } catch (error) {
-    console.warn('使用 Mock 数据:readIdCard')
-    return { ...mockIdCardResult }
+    console.error('读取身份证失败:', error)
+    throw error
   }
 }
 
@@ -242,20 +242,26 @@ export async function readIdCard() {
  * 预约查询
  */
 export async function queryAppointment(params) {
+  // #region debug log
+  fetch('http://127.0.0.1:7870/ingest/a84a825e-d3f2-4bc0-99e9-e2345f77eced',{method:'POST',headers:{'Content-Type':'application/json','X-Debug-Session-Id':'ce506e'},body:JSON.stringify({sessionId:'ce506e',location:'screen.js:queryAppointment',message:'API queryAppointment 被调用',data:{params},timestamp:Date.now()})}).catch(()=>{});
+  // #endregion
   try {
-    return await http.get('/robot-ops/appointments/query', {
-      data: params
-    })
+    const queryString = new URLSearchParams(params).toString()
+    const url = queryString ? `/robot-ops/appointments/query?${queryString}` : '/robot-ops/appointments/query'
+    // #region debug log
+    fetch('http://127.0.0.1:7870/ingest/a84a825e-d3f2-4bc0-99e9-e2345f77eced',{method:'POST',headers:{'Content-Type':'application/json','X-Debug-Session-Id':'ce506e'},body:JSON.stringify({sessionId:'ce506e',location:'screen.js:queryAppointment',message:'请求 URL',data:{url},timestamp:Date.now()})}).catch(()=>{});
+    // #endregion
+    const result = await http.get(url)
+    // #region debug log
+    fetch('http://127.0.0.1:7870/ingest/a84a825e-d3f2-4bc0-99e9-e2345f77eced',{method:'POST',headers:{'Content-Type':'application/json','X-Debug-Session-Id':'ce506e'},body:JSON.stringify({sessionId:'ce506e',location:'screen.js:queryAppointment',message:'API 返回结果',data:{result},timestamp:Date.now()})}).catch(()=>{});
+    // #endregion
+    return result
   } catch (error) {
-    console.warn('使用 Mock 数据:queryAppointment')
-    const { mobile, idCardNo } = params
-    const appointment = mockAppointments.find(
-      a => a.mobile === mobile || a.idCardNo === idCardNo
-    )
-    if (appointment) {
-      return appointment
-    }
-    throw new Error('未查询到预约记录')
+    // #region debug log
+    fetch('http://127.0.0.1:7870/ingest/a84a825e-d3f2-4bc0-99e9-e2345f77eced',{method:'POST',headers:{'Content-Type':'application/json','X-Debug-Session-Id':'ce506e'},body:JSON.stringify({sessionId:'ce506e',location:'screen.js:queryAppointment',message:'API 请求失败',data:{error:error?.message||error},timestamp:Date.now()})}).catch(()=>{});
+    // #endregion
+    console.error('预约查询失败:', error)
+    throw error
   }
 }
 

+ 53 - 60
src/stores/visitor.js

@@ -2,11 +2,6 @@ import { defineStore } from 'pinia'
 import { ref } from 'vue'
 import * as api from '@/api/screen'
 
-// ===== 开发调试开关 =====
-// 正式联调前请关闭此开关
-const ENABLE_APPOINTMENT_CONFIRM_MOCK = import.meta.env.DEV
-// =========================
-
 export const useVisitorStore = defineStore('visitor', () => {
   // 当前访客信息
   const currentVisitor = ref({
@@ -27,12 +22,13 @@ export const useVisitorStore = defineStore('visitor', () => {
     visitorName: '',
     mobile: '',
     idCardNo: '',
-    visitType: 'walk_in',
-    registerType: 'manual',
+    visitType: 'WALK_IN',
+    registerType: 'SCREEN',
     visitorSource: '',
     visitReason: '',
     visitedPerson: '',
-    appointmentNo: ''
+    appointmentNo: '',
+    visitorPhoto: ''
   })
 
   // 登记表单验证状态
@@ -54,7 +50,7 @@ export const useVisitorStore = defineStore('visitor', () => {
     currentVisitor.value = {
       ...currentVisitor.value,
       name: info.name || '',
-      idCardNo: info.idCardNo || '',
+      idCardNo: info.idCardNo || info.idNum || '',
       gender: info.gender || '',
       nation: info.nation || '',
       address: info.address || '',
@@ -62,7 +58,8 @@ export const useVisitorStore = defineStore('visitor', () => {
     }
     // 自动填充登记信息
     registrationInfo.value.visitorName = info.name || ''
-    registrationInfo.value.idCardNo = info.idCardNo || ''
+    registrationInfo.value.idCardNo = info.idCardNo || info.idNum || ''
+    registrationInfo.value.visitorPhoto = info.photoUrl || ''
   }
 
   function setMobile(mobile) {
@@ -91,60 +88,50 @@ export const useVisitorStore = defineStore('visitor', () => {
     return Object.keys(errors).length === 0
   }
 
-  async function queryAppointment(mobile) {
+  async function queryAppointment(params) {
+    // #region debug log
+    fetch('http://127.0.0.1:7870/ingest/a84a825e-d3f2-4bc0-99e9-e2345f77eced',{method:'POST',headers:{'Content-Type':'application/json','X-Debug-Session-Id':'ce506e'},body:JSON.stringify({sessionId:'ce506e',location:'visitor.js:queryAppointment',message:'store queryAppointment 被调用',data:{params},timestamp:Date.now()})}).catch(()=>{});
+    // #endregion
     try {
-      const res = await api.queryAppointment({ mobile })
-      appointmentInfo.value = res
-      return res
+      const res = await api.queryAppointment(params)
+      // #region debug log
+      fetch('http://127.0.0.1:7870/ingest/a84a825e-d3f2-4bc0-99e9-e2345f77eced',{method:'POST',headers:{'Content-Type':'application/json','X-Debug-Session-Id':'ce506e'},body:JSON.stringify({sessionId:'ce506e',location:'visitor.js:queryAppointment',message:'api.queryAppointment 返回',data:{res},timestamp:Date.now()})}).catch(()=>{});
+      // #endregion
+      // 后端返回格式: { code, msg, data }
+      appointmentInfo.value = res.data || res
+      // #region debug log
+      fetch('http://127.0.0.1:7870/ingest/a84a825e-d3f2-4bc0-99e9-e2345f77eced',{method:'POST',headers:{'Content-Type':'application/json','X-Debug-Session-Id':'ce506e'},body:JSON.stringify({sessionId:'ce506e',location:'visitor.js:queryAppointment',message:'设置 appointmentInfo',data:{appointmentInfo:appointmentInfo.value},timestamp:Date.now()})}).catch(()=>{});
+      // #endregion
+      return appointmentInfo.value
     } catch (e) {
+      // #region debug log
+      fetch('http://127.0.0.1:7870/ingest/a84a825e-d3f2-4bc0-99e9-e2345f77eced',{method:'POST',headers:{'Content-Type':'application/json','X-Debug-Session-Id':'ce506e'},body:JSON.stringify({sessionId:'ce506e',location:'visitor.js:queryAppointment',message:'api.queryAppointment 异常',data:{error:e?.message||e},timestamp:Date.now()})}).catch(()=>{});
+      // #endregion
       appointmentInfo.value = null
       throw e
     }
   }
 
-  /**
-   * Mock 预约数据,用于开发调试
-   * 正式联调前请关闭 ENABLE_APPOINTMENT_CONFIRM_MOCK
-   */
-  function setMockAppointment(mobile = '13800000000') {
-    const now = new Date()
-    const dateStr = now.toISOString().slice(0, 10)
-    const timeStr = `${dateStr} 14:30`
-    const mockData = {
-      appointmentNo: `APT${dateStr.replace(/-/g, '')}0001`,
-      visitorName: '张三',
-      mobile: mobile,
-      idCardNo: '320***********1234',
-      visitedPerson: '李经理',
-      visitedDepartment: '综合管理部',
-      appointmentTime: timeStr,
-      visitPurpose: '业务拜访',
-      companyName: '测试单位',
-      remark: '前端调试预约数据',
-      _isMock: true
-    }
-    appointmentInfo.value = mockData
-    return mockData
-  }
-
   async function submitRegistration() {
     if (!validateForm()) {
       throw new Error('表单验证失败')
     }
-    // 开发环境:Mock 提交成功
-    if (import.meta.env.DEV) {
-      const now = new Date()
-      const pad = (n) => String(n).padStart(2, '0')
-      const dateTime = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`
-      registrationInfo.value = {
-        ...registrationInfo.value,
-        registerTime: dateTime
-      }
-      return registrationInfo.value
-    }
+    // 开发环境:Mock 提交成功(临时禁用以测试后端接口)
+    // TODO: 正式部署时删除此 Mock 逻辑
+    // if (import.meta.env.DEV) {
+    //   const now = new Date()
+    //   const pad = (n) => String(n).padStart(2, '0')
+    //   const dateTime = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`
+    //   registrationInfo.value = {
+    //     ...registrationInfo.value,
+    //     registerTime: dateTime
+    //   }
+    //   return registrationInfo.value
+    // }
     try {
       const res = await api.submitVisitorRegistration(registrationInfo.value)
-      return res
+      // 后端返回格式: { code, msg, data }
+      return res.data || res
     } catch (e) {
       throw e
     }
@@ -153,8 +140,16 @@ export const useVisitorStore = defineStore('visitor', () => {
   async function readIdCard() {
     try {
       const res = await api.readIdCard()
-      setIdCardInfo(res)
-      return res
+      // 后端返回格式: { code, msg, data }
+      const info = res.data || res
+
+      // 验证身份证数据是否有效
+      if (!info.name || !info.idCardNo) {
+        throw new Error('未检测到身份证,请确认身份证已放置正确')
+      }
+
+      setIdCardInfo(info)
+      return info
     } catch (e) {
       throw e
     }
@@ -175,12 +170,13 @@ export const useVisitorStore = defineStore('visitor', () => {
       visitorName: '',
       mobile: '',
       idCardNo: '',
-      visitType: 'walk_in',
-      registerType: 'manual',
+      visitType: 'WALK_IN',
+      registerType: 'SCREEN',
       visitorSource: '',
       visitReason: '',
       visitedPerson: '',
-      appointmentNo: ''
+      appointmentNo: '',
+      visitorPhoto: ''
     }
     formErrors.value = {}
   }
@@ -198,9 +194,6 @@ export const useVisitorStore = defineStore('visitor', () => {
     queryAppointment,
     submitRegistration,
     readIdCard,
-    clearVisitorData,
-    // 开发调试用
-    setMockAppointment,
-    ENABLE_APPOINTMENT_CONFIRM_MOCK
+    clearVisitorData
   }
 })

+ 6 - 0
src/utils/request.js

@@ -41,6 +41,12 @@ class Request {
       }
 
       const result = await response.json()
+
+      // 检查业务状态码(非 200 视为错误)
+      if (result.code !== 200 && result.code !== undefined) {
+        throw new Error(result.msg || '请求失败')
+      }
+
       return result
     } catch (error) {
       clearTimeout(timeoutId)

+ 25 - 58
src/views/visitor/Appointment.vue

@@ -86,17 +86,6 @@
           </svg>
           <span>返回登记</span>
         </button>
-
-        <!-- 开发调试入口:仅 import.meta.env.DEV 显示 -->
-        <Teleport to="body">
-          <button
-            v-if="visitorStore.ENABLE_APPOINTMENT_CONFIRM_MOCK"
-            class="btn-debug-entry"
-            @click="handleDebugEnter"
-          >
-            调试进入预约确认页
-          </button>
-        </Teleport>
       </div>
     </div>
 
@@ -181,18 +170,6 @@ const goToWalkIn = () => {
   router.push('/visitor/walk-in')
 }
 
-// ===== 开发调试用 =====
-const handleDebugEnter = () => {
-  visitorStore.setMockAppointment()
-  screenStore.showAlert({
-    type: 'info',
-    message: '【调试】已进入预约确认页',
-    duration: 2000
-  })
-  router.push('/visitor/appointment-confirm')
-}
-// =========================
-
 const handleIdCard = async () => {
   if (loading.value) return
   loading.value = true
@@ -207,22 +184,11 @@ const handleIdCard = async () => {
         await visitorStore.queryAppointment({ idCardNo: result.idCardNo })
         router.push('/visitor/appointment-confirm')
       } catch {
-        // 开发环境:身份证读取成功但预约查询失败,兜底进入确认页
-        if (visitorStore.ENABLE_APPOINTMENT_CONFIRM_MOCK) {
-          visitorStore.setMockAppointment()
-          closePhoneModal()
-          screenStore.showAlert({
-            type: 'info',
-            message: '【调试】身份证读取成功但预约查询无数据,已进入预约确认页'
-          })
-          router.push('/visitor/appointment-confirm')
-        } else {
-          screenStore.showAlert({
-            type: 'info',
-            message: '未查询到预约信息,您可以转现场登记',
-            duration: 4000
-          })
-        }
+        screenStore.showAlert({
+          type: 'info',
+          message: '未查询到预约信息,您可以转现场登记',
+          duration: 4000
+        })
       }
     }
   } catch {
@@ -239,6 +205,10 @@ const handleIdCard = async () => {
 const handlePhoneConfirm = async () => {
   if (loading.value) return
 
+  // #region debug log
+  fetch('http://127.0.0.1:7870/ingest/a84a825e-d3f2-4bc0-99e9-e2345f77eced',{method:'POST',headers:{'Content-Type':'application/json','X-Debug-Session-Id':'ce506e'},body:JSON.stringify({sessionId:'ce506e',location:'Appointment.vue:handlePhoneConfirm',message:'手机号查询开始',data:{phoneNumber:phoneNumber.value},timestamp:Date.now()})}).catch(()=>{});
+  // #endregion
+
   if (!phoneNumber.value || phoneNumber.value.length !== 11) {
     phoneError.value = true
     screenStore.showAlert({
@@ -252,27 +222,24 @@ const handlePhoneConfirm = async () => {
   phoneError.value = false
   loading.value = true
   try {
-    await visitorStore.queryAppointment({ mobile: phoneNumber.value })
+    // #region debug log
+    fetch('http://127.0.0.1:7870/ingest/a84a825e-d3f2-4bc0-99e9-e2345f77eced',{method:'POST',headers:{'Content-Type':'application/json','X-Debug-Session-Id':'ce506e'},body:JSON.stringify({sessionId:'ce506e',location:'Appointment.vue:handlePhoneConfirm',message:'调用 visitorStore.queryAppointment',data:{params:{mobile:phoneNumber.value}},timestamp:Date.now()})}).catch(()=>{});
+    // #endregion
+    const result = await visitorStore.queryAppointment({ mobile: phoneNumber.value })
+    // #region debug log
+    fetch('http://127.0.0.1:7870/ingest/a84a825e-d3f2-4bc0-99e9-e2345f77eced',{method:'POST',headers:{'Content-Type':'application/json','X-Debug-Session-Id':'ce506e'},body:JSON.stringify({sessionId:'ce506e',location:'Appointment.vue:handlePhoneConfirm',message:'queryAppointment 成功',data:{result},timestamp:Date.now()})}).catch(()=>{});
+    // #endregion
     closePhoneModal()
     router.push('/visitor/appointment-confirm')
-  } catch {
-    // 开发环境:预约查询失败,兜底进入确认页
-    if (visitorStore.ENABLE_APPOINTMENT_CONFIRM_MOCK) {
-      visitorStore.setMockAppointment(phoneNumber.value)
-      closePhoneModal()
-      screenStore.showAlert({
-        type: 'info',
-        message: '【调试】预约查询无数据,已进入预约确认页',
-        duration: 3000
-      })
-      router.push('/visitor/appointment-confirm')
-    } else {
-      screenStore.showAlert({
-        type: 'info',
-        message: '未查询到预约信息,您可以转现场登记',
-        duration: 4000
-      })
-    }
+  } catch (err) {
+    // #region debug log
+    fetch('http://127.0.0.1:7870/ingest/a84a825e-d3f2-4bc0-99e9-e2345f77eced',{method:'POST',headers:{'Content-Type':'application/json','X-Debug-Session-Id':'ce506e'},body:JSON.stringify({sessionId:'ce506e',location:'Appointment.vue:handlePhoneConfirm',message:'queryAppointment 失败',data:{error:err?.message||err},timestamp:Date.now()})}).catch(()=>{});
+    // #endregion
+    screenStore.showAlert({
+      type: 'info',
+      message: '未查询到预约信息,您可以转现场登记',
+      duration: 4000
+    })
   } finally {
     loading.value = false
   }

+ 13 - 0
src/views/visitor/AppointmentConfirm.vue

@@ -312,12 +312,25 @@ const handleConfirm = async () => {
   loading.value = true
 
   try {
+    // 更新预约信息
     visitorStore.appointmentInfo = {
       ...visitorStore.appointmentInfo,
       ...form,
       visitReason: form.visitPurpose
     }
 
+    // 同步更新登记信息(包含所有必要字段)
+    visitorStore.registrationInfo = {
+      ...visitorStore.registrationInfo,
+      visitorName: form.visitorName,
+      mobile: form.mobile,
+      idCardNo: form.idCardNo,
+      visitType: 'APPOINTMENT', // 预约到访
+      appointmentNo: form.appointmentNo,
+      visitedPerson: form.visitedPerson,
+      visitReason: form.visitPurpose
+    }
+
     await visitorStore.submitRegistration()
     router.push('/visitor/success')
   } catch (error) {

+ 5 - 4
src/views/visitor/WalkIn.vue

@@ -453,10 +453,11 @@ const handleSubmit = async () => {
     visitorStore.setFormField('visitorName', formData.visitorName)
     visitorStore.setFormField('mobile', formData.mobile)
     visitorStore.setFormField('idCardNo', formData.idCardNo)
-    visitorStore.setFormField('visitorCompany', formData.visitorCompany)
+    visitorStore.setFormField('visitorSource', formData.visitorCompany)
     visitorStore.setFormField('visitedPerson', formData.visitedPerson)
     visitorStore.setFormField('visitReason', formData.visitReason)
-    visitorStore.setFormField('visitType', 'walk_in')
+    visitorStore.setFormField('visitType', 'WALK_IN')
+    visitorStore.setFormField('registerType', 'SCREEN')
 
     await visitorStore.submitRegistration()
     router.push('/visitor/success')
@@ -471,11 +472,11 @@ const handleSubmit = async () => {
         visitorName: formData.visitorName,
         mobile: formData.mobile,
         idCardNo: formData.idCardNo,
-        visitorCompany: formData.visitorCompany,
         visitorSource: formData.visitorCompany,
         visitedPerson: formData.visitedPerson,
         visitReason: formData.visitReason,
-        visitType: 'walk_in',
+        visitType: 'WALK_IN',
+        registerType: 'SCREEN',
         registerTime: dateTime,
         _mockSubmit: true
       }