生成短链接

扫描二维码 上传二维码
选择防红平台类型,避免链接被拦截
选择允许访问的平台类型

短链接跳转服务实现全指南

《快缩短网址》优雅重构版
项目域名:suo.run

一、系统总览
suo.run 以极简之姿,完成「长链→短链→跳转→洞察」的闭环。整个链路被抽象为一幅 DAG(有向无环图),节点即函数,边即数据。下文先给出 DAG 图,再逐一拆解每个节点的输入、输出与实现。

DAG 图(mermaid 语法,可直接渲染)

graph TD
A[长链输入] --> B{校验长链}
B -->|有效| C[生成短链]
B -->|无效| Z1[提示错误]
C --> D[持久化映射]
D --> E[返回短链给用户]

F[短链输入] --> G{校验短链}
G -->|存在| H[记录访问]
G -->|不存在| Z2[404 页面]
H --> I[302 跳转至长链]

J[查看统计] --> K{短链存在?}
K -->|是| L[聚合访问日志]
K -->|否| Z3[提示无数据]
L --> M[渲染图表]


二、节点规格(输入 / 输出 / 函数签名)



1. 长链校验节点
输入:long_url (str)
输出:is_valid (bool)
函数:
   async def validate_long_url(url: str) -> bool:
try:
async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=3)) as s:
async with s.head(url, allow_redirects=True) as r:
return r.status == 200
except Exception:
return False


2. 短链生成节点
输入:long_url (str)
输出:short_code (str, 6 位)
算法:
- 取当前 Unix 时间纳秒后 4 位
- 拼接 long_url 的 crc32 十六进制前 2 位
- 再随机 2 位 Base62 字符
保证 6 位、高离散、可逆查。

3. 持久化映射节点
输入:short_code, long_url, created_at
输出:None
Redis 结构:
   Key:   su:{short_code}
Value: {"l": long_url, "c": created_at, "v": []} # v 留空,后续追加访问时间

幂等写入,若已存在则直接返回原 short_code。

4. 短链校验节点
输入:short_code (str)
输出:long_url or None
函数:
   async def resolve_short_code(code: str) -> Optional[str]:
data = await redis.hgetall(f"su:{code}")
return data.get("l") if data else None


5. 记录访问节点
输入:short_code, now (ISO8601)
输出:None
行为:
- 原子地 LPUSH 访问时间到 su:{code} → v
- 异步写一份冷存(ClickHouse 或 S3 Parquet)用于离线分析

6. 302 跳转节点
输入:long_url
输出:HTTP 302 Location
函数:
   async def redirect(long_url: str) -> web.Response:
return web.Response(status=302, headers={"Location": long_url})


7. 聚合访问日志节点
输入:short_code
输出:stats (dict)
结构:
   {
"total": 1024,
"daily": {"2026-05-01": 100, "2026-05-02": 120, ...},
"referrers": {"t.co": 300, "weibo.com": 200, ...}
}

实现:
- 从 su:{code} → v 取最近 10k 条
- 在内存做直方图,O(n) 完成

8. 渲染图表节点
输入:stats (dict)
输出:HTML + ECharts 折线图
前端路由:/s/{code}/stats

三、RESTful API 规格

| Method | Path | 输入(JSON) | 输出(JSON) | 语义 |
|--------|---------------------|--------------------------|----------------------------------|--------------------|
| POST | /api/v1/shorten | {"long_url": "https://..."} | {"short_code":"aB3x9z"} | 长链→短链 |
| GET | /{code} | — | 302 Location | 跳转 |
| GET | /api/v1/stats/{code}| — | {"total":n,"daily":{...}} | 获取统计 |

四、前端极简实现(React + Tailwind)



1. 生成页 /
   const [long, setLong] = useState("");
const shorten = async () => {
const { short_code } = await fetch("/api/v1/shorten", {
method: "POST",
body: JSON.stringify({ long_url: long }),
headers: { "Content-Type": "application/json" }
}).then(r => r.json());
navigator.clipboard.writeText(https://suo.run/${short_code});
};


2. 统计页 /s/:code/stats
- 使用 SWR 拉 /api/v1/stats/${code}
- ECharts 折线图展示 daily 访问

五、Redis 键空间一览

su:{code}           Hash   主映射
su:counter:{code} String 原子计数器(可选,加速 total 查询)
su:ref:{code} ZSet 按 referrer 聚合


六、扩展预留

- 自定义短码:POST /api/v1/shorten 增加 alias 字段,冲突返回 409。
- 二维码:GET /{code}.png 动态生成 300x300 PNG。
- 密码保护:主 Hash 增加字段 "pwd",跳转前弹出输入框。

至此,suo.run 的核心与优雅扩展已完整呈现。