# 商品列表接口双跑对比报告(/store/goods/list) 日期:2026-10-17 对比对象:线上 PHP(shop.qinys.cn,带 Access-Token) vs Java 重写版(localhost:18080) ## 结论 **11 组参数组合全部 PASS**:total、per_page、last_page、返回商品 ID 顺序、每个商品的 39 个字段(含嵌套 images.file、goods_images、价格字符串、图片 URL)完全一致。 ## 测试用例 | 用例 | 结果 | |---|---| | 默认(page=1&listRows=15) | PASS | | goodsName 模糊搜索 | PASS | | status=10 上架筛选 | PASS | | listType=on_sale | PASS | | sortType=sales 销量排序 | PASS | | sortType=price 升/降序 | PASS ×2 | | page=2/3 翻页 | PASS | | categoryId 分类筛选 | PASS | | goodsNo 货号搜索 | PASS | 字段级 diff(8 商品 × 39 字段):**差异 0**。 ## 发现并已对齐的 PHP 隐性约定(重要经验) 1. **listRows 参数被 PHP 忽略**:商家端控制器只传筛选参数,`getList($param)` 的 `$listRows` 用默认值 15 → Java 端同样固定 15,忽略请求里的 listRows。 2. **价格是字符串**:`"168.00"`(DECIMAL(10,2) → 字符串序列化),Java 用 ToStringSerializer。 3. **时间戳在库内是 int**:create_time/update_time 库内为 Unix 时间戳,出参才格式化为 `Y-m-d H:i:s`(东八区)。 4. **列表不返回 content**:长文本字段在列表查询中既不 SELECT 也不出现在 JSON。 5. **图片 URL 拼接**:storage=local 时 `preview_url = base_url + /uploads/ + file_path`;goods_image 取图集第一张的 preview_url。 6. **计算字段 goods_sales** = sales_initial + sales_actual。 7. **MP table-prefix 对显式 @TableName 不生效**,实体需写全表名。 ## 复现方式 ```bash # 启动 Java 版(本机 8080 被占,用 18080) cd F:\shop\java-rewrite\yoshop-java mvn spring-boot:run -Dspring-boot.run.arguments=--server.port=18080 # 逐字段对比 python -X utf8 ..\tools\compare_goods.py # 黄金样本 # golden/store_goods_list.json PHP 原始响应(listRows=2) # golden/java_goods_list.json Java 原始响应(listRows=15) ``` ## 待办 - [ ] Access-Token 认证拦截器(当前 store 模块免登录,storeId 硬编码 10001 可由参数覆盖) - [ ] /store/goods/detail(含 specList、isSpecLocked 组装) - [ ] 其余 150 个 store 接口按本模式推进