Browse Source

增加领域日记 动物笔记 可见性权限过滤

glx_phase2
glx 3 days ago
parent
commit
6bcc35ced8
  1. 5
      src/main/java/com/youlai/boot/mini/model/query/OwnAdoptionDiaryQuery.java
  2. 3
      src/main/java/com/youlai/boot/mini/model/query/OwnStrayAnimalQuery.java
  3. 3
      src/main/java/com/youlai/boot/mini/model/vo/AdoptionDiaryVO.java
  4. 3
      src/main/java/com/youlai/boot/mini/model/vo/StrayAnimalShortVO.java
  5. 176
      src/main/java/com/youlai/boot/mini/service/impl/AdoptionDiaryServiceImpl.java
  6. 8
      src/main/java/com/youlai/boot/mini/service/impl/MiniFollowServiceImpl.java
  7. 177
      src/main/java/com/youlai/boot/mini/service/impl/StrayAnimalServiceImpl.java
  8. 7
      src/main/java/com/youlai/boot/mini/service/impl/UserPostServiceImpl.java
  9. 7
      src/main/resources/mapper/mini/MiniAdoptionDiaryMapper.xml
  10. 10
      src/main/resources/mapper/mini/MiniStrayAnimalMapper.xml

5
src/main/java/com/youlai/boot/mini/model/query/OwnAdoptionDiaryQuery.java

