Files
im_server/docs/IM-个人无企业码注册前后端对接说明.md
T

11 KiB
Raw Blame History

IM 个人/企业注册登录前后端对接说明

1. 目标

当前客户端没有独立的企业码进入页面,登录页和注册页由用户选择“个人用户”或“企业用户”。前端提交登录和注册时必须显式带类型,后端不能再通过企业码是否为空来推断个人/企业。

核心规则:

  1. 前端新增字段 registration_type,类型为 int,取值只能是 0 或 1。
  2. registration_type=1 时,后端按个人注册处理,内部租户使用 default,数据写入 default 租户业务库。
  3. registration_type=0 时,前端必须提交企业码,后端必须校验企业存在、启用且允许注册。
  4. 登录新增字段 login_type,类型为 int,取值同样是 0 企业、1 个人。
  5. 企业登录或企业注册都不允许使用 default。
  6. default 是系统保留的个人租户码,不允许作为企业码输入。

一句话规则:注册类型由 registration_type 决定,登录类型由 login_type 决定;个人落 default,企业必须校验企业码。

2. 前端注册字段

注册页提交发码和最终注册时都带同一组注册上下文字段:

字段 类型 必填 说明
registration_type int 是 0 表示企业注册,1 表示个人注册
tenant_code string 企业注册必填 企业码;个人注册不填或传空字符串

前端规则:

registration_type tenant_code 后端处理
1 空 允许提交,后端内部使用 default
1 非空 后端拒绝,避免误入库
0 空 后端拒绝,提示请输入企业码
0 default、Default、DEFAULT 后端拒绝
0 其他值 后端在注册链路中校验

用户在企业注册模式输入 default 时,建议提示:

default 是系统保留企业码。个人注册请选择个人用户,企业注册请输入企业提供的企业码。

3. 企业码校验

不新增单独的企业码校验接口,也不要求前端在注册前调用 /v1/im/tenants/resolve。

后端在注册发码和最终注册链路中校验:

  1. registration_type=1

    • 不要求企业码。
    • 即使请求 Header 或内部路由使用 default,也属于服务端内部租户,不是用户输入企业码。
    • 创建用户时写入 default 租户业务库。
  2. registration_type=0

    • tenant_code 必填。
    • tenant_code 大小写不敏感等于 default 时拒绝。
    • 查询企业是否存在、启用、允许注册。
    • 通过后在对应企业租户业务库创建用户。

企业不存在、停用或不允许注册时,客户端统一提示:

企业不存在或不可用

客户端不要区分不存在、停用、禁用注册等内部原因。

/v1/im/tenants/resolve 只保留给服务发现、品牌配置、历史企业入口等场景使用;当前注册页不依赖它完成主流程。

4. 前端登录字段

登录页提交 WebSocket {login} 时带登录上下文字段:

字段 类型 必填 说明
login_type int basic 登录必填 0 表示企业登录,1 表示个人登录
tenant_code string 企业登录必填 企业码;个人登录不填、传空字符串或内部传 default

登录规则:

login_type tenant_code / hi.tenant 后端处理
1 default 个人登录,允许
1 非 default 拒绝,避免个人登录误进企业租户
0 空 拒绝,提示请输入企业码
0 default、Default、DEFAULT 拒绝,统一提示“企业不存在或不可用”
0 其他值 企业登录,后端按该租户认证

企业用户如果在企业入口输入 default,后端返回策略错误,错误参数为 what=tenant_code、reason=TENANT_CODE_FORBIDDEN;客户端统一展示:

企业不存在或不可用

个人登录 {login} 示例:

{
  "login": {
    "id": "login-1",
    "scheme": "basic",
    "login_type": 1,
    "tenant_code": "",
    "secret": "base64(account:password)"
  }
}

企业登录 {login} 示例:

{
  "login": {
    "id": "login-1",
    "scheme": "basic",
    "login_type": 0,
    "tenant_code": "acme",
    "secret": "base64(account:password)"
  }
}

5. 个人注册流程

个人注册必须显式提交 registration_type=1。

  1. 用户选择个人注册。
  2. 前端提交 registration_type=1,tenant_code 为空。
  3. 注册发码、验证码校验、WebSocket hi、acc 注册链路使用服务端内部租户 default。
  4. 服务端将用户创建到 default 租户对应的业务库。

发码示例:

POST /v1/im/auth/verification-codes
X-MeetingIM-APIKey: <api_key>
X-IM-Schema: 1
X-Tenant-Code: default
X-Request-ID: <uuid>
{
  "scene": "register",
  "registration_type": 1,
  "tenant_code": "",
  "phone": "19122421333"
}

WebSocket 握手示例:

{
  "hi": {
    "id": "hi-1",
    "ver": "0.25",
    "ua": "ios/1.0.0",
    "platf": "ios",
    "lang": "zh-CN",
    "tenant": "default"
  }
}

最终注册 {acc} 示例:

{
  "acc": {
    "id": "acc-1",
    "user": "new",
    "scheme": "basic",
    "registration_type": 1,
    "tenant_code": "",
    "username": "henry",
    "phone": "19122421333",
    "secret": "base64(henry:password)",
    "login": true,
    "cred": [
      {
        "meth": "tel",
        "val": "19122421333",
        "resp": "verification_token",
        "params": {"challenge_id": "challenge-id"}
      }
    ]
  }
}

