实战篇之基于二进制思想的用户标签系统(Mysql+SpringBoot2

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 实战篇之基于二进制思想的用户标签系统(Mysql+SpringBoot2

四:一起动手实现用户标签系统 - 底层标签读写组件的实现
4.1: 建立用户标签表SQL
``CREATE TABLEt_user_tag(user_idbigint NOT NULL DEFAULT -1 COMMENT '用户 id',tag_info_01bigint NOT NULL DEFAULT '0' COMMENT '标签记录字段',tag_info_02bigint NOT NULL DEFAULT '0' COMMENT '标签记录字段',tag_info_03bigint NOT NULL DEFAULT '0' COMMENT '标签记录字段',create_timedatetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时 间',update_timedatetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin
COMMENT='用户标签记录';

  ###4.2:service层接口
  ```package com.laoyang.provider.service;

import com.laoyang.constants.UserTagsEnum;

/**
 * @author:Kevin
 * @create: 2023-08-01 09:53
 * @Description:
 */

public interface IUserTagService {

    /**
     * 设置标签 只能设置成功一次
     *
     * @param userId
     * @param userTagsEnum
     * @return
     */
    boolean setTag(Long userId, UserTagsEnum userTagsEnum);
    /**
     * 取消标签
     *
     * @param userId
     * @param userTagsEnum
     * @return
     */
    boolean cancelTag(Long userId, UserTagsEnum userTagsEnum);
    /**
     * 是否包含某个标签
     *
     * @param userId
     * @param userTagsEnum
     * @return
     */
    boolean containTag(Long userId,UserTagsEnum userTagsEnum);
}

```package com.laoyang.provider.service.impl;

import com.laoyang.common.utils.ConvertBeanUtils;
import com.laoyang.constants.UserTagFieldNameConstants;
import com.laoyang.constants.UserTagsEnum;
import com.laoyang.dto.UserTagDTO;
import com.laoyang.framework.redis.key.UserProviderCacheKeyBuilder;
import com.laoyang.provider.dao.mapper.IUserTagMapper;
import com.laoyang.provider.dao.po.UserTagPO;
import com.laoyang.provider.service.IUserTagService;
import com.laoyang.usils.TagInfoUtils;
import jakarta.annotation.Resource;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;

import java.nio.charset.StandardCharsets;

/**

  • @author:Kevin
  • @create: 2023-08-01 09:54
  • @Description:
    */

public class UserTagServiceImpl implements IUserTagService {

@Resource
private IUserTagMapper userTagMapper;
@Resource
private RedisTemplate<String, String> redisTemplate;

private RedisTemplate<String, UserTagDTO> userTagDTORedisTemplate;

@Resource
private UserProviderCacheKeyBuilder cacheKeyBuilder;

@Override
public boolean setTag(Long userId, UserTagsEnum userTagsEnum) {
    boolean updateStatus = userTagMapper.setTag(userId,userTagsEnum.getFieldName(),userTagsEnum.getTag()) > 0;
    if (updateStatus){
        String redisKey = cacheKeyBuilder.buildtagInfoKey(userId);
        userTagDTORedisTemplate.delete(redisKey);
        return  true;
    }
    String key = cacheKeyBuilder.buildTagLockKey(userId);
    //TODO 分布式锁 实现多个线程之间对同一个资源的互斥访问,保证同一时间只有一个线程能够获取到锁并执行相应的操作
    String setNxResult = redisTemplate.execute(new RedisCallback<String>() {
        @Override
        public String doInRedis(RedisConnection connection) throws DataAccessException {
            RedisSerializer keySerializer = redisTemplate.getKeySerializer();
            RedisSerializer valueSerializer = redisTemplate.getValueSerializer();
            String result = (String) connection.execute("set", keySerializer.serialize(key),
                    valueSerializer.serialize("-1"),
                    "NX".getBytes(StandardCharsets.UTF_8),
                    "EX".getBytes(StandardCharsets.UTF_8),
                    "3".getBytes(StandardCharsets.UTF_8));
            return result;
        }
    });

    if (!"OK".equals(setNxResult)){
        return false;
    }

    UserTagPO userTagPO = userTagMapper.selectById(userId);
    if (userTagPO!=null){
        return false;
    }
    userTagPO = new UserTagPO();
    userTagPO.setUserId(userId);
    userTagMapper.insert(userTagPO);
    updateStatus = userTagMapper.setTag(userId,userTagsEnum.getFieldName(),userTagsEnum.getTag()) > 0;
    redisTemplate.delete(key);
    return updateStatus;
}

@Override
public boolean cancelTag(Long userId, UserTagsEnum userTagsEnum) {
    boolean cancleStatus = userTagMapper.cancelTag(userId,userTagsEnum.getFieldName(),userTagsEnum.getTag()) > 0;
    if (cancleStatus){
        return false;
    }
    String redisKey = cacheKeyBuilder.buildtagInfoKey(userId);
    userTagDTORedisTemplate.delete(redisKey);
    return true;
}

@Override
public boolean containTag(Long userId, UserTagsEnum userTagsEnum) {
    UserTagDTO userTagDTO = this.queryByUserId(userId);
    if (userTagDTO == null) {
        return false;
    }
    String queryFieldName = userTagsEnum.getFieldName();
    if
    (UserTagFieldNameConstants.TAG_INFO_01.equals(queryFieldName)) {
        return
                TagInfoUtils.isContain(userTagDTO.getTagInfo01(),
                        userTagsEnum.getTag());
    } else if
    (UserTagFieldNameConstants.TAG_INFO_02.equals(queryFieldName)) {
        return
                TagInfoUtils.isContain(userTagDTO.getTagInfo02(),
                        userTagsEnum.getTag());
    } else if
    (UserTagFieldNameConstants.TAG_INFO_03.equals(queryFieldName)) {
        return
                TagInfoUtils.isContain(userTagDTO.getTagInfo03(),
                        userTagsEnum.getTag());
    }
    return false;
}

/**
 * 从redis查询用户标签
 * @param userId
 * @return
 */
private UserTagDTO queryByUserId(Long userId){
    String redisKey = cacheKeyBuilder.buildtagInfoKey(userId);
    UserTagDTO userTagDTO = userTagDTORedisTemplate.opsForValue().get(redisKey);
    if (userTagDTO != null){
        return userTagDTO;
    }
    UserTagPO userTagPO = userTagMapper.selectById(userId);
    if (userTagPO == null){
        return null;
    }
    userTagDTO = ConvertBeanUtils.convert(userTagPO,UserTagDTO.class);
    userTagDTORedisTemplate.opsForValue().set(redisKey, userTagDTO);

    return userTagDTO;

}

}
```
说明:我们使用了redis作为缓存,mybatisplus, 并自行创建了redis业务主键生成工具类等等,会放在最后,先把核心代码呈现。这里说明下使用到了redis分布式实现

这段代码是使用RedisTemplate执行一个"set"命令,并设置了一些选项参数。下面对代码进行解释:

首先,通过redisTemplate.getKeySerializer()获取key的序列化器,通过redisTemplate.getValueSerializer()获取value的序列化器。
在RedisCallback的doInRedis方法中,通过RedisConnection的execute方法执行"set"命令。
参数中,keySerializer.serialize(key)将key序列化为字节数组,valueSerializer.serialize("-1")将value序列化为字节数组。
"NX".getBytes(StandardCharsets.UTF_8)表示设置NX选项,即只有在key不存在时才进行set操作。
"EX".getBytes(StandardCharsets.UTF_8)表示设置EX选项,即设置key的过期时间为3秒。
"3".getBytes(StandardCharsets.UTF_8)表示设置key的过期时间为3秒。
connection.execute方法返回的是一个Object类型的结果,需要将其转换为String类型并返回。 总体来说,这段代码的作用是在Redis中执行一个set命令,将key和value存储到Redis中,并设置了过期时间和NX选项,确保只有在key不存在时才进行set操作。

    当多个节点同时尝试执行set操作来设置同一个key时,只有一个节点能够成功设置,因为Redis中的set命令默认具有原子性。如果设置了NX选项,即只有在key不存在时才进行set操作,那么只有第一个节点能够成功设置该key,其他节点将无法设置。 通过利用这个特性,可以将某个共享资源对应的key作为锁的名称,多个节点试图通过set操作来竞争该锁。只有一个节点能够成功设置该锁的key,即获得了分布式锁。其他节点则在设置失败后,可以选择等待或者进行其他处理。 同时,为了避免因为某个节点获得锁后发生故障而导致锁一直无法释放,还可以为锁设置过期时间。当锁的持有者在一定时间后未能释放锁,锁将自动过期并被其他节点获取。 综上所述,通过使用Redis的set操作和一些选项参数,可以实现简单的分布式锁。多个节点可以通过竞争设置同一个key来获得锁,并通过设置过期时间来避免因为锁的持有者发生故障而导致锁一直无法释放。

————————————————

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
24天前
|
存储 关系型数据库 MySQL
轻松入门MySQL:数据库设计之范式规范,优化企业管理系统效率(21)
轻松入门MySQL:数据库设计之范式规范,优化企业管理系统效率(21)
|
24天前
|
存储 关系型数据库 MySQL
轻松入门MySQL:优化进销存管理,掌握MySQL索引,提升系统效率(11)
轻松入门MySQL:优化进销存管理,掌握MySQL索引,提升系统效率(11)
|
7天前
|
小程序 JavaScript Java
基于SpringBoot+Vue+uniapp微信小程序的医院核酸检测服务系统的详细设计和实现
基于SpringBoot+Vue+uniapp微信小程序的医院核酸检测服务系统的详细设计和实现
32 0
|
8天前
|
人工智能 移动开发 前端开发
Springboot医院智慧导诊系统源码:精准推荐科室
医院智慧导诊系统是在医疗中使用的引导患者自助就诊挂号,在就诊的过程中有许多患者不知道需要挂什么号,要看什么病,通过智慧导诊系统,可输入自身疾病的症状表现,或选择身体部位,在经由智慧导诊系统多维度计算,精准推荐科室,引导患者挂号就诊,实现科学就诊,不用担心挂错号。
18 2
|
9天前
|
人工智能 前端开发 Java
Java语言开发的AI智慧导诊系统源码springboot+redis 3D互联网智导诊系统源码
智慧导诊解决盲目就诊问题,减轻分诊工作压力。降低挂错号比例,优化就诊流程,有效提高线上线下医疗机构接诊效率。可通过人体画像选择症状部位,了解对应病症信息和推荐就医科室。
150 10
|
9天前
|
Java 关系型数据库 MySQL
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
UWB (ULTRA WIDE BAND, UWB) 技术是一种无线载波通讯技术,它不采用正弦载波,而是利用纳秒级的非正弦波窄脉冲传输数据,因此其所占的频谱范围很宽。一套UWB精确定位系统,最高定位精度可达10cm,具有高精度,高动态,高容量,低功耗的应用。
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
|
10天前
|
存储 数据可视化 安全
Java全套智慧校园系统源码springboot+elmentui +Quartz可视化校园管理平台系统源码 建设智慧校园的5大关键技术
智慧校园指的是以物联网为基础的智慧化的校园工作、学习和生活一体化环境,这个一体化环境以各种应用服务系统为载体,将教学、科研、管理和校园生活进行充分融合。无处不在的网络学习、融合创新的网络科研、透明高效的校务治理、丰富多彩的校园文化、方便周到的校园生活。简而言之,“要做一个安全、稳定、环保、节能的校园。
35 6
|
17天前
|
监控 安全 关系型数据库
基于vue2 + element +mysql医院不良事件上报系统源码
不良事件管理系统从时间上报、PDCA分析、事件整改、评估效果实行闭环管理和分析,满足医院追根溯源,全流程闭环管理,提高不良事件上报率,减少同类不良事件发生,提高医疗安全。通过报告不良事件,及时发现潜在的不安全因素
19 1
|
4天前
|
SQL 存储 关系型数据库
MySQL Cluster集群安装及使用
MySQL Cluster集群安装及使用
|
8天前
|
关系型数据库 MySQL 数据库
《MySQL 简易速速上手小册》第1章:MySQL 基础和安装(2024 最新版)
《MySQL 简易速速上手小册》第1章:MySQL 基础和安装(2024 最新版)
33 4