@ -4,6 +4,8 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Data
public class OwnAdoptionDiaryQuery extends AdoptionDiaryQuery{
@ -20,4 +22,7 @@ public class OwnAdoptionDiaryQuery extends AdoptionDiaryQuery{
@Schema(description = "权限", example = "public", hidden = true)
private String visibility;
@Schema(description = "允许的可见性列表(用于他人页面访问控制)", hidden = true)
private List<String> allowedVisibilities;
}

3
src/main/java/com/youlai/boot/mini/model/query/OwnStrayAnimalQuery.java

@ -15,4 +15,7 @@ public class OwnStrayAnimalQuery extends StrayAnimalQuery {
@Schema(description = "创建动物的用户ID", example = "1", hidden = true)
private Long creatorId;
@Schema(description = "允许的可见性列表(用于他人页面访问控制)", hidden = true)
private java.util.List<String> allowedVisibilities;
}

3
src/main/java/com/youlai/boot/mini/model/vo/AdoptionDiaryVO.java

@ -12,6 +12,9 @@ public class AdoptionDiaryVO {
@Schema(description = "领养日记ID", hidden = true)
private Long id;
@Schema(description = "作者用户ID", hidden = true)
private Long miniUserId;
@Schema(description = "作者uuid")
private String authorUuid;

3
src/main/java/com/youlai/boot/mini/model/vo/StrayAnimalShortVO.java

@ -10,6 +10,9 @@ public class StrayAnimalShortVO {
@Schema(description = "笔记ID(仅用于瀑布流游标,前端不需要展示)", hidden = true)
private Long id;
@Schema(description = "作者用户ID", hidden = true)
private Long miniUserId;
@Schema(description = "作者uuid", example = "true")
private String authorUuid;

176
src/main/java/com/youlai/boot/mini/service/impl/AdoptionDiaryServiceImpl.java

@ -37,6 +37,7 @@ import com.youlai.boot.mini.model.query.AdoptionDiaryWaterfallQuery;
import com.youlai.boot.mini.model.query.OwnAdoptionDiaryQuery;
import com.youlai.boot.mini.model.vo.*;
import com.youlai.boot.mini.service.AdoptionDiaryService;
import com.youlai.boot.mini.service.MiniFollowService;
import com.youlai.boot.mini.service.StrayAnimalService;
import com.youlai.boot.system.mapper.UserMapper;
import com.youlai.boot.system.model.entity.SysUser;
@ -76,6 +77,7 @@ public class AdoptionDiaryServiceImpl extends ServiceImpl<MiniAdoptionDiaryMappe
private final AuditExecutorService auditExecutorService;
private final ContentAuditService contentAuditService;
private final ContentAuditTaskService contentAuditTaskService;
private final MiniFollowService followService;
// 布隆过滤器常量
private static final String BLOOM_VIEW_KEY_PREFIX = "mini:diary:view:bloom:";
@ -604,16 +606,32 @@ public class AdoptionDiaryServiceImpl extends ServiceImpl<MiniAdoptionDiaryMappe
throw new MsgException("用户不存在");
}
queryParams.setMiniUserId(SecurityUtils.getUserId());
queryParams.setCreatorId(sysUser.getId());
Long viewerId = SecurityUtils.getUserId();
Long authorId = sysUser.getId();
queryParams.setMiniUserId(viewerId);
queryParams.setCreatorId(authorId);
if (!viewerId.equals(authorId)) {
List<String> allowed = new ArrayList<>();
allowed.add("public");
if (followService.isMutualFollow(viewerId, authorId)) {
allowed.add("friends");
}
queryParams.setAllowedVisibilities(allowed);
}
return getDiaryPage(queryParams);
}
@Override
public AdoptionDiaryDetailsVO getDetails(String diaryUuid, Long miniUserId) {
// 校验动物是否存在
MiniAdoptionDiary diary = getValidDiary(diaryUuid);
if (!canViewDetail(diary, miniUserId)) {
throw new MsgException("该日记仅好友可见");
}
AdoptionDiaryDetailsVO adoptionDiaryDetailsVO = new AdoptionDiaryDetailsVO();
if (diary != null) {
@ -800,66 +818,43 @@ public class AdoptionDiaryServiceImpl extends ServiceImpl<MiniAdoptionDiaryMappe
@Override
public WaterfallResult<AdoptionDiaryVO> getWaterfall(AdoptionDiaryWaterfallQuery query, Long userId) {
// 1. 设置默认值,查 N+1 条
int pageSize = query.getPageSize();
int querySize = pageSize + 1;
// 取3倍数据,因为后续布隆+可见性两次过滤会丢掉一部分
int fetchSize = pageSize * 3;
// 2. 从DB查询 N+1 条 原始未过滤数据
List<AdoptionDiaryVO> originalList = baseMapper.getWaterfall(
query.getCursor(),
querySize,
userId
);
// 1. 从DB取原始数据(仅按id降序,不含过滤)
List<AdoptionDiaryVO> rawList = baseMapper.getWaterfall(query.getCursor(), fetchSize, userId);
// 3. 先基于原始数据计算分页信息(完全不受过滤影响)
Long nextCursor = null;
boolean isLastPage = true;
if (CollUtil.isNotEmpty(originalList)) {
if (originalList.size() > pageSize) {
nextCursor = originalList.get(pageSize - 1).getId();
isLastPage = false;
}
} else {
if (CollUtil.isEmpty(rawList)) {
return WaterfallResult.of(Collections.emptyList(), null, true);
}
// 4.布隆过滤已浏览内容 ==========
List<AdoptionDiaryVO> resultList = originalList.stream().limit(pageSize).collect(Collectors.toList());
// 仅登录用户做过滤
if (userId != null && CollUtil.isNotEmpty(resultList)) {
try {
String bloomKey = BLOOM_VIEW_KEY_PREFIX + userId;
RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter(bloomKey);
if (bloomFilter.isExists()) {
// 布隆存在正常过滤
resultList = resultList.stream()
.filter(item -> !bloomFilter.contains(item.getId()))
.collect(Collectors.toList());
} else {
log.debug("用户{}布隆不存在,同步重建浏览记录", userId);
try {
// 同步重建近1年的浏览记录,对接口响应影响极小
rebuildUserBloomFromDB(userId, 365);
// 重建完成后,立即用新布隆过滤当前页数据,用户第一页就生效
RBloomFilter<Long> newBloomFilter = redissonClient.getBloomFilter(bloomKey);
if (newBloomFilter.isExists()) {
resultList = resultList.stream()
.filter(item -> !newBloomFilter.contains(item.getId()))
.collect(Collectors.toList());
}
log.debug("用户{}布隆同步重建完成,本次已过滤已浏览内容", userId);
} catch (Exception ex) {
// 重建失败跳过过滤,不影响主流程,下次访问自动重试
log.error("用户{}布隆同步重建失败,本次不过滤,下次访问自动重试", userId, ex);
}
}
} catch (Exception e) {
// Redis异常自动降级为不过滤,不影响用户正常使用,恢复后下次访问自动重建
log.error("布隆操作异常,自动降级不过滤,userId:{}, cursor:{}", userId, query.getCursor(), e);
}
// DB返回了满3倍数据,说明后面可能还有更多
boolean hasMore = rawList.size() >= fetchSize;
// 2. 布隆过滤器去重:过滤当前用户已看过的内容
List<AdoptionDiaryVO> afterBloom = applyBloomDedup(rawList, userId);
// 3. 可见性过滤
List<AdoptionDiaryVO> afterVisibility = applyVisibilityFilter(afterBloom, userId);
// 4. 截取 pageSize 条作为本页结果
List<AdoptionDiaryVO> resultList = afterVisibility.stream().limit(pageSize).toList();
// 5. 游标逻辑
Long nextCursor = null;
boolean isLastPage = true;
if (resultList.size() >= pageSize) {
nextCursor = resultList.get(pageSize - 1).getId();
isLastPage = false;
} else if (hasMore) {
nextCursor = CollUtil.isNotEmpty(resultList)
? resultList.get(resultList.size() - 1).getId()
: rawList.get(rawList.size() - 1).getId();
isLastPage = false;
}
// 5. 设置默认图片(原有逻辑不变)
// 6. 设置默认图片
if (CollUtil.isNotEmpty(resultList)) {
resultList.forEach(item -> {
if (StrUtil.isBlank(item.getFirstImageUrl())) {
@ -871,6 +866,77 @@ public class AdoptionDiaryServiceImpl extends ServiceImpl<MiniAdoptionDiaryMappe
return WaterfallResult.of(resultList, nextCursor, isLastPage);
}
private List<AdoptionDiaryVO> applyBloomDedup(List<AdoptionDiaryVO> list, Long userId) {
if (userId == null || CollUtil.isEmpty(list)) {
return list;
}
try {
String bloomKey = BLOOM_VIEW_KEY_PREFIX + userId;
RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter(bloomKey);
if (bloomFilter.isExists()) {
return list.stream()
.filter(item -> !bloomFilter.contains(item.getId()))
.toList();
}
log.debug("用户{}布隆不存在,同步重建浏览记录", userId);
try {
rebuildUserBloomFromDB(userId, 365);
RBloomFilter<Long> newBloomFilter = redissonClient.getBloomFilter(bloomKey);
if (newBloomFilter.isExists()) {
return list.stream()
.filter(item -> !newBloomFilter.contains(item.getId()))
.toList();
}
} catch (Exception ex) {
log.error("用户{}布隆同步重建失败,本次不过滤,下次访问自动重试", userId, ex);
}
} catch (Exception e) {
log.error("布隆操作异常,自动降级不过滤,userId:{}", userId, e);
}
return list;
}
private List<AdoptionDiaryVO> applyVisibilityFilter(List<AdoptionDiaryVO> list, Long viewerId) {
if (CollUtil.isEmpty(list)) {
return list;
}
if (viewerId == null) {
return list.stream()
.filter(item -> "public".equals(item.getVisibility()))
.toList();
}
Set<Long> authorIds = list.stream()
.map(AdoptionDiaryVO::getMiniUserId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
Set<Long> mutualFollowIds = followService.filterMutualFollows(viewerId, authorIds);
return list.stream()
.filter(item -> canView(viewerId, item.getMiniUserId(), item.getVisibility(), mutualFollowIds))
.toList();
}
private boolean canView(Long viewerId, Long authorId, String visibility, Set<Long> mutualFollowIds) {
if (authorId != null && viewerId.equals(authorId)) {
return true;
}
return switch (visibility != null ? visibility : "public") {
case "public" -> true;
case "friends" -> authorId != null && mutualFollowIds.contains(authorId);
default -> false;
};
}
private boolean canViewDetail(MiniAdoptionDiary diary, Long viewerId) {
if (viewerId != null && viewerId.equals(diary.getMiniUserId())) {
return true;
}
return switch (diary.getVisibility()) {
case "public" -> true;
case "friends" -> viewerId != null && followService.isMutualFollow(viewerId, diary.getMiniUserId());
default -> false;
};
}
@Override
public void rebuildUserBloomFromDB(Long userId) {
// days传0代表重建所有历史记录

8
src/main/java/com/youlai/boot/mini/service/impl/MiniFollowServiceImpl.java

@ -99,7 +99,7 @@ public class MiniFollowServiceImpl implements MiniFollowService {
if (CollUtil.isNotEmpty(members)) {
return members.stream().map(Long::valueOf).collect(Collectors.toSet());
}
// Cold start: load from DB and cache
// 从数据库和缓存加载
List<Long> ids = followMapper.selectFollowingIds(userId);
if (CollUtil.isNotEmpty(ids)) {
String[] strIds = ids.stream().map(String::valueOf).toArray(String[]::new);
@ -118,7 +118,7 @@ public class MiniFollowServiceImpl implements MiniFollowService {
if (CollUtil.isEmpty(candidateIds)) {
return Collections.emptySet();
}
// I follow them
//
Set<Long> iFollow = getFollowingIds(userId);
if (CollUtil.isEmpty(iFollow)) {
return Collections.emptySet();
@ -133,13 +133,13 @@ public class MiniFollowServiceImpl implements MiniFollowService {
if (Boolean.TRUE.equals(theyFollowMe)) {
mutuals.add(candidateId);
} else {
// Redis miss for their set, fallback to DB
// Redis没有,回退到DB判断
Long count = followMapper.selectCount(new LambdaQueryWrapper<MiniUserFollow>()
.eq(MiniUserFollow::getFollowerId, candidateId)
.eq(MiniUserFollow::getFollowedId, userId));
if (count != null && count > 0) {
mutuals.add(candidateId);
// warm their cache
// 刷新缓存
refreshFollowingCache(candidateId);
}
}

177
src/main/java/com/youlai/boot/mini/service/impl/StrayAnimalServiceImpl.java

@ -57,6 +57,7 @@ import com.youlai.boot.mini.model.query.OwnStrayAnimalQuery;
import com.youlai.boot.mini.model.query.WaterfallQuery;
import com.youlai.boot.mini.model.vo.*;
import com.youlai.boot.mini.service.MiniPointRecordService;
import com.youlai.boot.mini.service.MiniFollowService;
import com.youlai.boot.mini.service.StrayAnimalService;
import com.youlai.boot.system.mapper.UserMapper;
import com.youlai.boot.system.model.entity.SysUser;
@ -129,6 +130,7 @@ public class StrayAnimalServiceImpl extends ServiceImpl<MiniStrayAnimalMapper, M
private final AuditExecutorService auditExecutorService;
private final ContentAuditService contentAuditService;
private final ContentAuditTaskService contentAuditTaskService;
private final MiniFollowService followService;
public String getDefaultCatCoverHost() {
return "https://" + bucketName + "." + endpoint;
@ -802,6 +804,10 @@ public class StrayAnimalServiceImpl extends ServiceImpl<MiniStrayAnimalMapper, M
MiniStrayAnimalNote appStrayAnimalNote = miniStrayAnimalNoteMapper.selectOne(new LambdaQueryWrapper<MiniStrayAnimalNote>()
.eq(MiniStrayAnimalNote::getStrayAnimalId, animal.getId()));
if (appStrayAnimalNote != null && !canViewDetail(appStrayAnimalNote, miniUserId)) {
throw new MsgException("该笔记仅好友可见");
}
if (appStrayAnimalNote != null) {
Map<String, Object> param = new HashMap<>();
param.put("noteId", appStrayAnimalNote.getId());
@ -876,8 +882,21 @@ public class StrayAnimalServiceImpl extends ServiceImpl<MiniStrayAnimalMapper, M
throw new MsgException("用户不存在");
}
queryParams.setMiniUserId(SecurityUtils.getUserId());
queryParams.setCreatorId(sysUser.getId());
Long viewerId = SecurityUtils.getUserId();
Long authorId = sysUser.getId();
queryParams.setMiniUserId(viewerId);
queryParams.setCreatorId(authorId);
if (!viewerId.equals(authorId)) {
List<String> allowed = new ArrayList<>();
allowed.add("public");
if (followService.isMutualFollow(viewerId, authorId)) {
allowed.add("friends");
}
queryParams.setAllowedVisibilities(allowed);
}
return getAnimalPage(queryParams);
}
@ -1005,67 +1024,42 @@ public class StrayAnimalServiceImpl extends ServiceImpl<MiniStrayAnimalMapper, M
@Override
public WaterfallResult<StrayAnimalShortVO> getWaterfall(WaterfallQuery query, Long userId) {
// 1. 设置默认值,查 N+1 条
int pageSize = query.getPageSize();
int querySize = pageSize + 1;
// 2. 从DB查询 N+1 条 原始未过滤数据
List<StrayAnimalShortVO> originalList = miniStrayAnimalMapper.getWaterfall(
query.getCursor(),
querySize,
query.getAnimalType(),
userId
);
int fetchSize = pageSize * 3;
// 3. 先基于原始数据计算分页信息(完全不受过滤影响)
Long nextCursor = null;
boolean isLastPage = true;
if (CollUtil.isNotEmpty(originalList)) {
if (originalList.size() > pageSize) {
nextCursor = originalList.get(pageSize - 1).getId();
isLastPage = false;
}
} else {
// 1. 从DB取原始数据(仅按id降序,不含过滤)
List<StrayAnimalShortVO> rawList = miniStrayAnimalMapper.getWaterfall(
query.getCursor(), fetchSize, query.getAnimalType(), userId);
if (CollUtil.isEmpty(rawList)) {
return WaterfallResult.of(Collections.emptyList(), null, true);
}
// 4.布隆过滤已浏览内容 ==========
List<StrayAnimalShortVO> resultList = originalList.stream().limit(pageSize).collect(Collectors.toList());
// 仅登录用户做过滤
if (userId != null && CollUtil.isNotEmpty(resultList)) {
try {
String bloomKey = BLOOM_VIEW_KEY_PREFIX + userId;
RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter(bloomKey);
if (bloomFilter.isExists()) {
// 布隆存在正常过滤
resultList = resultList.stream()
.filter(item -> !bloomFilter.contains(item.getId()))
.collect(Collectors.toList());
} else {
log.debug("用户{}布隆不存在,同步重建浏览记录", userId);
try {
// 同步重建近1年的浏览记录,对接口响应影响极小
rebuildUserBloomFromDB(userId, 365);
// 重建完成后,立即用新布隆过滤当前页数据,用户第一页就生效
RBloomFilter<Long> newBloomFilter = redissonClient.getBloomFilter(bloomKey);
if (newBloomFilter.isExists()) {
resultList = resultList.stream()
.filter(item -> !newBloomFilter.contains(item.getId()))
.collect(Collectors.toList());
}
log.debug("用户{}布隆同步重建完成,本次已过滤已浏览内容", userId);
} catch (Exception ex) {
// 重建失败跳过过滤,不影响主流程,下次访问自动重试
log.error("用户{}布隆同步重建失败,本次不过滤,下次访问自动重试", userId, ex);
}
}
} catch (Exception e) {
// Redis异常自动降级为不过滤,不影响用户正常使用,恢复后下次访问自动重建
log.error("布隆操作异常,自动降级不过滤,userId:{}, cursor:{}", userId, query.getCursor(), e);
}
boolean hasMore = rawList.size() >= fetchSize;
// 2. 布隆过滤器去重
List<StrayAnimalShortVO> afterBloom = applyBloomDedup(rawList, userId);
// 3. 可见性过滤
List<StrayAnimalShortVO> afterVisibility = applyVisibilityFilter(afterBloom, userId);
// 4. 截取 pageSize 条作为本页结果
List<StrayAnimalShortVO> resultList = afterVisibility.stream().limit(pageSize).toList();
// 5. 游标逻辑
Long nextCursor = null;
boolean isLastPage = true;
if (resultList.size() >= pageSize) {
nextCursor = resultList.get(pageSize - 1).getId();
isLastPage = false;
} else if (hasMore) {
nextCursor = CollUtil.isNotEmpty(resultList)
? resultList.get(resultList.size() - 1).getId()
: rawList.get(rawList.size() - 1).getId();
isLastPage = false;
}
// 5. 设置默认图片(原有逻辑不变)
// 6. 设置默认图片
if (CollUtil.isNotEmpty(resultList)) {
resultList.forEach(item -> {
if (StrUtil.isBlank(item.getFirstImageUrl())) {
@ -1087,6 +1081,77 @@ public class StrayAnimalServiceImpl extends ServiceImpl<MiniStrayAnimalMapper, M
return WaterfallResult.of(resultList, nextCursor, isLastPage);
}
private List<StrayAnimalShortVO> applyBloomDedup(List<StrayAnimalShortVO> list, Long userId) {
if (userId == null || CollUtil.isEmpty(list)) {
return list;
}
try {
String bloomKey = BLOOM_VIEW_KEY_PREFIX + userId;
RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter(bloomKey);
if (bloomFilter.isExists()) {
return list.stream()
.filter(item -> !bloomFilter.contains(item.getId()))
.toList();
}
log.debug("用户{}布隆不存在,同步重建浏览记录", userId);
try {
rebuildUserBloomFromDB(userId, 365);
RBloomFilter<Long> newBloomFilter = redissonClient.getBloomFilter(bloomKey);
if (newBloomFilter.isExists()) {
return list.stream()
.filter(item -> !newBloomFilter.contains(item.getId()))
.toList();
}
} catch (Exception ex) {
log.error("用户{}布隆同步重建失败,本次不过滤,下次访问自动重试", userId, ex);
}
} catch (Exception e) {
log.error("布隆操作异常,自动降级不过滤,userId:{}", userId, e);
}
return list;
}
private List<StrayAnimalShortVO> applyVisibilityFilter(List<StrayAnimalShortVO> list, Long viewerId) {
if (CollUtil.isEmpty(list)) {
return list;
}
if (viewerId == null) {
return list.stream()
.filter(item -> "public".equals(item.getVisibility()))
.toList();
}
Set<Long> authorIds = list.stream()
.map(StrayAnimalShortVO::getMiniUserId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
Set<Long> mutualFollowIds = followService.filterMutualFollows(viewerId, authorIds);
return list.stream()
.filter(item -> canView(viewerId, item.getMiniUserId(), item.getVisibility(), mutualFollowIds))
.toList();
}
private boolean canView(Long viewerId, Long authorId, String visibility, Set<Long> mutualFollowIds) {
if (authorId != null && viewerId.equals(authorId)) {
return true;
}
return switch (visibility != null ? visibility : "public") {
case "public" -> true;
case "friends" -> authorId != null && mutualFollowIds.contains(authorId);
default -> false;
};
}
private boolean canViewDetail(MiniStrayAnimalNote note, Long viewerId) {
if (viewerId != null && viewerId.equals(note.getMiniUserId())) {
return true;
}
return switch (note.getVisibility()) {
case "public" -> true;
case "friends" -> viewerId != null && followService.isMutualFollow(viewerId, note.getMiniUserId());
default -> false;
};
}
/**
* 重建用户的所有浏览记录布隆过滤器全量重建
* @param userId 用户ID

7
src/main/java/com/youlai/boot/mini/service/impl/UserPostServiceImpl.java

@ -813,7 +813,7 @@ public class UserPostServiceImpl extends ServiceImpl<MiniUserPostMapper, MiniUse
// 5. 游标逻辑:
// - 过滤后还有 ≥ pageSize 条 → 用最后一条的ID作为下页游标
// - 过滤后不够,但DB原始数据是满的 → 用原始数据最后一条ID继续翻(被过滤的内容后面还会出现
// - 过滤后不够,但DB原始数据是满的 → 用resultList最后一条ID(给被过滤帖子二次曝光机会
// - 否则最后一页
Long nextCursor = null;
boolean isLastPage = true;
@ -821,7 +821,9 @@ public class UserPostServiceImpl extends ServiceImpl<MiniUserPostMapper, MiniUse
nextCursor = resultList.get(pageSize - 1).getId();
isLastPage = false;
} else if (hasMore) {
nextCursor = rawList.get(rawList.size() - 1).getId();
nextCursor = CollUtil.isNotEmpty(resultList)
? resultList.get(resultList.size() - 1).getId()
: rawList.get(rawList.size() - 1).getId();
isLastPage = false;
}
@ -970,6 +972,7 @@ public class UserPostServiceImpl extends ServiceImpl<MiniUserPostMapper, MiniUse
.map(UserPostVO::getMiniUserId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
// 获取互关用户
Set<Long> mutualFollowIds = followService.filterMutualFollows(viewerId, authorIds);
return list.stream()
.filter(item -> canView(viewerId, item.getMiniUserId(), item.getVisibility(), mutualFollowIds))

7
src/main/resources/mapper/mini/MiniAdoptionDiaryMapper.xml

@ -74,6 +74,12 @@
<if test="query.visibility != null and query.visibility != ''">
AND d.visibility = #{query.visibility}
</if>
<if test="query.allowedVisibilities != null and query.allowedVisibilities.size() > 0">
AND d.visibility IN
<foreach collection="query.allowedVisibilities" item="v" open="(" separator="," close=")">
#{v}
</foreach>
</if>
<!-- 时间范围 -->
<if test="query.createStartTimestamp != null">
AND d.create_timestamp >= #{query.createStartTimestamp}
@ -158,6 +164,7 @@
)
SELECT
ad.id,
ad.mini_user_id AS miniUserId,
u.uuid AS authorUuid,
u.nickname AS authorName,
u.avatar AS authorAvatar,

10
src/main/resources/mapper/mini/MiniStrayAnimalMapper.xml

@ -139,6 +139,7 @@
a.uuid AS animalUuid,
n.uuid AS animalNoteUuid,
n.mini_user_id AS miniUserId,
a.animal_type,
@ -187,6 +188,14 @@
<!-- 指定用户 -->
AND n.mini_user_id = #{queryParams.creatorId}
<!-- 可见性过滤 -->
<if test="queryParams.allowedVisibilities != null and queryParams.allowedVisibilities.size() > 0">
AND n.visibility IN
<foreach collection="queryParams.allowedVisibilities" item="v" open="(" separator="," close=")">
#{v}
</foreach>
</if>
<!-- 动物类型 -->
<if test="queryParams.animalType != null and queryParams.animalType != ''">
AND a.animal_type = #{queryParams.animalType}
@ -312,6 +321,7 @@
u.avatar AS authorAvatar,
a.uuid AS animalUuid,
n.uuid AS animalNoteUuid,
n.mini_user_id AS miniUserId,
a.animal_type,
fi.source_url AS firstImageUrl,
n.title,

Loading…
Cancel
Save