From c0132e5eef39de92d5c6e3b9e688d1fb29e71358 Mon Sep 17 00:00:00 2001 From: glx <783262171@qq.com> Date: Wed, 10 Jun 2026 15:14:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B8=B8=E6=A0=87=E5=88=86=E9=A1=B5=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E5=8A=A0=E5=AF=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boot/common/util/CursorEncryptUtil.java | 24 +++++++++++++++++++ .../query/AdoptionDiaryWaterfallQuery.java | 4 ++-- .../model/query/UserPostWaterfallQuery.java | 4 ++-- .../boot/mini/model/query/WaterfallQuery.java | 4 ++-- .../boot/mini/model/vo/WaterfallResult.java | 6 ++--- .../impl/AdoptionDiaryServiceImpl.java | 11 +++++---- .../service/impl/StrayAnimalServiceImpl.java | 11 +++++---- .../service/impl/UserPostServiceImpl.java | 16 ++++++------- 8 files changed, 52 insertions(+), 28 deletions(-) create mode 100644 src/main/java/com/youlai/boot/common/util/CursorEncryptUtil.java diff --git a/src/main/java/com/youlai/boot/common/util/CursorEncryptUtil.java b/src/main/java/com/youlai/boot/common/util/CursorEncryptUtil.java new file mode 100644 index 0000000..833a84f --- /dev/null +++ b/src/main/java/com/youlai/boot/common/util/CursorEncryptUtil.java @@ -0,0 +1,24 @@ +package com.youlai.boot.common.util; + +import cn.hutool.crypto.SecureUtil; +import cn.hutool.crypto.symmetric.AES; + +/** + * 瀑布流游标加解密,使用 AES 对称加密,隐藏内部自增主键 ID。 + */ +public class CursorEncryptUtil { + + private static final byte[] AES_KEY = "CrsrK3y!2026_StrayAnimals_Proj".getBytes(java.nio.charset.StandardCharsets.UTF_8); + + private static final AES AES = SecureUtil.aes(AES_KEY); + + public static String encode(Long id) { + if (id == null) return null; + return AES.encryptBase64(String.valueOf(id)); + } + + public static Long decode(String cursor) { + if (cursor == null || cursor.isBlank()) return null; + return Long.parseLong(AES.decryptStr(cursor)); + } +} diff --git a/src/main/java/com/youlai/boot/mini/model/query/AdoptionDiaryWaterfallQuery.java b/src/main/java/com/youlai/boot/mini/model/query/AdoptionDiaryWaterfallQuery.java index 094e329..25250a0 100644 --- a/src/main/java/com/youlai/boot/mini/model/query/AdoptionDiaryWaterfallQuery.java +++ b/src/main/java/com/youlai/boot/mini/model/query/AdoptionDiaryWaterfallQuery.java @@ -7,7 +7,7 @@ import lombok.Data; @Data public class AdoptionDiaryWaterfallQuery extends BaseQuery { - @Schema(description = "游标(上一页最后一条的ID,首次请求不传)", example = "100") - private Long cursor; + @Schema(description = "游标(上一页返回的nextCursor,首次请求不传)", example = "xY3kA9zW") + private String cursor; } diff --git a/src/main/java/com/youlai/boot/mini/model/query/UserPostWaterfallQuery.java b/src/main/java/com/youlai/boot/mini/model/query/UserPostWaterfallQuery.java index 8733da0..32585c0 100644 --- a/src/main/java/com/youlai/boot/mini/model/query/UserPostWaterfallQuery.java +++ b/src/main/java/com/youlai/boot/mini/model/query/UserPostWaterfallQuery.java @@ -7,7 +7,7 @@ import lombok.Data; @Data public class UserPostWaterfallQuery extends BaseQuery { - @Schema(description = "游标(上一页最后一条的ID,首次请求不传)", example = "100") - private Long cursor; + @Schema(description = "游标(上一页返回的nextCursor,首次请求不传)", example = "xY3kA9zW") + private String cursor; } diff --git a/src/main/java/com/youlai/boot/mini/model/query/WaterfallQuery.java b/src/main/java/com/youlai/boot/mini/model/query/WaterfallQuery.java index d43b5d1..6607d1e 100644 --- a/src/main/java/com/youlai/boot/mini/model/query/WaterfallQuery.java +++ b/src/main/java/com/youlai/boot/mini/model/query/WaterfallQuery.java @@ -10,8 +10,8 @@ import lombok.Data; @Schema(description = "瀑布流查询参数") public class WaterfallQuery extends BaseQuery { - @Schema(description = "游标(上一页最后一条的ID,首次请求不传)", example = "100") - private Long cursor; + @Schema(description = "游标(上一页返回的nextCursor,首次请求不传)", example = "xY3kA9zW") + private String cursor; @Schema(description = "动物类型筛选(cat-猫 dog-狗 other-其他)", example = "cat") private String animalType; diff --git a/src/main/java/com/youlai/boot/mini/model/vo/WaterfallResult.java b/src/main/java/com/youlai/boot/mini/model/vo/WaterfallResult.java index 7e86135..bc92019 100644 --- a/src/main/java/com/youlai/boot/mini/model/vo/WaterfallResult.java +++ b/src/main/java/com/youlai/boot/mini/model/vo/WaterfallResult.java @@ -12,13 +12,13 @@ public class WaterfallResult { @Schema(description = "数据列表") private List list; - @Schema(description = "下一页游标(传null表示没有更多了)", example = "100") - private Long nextCursor; + @Schema(description = "下一页游标(传null表示没有更多了)", example = "xY3kA9zW") + private String nextCursor; @Schema(description = "是否最后一页", example = "false") private Boolean isLastPage; - public static WaterfallResult of(List list, Long nextCursor, boolean isLastPage) { + public static WaterfallResult of(List list, String nextCursor, boolean isLastPage) { WaterfallResult result = new WaterfallResult<>(); result.setList(list); result.setNextCursor(nextCursor); diff --git a/src/main/java/com/youlai/boot/mini/service/impl/AdoptionDiaryServiceImpl.java b/src/main/java/com/youlai/boot/mini/service/impl/AdoptionDiaryServiceImpl.java index ed77532..5f61c66 100644 --- a/src/main/java/com/youlai/boot/mini/service/impl/AdoptionDiaryServiceImpl.java +++ b/src/main/java/com/youlai/boot/mini/service/impl/AdoptionDiaryServiceImpl.java @@ -19,6 +19,7 @@ import com.youlai.boot.admin.service.ContentAuditConfigService; import com.youlai.boot.admin.service.ContentAuditService; import com.youlai.boot.admin.service.ContentAuditTaskService; import com.youlai.boot.common.exception.MsgException; +import com.youlai.boot.common.util.CursorEncryptUtil; import com.youlai.boot.common.util.FileUtils; import com.youlai.boot.common.util.JavaVCUtils; import com.youlai.boot.common.util.RandomNumberUtils; @@ -823,7 +824,7 @@ public class AdoptionDiaryServiceImpl extends ServiceImpl rawList = baseMapper.getWaterfall(query.getCursor(), fetchSize, userId); + List rawList = baseMapper.getWaterfall(CursorEncryptUtil.decode(query.getCursor()), fetchSize, userId); if (CollUtil.isEmpty(rawList)) { return WaterfallResult.of(Collections.emptyList(), null, true); @@ -842,13 +843,13 @@ public class AdoptionDiaryServiceImpl extends ServiceImpl resultList = afterVisibility.stream().limit(pageSize).toList(); // 5. 游标逻辑 - Long nextCursor = null; + Long nextCursorId = null; boolean isLastPage = true; if (resultList.size() >= pageSize) { - nextCursor = resultList.get(pageSize - 1).getId(); + nextCursorId = resultList.get(pageSize - 1).getId(); isLastPage = false; } else if (hasMore) { - nextCursor = CollUtil.isNotEmpty(resultList) + nextCursorId = CollUtil.isNotEmpty(resultList) ? resultList.get(resultList.size() - 1).getId() : rawList.get(rawList.size() - 1).getId(); isLastPage = false; @@ -863,7 +864,7 @@ public class AdoptionDiaryServiceImpl extends ServiceImpl applyBloomDedup(List list, Long userId) { diff --git a/src/main/java/com/youlai/boot/mini/service/impl/StrayAnimalServiceImpl.java b/src/main/java/com/youlai/boot/mini/service/impl/StrayAnimalServiceImpl.java index 3636e7f..8efcf3d 100644 --- a/src/main/java/com/youlai/boot/mini/service/impl/StrayAnimalServiceImpl.java +++ b/src/main/java/com/youlai/boot/mini/service/impl/StrayAnimalServiceImpl.java @@ -23,6 +23,7 @@ import com.youlai.boot.common.constant.CommonConstants; import com.youlai.boot.common.exception.MsgException; import com.youlai.boot.common.result.Result; import com.youlai.boot.common.util.CoordinateTransformUtils; +import com.youlai.boot.common.util.CursorEncryptUtil; import com.youlai.boot.common.util.FileUtils; import com.youlai.boot.common.util.JavaVCUtils; import com.youlai.boot.common.util.RandomNumberUtils; @@ -1029,7 +1030,7 @@ public class StrayAnimalServiceImpl extends ServiceImpl rawList = miniStrayAnimalMapper.getWaterfall( - query.getCursor(), fetchSize, query.getAnimalType(), userId); + CursorEncryptUtil.decode(query.getCursor()), fetchSize, query.getAnimalType(), userId); if (CollUtil.isEmpty(rawList)) { return WaterfallResult.of(Collections.emptyList(), null, true); @@ -1047,13 +1048,13 @@ public class StrayAnimalServiceImpl extends ServiceImpl resultList = afterVisibility.stream().limit(pageSize).toList(); // 5. 游标逻辑 - Long nextCursor = null; + Long nextCursorId = null; boolean isLastPage = true; if (resultList.size() >= pageSize) { - nextCursor = resultList.get(pageSize - 1).getId(); + nextCursorId = resultList.get(pageSize - 1).getId(); isLastPage = false; } else if (hasMore) { - nextCursor = CollUtil.isNotEmpty(resultList) + nextCursorId = CollUtil.isNotEmpty(resultList) ? resultList.get(resultList.size() - 1).getId() : rawList.get(rawList.size() - 1).getId(); isLastPage = false; @@ -1078,7 +1079,7 @@ public class StrayAnimalServiceImpl extends ServiceImpl applyBloomDedup(List list, Long userId) { diff --git a/src/main/java/com/youlai/boot/mini/service/impl/UserPostServiceImpl.java b/src/main/java/com/youlai/boot/mini/service/impl/UserPostServiceImpl.java index aa67833..fb9f576 100644 --- a/src/main/java/com/youlai/boot/mini/service/impl/UserPostServiceImpl.java +++ b/src/main/java/com/youlai/boot/mini/service/impl/UserPostServiceImpl.java @@ -19,6 +19,7 @@ import com.youlai.boot.admin.service.ContentAuditConfigService; import com.youlai.boot.admin.service.ContentAuditService; import com.youlai.boot.admin.service.ContentAuditTaskService; import com.youlai.boot.common.exception.MsgException; +import com.youlai.boot.common.util.CursorEncryptUtil; import com.youlai.boot.common.util.FileUtils; import com.youlai.boot.common.util.JavaVCUtils; import com.youlai.boot.common.util.RandomNumberUtils; @@ -791,7 +792,7 @@ public class UserPostServiceImpl extends ServiceImpl rawList = baseMapper.getWaterfall(query.getCursor(), fetchSize, userId); + List rawList = baseMapper.getWaterfall(CursorEncryptUtil.decode(query.getCursor()), fetchSize, userId); if (CollUtil.isEmpty(rawList)) { return WaterfallResult.of(Collections.emptyList(), null, true); @@ -811,17 +812,14 @@ public class UserPostServiceImpl extends ServiceImpl resultList = afterVisibility.stream().limit(pageSize).toList(); - // 5. 游标逻辑: - // - 过滤后还有 ≥ pageSize 条 → 用最后一条的ID作为下页游标 - // - 过滤后不够,但DB原始数据是满的 → 用resultList最后一条ID(给被过滤帖子二次曝光机会) - // - 否则最后一页 - Long nextCursor = null; + // 5. 游标逻辑 + Long nextCursorId = null; boolean isLastPage = true; if (resultList.size() >= pageSize) { - nextCursor = resultList.get(pageSize - 1).getId(); + nextCursorId = resultList.get(pageSize - 1).getId(); isLastPage = false; } else if (hasMore) { - nextCursor = CollUtil.isNotEmpty(resultList) + nextCursorId = CollUtil.isNotEmpty(resultList) ? resultList.get(resultList.size() - 1).getId() : rawList.get(rawList.size() - 1).getId(); isLastPage = false; @@ -836,7 +834,7 @@ public class UserPostServiceImpl extends ServiceImpl