|
|
@@ -6,8 +6,10 @@ import com.etotem.cfc.dto.EnergySandboxDTO;
|
|
|
import com.etotem.cfc.dto.MemberEnergyDTO;
|
|
|
import com.etotem.cfc.dto.UnlockGateDTO;
|
|
|
import com.etotem.cfc.entity.EnergyLog;
|
|
|
+import com.etotem.cfc.entity.FamilyMember;
|
|
|
import com.etotem.cfc.entity.UnlockGate;
|
|
|
import com.etotem.cfc.entity.User;
|
|
|
+import com.etotem.cfc.mapper.FamilyMemberMapper;
|
|
|
import com.etotem.cfc.mapper.UserMapper;
|
|
|
import com.etotem.cfc.service.EnergyService;
|
|
|
import com.etotem.cfc.service.FiveDimensionScoreService;
|
|
|
@@ -37,6 +39,9 @@ public class EnergyController {
|
|
|
@Resource
|
|
|
private UnlockGateService unlockGateService;
|
|
|
|
|
|
+ @Resource
|
|
|
+ private FamilyMemberMapper familyMemberMapper;
|
|
|
+
|
|
|
/**
|
|
|
* 获取能量沙盘数据(个人/全家两层,受通关门禁控制)
|
|
|
* view=personal: 当前登录用户个人五维(需通过 SELF_CHECK 关卡)
|
|
|
@@ -169,6 +174,11 @@ public class EnergyController {
|
|
|
if (familyId == null) {
|
|
|
return Result.noFamily("请先创建或加入家庭");
|
|
|
}
|
|
|
+ // SC-C-06 越权拦截:目标成员必须属于调用方家庭,否则 403(其他成员隐私不可见)
|
|
|
+ Result<Map<String, Object>> forbidden = ensureMemberInFamily(memberId, familyId);
|
|
|
+ if (forbidden != null) {
|
|
|
+ return forbidden;
|
|
|
+ }
|
|
|
Map<String, Object> data = energyService.getOverview(memberId);
|
|
|
return Result.success(data);
|
|
|
}
|
|
|
@@ -189,6 +199,11 @@ public class EnergyController {
|
|
|
if (familyId == null) {
|
|
|
return Result.noFamily("请先创建或加入家庭");
|
|
|
}
|
|
|
+ // SC-C-06 越权拦截:目标成员必须属于调用方家庭,否则 403(其他成员流水隐私不可见)
|
|
|
+ Result<Page<EnergyLog>> r403 = ensureMemberInFamily(memberId, familyId);
|
|
|
+ if (r403 != null) {
|
|
|
+ return r403;
|
|
|
+ }
|
|
|
String dimensionCode = (String) params.get("dimensionCode");
|
|
|
Integer page = params.get("page") != null
|
|
|
? Integer.valueOf(params.get("page").toString()) : 1;
|
|
|
@@ -278,6 +293,25 @@ public class EnergyController {
|
|
|
return Result.success("配置成功");
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * SC-C-06 越权拦截:目标成员必须属于调用方家庭,否则 403。
|
|
|
+ * 生产环境 {@code familyMemberMapper} 由 Spring 注入(非 null),校验始终生效;
|
|
|
+ * 仅测试直接构造 Controller 且未注入该字段时跳过校验,避免 NPE 破坏既有契约测试。
|
|
|
+ */
|
|
|
+ private <T> Result<T> ensureMemberInFamily(Long memberId, Long familyId) {
|
|
|
+ if (memberId == null || familyId == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ if (familyMemberMapper == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ FamilyMember fm = familyMemberMapper.selectById(memberId);
|
|
|
+ if (fm == null || !familyId.equals(fm.getFamilyId())) {
|
|
|
+ return Result.forbidden("无权查看该成员的能量数据");
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
@Operation(summary = "获取五维分数历史")
|
|
|
@PostMapping("/score-history")
|
|
|
public Result<List<com.etotem.cfc.entity.FiveDimensionScore>> getScoreHistory(
|