来源:2026-10-30 API 级 e2e 实跑(
tests/playwright.config.api.js,目标http://cfc.iwintrue.com) 复现环境:Chromium 1.61.1 + Playwright 1.61.1 / Node 22 / Windows 账号来源:测试专用直注接口/api/auth/direct-register自注册(每个用例独立家庭,互不干扰)
| 项 | 内容 |
|---|---|
| 模块 | 能量系统 /api/energy/grant |
| 严重程度 | 高(核心玩法链路不可用) |
| 优先级 | P0 |
| 类型 | 功能缺陷 / 健壮性(未捕获异常) |
| 关联用例 | tests/e2e/api/energy-api.spec.js 场景2(fixme)、场景2c(特征化)、场景6(fixme) |
前置条件:已登录家长账号,所在家庭存在成员记录,energy_dimension 五维配置已就绪(/api/energy/overview 可正常返回 body/mind/wisdom/action/wealth)。
复现步骤:
POST /api/auth/direct-register 注册家长账号,取得 token 与 familyId。POST /api/family/member/list(body {})取得自身 memberId。POST /api/energy/grant,body:{ memberId, taskId: 88888888, amount: 10, dimensionCode: "body" },Header 携带 Authorization: Bearer <token>。预期结果:code=200,返回 EnergyLog 记录,能量余额增加且 /api/energy/logs 可查到该 sourceId 的流水。
实际结果:code=500,message="服务器内部错误",data=null。响应耗时 365–975ms(非超时),带/不带 dimensionCode 均复现。
根因线索(代码级):
EnergyController.grantEnergy → EnergyService.grantEnergy(约 1890 行)→ awardEnergy(1541 行)→ awardByConfig → executeAward。
该链路内部存在未捕获异常并被 Spring 兜底为 500,建议在 executeAward 中补充成员余额记录存在性校验与 try/catch,并把异常转为业务错误(Result.error(e.getMessage()))而非「服务器内部错误」。
修复建议:
executeAward 更新成员能量余额前,先校验 family_members 记录存在且未删除,避免空指针。| 项 | 内容 |
|---|---|
| 模块 | 家庭成员 /api/family/member/update |
| 严重程度 | 中(数据未按用户操作落库) |
| 优先级 | P1 |
| 类型 | 功能缺陷(字段级不生效) |
| 关联用例 | tests/e2e/api/family-member-api.spec.js 场景4(fixme)、场景4c(特征化) |
前置条件:已登录家庭管理员(家庭创建者),家庭内存在一名「无关联登录账号」的手动成员。
复现步骤:
POST /api/family/member/add,body { generationLevel:"child", nickname:"QA_N1", gender:"male", birthday:"2015-06-15" },记录返回的 memberId。POST /api/family/member/update,body { memberId, nickname:"QA_NICK_NEW" }。POST /api/family/member/list 查看该成员昵称。预期结果:更新后 code=200,响应体与成员列表中该成员 nickname 均变为 QA_NICK_NEW。
实际结果:code=200、message="success",但响应体 nickname 仍为旧值 QA_N1,成员列表中同样为旧值。
对照实验(已验证):同一次 update 请求中携带 gender:"female"、birthday:"2017-05-05" 时,这两个字段均能正确更新并回显。故问题精确落在 nickname 单一字段,非整体 update 失效。
根因线索(代码级):
本地源码 FamilyMemberService.updateMember(约 969 行)中 nickname 分支确实存在:
String oldNickname = member.getNickname();
if (dto.getNickname() != null && !dto.getNickname().trim().isEmpty()) {
member.setNickname(dto.getNickname().trim());
}
...
familyMemberMapper.updateById(member);
FamilyMember.nickname 亦为普通字段(无 FieldStrategy 排除策略)。
因此高度怀疑:远程部署构建版本落后于当前代码仓库(远程为较旧构建,尚无 nickname 更新逻辑);次要可能为运行时 MyBatis-Plus 更新策略或触发器导致 nickname 列被跳过。
修复建议:
http://cfc.iwintrue.com 所部署的构建版本与 commit,确认是否包含 nickname 更新逻辑;若落后则重新部署并回归。updateMember 内于 updateById 后回查一次 family_members.nickname 做自校验,不一致即抛业务错误,避免「假成功」。/api/family/member/list 读回新值。| 项 | 内容 |
|---|---|
| 模块 | 部署环境 / 测试基础设施 |
| 严重程度 | 中(阻塞 UI 回归) |
| 优先级 | P1 |
| 类型 | 环境配置 |
| 关联用例 | tests/e2e/*.spec.js(36 个 DOM 型 spec)、tests/user-scenarios/* |
前置条件:使用 tests/playwright.remote.config.js(baseURL = http://cfc.iwintrue.com)运行任何基于 page.goto('/#/pages/...') 的用例。
复现步骤:
http://cfc.iwintrue.com/#/pages/family/add-member。预期结果:渲染出前端 SPA 的「添加家庭成员」页面(uni-app 哈希路由)。
实际结果:返回 Spring Boot 白标错误页:
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
There was an unexpected error (type=Not Found, status=404).
访问根路径 http://cfc.iwintrue.com/ 结果相同。说明该 host 仅部署后端 API,未部署前端静态资源。
影响:原 DOM 型 e2e(家庭成员/能量/文章/联系人/规划师看板等 32 条)在远程必然全部超时失败,与业务实现无关。
修复建议:
playwright.remote.config.js 中与 API host 分离配置(baseURL 指前端,extraHTTPHeaders/baseURL 供 API 调用)。| 项 | 内容 |
|---|---|
| 模块 | 家庭成员 /api/family/member/add 性能 |
| 严重程度 | 低 |
| 优先级 | P2 |
| 类型 | 性能 |
| 关联用例 | tests/e2e/api/family-member-api.spec.js 场景2/3/7 |
复现步骤:同一账号下连续调用 POST /api/family/member/add 三次并计时。
预期结果:各次响应耗时稳定且在秒级以内。
实际结果:首调 16.3s,后续 1.5s / 1.5s。首调包含 unlockGateService.refreshFamilyGates、通关联动初始化与数据源预热等一次性开销。
影响:超过 Playwright 默认 actionTimeout(15s) 会误报为接口失败(本次实测即因此产生 8 条假失败)。
修复建议:
helpers/api.js 中 API_TIMEOUT_MS = 60000,配置 timeout: 180000),但仍建议后端优化首调耗时。| 模块 | 用例数 | 结果 |
|---|---|---|
| api-smoke | 4 | 全部通过 |
| article-api | 7 | 全部通过 |
| contact-api | 6 | 全部通过 |
| energy-api | 10 | 8 通过 + 2 fixme(受 CFC-EN-001 阻塞) |
| family-member-api | 13 | 12 通过 + 1 fixme(受 CFC-FM-001 阻塞) |
| 合计 | 40 | 35 通过 / 2 fixme / 3 特征化用例通过 |
对比:同批场景在 DOM 型 e2e 上为 10 通过 / 32 失败(且失败原因全部为「远程无 SPA」)。