TypeScript类型建模与前后端错误码协议:手机号绑定场景的工程实践

引言

在前后端分离的开发中,手机号绑定、验证与授权是常见的业务场景。然而,不同平台对手机号的处理规则可能不一致,且政策或系统升级可能带来临时变化。本文从工程实践角度出发,讨论如何通过TypeScript类型建模和错误码协议,设计健壮的前端表单状态机与用户反馈机制。文中示例均为通用演示,不绑定任何具体税务系统或官方接口。

核心概念区分

在手机号相关流程中,需明确三个层次:

  1. 持有验证:确认用户对某个手机号的控制权(如通过短信验证码)。
  2. 身份验证:确认用户本人的身份(如通过证件信息或生物识别)。
  3. 授权:由服务端依据当前主体、资源与操作判断是否允许执行该业务;它与用户同意接收通知是不同概念。

这三者可能独立发生,也可能组合出现。前端应根据错误码区分当前失败发生在哪个阶段,从而给出针对性提示。

TypeScript类型建模

状态机设计

前端表单状态可建模为:

typescript 复制代码
type FormPhase = 'idle' | 'input' | 'verifying' | 'authorizing' | 'success' | 'failed';

type VerificationMethod = 'sms' | 'call' | 'appPush';

interface PhoneFormState {
  phase: FormPhase;
  phoneNumber: string;
  verificationMethod?: VerificationMethod;
  retryCount: number;
  lastError?: ErrorCode;
}

错误码协议

定义错误码枚举,区分可重试与不可重试错误:

typescript 复制代码
enum ErrorCode {
  // 可重试:网络或临时系统问题
  NETWORK_TIMEOUT = 'NETWORK_TIMEOUT',
  SERVICE_UNAVAILABLE = 'SERVICE_UNAVAILABLE',
  // 可重试:验证码错误或过期
  CODE_INVALID = 'CODE_INVALID',
  CODE_EXPIRED = 'CODE_EXPIRED',
  // 不可重试:业务规则或权限问题
  PHONE_ALREADY_BOUND = 'PHONE_ALREADY_BOUND',
  PHONE_NOT_ALLOWED = 'PHONE_NOT_ALLOWED',
  IDENTITY_MISMATCH = 'IDENTITY_MISMATCH',
  UNAUTHORIZED = 'UNAUTHORIZED',
  // 未知错误
  UNKNOWN = 'UNKNOWN'
}

interface ApiError {
  code: ErrorCode;
  message: string;
  retryable: boolean;
}

注意:以上错误码名称和含义是自拟的通用演示,不代表任何真实系统的规则。实际开发中,需根据后端接口文档定义。

前端表单状态与用户反馈

状态转换逻辑

typescript 复制代码
type FormEvent =
  | { type: 'SUBMIT' }
  | { type: 'SUCCESS' }
  | { type: 'ERROR'; error: ApiError };

function transition(state: PhoneFormState, event: FormEvent): PhoneFormState {
  switch (state.phase) {
    case 'idle':
      if (event.type === 'SUBMIT') return { ...state, phase: 'verifying' };
      break;
    case 'verifying':
      if (event.type === 'SUCCESS') return { ...state, phase: 'authorizing' };
      if (event.type === 'ERROR' && event.error.retryable) {
        return { ...state, retryCount: state.retryCount + 1, lastError: event.error.code };
      }
      if (event.type === 'ERROR' && !event.error.retryable) {
        return { ...state, phase: 'failed', lastError: event.error.code };
      }
      break;
    case 'authorizing':
      if (event.type === 'SUCCESS') return { ...state, phase: 'success' };
      if (event.type === 'ERROR') return { ...state, phase: 'failed', lastError: event.error.code };
      break;
  }
  return state;
}

用户反馈策略

  • 可重试错误:显示"网络异常,请稍后重试"或"验证码错误,请重新输入",并提供重试按钮。
  • 不可重试错误:显示具体原因(如"该手机号已被其他账号绑定"),并引导用户联系客服或更换手机号。
  • 幂等性:前端在提交请求时携带唯一请求ID,后端据此去重,避免重复提交导致多次扣费或发送多条验证码。

注意事项

  • 本文示例代码是通用演示,不涉及任何真实税务系统接口。
  • 实际业务中,手机号绑定规则可能因平台而异,且可能随政策调整。请以当地官方渠道的最新说明为准。
  • 不要使用他人实名手机号进行验证,也不要尝试绕过验证码机制。
  • 对于办税相关功能,务必参考当地电子税务局的官方指引,以实际界面和答复为准。

结语

通过TypeScript类型建模和清晰的错误码协议,前端可以更稳健地处理手机号绑定流程中的各种异常,提升用户体验。同时,保持对业务规则变化的敏感,避免依赖未经核实的"新规"。

示例仅建模前端界面,最终授权必须由服务端判断。retryable 表示可由用户纠正输入后再次提交或有限重试,不代表应自动循环发送验证码;验证码错误不能无上限重试。

相关推荐
卸任10 小时前
Vibe coding工具使用心得
前端·ai编程
brien911 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance11 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M11 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写12 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李12 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易12 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马12 小时前
前沿视界
前端
kyriewen12 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易12 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员