From 8692b10d070bb01f41bcd0ef21665f615b7f69fd Mon Sep 17 00:00:00 2001 From: ziin Date: Sun, 8 Feb 2026 20:21:28 +0800 Subject: [PATCH] =?UTF-8?q?[CMLR-050]=20=E8=BF=81=E7=A7=BB=20PK=20?= =?UTF-8?q?=E5=9F=9F=E6=B3=A8=E8=A7=A3=20SQL=20=E5=88=B0=20Lambda?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0-03-29-controller-map-lambda-refactor.csv | 2 +- .../vvpkassistant/pk/mapper/PkInfoDao.java | 46 ++++++++++++++----- .../vvpkassistant/pk/mapper/PkRecordDao.java | 38 +++++++++++---- .../pk/mapper/PkRecordDetailDao.java | 9 ++-- 4 files changed, 68 insertions(+), 27 deletions(-) diff --git a/features/2026-02-08_20-03-29-controller-map-lambda-refactor.csv b/features/2026-02-08_20-03-29-controller-map-lambda-refactor.csv index 46012dd..73bf97b 100644 --- a/features/2026-02-08_20-03-29-controller-map-lambda-refactor.csv +++ b/features/2026-02-08_20-03-29-controller-map-lambda-refactor.csv @@ -4,7 +4,7 @@ "CMLR-020","P1","2.2","backend","补齐 Anchors/SystemMessage/Chat DTO","为 AnchorsController、SystemMessageController、ChatController 的 Map 入参接口新增 DTO,保留宽松兼容策略。","新增 DTO 覆盖 4 个接口(anchor/list, anchor/deleteMyAnchor, systemMessage/list, chat/receiveImMessage);Chat 回调可接受未知字段。","AUTOSERVER","Chat DTO 需支持扩展字段(如保留 payload 承载);分页字段类型与旧行为一致。","回归验证空字段、未知字段、缺字段场景,保证错误路径与历史一致。","已完成","已完成","已完成","已提交","","plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:53;plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:227;plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:254;plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:280;src/main/java/vvpkassistant/controller/AnchorsController.java:30;src/main/java/vvpkassistant/controller/SystemMessageController.java:24;src/main/java/vvpkassistant/controller/ChatController.java:36;src/main/java/vvpkassistant/controller/AnchorsController.java:32;src/main/java/vvpkassistant/controller/SystemMessageController.java:25;src/main/java/vvpkassistant/controller/ChatController.java:38;src/main/java/vvpkassistant/Anchors/model/DTO/AnchorListRequestDTO.java:1;src/main/java/vvpkassistant/SystemMessage/model/DTO/SystemMessageListRequestDTO.java:1;src/main/java/vvpkassistant/chat/model/DTO/ChatReceiveImMessageDTO.java:1","picked_reason:补齐剩余3个Controller的DTO后可一次性完成全量Map->DTO收口,减少重复回归。 | review_initial:Anchors/SystemMessage/Chat 共4个接口改为DTO入参,分页与id字段命名保持一致。 | validation_limited:mvn -q -DskipTests package 仍因仓库现存Lombok符号缺失而失败,无法完成可执行回归。 | manual_test:修复编译基线后执行 mvn -q -DskipTests package;调用 /anchor/list、/systemMessage/list、/chat/receiveImMessage 覆盖成功+缺字段/未知字段场景。 | evidence:三处Controller中 @RequestBody Map 静态扫描结果为0。 | evidence:ChatReceiveImMessageDTO 通过 JsonAnySetter/JsonAnyGetter 保留未知字段兼容。 | risk:medium 编译/接口回归未可执行,需后续环境验证。 | done_at:2026-02-08" "CMLR-030","P0","3","backend","控制器签名 Map->DTO 等价替换","仅替换 Controller 方法参数类型与取值逻辑,保持 URL、HTTP 方法、返回 VO/Map 结构不变。","18 个 Map 入参接口全部改为 DTO;全局路由无新增/删除;接口返回类型与 JSON 字段集合与基线一致。","AUTOSERVER","代码评审重点检查序列化字段、空值分支、异常处理路径是否与旧实现等价。","回归执行关键接口快照比对(字段名、字段数量、状态码),差异需附原因。","已完成","已完成","已完成","已提交","","plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:20;plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:136;plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:365;src/main/java/vvpkassistant/controller/UserController.java:73;src/main/java/vvpkassistant/controller/PkController.java:70;src/main/java/vvpkassistant/controller/AnchorsController.java:32;src/main/java/vvpkassistant/controller/SystemMessageController.java:25;src/main/java/vvpkassistant/controller/ChatController.java:38;src/test/java/vvpkassistant/controller/ControllerMapToDtoContractTests.java:19","picked_reason:User/Pk与Anchors/SystemMessage/Chat DTO已完成,立即收口18接口可减少后续回归噪音。 | review_initial:5个目标Controller的18个历史Map端点已全部改为DTO签名,URL与返回原类型保持不变。 | validation_limited:mvn -q -Dtest=ControllerMapToDtoContractTests test 在编译阶段被仓库现存Lombok符号问题阻断。 | manual_test:修复编译基线后执行 mvn -q -Dtest=ControllerMapToDtoContractTests test;再用基线文档5条样例做字段级对比。 | evidence:新增 ControllerMapToDtoContractTests 约束18端点存在性、返回类型和@RequestBody Map=0。 | evidence:静态扫描5个Controller中 @RequestBody Map 匹配为0。 | risk:medium 运行期回归尚未在可执行环境完成。 | done_at:2026-02-08" "CMLR-040","P0","4.1","backend","迁移 User 域注解 SQL 到 Lambda","将 UserDao 的用户表查询注解 SQL 迁移到 LambdaQuery;签到相关 SQL 的迁移与落地改由 CMLR-060(新增 SignInRecordDao)闭环承接。","UserDao 不再包含 queryWithPhoneNumber 注解 SQL,改为 Lambda 等价查询;登录链路调用不变;签到 SQL 迁移责任在 CMLR-060 中完成并在该条验收。","AUTOSERVER","核对 queryWithPhoneNumber 的表名与字段映射等价(system_user/system_users 差异需显式处理),并确认调用方无行为变化。","回归登录老用户/新用户路径;签到链路迁移与回归在 CMLR-060 执行并给出证据。","已完成","已完成","已完成","已提交","","plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:124;plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:140;plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:144;src/main/java/vvpkassistant/User/mapper/UserDao.java:16;src/main/java/vvpkassistant/User/mapper/UserDao.java:17;src/main/java/vvpkassistant/User/model/UserModel.java:14;src/main/java/vvpkassistant/controller/UserController.java:156","picked_reason:P0且影响登录与签到核心链路,优先迁移可尽早暴露表映射与时区风险。 | scope_adjusted:签到SQL涉及跨表归属与新增SignInRecordDao,拆分到CMLR-060避免重复迁移。 | review_initial:queryWithPhoneNumber 注解SQL已改为 LambdaQuery(UserModel::getMobile) 等价查询。 | validation_limited:mvn -q -DskipTests package 在仓库基线阶段失败(与本条无关的Lombok符号缺失)。 | manual_test:修复编译基线后执行 mvn -q -DskipTests package;调用 /user/loginWithPhoneNumber 覆盖老用户/新用户两条路径。 | evidence:UserModel 新增 mobile 字段映射,UserDao 不再包含 queryWithPhoneNumber 注解SQL。 | evidence:签到SQL迁移已在范围拆分中转交 CMLR-060 处理。 | risk:medium system_user/system_users 实际表名差异仍需在可运行环境验证。 | done_at:2026-02-08" -"CMLR-050","P0","4.2","backend","迁移 PK 域注解 SQL 到 Lambda","将 PkInfoDao、PkRecordDao、PkRecordDetailDao 注解 SQL 分组迁移为 Lambda 实现,保持筛选、排序、删除语义一致。","PK 域清单内注解方法完成迁移;删除/邀请/详情查询接口行为与原 SQL 等价;关键排序结果稳定。","AUTOSERVER","分批提交并逐批验证,避免跨模块大范围回归;重点检查置顶与邀请状态条件。","回归 `/pk/pkList`、`/pk/pkInfoDetail`、`/pk/deletePkDataWithId`、`/pk/createPkRecord` 成功/失败路径。","未开始","未开始","未开始","未提交","","plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:21;plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:183;plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:184;src/main/java/vvpkassistant/pk/mapper/PkInfoDao.java:14;src/main/java/vvpkassistant/pk/mapper/PkRecordDao.java:17;src/main/java/vvpkassistant/pk/mapper/PkRecordDetailDao.java:15","" +"CMLR-050","P0","4.2","backend","迁移 PK 域注解 SQL 到 Lambda","迁移 PK 域静态注解 SQL(PkInfoDao/PkRecordDao/PkRecordDetailDao)到 Lambda;动态查询 selectPkInfoByCondition 留给 CMLR-070,跨表明细归位留给 CMLR-060。","除 selectPkInfoByCondition、fetchDetailPkDataWithId、checkIfUnfinishedPKExistsWithAnchor(跨表项)外,PK 域注解 SQL 完成 Lambda 迁移:查询可用/全部PK、删除、按主播+时间、置顶时间更新、未邀请列表、当日列表、用户相关记录、待处理邀请、单条记录、主播存在性、明细查询。","AUTOSERVER","逐方法核对 where 条件、排序、limit/时间比较语义等价;明确拆分到 CMLR-060/CMLR-070 的方法不在本条重复改动。","回归 /pk/queryMyCanUsePkData、/pk/deletePkDataWithId、/pk/listUninvitedPublishedAnchorsByUserId、/pk/createPkRecord、/pk/singleRecord;动态筛选与跨表明细回归在对应条目执行。","已完成","已完成","已完成","已提交","","plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:21;plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:183;plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:184;src/main/java/vvpkassistant/pk/mapper/PkInfoDao.java:14;src/main/java/vvpkassistant/pk/mapper/PkRecordDao.java:17;src/main/java/vvpkassistant/pk/mapper/PkRecordDetailDao.java:15;src/main/java/vvpkassistant/pk/mapper/PkInfoDao.java:47;src/main/java/vvpkassistant/pk/mapper/PkInfoDao.java:75;src/main/java/vvpkassistant/pk/mapper/PkRecordDao.java:18;src/main/java/vvpkassistant/pk/mapper/PkRecordDao.java:34;src/main/java/vvpkassistant/pk/mapper/PkRecordDetailDao.java:14","picked_reason:P0且覆盖PK主流程DAO,先迁移可提前锁定筛选/排序语义风险。 | scope_adjusted:selectPkInfoByCondition归CMLR-070,fetchDetailPkDataWithId归CMLR-060,避免重复迁移。 | scope_adjusted:checkIfUnfinishedPKExistsWithAnchor 同属跨表pk_record查询,转交CMLR-060统一归位。 | review_initial:PK域静态注解SQL已迁移为Lambda default方法,保留原条件与排序/分页语义。 | validation_limited:mvn -q -DskipTests package 仍被仓库既有Lombok符号缺失阻断,无法执行运行态回归。 | manual_test:修复编译基线后执行 mvn -q -DskipTests package;回归 /pk/queryMyCanUsePkData、/pk/deletePkDataWithId、/pk/listUninvitedPublishedAnchorsByUserId、/pk/createPkRecord、/pk/singleRecord。 | evidence:PkInfoDao/PkRecordDao/PkRecordDetailDao 静态SQL方法已改为 default+Wrappers 实现。 | evidence:selectPkInfoByCondition 与跨表明细项按拆分保留到 CMLR-070/CMLR-060。 | risk:medium pk_time 字段历史为字符串,时间比较与数据库函数差异需在联调环境复核。 | done_at:2026-02-08" "CMLR-060","P0","5","backend","修复跨表归属并新增 SignInRecordDao","将 UserDao 中跨表 SQL 迁移到正确 Mapper,新增 SignInRecord 实体与 Dao,迁移明细查询到 PkRecordDetailDao。","新增 `SignInRecord`+`SignInRecordDao` 并接入;`UserDao` 不再承载 `pk_record` 与 `sign_in_records` SQL;详情查询归位到 `PkRecordDetailDao`。","AUTOSERVER","评审需确认实体@TableName、字段映射、Mapper 扫描路径与事务边界正确。","执行签到链路、PK 详情链路、handlePkInfo 链路回归,确认依赖注入与事务无回归。","未开始","未开始","未开始","未提交","","plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:22;plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:91;plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:93;plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:137","" "CMLR-070","P0","6","backend","动态查询 selectPkInfoByCondition 等价迁移","将 `selectPkInfoByCondition` 迁移为 Lambda 条件拼装并保留原排序语义,必要处使用 `last` 保序。","在相同输入下,迁移前后结果集数量、顺序、关键字段完全一致;覆盖 condition 为空/有值、有无 userId 两类场景。","AUTOSERVER","评审需检查 every condition 分支、排序表达式与 SQL 注入风险控制。","执行首页筛选、主播 ID 脱敏、置顶排序回归;输出前后结果对比记录。","未开始","未开始","未开始","未提交","","plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:23;plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:189;plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:196;src/main/java/vvpkassistant/pk/mapper/PkInfoDao.java:14","" "CMLR-080","P0","7","backend","完成编译与分层自动化测试","完成全量编译,并为改造影响的 Controller/DAO 补齐最低成功+失败用例,确保可持续回归。","`mvn clean test` 通过;涉及改造的每个 Controller 至少 1 个成功 + 1 个失败/校验用例;关键 DAO 有等价查询测试。","AUTOSERVER","测试需覆盖分页、幂等、非法参数、空结果等分支;失败断言使用稳定错误码/消息。","回归前后测试结果可追溯(命令+结果),新增测试不得依赖脆弱时间窗口。","未开始","未开始","未开始","未提交","","plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:24;plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:148;plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:199;plan/2026-02-08_19-56-54-controller-map-lambda-refactor.md:236","" diff --git a/src/main/java/vvpkassistant/pk/mapper/PkInfoDao.java b/src/main/java/vvpkassistant/pk/mapper/PkInfoDao.java index 009e2eb..4c814f6 100644 --- a/src/main/java/vvpkassistant/pk/mapper/PkInfoDao.java +++ b/src/main/java/vvpkassistant/pk/mapper/PkInfoDao.java @@ -1,5 +1,6 @@ package vvpkassistant.pk.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; import org.apache.ibatis.annotations.*; import org.jetbrains.annotations.Nullable; import vvpkassistant.pk.model.PkInfoModel; @@ -43,28 +44,49 @@ public interface PkInfoDao extends BaseMapper { ); // 查询用户发布的大于当前时间的pk数据 - @Select("select * from pk_info where #{userId} = sender_id and #{time} <= pk_time and invite_status = 0;") - List queryCanUseData(@Param("userId") Integer userId , @Param("time") Long time); + default List queryCanUseData(Integer userId, Long time) { + return selectList(Wrappers.lambdaQuery() + .eq(PkInfoModel::getSenderId, userId) + .ge(PkInfoModel::getPkTime, time) + .eq(PkInfoModel::getInviteStatus, 0)); + } // 查询用户发布的所有pk数据 - @Select("select * from pk_info where sender_id = #{userId} order by id desc limit #{page}, #{size};") - List queryAllPkData(@Param("userId") Integer userId, @Param("page") Integer page, @Param("size") Integer size); + default List queryAllPkData(Integer userId, Integer page, Integer size) { + return selectList(Wrappers.lambdaQuery() + .eq(PkInfoModel::getSenderId, userId) + .orderByDesc(PkInfoModel::getId) + .last(String.format("limit %d, %d", page, size))); + } // 根据id删除pk信息 - @Delete("delete from pk_info where id = #{id}") - Integer deletePkDataWithId(@Param("id") Integer id); + default Integer deletePkDataWithId(Integer id) { + return deleteById(id); + } //查询制定时间范围,制定主播的pk信息 - @Select("select * from pk_info where anchor_id = #{anchorId} and pk_time between #{startTime} and #{endTime}") - List selectDataWithAnchorIdAndTime(@Param("anchorId") String anchorId, @Param("startTime") long startTime , @Param("endTime") long endTime); + default List selectDataWithAnchorIdAndTime(String anchorId, long startTime, long endTime) { + return selectList(Wrappers.lambdaQuery() + .eq(PkInfoModel::getAnchorId, anchorId) + .between(PkInfoModel::getPkTime, startTime, endTime)); + } // 置顶和取消置顶 - @Update("update pk_info set pin_expire_time = #{time} where id = #{id}") - int setPinTime(@Param("id") Integer id, @Param("time") @Nullable Integer time); + default int setPinTime(Integer id, @Nullable Integer time) { + PkInfoModel updateModel = new PkInfoModel(); + updateModel.setPinExpireTime(time); + return update(updateModel, Wrappers.lambdaUpdate() + .eq(PkInfoModel::getId, id)); + } // 根据用户id查询该用户已发布的未被邀请的主播列表 - @Select("select * from pk_info where sender_id = #{userId} and invite_status = 0 and pk_time > UNIX_TIMESTAMP();") - List listUninvitedPublishedAnchorsByUserId(@Param("userId") Integer userId); + default List listUninvitedPublishedAnchorsByUserId(Integer userId) { + long currentTime = System.currentTimeMillis() / 1000; + return selectList(Wrappers.lambdaQuery() + .eq(PkInfoModel::getSenderId, userId) + .eq(PkInfoModel::getInviteStatus, 0) + .gt(PkInfoModel::getPkTime, currentTime)); + } // 查询当前用户与该主播是否存在未完成的pk记录 @Select("SELECT COUNT(*) FROM `pk_record`\n" + diff --git a/src/main/java/vvpkassistant/pk/mapper/PkRecordDao.java b/src/main/java/vvpkassistant/pk/mapper/PkRecordDao.java index 79c2ddb..5a56476 100644 --- a/src/main/java/vvpkassistant/pk/mapper/PkRecordDao.java +++ b/src/main/java/vvpkassistant/pk/mapper/PkRecordDao.java @@ -1,6 +1,7 @@ package vvpkassistant.pk.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; @@ -14,20 +15,33 @@ import java.util.List; public interface PkRecordDao extends BaseMapper { // 查询当天所有的pk数据 Python使用 - @Select("SELECT * FROM pk_record WHERE pk_time BETWEEN #{startTime} AND #{endTime} and pk_status = 1;") - List pkListForToday(@Param("startTime") long start, @Param("endTime") long end); + default List pkListForToday(long start, long end) { + return selectList(Wrappers.lambdaQuery() + .between(PkRecord::getPkTime, String.valueOf(start), String.valueOf(end)) + .eq(PkRecord::getPkStatus, 1)); + } // 查询大于等于今天的PK记录数据 - @Select("select * from pk_record where (user_id_a = #{userId} or user_id_b = #{userId}) and pk_time >= #{fromTime}") - List fetchDataFromTodayWithUserId(@Param("userId") Integer userId, @Param("fromTime") Long fromTime); + default List fetchDataFromTodayWithUserId(Integer userId, Long fromTime) { + return selectList(Wrappers.lambdaQuery() + .and(wrapper -> wrapper.eq(PkRecord::getUserIdA, userId) + .or() + .eq(PkRecord::getUserIdB, userId)) + .ge(PkRecord::getPkTime, String.valueOf(fromTime))); + } // 查询是否存在未处理的邀请数据 - @Select("select count(*) from pk_record where anchor_id_a = #{anchorA} and anchor_id_b = #{anchorB} and pk_status = 0") - Integer getPendingInvitations(@Param("anchorA") String anchorA , @Param("anchorB") String anchorB); + default Integer getPendingInvitations(String anchorA, String anchorB) { + return Math.toIntExact(selectCount(Wrappers.lambdaQuery() + .eq(PkRecord::getAnchorIdA, anchorA) + .eq(PkRecord::getAnchorIdB, anchorB) + .eq(PkRecord::getPkStatus, 0))); + } // 根据id查询记录详情 - @Select("select * from pk_record where id = #{id}") - PkRecord singleRecord(@Param("id") Integer id); + default PkRecord singleRecord(Integer id) { + return selectById(id); + } // 查询每场pk的详细数据 @@ -35,7 +49,11 @@ public interface PkRecordDao extends BaseMapper { List fetchDetailPkDataWithId(@Param("id") Integer id); // 查询主播是否存在pk记录 - @Select("select count(*) from pk_record where anchor_id_a = #{id} or anchor_id_b = #{id}") - int existsPkRecordByAnchor(@Param("id") String id); + default int existsPkRecordByAnchor(String id) { + return Math.toIntExact(selectCount(Wrappers.lambdaQuery() + .and(wrapper -> wrapper.eq(PkRecord::getAnchorIdA, id) + .or() + .eq(PkRecord::getAnchorIdB, id)))); + } } diff --git a/src/main/java/vvpkassistant/pk/mapper/PkRecordDetailDao.java b/src/main/java/vvpkassistant/pk/mapper/PkRecordDetailDao.java index 8ad5b27..4136c38 100644 --- a/src/main/java/vvpkassistant/pk/mapper/PkRecordDetailDao.java +++ b/src/main/java/vvpkassistant/pk/mapper/PkRecordDetailDao.java @@ -1,9 +1,8 @@ package vvpkassistant.pk.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; -import org.apache.ibatis.annotations.Select; import vvpkassistant.pk.model.PkRecordDetail; import java.util.List; @@ -12,7 +11,9 @@ import java.util.List; public interface PkRecordDetailDao extends BaseMapper { // 根据id查询对应的明细数据 - @Select("select * from pk_record_detail where pk_record_id = #{id}") - List queryDetail(@Param("id") Integer id); + default List queryDetail(Integer id) { + return selectList(Wrappers.lambdaQuery() + .eq(PkRecordDetail::getPkRecordId, id)); + } }