From 19a4ccfcf9b7ba597ddcfd3f637f8873b4386d0b Mon Sep 17 00:00:00 2001 From: glx <783262171@qq.com> Date: Fri, 22 May 2026 17:31:53 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=A2=86=E5=85=BB=E6=97=A5?= =?UTF-8?q?=E8=AE=B0=E7=82=B9=E8=B5=9E=E6=94=B6=E8=97=8F=E7=80=91=E5=B8=83?= =?UTF-8?q?=E6=B5=81=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../freemarker/MyBatisPlusGenerator.java | 1 - .../controller/AdoptionDiaryController.java | 46 +++- .../controller/StrayAnimalController.java | 2 +- .../MiniAdoptionDiaryCollectMapper.java | 8 +- .../mini/mapper/MiniAdoptionDiaryMapper.java | 12 + .../mapper/MiniAdoptionDiaryViewMapper.java | 6 + .../mini/model/form/DiaryCollectForm.java | 15 ++ .../query/AdoptionDiaryWaterfallQuery.java | 13 + .../mini/service/AdoptionDiaryService.java | 20 +- .../impl/AdoptionDiaryServiceImpl.java | 233 +++++++++++++++++- .../mini/MiniAdoptionDiaryCollectMapper.xml | 32 +++ .../mapper/mini/MiniAdoptionDiaryMapper.xml | 93 +++++++ .../mini/MiniAdoptionDiaryViewMapper.xml | 12 + 13 files changed, 478 insertions(+), 15 deletions(-) create mode 100644 src/main/java/com/youlai/boot/mini/model/form/DiaryCollectForm.java create mode 100644 src/main/java/com/youlai/boot/mini/model/query/AdoptionDiaryWaterfallQuery.java diff --git a/src/main/java/com/youlai/boot/codegen/freemarker/MyBatisPlusGenerator.java b/src/main/java/com/youlai/boot/codegen/freemarker/MyBatisPlusGenerator.java index 394c241..e776058 100644 --- a/src/main/java/com/youlai/boot/codegen/freemarker/MyBatisPlusGenerator.java +++ b/src/main/java/com/youlai/boot/codegen/freemarker/MyBatisPlusGenerator.java @@ -108,7 +108,6 @@ public class MyBatisPlusGenerator { ,new TableConfig("mini_adoption_diary_media", IdType.AUTO, "mini") ,new TableConfig("mini_adoption_diary_view", IdType.AUTO, "mini") ,new TableConfig("mini_adoption_diary_like", IdType.AUTO, "mini") - ,new TableConfig("mini_adoption_diary_like", IdType.AUTO, "mini") ,new TableConfig("mini_adoption_diary_collect", IdType.AUTO, "mini") // ,new TableConfig("mini_stray_animal", IdType.AUTO, "mini") diff --git a/src/main/java/com/youlai/boot/mini/controller/AdoptionDiaryController.java b/src/main/java/com/youlai/boot/mini/controller/AdoptionDiaryController.java index a0e4338..f453691 100644 --- a/src/main/java/com/youlai/boot/mini/controller/AdoptionDiaryController.java +++ b/src/main/java/com/youlai/boot/mini/controller/AdoptionDiaryController.java @@ -8,14 +8,15 @@ import com.youlai.boot.common.result.PageResult; import com.youlai.boot.common.result.Result; import com.youlai.boot.framework.security.util.SecurityUtils; import com.youlai.boot.mini.model.dto.*; -import com.youlai.boot.mini.model.form.AdoptionDiaryForm; -import com.youlai.boot.mini.model.form.DiaryLikeForm; -import com.youlai.boot.mini.model.form.NoteLikeForm; +import com.youlai.boot.mini.model.form.*; +import com.youlai.boot.mini.model.query.AdoptionDiaryWaterfallQuery; import com.youlai.boot.mini.model.query.OwnAdoptionDiaryQuery; import com.youlai.boot.mini.model.query.OwnStrayAnimalQuery; +import com.youlai.boot.mini.model.query.WaterfallQuery; import com.youlai.boot.mini.model.vo.AdoptionDiaryVO; import com.youlai.boot.mini.model.vo.AdoptionDiaryDetailsVO; import com.youlai.boot.mini.model.vo.StrayAnimalShortVO; +import com.youlai.boot.mini.model.vo.WaterfallResult; import com.youlai.boot.mini.service.AdoptionDiaryService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -155,5 +156,44 @@ public class AdoptionDiaryController { return Result.success(result); } + @Operation(summary = "日记收藏/取消收藏接口") + @PostMapping(value = "/diary/collect/toggle") + @PreAuthorize("isAuthenticated()") + public Result> toggleDiaryCollect( + @Valid @RequestBody DiaryCollectForm form + ) { + Long userId = SecurityUtils.getUserId(); + Map result = adoptionDiaryService.toggleDiaryCollect(form, userId); + return Result.success(result); + } + + @Operation(summary = "获取领养日记瀑布流") + @GetMapping(value = "/diary/waterfall") + public Result> getWaterfall( + @Valid @RequestBody AdoptionDiaryWaterfallQuery query + ) { + Long userId = SecurityUtils.getUserId(); + WaterfallResult result = adoptionDiaryService.getWaterfall(query, userId); + return Result.success(result); + } + + @Operation(summary = "重置当前用户的浏览记录过滤规则", description = "重置后瀑布流会重新展示所有看过的内容") + @PostMapping(value = "/resetViewBloom") + @Log(module = LogModuleEnum.ADOPTION_DIARY_INFO, value = ActionTypeEnum.UPDATE) + public Result resetViewBloom() { + Long userId = SecurityUtils.getUserId(); + adoptionDiaryService.resetCurrentUserBloom(userId); + return Result.success("重置成功"); + } + + @Operation(summary = "增加领养日记浏览量") + @PostMapping(value = "/note/increase-view") + @Log(module = LogModuleEnum.ADOPTION_DIARY_INFO, value = ActionTypeEnum.UPDATE) + @RepeatSubmit(expire = 1) + public Result increaseDiaryViewCount(@RequestParam String diaryUuid) { + adoptionDiaryService.increaseDiaryViewCount(diaryUuid); + return Result.success(); + } + } diff --git a/src/main/java/com/youlai/boot/mini/controller/StrayAnimalController.java b/src/main/java/com/youlai/boot/mini/controller/StrayAnimalController.java index 2ffaa20..e6eb7fc 100644 --- a/src/main/java/com/youlai/boot/mini/controller/StrayAnimalController.java +++ b/src/main/java/com/youlai/boot/mini/controller/StrayAnimalController.java @@ -180,7 +180,7 @@ public class StrayAnimalController { @Operation(summary = "获取流浪动物笔记瀑布流") @GetMapping(value = "/note/waterfall") public Result> getWaterfall( - @Valid WaterfallQuery query + @Valid @RequestBody WaterfallQuery query ) { Long userId = SecurityUtils.getUserId(); WaterfallResult result = strayAnimalService.getWaterfall(query, userId); diff --git a/src/main/java/com/youlai/boot/mini/mapper/MiniAdoptionDiaryCollectMapper.java b/src/main/java/com/youlai/boot/mini/mapper/MiniAdoptionDiaryCollectMapper.java index eef61fc..ce7634f 100644 --- a/src/main/java/com/youlai/boot/mini/mapper/MiniAdoptionDiaryCollectMapper.java +++ b/src/main/java/com/youlai/boot/mini/mapper/MiniAdoptionDiaryCollectMapper.java @@ -2,13 +2,19 @@ package com.youlai.boot.mini.mapper; import com.youlai.boot.mini.model.entity.MiniAdoptionDiaryCollect; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Param; /** * 领养日记收藏表 Mapper 接口 * * @author jwy -* @since +* @since */ public interface MiniAdoptionDiaryCollectMapper extends BaseMapper { + Integer selectUserCollectCount(Long diaryId, Long userId); + + void insertOrUpdateCollect(MiniAdoptionDiaryCollect collect); + + void deleteCollect(@Param("diaryId") Long diaryId, @Param("userId") Long userId, @Param("currentTime") long currentTime); } diff --git a/src/main/java/com/youlai/boot/mini/mapper/MiniAdoptionDiaryMapper.java b/src/main/java/com/youlai/boot/mini/mapper/MiniAdoptionDiaryMapper.java index 31b0431..1320396 100644 --- a/src/main/java/com/youlai/boot/mini/mapper/MiniAdoptionDiaryMapper.java +++ b/src/main/java/com/youlai/boot/mini/mapper/MiniAdoptionDiaryMapper.java @@ -7,9 +7,12 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.youlai.boot.mini.model.query.OwnAdoptionDiaryQuery; import com.youlai.boot.mini.model.vo.AdoptionDiaryDetailsVO; import com.youlai.boot.mini.model.vo.AdoptionDiaryVO; +import com.youlai.boot.mini.model.vo.StrayAnimalShortVO; import jakarta.validation.constraints.NotBlank; import org.apache.ibatis.annotations.Param; +import java.util.List; + /** * 领养日记表 Mapper 接口 * @@ -31,4 +34,13 @@ public interface MiniAdoptionDiaryMapper extends BaseMapper { Long selectLikeCount(@Param("diaryId") Long diaryId); + void incrementCollectCount(@Param("diaryId") Long diaryId); + + void decrementCollectCount(@Param("diaryId") Long diaryId); + + Long selectCollectCount(Long diaryId); + + List getWaterfall(Long cursor, int querySize, Long userId); + + void increaseDiaryViewCount(String diaryUuid); } diff --git a/src/main/java/com/youlai/boot/mini/mapper/MiniAdoptionDiaryViewMapper.java b/src/main/java/com/youlai/boot/mini/mapper/MiniAdoptionDiaryViewMapper.java index 0c52403..17c2ca1 100644 --- a/src/main/java/com/youlai/boot/mini/mapper/MiniAdoptionDiaryViewMapper.java +++ b/src/main/java/com/youlai/boot/mini/mapper/MiniAdoptionDiaryViewMapper.java @@ -2,6 +2,9 @@ package com.youlai.boot.mini.mapper; import com.youlai.boot.mini.model.entity.MiniAdoptionDiaryView; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; /** * 领养日记浏览记录表 Mapper 接口 @@ -12,4 +15,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; public interface MiniAdoptionDiaryViewMapper extends BaseMapper { void insertOrUpdateView(MiniAdoptionDiaryView view); + + List selectViewedDiaryIdsByUserAndTime(@Param("userId") Long userId, @Param("startTime") Long startTime); + } diff --git a/src/main/java/com/youlai/boot/mini/model/form/DiaryCollectForm.java b/src/main/java/com/youlai/boot/mini/model/form/DiaryCollectForm.java new file mode 100644 index 0000000..208d9c7 --- /dev/null +++ b/src/main/java/com/youlai/boot/mini/model/form/DiaryCollectForm.java @@ -0,0 +1,15 @@ +package com.youlai.boot.mini.model.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +@Data +@Schema(description = "日记收藏请求参数") +public class DiaryCollectForm { + + @NotBlank(message = "日记UUID不能为空") + @Schema(description = "日记UUID", requiredMode = Schema.RequiredMode.REQUIRED, example = "a1b2c3d4e5f6g7h8i9j0") + private String diaryUuid; + +} 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 new file mode 100644 index 0000000..094e329 --- /dev/null +++ b/src/main/java/com/youlai/boot/mini/model/query/AdoptionDiaryWaterfallQuery.java @@ -0,0 +1,13 @@ +package com.youlai.boot.mini.model.query; + +import com.youlai.boot.common.base.BaseQuery; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class AdoptionDiaryWaterfallQuery extends BaseQuery { + + @Schema(description = "游标(上一页最后一条的ID,首次请求不传)", example = "100") + private Long cursor; + +} diff --git a/src/main/java/com/youlai/boot/mini/service/AdoptionDiaryService.java b/src/main/java/com/youlai/boot/mini/service/AdoptionDiaryService.java index 5648851..d6892e0 100644 --- a/src/main/java/com/youlai/boot/mini/service/AdoptionDiaryService.java +++ b/src/main/java/com/youlai/boot/mini/service/AdoptionDiaryService.java @@ -7,12 +7,11 @@ import com.youlai.boot.mini.model.dto.DeleteAdoptionDiaryMediaDTO; import com.youlai.boot.mini.model.dto.EditVisibilityDTO; import com.youlai.boot.mini.model.entity.MiniAdoptionDiary; import com.youlai.boot.mini.model.form.AdoptionDiaryForm; +import com.youlai.boot.mini.model.form.DiaryCollectForm; import com.youlai.boot.mini.model.form.DiaryLikeForm; +import com.youlai.boot.mini.model.query.AdoptionDiaryWaterfallQuery; import com.youlai.boot.mini.model.query.OwnAdoptionDiaryQuery; -import com.youlai.boot.mini.model.vo.AdoptionDiaryDetailsVO; -import com.youlai.boot.mini.model.vo.AdoptionDiaryVO; -import com.youlai.boot.mini.model.vo.SaveStrayAnimalVO; -import com.youlai.boot.mini.model.vo.StrayAnimalDetailsVO; +import com.youlai.boot.mini.model.vo.*; import jakarta.validation.Valid; import org.springframework.web.multipart.MultipartFile; @@ -42,4 +41,17 @@ public interface AdoptionDiaryService extends IService { IPage getOthersCreatedPage(String authorUuid, OwnAdoptionDiaryQuery queryParams); Map toggleDiaryLike(@Valid DiaryLikeForm form, Long userId); + + Map toggleDiaryCollect(@Valid DiaryCollectForm form, Long userId); + + WaterfallResult getWaterfall(@Valid AdoptionDiaryWaterfallQuery query, Long userId); + + void rebuildUserBloomFromDB(Long userId); + + void rebuildUserBloomFromDB(Long userId, int days); + + void resetCurrentUserBloom(Long userId); + + void increaseDiaryViewCount(String diaryUuid); + } 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 84ae704..4ad0426 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 @@ -15,17 +15,16 @@ import com.youlai.boot.common.util.JavaVCUtils; import com.youlai.boot.common.util.RandomNumberUtils; import com.youlai.boot.file.service.impl.AliyunFileService; import com.youlai.boot.framework.security.util.SecurityUtils; -import com.youlai.boot.mini.mapper.MiniAdoptionDiaryLikeMapper; -import com.youlai.boot.mini.mapper.MiniAdoptionDiaryMapper; -import com.youlai.boot.mini.mapper.MiniAdoptionDiaryMediaMapper; -import com.youlai.boot.mini.mapper.MiniAdoptionDiaryViewMapper; +import com.youlai.boot.mini.mapper.*; import com.youlai.boot.mini.model.dto.DeleteAdoptionDiaryDTO; import com.youlai.boot.mini.model.dto.DeleteAdoptionDiaryMediaDTO; import com.youlai.boot.mini.model.dto.EditVisibilityDTO; import com.youlai.boot.mini.model.entity.*; import com.youlai.boot.mini.model.enums.AnimalNoteMediaTypeEnum; import com.youlai.boot.mini.model.form.AdoptionDiaryForm; +import com.youlai.boot.mini.model.form.DiaryCollectForm; import com.youlai.boot.mini.model.form.DiaryLikeForm; +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; @@ -35,6 +34,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FilenameUtils; import org.redisson.api.RBloomFilter; +import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Value; import org.springframework.dao.DuplicateKeyException; @@ -47,6 +47,7 @@ import java.awt.image.BufferedImage; import java.io.File; import java.util.*; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; @Slf4j @Service @@ -59,6 +60,7 @@ public class AdoptionDiaryServiceImpl extends ServiceImpl 0) { result.getRecords().forEach(item -> { - if (cn.hutool.core.util.StrUtil.isBlank(item.getFirstImageUrl())) { + if (StrUtil.isBlank(item.getFirstImageUrl())) { // 领养日记默认封面图 item.setFirstImageUrl(getDefaultCoverHost() + "/default_diary.png"); } @@ -573,4 +575,225 @@ public class AdoptionDiaryServiceImpl extends ServiceImpl toggleDiaryCollect(DiaryCollectForm form, Long userId) { + Long diaryId = baseMapper.selectIdByUuid(form.getDiaryUuid()); + if (diaryId == null) { + throw new MsgException("笔记不存在或已删除"); + } + + long currentTime = System.currentTimeMillis(); + Boolean currentCollected; + + Integer count = miniAdoptionDiaryCollectMapper.selectUserCollectCount(diaryId, userId); + if (count != null && count > 0) { + currentCollected = true; + } else { + currentCollected = false; + } + + boolean targetCollect = !currentCollected; + + if (targetCollect) { + if (!Boolean.TRUE.equals(currentCollected)) { + MiniAdoptionDiaryCollect collect = new MiniAdoptionDiaryCollect(); + collect.setUuid(IdWorker.get32UUID()); + collect.setDiaryId(diaryId); + collect.setMiniUserId(userId); + collect.setCreateBy(userId); + collect.setCreateTimestamp(currentTime); + collect.setCreateTime(new Date(currentTime)); + collect.setUpdateBy(userId); + collect.setUpdateTimestamp(currentTime); + collect.setUpdateTime(new Date(currentTime)); + collect.setDeleted(false); + miniAdoptionDiaryCollectMapper.insertOrUpdateCollect(collect); + + baseMapper.incrementCollectCount(diaryId); + } + } else { + if (Boolean.TRUE.equals(currentCollected)) { + miniAdoptionDiaryCollectMapper.deleteCollect(diaryId, userId, currentTime); + + baseMapper.decrementCollectCount(diaryId); + } + } + + Long collectCount = baseMapper.selectCollectCount(diaryId); + Map result = new HashMap<>(); + result.put("isCollected", targetCollect); + result.put("collectCount", collectCount != null ? collectCount : 0); + return result; + } + + @Override + public WaterfallResult getWaterfall(AdoptionDiaryWaterfallQuery query, Long userId) { + // 1. 设置默认值,查 N+1 条 + int pageSize = query.getPageSize(); + int querySize = pageSize + 1; + + // 2. 从DB查询 N+1 条 原始未过滤数据 + List originalList = baseMapper.getWaterfall( + query.getCursor(), + querySize, + 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 { + return WaterfallResult.of(Collections.emptyList(), null, true); + } + + // 4.布隆过滤已浏览内容 ========== + List resultList = originalList.stream().limit(pageSize).collect(Collectors.toList()); + // 仅登录用户做过滤 + if (userId != null && CollUtil.isNotEmpty(resultList)) { + try { + String bloomKey = BLOOM_VIEW_KEY_PREFIX + userId; + RBloomFilter 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 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); + } + } + + // 5. 设置默认图片(原有逻辑不变) + if (CollUtil.isNotEmpty(resultList)) { + resultList.forEach(item -> { + if (StrUtil.isBlank(item.getFirstImageUrl())) { + item.setFirstImageUrl(getDefaultCoverHost() + "/default_diary.png"); + } + }); + } + + return WaterfallResult.of(resultList, nextCursor, isLastPage); + } + + @Override + public void rebuildUserBloomFromDB(Long userId) { + // days传0代表重建所有历史记录 + rebuildUserBloomFromDB(userId, 0); + } + + @Override + public void rebuildUserBloomFromDB(Long userId, int days) { + if (userId == null) { + return; + } + Long startTime = null; + if (days > 0) { + startTime = System.currentTimeMillis() - (long) days * 24 * 60 * 60 * 1000; + } + // startTime为null时查询用户所有历史浏览记录,否则只查指定天数内的 + List viewedDiaryIds = miniAdoptionDiaryViewMapper.selectViewedDiaryIdsByUserAndTime(userId, startTime); + + if (CollUtil.isEmpty(viewedDiaryIds)) { + return; + } + + String bloomKey = BLOOM_VIEW_KEY_PREFIX + userId; + RBloomFilter bloomFilter = redissonClient.getBloomFilter(bloomKey); + // 加分布式锁,避免同一个用户并发重建 + RLock lock = redissonClient.getLock(BLOOM_REBUILD_LOCK_PREFIX + userId); + + try { + if (lock.tryLock(3, TimeUnit.SECONDS)) { + try { + // 布隆已经存在的话先删除旧的 + if (bloomFilter.isExists()) { + bloomFilter.delete(); + } + // 初始化新布隆,用配置的参数 + bloomFilter.tryInit(bloomExpectedInsertions, bloomFpp); + // 只有配置了过期时间且大于0时才设置,否则永久有效 + if (bloomExpireDays != null && bloomExpireDays > 0) { + bloomFilter.expire(bloomExpireDays, TimeUnit.DAYS); + } + + // 批量写入布隆,1000条只需要几毫秒 + for (Long noteId : viewedDiaryIds) { + bloomFilter.add(noteId); + } + + log.info("用户{}的浏览布隆重建完成,共写入{}条记录", userId, viewedDiaryIds.size()); + } finally { + if (lock.isHeldByCurrentThread()) { + lock.unlock(); + } + } + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + log.error("重建用户{}的领养日记布隆时获取锁异常", userId, e); + } catch (Exception e) { + log.error("重建用户{}的领养日记布隆失败", userId, e); + // 异常不抛出,不影响主流程,下次访问会自动重试 + } + } + + @Override + public void resetCurrentUserBloom(Long userId) { + if (userId == null) { + throw new MsgException("用户未登录"); + } + try { + String bloomKey = BLOOM_VIEW_KEY_PREFIX + userId; + RBloomFilter bloomFilter = redissonClient.getBloomFilter(bloomKey); + // 1. 先删除旧布隆 + if (bloomFilter.isExists()) { + bloomFilter.delete(); + } + // 2. 初始化一个空的新布隆,不加载历史浏览记录 + bloomFilter.tryInit(bloomExpectedInsertions, bloomFpp); + // 保持和全局配置一致的过期时间 + if (bloomExpireDays != null && bloomExpireDays > 0) { + bloomFilter.expire(bloomExpireDays, TimeUnit.DAYS); + } + log.info("用户{}的领养日记布隆过滤器重置成功,已清空历史过滤规则,新浏览内容将继续记录", userId); + } catch (Exception e) { + log.error("重置用户{}的领养日记布隆过滤器失败", userId, e); + throw new MsgException("重置浏览记录失败,请稍后重试"); + } + } + + @Override + public void increaseDiaryViewCount(String diaryUuid) { + try { + baseMapper.increaseDiaryViewCount(diaryUuid); + } catch (Exception e) { + // 浏览量增加失败不影响主流程,仅打日志 + log.error("增加领养日记{}浏览量失败", diaryUuid, e); + } + } + } diff --git a/src/main/resources/mapper/mini/MiniAdoptionDiaryCollectMapper.xml b/src/main/resources/mapper/mini/MiniAdoptionDiaryCollectMapper.xml index 72f623c..6efcde4 100644 --- a/src/main/resources/mapper/mini/MiniAdoptionDiaryCollectMapper.xml +++ b/src/main/resources/mapper/mini/MiniAdoptionDiaryCollectMapper.xml @@ -5,5 +5,37 @@ + + + + + INSERT INTO mini_adoption_diary_collect + (uuid, diary_id, mini_user_id, create_time, create_timestamp, create_by, update_time, update_timestamp, update_by, is_deleted) + VALUES + (#{uuid}, #{diaryId}, #{miniUserId}, #{createTime}, #{createTimestamp}, #{createBy}, #{updateTime}, #{updateTimestamp}, #{updateBy}, #{deleted}) + ON DUPLICATE KEY UPDATE + is_deleted = 0, + update_time = VALUES(update_time), + update_timestamp = VALUES(update_timestamp), + update_by = VALUES(update_by) + + + + + UPDATE mini_adoption_diary_collect + SET is_deleted = 1, + update_time = NOW(), + update_timestamp = #{currentTime}, + update_by = #{userId} + WHERE diary_id = #{diaryId} + AND mini_user_id = #{userId} + AND is_deleted = 0 + diff --git a/src/main/resources/mapper/mini/MiniAdoptionDiaryMapper.xml b/src/main/resources/mapper/mini/MiniAdoptionDiaryMapper.xml index 8926b6b..4ce5dbf 100644 --- a/src/main/resources/mapper/mini/MiniAdoptionDiaryMapper.xml +++ b/src/main/resources/mapper/mini/MiniAdoptionDiaryMapper.xml @@ -121,4 +121,97 @@ AND is_deleted = 0 + + + UPDATE mini_adoption_diary + SET collect_count = collect_count + 1 + WHERE id = #{diaryId} + AND is_deleted = 0 + + + + + UPDATE mini_adoption_diary + SET collect_count = collect_count - 1 + WHERE id = #{diaryId} + AND is_deleted = 0 + AND collect_count > 0 + + + + + + + + + + + UPDATE mini_adoption_diary + SET view_count = view_count + 1 + WHERE uuid = #{diaryUuid} + AND is_deleted = 0 + + diff --git a/src/main/resources/mapper/mini/MiniAdoptionDiaryViewMapper.xml b/src/main/resources/mapper/mini/MiniAdoptionDiaryViewMapper.xml index ff98dc6..9dc05f6 100644 --- a/src/main/resources/mapper/mini/MiniAdoptionDiaryViewMapper.xml +++ b/src/main/resources/mapper/mini/MiniAdoptionDiaryViewMapper.xml @@ -16,4 +16,16 @@ is_deleted = 0 + + +