自定义设计

那封改变一切的邮件 💬

那天早上,我像往常一样打开邮箱,准备处理用户的反馈邮件。大多是些”链接打不开”、“访问速度慢”之类的问题,我已经习惯了。

但有一封邮件让我停下了手头的动作。

发件人:Nike Digital Team 主题:关于您的短链接服务

您好,

我们在社交媒体上看到很多用户在使用您的短链接服务,效果很好。

我们有一个商业需求:我们希望获得 short.url/nike 这样的品牌短链接。 我们的产品链接会定期更新,但短链接可以保持不变,这样在广告投放时更加稳定。

关于价格,我们可以接受按月或按年付费。请告知您的合作方式。

期待您的回复。

—— Nike Digital Team

我的心跳突然加速了。

付费? 这是我的短链接服务第一次有人主动提出要付费!

我立刻意识到:这不是一个简单的功能需求,这是一个商业机会。

自定义短链接——这三个字在我脑海中闪烁。用户想要自己定义的短链接后缀,而不是系统生成的随机字符。

这不仅仅是个技术问题,这是个产品设计问题。我需要设计一套完整的自定义别名系统。


定义规则:什么别名是合法的?📋

我开始思考第一个问题:什么样的自定义别名应该被允许?

如果允许用户输入任意字符,可能会出现安全问题、URL冲突问题、甚至是系统路径混乱的问题。

我拿出一张纸,开始画规则边界:

┌─────────────────────────────────────────────┐
│           自定义别名验证规则                  │
├─────────────────────────────────────────────┤
│ 1. 长度限制:3-30个字符                       │
│ 2. 字符集:字母、数字、连字符、下划线          │
│ 3. 保留词:系统保留的敏感词汇                  │
│ 4. 重复检查:不能与现有别名冲突               │
│ 5. 敏感词过滤:不当词汇拦截                   │
└─────────────────────────────────────────────┘

规则1:长度限制

为什么不能太短?

长度 < 3字符:
- 组合太少(26个字母 × 26个字母 = 676种)
- 很容易冲突
- 不够有意义
- 容易与系统路径混淆

为什么不能太长?

长度 > 30字符:
- 失去短链接的意义
- 用户体验差
- 违背了"短"的初衷
- 可能超过某些系统的URL长度限制

我先把验证规则写清楚:

落地思路

  • 这里省略具体语法,只保留设计层面的职责边界。
  • 读这段时重点看:输入是什么、系统做哪些判断、状态如何变化、失败时如何兜底。

规则2:字符集限制

允许哪些字符?

我查阅了RFC 3986(URL规范),发现URL路径段中安全使用的字符包括:

  • 字母:a-z, A-Z
  • 数字:0-9
  • 连字符:-
  • 下划线:_
  • 点号:.(但我不想使用,因为看起来像文件扩展名)

不允许哪些字符?

× 空格:会被编码成 %20,破坏短链接的简洁性
× 中文/日文等非ASCII字符:需要编码,变长
× 特殊字符:/、?、&、#、@等都有URL特殊含义
× 控制字符:可能破坏系统
× 表情符号:编码后极长

我继续把字符规则写清楚:

落地思路

  • 这里省略具体语法,只保留设计层面的职责边界。
  • 读这段时重点看:输入是什么、系统做哪些判断、状态如何变化、失败时如何兜底。

规则3:保留词检查

这是最棘手的部分。我需要确保用户不能注册与系统路径冲突的别名。

我列出了第一版保留词清单:

落地思路

  • 这里省略具体语法,只保留设计层面的职责边界。
  • 读这段时重点看:输入是什么、系统做哪些判断、状态如何变化、失败时如何兜底。

规则4:重复检查

即使别名通过了所有验证规则,我还必须确保它没有被其他用户使用。

落地思路

  • 这里省略具体语法,只保留设计层面的职责边界。
  • 读这段时重点看:输入是什么、系统做哪些判断、状态如何变化、失败时如何兜底。

规则5:敏感词过滤

最后一个安全网:防止用户注册不当的别名。

落地思路

  • 这里省略具体语法,只保留设计层面的职责边界。
  • 读这段时重点看:输入是什么、系统做哪些判断、状态如何变化、失败时如何兜底。

完整的验证器 🔍

现在我把所有规则整合成一个完整的验证器:

落地思路

  • 这里省略具体语法,只保留设计层面的职责边界。
  • 读这段时重点看:输入是什么、系统做哪些判断、状态如何变化、失败时如何兜底。

数据库设计:自定义别名需要独立存储 💾

我意识到自定义别名和自动生成的短码是两个不同的体系:

自动生成的短码:
- 长度固定(如6字符)
- 字符集固定(Base62)
- 无特殊含义
- 冲突概率极低

自定义别名:
- 长度不固定(3-30字符)
- 字符集宽松但有限制
- 有商业价值
- 冲突概率较高

这意味着我需要设计一个新的表结构,而不是简单地复用现有的urls表。

设计方案1:扩展现有表

数据设计要点

  • 这是一次表结构演进:随着链接管理能力增加,把新状态、新时间点或新归属关系补进数据模型。
  • 索引服务于高频查询,重点关注 idx_custom_alias

优点

  • 改动最小
  • 查询简单(一张表)

