一、联调前准备
在开始前,先确认这几件事:
1. 服务能正常启动
比如:
mvn spring-boot:run
或
mvn clean package
java -jar target/xxx.jar
2. Base URL
假设你的本地地址是:
http://localhost:8080
后面都按这个写。
3. 认证方式先统一
你现在 controller 里很多地方用了:
@RequestAttribute("currentUserId")
这意味着如果你还没接 JWT / Security,直接用 Postman 是打不通的。
所以联调前你需要二选一:
方案 A:你已经有登录 + token
那就正常走登录接口,后面带 token。
方案 B:你还没接完认证
那你需要临时用拦截器 / filter 模拟注入 currentUserId,或者先改成临时固定用户测试。
二、推荐联调总顺序
建议严格按这个顺序测:
- 用户注册
- 用户登录
- 创建家庭
- 查询当前家庭
- 创建分类
- 查询分类列表
- 创建物品
- 查询物品列表
- 查看物品详情
- 物品入库
- 物品出库
- 调整库存
- 查询库存日志
- 查询临期/过期/闲置物品
- 发起断舍离
- 查询断舍离列表
- 查看断舍离详情
- 更新断舍离状态
- 删除物品(失败场景)
- 取消断舍离后删除物品(成功场景)
- 查询通知列表(如果通知已接)
三、接口联调清单
1. 用户注册
如果你的项目已经有这个接口,就按你自己的路径;
下面先给一个通用模板。
接口
POST /api/auth/register
请求体示例
{
"username": "user1",
"password": "123456",
"nickname": "测试用户1",
"email": "user1@test.com"
}
成功预期
{
"code": 200,
"message": "success",
"data": {
"id": 1,
"username": "user1"
}
}
联调要点
- 用户名不能重复
- 密码是否加密存储
- 返回结构是否统一
2. 用户登录
接口
POST /api/auth/login
请求体示例
{
"username": "user1",
"password": "123456"
}
成功预期
如果你用 JWT,通常类似:
{
"code": 200,
"message": "success",
"data": {
"token": "eyJhbGciOiJIUzI1NiJ9...",
"userId": 1,
"username": "user1"
}
}
联调要点
- 把返回的 token 保存到 Postman 环境变量,例如
{{token}} - 后面接口统一带:
Authorization: Bearer {{token}}
3. 创建家庭
接口
POST /api/families
请求头
Authorization: Bearer {{token}}
Content-Type: application/json
请求体示例
{
"name": "我的家庭",
"description": "三口之家共享空间"
}
成功预期
{
"code": 200,
"message": "success",
"data": {
"id": 1,
"name": "我的家庭"
}
}
联调要点
- 创建家庭后,当前用户应该成为家庭成员/管理员
- 后续很多模块都会依赖当前家庭 ID
4. 查询当前家庭信息
接口
GET /api/families/current
请求头
Authorization: Bearer {{token}}
成功预期
{
"code": 200,
"message": "success",
"data": {
"id": 1,
"name": "我的家庭",
"memberCount": 1
}
}
联调要点
- 这里能验证
getCurrentUserFamilyId()是否接通 - 如果这里拿不到家庭,后面 item/inventory/declutter 都别测
5. 创建分类
接口
POST /api/categories
请求体示例
{
"name": "食品"
}
成功预期
{
"code": 200,
"message": "success",
"data": {
"id": 10,
"name": "食品"
}
}
联调要点
- 创建后记下
categoryId - 确认分类归属当前家庭
6. 查询分类列表
接口
GET /api/categories
成功预期
{
"code": 200,
"message": "success",
"data": [
{
"id": 10,
"name": "食品"
}
]
}
联调要点
- 确认只返回当前家庭可见分类
7. 创建物品
接口
POST /api/items
请求体示例
{
"name": "牛奶",
"categoryId": 10,
"stock": 2,
"unit": "盒",
"remark": "早餐常用",
"minStock": 1,
"imageUrl": "https://example.com/milk.jpg",
"expiryDate": "2026-05-01"
}
成功预期
{
"code": 200,
"message": "success",
"data": {
"id": 100,
"familyId": 1,
"categoryId": 10,
"name": "牛奶",
"stock": 2,
"unit": "盒",
"remark": "早餐常用",
"minStock": 1,
"imageUrl": "https://example.com/milk.jpg",
"expiryDate": "2026-05-01",
"statusTag": "NORMAL"
}
}
联调要点
- 记下
itemId - 检查 category 是否属于当前家庭
- 检查默认
statusTag
8. 查询物品列表
接口
GET /api/items
成功预期
{
"code": 200,
"message": "success",
"data": [
{
"id": 100,
"name": "牛奶",
"stock": 2
}
]
}
联调要点
- 只返回当前家庭的 item
9. 查询物品详情
接口
GET /api/items/100
成功预期
返回你刚创建的 item 详情。
联调要点
- 校验 family 权限隔离
- 如果换其他用户 token,不应看到别的家庭物品
10. 更新物品信息(不改库存)
接口
PUT /api/items/100
请求体示例
{
"name": "纯牛奶",
"categoryId": 10,
"unit": "盒",
"remark": "早餐每日一盒",
"minStock": 2,
"imageUrl": "https://example.com/milk-new.jpg",
"expiryDate": "2026-05-08",
"lastUsedAt": "2026-04-20T09:30:00"
}
成功预期
- 更新成功
stock不应被修改
联调要点
- 检查普通更新接口是否真的没有 stock 字段
- 防止前端误传库存
11. 物品入库
接口
POST /api/items/100/inventory/in
请求体示例
{
"quantity": 5,
"remark": "超市采购补货"
}
成功预期
{
"code": 200,
"message": "success",
"data": {
"id": 100,
"name": "纯牛奶",
"stock": 7
}
}
联调要点
- 原库存 2,入库 5,结果应为 7
- 应写入一条
IN类型库存日志
12. 物品出库
接口
POST /api/items/100/inventory/out
请求体示例
{
"quantity": 2,
"remark": "早餐使用"
}
成功预期
{
"code": 200,
"message": "success",
"data": {
"id": 100,
"name": "纯牛奶",
"stock": 5
}
}
联调要点
- 原库存 7,出库 2,结果 5
lastUsedAt应更新- 应写
OUT类型库存日志
13. 出库失败场景:库存不足
接口
POST /api/items/100/inventory/out
请求体示例
{
"quantity": 999,
"remark": "错误测试"
}
预期结果
{
"code": 400,
"message": "库存不足,无法出库",
"data": null
}
联调要点
- 库存不能变负数
- 失败时不能写错误库存日志
14. 调整库存
接口
POST /api/items/100/inventory/adjust
请求体示例
{
"targetStock": 3,
"remark": "盘点修正"
}
成功预期
库存直接变为 3。
联调要点
- 应写
ADJUST类型日志 - quantity 通常记录差值绝对值
15. 查询库存日志
接口
GET /api/items/100/inventory/logs
成功预期
{
"code": 200,
"message": "success",
"data": [
{
"operateType": "ADJUST",
"quantity": 2,
"beforeStock": 5,
"afterStock": 3,
"remark": "盘点修正"
},
{
"operateType": "OUT",
"quantity": 2,
"beforeStock": 7,
"afterStock": 5,
"remark": "早餐使用"
},
{
"operateType": "IN",
"quantity": 5,
"beforeStock": 2,
"afterStock": 7,
"remark": "超市采购补货"
}
]
}
联调要点
- 顺序一般按时间倒序
- 校验 before / after 是否正确
16. 查询过期物品
接口
GET /api/items/expired
成功预期
返回 expiryDate < today 的物品列表。
联调要点
为了测试,你可以先创建一个过期物品:
{
"name": "酸奶",
"categoryId": 10,
"stock": 1,
"unit": "盒",
"remark": "已过期测试",
"minStock": 1,
"expiryDate": "2024-01-01"
}
17. 查询临期物品
接口
GET /api/items/expiring?days=7
成功预期
返回未来 7 天内到期的物品。
联调要点
可创建一条即将过期数据,比如:
{
"name": "面包",
"categoryId": 10,
"stock": 1,
"unit": "袋",
"remark": "临期测试",
"minStock": 1,
"expiryDate": "2026-04-28"
}
18. 查询闲置物品
接口
GET /api/items/idle?days=90
成功预期
返回长期未使用物品。
联调要点
需要 item 的 lastUsedAt 早于阈值;
可以通过更新物品时传旧时间,或手动改库测试。
19. 发起断舍离
接口
POST /api/declutter
请求体示例
{
"itemId": 100,
"actionType": "DONATE",
"reasonType": "NEAR_EXPIRY",
"remark": "快到期了,考虑捐赠"
}
成功预期
{
"code": 200,
"message": "success",
"data": {
"id": 500,
"itemId": 100,
"actionType": "DONATE",
"reasonType": "NEAR_EXPIRY",
"status": "PENDING"
}
}
联调要点
- 记下
declutterId - 验证是否限制同一 item 只能有一个
PENDING
20. 重复发起断舍离(失败场景)
接口
POST /api/declutter
请求体示例
{
"itemId": 100,
"actionType": "DISCARD",
"reasonType": "UNUSED",
"remark": "重复测试"
}
预期结果
{
"code": 400,
"message": "该物品已存在待处理的断舍离记录",
"data": null
}
21. 查询断舍离列表
接口
GET /api/declutter
或带筛选:
GET /api/declutter?status=PENDING&page=0&size=10
成功预期
返回当前家庭下的断舍离记录列表。
联调要点
- 分页参数是否生效
- 状态筛选是否生效
22. 查询断舍离详情
接口
GET /api/declutter/500
成功预期
返回该条断舍离详情,包括:
- itemId
- actionType
- reasonType
- status
- createdBy
- handledBy
- itemName
23. 更新断舍离状态为完成
接口
PUT /api/declutter/500/status
请求体示例
{
"status": "COMPLETED"
}
成功预期
{
"code": 200,
"message": "success",
"data": {
"id": 500,
"status": "COMPLETED",
"handledBy": 1
}
}
联调要点
- 只有
PENDING可更新 - 更新后再改应失败
24. 再次更新已完成断舍离(失败场景)
接口
PUT /api/declutter/500/status
请求体示例
{
"status": "CANCELLED"
}
预期结果
{
"code": 400,
"message": "只有待处理状态才允许更新",
"data": null
}
25. 删除物品失败场景:有 pending 断舍离
这个场景要单独造一个新 item 来测,不要用已经 completed 的记录。
先创建一个新物品
例如:
{
"name": "旧衣服",
"categoryId": 10,
"stock": 1,
"unit": "件",
"remark": "待断舍离测试",
"minStock": 0,
"expiryDate": null
}
记下 itemId = 101
再发起断舍离
{
"itemId": 101,
"actionType": "DONATE",
"reasonType": "NO_LONGER_NEEDED",
"remark": "准备送人"
}
然后删除 item
接口
DELETE /api/items/101
预期结果
{
"code": 400,
"message": "该物品存在待处理断舍离记录,不能直接删除",
"data": null
}
26. 取消断舍离
接口
PUT /api/declutter/{declutterId}/cancel
成功预期
状态变为 CANCELLED
27. 再删除物品(成功场景)
接口
DELETE /api/items/101
成功预期
{
"code": 200,
"message": "success",
"data": null
}
28. 查询通知列表(如果已接通知模块)
接口
GET /api/notifications
成功预期
{
"code": 200,
"message": "success",
"data": [
{
"id": 1,
"type": "LOW_STOCK",
"title": "库存不足提醒",
"content": "纯牛奶库存已低于阈值",
"read": false
}
]
}
29. 标记通知已读
接口
PUT /api/notifications/1/read
成功预期
通知状态变为已读
30. 全部通知已读
接口
PUT /api/notifications/read-all
成功预期
全部已读
四、建议你在 Postman 里配置的环境变量
你可以配置这些变量,调起来会方便很多:
baseUrl = http://localhost:8080
token = 登录后保存
categoryId = 创建分类后保存
itemId = 创建物品后保存
declutterId = 发起断舍离后保存
后面请求里就能写:
{{baseUrl}}/api/items/{{itemId}}
五、推荐的 Postman Header 模板
如果你用 token:
Authorization: Bearer {{token}}
Content-Type: application/json
如果你当前还是临时测试,没有 token,而是后端临时固定用户,那就先不带 Authorization。
六、建议你重点验证的“失败场景”
联调时不要只测成功,至少把这些失败场景测掉:
权限类
- 非本家庭用户访问 item 详情
- 非本家庭用户查库存日志
- 非本家庭用户操作断舍离
业务类
- 出库大于库存
- 重复发起 pending 断舍离
- 非 pending 状态更新断舍离
- item 有 pending 断舍离时删除
- category 不属于当前家庭却拿来创建 item
参数类
- 创建 item 时 name 为空
- 入库数量为 0
- 出库数量为负数
- 调整库存目标为负数
七、最推荐你实际演示的一条接口链
如果你最后要现场演示,不建议全演,建议演这一条:
- 登录
- 创建分类
- 创建物品
- 入库
- 出库
- 查库存日志
- 发起断舍离
- 查断舍离列表
- 删除物品失败
- 取消断舍离
- 删除物品成功
这条链非常完整,能体现:
- 数据归属
- 库存流转
- 日志留痕
- 断舍离状态控制
- 删除保护
八、联调完成的判定标准
如果下面这些都能通过,你的后端就已经算“主流程跑通”:
- 登录后能拿到当前用户
- 当前用户能拿到家庭
- 分类能创建并查询
- item 能创建、更新、查询、删除
- 入库/出库/调整库存能正常修改 stock
- 每次库存变化都写日志
- 断舍离能发起、查询、更新、取消
- pending 断舍离会阻止 item 删除
- 主要失败场景有正确报错