1.初始化版本,2.引入thirdpart模块,引入canteen模块,初始化菜品分类,spu

This commit is contained in:
zjf committed 2024-12-06 16:54:30 +08:00
commit aff77150d6
1595 files changed
+168833

No files matched your search

@@ -0,0 +1,74 @@
/*
* Copyright (c) 2018-2025, pjcloud All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: pjcloud
*/
package com.bsd.pjcloud.common.sequence;
import com.bsd.pjcloud.common.sequence.properties.BaseSequenceProperties;
import com.bsd.pjcloud.common.sequence.range.impl.db.SequenceTable;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.anyline.metadata.Column;
import org.anyline.metadata.Index;
import org.anyline.metadata.Table;
import org.anyline.proxy.ServiceProxy;
import org.anyline.service.AnylineService;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.context.WebServerInitializedEvent;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import java.util.LinkedHashMap;
import java.util.Objects;
/**
* @author pjcloud
* @date 2019-05-26
*/
@Slf4j
@Configuration
@RequiredArgsConstructor
@EnableConfigurationProperties(BaseSequenceProperties.class)
public class SequenceAutoConfiguration {
private final BaseSequenceProperties sequenceDbProperties;
/**
* 启动后自动创建表
*/
@EventListener({WebServerInitializedEvent.class})
public void onApplicationReady() throws Exception {
// 判断是否需要创建表
AnylineService service = ServiceProxy.service();
Table tableMetadata = service.metadata().table(sequenceDbProperties.getDb().getTableName(), false);
if (Objects.isNull(tableMetadata)) {
Table table = Table.from(SequenceTable.class);
table.setName(sequenceDbProperties.getDb().getTableName());
table.setComment("序列号区间管理表");
table.setPrimaryKey(SequenceTable.Fields.id);
LinkedHashMap<String, Index> indexLinkedHashMap = new LinkedHashMap<>();
Index nameUnique = new Index(SequenceTable.Fields.name);
nameUnique.setUnique(true);
nameUnique.addColumn(new Column(SequenceTable.Fields.name));
indexLinkedHashMap.put(SequenceTable.Fields.name, nameUnique);
table.setIndexes(indexLinkedHashMap);
service.ddl().create(table);
log.debug("发号器自动创建表成功:{} ", table);
}
}
}
@@ -0,0 +1,98 @@
package com.bsd.pjcloud.common.sequence.builder;
import com.bsd.pjcloud.common.sequence.range.BizName;
import com.bsd.pjcloud.common.sequence.range.impl.db.DbSeqRangeMgr;
import com.bsd.pjcloud.common.sequence.sequence.Sequence;
import com.bsd.pjcloud.common.sequence.sequence.impl.DefaultRangeSequence;
import javax.sql.DataSource;
/**
* 基于DB取步长,序列号生成器构建者
*
* @author xuan
*/
public class DbSeqBuilder implements SeqBuilder {
/**
* 数据库数据源[必选]
*/
private DataSource dataSource;
/**
* 业务名称[必选]
*/
private BizName bizName;
/**
* 存放序列号步长的表[可选:默认:sequence]
*/
private String tableName = "sequence";
/**
* 并发是数据使用了乐观策略,这个是失败重试的次数[可选:默认:100]
*/
private int retryTimes = 100;
/**
* 获取range步长[可选:默认:1000]
*/
private int step = 1000;
/**
* 序列号分配起始值[可选:默认:0]
*/
private long stepStart = 0;
public static DbSeqBuilder create() {
DbSeqBuilder builder = new DbSeqBuilder();
return builder;
}
@Override
public Sequence build() {
// 利用DB获取区间管理器
DbSeqRangeMgr dbSeqRangeMgr = new DbSeqRangeMgr();
dbSeqRangeMgr.setTableName(this.tableName);
dbSeqRangeMgr.setRetryTimes(this.retryTimes);
dbSeqRangeMgr.setStep(this.step);
dbSeqRangeMgr.setStepStart(stepStart);
dbSeqRangeMgr.init();
// 构建序列号生成器
DefaultRangeSequence sequence = new DefaultRangeSequence();
sequence.setName(this.bizName);
sequence.setSeqRangeMgr(dbSeqRangeMgr);
return sequence;
}
public DbSeqBuilder dataSource(DataSource dataSource) {
this.dataSource = dataSource;
return this;
}
public DbSeqBuilder tableName(String tableName) {
this.tableName = tableName;
return this;
}
public DbSeqBuilder retryTimes(int retryTimes) {
this.retryTimes = retryTimes;
return this;
}
public DbSeqBuilder step(int step) {
this.step = step;
return this;
}
public DbSeqBuilder bizName(BizName bizName) {
this.bizName = bizName;
return this;
}
public DbSeqBuilder stepStart(long stepStart) {
this.stepStart = stepStart;
return this;
}
}
@@ -0,0 +1,70 @@
package com.bsd.pjcloud.common.sequence.builder;
import com.bsd.pjcloud.common.core.util.SpringContextHolder;
import com.bsd.pjcloud.common.sequence.range.BizName;
import com.bsd.pjcloud.common.sequence.range.impl.redis.RedisSeqRangeMgr;
import com.bsd.pjcloud.common.sequence.sequence.Sequence;
import com.bsd.pjcloud.common.sequence.sequence.impl.DefaultRangeSequence;
import org.springframework.data.redis.core.StringRedisTemplate;
/**
* 基于redis取步长,序列号生成器构建者
*
* @author xuan on 2018/5/30.
*/
public class RedisSeqBuilder implements SeqBuilder {
/**
* 业务名称[必选]
*/
private BizName bizName;
/**
* 获取range步长[可选,默认:1000]
*/
private int step = 1000;
/**
* 序列号分配起始值[可选:默认:0]
*/
private long stepStart = 0;
public static RedisSeqBuilder create() {
RedisSeqBuilder builder = new RedisSeqBuilder();
return builder;
}
@Override
public Sequence build() {
// 利用Redis获取区间管理器
RedisSeqRangeMgr redisSeqRangeMgr = new RedisSeqRangeMgr();
redisSeqRangeMgr.setStringRedisTemplate(null);
redisSeqRangeMgr.setStepStart(stepStart);
redisSeqRangeMgr.setStringRedisTemplate(SpringContextHolder.getBean(StringRedisTemplate.class));
redisSeqRangeMgr.init();
// 构建序列号生成器
DefaultRangeSequence sequence = new DefaultRangeSequence();
sequence.setName(this.bizName);
sequence.setSeqRangeMgr(redisSeqRangeMgr);
return sequence;
}
public RedisSeqBuilder step(int step) {
this.step = step;
return this;
}
public RedisSeqBuilder bizName(BizName bizName) {
this.bizName = bizName;
return this;
}
public RedisSeqBuilder stepStart(long stepStart) {
this.stepStart = stepStart;
return this;
}
}
@@ -0,0 +1,18 @@
package com.bsd.pjcloud.common.sequence.builder;
import com.bsd.pjcloud.common.sequence.sequence.Sequence;
/**
* 序列号生成器构建者
*
* @author xuan on 2018/5/30.
*/
public interface SeqBuilder {
/**
* 构建一个序列号生成器
* @return 序列号生成器
*/
Sequence build();
}
@@ -0,0 +1,46 @@
package com.bsd.pjcloud.common.sequence.builder;
import com.bsd.pjcloud.common.sequence.sequence.Sequence;
import com.bsd.pjcloud.common.sequence.sequence.impl.SnowflakeSequence;
/**
* 基于雪花算法,序列号生成器构建者
*
* @author xuan on 2018/5/30.
*/
public class SnowflakeSeqBuilder implements SeqBuilder {
/**
* 数据中心ID,值的范围在[0,31]之间,一般可以设置机房的IDC[必选]
*/
private long datacenterId;
/**
* 工作机器ID,值的范围在[0,31]之间,一般可以设置机器编号[必选]
*/
private long workerId;
public static SnowflakeSeqBuilder create() {
SnowflakeSeqBuilder builder = new SnowflakeSeqBuilder();
return builder;
}
@Override
public Sequence build() {
SnowflakeSequence sequence = new SnowflakeSequence();
sequence.setDatacenterId(this.datacenterId);
sequence.setWorkerId(this.workerId);
return sequence;
}
public SnowflakeSeqBuilder datacenterId(long datacenterId) {
this.datacenterId = datacenterId;
return this;
}
public SnowflakeSeqBuilder workerId(long workerId) {
this.workerId = workerId;
return this;
}
}
@@ -0,0 +1,20 @@
package com.bsd.pjcloud.common.sequence.exception;
/**
* 序列号生成异常
*
* @author xuan on 2018/1/10.
*/
public class SeqException extends RuntimeException {
private static final long serialVersionUID = 1L;
public SeqException(String message) {
super(message);
}
public SeqException(Throwable cause) {
super(cause);
}
}
@@ -0,0 +1,42 @@
package com.bsd.pjcloud.common.sequence.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
/**
* @author pjcloud
* @date 2019-05-26
* <p>
* 发号器通用属性
*/
@Data
@ConfigurationProperties(prefix = "pjcloud.xsequence")
public class BaseSequenceProperties {
/**
* 获取range步长[可选,默认:1000]
*/
private int step = 10;
/**
* 序列号分配起始值[可选:默认:0]
*/
private long stepStart = 0;
/**
* 业务名称
*/
private String bizName = "pjcloud";
@NestedConfigurationProperty
private SequenceSnowflakeProperties snowflake = new SequenceSnowflakeProperties();
@NestedConfigurationProperty
private SequenceRedisProperties redis = new SequenceRedisProperties();
@NestedConfigurationProperty
private SequenceDbProperties db = new SequenceDbProperties();
}
@@ -0,0 +1,29 @@
package com.bsd.pjcloud.common.sequence.properties;
/**
* @author pjcloud
* @date 2019-05-26
*/
import lombok.Data;
/**
* @author pjcloud
* @date 2019/5/26
* <p>
* 发号器DB配置属性
*/
@Data
public class SequenceDbProperties {
/**
* 表名称
*/
private String tableName = "pjcloud_sequence";
/**
* 重试次数
*/
private int retryTimes = 1;
}
@@ -0,0 +1,19 @@
package com.bsd.pjcloud.common.sequence.properties;
/**
* @author pjcloud
* @date 2019-05-26
*/
import lombok.Data;
/**
* @author pjcloud
* @date 2019/5/26
* <p>
* 发号器Redis配置属性
*/
@Data
public class SequenceRedisProperties {
}
@@ -0,0 +1,24 @@
package com.bsd.pjcloud.common.sequence.properties;
import lombok.Data;
/**
* @author pjcloud
* @date 2019-05-26
* <p>
* Snowflake 发号器属性
*/
@Data
public class SequenceSnowflakeProperties {
/**
* 数据中心ID,值的范围在[0,31]之间,一般可以设置机房的IDC[必选]
*/
private long datacenterId;
/**
* 工作机器ID,值的范围在[0,31]之间,一般可以设置机器编号[必选]
*/
private long workerId;
}
@@ -0,0 +1,17 @@
package com.bsd.pjcloud.common.sequence.range;
/**
* @author pjcloud
* @date 2019-05-26
* <p>
* 名称生成器
*/
public interface BizName {
/**
* 生成名称
* @return 返回文本序号
*/
String create();
}
@@ -0,0 +1,73 @@
package com.bsd.pjcloud.common.sequence.range;
import java.util.concurrent.atomic.AtomicLong;
/**
* 序列号区间对象模型
*
* @author xuan on 2018/1/10.
*/
public class SeqRange {
/**
* 区间的序列号开始值
*/
private final long min;
/**
* 区间的序列号结束值
*/
private final long max;
/**
* 区间的序列号当前值
*/
private final AtomicLong value;
/**
* 区间的序列号是否分配完毕,每次分配完毕就会去重新获取一个新的区间
*/
private volatile boolean over = false;
public SeqRange(long min, long max) {
this.min = min;
this.max = max;
this.value = new AtomicLong(min);
}
/**
* 返回并递增下一个序列号
* @return 下一个序列号,如果返回-1表示序列号分配完毕
*/
public long getAndIncrement() {
long currentValue = value.getAndIncrement();
if (currentValue > max) {
over = true;
return -1;
}
return currentValue;
}
public long getMin() {
return min;
}
public long getMax() {
return max;
}
public boolean isOver() {
return over;
}
public void setOver(boolean over) {
this.over = over;
}
@Override
public String toString() {
return "max: " + max + ", min: " + min + ", value: " + value;
}
}
@@ -0,0 +1,28 @@
package com.bsd.pjcloud.common.sequence.range;
import com.bsd.pjcloud.common.sequence.exception.SeqException;
/**
* 区间管理器
*
* @author xuan on 2018/1/10.
*/
public interface SeqRangeMgr {
/**
* 获取指定区间名的下一个区间
*
* @param name 区间名
* @return 返回区间
* @throws SeqException 异常
*/
SeqRange nextRange(String name) throws SeqException;
/**
* 初始化
*/
default void init() {
}
}
@@ -0,0 +1,83 @@
package com.bsd.pjcloud.common.sequence.range.impl.db;
import cn.hutool.core.util.RandomUtil;
import com.bsd.pjcloud.common.sequence.exception.SeqException;
import org.anyline.data.param.ConfigStore;
import org.anyline.data.param.init.DefaultConfigStore;
import org.anyline.entity.DataRow;
import org.anyline.proxy.ServiceProxy;
import org.anyline.service.AnylineService;
import java.time.LocalDateTime;
import java.util.Objects;
/**
* 操作DB帮助类
*
* @author xuan on 2018/4/29.
*/
abstract class BaseDbHelper {
private static final long DELTA = 100000000L;
/**
* 更新区间,乐观策略
*
* @param tableName 表名
* @param newValue 更新新数据
* @param oldValue 更新旧数据
* @param name 区间名称
* @return 成功/失败
*/
static boolean updateRange(String tableName, Long newValue, Long oldValue, String name) {
AnylineService service = ServiceProxy.service();
SequenceTable sequenceTable = new SequenceTable();
sequenceTable.setValue(newValue);
sequenceTable.setGmtModified(LocalDateTime.now());
ConfigStore configs = new DefaultConfigStore();
configs.and("value", oldValue);
return service.update(tableName, sequenceTable, configs) > 0;
}
/**
* 查询区间,如果区间不存在,会新增一个区间,并返回null,由上层重新执行
*
* @param tableName 来源
* @param name 区间名称
* @param stepStart 初始位置
* @return 区间值
*/
static Long selectRange(String tableName, String name, long stepStart) {
AnylineService service = ServiceProxy.service();
DataRow dataRow = service.query(tableName, SequenceTable.Fields.name + ":" + name);
if (Objects.isNull(dataRow)) {
// 没有此类型数据,需要初始化
SequenceTable sequenceTable = new SequenceTable();
sequenceTable.setId(RandomUtil.getSecureRandom().nextLong());
sequenceTable.setName(name);
sequenceTable.setValue(stepStart);
sequenceTable.setGmtCreate(LocalDateTime.now());
sequenceTable.setGmtModified(LocalDateTime.now());
service.insert(tableName, sequenceTable);
return stepStart;
}
long oldValue = dataRow.getLong(SequenceTable.Fields.value);
if (oldValue < 0) {
String msg = "Sequence value cannot be less than zero, value = " + oldValue
+ ", please check table sequence" + tableName;
throw new SeqException(msg);
}
if (oldValue > Long.MAX_VALUE - DELTA) {
String msg = "Sequence value overflow, value = " + oldValue + ", please check table sequence"
+ tableName;
throw new SeqException(msg);
}
return oldValue;
}
}
@@ -0,0 +1,130 @@
package com.bsd.pjcloud.common.sequence.range.impl.db;
import cn.hutool.core.thread.ThreadUtil;
import com.bsd.pjcloud.common.sequence.exception.SeqException;
import com.bsd.pjcloud.common.sequence.range.SeqRange;
import com.bsd.pjcloud.common.sequence.range.SeqRangeMgr;
import java.util.concurrent.TimeUnit;
/**
* DB区间管理器
*
* @author xuan on 2018/4/29.
*/
public class DbSeqRangeMgr implements SeqRangeMgr {
/**
* 区间步长
*/
private int step = 1000;
/**
* 区间起始位置,真实从stepStart+1开始
*/
private long stepStart = 0;
/**
* 获取区间失败重试次数
*/
private int retryTimes = 100;
/**
* 表名,默认range
*/
private String tableName = "range";
@Override
public SeqRange nextRange(String name) throws SeqException {
if (isEmpty(name)) {
throw new SecurityException("[DbSeqRangeMgr-nextRange] name is empty.");
}
Long oldValue;
Long newValue;
for (int i = 0; i < getRetryTimes(); i++) {
oldValue = BaseDbHelper.selectRange(getRealTableName(), name, getStepStart());
if (null == oldValue) {
// 区间不存在,重试
continue;
}
newValue = oldValue + getStep();
if (BaseDbHelper.updateRange(getRealTableName(), newValue, oldValue, name)) {
return new SeqRange(oldValue + 1, newValue);
}
// else 1秒后 失败重试
ThreadUtil.sleep(1, TimeUnit.SECONDS);
}
throw new SeqException("Retried too many times, retryTimes = " + getRetryTimes());
}
@Override
public void init() {
checkParam();
}
private boolean isEmpty(String str) {
return null == str || str.trim().isEmpty();
}
private String getRealTableName() {
return getTableName();
}
private void checkParam() {
if (step <= 0) {
throw new SecurityException("[DbSeqRangeMgr-checkParam] step must greater than 0.");
}
if (stepStart < 0) {
throw new SecurityException("[DbSeqRangeMgr-setStepStart] stepStart < 0.");
}
if (retryTimes <= 0) {
throw new SecurityException("[DbSeqRangeMgr-setRetryTimes] retryTimes must greater than 0.");
}
if (isEmpty(tableName)) {
throw new SecurityException("[DbSeqRangeMgr-setTableName] tableName is empty.");
}
}
//////// getter and setter
public int getStep() {
return step;
}
public void setStep(int step) {
this.step = step;
}
public long getStepStart() {
return stepStart;
}
public void setStepStart(long stepStart) {
this.stepStart = stepStart;
}
public int getRetryTimes() {
return retryTimes;
}
public void setRetryTimes(int retryTimes) {
this.retryTimes = retryTimes;
}
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
}
@@ -0,0 +1,30 @@
package com.bsd.pjcloud.common.sequence.range.impl.db;
import lombok.Data;
import lombok.experimental.FieldNameConstants;
import java.time.LocalDateTime;
/**
* 存储序列化的区间
*
* @author pjcloud
* @date 2024/6/6
* "CREATE TABLE IF NOT EXISTS #tableName(" + "id bigint(20) NOT NULL AUTO_INCREMENT,"
* + "value bigint(20) NOT NULL," + "name varchar(64) NOT NULL," + "gmt_create DATETIME NOT NULL,"
* + "gmt_modified DATETIME NOT NULL," + "PRIMARY KEY (`id`),UNIQUE uk_name (`name`)" + ")";
*/
@Data
@FieldNameConstants
public class SequenceTable {
private Long id;
private Long value;
private String name;
private LocalDateTime gmtCreate;
private LocalDateTime gmtModified;
}
@@ -0,0 +1,28 @@
package com.bsd.pjcloud.common.sequence.range.impl.name;
import cn.hutool.core.date.DateUtil;
import com.bsd.pjcloud.common.sequence.range.BizName;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
/**
* @author pjcloud
* @date 2019-05-26
* <p>
* 根据时间重置bizname
*/
@NoArgsConstructor
@AllArgsConstructor
public class DateBizName implements BizName {
private String bizName;
/**
* 生成空间名称
*/
@Override
public String create() {
return bizName + DateUtil.today();
}
}
@@ -0,0 +1,23 @@
package com.bsd.pjcloud.common.sequence.range.impl.name;
import com.bsd.pjcloud.common.sequence.range.BizName;
import lombok.AllArgsConstructor;
/**
* @author pjcloud
* @date 2019-05-26 根据传入返回bizname
*/
@AllArgsConstructor
public class DefaultBizName implements BizName {
private String bizName;
/**
* 生成空间名称
*/
@Override
public String create() {
return bizName;
}
}
@@ -0,0 +1,80 @@
package com.bsd.pjcloud.common.sequence.range.impl.redis;
import com.bsd.pjcloud.common.sequence.exception.SeqException;
import com.bsd.pjcloud.common.sequence.range.SeqRange;
import com.bsd.pjcloud.common.sequence.range.SeqRangeMgr;
import org.springframework.data.redis.core.StringRedisTemplate;
/**
* Redis区间管理器
*
* @author xuan on 2018/5/8.
*/
public class RedisSeqRangeMgr implements SeqRangeMgr {
/**
* 前缀防止key重复
*/
private final static String KEY_PREFIX = "x_sequence_";
/**
* redis客户端
*/
private StringRedisTemplate stringRedisTemplate;
/**
* 区间步长
*/
private int step = 1000;
/**
* 区间起始位置,真实从stepStart+1开始
*/
private long stepStart = 0;
/**
* 标记业务key是否存在,如果false,在取nextRange时,会取check一把 这个boolean只为提高性能,不用每次都取redis check
*/
private volatile boolean keyAlreadyExist;
@Override
public SeqRange nextRange(String name) throws SeqException {
if (!keyAlreadyExist) {
if (Boolean.FALSE.equals(stringRedisTemplate.hasKey(getRealKey(name)))) {
// 第一次不存在,进行初始化,setnx不存在就set,存在就忽略
stringRedisTemplate.opsForValue().setIfAbsent(getRealKey(name), String.valueOf(stepStart));
}
keyAlreadyExist = true;
}
Long max = stringRedisTemplate.opsForValue().increment(getRealKey(name), step);
Long min = max - step + 1;
return new SeqRange(min, max);
}
private String getRealKey(String name) {
return KEY_PREFIX + name;
}
private boolean isEmpty(String str) {
return null == str || str.trim().length() == 0;
}
public long getStepStart() {
return stepStart;
}
public void setStepStart(long stepStart) {
this.stepStart = stepStart;
}
public StringRedisTemplate getStringRedisTemplate() {
return stringRedisTemplate;
}
public void setStringRedisTemplate(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
}
@@ -0,0 +1,25 @@
package com.bsd.pjcloud.common.sequence.sequence;
import com.bsd.pjcloud.common.sequence.range.BizName;
import com.bsd.pjcloud.common.sequence.range.SeqRangeMgr;
/**
* 序列号区间生成器接口
*
* @author xuan on 2018/5/6.
*/
public interface RangeSequence extends Sequence {
/**
* 设置区间管理器
* @param seqRangeMgr 区间管理器
*/
void setSeqRangeMgr(SeqRangeMgr seqRangeMgr);
/**
* 设置获取序列号名称
* @param name 名称
*/
void setName(BizName name);
}
@@ -0,0 +1,26 @@
package com.bsd.pjcloud.common.sequence.sequence;
import com.bsd.pjcloud.common.sequence.exception.SeqException;
/**
* 序列号生成器接口
*
* @author xuan on 2018/1/10.
*/
public interface Sequence {
/**
* 生成下一个序列号
* @return 序列号
* @throws SeqException 序列号异常
*/
long nextValue() throws SeqException;
/**
* 下一个生成序号(带格式)
* @return
* @throws SeqException
*/
String nextNo() throws SeqException;
}
@@ -0,0 +1,122 @@
package com.bsd.pjcloud.common.sequence.sequence.impl;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.bsd.pjcloud.common.sequence.exception.SeqException;
import com.bsd.pjcloud.common.sequence.range.BizName;
import com.bsd.pjcloud.common.sequence.range.SeqRange;
import com.bsd.pjcloud.common.sequence.range.SeqRangeMgr;
import com.bsd.pjcloud.common.sequence.sequence.RangeSequence;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 序列号区间生成器接口默认实现
*
* @author xuan on 2018/1/10.
* <p>
* 根据biz name 自增
*/
public class DefaultRangeSequence implements RangeSequence {
/**
* 获取区间是加一把独占锁防止资源冲突
*/
private final Lock lock = new ReentrantLock();
/**
* 序列号区间管理器
*/
private SeqRangeMgr seqRangeMgr;
/**
* 当前序列号区间
*/
private volatile SeqRange currentRange;
private static Map<String, SeqRange> seqRangeMap = new ConcurrentHashMap<>(8);
/**
* 需要获取区间的业务名称
*/
private BizName bizName;
@Override
public long nextValue() throws SeqException {
String name = bizName.create();
currentRange = seqRangeMap.get(name);
// 当前区间不存在,重新获取一个区间
if (null == currentRange) {
lock.lock();
try {
if (null == currentRange) {
currentRange = seqRangeMgr.nextRange(name);
seqRangeMap.put(name, currentRange);
}
}
finally {
lock.unlock();
}
}
// 当value值为-1时,表明区间的序列号已经分配完,需要重新获取区间
long value = currentRange.getAndIncrement();
if (value == -1) {
lock.lock();
try {
for (;;) {
if (currentRange.isOver()) {
currentRange = seqRangeMgr.nextRange(name);
seqRangeMap.put(name, currentRange);
}
value = currentRange.getAndIncrement();
if (value == -1) {
continue;
}
break;
}
}
finally {
lock.unlock();
}
}
if (value < 0) {
throw new SeqException("Sequence value overflow, value = " + value);
}
return value;
}
/**
* 下一个生成序号(带格式)
* @return
* @throws SeqException
*/
@Override
public String nextNo() throws SeqException {
return String.format("%s%05d", DateUtil.format(new Date(), DatePattern.PURE_DATE_FORMAT), nextValue());
}
@Override
public void setSeqRangeMgr(SeqRangeMgr seqRangeMgr) {
this.seqRangeMgr = seqRangeMgr;
}
public SeqRangeMgr getSeqRangeMgr() {
return seqRangeMgr;
}
@Override
public void setName(BizName name) {
this.bizName = name;
}
}
@@ -0,0 +1,166 @@
package com.bsd.pjcloud.common.sequence.sequence.impl;
import com.bsd.pjcloud.common.sequence.exception.SeqException;
import com.bsd.pjcloud.common.sequence.sequence.Sequence;
/**
* 使用雪花算法 一个long类型的数据,64位。以下是每位的具体含义。 <br>
* snowflake的结构如下(每部分用-分开): <br>
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>
* (1)第一位为未使用 (2)接下来的41位为毫秒级时间(41位的长度可以使用69年) (3)然后是5位datacenterId (4)5位workerId
* (5)最后12位是毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号) <br>
* 一共加起来刚好64位,为一个Long型。(转换成字符串长度为18)
*
* @author xuan on 2018/5/9.
*/
public class SnowflakeSequence implements Sequence {
/**
* 开始时间截 (2018-01-01)
*/
private final long twepoch = 1514736000000L;
/**
* 机器id所占的位数
*/
private final long workerIdBits = 5L;
/**
* 数据标识id所占的位数
*/
private final long datacenterIdBits = 5L;
/**
* 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
*/
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
/**
* 支持的最大数据标识id,结果是31
*/
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
/**
* 序列在id中占的位数
*/
private final long sequenceBits = 12L;
/**
* 机器ID向左移12位
*/
private final long workerIdShift = sequenceBits;
/**
* 数据标识id向左移17位(12+5)
*/
private final long datacenterIdShift = sequenceBits + workerIdBits;
/**
* 时间截向左移22位(5+5+12)
*/
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
/**
* 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
*/
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
/**
* 工作机器ID(0~31)
*/
private long workerId;
/**
* 数据中心ID(0~31)
*/
private long datacenterId;
/**
* 毫秒内序列(0~4095)
*/
private long sequence = 0L;
/**
* 上次生成ID的时间截
*/
private long lastTimestamp = -1L;
@Override
public synchronized long nextValue() throws SeqException {
long timestamp = timeGen();
// 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
if (timestamp < lastTimestamp) {
throw new SeqException("[SnowflakeSequence-nextValue] 当前时间小于上次生成序列号的时间,时间被回退了,请确认服务器时间的设置.");
}
// 如果是同一时间生成的,则进行毫秒内序列
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
// 毫秒内序列溢出
if (sequence == 0) {
// 阻塞到下一个毫秒,获得新的时间戳
timestamp = tilNextMillis(lastTimestamp);
}
}
else {
// 时间戳改变,毫秒内序列重置
sequence = 0L;
}
// 上次生成ID的时间截
lastTimestamp = timestamp;
// 移位并通过或运算拼到一起组成64位的ID
return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift)
| (workerId << workerIdShift) | sequence;
}
/**
* 阻塞到下一个毫秒,直到获得新的时间戳
* @param lastTimestamp 上次生成ID的时间截
* @return 当前时间戳
*/
private long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
/**
* 返回以毫秒为单位的当前时间
* @return 当前时间(毫秒)
*/
private long timeGen() {
return System.currentTimeMillis();
}
public void setWorkerId(long workerId) {
if (workerId > maxWorkerId) {
throw new SeqException("[SnowflakeSequence-setWorkerId] workerId 不能大于31.");
}
this.workerId = workerId;
}
public void setDatacenterId(long datacenterId) {
if (datacenterId > maxDatacenterId) {
throw new SeqException("[SnowflakeSequence-setDatacenterId] datacenterId 不能大于31.");
}
this.datacenterId = datacenterId;
}
/**
* 下一个生成序号(带格式)
* @return
* @throws SeqException
*/
@Override
public String nextNo() throws SeqException {
return String.valueOf(nextValue());
}
}
@@ -0,0 +1 @@
com.bsd.pjcloud.common.sequence.SequenceAutoConfiguration