Commit d06f4cc4 authored by 万成波's avatar 万成波

动态点赞

parent ec025c69
package com.tangguo.controller.mobile;
import cn.hutool.core.lang.Opt;
import com.tangguo.common.core.domain.AjaxResult;
import com.tangguo.common.mauth.MobileAuth;
import com.tangguo.common.mauth.MobileTokenHelper;
import com.tangguo.common.utils.PageUtils;
import com.tangguo.common.utils.ValidateOperations;
import com.tangguo.domain.bo.CreateMomentBO;
import com.tangguo.domain.bo.LikeMomentBO;
import com.tangguo.domain.vo.BbsMomentListVO;
import com.tangguo.service.IBbsMomentService;
import org.springframework.web.bind.annotation.*;
......@@ -68,4 +71,21 @@ public class MBbsMomentController {
return AjaxResult.success();
}
/**
* 点赞动态
*
* @param bo 动态
* @return 点赞结果
*/
@MobileAuth
@PostMapping("/like")
public AjaxResult likeMoment(@RequestBody LikeMomentBO bo) {
ValidateOperations.generalValidate(bo);
synchronized (String.valueOf(bo.getMomentId()).intern()) {
int likeCount = this.momentService.likeMoment(bo);
return AjaxResult.success(likeCount);
}
}
}
......@@ -42,9 +42,4 @@ public class BbsMomentLike extends BaseEntity {
@ApiModelProperty("点赞用户名")
private String userName;
/** 点赞状态:0 取消、1 正常 */
@Excel(name = "点赞状态:0 取消、1 正常")
@ApiModelProperty("点赞状态:0 取消、1 正常")
private Integer likeStatus;
}
package com.tangguo.domain.bo;
import lombok.Data;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
/**
*
*
* @author 谈笑
* @createTime 2025-09-04 15:28:48 星期四
*/
@Data
public class LikeMomentBO {
/**
* 动态Id
*/
@NotNull(message = "动态Id不能为空")
private Long momentId;
/**
* 点赞状态:0 取消点赞、1 点赞
*/
@NotNull(message = "点赞状态不能为空")
@Min(value = 0, message = "点赞状态值错误")
@Max(value = 1, message = "点赞状态值错误")
private Integer likeStatus;
}
package com.tangguo.domain.vo;
import lombok.Data;
/**
*
*
* @author 谈笑
* @createTime 2025-09-04 14:17:01 星期四
*/
@Data
public class BbsAttachmentVO {
/** 附件Id */
private Long id;
/** 附件名称 */
private String name;
/** 附件预览地址 */
private String url;
}
......@@ -20,12 +20,6 @@ public class BbsCommentVO {
/** 评论用户名 */
private String userName;
/** 回复的用户姓名 */
private String replyNikeName;
/** 回复的用户姓名 */
private String replyUserName;
/** 评论回复内容 */
private String content;
......
package com.tangguo.domain.vo;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
......@@ -22,6 +24,9 @@ public class BbsMomentListVO {
/** 动态Id */
private Long id;
/** 发布人用户姓名 */
private String nikeName;
/** 发布人用户名 */
private String userName;
......@@ -74,50 +79,32 @@ public class BbsMomentListVO {
private Integer isSelf;
/** 动态附件 */
private List<Attachment> attachments;
private List<BbsAttachmentVO> attachments;
/** 动态投票选项 */
private List<VoteOption> voteOptions;
private List<BbsVoteOptionVO> voteOptions;
/** 动态附件 */
private List<BbsCommentVO> comments;
/**
* 动态附件
* 处理主题名称
*/
@Data
public static class Attachment {
/** 附件名称 */
private String name;
/** 附件预览地址 */
private String url;
public void setTopicNames(String topicNames) {
if (StrUtil.isNotBlank(topicNames)) {
this.topicNames = Arrays.asList(topicNames.split(","));
}
}
/**
* 动态投票选项
* 处理主题Id
*/
@Data
public static class VoteOption {
/** 选项类型:TEXT 文字、IMAGE 图片 */
private String type;
/** 选项名称 */
private String name;
/** 选项编码 */
private String code;
/** 图片地址 */
private String imageUrl;
/** 排序值 */
private Integer sort;
/** 投票人数 */
private Integer voteCount;
public void setTopicIds(String topicIds) {
if (StrUtil.isNotBlank(topicIds)) {
this.topicIds = Arrays.asList(topicIds.split(","));
}
}
}
package com.tangguo.domain.vo;
import lombok.Data;
/**
*
*
* @author 谈笑
* @createTime 2025-09-04 14:17:24 星期四
*/
@Data
public class BbsVoteOptionVO {
/** 选项Id */
private Long id;
/** 选项类型:TEXT 文字、IMAGE 图片 */
private String type;
/** 选项名称 */
private String name;
/** 选项编码 */
private String code;
/** 图片地址 */
private String imageUrl;
/** 排序值 */
private Integer sort;
/** 投票人数 */
private Integer voteCount;
}
package com.tangguo.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 动态点赞状态枚举
*
* @author 谈笑
* @createTime 2025-09-04 15:30:52 星期四
*/
@Getter
@AllArgsConstructor
public enum LikeStatus {
LIKE(1, "点赞"), UNLIKE(0, "取消点赞");
private final int status;
private final String desc;
public static LikeStatus getLikeStatus(int status) {
return Arrays.stream(LikeStatus.values())
.filter(s -> s.getStatus() == status).findFirst().orElse(null);
}
}
......@@ -15,7 +15,7 @@ import java.util.Arrays;
@AllArgsConstructor
public enum MomentType {
IMAGE("图片动态"), VIDEO("视频动态"), URL("链接动态");
TEXT("文本动态"), IMAGE("图片动态"), VIDEO("视频动态"), URL("链接动态");
private final String desc;
......
......@@ -20,4 +20,7 @@ public interface BbsMomentCommentMapper extends BaseMapper<BbsMomentComment> {
List<BbsMomentComment> selectBbsMomentComments(@Param("commentId") Long commentId);
List<BbsMomentComment> selectComments(@Param("momentIds") List<Long> momentIds, @Param("rows") int rows);
}
......@@ -2,6 +2,7 @@ package com.tangguo.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.tangguo.domain.BbsMomentComment;
import com.tangguo.domain.BbsMomentVoteOption;
import java.util.List;
......@@ -31,6 +32,15 @@ public interface IBbsMomentCommentService extends IService<BbsMomentComment> {
BbsMomentComment getBbsMomentComment(Long commentId);
/**
* 查询动态评论
*
* @param momentIds 动态Ids
* @return 投票选项
*/
List<BbsMomentComment> getComments(List<Long> momentIds);
/**
* 删除评论
*
......
......@@ -11,6 +11,31 @@ import com.tangguo.domain.BbsMomentLike;
*/
public interface IBbsMomentLikeService extends IService<BbsMomentLike> {
/**
* 查询用户点赞记录数量
*
* @param momentId 动态Id
* @param userName 用户名
* @return 点赞记录
*/
long getMomentLikeCount(Long momentId, String userName);
/**
* 添加点赞记录
*
* @param momentId 动态Id
* @param userName 用户名
*/
void addMomentLike(Long momentId, String userName);
/**
* 删除点赞记录
*
* @param momentId 动态Id
* @param userName 用户名
*/
void deleteMomentLike(Long momentId, String userName);
}
......@@ -3,6 +3,7 @@ package com.tangguo.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.tangguo.domain.BbsMoment;
import com.tangguo.domain.bo.CreateMomentBO;
import com.tangguo.domain.bo.LikeMomentBO;
import com.tangguo.domain.vo.BbsMomentListVO;
import com.tangguo.domain.vo.BbsUserMomentCountVO;
......@@ -76,4 +77,12 @@ public interface IBbsMomentService extends IService<BbsMoment> {
*/
void userDeleteMoment(Long momentId);
/**
* 点赞动态
*
* @param bo 动态
*/
int likeMoment(LikeMomentBO bo);
}
......@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
import com.tangguo.domain.BbsMoment;
import com.tangguo.domain.BbsMomentVoteOption;
import com.tangguo.domain.bo.CreateMomentBO;
import com.tangguo.domain.vo.BbsVoteOptionVO;
import java.util.List;
......
package com.tangguo.service.impl;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tangguo.common.exception.ServiceException;
import com.tangguo.domain.BbsMomentComment;
......@@ -9,6 +11,8 @@ import com.tangguo.service.IBbsMomentCommentService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
......@@ -54,6 +58,22 @@ public class BbsMomentCommentServiceImpl extends ServiceImpl<BbsMomentCommentMap
}
/**
* 查询动态评论
*
* @param momentIds 动态Ids
* @return 投票选项
*/
@Override
public List<BbsMomentComment> getComments(List<Long> momentIds) {
List<BbsMomentComment> dbComments = new ArrayList<>(30);
if (CollUtil.isNotEmpty(momentIds)) {
dbComments = this.baseMapper.selectComments(momentIds, 3);
}
return dbComments;
}
/**
* 删除评论
*
......
package com.tangguo.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tangguo.domain.BbsMomentLike;
import com.tangguo.mapper.BbsMomentLikeMapper;
......@@ -20,4 +21,52 @@ public class BbsMomentLikeServiceImpl extends ServiceImpl<BbsMomentLikeMapper, B
@Resource
private BbsMomentLikeMapper bbsMomentLikeMapper;
/**
* 查询用户点赞记录数量
*
* @param momentId 动态Id
* @param userName 用户名
* @return 点赞记录
*/
@Override
public long getMomentLikeCount(Long momentId, String userName) {
return this.count(
Wrappers.lambdaQuery(BbsMomentLike.class)
.eq(BbsMomentLike::getMomentId, momentId)
.eq(BbsMomentLike::getUserName, userName)
);
}
/**
* 添加点赞记录
*
* @param momentId 动态Id
* @param userName 用户名
*/
@Override
public void addMomentLike(Long momentId, String userName) {
BbsMomentLike newLike = new BbsMomentLike();
newLike.setMomentId(momentId);
newLike.setUserName(userName);
this.save(newLike);
}
/**
* 删除点赞记录
*
* @param momentId 动态Id
* @param userName 用户名
*/
@Override
public void deleteMomentLike(Long momentId, String userName) {
this.remove(
Wrappers.lambdaQuery(BbsMomentLike.class)
.eq(BbsMomentLike::getMomentId, momentId)
.eq(BbsMomentLike::getUserName, userName)
);
}
}
......@@ -6,22 +6,20 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tangguo.common.exception.ServiceException;
import com.tangguo.common.mauth.MobileTokenHelper;
import com.tangguo.domain.BbsMoment;
import com.tangguo.domain.BbsMomentAttachment;
import com.tangguo.domain.BbsMomentVoteOption;
import com.tangguo.domain.*;
import com.tangguo.domain.bo.CreateMomentBO;
import com.tangguo.domain.vo.BbsMomentListVO;
import com.tangguo.domain.vo.BbsUserMomentCountVO;
import com.tangguo.domain.bo.LikeMomentBO;
import com.tangguo.domain.vo.*;
import com.tangguo.enums.EnableStatus;
import com.tangguo.enums.LikeStatus;
import com.tangguo.enums.MomentType;
import com.tangguo.enums.VoteOptionType;
import com.tangguo.mapper.BbsMomentMapper;
import com.tangguo.service.IBbsMomentAttachmentService;
import com.tangguo.service.IBbsMomentService;
import com.tangguo.service.IBbsMomentVoteOptionService;
import com.tangguo.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StopWatch;
import javax.annotation.Resource;
import java.util.ArrayList;
......@@ -50,6 +48,12 @@ public class BbsMomentServiceImpl extends ServiceImpl<BbsMomentMapper, BbsMoment
@Resource
private IBbsMomentVoteOptionService voteOptionService;
@Resource
private IBbsMomentCommentService commentService;
@Resource
private IBbsMomentLikeService likeService;
/**
* 查询动态列表
......@@ -165,6 +169,47 @@ public class BbsMomentServiceImpl extends ServiceImpl<BbsMomentMapper, BbsMoment
}
/**
* 点赞动态
*
* @param bo 动态
*/
@Transactional(rollbackFor = Exception.class)
@Override
public int likeMoment(LikeMomentBO bo) {
BbsMoment dbMoment = this.getById(bo.getMomentId());
if (Objects.isNull(dbMoment)) {
throw new ServiceException("点赞失败,未查询到当前动态数据。");
}
String userName = MobileTokenHelper.getUsername();
long likeStatus = this.likeService.getMomentLikeCount(dbMoment.getId(), userName);
int likeCount = dbMoment.getLikeCount();
if (LikeStatus.LIKE.getStatus() == bo.getLikeStatus()) {
if (likeStatus > 0) {
throw new ServiceException("点赞失败,已对当前动态点赞。");
} else {
this.likeService.addMomentLike(dbMoment.getId(), userName);
likeCount++;
}
} else {
if (likeStatus < 1) {
throw new ServiceException("取消失败,没有对当前动态点赞。");
} else {
this.likeService.deleteMomentLike(dbMoment.getId(), userName);
likeCount--;
}
}
BbsMoment updMoment = new BbsMoment();
updMoment.setId(dbMoment.getId());
updMoment.setLikeCount(likeCount);
this.updateById(updMoment);
return likeCount;
}
/**
* 构建动态实体
*
......@@ -182,7 +227,12 @@ public class BbsMomentServiceImpl extends ServiceImpl<BbsMomentMapper, BbsMoment
BbsMoment newMoment = new BbsMoment();
List<CreateMomentBO.Attachment> attachments = bo.getAttachments();
String linkUrl = bo.getLinkUrl();
if (MomentType.IMAGE == momentType || MomentType.VIDEO == momentType) {
if (MomentType.TEXT == momentType) {
if (StrUtil.isBlank(bo.getContent())) {
throw new ServiceException("发布失败,动态内容不能为空。");
}
}
else if (MomentType.IMAGE == momentType || MomentType.VIDEO == momentType) {
if (CollUtil.isEmpty(attachments)) {
throw new ServiceException("发布失败,动态附件不能为空。");
}
......@@ -190,13 +240,12 @@ public class BbsMomentServiceImpl extends ServiceImpl<BbsMomentMapper, BbsMoment
else if (MomentType.URL == momentType) {
if (StrUtil.isBlank(linkUrl)) {
throw new ServiceException("发布失败,动态链接不能为空。");
} else {
newMoment.setLinkUrl(linkUrl);
}
}
newMoment.setType(momentType.name());
newMoment.setContent(bo.getContent());
newMoment.setLinkUrl(linkUrl);
newMoment.setIsEnableComment(bo.getIsEnableComment());
newMoment.setIsEnableFeaturedComment(bo.getIsEnableFeaturedComment());
......@@ -237,11 +286,15 @@ public class BbsMomentServiceImpl extends ServiceImpl<BbsMomentMapper, BbsMoment
/**
* 填充动态附件和投票选项
* 填充动态附件、投票选项和评论
*
* @param moments 动态
*/
private void fillMoment(List<BbsMomentListVO> moments) {
log.info("=> 填充动态附件、投票选项和评论");
StopWatch stopWatch = new StopWatch();
stopWatch.start();
List<Long> momentIds = moments.stream().map(BbsMomentListVO::getId).collect(Collectors.toList());
if (CollUtil.isEmpty(momentIds)) {
return;
......@@ -260,32 +313,40 @@ public class BbsMomentServiceImpl extends ServiceImpl<BbsMomentMapper, BbsMoment
return opts.stream().collect(Collectors.groupingBy(BbsMomentVoteOption::getMomentId));
});
// 查询动态评论
CompletableFuture<Map<Long, List<BbsMomentComment>>> cf = CompletableFuture.supplyAsync(() -> {
List<BbsMomentComment> opts = this.commentService.getComments(momentIds);
return opts.stream().collect(Collectors.groupingBy(BbsMomentComment::getMomentId));
});
// 等待全部任务处理完毕
CompletableFuture.allOf(af, of).join();
CompletableFuture.allOf(af, of, cf).join();
Map<Long, List<BbsMomentAttachment>> attsMap = af.join();
Map<Long, List<BbsMomentVoteOption>> optsMap = of.join();
Map<Long, List<BbsMomentComment>> comsMap = cf.join();
// 填充动态
for (BbsMomentListVO m : moments) {
for (BbsMomentListVO moment : moments) {
// 填充动态附件
List<BbsMomentAttachment> as = attsMap.get(m.getId());
List<BbsMomentAttachment> as = attsMap.get(moment.getId());
if (CollUtil.isNotEmpty(as)) {
List<BbsMomentListVO.Attachment> avs = new ArrayList<>(as.size());
List<BbsAttachmentVO> avs = new ArrayList<>(as.size());
for (BbsMomentAttachment a : as) {
BbsMomentListVO.Attachment av = new BbsMomentListVO.Attachment();
BbsAttachmentVO av = new BbsAttachmentVO();
av.setId(a.getId());
av.setName(a.getName());
av.setUrl(a.getUrl());
avs.add(av);
}
m.setAttachments(avs);
moment.setAttachments(avs);
}
// 填充动态投票选项
List<BbsMomentVoteOption> os = optsMap.get(m.getId());
List<BbsMomentVoteOption> os = optsMap.get(moment.getId());
if (CollUtil.isNotEmpty(os)) {
List<BbsMomentListVO.VoteOption> ovs = new ArrayList<>(os.size());
List<BbsVoteOptionVO> ovs = new ArrayList<>(os.size());
for (BbsMomentVoteOption o : os) {
BbsMomentListVO.VoteOption ov = new BbsMomentListVO.VoteOption();
BbsVoteOptionVO ov = new BbsVoteOptionVO();
ov.setId(o.getId());
ov.setType(o.getType());
ov.setName(o.getName());
ov.setCode(o.getCode());
......@@ -294,13 +355,32 @@ public class BbsMomentServiceImpl extends ServiceImpl<BbsMomentMapper, BbsMoment
ov.setVoteCount(o.getVoteCount());
ovs.add(ov);
}
m.setVoteOptions(ovs);
moment.setVoteOptions(ovs);
}
// 填充动态评论
List<BbsMomentComment> cs = comsMap.get(moment.getId());
if (CollUtil.isNotEmpty(cs)) {
List<BbsCommentVO> cvs = new ArrayList<>(cs.size());
for (BbsMomentComment c : cs) {
BbsCommentVO cv = new BbsCommentVO();
cv.setId(c.getId());
cv.setNikeName(c.getNikeName());
cv.setUserName(c.getUserName());
cv.setContent(c.getContent());
cvs.add(cv);
}
moment.setComments(cvs);
}
}
} catch (Exception e) {
log.error("=> 填充动态附件和投票选项失败:", e);
}
stopWatch.stop();
long millis = stopWatch.getLastTaskTimeMillis();
log.info("=> 填充动态附件、投票选项和评论完毕,耗时:{} 毫秒", millis);
}
}
......@@ -4,8 +4,11 @@ import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tangguo.domain.BbsMoment;
import com.tangguo.domain.BbsMomentAttachment;
import com.tangguo.domain.BbsMomentVoteOption;
import com.tangguo.domain.bo.CreateMomentBO;
import com.tangguo.domain.vo.BbsMomentListVO;
import com.tangguo.domain.vo.BbsVoteOptionVO;
import com.tangguo.enums.VoteOptionType;
import com.tangguo.mapper.BbsMomentVoteOptionMapper;
import com.tangguo.service.IBbsMomentVoteOptionService;
......
......@@ -38,4 +38,23 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
r.create_time
</select>
<select id="selectComments" resultType="com.tangguo.domain.BbsMomentComment">
SELECT c.* FROM (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY moment_id ORDER BY create_time DESC) AS rn
FROM
bbs_moment_comment
WHERE
parent_id IS NULL
AND
moment_id IN
<foreach collection="momentIds" item="id" open="(" separator="," close=")">
#{id}
</foreach>
) AS c
WHERE rn &lt;= #{rows};
</select>
</mapper>
......@@ -78,11 +78,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<select id="selectMoments" resultType="com.tangguo.domain.vo.BbsMomentListVO">
SELECT
m.*,
uv.nike_name,
IF(l.id IS NOT NULL AND l.like_status = 1, 1, 0) AS is_like,
IF(v.id IS NOT NULL, 1, 0) AS is_vote,
IF(m.user_name = #{userName}, 1, 0) AS is_self
FROM
bbs_moment m
LEFT JOIN
qwmh_sys_user_view uv ON uv.user_name = m.user_name
LEFT JOIN
(
SELECT
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment