diff --git "a/docs/02-\350\256\276\350\256\241\346\226\207\346\241\243/interface-tyh.md" "b/docs/02-\350\256\276\350\256\241\346\226\207\346\241\243/interface-tyh.md" index 8d3432bf53801b19e0c185822fe93b5f13189ccd..3b02c0ce7ec8f012aa5b2202fd06f65c11d3f62b 100644 --- "a/docs/02-\350\256\276\350\256\241\346\226\207\346\241\243/interface-tyh.md" +++ "b/docs/02-\350\256\276\350\256\241\346\226\207\346\241\243/interface-tyh.md" @@ -11,6 +11,7 @@ | 版本 | 日期 | 修改人 | 修改说明 | |---|---|---|---| | v0.1 | 2026-07-24 | 唐宇昊 | 建立 `A001`~`A023` 接口清单并补齐全部详细定义 | +| v0.2 | 2026-07-24 | 唐宇昊 | 补登 `A024` 记录浏览历史、`A025` 查询浏览记录开关;将 `A006`/`A007` 路径迁回 `/api/users/me` 资源域;按 F03 收紧 `A010`~`A014` 鉴权 Policy 为仅 `BuyerOnly` | ## 一、接口清单 @@ -20,16 +21,16 @@ | A002 | Identity | F02 | 登录 | POST | `/api/auth/login` | `Identity_Login` | `LoginRequest` | `LoginResponse` | 允许游客 | DB001 | 已定义 | | A003 | Identity | F02 | 退出当前令牌 | POST | `/api/auth/logout` | `Identity_Logout` | 无 | `LogoutResponse` | BuyerOnly / MerchantOnly / AdminOnly | DB001、DB004 | 已定义 | | A004 | Identity | F02 | 获取当前用户 | GET | `/api/auth/me` | `Identity_GetCurrentUser` | 无 | `CurrentUserResponse` | BuyerOnly / MerchantOnly / AdminOnly | DB001 | 已定义 | -| A005 | Identity | F02 | 刷新访问令牌 | POST | `/api/auth/refresh-token` | `Identity_RefreshToken` | `RefreshTokenRequest` | `LoginResponse` | 已认证用户 | DB001、DB004 | 已定义 | -| A006 | Identity | F03 | 修改手机号 | POST | `/api/auth/change-phone` | `Identity_ChangePhone` | `ChangePhoneRequest` | `CurrentUserResponse` | BuyerOnly / MerchantOnly | DB001、DB004 | 已定义 | -| A007 | Identity | F03 | 重置用户名 | POST | `/api/auth/reset-username` | `Identity_ResetUsername` | 无 | `ResetUsernameResponse` | BuyerOnly / MerchantOnly | DB001 | 已定义 | +| A005 | Identity | F02 | 刷新访问令牌 | POST | `/api/auth/refresh-token` | `Identity_RefreshToken` | `RefreshTokenRequest` | `LoginResponse` | BuyerOnly / MerchantOnly / AdminOnly | DB001、DB004 | 已定义 | +| A006 | Identity | F03 | 修改手机号 | POST | `/api/users/me/phone` | `Identity_ChangePhone` | `ChangePhoneRequest` | `CurrentUserResponse` | BuyerOnly / MerchantOnly | DB001、DB004 | 已定义 | +| A007 | Identity | F03 | 重置用户名 | POST | `/api/users/me/username/reset` | `Identity_ResetUsername` | 无 | `ResetUsernameResponse` | BuyerOnly / MerchantOnly | DB001 | 已定义 | | A008 | Identity | F03 | 获取本人资料 | GET | `/api/users/me` | `Identity_GetMyProfile` | 无 | `MyProfileResponse` | BuyerOnly / MerchantOnly | DB001 | 已定义 | | A009 | Identity | F03 | 修改本人资料 | PATCH | `/api/users/me` | `Identity_UpdateMyProfile` | `UpdateMyProfileRequest` | `MyProfileResponse` | BuyerOnly / MerchantOnly | DB001 | 已定义 | -| A010 | Identity | F03 | 我的地址列表 | GET | `/api/users/me/addresses` | `Identity_ListMyAddresses` | 无 | `AddressListResponse` | BuyerOnly / MerchantOnly | DB003 | 已定义 | -| A011 | Identity | F03 | 新增地址 | POST | `/api/users/me/addresses` | `Identity_CreateMyAddress` | `CreateAddressRequest` | `AddressResponse` | BuyerOnly / MerchantOnly | DB003 | 已定义 | -| A012 | Identity | F03 | 编辑地址 | PATCH | `/api/users/me/addresses/{addressId}` | `Identity_UpdateMyAddress` | `UpdateAddressRequest` | `AddressResponse` | BuyerOnly / MerchantOnly | DB003 | 已定义 | -| A013 | Identity | F03 | 删除地址 | DELETE | `/api/users/me/addresses/{addressId}` | `Identity_DeleteMyAddress` | 无 | 无(204) | BuyerOnly / MerchantOnly | DB003 | 已定义 | -| A014 | Identity | F03 | 设置默认地址 | POST | `/api/users/me/addresses/{addressId}/default` | `Identity_SetDefaultAddress` | 无 | `AddressResponse` | BuyerOnly / MerchantOnly | DB003 | 已定义 | +| A010 | Identity | F03 | 我的地址列表 | GET | `/api/users/me/addresses` | `Identity_ListMyAddresses` | 无 | `AddressListResponse` | BuyerOnly | DB003 | 已定义 | +| A011 | Identity | F03 | 新增地址 | POST | `/api/users/me/addresses` | `Identity_CreateMyAddress` | `CreateAddressRequest` | `AddressResponse` | BuyerOnly | DB003 | 已定义 | +| A012 | Identity | F03 | 编辑地址 | PATCH | `/api/users/me/addresses/{addressId}` | `Identity_UpdateMyAddress` | `UpdateAddressRequest` | `AddressResponse` | BuyerOnly | DB003 | 已定义 | +| A013 | Identity | F03 | 删除地址 | DELETE | `/api/users/me/addresses/{addressId}` | `Identity_DeleteMyAddress` | 无 | 无(204) | BuyerOnly | DB003 | 已定义 | +| A014 | Identity | F03 | 设置默认地址 | POST | `/api/users/me/addresses/{addressId}/default` | `Identity_SetDefaultAddress` | 无 | `AddressResponse` | BuyerOnly | DB003 | 已定义 | | A015 | Identity | F13 | 后台账号列表 | GET | `/api/admin/users` | `Identity_AdminListUsers` | 无(Query 分页/筛选) | `AdminUserListResponse` | AdminOnly | DB001 | 已定义 | | A016 | Identity | F13 | 禁用账号 | POST | `/api/admin/users/{userId}/disable` | `Identity_AdminDisableUser` | 无 | `AdminUserResponse` | AdminOnly | DB001、DB004 | 已定义 | | A017 | Identity | F13 | 启用账号 | POST | `/api/admin/users/{userId}/enable` | `Identity_AdminEnableUser` | 无 | `AdminUserResponse` | AdminOnly | DB001、DB004 | 已定义 | @@ -39,6 +40,8 @@ | A021 | Engagement | X02 | 浏览历史列表 | GET | `/api/browsing-history` | `Engagement_ListBrowsingHistory` | 无(Query 分页) | `BrowsingHistoryListResponse` | BuyerOnly | DB006 | 已定义 | | A022 | Engagement | X02 | 修改浏览记录开关 | PATCH | `/api/browsing-history/settings` | `Engagement_UpdateBrowsingHistorySetting` | `UpdateBrowsingHistorySettingRequest` | `BrowsingHistorySettingResponse` | BuyerOnly | DB006 | 已定义 | | A023 | Engagement | X02 | 清空浏览历史 | DELETE | `/api/browsing-history` | `Engagement_ClearBrowsingHistory` | 无 | 无(204) | BuyerOnly | DB006 | 已定义 | +| A024 | Engagement | X02 | 记录浏览历史 | POST | `/api/browsing-history/records` | `Engagement_RecordBrowsingHistory` | `RecordBrowsingHistoryRequest` | `BrowsingHistoryResponse` | BuyerOnly | DB006 | 已定义(v0.2 补登) | +| A025 | Engagement | X02 | 查询浏览记录开关 | GET | `/api/browsing-history/settings` | `Engagement_GetBrowsingHistorySetting` | 无 | `BrowsingHistorySettingResponse` | BuyerOnly | DB006 | 已定义(v0.2 补登) | 接口路径补充说明: @@ -374,7 +377,7 @@ RefreshTokenRequest { - 关联数据表:DB001、DB004 - 当前状态:已定义 - 用途:买家或商家修改本人手机号,提交后旧登录态全部失效并要求重新登录。 -- 方法与路径:`POST /api/auth/change-phone` +- 方法与路径:`POST /api/users/me/phone` - operationId:`Identity_ChangePhone` #### 请求 @@ -431,7 +434,7 @@ ChangePhoneRequest { - 关联数据表:DB001 - 当前状态:已定义 - 用途:用户自助重置一次用户名,重置次数用完即返回错误。 -- 方法与路径:`POST /api/auth/reset-username` +- 方法与路径:`POST /api/users/me/username/reset` - operationId:`Identity_ResetUsername` #### 请求 @@ -1304,4 +1307,124 @@ BrowsingHistorySettingResponse { #### 验证场景 - 清空本人浏览历史 → 204,后续列表为空。 -- 重复清空 → 204,幂等。 \ No newline at end of file +- 重复清空 → 204,幂等。 + +### A024 记录浏览历史 + +- 模块 / Tag:Engagement +- 需求编号:X02、M08、M08-FR04 +- 负责人:唐宇昊 +- 关联数据表:DB006 +- 当前状态:已定义 +- 用途:买家查看商品详情时,记录或更新其最近浏览时间;同一买家同一商品只保留一条记录,由 Catalog 模块的"商品详情查询成功"或前端埋点触发。 +- 方法与路径:`POST /api/browsing-history/records` +- operationId:`Engagement_RecordBrowsingHistory` + +#### 请求 + +- Header:`Authorization: Bearer `(必填,角色 Buyer) +- Body: + +```text +RecordBrowsingHistoryRequest { + productId: uuid // 必填 + viewedAt: string? // 可选,UTC ISO 8601;缺省时取服务端时间。客户端不得伪造未来时间 +} +``` + +- 校验规则:`productId` 必须存在且为已上架商品;字段缺失或格式错误返回字段级 ProblemDetails。 + +#### 成功响应 + +- HTTP 状态:`201 Created`(首次写入)或 `200 OK`(仅更新时间) +- 响应 Schema:`BrowsingHistoryResponse` + +```text +BrowsingHistoryResponse { + productId: uuid + viewedAt: string // UTC ISO 8601 + expired: boolean // 达到记录上限被清理时返回 true +} +``` + +#### 失败响应 + +| HTTP 状态 | 业务错误码 | 触发条件 | +|---:|---|---| +| 400 | `COMMON.VALIDATION_FAILED` | 字段缺失或格式错误 | +| 401 | `AUTH.UNAUTHENTICATED` | 已登录用户令牌无效 | +| 403 | `ENGAGEMENT.BROWSING_HISTORY_DISABLED` | 当前买家已关闭浏览记录开关 | +| 404 | `CATALOG.PRODUCT_NOT_FOUND` | 商品不存在或未上架 | +| 429 | `COMMON.RATE_LIMITED` | 同一买家短时间内高频记录浏览 | + +#### 业务规则与并发 + +- 若调用方为已登录买家且其浏览记录开关关闭,返回 `403 / ENGAGEMENT.BROWSING_HISTORY_DISABLED`,不写入任何记录。 +- 游客身份不持久化记录;本接口仅 BuyerOnly;前端在游客访问商品详情时引导登录,登录后再调用本接口。 +- 同一买家同一商品只保留一条记录;按 `(user_id, product_id)` 唯一约束更新 `viewed_at` 为最新值。 +- 默认单买家最多保留 200 条记录;超出时按 `viewed_at` 由小到大移除多余记录,并在响应 `expired=true` 告知。 +- 商品下架后续访问记录依旧写入;记录保留但不提供购买入口。 +- 触发方(Catalog 详情查询成功或前端埋点)不得伪造未来时间;服务端校正为 `min(viewed_at, NOW())`。 + +#### 缓存、事件或外部依赖 + +- 不缓存;不再写入 PostgreSQL。 +- 不发布集成事件。 + +#### 验证场景 + +- 已开启开关的买家查看新商品 → 201,记录新增。 +- 同一买家再次查看该商品 → 200,仅更新时间,原有过期记录不重复创建。 +- 关闭开关后调用 → 403 / `ENGAGEMENT.BROWSING_HISTORY_DISABLED`。 +- 商家或游客调用 → 403 / `AUTH.FORBIDDEN`。 +- 达到 200 条上限后再记录 → 200,且响应 `expired=true`。 +- 商品下架后调用 → 仍记录并返回 200。 + +### A025 查询浏览记录开关 + +- 模块 / Tag:Engagement +- 需求编号:X02、M08、M08-FR07 +- 负责人:唐宇昊 +- 关联数据表:DB006 +- 当前状态:已定义 +- 用途:买家查询本人"是否记录浏览历史"开关当前值,前端用于初始化控件状态。 +- 方法与路径:`GET /api/browsing-history/settings` +- operationId:`Engagement_GetBrowsingHistorySetting` + +#### 请求 + +- Header:`Authorization: Bearer `(必填,角色 Buyer) + +#### 成功响应 + +- HTTP 状态:`200 OK` +- 响应 Schema:`BrowsingHistorySettingResponse` + +```text +BrowsingHistorySettingResponse { + enabled: boolean + updatedAt: string +} +``` + +#### 失败响应 + +| HTTP 状态 | 业务错误码 | 触发条件 | +|---:|---|---| +| 401 | `AUTH.UNAUTHENTICATED` | 缺少有效访问令牌 | +| 403 | `AUTH.FORBIDDEN` | 当前账号不是买家 | + +#### 业务规则与并发 + +- 严格按 `user_id = current_user_id` 过滤;不存在记录时按 `enabled=true` 返回(默认开启)。 +- 与 A022 配对:GET 读取当前值,PATCH 修改值;同一资源不重复定义写入入口。 + +#### 缓存、事件或外部依赖 + +- 不缓存、不发布事件。 + +#### 验证场景 + +- 默认账号 → 200,`enabled=true`。 +- 已通过 A022 关闭过 → 200,`enabled=false`。 +- 商家账号调用 → 403。 \ No newline at end of file