Appearance
city —— 错误码矩阵(40431xxx)
完整 action 字段表见 city 概览。 错误码权威注册:
docs/ai-contexts/error-code-registry.md。 HTTP 状态映射:src/app/city/domain/exceptions.py::cz_city_error_to_http_status。
5 个错误码全表
| code | 名称 | HTTP | 触发场景 | 前端处置 |
|---|---|---|---|---|
40431001 | PARAM_INVALID | 400 | 双模入参并存 / 都缺 / adcode 格式非 6 位 / level 越界({1,2,3} 之外)/ unknown action | toast「参数错误」 |
40431002 | CITY_NOT_FOUND | 404 | adcode 不在 seed(数据漂移) | toast「城市不存在」+ 排查 seed |
40431003 | USER_CITY_NOT_BOUND | 404 | 用户尚未绑定城市(新用户 / 清 cookie / 重装) | **跳引导页 / 选城市页,不当错误 |
40431004 | UNAUTHORIZED | 401 | 未登录 / token 无效 | 走 OAuth 登录页 |
40431005 | GPS_RESOLVE_FAILED | 400 | GPS 转码失败(v1 必触发 —— SeedNearestCityLocator 恒返 None) | 前端 v1 不做「自动定位」按钮 |
关键设计点
40431003 ≠ 错误
新用户进入 App 第一时间 getCurrentCity 必然拿到 40431003,这是业务常态,不是异常。前端必须按 code 判断 → 跳引导页,不弹错误 toast(不弹任何错误提示,体验上等同于「首次进入引导」)。
实现层:use case 内 raise CityError(USER_CITY_NOT_BOUND, ...) → handler 转 HTTP 404 → 前端按 status === 404 且 code === 40431003 走引导分支。
40431005 = v1 已知缺口
- 原因:seed
adcode_seed.json不含 geo 坐标 →SeedNearestCityLocator.cz_locate()永远返None - 影响:v1
selectCity {gps: ...}路径永远返 40431005 - 前端约束:选城市页不要做「自动定位」按钮(用户点了 = 必失败体验差)
- v2.1 修复:接逆地理服务(高德 district API + 生产 AMAP_KEY)→ 替换
SeedNearestCityLocator实现即可,端口契约不变
HTTP 状态码设计
| 类别 | HTTP | 错误码 |
|---|---|---|
| 入参校验失败 | 400 | 40431001 |
| GPS 转换失败 | 400 | 40431005 |
| 未登录 | 401 | 40431004 |
| 城市不存在 | 404 | 40431002 |
| 用户未绑(常态) | 404 | 40431003 |
40431003 给 404 而非 400 —— 体现「这个资源状态不存在(用户→城市关联)」,与 40431002(城市字典不存在)形成语义分层。
错误码段治理
- 段位:
40431xxx - 本段已用:5/1000 个(
001~005) - 预留:
006~999(同段内顺序分配,禁止跳号) - 守卫:
scripts/check_error_code_segments.py(CI 拦截未登记段) - 注册表:
docs/ai-contexts/error-code-registry.md
v1 实测(来自 test_city_actions.py)
text
40431001 → 双模入参并存 (selectCity {adcode,gps}) / adcode 非 6 位 / level=4
40431002 → adcode="999999"(不在 seed)
40431003 → 新用户 getCurrentCity(unbound binding)
40431004 → 无 Bearer token
40431005 → gps {lng,lat}(v1 必触发)后端 5 例全绿,回归 tests/integration/test_city_actions.py(20 例)。