缺点

  • 字段冗余(short_codecustom_alias
  • 不够清晰(自定义和自动生成的混在一起)

设计方案2:独立表(我选择这个)

数据设计要点

  • 核心是在 custom_aliases 里保存业务事实,而不是把规则散落在应用逻辑里。
  • 写入时要保证短码唯一、字段完整,并为后续查询留下必要状态。
  • 更新操作通常意味着链接状态变化,要同步考虑缓存刷新和审计记录。
  • 关键字段包括 iduser_idcustom_aliaslong_urlcreated_atupdated_atwordreason,它们决定了后续查询和管理能力。

优点

  • 职责清晰
  • 易于扩展(比如未来要给自定义别名加更多属性)
  • 便于审计和历史追踪

缺点

  • 查询时需要JOIN两张表(但这个场景很少)

我选择了方案2。


冲突处理:两个人都想要 ‘nike’ 怎么办?⚡

这是一个真实的问题:如果有两个商家都想注册 nike 这个别名,应该给谁?

我设计了一个”先到先得”的机制,配合事务保证原子性:

落地思路

  • 这里省略具体语法,只保留设计层面的职责边界。
  • 读这段时重点看:输入是什么、系统做哪些判断、状态如何变化、失败时如何兜底。

并发安全

在高并发场景下,两个用户可能同时注册同一个别名。我需要确保只有一个人能成功。

落地思路

  • 这里省略具体语法,只保留设计层面的职责边界。
  • 读这段时重点看:输入是什么、系统做哪些判断、状态如何变化、失败时如何兜底。

用户体验:实时可用性检查 🎨

我花了两天时间优化一个细节:当用户输入自定义别名时,实时显示它是否可用。

这需要前端的防抖(debounce)和后端的高效验证配合。

前端实现

落地思路

  • 这里省略具体语法,只保留设计层面的职责边界。
  • 读这段时重点看:输入是什么、系统做哪些判断、状态如何变化、失败时如何兜底。

后端API

落地思路

  • 这里省略具体语法,只保留设计层面的职责边界。
  • 读这段时重点看:输入是什么、系统做哪些判断、状态如何变化、失败时如何兜底。

这个交互细节花了两天时间,但效果很好——用户可以立即知道他们想要的别名是否可用,避免了填写完表单后才发现别名被占用的挫败感。


上线第一天:有人注册了50个品牌名 🚨

自定义别名功能上线了。

我满怀期待地打开后台监控,看看有没有人会使用这个新功能。

第一天结束时,我看到了一些数据:

自定义别名注册数量:127个
最热门的别名:
- nike (1次)
- adidas (1次)
- apple (1次)
- samsung (1次)
- test (3次)
- mylink (5次)
- brand123 (2次)

看起来还好。

但当我深入查看时,发现了一个问题:有一个用户注册了50个知名品牌的别名。

用户 ID: 18472
注册的别名:
- nike
- adidas
- puma
- reebok
- newbalance
- converse
- asics
- saucony
- ...
(共50个运动品牌名)

更糟糕的是,这些别名指向的URL都是一些不知名的电商网站,很明显是抢注行为。

我意识到:这需要防抢注机制。

问题

  1. 恶意用户可以批量注册大量品牌名
  2. 真正的品牌所有者反而无法使用自己的品牌名
  3. 这可能导致法律纠纷
  4. 破坏了平台的公信力

我需要解决

  • 限制每个用户的注册数量
  • 实施品牌名验证机制
  • 建立申诉流程
  • 考虑引入付费门槛

这些,将是下一节的内容。


本节小结 📝

完整的别名验证流程

用户输入别名

【规则1】长度检查(3-30字符)

【规则2】字符检查(字母、数字、-、_)

【规则3】保留词检查(系统路径)

【规则4】敏感词检查(不当词汇)

【规则5】重复检查(数据库查询)

【规则6】冲突处理(事务保证)

创建成功 ✅

核心能力拆解

组件职责
CustomAliasValidator完整的别名验证器
AliasCharacterValidator字符规则验证
ReservedWordChecker保留词检查
ProfanityFilter敏感词过滤
custom_aliases存储自定义别名
reserved_words存储保留词

数据库表结构

数据设计要点

  • 更新操作通常意味着链接状态变化,要同步考虑缓存刷新和审计记录。

想一想

问题1:品牌验证

Nike团队真的注册了nike这个别名,还是被抢注者抢占了?

提示

考虑:

  1. 如何验证用户真的是品牌所有者?
  2. 是否需要上传商标证书?
  3. 是否需要企业认证?
  4. 未认证用户是否允许注册品牌名?

我们将在下一节讨论这个问题。

问题2:批量注册防护

如何防止一个用户在短时间内注册大量别名?

提示

考虑:

  1. 限制每个用户的注册数量(如10个)
  2. 限制注册频率(如每小时最多5个)
  3. 短别名(3-4字符)需要额外审核
  4. 引入付费门槛(减少恶意注册)

问题3:历史遗留

如果某个别名被抢注了,真正的品牌所有者怎么办?

提示

考虑:

  1. 是否可以强制转移别名?
  2. 是否需要建立申诉流程?
  3. 如何证明所有权?
  4. 是否需要仲裁机制?

(下一节:抢注防护与品牌验证)