Browse Source

增加领养日记点赞收藏瀑布流接口

glx_phase2
glx 1 week ago
parent
commit
19a4ccfcf9
  1. 1
      src/main/java/com/youlai/boot/codegen/freemarker/MyBatisPlusGenerator.java
  2. 46
      src/main/java/com/youlai/boot/mini/controller/AdoptionDiaryController.java
  3. 2
      src/main/java/com/youlai/boot/mini/controller/StrayAnimalController.java
  4. 6
      src/main/java/com/youlai/boot/mini/mapper/MiniAdoptionDiaryCollectMapper.java
  5. 12
      src/main/java/com/youlai/boot/mini/mapper/MiniAdoptionDiaryMapper.java
  6. 6
      src/main/java/com/youlai/boot/mini/mapper/MiniAdoptionDiaryViewMapper.java
  7. 15
      src/main/java/com/youlai/boot/mini/model/form/DiaryCollectForm.java
  8. 13
      src/main/java/com/youlai/boot/mini/model/query/AdoptionDiaryWaterfallQuery.java
  9. 20
      src/main/java/com/youlai/boot/mini/service/AdoptionDiaryService.java
  10. 233
      src/main/java/com/youlai/boot/mini/service/impl/AdoptionDiaryServiceImpl.java
  11. 32
      src/main/resources/mapper/mini/MiniAdoptionDiaryCollectMapper.xml
  12. 93
      src/main/resources/mapper/mini/MiniAdoptionDiaryMapper.xml
  13. 12
      src/main/resources/mapper/mini/MiniAdoptionDiaryViewMapper.xml

1
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")

46
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<Map<String, Object>> toggleDiaryCollect(
@Valid @RequestBody DiaryCollectForm form
) {
Long userId = SecurityUtils.getUserId();
Map<String, Object> result = adoptionDiaryService.toggleDiaryCollect(form, userId);
return Result.success(result);
}
@Operation(summary = "获取领养日记瀑布流")
@GetMapping(value = "/diary/waterfall")
public Result<WaterfallResult<AdoptionDiaryVO>> getWaterfall(
@Valid @RequestBody AdoptionDiaryWaterfallQuery query
) {
Long userId = SecurityUtils.getUserId();
WaterfallResult<AdoptionDiaryVO> 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();
}
}

2
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<WaterfallResult<StrayAnimalShortVO>> getWaterfall(
@Valid WaterfallQuery query
@Valid @RequestBody WaterfallQuery query
) {
Long userId = SecurityUtils.getUserId();
WaterfallResult<StrayAnimalShortVO> result = strayAnimalService.getWaterfall(query, userId);

6
src/main/java/com/youlai/boot/mini/mapper/MiniAdoptionDiaryCollectMapper.java

@ -2,6 +2,7 @@ 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 接口
@ -11,4 +12,9 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
*/
public interface MiniAdoptionDiaryCollectMapper extends BaseMapper<MiniAdoptionDiaryCollect> {
Integer selectUserCollectCount(Long diaryId, Long userId);
void insertOrUpdateCollect(MiniAdoptionDiaryCollect collect);
void deleteCollect(@Param("diaryId") Long diaryId, @Param("userId") Long userId, @Param("currentTime") long currentTime);
}

12
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<MiniAdoptionDiary> {
Long selectLikeCount(@Param("diaryId") Long diaryId);
void incrementCollectCount(@Param("diaryId") Long diaryId);
void decrementCollectCount(@Param("diaryId") Long diaryId);
Long selectCollectCount(Long diaryId);
List<AdoptionDiaryVO> getWaterfall(Long cursor, int querySize, Long userId);
void increaseDiaryViewCount(String diaryUuid);
}

6
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<MiniAdoptionDiaryView> {
void insertOrUpdateView(MiniAdoptionDiaryView view);
List<Long> selectViewedDiaryIdsByUserAndTime(@Param("userId") Long userId, @Param("startTime") Long startTime);
}

15
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;
}

13
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;
}

20
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<MiniAdoptionDiary> {
IPage<AdoptionDiaryVO> getOthersCreatedPage(String authorUuid, OwnAdoptionDiaryQuery queryParams);
Map<String, Object> toggleDiaryLike(@Valid DiaryLikeForm form, Long userId);
Map<String, Object> toggleDiaryCollect(@Valid DiaryCollectForm form, Long userId);
WaterfallResult<AdoptionDiaryVO> getWaterfall(@Valid AdoptionDiaryWaterfallQuery query, Long userId);
void rebuildUserBloomFromDB(Long userId);
void rebuildUserBloomFromDB(Long userId, int days);
void resetCurrentUserBloom(Long userId);
void increaseDiaryViewCount(String diaryUuid);
}

