diff --git a/bonus-common/bonus-common-core/pom.xml b/bonus-common/bonus-common-core/pom.xml index 26fe105..2cfc7bd 100644 --- a/bonus-common/bonus-common-core/pom.xml +++ b/bonus-common/bonus-common-core/pom.xml @@ -164,6 +164,16 @@ 1.10.19 test + + com.google.guava + guava + 33.0.0-jre + compile + + + org.springframework.data + spring-data-redis + diff --git a/bonus-common/bonus-common-core/src/main/java/com/bonus/common/core/utils/id/Id.java b/bonus-common/bonus-common-core/src/main/java/com/bonus/common/core/utils/id/Id.java new file mode 100644 index 0000000..ab417ae --- /dev/null +++ b/bonus-common/bonus-common-core/src/main/java/com/bonus/common/core/utils/id/Id.java @@ -0,0 +1,53 @@ +package com.bonus.common.core.utils.id; + +import com.google.common.base.Preconditions; +import java.time.LocalDateTime; +import java.time.ZoneId; + +public class Id { + private static final long EPOCH = LocalDateTime.of(2021, 12, 29, 9, 2).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + private static final long SEQUENCE_BITS = 12L; + private static final long WORKER_ID_BITS = 10L; + static final long WORKER_ID_MAX_VALUE = 1024L; + private static final long SEQUENCE_MASK = 4095L; + private static final long WORKER_ID_LEFT_SHIFT_BITS = 12L; + private static final long TIMESTAMP_LEFT_SHIFT_BITS = 22L; + static Long WORKER_ID; + private static long SEQUENCE; + private static long LAST_TIME; + + public Id(Long workerId) { + WORKER_ID = workerId; + } + + public static long next() { + return nextKey(); + } + + public static String nextString() { + return String.valueOf(next()); + } + + private static synchronized long nextKey() { + long currentMillis = System.currentTimeMillis(); + Preconditions.checkState(LAST_TIME <= currentMillis, "Clock is moving backwards, last time is %d milliseconds, current time is %d milliseconds", LAST_TIME, currentMillis); + if (LAST_TIME == currentMillis) { + if (0L == (SEQUENCE = SEQUENCE + 1L & 4095L)) { + currentMillis = waitUntilNextTime(currentMillis); + } + } else { + SEQUENCE = 0L; + } + + LAST_TIME = currentMillis; + return currentMillis - EPOCH << 22 | WORKER_ID << 12 | SEQUENCE; + } + + private static long waitUntilNextTime(final long lastTime) { + long time; + for(time = System.currentTimeMillis(); time <= lastTime; time = System.currentTimeMillis()) { + } + + return time; + } +} diff --git a/bonus-common/bonus-common-core/src/main/java/com/bonus/common/core/utils/id/IdProperties.java b/bonus-common/bonus-common-core/src/main/java/com/bonus/common/core/utils/id/IdProperties.java new file mode 100644 index 0000000..b1d2476 --- /dev/null +++ b/bonus-common/bonus-common-core/src/main/java/com/bonus/common/core/utils/id/IdProperties.java @@ -0,0 +1,37 @@ +package com.bonus.common.core.utils.id; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties( + prefix = "id" +) +public class IdProperties { + public static final String PREFIX = "id"; + private Boolean enabled = true; + private String key = "worker_id_sequence"; + private String workspace; + + public Boolean getEnabled() { + return this.enabled; + } + + public String getKey() { + return this.key; + } + + public String getWorkspace() { + return this.workspace; + } + + public void setEnabled(final Boolean enabled) { + this.enabled = enabled; + } + + public void setKey(final String key) { + this.key = key; + } + + public void setWorkspace(final String workspace) { + this.workspace = workspace; + } +} diff --git a/bonus-common/bonus-common-core/src/main/java/com/bonus/common/core/utils/id/IdWorkConfiguration.java b/bonus-common/bonus-common-core/src/main/java/com/bonus/common/core/utils/id/IdWorkConfiguration.java new file mode 100644 index 0000000..aae1b11 --- /dev/null +++ b/bonus-common/bonus-common-core/src/main/java/com/bonus/common/core/utils/id/IdWorkConfiguration.java @@ -0,0 +1,123 @@ +package com.bonus.common.core.utils.id; + +import cn.hutool.core.text.CharSequenceUtil; +import org.apache.commons.io.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.UUID; + +@Component +@ConditionalOnClass({StringRedisTemplate.class}) +@EnableConfigurationProperties({IdProperties.class}) +@ConditionalOnProperty( + prefix = "id", + name = {"enabled"}, + havingValue = "true", + matchIfMissing = true +) +public class IdWorkConfiguration { + private static final Logger log = LoggerFactory.getLogger(IdWorkConfiguration.class); + private final IdProperties idProperties; + + public IdWorkConfiguration(StringRedisTemplate redisTemplate, IdProperties idProperties) throws IOException { + this.idProperties = idProperties; + Long workerId = this.getLocalWorkId(); + + try { + while(workerId == null) { + Long newWorkId = redisTemplate.opsForValue().increment(idProperties.getKey(), 1L); + this.saveLocalWorkId(String.valueOf(newWorkId)); + workerId = this.getLocalWorkId(); + } + } catch (Throwable var5) { + log.error("获取workID失败", var5); + throw var5; + } + + if (workerId > 1024L) { + throw new RuntimeException("超过最大启动实例"); + } else { + Id.WORKER_ID = workerId; + } + } + + private void saveLocalWorkId(String workId) throws IOException { + File workIdHome = this.getWorkIdHome(); + String var10002 = String.valueOf(workIdHome.getAbsoluteFile()); + FileUtils.writeStringToFile(new File(var10002 + "/" + String.valueOf(UUID.randomUUID()) + ".lock"), workId, StandardCharsets.UTF_8); + } + + private File getWorkIdHome() { + String workHome = this.idProperties.getWorkspace(); + if (CharSequenceUtil.isBlank(workHome)) { + workHome = FileUtils.getUserDirectoryPath() + "/.workId/"; + } + + String var10002 = CharSequenceUtil.removeSuffix(workHome, "/"); + return new File(var10002 + "/" + this.idProperties.getKey()); + } + + private Long getLocalWorkId() throws IOException { + File workIdHome = this.getWorkIdHome(); + if (!workIdHome.exists()) { + return null; + } + + Collection files = FileUtils.listFiles(workIdHome, new String[]{"lock"}, false); + if (CollectionUtils.isEmpty(files)) { + return null; + } + + for (File file : files) { + FileChannel channel = null; + FileLock fileLock = null; + + try { + RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw"); + channel = randomAccessFile.getChannel(); + fileLock = channel.tryLock(); + + if (fileLock != null) { // 成功获取文件锁 + Long workId = Long.valueOf(randomAccessFile.readLine()); + releaseResourcesOnShutdown(channel); + return workId; + } + } catch (IOException e) { + log.error("Error accessing workId file", e); + } finally { + if (fileLock == null && channel != null) { + try { + channel.close(); + } catch (IOException e) { + log.error("Failed to close file channel", e); + } + } + } + } + return null; + } + + private void releaseResourcesOnShutdown(FileChannel channel) { + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + try { + channel.close(); + } catch (IOException e) { + log.error("Release WorkId file lock error", e); + } + })); + } +} diff --git a/bonus-modules/bonus-system/src/main/java/com/bonus/system/config/CommonConfiguration.java b/bonus-modules/bonus-system/src/main/java/com/bonus/system/config/CommonConfiguration.java new file mode 100644 index 0000000..9c36405 --- /dev/null +++ b/bonus-modules/bonus-system/src/main/java/com/bonus/system/config/CommonConfiguration.java @@ -0,0 +1,10 @@ +package com.bonus.system.config; + +import com.bonus.common.core.utils.id.IdWorkConfiguration; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +@Configuration +@Import({IdWorkConfiguration.class}) +public class CommonConfiguration { +} diff --git a/bonus-modules/bonus-system/src/main/java/com/bonus/system/service/impl/SysUserServiceImpl.java b/bonus-modules/bonus-system/src/main/java/com/bonus/system/service/impl/SysUserServiceImpl.java index 740ca9d..058767c 100644 --- a/bonus-modules/bonus-system/src/main/java/com/bonus/system/service/impl/SysUserServiceImpl.java +++ b/bonus-modules/bonus-system/src/main/java/com/bonus/system/service/impl/SysUserServiceImpl.java @@ -9,6 +9,7 @@ import com.bonus.common.core.utils.SpringUtils; import com.bonus.common.core.utils.StringUtils; import com.bonus.common.core.utils.bean.BeanValidators; import com.bonus.common.core.utils.encryption.Sm4Utils; +import com.bonus.common.core.utils.id.Id; import com.bonus.common.core.utils.sms.SmsUtils; import com.bonus.common.core.web.domain.AjaxResult; import com.bonus.common.core.web.domain.BaseEntity; @@ -666,7 +667,7 @@ public class SysUserServiceImpl implements ISysUserService { if (Objects.isNull(custCasual)) { CustCasual insertCasual = new CustCasual(); openid = IdUtil.simpleUUID(); - //insertCasual.setCasualId(Id.next()); //TODO, NPE + insertCasual.setCasualId(Id.next()); insertCasual.setCasualName(Sm4Utils.custDecrypt(result.getCustName())); insertCasual.setCustId(result.getCustId()); insertCasual.setMobile(Sm4Utils.custDecrypt(result.getPhonenumber()));