Appearance
模块手册 · 社区域(community)
收录 4 个模块的权威开发手册(源自各模块
CLAUDE.md,随仓库同步)。
本文目录
- feed
- follow
- favorite
- review
1. feed 模块
源:
src/app/feed/CLAUDE.md(随仓库同步)
src/app/feed/
feed 模块 — V1 运动动态 9 action 迁移(V2 DDD 4 层 + InMemory + 行为兼容),L3-A 社交域。
模块职责
feed 承接 C 端运动动态域(对齐 V1 server/src/modules/feed/),路由风格 = action 协议:
- 入口:
POST /api/v1/feed+body {action, payload}(V1POST /api/feed路径平移,协议 100% 兼容) - 9 action(routes.ts switch 权威 — 比 V1 CLAUDE.md 记载的 7 action 多 2):
list/myFeeds/publish/like/unlike/comment/listComments/hotTopics/shoesForPicker - 鉴权:全部需已登录(
Authorization: Bearer <access_token>) - 错误码区段:40420xxx(与 upload 40419xxx / follow 40421xxx 相邻隔离)
- ⚠️ 2026-09-16 撞码治理时从
40413xxx迁出(该段现属 group_buy)
- ⚠️ 2026-09-16 撞码治理时从
- HTTP 状态:400 / 401 / 404(V1 行为 100% 兼容)
DDD 4 层结构
src/app/feed/
├── domain/ # 零 web 依赖
│ ├── entities/feed.py # Feed / FeedLike / FeedComment + CZ_FEED_NOTIFY_CONTENT_MAX(50)
│ └── exceptions.py # FeedErrorCode (40420xxx) + cz_feed_error_to_http_status
│
├── application/
│ ├── ports/repositories.py # FeedRepositoryPort + FeedUser/FeedShoe/FeedFollow 最小端口
│ ├── ports/services.py # AccessTokenDecoderPort + NotificationPort
│ ├── usecase/feed_timeline.py # list / myFeeds / listComments / hotTopics(4)
│ ├── usecase/feed_publish.py # publish / shoesForPicker(2)
│ ├── usecase/feed_interactions.py # like / unlike / comment(3)
│ └── factories/create_feed_usecases.py
│
├── infrastructure/persistence/inmemory_feed_repositories.py # 主仓储 + 4 适配器 + 种子
└── interface/{api/feed_actions.py, handlers.py}对外接口(9 action 一览)
| action | payload | 返回(data) | 说明 |
|---|---|---|---|
list | {page=1, pageSize=20(1..50), sort=latest|hot, topic?, userId?} | {list, total, page, pageSize, hasMore} | 动态流;hot=likeCount desc;含作者 + shoe + liked |
myFeeds | {page, pageSize} | 同上 | 我的动态(createdAt desc) |
publish | {content 1..500, images ≤9, checkinId?, distanceKm? 0..200, topic? ≤30, videoUrl?(url), shoeId?} | {id} | 发布(shoeId 归属校验不属则忽略 + fan-out 粉丝通知) |
like | {feedId} | {ok: true, liked: true} | 幂等点赞(事务计数 +1;notify type=like) |
unlike | {feedId} | {ok: true, liked: false} | 幂等取消(不校验 feed 存在 — V1 口径) |
comment | {feedId, content 1..200} | {id} | 评论(事务计数 +1;notify content 50 字截断 + '…') |
listComments | {feedId, page=1, pageSize=50} | {list, total} | 评论列表(createdAt desc + user) |
hotTopics | — | {topics: [{topic, count}]} | groupBy topic(非空)count desc take 10 |
shoesForPicker | — | {shoes: [...]} | active 跑鞋(createdAt desc — 发动态 picker) |
关键行为约束(V1 对拍)
$transaction 计数原子(like/unlike/comment — 02 §6 铁律)
cz_like_feed/cz_unlike_feed/cz_add_comment:create|delete + 计数 ±1 收敛到同一 async 方法内(无 await 间隙 → asyncio 单线程原子 — V1 $transaction 回调等价)- 幂等:like 先 findUnique 检查(已赞不重复计数);unlike 未赞也返 ok;重复 PublishLike 由 @@unique([feedId, userId]) 语义兜底(仓储 upsert 式跳过)
fan-out(publish + V0.2.125)
publish 后给作者所有粉丝推 type=new_post 通知:粉丝列表查询失败 → 静默(外层); 单条 notify 失败 → 独立吞错(Promise.allSettled 等价 — 不影响其他粉丝与主返回)。
notify 集成(V1 notification.notify 对拍)
- 自己触发自己跳过(
user_id === actor_id→ 不写通知 — 在端口实现内,V1 notify 首行) - 调用方
contextlib.suppress(Exception)包裹(通知失败不阻塞点赞/评论/发布主链路) - V1 另有 realtime 推送(publishToUser)— V2 InMemory 阶段不模拟(与缓存同决策)
list 过滤组合(V1 where spread 语义)
where = {...topic?, ...userId?} — topic 空串/缺省不过滤(topic ? {topic} : {});sort=hot 按 likeCount desc 否则 createdAt desc;liked = viewer 对 feed 的点赞集合(批量查询等价 V1 include likes.where(userId))。
评论 notify 截断(50 字)
V1 content.length > 50 → slice(0, 50) + '…'(>50 才截 — 恰好 50 不截)。 ⚠️ V1 length 为 UTF-16 code units;V2 用 Python len(code points)— 中文/拉丁文一致, 增补平面 emoji 边缘差异已接受。
postman/videoUrl 校验
V1 z.string().url()(JS new URL() try/catch — 绝对 URL)→ V2 pydantic field_validator (urlparse().scheme 非空判定 — 语义近似)。
跨模块最小端口(对应模块未接真实现 — 生产阶段替换)
| 端口 | InMemory 实现 | 将来接入 |
|---|---|---|
FeedUserPort(作者 id/nickname/avatarUrl) | InMemoryFeedUserAdapter | user 模块共库 |
FeedShoePort(归属校验 / picker / include 批量) | InMemoryFeedShoeAdapter | shoes 模块真实现(共读 shoes 表) |
FeedFollowPort(粉丝 userIds — fan-out) | InMemoryFeedFollowAdapter | follow 模块真实现(共读 follows 表) |
NotificationPort(notify 集成) | InMemoryFeedNotificationAdapter(记录 + skip-self) | notification 模块迁移时接真实现 |
与 V1 行为差异(已接受)
| 项 | V1 | V2 |
|---|---|---|
| 时间序列化 | toISOString()(UTC 带 Z) | naive 本地 isoformat(全局口径 — 显示层等价) |
| 缓存 | list 无缓存;V1 部分读走 Cache.wrap | 不模拟(与 goal/shoes/training/mall 同决策) |
| realtime 推送 | notify 后 publishToUser(WS) | 不模拟(InMemory 阶段无 WS 通道) |
| listComments 入参校验 | route 层无 zod(TS 仅类型断言 — 垃圾入参未定义行为) | pydantic 宽容归一(feedId 必传 + 默认 1/50 — 防御性增强) |
| 截断口径(评论 notify) | UTF-16 code units | Python len(code points — emoji 边缘差异) |
| 事务 | Prisma $transaction 回调 | InMemory 单方法原子(生产 Prisma 阶段经端口恢复真事务) |
未迁移项(标注)
- V1 feed 的 redis 缓存/热度排行(如有):本批未涉及(V1 侦察确认无)
- 跨模块调用方(frontend hot/topic 页)只消费 HTTP 契约 — 无后端缺口
测试与质量
| 文件 | 范围 |
|---|---|
tests/integration/test_feed_actions.py | 9 action E2E(鉴权 / 契约 / publish→like→comment 链 / 404) |
tests/unit/application/test_feed_usecases.py | 9 action 全分支(幂等 / fan-out / 截断 / hot 排序) |
bash
PYTHONPATH=src python3 -m pytest tests/integration/test_feed_actions.py \
tests/unit/application/test_feed_usecases.py -q相关文件
src/app/feed/ ← 本文件所在模块
tests/integration/test_feed_actions.py
tests/unit/application/test_feed_usecases.py
src/main.py # 装配("── 13. L3-A 社交域"段)变更记录 (Asia/Shanghai)
| 日期 | 版本 | 变更 |
|---|---|---|
| 2026-09-16 | 1.0.0 | 首版 — V1 运动动态 9 action 迁移(DDD 4 层 + 40413xxx + 原子计数 + fan-out + 4 最小端口) |
2. follow 模块
源:
src/app/follow/CLAUDE.md(随仓库同步)
src/app/follow/
follow 模块 — V1 关注/粉丝 6 action 迁移(V2 DDD 4 层 + InMemory + 行为兼容),L3-A 社交域。
模块职责
follow 承接 C 端关注关系域(对齐 V1 server/src/modules/follow/),路由风格 = action 协议:
- 入口:
POST /api/v1/follow+body {action, payload}(V1POST /api/follow路径平移,协议 100% 兼容) - 6 action:
follow/unfollow/isFollowing/myFollowing/myFollowers/myCounts - 鉴权:全部需已登录(
Authorization: Bearer <access_token>) - 错误码区段:40421xxx(与 feed 40420xxx 相邻隔离)
- ⚠️ 2026-09-16 撞码治理时从
40414xxx迁出(该段现属 distribution)
- ⚠️ 2026-09-16 撞码治理时从
- HTTP 状态:400 / 401 / 404(V1 行为 100% 兼容)
DDD 4 层结构
src/app/follow/
├── domain/ # 零 web 依赖
│ ├── entities/follow.py # Follow(followerId / followeeId / createdAt)
│ └── exceptions.py # FollowErrorCode (40421xxx) + cz_follow_error_to_http_status
│
├── application/
│ ├── ports/repositories.py # FollowRepositoryPort + FollowUserPort
│ ├── ports/services.py # AccessTokenDecoderPort + NotificationPort
│ ├── usecase/follow_relations.py # follow / unfollow / isFollowing(3)
│ ├── usecase/follow_queries.py # myFollowing / myFollowers / myCounts(3)
│ └── factories/create_follow_usecases.py
│
├── infrastructure/persistence/inmemory_follow_repositories.py # 主仓储 + 2 适配器 + 种子
└── interface/{api/follow_actions.py, handlers.py}对外接口(6 action 一览)
| action | payload | 返回(data) | 说明 |
|---|---|---|---|
follow | {userId} | {ok: true, following: true} | upsert 幂等;'不能关注自己' 400 / '用户不存在' 404;notify type=follow |
unfollow | {userId} | {ok: true, following: false} | deleteMany 幂等(不存在也 ok) |
isFollowing | {userIds 1..50} | {results: [{userId, following}]} | 批量(保 input 顺序) |
myFollowing | {page, pageSize} | {list: [{userId, createdAt, user}], total, page, pageSize, hasMore} | 我关注的人(createdAt desc) |
myFollowers | {page, pageSize} | 同上 | 我的粉丝(createdAt desc) |
myCounts | {userId} | {user, followingCount, followerCount, isFollowing, isSelf} | 用户主页一次拿全(可查任意 userId) |
关键行为约束(V1 对拍)
follow 校验链(顺序严格)
① 自己关注自己 → 400 不能关注自己(先于目标存在校验);② 目标不存在 → 404 用户不存在; ③ upsert(已存在则 update {} 不重复建 — @@unique([followerId, followeeId]) 语义)。
notify(fail-safe)
follow 成功后 notify 被关注者(type=follow — 无 targetType/targetId);通知失败静默 (contextlib.suppress — 不阻塞关注主链路);自己触发自己跳过在端口内(V1 notify 首行)。 ⚠️ 幂等重复 follow 依然调 notify(V1 无重查短路 — 逐字对齐)。
myCounts 视图逻辑(V1 三元表达式逐字)
viewerId && viewerId !== userId ? findUnique : null — viewerId 恒存在(已登录)→ viewer === user 时跳过查询(isFollowing 恒 false,isSelf true);user 不存在 → 404 用户不存在(Promise.all 后判空 — V1 顺序)。
分页
myFollowing / myFollowers:createdAt desc + hasMore = page * pageSize < total; user include select {id, nickname, avatarUrl}。
跨模块最小端口(对应模块未接真实现 — 生产阶段替换)
| 端口 | InMemory 实现 | 将来接入 |
|---|---|---|
FollowUserPort(目标校验 + user include) | InMemoryFollowUserAdapter | user 模块共库 |
NotificationPort(notify 集成) | InMemoryFollowNotificationAdapter(记录 + skip-self) | notification 模块迁移时接真实现 |
与 V1 行为差异(已接受)
| 项 | V1 | V2 |
|---|---|---|
| 时间序列化 | toISOString()(UTC 带 Z) | naive 本地 isoformat(全局口径) |
| realtime 推送 | notify 后 publishToUser(WS) | 不模拟(InMemory 阶段无 WS 通道) |
| 事务 | Prisma upsert/deleteMany 原子 | InMemory 单方法(等价) |
测试与质量
| 文件 | 范围 |
|---|---|
tests/integration/test_follow_actions.py | 6 action E2E(鉴权 / 幂等 / 400+401+404) |
tests/unit/application/test_follow_usecases.py | 6 action 全分支(校验收敛 / 保序 / isSelf) |
bash
PYTHONPATH=src python3 -m pytest tests/integration/test_follow_actions.py \
tests/unit/application/test_follow_usecases.py -q相关文件
src/app/follow/ ← 本文件所在模块
tests/integration/test_follow_actions.py
tests/unit/application/test_follow_usecases.py
src/main.py # 装配("── 13. L3-A 社交域"段)变更记录 (Asia/Shanghai)
| 日期 | 版本 | 变更 |
|---|---|---|
| 2026-09-16 | 1.0.0 | 首版 — V1 关注/粉丝 6 action 迁移(DDD 4 层 + 40414xxx + 最小端口 ×2) |
3. favorite 模块
源:
src/app/favorite/CLAUDE.md(随仓库同步)
src/app/favorite/
favorite 模块 — V1 收藏 4 action 迁移(V2 DDD 4 层 + InMemory + 行为兼容),L3-A 社交域。
模块职责
favorite 承接 C 端 Content/Product 收藏域(对齐 V1 server/src/modules/favorite/), 路由风格 = action 协议:
- 入口:
POST /api/v1/favorite+body {action, payload}(V1POST /api/favorite路径平移,协议 100% 兼容) - 4 action:
list/add/remove/isFavorited - 鉴权:全部需已登录(
Authorization: Bearer <access_token>) - 错误码区段:40415xxx(与 follow 40421xxx / review 40416xxx 相邻隔离)
- HTTP 状态:400 / 401(4 action 无业务抛出 — V1 口径)
DDD 4 层结构
src/app/favorite/
├── domain/ # 零 web 依赖
│ ├── entities/favorite.py # Favorite + CZ_FAVORITE_TYPES(content | product)
│ ├── services/money.py # cz_js_decimal_string(product price — 各模块自带一份)
│ └── exceptions.py # FavoriteErrorCode (40415xxx) + cz_favorite_error_to_http_status
│
├── application/
│ ├── ports/repositories.py # FavoriteRepositoryPort + FavoriteContent/FavoriteProduct 最小端口
│ ├── ports/services.py # AccessTokenDecoderPort
│ ├── usecase/favorite_actions.py # list / add / remove / isFavorited(4)
│ └── factories/create_favorite_usecases.py
│
├── infrastructure/persistence/inmemory_favorite_repositories.py # 主仓储 + 2 适配器 + 种子
└── interface/{api/favorite_actions.py, handlers.py}对外接口(4 action 一览)
| action | payload | 返回(data) | 说明 |
|---|---|---|---|
list | {targetType?: content|product} | {favorites: [{id, targetType, targetId, createdAt, detail}]} | 我的收藏(createdAt desc,无分页 — V1 口径);detail 为精确 select 或 null |
add | {targetType, targetId} | {ok: true} | upsert 幂等;不校验目标存在 — V1 口径 |
remove | {targetType, targetId} | {ok: true} | deleteMany 幂等(不存在也 ok) |
isFavorited | {items 1..50: [{targetType, targetId}]} | {results: [{targetType, targetId, favorited}]} | 批量红心(保 input 顺序) |
关键行为约束(V1 对拍)
list 批量关联(避免 N+1)
先查 favorites → 拆 content/product ids → 批量查详情(Map 关联);id 列表为空时短路不查 (V1 length === 0 ? [] : findMany 逐字);目标已删除 → detail: null(前端据此隐藏)。
detail 精确键集(V1 select)
- content:
{id, title, cover, summary, type, location, date}(可空字段 null 直通) - product:
{id, name, price, images, category, status}— price → decimal.js toString 字符串 (Decimal('199.00')→'199'去尾随零 — cz_js_decimal_string)
add 不校验目标存在(V1 口径)
V1 upsert 直写不校验 content/product 存在 — 幽灵目标可收藏(list detail=null)。
isFavorited key 格式
'type:id' 集合匹配(V1 ${targetType}:${targetId})— results 保 input 顺序。
跨模块最小端口(对应模块未接真实现 — 生产阶段替换)
| 端口 | InMemory 实现 | 将来接入 |
|---|---|---|
FavoriteContentPort(内容详情 select) | InMemoryFavoriteContentAdapter | content 模块迁移时接真实现 |
FavoriteProductPort(商品详情 select) | InMemoryFavoriteProductAdapter | mall 模块真实现(共读 products 表) |
与 V1 行为差异(已接受)
| 项 | V1 | V2 |
|---|---|---|
| 时间序列化 | toISOString()(UTC 带 Z) | naive 本地 isoformat(全局口径) |
| 可选键 null | JS undefined 键被丢弃 | 输出 null(JS falsy 等价 — detail 显式 null 是 V1 行为) |
测试与质量
| 文件 | 范围 |
|---|---|
tests/integration/test_favorite_actions.py | 4 action E2E(price 字符串契约 / detail null / 校验 400) |
tests/unit/application/test_favorite_usecases.py | 4 action 全分支(幂等 / 幽灵目标 / 保序) |
tests/unit/domain/test_favorite_money.py | decimal.js toString 纯函数 |
bash
PYTHONPATH=src python3 -m pytest tests/integration/test_favorite_actions.py \
tests/unit/application/test_favorite_usecases.py tests/unit/domain/test_favorite_money.py -q相关文件
src/app/favorite/ ← 本文件所在模块
tests/{integration/test_favorite_actions.py, unit/application/test_favorite_usecases.py, unit/domain/test_favorite_money.py}
src/main.py # 装配("── 13. L3-A 社交域"段)变更记录 (Asia/Shanghai)
| 日期 | 版本 | 变更 |
|---|---|---|
| 2026-09-16 | 1.0.0 | 首版 — V1 收藏 4 action 迁移(DDD 4 层 + 40415xxx + 最小端口 ×2 + Decimal price 口径) |
4. review 模块
源:
src/app/review/CLAUDE.md(随仓库同步)
src/app/review/
review 模块 — V1 C 端评价域 7 action 迁移(V2 DDD 4 层 + InMemory + 行为兼容),L3-A 社交域。
模块职责
review 承接 C 端评价域(对齐 V1 server/src/modules/review/ — V0.1.113 电商评价闭环 + V0.1.137 鞋评),路由风格 = action 协议:
- 入口:
POST /api/v1/review+body {action, payload}(V1POST /api/review路径平移,协议 100% 兼容) - 7 action:
create/list/listByTarget/stats/targetStats/myReviews/remove - 鉴权:全部需已登录(
Authorization: Bearer <access_token>) - 错误码区段:40416xxx(避开 admin 侧 review 管理 40201500-599 — 后台审核链路独立)
- HTTP 状态:400 / 401 / 403 / 404(V1 行为 100% 兼容)
DDD 4 层结构
src/app/review/
├── domain/ # 零 web 依赖
│ ├── entities/review.py # Review + CZ_REVIEWABLE_ORDER_STATUS + CZ_SHOE_REVIEW_PREFIX
│ ├── services/review_calc.py # syntheticId / tag 前缀 / 剥前缀 / cz_js_round1
│ └── exceptions.py # ReviewErrorCode (40416xxx) + cz_review_error_to_http_status
│
├── application/
│ ├── ports/repositories.py # ReviewRepositoryPort + ReviewOrder/Shoe/User/Product 最小端口
│ ├── ports/services.py # AccessTokenDecoderPort
│ ├── usecase/review_create.py # create(product 7 步链 / shoe 路径)
│ ├── usecase/review_queries.py # list / listByTarget / stats / targetStats / myReviews(5)
│ ├── usecase/review_remove.py # remove(1)
│ └── factories/create_review_usecases.py
│
├── infrastructure/persistence/inmemory_review_repositories.py # 主仓储 + 4 适配器 + 种子
└── interface/{api/review_actions.py, handlers.py}对外接口(7 action 一览)
| action | payload | 返回(data) | 说明 |
|---|---|---|---|
create | {targetType=product|shoe, targetId, productId?, orderId?, rating 1..5, content? ≤500, images? ≤9} | product: {id} / shoe: {id, reviewType: 'shoe'} | 商品(已购校验)/ 鞋评(归属校验)双路径 |
list | {productId, page=1, pageSize=10} | {list, total, page, pageSize} | 旧商品入口(不剥 [shoe-review] 前缀 — V1 口径) |
listByTarget | {targetType, targetId, page, pageSize} | 同上 | V0.1.137 通用(syntheticId 查询 + 剥前缀) |
stats | {productId} | {avg, count, distribution{1..5}} | 商品评分汇总(= targetStats(productId)) |
targetStats | {targetType, targetId} | 同上 | 通用汇总(shoe → syntheticId) |
myReviews | {page, pageSize} | {list, total, page, pageSize} | 我的评价(含 product select;无 user 键 — V1 口径) |
remove | {id} | {ok: true} | 仅本人可删('评价不存在' 404 / '不是你的评价' 403) |
关键行为约束(V1 对拍)
create — product 校验链(顺序严格不可调换)
① productId = input.productId ?? input.targetId 缺 → 400 productId required(防御 — zod 拦截在前); ② orderId 缺 → 400 orderId required;③ 订单不存在 → 404 订单不存在;④ 非本人 → 403 不是你的订单;⑤ status ∉ {paid, shipped, done} → 400 订单未支付,不可评价; ⑥ 商品不在订单 → 400 该商品不在此订单;⑦ 三元组防重 → 400 已评价过该商品。
create — shoe 路径(V0.1.137)
归属校验('跑鞋不存在或不属于你' 404)→ syntheticId shoe:{shoeId} 防重('已评价过该跑鞋' 400) → content 存 tag `[shoe-review] ${content ?? ''}`.trim()(V1 模板拼接语义 — content=None → '[shoe-review]'(去尾空格);content=' 缓震一流 ' → '[shoe-review] 缓震一流'(中间双空格保留))。
前缀剥除不对称(V1 生产行为 — 逐字复刻)
listByTarget:content.startsWith('[shoe-review] ')才剥(含尾空格;裸 tag'[shoe-review]'不满足 → 不剥)list(旧 productId 入口)/myReviews:不剥前缀(V1 实现差异)
评分汇总(JS Math.round 语义)
avg = agg._avg.rating ? Math.round(x * 10) / 10 : 0 — 空态 0;无评价 distribution 全 0 键 {1..5}。⚠️ 不可用 Python round(银行家舍入)→ cz_js_round1 = floor(v*10 + 0.5)/10 (4.25 → 4.3 — 单测锁定)。
输出键集(V1 include 差异)
list/listByTarget:{id, userId, productId, orderId, rating, content, images, createdAt, replyContent, repliedAt, user}(user=include select {id, nickname, avatarUrl};repliedAt 恒输出 null/ISO)myReviews:同字段集但 无 user 键 +product: {id, name, images} | null(鞋评 → null)
跨模块最小端口(对应模块未接真实现 — 生产阶段替换)
| 端口 | InMemory 实现 | 将来接入 |
|---|---|---|
ReviewOrderPort(订单存在/归属/状态/商品在单) | InMemoryReviewOrderAdapter | order 模块共库 |
ReviewShoePort(跑鞋归属) | InMemoryReviewShoeAdapter | shoes 模块真实现 |
ReviewUserPort(评价列表 user include) | InMemoryReviewUserAdapter | user 模块共库 |
ReviewProductPort(myReviews product include) | InMemoryReviewProductAdapter | mall 模块真实现 |
与 V1 行为差异(已接受)
| 项 | V1 | V2 |
|---|---|---|
| 时间序列化 | toISOString()(UTC 带 Z) | naive 本地 isoformat(全局口径) |
| content 截断口径 | zod max(500) 按 UTF-16 | pydantic max_length 按 code points(emoji 边缘差异) |
| distribution 键类型 | JS 对象数字键(JSON 序列化为字符串键) | Python int 键(JSON 序列化同为字符串键 — 等价) |
未迁移项(标注)
- admin 侧评价审核(P2-A
src/app/admin/application/usecase/review_mgmt.py— 40201500-599) 与本模块独立;admin 审核的 [shoe-review] tag 识别由 admin 迁移批承载
测试与质量
| 文件 | 范围 |
|---|---|
tests/integration/test_review_actions.py | 7 action E2E(错误链 HTTP 状态 400/403/404 / 鞋评 tag / 统计契约) |
tests/unit/application/test_review_usecases.py | 7 action 全分支(校验链逐项 / avg JS 语义 4.25→4.3 / 前缀不对称) |
tests/unit/domain/test_review_calc.py | syntheticId / tag / 剥前缀 / round1 纯函数 |
bash
PYTHONPATH=src python3 -m pytest tests/integration/test_review_actions.py \
tests/unit/application/test_review_usecases.py tests/unit/domain/test_review_calc.py -q相关文件
src/app/review/ ← 本文件所在模块
tests/{integration/test_review_actions.py, unit/application/test_review_usecases.py, unit/domain/test_review_calc.py}
src/main.py # 装配("── 13. L3-A 社交域"段 — InMemoryReviewRepository 别名防 admin 同名遮蔽)变更记录 (Asia/Shanghai)
| 日期 | 版本 | 变更 |
|---|---|---|
| 2026-09-16 | 1.0.0 | 首版 — V1 C 端评价域 7 action 迁移(DDD 4 层 + 40416xxx + 鞋评 syntheticId/tag + 最小端口 ×4) |