|
|
@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
|
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
|
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
|
|
import com.baomidou.mybatisplus.core.metadata.IPage; |
|
|
import com.baomidou.mybatisplus.core.metadata.IPage; |
|
|
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; |
|
|
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; |
|
|
|
|
|
import com.baomidou.mybatisplus.core.toolkit.IdWorker; |
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
|
|
import com.youlai.boot.common.exception.MsgException; |
|
|
import com.youlai.boot.common.exception.MsgException; |
|
|
@ -14,23 +15,29 @@ import com.youlai.boot.common.util.JavaVCUtils; |
|
|
import com.youlai.boot.common.util.RandomNumberUtils; |
|
|
import com.youlai.boot.common.util.RandomNumberUtils; |
|
|
import com.youlai.boot.file.service.impl.AliyunFileService; |
|
|
import com.youlai.boot.file.service.impl.AliyunFileService; |
|
|
import com.youlai.boot.framework.security.util.SecurityUtils; |
|
|
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.MiniAdoptionDiaryMapper; |
|
|
import com.youlai.boot.mini.mapper.MiniAdoptionDiaryMediaMapper; |
|
|
import com.youlai.boot.mini.mapper.MiniAdoptionDiaryMediaMapper; |
|
|
|
|
|
import com.youlai.boot.mini.mapper.MiniAdoptionDiaryViewMapper; |
|
|
import com.youlai.boot.mini.model.dto.DeleteAdoptionDiaryDTO; |
|
|
import com.youlai.boot.mini.model.dto.DeleteAdoptionDiaryDTO; |
|
|
import com.youlai.boot.mini.model.dto.DeleteAdoptionDiaryMediaDTO; |
|
|
import com.youlai.boot.mini.model.dto.DeleteAdoptionDiaryMediaDTO; |
|
|
import com.youlai.boot.mini.model.dto.EditVisibilityDTO; |
|
|
import com.youlai.boot.mini.model.dto.EditVisibilityDTO; |
|
|
import com.youlai.boot.mini.model.entity.MiniAdoptionDiary; |
|
|
import com.youlai.boot.mini.model.entity.*; |
|
|
import com.youlai.boot.mini.model.entity.MiniAdoptionDiaryMedia; |
|
|
|
|
|
import com.youlai.boot.mini.model.enums.AnimalNoteMediaTypeEnum; |
|
|
import com.youlai.boot.mini.model.enums.AnimalNoteMediaTypeEnum; |
|
|
import com.youlai.boot.mini.model.form.AdoptionDiaryForm; |
|
|
import com.youlai.boot.mini.model.form.AdoptionDiaryForm; |
|
|
|
|
|
import com.youlai.boot.mini.model.form.DiaryLikeForm; |
|
|
import com.youlai.boot.mini.model.query.OwnAdoptionDiaryQuery; |
|
|
import com.youlai.boot.mini.model.query.OwnAdoptionDiaryQuery; |
|
|
import com.youlai.boot.mini.model.vo.AdoptionDiaryDetailsVO; |
|
|
import com.youlai.boot.mini.model.vo.*; |
|
|
import com.youlai.boot.mini.model.vo.AdoptionDiaryVO; |
|
|
|
|
|
import com.youlai.boot.mini.service.AdoptionDiaryService; |
|
|
import com.youlai.boot.mini.service.AdoptionDiaryService; |
|
|
|
|
|
import com.youlai.boot.system.mapper.UserMapper; |
|
|
|
|
|
import com.youlai.boot.system.model.entity.SysUser; |
|
|
import lombok.RequiredArgsConstructor; |
|
|
import lombok.RequiredArgsConstructor; |
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
import org.apache.commons.io.FilenameUtils; |
|
|
import org.apache.commons.io.FilenameUtils; |
|
|
|
|
|
import org.redisson.api.RBloomFilter; |
|
|
|
|
|
import org.redisson.api.RedissonClient; |
|
|
import org.springframework.beans.factory.annotation.Value; |
|
|
import org.springframework.beans.factory.annotation.Value; |
|
|
|
|
|
import org.springframework.dao.DuplicateKeyException; |
|
|
import org.springframework.stereotype.Service; |
|
|
import org.springframework.stereotype.Service; |
|
|
import org.springframework.transaction.annotation.Transactional; |
|
|
import org.springframework.transaction.annotation.Transactional; |
|
|
import org.springframework.web.multipart.MultipartFile; |
|
|
import org.springframework.web.multipart.MultipartFile; |
|
|
@ -38,10 +45,8 @@ import org.springframework.web.multipart.MultipartFile; |
|
|
import javax.imageio.ImageIO; |
|
|
import javax.imageio.ImageIO; |
|
|
import java.awt.image.BufferedImage; |
|
|
import java.awt.image.BufferedImage; |
|
|
import java.io.File; |
|
|
import java.io.File; |
|
|
import java.util.ArrayList; |
|
|
import java.util.*; |
|
|
import java.util.Date; |
|
|
import java.util.concurrent.TimeUnit; |
|
|
import java.util.List; |
|
|
|
|
|
import java.util.UUID; |
|
|
|
|
|
|
|
|
|
|
|
@Slf4j |
|
|
@Slf4j |
|
|
@Service |
|
|
@Service |
|
|
@ -50,6 +55,15 @@ public class AdoptionDiaryServiceImpl extends ServiceImpl<MiniAdoptionDiaryMappe |
|
|
|
|
|
|
|
|
private final AliyunFileService aliyunFileService; |
|
|
private final AliyunFileService aliyunFileService; |
|
|
private final MiniAdoptionDiaryMediaMapper miniAdoptionDiaryMediaMapper; |
|
|
private final MiniAdoptionDiaryMediaMapper miniAdoptionDiaryMediaMapper; |
|
|
|
|
|
private final UserMapper userMapper; |
|
|
|
|
|
private final MiniAdoptionDiaryViewMapper miniAdoptionDiaryViewMapper; |
|
|
|
|
|
private final RedissonClient redissonClient; |
|
|
|
|
|
private final MiniAdoptionDiaryLikeMapper miniAdoptionDiaryLikeMapper; |
|
|
|
|
|
|
|
|
|
|
|
// 布隆过滤器常量
|
|
|
|
|
|
private static final String BLOOM_VIEW_KEY_PREFIX = "mini:diary:view:bloom:"; |
|
|
|
|
|
// 布隆重建分布式锁前缀
|
|
|
|
|
|
private static final String BLOOM_REBUILD_LOCK_PREFIX = "lock:rebuild:bloom:"; |
|
|
|
|
|
|
|
|
private final static String OSS_THUMBNAIL_DIR = "adoption_diary/thumbnail/"; |
|
|
private final static String OSS_THUMBNAIL_DIR = "adoption_diary/thumbnail/"; |
|
|
private final static String OSS_IMAGE_DIR = "adoption_diary/image/"; |
|
|
private final static String OSS_IMAGE_DIR = "adoption_diary/image/"; |
|
|
@ -61,6 +75,15 @@ public class AdoptionDiaryServiceImpl extends ServiceImpl<MiniAdoptionDiaryMappe |
|
|
@Value("${oss.aliyun.bucket-name}") |
|
|
@Value("${oss.aliyun.bucket-name}") |
|
|
private String bucketName; |
|
|
private String bucketName; |
|
|
|
|
|
|
|
|
|
|
|
@Value("${bloom.view.expected-insertions:1000}") |
|
|
|
|
|
private Integer bloomExpectedInsertions; |
|
|
|
|
|
|
|
|
|
|
|
@Value("${bloom.view.fpp:0.001}") |
|
|
|
|
|
private Double bloomFpp; |
|
|
|
|
|
|
|
|
|
|
|
@Value("${bloom.view.expire-days:}") |
|
|
|
|
|
private Integer bloomExpireDays; |
|
|
|
|
|
|
|
|
public String getDefaultCoverHost() { |
|
|
public String getDefaultCoverHost() { |
|
|
return "https://" + bucketName + "." + endpoint; |
|
|
return "https://" + bucketName + "." + endpoint; |
|
|
} |
|
|
} |
|
|
@ -376,6 +399,10 @@ public class AdoptionDiaryServiceImpl extends ServiceImpl<MiniAdoptionDiaryMappe |
|
|
queryParams.setMiniUserId(userId); |
|
|
queryParams.setMiniUserId(userId); |
|
|
queryParams.setCreatorId(userId); |
|
|
queryParams.setCreatorId(userId); |
|
|
|
|
|
|
|
|
|
|
|
return getDiaryPage(queryParams); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private IPage<AdoptionDiaryVO> getDiaryPage(OwnAdoptionDiaryQuery queryParams) { |
|
|
int pageNum = queryParams.getPageNum(); |
|
|
int pageNum = queryParams.getPageNum(); |
|
|
int pageSize = queryParams.getPageSize(); |
|
|
int pageSize = queryParams.getPageSize(); |
|
|
Page<AdoptionDiaryVO> page = new Page<>(pageNum, pageSize); |
|
|
Page<AdoptionDiaryVO> page = new Page<>(pageNum, pageSize); |
|
|
@ -395,9 +422,155 @@ public class AdoptionDiaryServiceImpl extends ServiceImpl<MiniAdoptionDiaryMappe |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@Override |
|
|
@Override |
|
|
public AdoptionDiaryDetailsVO getDetails(String diaryUuid, Long userId) { |
|
|
public IPage<AdoptionDiaryVO> getOthersCreatedPage(String authorUuid, OwnAdoptionDiaryQuery queryParams) { |
|
|
AdoptionDiaryDetailsVO diaryVO = baseMapper.getDiaryDetails(diaryUuid, userId); |
|
|
SysUser sysUser = userMapper.selectOne(new LambdaQueryWrapper<SysUser>() |
|
|
return diaryVO; |
|
|
.eq(SysUser::getUuid, authorUuid) |
|
|
|
|
|
.eq(SysUser::getIsDeleted, 0)); |
|
|
|
|
|
if (sysUser == null) { |
|
|
|
|
|
throw new MsgException("用户不存在"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
queryParams.setMiniUserId(SecurityUtils.getUserId()); |
|
|
|
|
|
queryParams.setCreatorId(sysUser.getId()); |
|
|
|
|
|
return getDiaryPage(queryParams); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
public AdoptionDiaryDetailsVO getDetails(String diaryUuid, Long miniUserId) { |
|
|
|
|
|
// 校验动物是否存在
|
|
|
|
|
|
MiniAdoptionDiary diary = getValidDiary(diaryUuid); |
|
|
|
|
|
|
|
|
|
|
|
AdoptionDiaryDetailsVO adoptionDiaryDetailsVO = new AdoptionDiaryDetailsVO(); |
|
|
|
|
|
|
|
|
|
|
|
if (diary != null) { |
|
|
|
|
|
Map<String, Object> param = new HashMap<>(); |
|
|
|
|
|
param.put("diaryId", diary.getId()); |
|
|
|
|
|
|
|
|
|
|
|
param.put("mediaType", AnimalNoteMediaTypeEnum.IMAGE.name().toLowerCase()); |
|
|
|
|
|
List<MiniAdoptionDiaryMediaVO> images = miniAdoptionDiaryMediaMapper.getMediaByDiaryIdAndType(param); |
|
|
|
|
|
if (CollectionUtils.isNotEmpty(images)) { |
|
|
|
|
|
images.forEach(item -> { |
|
|
|
|
|
item.setDiaryUuid(diary.getUuid()); |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
param.put("mediaType", AnimalNoteMediaTypeEnum.VIDEO.name().toLowerCase()); |
|
|
|
|
|
List<MiniAdoptionDiaryMediaVO> videos = miniAdoptionDiaryMediaMapper.getMediaByDiaryIdAndType(param); |
|
|
|
|
|
if (CollectionUtils.isNotEmpty(videos)) { |
|
|
|
|
|
videos.forEach(item -> { |
|
|
|
|
|
item.setDiaryUuid(diary.getUuid()); |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
adoptionDiaryDetailsVO.setImages(images); |
|
|
|
|
|
adoptionDiaryDetailsVO.setVideos(videos); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 记录用户浏览历史
|
|
|
|
|
|
if (miniUserId != null && diary != null) { |
|
|
|
|
|
Long diaryId = diary.getId(); |
|
|
|
|
|
long currentTime = System.currentTimeMillis(); |
|
|
|
|
|
// 1. 写数据库
|
|
|
|
|
|
try { |
|
|
|
|
|
MiniAdoptionDiaryView view = new MiniAdoptionDiaryView(); |
|
|
|
|
|
view.setUuid(IdWorker.get32UUID()); |
|
|
|
|
|
view.setMiniUserId(miniUserId); |
|
|
|
|
|
view.setDiaryId(diaryId); |
|
|
|
|
|
view.setCreateTimestamp(currentTime); |
|
|
|
|
|
view.setCreateTime(new Date(currentTime)); |
|
|
|
|
|
view.setCreateBy(miniUserId); |
|
|
|
|
|
miniAdoptionDiaryViewMapper.insertOrUpdateView(view); |
|
|
|
|
|
} catch (DuplicateKeyException e) { |
|
|
|
|
|
log.debug("用户已浏览过该领养日记,userId:{}, diaryId:{}", miniUserId, diaryId); |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
log.error("记录浏览历史到数据库失败,userId:{}, diaryId:{}", miniUserId, diaryId, e); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 2. 写布隆过滤器
|
|
|
|
|
|
try { |
|
|
|
|
|
String bloomKey = BLOOM_VIEW_KEY_PREFIX + miniUserId; |
|
|
|
|
|
RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter(bloomKey); |
|
|
|
|
|
if (!bloomFilter.isExists()) { |
|
|
|
|
|
// 初始化新布隆,用配置的参数
|
|
|
|
|
|
bloomFilter.tryInit(bloomExpectedInsertions, bloomFpp); |
|
|
|
|
|
// 只有配置了过期时间且大于0时才设置,否则永久有效
|
|
|
|
|
|
if (bloomExpireDays != null && bloomExpireDays > 0) { |
|
|
|
|
|
bloomFilter.expire(bloomExpireDays, TimeUnit.DAYS); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
bloomFilter.add(diaryId); |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
log.error("记录浏览历史到布隆失败,userId:{}, noteId:{}", miniUserId, diaryId, e); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return adoptionDiaryDetailsVO; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private MiniAdoptionDiary getValidDiary(String diaryUuid) { |
|
|
|
|
|
MiniAdoptionDiary diary = baseMapper.selectOne( |
|
|
|
|
|
new LambdaQueryWrapper<MiniAdoptionDiary>() |
|
|
|
|
|
.eq(MiniAdoptionDiary::getUuid, diaryUuid) |
|
|
|
|
|
.eq(MiniAdoptionDiary::getDeleted, 0) |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
if (diary == null) { |
|
|
|
|
|
throw new MsgException("日记不存在"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return diary; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
public Map<String, Object> toggleDiaryLike(DiaryLikeForm form, Long userId) { |
|
|
|
|
|
Long diaryId = baseMapper.selectIdByUuid(form.getDiaryUuid()); |
|
|
|
|
|
if (diaryId == null) { |
|
|
|
|
|
throw new MsgException("日记不存在或已删除"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
long currentTime = System.currentTimeMillis(); |
|
|
|
|
|
Boolean currentLiked; |
|
|
|
|
|
|
|
|
|
|
|
Integer count = miniAdoptionDiaryLikeMapper.selectUserLikeCount(diaryId, userId); |
|
|
|
|
|
if (count != null && count > 0) { |
|
|
|
|
|
currentLiked = true; |
|
|
|
|
|
} else { |
|
|
|
|
|
currentLiked = false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
boolean targetLike = !currentLiked; |
|
|
|
|
|
|
|
|
|
|
|
if (targetLike) { |
|
|
|
|
|
if (!Boolean.TRUE.equals(currentLiked)) { |
|
|
|
|
|
MiniAdoptionDiaryLike like = new MiniAdoptionDiaryLike(); |
|
|
|
|
|
like.setUuid(IdWorker.get32UUID()); |
|
|
|
|
|
like.setDiaryId(diaryId); |
|
|
|
|
|
like.setMiniUserId(userId); |
|
|
|
|
|
like.setCreateBy(userId); |
|
|
|
|
|
like.setCreateTimestamp(currentTime); |
|
|
|
|
|
like.setCreateTime(new Date(currentTime)); |
|
|
|
|
|
like.setUpdateBy(userId); |
|
|
|
|
|
like.setUpdateTimestamp(currentTime); |
|
|
|
|
|
like.setUpdateTime(new Date(currentTime)); |
|
|
|
|
|
like.setDeleted(false); |
|
|
|
|
|
miniAdoptionDiaryLikeMapper.insertOrUpdateLike(like); |
|
|
|
|
|
|
|
|
|
|
|
baseMapper.incrementLikeCount(diaryId); |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
if (Boolean.TRUE.equals(currentLiked)) { |
|
|
|
|
|
miniAdoptionDiaryLikeMapper.deleteLike(diaryId, userId, currentTime); |
|
|
|
|
|
|
|
|
|
|
|
baseMapper.decrementLikeCount(diaryId); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Long likeCount = baseMapper.selectLikeCount(diaryId); |
|
|
|
|
|
Map<String, Object> result = new HashMap<>(); |
|
|
|
|
|
result.put("isLiked", targetLike); |
|
|
|
|
|
result.put("likeCount", likeCount != null ? likeCount : 0); |
|
|
|
|
|
return result; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
|