4.3 点赞

110 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第21天,点击查看活动详情

4.3 点赞

image-20220721071408217

因为点赞频率非常高,如果把数据存到硬盘里,读写性能很差,所以我们把数据存到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;
    }

}

image-20220721081019167

开发 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;
    }
}

image-20220721121940158

开发 表现层

这个“点赞”是一个异步请求,所以方法上也要加上 @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);
    }
}

image-20220721122128053

点赞是在帖子详情页面点的,所以我们需要打开帖子详情页面discuss-detail.html做一个处理:

因为是异步请求,所以我们还需要提供一个js方法来实现点赞提交请求的逻辑。

discuss-detail.html

image-20220721115948073

image-20220721120219952

image-20220721120303635

image-20220721120337825

image-20220721155757355

我们还需要对首页index.html帖子显示的赞的数量做一个处理

首先是返回首页请求的HomeController

image-20220721112050509

然后是 index.html

image-20220721112202343


然后需要对帖子详情页面显示的赞的数量做一个处理(加一些内容)

DiscussPostController

image-20220721114819755

image-20220721114958292

image-20220721115124997

image-20220721115517062

然后是详情页面discuss-detail.html

image-20220721121703542

image-20220721121759630

image-20220721121828424