90 lines
5.2 KiB
Markdown
90 lines
5.2 KiB
Markdown
# UR#60 店铺联系电话精确查询功能总结
|
||
|
||
## 本次交付范围
|
||
|
||
本次完成当前后端仓库内可执行的三个纵向切片:
|
||
|
||
- Ticket 01:修复 `GET /api/admin/shops` 的完整查询参数校验与默认分页一致性。
|
||
- Ticket 02:交付联系电话精确查询、数据库部分索引、OpenAPI 和真实 HTTP 集成验证。
|
||
- Ticket 03:禁止企业账号访问店铺列表、创建、更新、删除和联级查询五个核心店铺管理入口。
|
||
|
||
Ticket 02 中的后台页面筛选区、浏览器联调和人工验收需要前端仓库。本仓库未包含前端源码,因此后端交付完成后该票转为 `ready-for-human`,仅等待跨仓前端实施与人工验收。
|
||
|
||
## 联系电话查询契约
|
||
|
||
店铺列表新增可选查询参数 `contact_phone`:
|
||
|
||
- 非空值必须完整匹配 `^[0-9]{11}$`,只接受 11 位 ASCII 数字。
|
||
- 空字符串和未传参数均不启用电话筛选。
|
||
- 不 trim,不接受空格、全角数字、`+86`、连字符、字母或长度不符的输入。
|
||
- Store 使用 `contact_phone = ?` 等值条件,与店铺名称、编号、上级店铺、层级和状态按 AND 组合。
|
||
- 相同电话允许返回多个可见店铺;无匹配项返回成功空分页。
|
||
- 保持 `created_at DESC`、软删除排除和代理店铺层级数据范围。
|
||
|
||
前端接入时应新增“联系电话”输入框。非法值应在前端阻止请求并显示中文提示;合法值以 `contact_phone` 与其他筛选参数一并提交;空值不提交该参数。加载、空态和失败重试沿用现有列表交互,失败后不得将旧结果伪装成新查询结果。
|
||
|
||
## 参数与分页契约
|
||
|
||
店铺列表在查询参数解析后执行完整 DTO 校验。解析失败或任一字段违反约束时,客户端统一收到:
|
||
|
||
```json
|
||
{
|
||
"code": 1001,
|
||
"msg": "参数验证失败",
|
||
"data": null,
|
||
"timestamp": "RFC3339 时间"
|
||
}
|
||
```
|
||
|
||
具体解析或 Validator 错误只写入服务端日志,不返回给客户端。未传分页参数时,请求在进入 Service 前归一化为 `page=1`、`page_size=20`,数据库查询参数与响应中的 `page`、`size` 保持一致;显式合法分页值原样生效。
|
||
|
||
## 核心店铺管理权限
|
||
|
||
企业账号访问以下入口统一收到 HTTP 403、`code=1005`、`msg=无权限访问店铺管理功能`:
|
||
|
||
- `GET /api/admin/shops`
|
||
- `POST /api/admin/shops`
|
||
- `PUT /api/admin/shops/:id`
|
||
- `DELETE /api/admin/shops/:id`
|
||
- `GET /api/admin/shops/cascade`
|
||
|
||
限制通过逐路由 Handler 包装实现,不使用 `/shops` 前缀组中间件,避免误伤店铺角色、资金、佣金、提现和钱包流水等独立路由。超级管理员、平台账号和代理账号保留原有核心列表访问能力,代理账号继续使用既有店铺及下级范围过滤。
|
||
|
||
## 架构与迁移边界
|
||
|
||
实现继续沿用现有 `Handler → Service → Store → GORM/DTO` 读取链路,只修改请求校验、筛选条件、分页归一化和路由权限边界。未迁移店铺模块到 DDD 或 Query 目录,未修改店铺创建/更新电话规则,也未新增缓存、审计业务日志、幂等控制或异步任务。
|
||
|
||
数据库迁移 `000160_add_shop_contact_phone_index` 创建非唯一部分 B-tree 索引 `idx_shop_contact_phone`:
|
||
|
||
```sql
|
||
CREATE INDEX idx_shop_contact_phone
|
||
ON tb_shop USING btree (contact_phone)
|
||
WHERE deleted_at IS NULL;
|
||
```
|
||
|
||
回滚只删除该索引,不清洗、回填或删除历史联系电话,也不改变业务数据。
|
||
|
||
## 验证证据
|
||
|
||
HTTP 集成测试在进程内启动 Fiber App,通过真实 Redis Token 状态、后台认证中间件、真实 PostgreSQL、Handler、Service、Store、GORM 和统一错误处理验证:
|
||
|
||
- 非法分页、层级、状态、超长名称或编号及解析失败均返回统一参数错误。
|
||
- 默认分页返回第 1 页、每页 20 条,显式合法分页不被重置。
|
||
- 未认证和无效 Token 保持既有认证错误契约。
|
||
- 企业账号在五个核心入口进入 Handler 前被拒绝。
|
||
- 超级管理员、平台账号和代理账号保留列表访问能力。
|
||
- 代理账号的联系电话结果只包含自身及全部下级店铺,范围外同电话店铺不可见。
|
||
- 店铺角色、资金概况、提现、佣金和钱包流水路由未被核心店铺管理权限包装误拦截。
|
||
- 联系电话精确匹配、重复号码、AND 组合、空参数、非法格式、空结果、显式分页、排序和软删除语义均通过真实 HTTP 验证。
|
||
- 非法联系电话在执行店铺查询前被拒绝;数据库查询失败返回脱敏的 HTTP 500。
|
||
- 索引已在开发库完成 `160 up → 159 down → 160 up` 演练,并通过 PostgreSQL 系统目录确认 B-tree、非唯一、列和 `deleted_at IS NULL` 条件。
|
||
- OpenAPI 由 `go run ./cmd/gendocs` 生成并核验 `contact_phone`、正则、精确查询、AND/空值和权限说明。
|
||
|
||
测试使用唯一 Redis Token 并在结束后清理,不打印 `.env.local` 中的数据库、Redis 或 JWT 敏感配置。
|
||
|
||
## 剩余人工交付
|
||
|
||
- 在后台前端仓库实现联系电话输入、校验、查询、清空、加载、空态和失败重试。
|
||
- 使用浏览器网络面板与真实接口共同核验请求参数和响应。
|
||
- 完成超级管理员、平台、代理和企业四类账号人工验收后更新需求状态。
|