fix(mapper): 补 5 个「只有声明、无任何 SQL」的方法;新建 Mapper 绑定守卫持续挡住该类缺陷
全仓无 mapper XML、无自定义 ISqlInjector,自定义方法只能靠注解提供 SQL。 这 5 个方法漏写注解,编译期无感知,调用必抛 Invalid bound statement: UserPrivacySettingsMapper.findByUserId —— 隐私设置读取 UserPreferenceMapper.findByUserId —— 推荐引擎 / 匹配资格共 6 处调用 ReviewReasonMapper.findAllEnabled —— 审核原因字典下拉 ReviewReasonMapper.findByCategory ReviewItemMapper.findByReviewTaskId 长期未被发现的原因:候选池为空(分支走不到)+ 测试库根本没有这 4 张表(双重不可达)。 补的 SQL 一律带 AND deleted_at IS NULL —— V037 之后这些表都有软删列, 只看 V002 建表会漏掉软删过滤,制造「读到已删数据」的新缺陷。 test(invariants): MapperBindingGuardTest —— 三用例 ① 绑定守卫:反射遍历 MapperRegistry 全部 Mapper 的声明方法,hasStatement 为假即失败; 按「实体有无主键」语义豁免复合主键实体的 selectById/updateById/deleteById 等 (MP 本就不为无主键实体注入,属正确行为;非类名白名单,故新增复合主键表同样适用)。 ② 探测自检:虚构 statementId 必须为假,防断言静默永真。 ③ 可执行性:插真实数据后真查一遍 —— 绑定存在 ≠ 能跑(本次即借此确认 JsonNode 映射无碍)。 负向注入已验证:移除 ReviewItemMapper 的 @Select → 守卫报恰 1 项缺失, 且可执行性用例抛真实 Invalid bound statement;还原后复跑全绿。 test(db): schema-h2.sql 补 4 张缺失表(user_privacy_settings / user_preferences / review_reasons / review_items),按 V002 + V037 之后的最终态建模(含 id、deleted_at 与 11 个匹配权重列),并保留 chk_item_type / chk_item_status / chk_pref_priority 约束。 docs(plan): 附 B 新增第 12 项;更正第 11 项中我自己上一轮的错误断言 —— 「表无 id 列导致 insert 必崩」不成立:V037 已补齐 id/deleted_at/权重列。 真实缺陷是 updatePrivacySettings 对已存在记录也走 insert(非 upsert)→ 第二次更新撞主键冲突。 方法论:判断表结构必须遍历全部迁移,只看 CREATE TABLE 会得出与生产相反的结论。
This commit is contained in:
+11
@@ -3,11 +3,22 @@ package com.danyang.match.platform.profile.mapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.danyang.match.platform.profile.entity.UserPreference;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Mapper
|
||||
public interface UserPreferenceMapper extends BaseMapper<UserPreference> {
|
||||
|
||||
/**
|
||||
* 按用户读取择偶偏好(推荐引擎打分与匹配资格判定的输入)。
|
||||
*
|
||||
* <p>补于 2026-09-19(Phase 23):本方法此前<b>只有声明、没有任何 SQL</b>,
|
||||
* 调用必抛 {@code Invalid bound statement}。它在推荐链路上有 6 处调用点,
|
||||
* 但因候选池为空而从未被执行到。
|
||||
*
|
||||
* <p>{@code deleted_at IS NULL}:V037 已为 user_preferences 补上软删列。
|
||||
*/
|
||||
@Select("SELECT * FROM user_preferences WHERE user_id = #{userId} AND deleted_at IS NULL LIMIT 1")
|
||||
Optional<UserPreference> findByUserId(String userId);
|
||||
}
|
||||
|
||||
+13
@@ -3,11 +3,24 @@ package com.danyang.match.platform.profile.mapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.danyang.match.platform.profile.entity.UserPrivacySettings;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Mapper
|
||||
public interface UserPrivacySettingsMapper extends BaseMapper<UserPrivacySettings> {
|
||||
|
||||
/**
|
||||
* 按用户读取隐私设置。
|
||||
*
|
||||
* <p>补于 2026-09-19(Phase 23):本方法此前<b>只有声明、没有任何 SQL</b>。
|
||||
* 本项目全仓无 mapper XML、无自定义 {@code ISqlInjector},自定义方法只能靠注解提供 SQL,
|
||||
* 因此调用必抛 {@code Invalid bound statement}。之所以长期未被发现,是因为
|
||||
* 测试环境候选池为空、且测试库<b>根本没有这张表</b>,两条路径都走不到。
|
||||
*
|
||||
* <p>{@code deleted_at IS NULL}:V037 已为 user_privacy_settings 补上软删列,
|
||||
* 不过滤会读到已删记录。
|
||||
*/
|
||||
@Select("SELECT * FROM user_privacy_settings WHERE user_id = #{userId} AND deleted_at IS NULL LIMIT 1")
|
||||
Optional<UserPrivacySettings> findByUserId(String userId);
|
||||
}
|
||||
|
||||
@@ -3,11 +3,20 @@ package com.danyang.match.platform.review.mapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.danyang.match.platform.review.entity.ReviewItem;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface ReviewItemMapper extends BaseMapper<ReviewItem> {
|
||||
|
||||
/**
|
||||
* 按审核任务读取审核项。
|
||||
*
|
||||
* <p>补于 2026-09-19(Phase 23):本方法此前<b>只有声明、没有任何 SQL</b>。
|
||||
*
|
||||
* <p>{@code deleted_at IS NULL}:V037 已为 review_items 补上软删列。
|
||||
*/
|
||||
@Select("SELECT * FROM review_items WHERE review_task_id = #{reviewTaskId} AND deleted_at IS NULL ORDER BY created_at")
|
||||
List<ReviewItem> findByReviewTaskId(String reviewTaskId);
|
||||
}
|
||||
|
||||
+15
@@ -3,13 +3,28 @@ package com.danyang.match.platform.review.mapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.danyang.match.platform.review.entity.ReviewReason;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface ReviewReasonMapper extends BaseMapper<ReviewReason> {
|
||||
|
||||
/**
|
||||
* 按类别读取审核原因字典。
|
||||
*
|
||||
* <p>补于 2026-09-19(Phase 23):本方法此前<b>只有声明、没有任何 SQL</b>。
|
||||
*
|
||||
* <p>{@code deleted_at IS NULL}:V037 已为 review_reasons 补上软删列(该表 V003 初始无此列)。
|
||||
*/
|
||||
@Select("SELECT * FROM review_reasons WHERE category = #{category} AND deleted_at IS NULL ORDER BY sort_order")
|
||||
List<ReviewReason> findByCategory(String category);
|
||||
|
||||
/**
|
||||
* 读取全部启用中的审核原因(审核结论下拉选项)。
|
||||
*
|
||||
* <p>补于 2026-09-19(Phase 23):本方法此前<b>只有声明、没有任何 SQL</b>。
|
||||
*/
|
||||
@Select("SELECT * FROM review_reasons WHERE enabled = TRUE AND deleted_at IS NULL ORDER BY sort_order")
|
||||
List<ReviewReason> findAllEnabled();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user