問卷後段 · 用戶資訊分支圖

LINE-UX-SPEC.md §4.5.0 自動抽出圖源產生 —— 修改規格後重新產生即可,兩邊不會漂移。

圖形約定:圓角框=畫面,方框=使用者狀態,平行四邊形= API 呼叫。
核心規則:要不要證明,取決於 Email 的來源,不是命中與否。
line_user_id 綁定與 LINE 給的 Email 都是 LINE 驗證過的身分,命中即登入;使用者打字的 Email 只是宣稱,命中才要走所有權證明。

4.5.0a 識別順序

識別依序試三把鑰匙:line_user_id 綁定 → LINE 給的 Email → 使用者打字的 Email。前兩把在進場由後端比對,第三把才發生在畫面上。

flowchart TD
  START([LIFF 進場・驗證 id_token]) --> BOUND{"line_user_id<br/>已綁定帳號?"}

  BOUND -->|是| REC["<b>【已認出】</b>直接已登入<br/>稱呼/Email 皆唯讀"]

  BOUND -->|否| HASMAIL{"LINE 有給 Email?<br/>(同意畫面的 toggle)"}

  HASMAIL -->|有 → 後端比對| MATCH{"命中舊帳號?"}
  MATCH -->|命中| REC
  MATCH -->|查無| NEWLOCK["<b>【新用戶】</b>Email <b>鎖定</b><br/>稱呼可改・要勾條款"]

  HASMAIL -->|沒有| CE(["<b>CONTACT_EMAIL</b><br/>Email 可編輯・使用者打字輸入"])
  CE -->|按「下一步」| CHK[/"check_account"/]
  CHK -->|error 11| CEERR(["停在本頁顯示<br/>『無效的 Email』"])
  CEERR --> CE
  CHK -->|account_exist = false| NEWEDIT["<b>【新用戶】</b>Email <b>可編輯</b><br/>稱呼可改・要勾條款"]
  CHK -->|account_exist = true| PROVING["<b>【待證明】</b><br/>⚠️ 打字的 Email 不是證明"]

  REC --> P1[["→ 4.5.0b 電話頁判定"]]
  NEWLOCK --> P1
  NEWEDIT --> P1
  PROVING --> P1

  style REC fill:#e8f5e9
  style NEWLOCK fill:#f8fafc
  style NEWEDIT fill:#f8fafc
  style PROVING fill:#fff3e0

4.5.0b 電話頁樣貌判定

樣貌不是流程走出來的,是每次進入 CONTACT_PHONE 時重新判定。所有權證明通過後會再回來判定一次。

flowchart TD
  IN([進入 CONTACT_PHONE]) --> V{"判定"}

  V -->|"已認出<br/>且 is_phone_confirmed = 1"| A["<b>樣貌 A</b> 已驗證唯讀<br/>顯示號碼・不可改・<b>不發 OTP</b><br/>+『提供電話給專家』勾選"]
  V -->|"待證明"| C[["<b>樣貌 C</b> → 4.5.0c"]]
  V -->|"其餘:新用戶/已認出但沒驗過手機"| B["<b>樣貌 B</b> 一般驗證<br/>國碼+號碼輸入"]

  A --> DONE([→ 送出需求])

  B --> PRE[/"verify/phone/preauth"/]
  PRE -->|"message = in_used"| WARN["顯示既有警語:此號碼已被其他帳戶<br/>驗證使用…(<b>不阻擋</b>)"]
  WARN --> OTPB
  PRE -->|正常| OTPB["<b>OTP_PHONE</b>・4 碼"]
  OTPB --> AUTH[/"verify/phone/auth"/]
  AUTH -->|錯誤| OTPB
  AUTH -->|通過| DONE

  style A fill:#e8f5e9
  style DONE fill:#e3f2fd

4.5.0c 所有權證明(樣貌 C)

只有「待證明」會走到這裡。不擋、不要密碼,用既有驗證碼機制完成授權登入;走哪一條由 check_account 決定,不由使用者選。它是一個要按按鈕的頁面(版型比照樣貌 B 的電話頁),按下「傳送驗證碼」才真的發送。

flowchart TD
  IN([待證明・進入 CONTACT_PHONE]) --> BR{"check_account 回傳<br/>有 phone 且 is_phone_confirmed = 1?"}

  BR -->|是| C1(["<b>樣貌 C・簡訊版</b> 版型比照樣貌 B<br/>標題『這是您的帳號嗎?』<br/>唯讀欄位:帳號手機 09xx-xxx-836<br/>說明:將發送驗證碼到此號碼<br/>主鈕【傳送驗證碼】"])
  BR -->|否| C2(["<b>樣貌 C・Email 版</b> 版型比照樣貌 B<br/>標題『這是您的帳號嗎?』<br/>唯讀欄位:帳號 Email your@mail.com<br/>說明:將寄送驗證碼到此信箱<br/>主鈕【傳送驗證碼】"])

  C1 -->|按主鈕才送出| S1[/"send_otp_to_phone_by_email<br/>type = phone|voice"/]
  S1 --> O1(["<b>OTP_PROVE</b>・4 碼<br/>『驗證碼已發送至 09xx-xxx-836』"])
  O1 --> A1[/"verify/phone/auth(is_login = 1)"/]
  A1 -->|錯誤| O1
  A1 -->|通過| LOGIN

  C2 -->|按主鈕才送出| S2[/"email_verifications/preauth_login"/]
  S2 --> O2(["<b>OTP_PROVE</b>・6 碼<br/>『驗證碼已寄至 your@mail.com』"])
  O2 --> A2[/"email_verifications/auth"/]
  A2 -->|錯誤| O2
  A2 -->|通過| LOGIN

  LOGIN["<b>授權登入完成 → 【已認出】</b><br/>CONTACT_EMAIL 轉唯讀・條款勾選消失<br/>改 Email 出口關閉"]
  LOGIN --> BACK[["回 CONTACT_PHONE 重新判定(4.5.0b)"]]
  BACK -->|"帳號手機已驗證"| RA(["⇒ 樣貌 A・直接送出"])
  BACK -->|"帳號沒驗過手機"| RB(["⇒ 樣貌 B・仍要補驗手機"])

  O1 -.收不到.-> RESEND["重新發送(60 秒倒數)<br/>/改用語音(type = voice)"]
  RESEND --> O1
  C1 -.不是我的帳號.-> GIVEUP["上一步改用其他 Email<br/>⇒ 回【新用戶】,開新帳號"]
  C2 -.不是我的帳號.-> GIVEUP
  O1 -.證不出來.-> GIVEUP
  O2 -.證不出來.-> GIVEUP

  style LOGIN fill:#e8f5e9
  style GIVEUP fill:#fff3e0

五條不變式

  1. 樣貌判定只能有一處實作。它會被進入、返回、證明通過三種時機呼叫,複製成多份必定漂移。
  2. 證明通過後回 CONTACT_PHONE 重新判定,不要直接送出。Email 驗證那條路進來的帳號可能沒驗過手機 —— 登入不豁免手機硬門檻。
  3. 「已認出」只有一套狀態。進場就認出的、與證明完回頭的,是同一個狀態。
  4. 只有 Email 可編輯的路徑才呼叫 check_account。前兩把鑰匙的比對在進場由後端完成。
  5. CONTACT_EMAIL 不揭露帳號是否存在。否則這頁就成了帳號探測工具。