feat: 完成B端认证系统和商户管理模块测试补全
主要变更: - 新增B端认证系统(后台+H5):登录、登出、Token刷新、密码修改 - 完善商户管理和商户账号管理功能 - 补全单元测试(ShopService: 72.5%, ShopAccountService: 79.8%) - 新增集成测试(商户管理+商户账号管理) - 归档OpenSpec提案(add-shop-account-management, implement-b-end-auth-system) - 完善文档(使用指南、API文档、认证架构说明) 测试统计: - 13个测试套件,37个测试用例,100%通过率 - 平均覆盖率76.2%,达标 OpenSpec验证:通过(strict模式)
This commit is contained in:
143
openspec/specs/b-end-auth/spec.md
Normal file
143
openspec/specs/b-end-auth/spec.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# b-end-auth Specification
|
||||
|
||||
## Purpose
|
||||
TBD - created by archiving change implement-b-end-auth-system. Update Purpose after archive.
|
||||
## Requirements
|
||||
### Requirement: B 端用户登录
|
||||
系统 SHALL 支持后台管理员、代理商和企业用户通过用户名/手机号和密码进行登录认证。
|
||||
|
||||
#### Scenario: 后台管理员登录成功
|
||||
- **WHEN** 用户访问 `POST /api/admin/login` 并提供有效的用户名和密码
|
||||
- **THEN** 系统验证凭据,生成 access token 和 refresh token,返回 token 和用户信息
|
||||
|
||||
#### Scenario: H5 端代理商登录成功
|
||||
- **WHEN** 用户访问 `POST /api/h5/login` 并提供有效的用户名和密码
|
||||
- **THEN** 系统验证凭据,生成 access token 和 refresh token,返回 token 和用户信息
|
||||
|
||||
#### Scenario: 登录失败 - 凭据无效
|
||||
- **WHEN** 用户提供错误的用户名或密码
|
||||
- **THEN** 系统返回 401 错误,错误码 1040,消息"用户名或密码错误"
|
||||
|
||||
#### Scenario: 登录失败 - 账号已禁用
|
||||
- **WHEN** 用户账号状态为禁用
|
||||
- **THEN** 系统返回 403 错误,错误码 1041,消息"账号已被锁定或禁用"
|
||||
|
||||
### Requirement: Token 管理
|
||||
系统 SHALL 使用 Redis 存储的双令牌机制管理用户会话,包括 access token(24小时有效)和 refresh token(7天有效)。
|
||||
|
||||
#### Scenario: 生成 Token 对
|
||||
- **WHEN** 用户登录成功
|
||||
- **THEN** 系统生成随机 UUID 作为 access token 和 refresh token,将用户信息(UserID、UserType、ShopID、EnterpriseID、Username、Device、IP、LoginTime)存储到 Redis,设置相应的 TTL
|
||||
|
||||
#### Scenario: 验证 Access Token
|
||||
- **WHEN** 请求受保护的 API 端点时,在 Authorization 头中提供 Bearer token
|
||||
- **THEN** 系统从 Redis 查询 token 对应的用户信息,验证 token 有效性,将用户信息注入到请求上下文
|
||||
|
||||
#### Scenario: Token 过期
|
||||
- **WHEN** access token 超过 24 小时未使用
|
||||
- **THEN** Redis 自动删除 token,后续验证返回 401 错误,错误码 1002,消息"令牌无效或已过期"
|
||||
|
||||
#### Scenario: Token 不存在
|
||||
- **WHEN** 提供的 token 在 Redis 中不存在
|
||||
- **THEN** 系统返回 401 错误,错误码 1002,消息"令牌无效或已过期"
|
||||
|
||||
### Requirement: 用户登出
|
||||
系统 SHALL 支持用户主动登出,撤销当前使用的 access token 和 refresh token。
|
||||
|
||||
#### Scenario: 成功登出
|
||||
- **WHEN** 用户访问 `POST /api/admin/logout` 或 `POST /api/h5/logout` 并提供有效的 token
|
||||
- **THEN** 系统从 Redis 删除对应的 access token 和 refresh token,并从用户 token 列表中移除,返回成功响应
|
||||
|
||||
#### Scenario: 已登出的 Token 无法再使用
|
||||
- **WHEN** 用户登出后,使用相同的 token 访问受保护端点
|
||||
- **THEN** 系统返回 401 错误,消息"令牌无效或已过期"
|
||||
|
||||
### Requirement: Token 刷新
|
||||
系统 SHALL 支持使用 refresh token 刷新 access token,延长会话有效期而无需重新登录。
|
||||
|
||||
#### Scenario: 成功刷新 Access Token
|
||||
- **WHEN** 用户访问 `POST /api/admin/refresh-token` 或 `POST /api/h5/refresh-token` 并提供有效的 refresh token
|
||||
- **THEN** 系统验证 refresh token,生成新的 access token(保持 refresh token 不变),返回新的 access token
|
||||
|
||||
#### Scenario: Refresh Token 无效
|
||||
- **WHEN** 提供的 refresh token 不存在或已过期
|
||||
- **THEN** 系统返回 401 错误,错误码 1002,消息"刷新令牌无效或已过期"
|
||||
|
||||
### Requirement: 获取当前用户信息
|
||||
系统 SHALL 支持已认证用户查询当前用户的详细信息和权限列表。
|
||||
|
||||
#### Scenario: 成功获取用户信息
|
||||
- **WHEN** 用户访问 `GET /api/admin/me` 或 `GET /api/h5/me` 并提供有效的 access token
|
||||
- **THEN** 系统从 token 解析用户 ID,查询数据库获取用户信息(ID、用户名、手机号、用户类型、店铺 ID、企业 ID)和权限列表,返回完整的用户信息
|
||||
|
||||
#### Scenario: Token 无效时无法获取用户信息
|
||||
- **WHEN** 提供无效或过期的 token
|
||||
- **THEN** 系统在中间件层拦截,返回 401 错误
|
||||
|
||||
### Requirement: 修改密码
|
||||
系统 SHALL 支持已认证用户修改自己的密码,并在密码修改后撤销所有旧 token。
|
||||
|
||||
#### Scenario: 成功修改密码
|
||||
- **WHEN** 用户访问 `PUT /api/admin/password` 或 `PUT /api/h5/password`,提供旧密码和新密码
|
||||
- **THEN** 系统验证旧密码,使用 bcrypt 哈希新密码并更新数据库,撤销用户所有 token(包括当前使用的 token),返回成功响应
|
||||
|
||||
#### Scenario: 旧密码错误
|
||||
- **WHEN** 提供的旧密码不正确
|
||||
- **THEN** 系统返回 400 错误,错误码 1043,消息"旧密码不正确"
|
||||
|
||||
#### Scenario: 密码修改后旧 Token 失效
|
||||
- **WHEN** 用户修改密码后,使用旧的 token 访问任何端点
|
||||
- **THEN** 系统返回 401 错误,消息"令牌无效或已过期"
|
||||
|
||||
### Requirement: 多端认证隔离
|
||||
系统 SHALL 通过认证中间件实现后台和 H5 端的用户类型隔离,确保不同端点只能被对应用户类型访问。
|
||||
|
||||
#### Scenario: 后台端点用户类型验证
|
||||
- **WHEN** 用户访问 `/api/admin/*` 端点
|
||||
- **THEN** 认证中间件验证用户类型必须为 SuperAdmin(1)、Platform(2) 或 Agent(3),否则返回 403 错误
|
||||
|
||||
#### Scenario: H5 端点用户类型验证
|
||||
- **WHEN** 用户访问 `/api/h5/*` 端点
|
||||
- **THEN** 认证中间件验证用户类型必须为 Agent(3) 或 Enterprise(4),否则返回 403 错误
|
||||
|
||||
#### Scenario: 公开端点无需认证
|
||||
- **WHEN** 用户访问 `/api/admin/login`、`/api/admin/refresh-token`、`/api/h5/login` 或 `/api/h5/refresh-token`
|
||||
- **THEN** 中间件跳过认证检查,允许匿名访问
|
||||
|
||||
### Requirement: Token 批量撤销
|
||||
系统 SHALL 支持撤销指定用户的所有 token,用于密码修改或账号禁用场景。
|
||||
|
||||
#### Scenario: 撤销用户所有 Token
|
||||
- **WHEN** 调用 `RevokeAllUserTokens(userID)` 方法(内部使用,密码修改时触发)
|
||||
- **THEN** 系统从 Redis 查询用户 token 列表(`auth:user:{userID}:tokens`),删除所有 access token 和 refresh token 及其对应的用户信息,清空 token 列表
|
||||
|
||||
#### Scenario: 撤销不存在用户的 Token
|
||||
- **WHEN** 调用 `RevokeAllUserTokens` 但用户没有任何活跃 token
|
||||
- **THEN** 系统不报错,直接返回成功
|
||||
|
||||
### Requirement: 并发安全
|
||||
系统 SHALL 保证 Token 管理器在高并发场景下的线程安全和数据一致性。
|
||||
|
||||
#### Scenario: 并发生成 Token
|
||||
- **WHEN** 同一用户在不同设备上同时登录(多个并发请求)
|
||||
- **THEN** 每个请求生成独立的 token 对,所有 token 都有效,互不干扰
|
||||
|
||||
#### Scenario: 并发撤销 Token
|
||||
- **WHEN** 多个请求同时撤销同一 token
|
||||
- **THEN** Redis 操作原子性保证只有一个请求成功删除,其他请求不报错
|
||||
|
||||
### Requirement: 性能要求
|
||||
系统 SHALL 满足以下性能指标。
|
||||
|
||||
#### Scenario: 登录响应时间
|
||||
- **WHEN** 用户发起登录请求
|
||||
- **THEN** API P95 响应时间 < 200ms,P99 响应时间 < 500ms
|
||||
|
||||
#### Scenario: Token 验证响应时间
|
||||
- **WHEN** 请求受保护端点触发 token 验证
|
||||
- **THEN** Redis 查询时间 < 50ms
|
||||
|
||||
#### Scenario: Token 生成唯一性
|
||||
- **WHEN** 系统生成 token
|
||||
- **THEN** 使用 UUID v4 保证全局唯一性,碰撞概率 < 10^-15
|
||||
|
||||
177
openspec/specs/shop-account-management/spec.md
Normal file
177
openspec/specs/shop-account-management/spec.md
Normal file
@@ -0,0 +1,177 @@
|
||||
# shop-account-management Specification
|
||||
|
||||
## Purpose
|
||||
TBD - created by archiving change add-shop-account-management. Update Purpose after archive.
|
||||
## Requirements
|
||||
### Requirement: 代理商账号分页列表查询
|
||||
|
||||
系统 SHALL 提供代理商账号分页列表查询功能,支持按店铺ID和账号名称过滤(均为可选条件),返回账号基本信息。
|
||||
|
||||
#### Scenario: 查询指定店铺的账号列表
|
||||
|
||||
- **WHEN** 用户传入店铺ID查询参数(不传账号名称)
|
||||
- **THEN** 返回该店铺的所有账号(user_type=3 且 shop_id=指定店铺ID)
|
||||
- **AND** 包含分页信息(总数、当前页、每页数量)
|
||||
- **AND** 每条记录包含:账号名称(username)、手机号、创建时间
|
||||
|
||||
#### Scenario: 按账号名称模糊查询
|
||||
|
||||
- **WHEN** 用户传入账号名称查询参数(不传店铺ID)
|
||||
- **THEN** 返回账号名称包含该关键字的所有代理商账号(user_type=3)
|
||||
- **AND** 使用 LIKE 模糊匹配
|
||||
- **AND** 支持分页
|
||||
|
||||
#### Scenario: 组合条件查询
|
||||
|
||||
- **WHEN** 用户同时传入店铺ID和账号名称查询参数
|
||||
- **THEN** 返回同时满足两个条件的账号
|
||||
- **AND** 使用 AND 逻辑组合条件
|
||||
- **AND** shop_id = 指定店铺ID AND username LIKE '%关键字%'
|
||||
|
||||
#### Scenario: 查询所有代理商账号(无过滤条件)
|
||||
|
||||
- **WHEN** 用户不传任何查询条件(店铺ID和账号名称都为空)
|
||||
- **AND** 当前用户是平台管理员
|
||||
- **THEN** 返回所有代理商账号(user_type=3)
|
||||
- **AND** 支持分页
|
||||
|
||||
#### Scenario: 数据权限过滤
|
||||
|
||||
- **WHEN** 代理账号访问账号列表(无论是否传查询条件)
|
||||
- **THEN** 通过 GORM Callback 自动过滤
|
||||
- **AND** 只返回当前店铺及下级店铺的账号
|
||||
- **AND** 在数据权限过滤的基础上,再应用用户传入的查询条件
|
||||
|
||||
#### Scenario: 空结果处理
|
||||
|
||||
- **WHEN** 查询条件无匹配结果
|
||||
- **THEN** 返回空数组
|
||||
- **AND** 总数为 0
|
||||
- **AND** HTTP 状态码 200
|
||||
|
||||
### Requirement: 代理商账号新增
|
||||
|
||||
系统 SHALL 提供代理商账号新增功能,支持创建绑定到指定店铺的代理账号。
|
||||
|
||||
#### Scenario: 新增代理商账号
|
||||
|
||||
- **WHEN** 用户提交新增账号请求
|
||||
- **AND** 提供账号名称、手机号、登录密码、关联店铺ID
|
||||
- **THEN** 验证店铺存在且未删除
|
||||
- **AND** 验证手机号唯一性(未被使用)
|
||||
- **AND** 验证账号名称唯一性(未被使用)
|
||||
- **AND** 密码使用 bcrypt 加密
|
||||
- **AND** 创建账号(user_type=3,shop_id=指定店铺ID)
|
||||
- **AND** 状态默认为启用(status=1)
|
||||
- **AND** 返回新创建的账号信息(不包含密码)
|
||||
|
||||
#### Scenario: 手机号已存在
|
||||
|
||||
- **WHEN** 用户提交的手机号已被使用
|
||||
- **THEN** 返回错误码 2002(手机号已存在)
|
||||
- **AND** HTTP 状态码 400
|
||||
|
||||
#### Scenario: 账号名称已存在
|
||||
|
||||
- **WHEN** 用户提交的账号名称已被使用
|
||||
- **THEN** 返回错误码 2001(用户名已存在)
|
||||
- **AND** HTTP 状态码 400
|
||||
|
||||
#### Scenario: 关联店铺不存在
|
||||
|
||||
- **WHEN** 用户提交的店铺ID不存在或已删除
|
||||
- **THEN** 返回错误码 2103(店铺不存在)
|
||||
- **AND** HTTP 状态码 404
|
||||
|
||||
### Requirement: 代理商账号编辑
|
||||
|
||||
系统 SHALL 提供代理商账号编辑功能,支持更新账号名称,但不允许修改密码和手机号。
|
||||
|
||||
#### Scenario: 更新账号名称
|
||||
|
||||
- **WHEN** 用户提交编辑账号请求(更新账号名称)
|
||||
- **THEN** 验证账号存在且未删除
|
||||
- **AND** 验证新账号名称唯一性(如果修改)
|
||||
- **AND** 更新账号名称
|
||||
- **AND** 更新 updater 字段为当前用户ID
|
||||
- **AND** 返回更新后的账号信息
|
||||
|
||||
#### Scenario: 不允许修改手机号
|
||||
|
||||
- **WHEN** 编辑请求中包含手机号字段
|
||||
- **THEN** 忽略该字段
|
||||
- **AND** 不更新手机号
|
||||
|
||||
#### Scenario: 不允许修改密码
|
||||
|
||||
- **WHEN** 编辑请求中包含密码字段
|
||||
- **THEN** 忽略该字段
|
||||
- **AND** 不更新密码
|
||||
- **AND** 密码修改需通过专用接口
|
||||
|
||||
#### Scenario: 编辑不存在的账号
|
||||
|
||||
- **WHEN** 用户尝试编辑不存在或已删除的账号
|
||||
- **THEN** 返回错误码 2101(账号不存在)
|
||||
- **AND** HTTP 状态码 404
|
||||
|
||||
### Requirement: 代理商账号密码修改
|
||||
|
||||
系统 SHALL 提供代理商账号密码修改功能,支持管理员重置密码,不需要验证旧密码。
|
||||
|
||||
#### Scenario: 管理员重置密码
|
||||
|
||||
- **WHEN** 管理员提交密码修改请求
|
||||
- **AND** 提供新密码
|
||||
- **THEN** 验证账号存在且未删除
|
||||
- **AND** 验证新密码格式(8-32位)
|
||||
- **AND** 使用 bcrypt 加密新密码
|
||||
- **AND** 更新账号密码
|
||||
- **AND** 更新 updater 字段为当前用户ID
|
||||
- **AND** 返回成功响应
|
||||
|
||||
#### Scenario: 新密码格式验证
|
||||
|
||||
- **WHEN** 用户提交的新密码不符合要求(长度不在8-32位)
|
||||
- **THEN** 返回参数验证错误
|
||||
- **AND** HTTP 状态码 400
|
||||
|
||||
#### Scenario: 修改不存在账号的密码
|
||||
|
||||
- **WHEN** 用户尝试修改不存在或已删除账号的密码
|
||||
- **THEN** 返回错误码 2101(账号不存在)
|
||||
- **AND** HTTP 状态码 404
|
||||
|
||||
### Requirement: 代理商账号启用/禁用
|
||||
|
||||
系统 SHALL 提供代理商账号启用/禁用功能,支持快速切换账号状态。
|
||||
|
||||
#### Scenario: 启用账号
|
||||
|
||||
- **WHEN** 管理员提交启用账号请求
|
||||
- **THEN** 验证账号存在且未删除
|
||||
- **AND** 更新账号状态为 1(启用)
|
||||
- **AND** 更新 updater 字段为当前用户ID
|
||||
- **AND** 返回成功响应
|
||||
|
||||
#### Scenario: 禁用账号
|
||||
|
||||
- **WHEN** 管理员提交禁用账号请求
|
||||
- **THEN** 验证账号存在且未删除
|
||||
- **AND** 更新账号状态为 0(禁用)
|
||||
- **AND** 更新 updater 字段为当前用户ID
|
||||
- **AND** 返回成功响应
|
||||
|
||||
#### Scenario: 禁用后的账号无法登录
|
||||
|
||||
- **WHEN** 账号状态为禁用(status=0)
|
||||
- **AND** 用户尝试使用该账号登录
|
||||
- **THEN** 登录失败
|
||||
- **AND** 返回账号已禁用错误
|
||||
|
||||
#### Scenario: 操作不存在的账号
|
||||
|
||||
- **WHEN** 用户尝试启用/禁用不存在或已删除的账号
|
||||
- **THEN** 返回错误码 2101(账号不存在)
|
||||
- **AND** HTTP 状态码 404
|
||||
|
||||
136
openspec/specs/shop-management/spec.md
Normal file
136
openspec/specs/shop-management/spec.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# shop-management Specification
|
||||
|
||||
## Purpose
|
||||
TBD - created by archiving change add-shop-account-management. Update Purpose after archive.
|
||||
## Requirements
|
||||
### Requirement: 店铺分页列表查询
|
||||
|
||||
系统 SHALL 提供店铺分页列表查询功能,支持按店铺名称模糊查询,返回详细的店铺信息。
|
||||
|
||||
#### Scenario: 查询所有店铺(平台管理员)
|
||||
|
||||
- **WHEN** 平台管理员访问店铺列表(不传店铺名称过滤条件)
|
||||
- **THEN** 返回所有未删除的店铺列表
|
||||
- **AND** 包含分页信息(总数、当前页、每页数量)
|
||||
- **AND** 每条记录包含:店铺名称、店铺编号、上级店铺名称、层级、联系人、联系电话、省市区(合并字段)、创建时间、创建人
|
||||
|
||||
#### Scenario: 按店铺名称模糊查询
|
||||
|
||||
- **WHEN** 用户传入店铺名称查询参数(如"华东")
|
||||
- **THEN** 返回店铺名称包含"华东"的所有店铺
|
||||
- **AND** 使用 LIKE 模糊匹配
|
||||
- **AND** 支持分页
|
||||
|
||||
#### Scenario: 代理账号查询(数据权限过滤)
|
||||
|
||||
- **WHEN** 代理账号访问店铺列表
|
||||
- **THEN** 只返回当前店铺及所有下级店铺
|
||||
- **AND** 通过 GORM Callback 自动应用过滤条件
|
||||
- **AND** 支持分页
|
||||
|
||||
#### Scenario: 空结果处理
|
||||
|
||||
- **WHEN** 查询条件无匹配结果
|
||||
- **THEN** 返回空数组
|
||||
- **AND** 总数为 0
|
||||
- **AND** HTTP 状态码 200
|
||||
|
||||
### Requirement: 店铺新增
|
||||
|
||||
系统 SHALL 提供店铺新增功能,支持完整的店铺信息录入,并自动创建店铺初始账号。
|
||||
|
||||
#### Scenario: 新增一级代理店铺
|
||||
|
||||
- **WHEN** 用户提交新增店铺请求(未填写上级店铺)
|
||||
- **AND** 提供店铺名称、店铺编号、联系电话、初始密码
|
||||
- **THEN** 创建店铺记录,层级设为 1
|
||||
- **AND** 自动创建初始账号(用户类型=3,shop_id=新店铺ID)
|
||||
- **AND** 账号手机号和登录账号使用联系电话
|
||||
- **AND** 密码使用 bcrypt 加密
|
||||
- **AND** 返回新创建的店铺信息
|
||||
|
||||
#### Scenario: 新增下级代理店铺
|
||||
|
||||
- **WHEN** 用户提交新增店铺请求(填写上级店铺ID)
|
||||
- **THEN** 验证上级店铺存在且未删除
|
||||
- **AND** 计算层级(上级层级 + 1)
|
||||
- **AND** 验证层级不超过 7
|
||||
- **AND** 创建店铺记录
|
||||
- **AND** 自动创建初始账号
|
||||
|
||||
#### Scenario: 店铺编号唯一性校验
|
||||
|
||||
- **WHEN** 用户提交的店铺编号已存在(未删除记录)
|
||||
- **THEN** 返回错误码 2101(店铺编号已存在)
|
||||
- **AND** HTTP 状态码 400
|
||||
- **AND** 不创建店铺记录
|
||||
|
||||
#### Scenario: 层级超过限制
|
||||
|
||||
- **WHEN** 用户尝试创建第 8 级店铺
|
||||
- **THEN** 返回错误码 2102(超过最大层级限制)
|
||||
- **AND** HTTP 状态码 400
|
||||
|
||||
#### Scenario: 联系电话必填校验
|
||||
|
||||
- **WHEN** 用户提交新增请求时未填写联系电话
|
||||
- **THEN** 返回参数验证错误
|
||||
- **AND** HTTP 状态码 400
|
||||
|
||||
### Requirement: 店铺编辑
|
||||
|
||||
系统 SHALL 提供店铺编辑功能,支持更新店铺信息,但不允许修改密码和登录账号。
|
||||
|
||||
#### Scenario: 更新店铺基本信息
|
||||
|
||||
- **WHEN** 用户提交编辑店铺请求(更新店铺名称、联系人等)
|
||||
- **THEN** 验证店铺存在且未删除
|
||||
- **AND** 更新允许编辑的字段
|
||||
- **AND** 更新 updater 字段为当前用户ID
|
||||
- **AND** 返回更新后的店铺信息
|
||||
|
||||
#### Scenario: 不允许修改店铺编号
|
||||
|
||||
- **WHEN** 编辑请求中包含店铺编号字段
|
||||
- **THEN** 忽略该字段
|
||||
- **AND** 不更新店铺编号
|
||||
|
||||
#### Scenario: 不允许修改上级店铺
|
||||
|
||||
- **WHEN** 编辑请求中包含上级店铺字段
|
||||
- **THEN** 忽略该字段
|
||||
- **AND** 不更新上级店铺和层级
|
||||
|
||||
#### Scenario: 编辑不存在的店铺
|
||||
|
||||
- **WHEN** 用户尝试编辑不存在或已删除的店铺
|
||||
- **THEN** 返回错误码 2103(店铺不存在)
|
||||
- **AND** HTTP 状态码 404
|
||||
|
||||
### Requirement: 店铺删除
|
||||
|
||||
系统 SHALL 提供店铺删除功能,执行软删除并同步禁用店铺下的所有账号。
|
||||
|
||||
#### Scenario: 删除店铺并禁用账号
|
||||
|
||||
- **WHEN** 用户提交删除店铺请求
|
||||
- **THEN** 验证店铺存在且未删除
|
||||
- **AND** 执行软删除(设置 deleted_at)
|
||||
- **AND** 查询该店铺的所有账号(shop_id = 店铺ID)
|
||||
- **AND** 批量更新所有账号状态为 0(禁用)
|
||||
- **AND** 使用事务保证原子性
|
||||
- **AND** 返回成功响应
|
||||
|
||||
#### Scenario: 删除不存在的店铺
|
||||
|
||||
- **WHEN** 用户尝试删除不存在或已删除的店铺
|
||||
- **THEN** 返回错误码 2103(店铺不存在)
|
||||
- **AND** HTTP 状态码 404
|
||||
|
||||
#### Scenario: 删除有下级店铺的店铺
|
||||
|
||||
- **WHEN** 用户尝试删除有下级店铺的店铺
|
||||
- **THEN** 返回错误码 2104(存在下级店铺,无法删除)
|
||||
- **AND** HTTP 状态码 400
|
||||
- **AND** 不执行删除操作
|
||||
|
||||
Reference in New Issue
Block a user