Files
character-roleplay-backend/app/models/base.py
T

85 lines
2.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import hashlib
from sqlalchemy import Column, String
from sqlalchemy.orm import declarative_base, declared_attr
import ulid
Base = declarative_base()
# Base62 字符集
ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
def encode_base62(num: int, length: int = None) -> str:
"""将一个整数编码为 Base62 字符串,支持左侧补 0 固定长度"""
if num == 0:
res = ALPHABET[0]
else:
arr = []
base = len(ALPHABET)
while num:
num, rem = divmod(num, base)
arr.append(ALPHABET[rem])
res = "".join(reversed(arr))
if length:
# 如果太长就截取,太短就用第一个字符(0)补齐
return res.rjust(length, ALPHABET[0])[-length:]
return res
def encode_tablename_to_prefix(tablename: str, length: int = 3) -> str:
"""
将表名 encode 成固定长度的前缀,使用 MD5 拿到表名的唯一哈希值,转为整数后用 Base62 截取前 N 位
"""
# 计算表名的 MD5 哈希
hasher = hashlib.md5(tablename.encode("utf-8"))
hex_digest = hasher.hexdigest()
# 将十六进制哈希转为大整数
num = int(hex_digest, 16)
# 转为 Base62 字符串
b62_str = encode_base62(num, length=length)
# 截取固定长度(例如 3 位)作为该表的专属代码
return b62_str[:length]
def gen_convex_payload() -> str:
"""生成 128位 ULID 转 Base62 的趋势递增 Payload"""
u = ulid.new()
return encode_base62(u.int)
class AutoID:
"""
全自动 Convex 风格 ID 注入器
不再需要手动定义任何 prefix,全自动根据 __tablename__ 编码生成
支持子类自定义长度
"""
# 默认配置(如果子类不定义,则使用这些默认值)
_id_prefix_len = 3 # 前缀长度
_id_payload_len = 22 # 主体长度(ULID 转 Base62 最大约 22 位)
@declared_attr
def id(cls):
# 运行时动态获取子类的 __tablename__
tablename = cls.__tablename__
# 获取子类配置的长度
p_len = cls._id_prefix_len
s_len = cls._id_payload_len
# 将表名编码为固定前缀(例如 "users" -> "7X1"
hasher = hashlib.md5(tablename.encode("utf-8"))
table_num = int(hasher.hexdigest(), 16)
table_prefix = encode_base62(table_num, length=p_len)
# 核心工厂函数:拼接【编码后的表前缀】和【递增Payload】
def id_factory():
# 生成固定长度的 Payload
u = ulid.new()
payload = encode_base62(u.int, length=s_len)
# 直接拼接,不带下划线
return f"{table_prefix}{payload}"
return Column(String(p_len + s_len), primary_key=True, default=id_factory)