携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第21天,点击查看活动详情
4.3 点赞
因为点赞频率非常高,如果把数据存到硬盘里,读写性能很差,所以我们把数据存到redis里,存到内存里性能非常好,还可以使用redis的持久化机制将数据从内存存到硬盘里。
因为之前开发的时候是存到硬盘里,开发顺序 dao -> service -> controller,但是因为我们这次是存到redis里,直接写redis命令就可以,所以我们数据访问层就不开发了,直接将redis命令写到service层里。
首先我们写一个工具类专门生成redis的key,好复用这个工具类RedisKeyUtil。
配置 RedisTemplate 和 书写工具类生成redis的key
配置 RedisTemplate 之前已写过,所以可以不用写了。
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
// 方法参数 RedisConnectionFactory 会自动被工厂注入
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 通过链接工厂创建链接
template.setConnectionFactory(factory);
// 设置key的序列化方式
template.setKeySerializer(RedisSerializer.string());
// 设置value的序列化方式
template.setValueSerializer(RedisSerializer.json());
// 设置hash的key的序列化方式
template.setHashKeySerializer(RedisSerializer.string());
// 设置hash的value的序列化方式
template.setHashValueSerializer(RedisSerializer.json());
template.afterPropertiesSet();
return template;
}
}
写一个工具类去生成redis的key
public class RedisKeyUtil {
private static final String SPLIT = ":"; // key中间的 :
private static final String PREFIX_ENTITY_LIKE = "like:entity"; // key的前缀
// 某个实体的赞
// like:entity:entityType:entityId -> set(userId)
// 我们最终要把这个点赞的userId存到set里,因为我们未来可能会开发查看谁给我点赞的功能
public static String getEntityLikeKey(int entityType, int entityId) {
return PREFIX_ENTITY_LIKE + SPLIT + entityType + SPLIT + entityId;
}
}
开发 Service 层
@Service
public class LikeService {
@Autowired
private RedisTemplate redisTemplate;
// 点赞
public void like(int userId, int entityType, int entityId, int entityUserId) {
redisTemplate.execute(new SessionCallback() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType, entityId);
boolean isMember = operations.opsForSet().isMember(entityLikeKey, userId);
operations.multi();
if (isMember) {
// 点过赞再点赞就是取消赞
operations.opsForSet().remove(entityLikeKey, userId);
} else {
// 没过点赞的话就是点赞
operations.opsForSet().add(entityLikeKey, userId);
}
return operations.exec();
}
});
}
// 查询某实体(帖子被点赞)点赞的数量
public long findEntityLikeCount(int entityType, int entityId) {
String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType, entityId);
return redisTemplate.opsForSet().size(entityLikeKey);
}
// 查询某人对某实体的点赞状态
public int findEntityLikeStatus(int userId, int entityType, int entityId) {
String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType, entityId);
return redisTemplate.opsForSet().isMember(entityLikeKey, userId) ? 1 : 0;
}
}
开发 表现层
这个“点赞”是一个异步请求,所以方法上也要加上 @ResponseBody 注解
@Controller
public class LikeController {
@Autowired
private LikeService likeService;
@Autowired
private HostHolder hostHolder;
@RequestMapping(path = "/like", method = RequestMethod.POST)
@ResponseBody
public String like(int entityType, int entityId){
User user = hostHolder.getUser();
// 点赞
likeService.like(user.getId(), entityType, entityId);
// 数量
Long likeCount = likeService.findEntityLikeCount(entityType, entityId);
// 状态
int likeStatus = likeService.findEntityLikeStatus(user.getId(), entityType, entityId);
// 返回的结果
Map<String, Object> map = new HashMap<>();
map.put("likeCount", likeCount);
map.put("likeStatus", likeStatus);
return CommunityUtil.getJSONString(0, null, map);
}
}
点赞是在帖子详情页面点的,所以我们需要打开帖子详情页面discuss-detail.html做一个处理:
因为是异步请求,所以我们还需要提供一个js方法来实现点赞提交请求的逻辑。
discuss-detail.html:
我们还需要对首页index.html帖子显示的赞的数量做一个处理
首先是返回首页请求的HomeController
然后是 index.html
然后需要对帖子详情页面显示的赞的数量做一个处理(加一些内容)
DiscussPostController:
然后是详情页面discuss-detail.html