市场分类
服务:zepto-server(链外),不是链节点。
生产基址:https://admin.auroran.io/api/
例:https://admin.auroran.io/api/public/market-categories
交易站浏览器前缀/market-classifications-api/*→/api/public/*。
管理接口要 Admin Bearer,公开读无鉴权。
链上市场列表仍走getMarkets。
市场分类是后台可配置的运营元数据,不参与链上状态与共识。 分类体系为两级:总分类 → 子分类;名称、数量、层级均可在后台增删改。
分类与市场的关联采用 「分类大集合」模式:后端维护一张 market_catalog,
以 symbol 为键(统一小写存储) 记录「市场 → 分类」,包含链上尚未上架的市场。
前端拉取目录后,与链上市场列表按 symbol 忽略大小写 匹配归类;
管理员也可按 symbol 手动向分类中添加/移除市场。
默认种子对齐 Hyperliquid
perpCategories:总分类crypto/tradfi; crypto 下layer1/layer2/defi/meme/ai/gaming/other, tradfi 下stocks/indices/commodities/forex/bonds/other。preipo(上市前)已从分类体系移除,同步时跳过且不会重建。
数据模型
market_categories(分类树):
| 字段 | 类型 | 说明 |
|---|---|---|
id | u32 | 分类 ID |
parent_id | u32? | 父分类 ID;null = 总分类(仅支持两级) |
code | String | 稳定标识符(^[a-z0-9_]+$),用于 HL 导入映射 |
labels | Map<String,String> | 多语言展示名:locale → label(缺语言回退 zh → en → code) |
sort_order | u32 | 排序 |
enabled | bool | 停用后不出现在公开接口 |
source | String | seed(内置种子)/ manual(手工)/ hl(HL 同步) |
created_at_ms | u64 | 创建时间 |
updated_at_ms | u64 | 最近更新时间 |
market_catalog(市场 → 分类大集合,现行分类来源):
| 字段 | 类型 | 说明 |
|---|---|---|
symbol | String | 主键,统一小写存储,匹配忽略大小写 |
category_id | u32 | 关联的分类节点(允许总分类或子分类) |
source | String | hl(HL 同步)/ manual(管理员手工添加) |
updated_by | String | 最近操作人 |
market_classifications(旧 market_id 关联表)不再作为分类来源。
迁移时历史行一次性导入 market_catalog(source='manual')。
对应 HTTP 路由仍挂在 zepto-server(见 遗留路由),
交易界面与分类页只读 market_catalog。
公开只读接口(无鉴权)
生产:https://admin.auroran.io/api/public/…。
交易站可由 nginx / Vite 反代 /market-classifications-api/* → /api/public/*。
GET /api/public/market-categories
仅返回启用分类,按 parent_id IS NULL DESC, parent_id, sort_order 排序。
响应含 source / created_at_ms / updated_at_ms。
{
"categories": [{
"id": 1,
"parent_id": null,
"code": "crypto",
"labels": { "zh": "加密货币", "en": "Crypto" },
"sort_order": 10,
"enabled": true,
"source": "seed",
"created_at_ms": 1755000000000,
"updated_at_ms": 1755000000000
}]
}
GET /api/public/market-catalog
分类大集合(全部市场,含链上未上架)。symbol 已小写。不含 source / updated_by。
{
"markets": [{
"symbol": "xyz:tsla",
"category_id": 12,
"category_code": "stocks"
}]
}
管理接口(Bearer 鉴权)
| 路由 | 方法 | 说明 |
|---|---|---|
/api/market-categories | GET | 全部分类(含停用;字段同公开接口) |
/api/market-categories | POST | 创建分类:{ parent_id?, code, labels, sort_order?, enabled? } → { ok, category } |
/api/market-categories/{id} | PUT | 更新分类(parent_id: null 显式清空为总分类) |
/api/market-categories/{id} | DELETE | 删除分类(有子分类或被市场使用时返回 409) |
/api/market-catalog | GET | 全量目录(含 source / updated_by) |
/api/market-categories/{id}/markets | POST | 按 symbol 添加/移除市场:{ add?: string[], remove?: string[] }(忽略大小写,添加为 manual);空 add 且空 remove → 400 |
/api/market-categories/cleanup | POST | 一键整理:白名单子分类(股票/外汇/大宗商品/指数等)归位到对应总分类;删除已移除分类(如 preipo)及其目录市场 |
约束:两级层级(总分类不能挂在子分类下);停用分类不可被市场关联; 删除分类前需先解除子分类与市场关联。
从 Hyperliquid 同步(点一次执行一次,无定时)
以 Hyperliquid perpCategories 为源做全量覆盖同步,仅在管理员点击时执行。
同步不依赖链上市场,每个同步的分类会把 HL 中该分类下的全部市场
(含链上未上架)写入 market_catalog;manual 条目不会被覆盖。
POST /api/market-categories/sync/preview
预览(不写库),返回:
| 字段 | 类型 | 说明 |
|---|---|---|
candidates | SyncCandidate[] | HL 有、本地没有的新分类 |
updates | SyncCandidate[] | 已有 source=hl 分类,本次会被覆盖 |
SyncCandidate:{ code, labels, parent_code, hl_value, market_count },
其中 market_count 为该分类下 HL 市场数量(含未上架)。
POST /api/market-categories/sync/import
Body:{ codes: string[] }(无 apply_to_markets;该字段已删除)。
语义:
- 按 code 归一化(
fx→forex、Layer 1→layer1);preipo跳过不导入; - 不存在的分类 → 新建(
source=hl);已存在的source=hl分类 → 覆盖名称/挂载/启用; 手工(source=manual)与内置种子(source=seed)分类受保护不覆盖; - 未知 HL 分类挂到自动创建的兜底总分类
misc下(管理员可再调整); - 每个同步的分类:删除该分类下
source='hl'的目录条目,写入 HL 当前全量市场 (统一小写、去重);manual条目保留。
返回 { added, updated, skipped, markets_synced }。
遗留路由(market_classifications)
下列路由仍注册、仍可调用,但不是交易界面的分类来源。
zepto-admin 从 Hyperliquid 创建市场时仍可能写旧表(best-effort)。
| 路由 | 方法 | 说明 |
|---|---|---|
/api/public/market-classifications | GET | 旧 market_id → 分类列表(无鉴权) |
/api/market-classifications | GET | 同上,含 updated_at_ms / updated_by(Bearer) |
/api/market-classifications/{marketId} | PUT | body { symbol, category_id },按 market_id upsert |
/api/market-classifications/{marketId} | DELETE | 按 market_id 删除旧关联 |
新接入只读 /api/public/market-catalog + /api/public/market-categories。
前端匹配规则
前端将链上 getMarkets 与公开目录按 symbol 匹配(忽略大小写),解析顺序:
- 覆盖表(特殊命名,如
kBONK-USD→kBONK); - 去
{dex}:前缀(HIP-3,如xyz:TSLA→TSLA); - 交易对 base(如
BTC-USDT→BTC)。
未匹配的市场保持「未分类」(交易界面回退旧的 symbol 前缀启发式分组); Native 市场由管理员在后台把其 symbol 添加到对应分类即可自动匹配。
交易界面(zepto-web)的市场选择器模仿 Hyperliquid 做两排分类: 第一排为总分类(全部 / 永续 + 总分类),选中总分类后出现第二排子分类 (全部 + 该总分类下的子分类),点子分类仅显示该子分类的市场。