hyapp-server/server/admin/docs/靓号管理服务端技术文档.md
2026-06-11 01:02:20 +08:00

26 KiB
Raw Permalink Blame History

靓号管理服务端技术文档

当前结论

靓号管理不能只做成后台页面配置。当前仓库里 user-service 已经是 user_iddisplay_user_id 和临时靓号租约的 ownerserver/admin 只能做后台 HTTP 入口、权限、审计和管理适配App 入口继续由 gateway-service 暴露。

本需求新增的是“靓号池申请 + 后台发放靓号 + 资料返回靓号字段”,要在现有 display_user_id 体系上扩展,不重新引入一套用户 ID。

已有事实

  • users.user_id 是内部不可变用户主键。
  • users.current_display_user_id 是当前对外展示短号。
  • users.current_display_user_id_kind 当前支持 default / pretty
  • users.current_display_user_id_expires_at_ms0 或空时表示当前展示号不自动过期。
  • user_display_user_ids 已保存默认短号和靓号的实时归属。
  • pretty_display_user_id_leases 已保存靓号覆盖关系。
  • 当前默认短号和旧自助靓号校验是 5 到 11 位、不能 0 开头的纯数字。
  • 本需求的靓号池批量生成默认仍可使用数字规则;后台发放靓号不能复用纯数字限制,需要支持阿拉伯语、英文和数字组合。
  • 当前 App 端已有 /api/v1/users/me/display-id/pretty/apply,但参数是 pretty_display_user_id + lease_duration_ms + payment_receipt_id,不是靓号池申请要的 pretty_id

目标

  • 后台在“用户管理”下新增二级菜单“靓号管理”。
  • 后台可以配置靓号池的等级轨道、等级区间、生成规则和生成数量。
  • 后台批量生成靓号时,不能和当前库里的 users.user_id、默认短号、当前短号、历史有效短号、已有靓号池号码冲突。
  • 每个靓号池号码都有一个稳定 pretty_idApp 靓号池申请时只传 pretty_id
  • 靓号池申请默认长期持有;用户已有靓号时,新靓号直接覆盖旧靓号,旧靓号释放。
  • 后台发放靓号可以直接输入靓号内容,可以填写有效期;不填有效期时默认长期持有。
  • 后台发放靓号内容不限定纯数字,需要支持阿拉伯语、英文和数字组合。
  • App 用户申请靓号池号码时,服务端根据靓号池配置校验用户是否在对应等级区间内,在区间内才允许占用。
  • 用户拥有靓号后,个人信息、个人资料卡、房间个人资料卡、房间详情等资料返回都带当前靓号字段。

