import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.jeecg.boot.starter.lock.client.RedissonLockClient;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.modules.system.counter.entity.Counter;
import org.jeecg.modules.system.counter.mapper.CounterMapper;
import org.jeecg.modules.system.counter.service.ICounterService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import javax.transaction.Transactional;
import java.util.Objects;

/**
 * @Description: 计数器
 * @Author: jiangfeng
 * @Date: 2022-07-03
 * @Version: V1.0
 */
@Service
@Slf4j
public class CounterServiceImpl extends ServiceImpl<CounterMapper, Counter> implements ICounterService {
    @Autowired
    private ICounterService counterService;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    RedissonLockClient redissonLock;

    @Override
    public String tableCodeCounter(String key, int codeLength, String prefix) throws Exception{
        String finalCode;
        // 从 redis 中获取一个自增的计数器
        long incrementCounter = RedisAutoIncr(key);
        // 格式化编码长度,不包括前缀
        String formatCounter = String.format("%0" + codeLength + "d", incrementCounter);
        // 生成后的计数器
        if (StringUtils.isNotEmpty(prefix)) {
            finalCode = prefix + formatCounter;
        } else {
            finalCode = String.valueOf(incrementCounter);
        }
        // 保存至数据库
        boolean isSubmit = saveToTable(key, codeLength, prefix, incrementCounter);
        if (!isSubmit) {
            throw new Exception("数据库保存失败");
        }
        return finalCode;
    }

    @Transactional
    public boolean saveToTable(String key, int codeLength, String prefix, long counter){
        // 兼容数据库此处使用查询来判断数据库是否存在数据
        boolean submit;
        QueryWrapper<Counter> queryWrapper= new QueryWrapper<>();
        queryWrapper.eq("field_name", key);
        Counter countValue = counterService.getOne(queryWrapper);
        Counter entity = new Counter();
        if (countValue == null) {
            //不存在,新增
            entity.setFieldName(key);
            entity.setCodeLength(codeLength);
            entity.setPrefix(prefix);
            entity.setCounter(counter);
            submit = counterService.save(entity);
        } else {
            //存在,修改
            entity.setId(countValue.getId());
            entity.setCounter(counter);
            submit = counterService.updateById(entity);
        }
        return submit;
    }

    /**
     * @description 生成分布式唯一自增Id
     * @param key
     */
    public Long RedisAutoIncr(String key) { // 初始值是从1 开始 还是从0开始?
        // 加Redis分布式锁 双重保险,防止初始化的时候并发【不存在会有两种情况,一种是没有这个key的自增,一种是数据库中有,redis过期了或重启了】
        RedisAtomicLong redisAtomicLong;
        if (redisUtil.get(key) == null) {
            // 仅在key为空的时候加锁,不影响执行效率
            try {
                redissonLock.tryLock(key+ "_redisLock", -1, 3000);
                //查询是否在数据库中存在
                Counter counter = counterService.getOne(new QueryWrapper<Counter>().eq("field_name", key));
                if (counter!=null) {
                    // 从数据库赋值后,redis初始化不自增
                    redisUtil.set(key, counter.getCounter()+1);
                }
                redisAtomicLong = new RedisAtomicLong(key, Objects.requireNonNull(redisTemplate.getConnectionFactory()));
                // redisAtomicLong.persist();
                // -1 为不过期
                log.warn(String.valueOf(redisAtomicLong.getExpire()));
                // 解锁
                redissonLock.unlock(key+ "_redisLock");
            } catch (Exception e){
                // 异常了,解锁
                redissonLock.unlock(key+ "_redisLock");
                throw e;
            }
        } else {
            // 如果存在key ,increment 是原子性的,不会重复计数
            redisAtomicLong = new RedisAtomicLong(key, Objects.requireNonNull(redisTemplate.getConnectionFactory()) );
        }

        long Incr = redisAtomicLong.getAndIncrement();
        log.info("Incr------:" + Incr);
        return Incr;
    }
}

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