6. 企业注册流程

企业注册必须显式提交 registration_type=0。

  1. 用户选择企业注册。
  2. 用户填写企业码,例如 acme。
  3. 前端直接提交用户输入的企业码,不做企业码存在性或保留字拦截。
  4. 获取验证码、提交注册时都带 registration_type=0 和 tenant_code=acme。
  5. 后端在注册链路中校验企业是否存在且可用。
  6. 校验通过后,服务端在该企业租户业务库中创建用户;校验不通过时返回“企业不存在或不可用”。

注册发码请求:

POST /v1/im/auth/verification-codes
X-MeetingIM-APIKey: <api_key>
X-IM-Schema: 1
X-Tenant-Code: acme
X-Request-ID: <uuid>
{
  "scene": "register",
  "registration_type": 0,
  "tenant_code": "acme",
  "phone": "19122421333"
}

WebSocket hi:

{
  "hi": {
    "id": "hi-1",
    "ver": "0.25",
    "ua": "android/1.0.0",
    "platf": "android",
    "lang": "zh-CN",
    "tenant": "acme"
  }
}

最终注册 {acc} 示例:

{
  "acc": {
    "id": "acc-1",
    "user": "new",
    "scheme": "basic",
    "registration_type": 0,
    "tenant_code": "acme",
    "username": "henry",
    "phone": "19122421333",
    "secret": "base64(henry:password)",
    "login": true,
    "cred": [
      {
        "meth": "tel",
        "val": "19122421333",
        "resp": "verification_token",
        "params": {"challenge_id": "challenge-id"}
      }
    ]
  }
}

7. 后端校验规则

后端负责注册链路的最终校验,前端不做企业码拦截。

  1. registration_type 缺失或不是 0|1

    • 拒绝注册。
    • 返回 MALFORMED_REQUEST,提示客户端升级或检查参数。
  2. registration_type=1

    • 不从空企业码推断,而是由该字段明确进入个人注册。
    • 内部租户码设置为 default。
    • 拒绝用户提交的非空 tenant_code,避免误入库。
    • 注册时写入 default 租户业务库。
  3. registration_type=0

    • tenant_code 必填。
    • 先 trim 首尾空格。
    • 如果大小写不敏感等于 default,拒绝注册。
    • 否则查询租户是否存在、是否启用、是否允许注册。
    • 校验通过后绑定对应租户。

建议错误:

{
  "code": 30000,
  "reason": "MALFORMED_REQUEST",
  "message": "invalid registration_type or tenant_code"
}

企业不可用时,客户端展示:

企业不存在或不可用

8. 缓存和本地数据隔离

客户端本地缓存、Token、会话、草稿、附件缓存必须继续按 tenant_code + user_id 隔离。

个人注册用户登录后,缓存键中的 tenant_code 使用 default。企业用户登录后,使用对应企业码。切换个人/企业身份时必须清理当前连接并重新 hi,不能在同一个 WebSocket 中切换租户。

9. 验收用例

场景 预期
login_type=1,hi.tenant=default 个人登录成功进入 default 租户认证
login_type=1,hi.tenant=acme 拒绝登录
login_type=0,企业码空 拒绝登录,提示请输入企业码
login_type=0,企业码为 default 拒绝登录,提示企业不存在或不可用
login_type=0,企业码有效 在该企业租户认证
registration_type=1,企业码空,手机号注册 成功进入 default 租户注册链路
registration_type 缺失 拒绝注册,返回参数错误
registration_type=1,企业码非空 拒绝或忽略,建议拒绝,避免误入库
registration_type=0,企业码空 拒绝注册,提示请输入企业码
registration_type=0,企业码为 default 拒绝注册
registration_type=0,企业码不存在 发码或注册失败,提示企业不存在或不可用
registration_type=0,企业码已停用 统一提示企业不存在或不可用
registration_type=0,企业码有效 注册写入该企业租户库
个人用户注册成功后登录 tenant_code=default,可正常登录、收发消息

10. 对客户端的明确要求

  1. 登录页和注册页都必须明确区分个人和企业。
  2. basic 登录必须提交 login_type。
  3. 发码和最终注册都必须提交 registration_type。
  4. 个人登录/注册提交类型 1,不要让用户输入或看到 default。
  5. 企业登录/注册提交类型 0 和用户输入的企业码。
  6. 企业用户手动输入 default 也直接提交,由后端返回错误,客户端按后端错误展示提示。
  7. 不需要为了注册主流程调用 /v1/im/tenants/resolve。

11. 对后端的明确要求

  1. basic 登录链路必须以 login_type 作为个人/企业登录的判定来源。
  2. 注册链路必须以 registration_type 作为个人/企业注册的判定来源。
  3. 不允许仅根据企业码是否为空推断个人注册或个人登录。
  4. 登录、注册、发码、验证码校验、WebSocket hi/login/acc 链路保持同一租户上下文。
  5. 显式企业码 default 必须拒绝,防止用户绕过企业码校验。
  6. 企业登录/注册的企业码必须校验存在、启用、允许登录/注册。
  7. 日志中要能看出来源是 personal_default 还是 enterprise_code,方便排查误入库问题。