完善点赞和收藏的实时展示

This commit is contained in:
柏码の讲师 2023-10-23 00:09:07 +08:00
parent e808240a32
commit ebcd6ab4ae
7 changed files with 51 additions and 5 deletions

View File

@ -16,7 +16,7 @@ public class Interact {
public static Interact parseInteract(String key, String type) { public static Interact parseInteract(String key, String type) {
String[] keys = key.split(":"); String[] keys = key.split(":");
return new Interact(Integer.parseInt(keys[0]), return new Interact(Integer.parseInt(keys[0]),
Integer.parseInt(keys[0]), Integer.parseInt(keys[1]),
new Date(), type); new Date(), type);
} }

View File

@ -1,5 +1,6 @@
package com.example.entity.vo.response; package com.example.entity.vo.response;
import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import java.util.Date; import java.util.Date;
@ -12,6 +13,14 @@ public class TopicDetailVO {
Integer type; Integer type;
Date time; Date time;
User user; User user;
Interact interact;
@Data
@AllArgsConstructor
public static class Interact {
Boolean like;
Boolean collect;
}
@Data @Data
public static class User { public static class User {

View File

@ -16,4 +16,6 @@ public class TopicPreviewVO {
int uid; int uid;
String username; String username;
String avatar; String avatar;
int like;
int collect;
} }

View File

@ -6,6 +6,7 @@ import com.example.entity.dto.Topic;
import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List; import java.util.List;
@ -30,4 +31,14 @@ public interface TopicMapper extends BaseMapper<Topic> {
</script> </script>
""") """)
int deleteInteract(List<Interact> interacts, String type); int deleteInteract(List<Interact> interacts, String type);
@Select("""
select count(*) from db_topic_interact_${type} where tid = #{tid}
""")
int interactCount(int tid, String type);
@Select("""
select count(*) from db_topic_interact_${type} where tid = #{tid} and uid = #{uid}
""")
int userInteractCount(int tid, int uid, String type);
} }

View File

@ -109,6 +109,11 @@ public class TopicServiceImpl extends ServiceImpl<TopicMapper, Topic> implements
TopicDetailVO vo = new TopicDetailVO(); TopicDetailVO vo = new TopicDetailVO();
Topic topic = baseMapper.selectById(tid); Topic topic = baseMapper.selectById(tid);
BeanUtils.copyProperties(topic, vo); BeanUtils.copyProperties(topic, vo);
TopicDetailVO.Interact interact = new TopicDetailVO.Interact(
hasInteract(tid, topic.getUid(), "like"),
hasInteract(tid, topic.getUid(), "collect")
);
vo.setInteract(interact);
TopicDetailVO.User user = new TopicDetailVO.User(); TopicDetailVO.User user = new TopicDetailVO.User();
BeanUtils.copyProperties(topic, user); BeanUtils.copyProperties(topic, user);
vo.setUser(this.fillUserDetailsByPrivacy(user, topic.getUid())); vo.setUser(this.fillUserDetailsByPrivacy(user, topic.getUid()));
@ -124,6 +129,13 @@ public class TopicServiceImpl extends ServiceImpl<TopicMapper, Topic> implements
} }
} }
private boolean hasInteract(int tid, int uid, String type){
String key = tid + ":" + uid;
if(template.opsForHash().hasKey(type, key))
return Boolean.parseBoolean(template.opsForHash().entries(type).get(key).toString());
return baseMapper.userInteractCount(tid, uid, type) > 0;
}
/** /**
* 由于论坛交互数据如点赞收藏等更新可能会非常频繁 * 由于论坛交互数据如点赞收藏等更新可能会非常频繁
* 更新信息实时到MySQL不太现实所以需要用Redis做缓冲并在合适的时机一次性入库一段时间内的全部数据 * 更新信息实时到MySQL不太现实所以需要用Redis做缓冲并在合适的时机一次性入库一段时间内的全部数据
@ -134,7 +146,6 @@ public class TopicServiceImpl extends ServiceImpl<TopicMapper, Topic> implements
private final Map<String, Boolean> state = new HashMap<>(); private final Map<String, Boolean> state = new HashMap<>();
ScheduledExecutorService service = Executors.newScheduledThreadPool(2); ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
private void saveInteractData(String type) { private void saveInteractData(String type) {
System.out.println(state);
if(!state.getOrDefault(type, false)) { if(!state.getOrDefault(type, false)) {
state.put(type, true); state.put(type, true);
service.schedule(() -> { service.schedule(() -> {
@ -146,7 +157,6 @@ public class TopicServiceImpl extends ServiceImpl<TopicMapper, Topic> implements
private void saveInteract(String type) { private void saveInteract(String type) {
synchronized (type.intern()) { synchronized (type.intern()) {
System.out.println(type);
List<Interact> check = new LinkedList<>(); List<Interact> check = new LinkedList<>();
List<Interact> uncheck = new LinkedList<>(); List<Interact> uncheck = new LinkedList<>();
template.opsForHash().entries(type).forEach((k, v) -> { template.opsForHash().entries(type).forEach((k, v) -> {
@ -177,6 +187,8 @@ public class TopicServiceImpl extends ServiceImpl<TopicMapper, Topic> implements
TopicPreviewVO vo = new TopicPreviewVO(); TopicPreviewVO vo = new TopicPreviewVO();
BeanUtils.copyProperties(accountMapper.selectById(topic.getUid()), vo); BeanUtils.copyProperties(accountMapper.selectById(topic.getUid()), vo);
BeanUtils.copyProperties(topic, vo); BeanUtils.copyProperties(topic, vo);
vo.setLike(baseMapper.interactCount(topic.getId(), "like"));
vo.setCollect(baseMapper.interactCount(topic.getId(), "collect"));
List<String> images = new ArrayList<>(); List<String> images = new ArrayList<>();
StringBuilder previewText = new StringBuilder(); StringBuilder previewText = new StringBuilder();
JSONArray ops = JSONObject.parseObject(topic.getContent()).getJSONArray("ops"); JSONArray ops = JSONObject.parseObject(topic.getContent()).getJSONArray("ops");

View File

@ -23,7 +23,11 @@ const topic = reactive({
comments: [] comments: []
}) })
get(`/api/forum/topic?tid=${tid}`, data => topic.data = data) get(`/api/forum/topic?tid=${tid}`, data => {
topic.data = data
topic.like = data.interact.like
topic.collect = data.interact.collect
})
const content = computed(() => { const content = computed(() => {
const ops = JSON.parse(topic.data.content).ops const ops = JSON.parse(topic.data.content).ops

View File

@ -10,7 +10,7 @@ import {
Edit, Edit,
EditPen, EditPen,
Link, Link,
Microphone Microphone, CircleCheck, Star
} from "@element-plus/icons-vue"; } from "@element-plus/icons-vue";
import {computed, reactive, ref, watch} from "vue" import {computed, reactive, ref, watch} from "vue"
import Weather from "@/components/Weather.vue"; import Weather from "@/components/Weather.vue";
@ -156,6 +156,14 @@ navigator.geolocation.getCurrentPosition(position => {
<div style="display: grid;grid-template-columns: repeat(3, 1fr);grid-gap: 10px"> <div style="display: grid;grid-template-columns: repeat(3, 1fr);grid-gap: 10px">
<el-image v-for="img in item.images" fit="cover" :src="img" class="topic-image"></el-image> <el-image v-for="img in item.images" fit="cover" :src="img" class="topic-image"></el-image>
</div> </div>
<div style="display: flex;gap: 20px;font-size: 13px;margin-top: 10px">
<div>
<el-icon style="vertical-align: middle"><CircleCheck /></el-icon> {{item.like}} 点赞
</div>
<div>
<el-icon style="vertical-align: middle"><Star /></el-icon> {{item.collect}} 收藏
</div>
</div>
</light-card> </light-card>
</div> </div>
</div> </div>