233
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<MiniAdoptionDiaryMappe
private final MiniAdoptionDiaryViewMapper miniAdoptionDiaryViewMapper;
private final RedissonClient redissonClient;
private final MiniAdoptionDiaryLikeMapper miniAdoptionDiaryLikeMapper;
private final MiniAdoptionDiaryCollectMapper miniAdoptionDiaryCollectMapper;
// 布隆过滤器常量
private static final String BLOOM_VIEW_KEY_PREFIX = "mini:diary:view:bloom:";
@ -412,7 +414,7 @@ public class AdoptionDiaryServiceImpl extends ServiceImpl<MiniAdoptionDiaryMappe
// 处理默认封面
if (result.getTotal() > 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<MiniAdoptionDiaryMappe
return result;
}
@Override
public Map<String, Object> 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<String, Object> result = new HashMap<>();
result.put("isCollected", targetCollect);
result.put("collectCount", collectCount != null ? collectCount : 0);
return result;
}
@Override
public WaterfallResult<AdoptionDiaryVO> getWaterfall(AdoptionDiaryWaterfallQuery query, Long userId) {
// 1. 设置默认值,查 N+1 条
int pageSize = query.getPageSize();
int querySize = pageSize + 1;
// 2. 从DB查询 N+1 条 原始未过滤数据
List<AdoptionDiaryVO> 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<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);
}
}
// 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<Long> viewedDiaryIds = miniAdoptionDiaryViewMapper.selectViewedDiaryIdsByUserAndTime(userId, startTime);
if (CollUtil.isEmpty(viewedDiaryIds)) {
return;
}
String bloomKey = BLOOM_VIEW_KEY_PREFIX + userId;
RBloomFilter<Long> 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<Long> 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);
}
}
}

32
src/main/resources/mapper/mini/MiniAdoptionDiaryCollectMapper.xml

@ -5,5 +5,37 @@
<mapper namespace="com.youlai.boot.mini.mapper.MiniAdoptionDiaryCollectMapper">
<select id="selectUserCollectCount" resultType="java.lang.Integer">
SELECT COUNT(1)
FROM mini_adoption_diary_collect
WHERE diary_id = #{diaryId}
AND mini_user_id = #{userId}
AND is_deleted = 0
</select>
<!-- 新增或更新收藏记录(原子操作,依赖diary_id和mini_user_id联合唯一索引) -->
<insert id="insertOrUpdateCollect" parameterType="com.youlai.boot.mini.model.entity.MiniAdoptionDiaryCollect">
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)
</insert>
<!-- 逻辑删除收藏记录(取消收藏) -->
<update id="deleteCollect">
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
</update>
</mapper>

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

@ -121,4 +121,97 @@
AND is_deleted = 0
</select>
<!-- 原子增加收藏数 -->
<update id="incrementCollectCount">
UPDATE mini_adoption_diary
SET collect_count = collect_count + 1
WHERE id = #{diaryId}
AND is_deleted = 0
</update>
<!-- 原子减少笔记收藏数 -->
<update id="decrementCollectCount">
UPDATE mini_adoption_diary
SET collect_count = collect_count - 1
WHERE id = #{diaryId}
AND is_deleted = 0
AND collect_count > 0
</update>
<!-- 查询笔记收藏数 -->
<select id="selectCollectCount" resultType="java.lang.Long">
SELECT collect_count
FROM mini_adoption_diary
WHERE id = #{diaryId}
AND is_deleted = 0
</select>
<!-- 瀑布流查询 -->
<select id="getWaterfall" resultType="com.youlai.boot.mini.model.vo.AdoptionDiaryVO" databaseId="mysql">
WITH first_image AS (
SELECT
m.diary_id,
m.source_url,
ROW_NUMBER() OVER (
PARTITION BY m.diary_id
ORDER BY m.id ASC
) AS rn
FROM mini_adoption_diary_media m
WHERE m.media_type = 'image'
AND m.is_deleted = 0
)
SELECT
ad.id,
u.uuid AS authorUuid,
u.nickname AS authorName,
u.avatar AS authorAvatar,
ad.uuid AS diaryUuid,
fi.source_url AS firstImageUrl,
ad.title,
ad.content,
ad.visibility,
ad.view_count,
ad.like_count,
ad.comment_count,
<if test="miniUserId != null">
-- 当前用户是否点赞
EXISTS (
SELECT 1
FROM mini_adoption_diary_like l
WHERE l.diary_id = ad.id
AND l.mini_user_id = #{miniUserId}
AND l.is_deleted = 0
) AS isLiked,
-- 当前用户是否收藏
EXISTS (
SELECT 1
FROM mini_adoption_diary_collect c
WHERE c.diary_id = ad.id
AND c.mini_user_id = #{miniUserId}
AND c.is_deleted = 0
) AS isCollected,
</if>
ad.create_time,
ad.collect_count
<!-- ad.audit_status-->
FROM mini_adoption_diary ad
INNER JOIN sys_user u ON ad.mini_user_id = u.id
LEFT JOIN first_image fi ON fi.diary_id = ad.id AND fi.rn = 1
WHERE ad.is_deleted = 0
AND ad.is_deleted = 0
<if test="cursor != null">
AND ad.id &lt; #{cursor}
</if>
ORDER BY ad.id DESC
LIMIT #{pageSize}
</select>
<!-- 原子增加浏览量 -->
<update id="increaseDiaryViewCount">
UPDATE mini_adoption_diary
SET view_count = view_count + 1
WHERE uuid = #{diaryUuid}
AND is_deleted = 0
</update>
</mapper>

12
src/main/resources/mapper/mini/MiniAdoptionDiaryViewMapper.xml

@ -16,4 +16,16 @@
is_deleted = 0
</insert>
<!-- 查询指定用户的浏览过的笔记ID 走idx_user_time联合索引 -->
<select id="selectViewedDiaryIdsByUserAndTime" resultType="java.lang.Long">
SELECT diary_id
FROM mini_adoption_diary_view
WHERE mini_user_id = #{userId}
<if test="startTime != null">
AND create_timestamp >= #{startTime}
</if>
AND is_deleted = 0
ORDER BY create_timestamp DESC
</select>
</mapper>

Loading…
Cancel
Save