非目标

  • 不把靓号作为内部主键使用,所有业务关系仍然使用 user_id
  • 不让 room-service 保存靓号状态。房间只保存房间状态和用户 user_id,展示资料由 gateway 汇总用户资料。
  • 不让 server/admin 直接 import app 后端 services/*pkg/*
  • 首版不做等级下降后的自动回收。用户申请时校验等级区间;后续如果需要降级回收,再新增独立回收规则和定时任务。
  • 后台发放不需要等级校验,管理员只受后台权限和审计约束。
  • 首版不对外推送 IM。靓号申请成功后客户端以接口返回和下次资料刷新为准。

服务边界

模块 责任
user-service 靓号池、靓号项、后台发放、替换释放事务、短号唯一性、当前展示号覆盖、资料字段
activity-service 提供用户等级展示投影,至少能按 user_id 返回 wealth/game/charm 当前等级
gateway-service App HTTP 入口,按 token 用户申请靓号,返回统一 envelope
server/admin 后台 HTTP API、菜单、权限、审计调用 hyapp.local/api protobuf client
hyapp-admin-platform 用户管理下新增“靓号管理”页面,消费后台 API

等级区间规则

当前等级体系有三条轨道:

track 含义
wealth 财富等级
game 游戏等级
charm 魅力等级

靓号池配置必须保存 level_track,不能在服务端猜默认轨道。后台页面可以默认选中 wealth,但请求和数据库必须明确保存轨道。

靓号池申请时:

  1. 根据 pretty_id 锁定靓号项和所属靓号池。
  2. 读取用户当前等级,使用池子的 level_track
  3. 判断 min_level <= current_level <= max_level
  4. 通过后进入靓号占用事务。

数据库设计

新增表归属 hyapp_user,因为靓号和短号唯一性属于 user-service

现有短号表扩展

后台发放的靓号不一定是数字,现有短号相关字段需要从数字短号能力扩展成“展示号字符串”能力:

ALTER TABLE users
  MODIFY COLUMN current_display_user_id VARCHAR(64) NOT NULL,
  MODIFY COLUMN default_display_user_id VARCHAR(64) NOT NULL;

ALTER TABLE user_display_user_ids
  MODIFY COLUMN display_user_id VARCHAR(64) NOT NULL,
  MODIFY COLUMN live_display_user_id VARCHAR(64)
    GENERATED ALWAYS AS (
      CASE
        WHEN status IN ('active', 'held', 'reserved', 'blocked') THEN display_user_id
        ELSE NULL
      END
    ) STORED;

ALTER TABLE pretty_display_user_id_leases
  MODIFY COLUMN display_user_id VARCHAR(64) NOT NULL,
  MODIFY COLUMN active_display_user_id VARCHAR(64)
    GENERATED ALWAYS AS (
      CASE WHEN status = 'active' THEN display_user_id ELSE NULL END
    ) STORED,
  ADD COLUMN released_at_ms BIGINT NOT NULL DEFAULT 0 AFTER expires_at_ms,
  ADD COLUMN release_reason VARCHAR(64) NOT NULL DEFAULT '' AFTER released_at_ms;

ALTER TABLE display_user_id_change_logs
  MODIFY COLUMN old_display_user_id VARCHAR(64) NOT NULL,
  MODIFY COLUMN new_display_user_id VARCHAR(64) NOT NULL;

规则:

  • 默认短号仍然按现有数字规则生成。
  • 靓号池批量生成默认仍然按数字规则生成。
  • 后台发放靓号允许 1 到 64 个字符。
  • 后台发放靓号允许 Unicode 字母、Unicode 数字和组合标记,用于支持阿拉伯语、英文和数字。
  • 后台发放靓号不允许空白字符、控制字符、emoji 和标点符号。
  • 唯一性仍按 app_code + display_user_id 判断;如果产品要求大小写敏感,相关唯一列需要改成 utf8mb4_bin 或在写入前做明确大小写归一。

pretty_display_id_pools

保存后台配置的等级区间和默认生成规则。

CREATE TABLE pretty_display_id_pools (
  app_code VARCHAR(32) NOT NULL DEFAULT 'lalu',
  pool_id VARCHAR(64) NOT NULL PRIMARY KEY,
  name VARCHAR(128) NOT NULL,
  level_track VARCHAR(32) NOT NULL,
  min_level INT NOT NULL,
  max_level INT NOT NULL,
  rule_type VARCHAR(32) NOT NULL,
  rule_config_json JSON NULL,
  status VARCHAR(32) NOT NULL,
  sort_order INT NOT NULL DEFAULT 0,
  created_by_admin_id BIGINT NOT NULL DEFAULT 0,
  updated_by_admin_id BIGINT NOT NULL DEFAULT 0,
  created_at_ms BIGINT NOT NULL,
  updated_at_ms BIGINT NOT NULL,
  UNIQUE KEY uk_pretty_pool_range_rule (app_code, level_track, min_level, max_level, name),
  KEY idx_pretty_pool_status (app_code, status, sort_order)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='靓号池配置表';

字段说明:

字段 说明
pool_id 靓号池 ID
level_track wealth / game / charm
min_level 可申请最小等级,包含
max_level 可申请最大等级,包含
rule_type 生成规则,例如 aabbcc
rule_config_json 长度、号段、排除数字等扩展配置
status active / disabled

约束:

  • min_level 不能大于 max_level
  • level_track 必须是已有等级轨道。
  • active 池才允许 App 用户申请。

pretty_display_ids

保存每一个可申请靓号。每个靓号都有自己的 pretty_id

CREATE TABLE pretty_display_ids (
  app_code VARCHAR(32) NOT NULL DEFAULT 'lalu',
  pretty_id VARCHAR(64) NOT NULL PRIMARY KEY,
  pool_id VARCHAR(64) NOT NULL DEFAULT '',
  source VARCHAR(32) NOT NULL DEFAULT 'pool',
  display_user_id VARCHAR(64) NOT NULL,
  status VARCHAR(32) NOT NULL,
  assigned_user_id BIGINT NOT NULL DEFAULT 0,
  assigned_lease_id VARCHAR(64) NOT NULL DEFAULT '',
  assigned_at_ms BIGINT NOT NULL DEFAULT 0,
  released_at_ms BIGINT NOT NULL DEFAULT 0,
  release_reason VARCHAR(64) NOT NULL DEFAULT '',
  generated_batch_id VARCHAR(64) NOT NULL DEFAULT '',
  created_by_admin_id BIGINT NOT NULL DEFAULT 0,
  created_at_ms BIGINT NOT NULL,
  updated_at_ms BIGINT NOT NULL,
  live_display_user_id VARCHAR(64)
    GENERATED ALWAYS AS (
      CASE WHEN status IN ('available', 'assigned', 'disabled', 'reserved') THEN display_user_id ELSE NULL END
    ) STORED,
  UNIQUE KEY uk_pretty_display_ids_live (app_code, live_display_user_id),
  KEY idx_pretty_display_ids_pool_status (app_code, pool_id, status),
  KEY idx_pretty_display_ids_assigned_user (app_code, assigned_user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='靓号池号码表';

状态说明:

status 说明
available 可申请
assigned 已被用户占用
disabled 后台下架,不允许申请
reserved 系统预留,不允许普通用户申请
released 曾经发放或占用,已释放,不参与可申请列表
deleted 软删除,只保留审计

source 说明:

source 说明
pool 靓号池生成,释放后可以回到 available
admin_grant 后台直接发放,释放后默认进入 released

pretty_display_id_generation_batches

保存批量生成记录,方便后台展示和排查。

CREATE TABLE pretty_display_id_generation_batches (
  app_code VARCHAR(32) NOT NULL DEFAULT 'lalu',
  batch_id VARCHAR(64) NOT NULL PRIMARY KEY,
  pool_id VARCHAR(64) NOT NULL,
  rule_type VARCHAR(32) NOT NULL,
  rule_config_json JSON NULL,
  requested_count INT NOT NULL,
  generated_count INT NOT NULL,
  skipped_conflict_count INT NOT NULL,
  status VARCHAR(32) NOT NULL,
  operator_admin_id BIGINT NOT NULL,
  request_id VARCHAR(64) NOT NULL DEFAULT '',
  created_at_ms BIGINT NOT NULL,
  updated_at_ms BIGINT NOT NULL,
  KEY idx_pretty_batch_pool (app_code, pool_id, created_at_ms)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='靓号批量生成记录表';

靓号池生成规则

靓号池批量生成支持数字规则、日期规则、寓意号和自定义规则。数字规则允许前导 0号码以字符串保存。

rule_type 说明
aaaaaaaaaaaaaaaaaaaaaaaaaaa 2A 到 7A 连号
aabb 两组对子
aabbcc 三组重复数字
aabbccdd 四组对子
ababababab 交替号
abbaabcbaabccba 回文号
aaabaaaabaaaaab 拖一号
aaabbbaaaabbbb 双组号
abcabcdabcdeabcdef 顺子号
cbadcbaedcbafedcba 倒顺号
abcabc 顺子重复号
date_yyyymmdddate_yymmdd 日期号
love 520、521、1314 等寓意号
custom_values 使用 rule_config_json.values 固定候选
custom_pattern 使用 rule_config_json.pattern 自定义模式

rule_config_json 可选字段:

字段 说明
digits 限制可用数字,例如 ["6","8","9"]
exclude_digits 排除数字,例如 ["4"]
prefixsuffix 给生成候选加前缀或后缀
values custom_values 的候选数组
pattern custom_pattern 的模式,例如 AAAABBBB
year_fromyear_to 日期号年份范围
start_dateend_date 日期号精确范围,格式 yyyy-mm-dd

后台直接发放靓号不受本节数字生成规则限制,按“现有短号表扩展”里的后台发放字符规则校验。

批量生成事务

批量生成时由 user-service 完成冲突检查和入库。

冲突范围:

  • CAST(users.user_id AS CHAR)
  • users.default_display_user_id
  • users.current_display_user_id
  • user_display_user_idsstatus IN ('active','held','reserved','blocked') 的号码。
  • pretty_display_idsstatus IN ('available','assigned','disabled','reserved') 的号码。

流程:

  1. 后台提交 pool_id + rule_type + rule_config + count
  2. user-service 生成候选号码。
  3. 每个候选号码先做数字短号格式校验。
  4. 批量查询冲突号码,过滤已冲突项。
  5. 插入 pretty_display_ids,唯一键兜底处理并发冲突。
  6. pretty_display_id_generation_batches
  7. 返回本次生成数量和跳过冲突数量。

为了避免生成器死循环,服务端需要设置最大尝试次数,例如 count * 20,仍不足时返回实际生成数量和 skipped_conflict_count

通用替换释放事务

靓号池申请和后台发放都复用同一个“替换当前靓号”事务。

如果用户没有 active pretty 展示号:

  1. 默认短号从 active 改为 held
  2. 新靓号写入 pretty_display_user_id_leases
  3. 新靓号写入 user_display_user_idsdisplay_user_id_kind='pretty'
  4. 更新 users.current_display_user_idcurrent_display_user_id_kindcurrent_display_user_id_expires_at_ms
  5. display_user_id_change_logs

如果用户已有 active pretty 展示号:

  1. 锁定当前用户行和旧 active lease。
  2. pretty_display_user_id_leases.status 改为 cancelledrelease_reason='replaced_by_new_pretty'
  3. user_display_user_ids.status 改为 released,记录 released_at_ms
  4. 如果旧靓号来自靓号池,旧 pretty_display_ids.status 改回 available,清空 assigned_user_idassigned_lease_idassigned_at_ms
  5. 如果旧靓号来自后台发放,旧 pretty_display_ids.status 改为 released,保留历史。
  6. 写旧靓号释放日志。
  7. 再按新靓号写入当前 active 展示号。

这个规则意味着:用户申请或被后台发放新靓号时,不返回“已有靓号”冲突,而是直接覆盖旧靓号。

靓号池申请事务

App 靓号池申请只接受 pretty_id,不接受客户端自报号码、等级、用户 ID 或租期。

事务流程:

  1. 从 access token 读取当前 user_id
  2. SELECT ... FOR UPDATE 锁定 pretty_display_ids
  3. 校验靓号项为 availablesource='pool'
  4. 锁定所属 pretty_display_id_pools,校验为 active
  5. 调用 activity-service 读取当前用户在 level_track 下的等级。
  6. 校验用户等级在 [min_level, max_level]
  7. 进入通用替换释放事务。
  8. 写新 pretty_display_user_id_leasessource='level_pool'expires_at_ms=0
  9. 更新新 pretty_display_ids.status='assigned',写入 assigned_user_idassigned_lease_id
  10. 返回当前身份投影。

靓号池申请默认长期持有:

  • pretty_display_user_id_leases.expires_at_ms = 0
  • users.current_display_user_id_expires_at_ms = 0
  • 不由定时任务自动过期。

后台发放事务

后台发放不依赖靓号池,也不校验用户等级。

输入:

  • target_user_id:目标 App 用户。
  • display_user_id:管理员输入的靓号内容。
  • duration_ms:可选;大于 0 表示限时靓号,不传或传 0 表示长期。
  • reason:发放原因,写入审计和 change log。

事务流程:

  1. 校验管理员有 pretty-id:grant 权限。
  2. 校验 display_user_id 符合后台发放字符规则。
  3. 检查 display_user_id 不与当前有效 users.user_id 字符串、默认短号、当前短号、历史有效短号、已有靓号冲突。
  4. 锁定目标用户行。
  5. 进入通用替换释放事务。
  6. 生成新的 pretty_idlease_id
  7. pretty_display_idssource='admin_grant'status='assigned'
  8. pretty_display_user_id_leasessource='admin_grant'
  9. 如果 duration_ms > 0expires_at_ms = now_ms + duration_ms;否则 expires_at_ms = 0
  10. 更新 users.current_display_user_id_expires_at_ms 为同一个过期时间。
  11. 返回当前身份投影。

限时后台靓号:

  • expires_at_ms > 0 时继续复用现有靓号过期恢复逻辑。
  • 到期后恢复默认短号,不回到靓号池。
  • 如果到期前又发放或申请了新靓号,旧限时靓号按通用替换释放事务取消。

Proto 变更

api/proto/user/v1/user.proto 中扩展或新增 identity 管理 RPC。

新增 App 申请 RPC

message ApplyPrettyDisplayIDFromPoolRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  string pretty_id = 3;
}

message ApplyPrettyDisplayIDFromPoolResponse {
  UserIdentity identity = 1;
  string pretty_id = 2;
  string lease_id = 3;
}

新增后台管理 RPC

message AdminGrantPrettyDisplayIDRequest {
  RequestMeta meta = 1;
  int64 target_user_id = 2;
  string display_user_id = 3;
  int64 duration_ms = 4;
  string reason = 5;
  int64 operator_admin_id = 6;
}

message AdminGrantPrettyDisplayIDResponse {
  UserIdentity identity = 1;
  string pretty_id = 2;
  string lease_id = 3;
}

service UserPrettyDisplayIDAdminService {
  rpc ListPrettyDisplayIDPools(ListPrettyDisplayIDPoolsRequest) returns (ListPrettyDisplayIDPoolsResponse);
  rpc CreatePrettyDisplayIDPool(CreatePrettyDisplayIDPoolRequest) returns (PrettyDisplayIDPoolResponse);
  rpc UpdatePrettyDisplayIDPool(UpdatePrettyDisplayIDPoolRequest) returns (PrettyDisplayIDPoolResponse);
  rpc GeneratePrettyDisplayIDs(GeneratePrettyDisplayIDsRequest) returns (GeneratePrettyDisplayIDsResponse);
  rpc ListPrettyDisplayIDs(ListPrettyDisplayIDsRequest) returns (ListPrettyDisplayIDsResponse);
  rpc SetPrettyDisplayIDStatus(SetPrettyDisplayIDStatusRequest) returns (PrettyDisplayIDResponse);
  rpc AdminGrantPrettyDisplayID(AdminGrantPrettyDisplayIDRequest) returns (AdminGrantPrettyDisplayIDResponse);
}

资料投影建议增加字段:

message User {
  // existing fields...
  string pretty_id = 24;
  string pretty_display_user_id = 25;
}

message UserIdentity {
  // existing fields...
  string pretty_id = 8;
  string pretty_display_user_id = 9;
}

字段规则:

字段 说明
display_user_id 当前对外展示号;有靓号时等于靓号
display_user_id_kind default / pretty
display_user_id_expires_at_ms 长期持有为 0
pretty_id 当前占用的靓号池记录 ID没有则为空
pretty_display_user_id 当前占用的靓号号码,没有则为空

后台 HTTP 接口

后台接口归属 server/admin/internal/modules/prettyid,路径放在用户管理域。

靓号池列表

内容
地址 GET /api/v1/admin/users/pretty-id-pools
参数 statuslevel_trackpagepage_size
返回值 items[]total,每项包含 pool_idnamelevel_trackmin_levelmax_levelrule_typestatus
相关 IM

创建靓号池

内容
地址 POST /api/v1/admin/users/pretty-id-pools
参数 namelevel_trackmin_levelmax_levelrule_typerule_config_jsonstatus
返回值 靓号池详情
相关 IM

更新靓号池

内容
地址 PUT /api/v1/admin/users/pretty-id-pools/{pool_id}
参数 namelevel_trackmin_levelmax_levelrule_typerule_config_jsonstatus
返回值 靓号池详情
相关 IM

批量生成靓号

内容
地址 POST /api/v1/admin/users/pretty-id-pools/{pool_id}/generate
参数 rule_typerule_config_jsoncount
返回值 batch_idrequested_countgenerated_countskipped_conflict_count
相关 IM

靓号列表

内容
地址 GET /api/v1/admin/users/pretty-ids
参数 pool_idsourcestatuskeywordassigned_user_idpagepage_size
返回值 items[]total,每项包含 pretty_idsourcedisplay_user_idpoolstatusassigned_user_idassigned_lease_id
相关 IM

后台发放靓号

内容
地址 POST /api/v1/admin/users/pretty-ids/grant
参数 target_user_iddisplay_user_idduration_msreasonduration_ms 不传或为 0 表示长期
返回值 user_iddisplay_user_iddisplay_user_id_kinddisplay_user_id_expires_at_mspretty_idpretty_display_user_idlease_id
相关 IM

靓号状态修改

内容
地址 POST /api/v1/admin/users/pretty-ids/{pretty_id}/status
参数 status,只允许未占用的靓号在 availabledisabledreserved 之间切换
返回值 靓号详情
相关 IM

App HTTP 接口

可申请靓号列表

内容
地址 GET /api/v1/users/pretty-ids/available
参数 pagepage_size
返回值 items[]total,每项包含 pretty_iddisplay_user_idlevel_trackmin_levelmax_level
相关 IM

返回只包含当前用户等级满足条件的 available 靓号。

靓号池申请

内容
地址 POST /api/v1/users/me/display-id/pretty/apply
参数 pretty_id
返回值 user_iddisplay_user_iddisplay_user_id_kinddisplay_user_id_expires_at_mspretty_idpretty_display_user_idlease_id
相关 IM

说明:

  • 保留当前地址,避免前端路由重复;请求体改为优先读取 pretty_id
  • 靓号池申请默认长期持有,display_user_id_expires_at_ms=0
  • 用户已有靓号时,新靓号直接覆盖旧靓号,旧靓号释放。
  • 如果要兼容旧付费参数,服务端可以临时保留 pretty_display_user_id + lease_duration_ms + payment_receipt_id 分支,但新 App 只使用 pretty_id

资料返回改造

所有资料返回统一使用同一套字段。

场景 当前链路 改造点
个人信息 GET /api/v1/users/me -> user-service.GetUser User proto 增加 pretty_idpretty_display_user_id
个人资料卡 用户资料批量接口或资料详情 复用 User 投影字段
房间个人资料卡 GET /api/v1/users/room-display-profiles:batch roomDisplayProfileData 增加靓号字段
房间详情 room detail 中的 profiles[] 来自同一批 roomDisplayProfileData
登录 / 刷新 token AuthToken 已有 display 字段 如客户端需要启动后立即知道靓号,补 pretty_idpretty_display_user_id

响应规则:

  • 用户没有靓号:display_user_id 返回默认短号,display_user_id_kind='default'pretty_id=''pretty_display_user_id=''
  • 用户有靓号:display_user_id 返回靓号,display_user_id_kind='pretty'pretty_id 返回池记录 IDpretty_display_user_id 返回靓号号码。
  • 长期持有靓号:display_user_id_expires_at_ms=0
  • 后台限时发放靓号:display_user_id_expires_at_ms 返回实际过期时间。

后台菜单和权限

新增权限:

权限码 kind 说明
pretty-id:view menu 查看靓号管理
pretty-id:update button 新增、编辑、上下架靓号池和靓号
pretty-id:generate button 批量生成靓号
pretty-id:grant button 后台直接发放靓号

新增菜单:

字段
parent app-users
title 靓号管理
code app-user-pretty-ids
path /app/users/pretty-ids
icon badge
permission_code pretty-id:view
sort 65

需要同步:

  • server/admin/migrations/044_pretty_id_management.sql
  • server/admin/internal/repository/seed.go 默认权限和菜单。
  • platform-admin 默认绑定全部新增权限。
  • 只读角色只绑定 pretty-id:view

代码落点

位置 改动
api/proto/user/v1/user.proto 增加靓号池 RPC、后台发放 RPC、资料字段
services/user-service/internal/domain/user 增加 pool、pretty item、申请命令、后台发放命令和非数字靓号校验
services/user-service/internal/storage/mysql/identity 增加靓号池、生成、申请、后台发放、替换释放事务
services/user-service/deploy/mysql/initdb/001_user_service.sql 增加新表并扩展短号字段长度
services/user-service/migrations 增加线上迁移
services/gateway-service/internal/transport/http/userapi 改造靓号池申请接口、可申请列表、资料字段
services/gateway-service/internal/transport/http/roomapi 房间资料结构补靓号字段
server/admin/internal/integration/userclient 增加后台靓号管理 client
server/admin/internal/modules/prettyid 新增后台模块
server/admin/internal/router/router.go 注册后台模块
server/admin/migrations 菜单和权限迁移

错误码建议

code 场景
PRETTY_ID_NOT_FOUND pretty_id 不存在
PRETTY_ID_NOT_AVAILABLE 靓号不可申请或已被占用
PRETTY_ID_LEVEL_NOT_MATCH 用户等级不在池区间内
PRETTY_DISPLAY_ID_INVALID 后台发放的靓号内容不符合字符规则
DISPLAY_USER_ID_CONFLICT 生成或申请时发现短号冲突

验证项

必须验证:

  • make proto 成功,生成 .pb.go_grpc.pb.go
  • go test ./...hyapp-server 根目录通过。
  • go test ./...hyapp-server/server/admin 通过。
  • 批量生成 1000 个 aabbcc 靓号时,不插入与 users.user_id、默认短号、当前短号、已有靓号冲突的号码。
  • 用户等级低于池区间时,申请返回 PRETTY_ID_LEVEL_NOT_MATCH
  • 用户等级在池区间内时,申请成功并更新:
    • pretty_display_ids.status='assigned'
    • pretty_display_user_id_leases.source='pool'
    • user_display_user_ids.display_user_id_kind='pretty'
    • users.current_display_user_id_kind='pretty'
  • 用户已有靓号时,再申请另一个靓号会覆盖旧靓号,旧靓号释放;旧池靓号回到 available,旧后台发放靓号进入 released
  • 后台发放 VIP2026ملك123 这类英文数字或阿拉伯语数字组合可以成功。
  • 后台发放不传 duration_ms 时,返回 display_user_id_expires_at_ms=0
  • 后台发放传 duration_ms 时,返回实际 display_user_id_expires_at_ms,到期后恢复默认短号。
  • GET /api/v1/users/me 返回靓号字段。
  • GET /api/v1/users/room-display-profiles:batch 返回靓号字段。
  • 房间详情 profiles[] 返回靓号字段。
  • 后台 /api/v1/navigation/menus 能看到“用户管理 > 靓号管理”。