diff --git a/chsm-common/pom.xml b/chsm-common/pom.xml
index cfe4c33..e768b9f 100644
--- a/chsm-common/pom.xml
+++ b/chsm-common/pom.xml
@@ -41,6 +41,14 @@
org.bouncycastle
bcpkix-jdk18on
+
+ org.apache.commons
+ commons-pool2
+
+
+ net.java.dev.jna
+ jna
+
diff --git a/chsm-common/src/main/java/com/sunyard/chsm/mapper/TmkInfoMapper.java b/chsm-common/src/main/java/com/sunyard/chsm/mapper/TmkInfoMapper.java
new file mode 100644
index 0000000..dfc0391
--- /dev/null
+++ b/chsm-common/src/main/java/com/sunyard/chsm/mapper/TmkInfoMapper.java
@@ -0,0 +1,16 @@
+package com.sunyard.chsm.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.sunyard.chsm.model.entity.TmkInfo;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @author liulu
+ * @since 2024/10/29
+ */
+@Mapper
+public interface TmkInfoMapper extends BaseMapper {
+
+
+
+}
diff --git a/chsm-common/src/main/java/com/sunyard/chsm/model/entity/Device.java b/chsm-common/src/main/java/com/sunyard/chsm/model/entity/Device.java
index e32b273..bdd8527 100644
--- a/chsm-common/src/main/java/com/sunyard/chsm/model/entity/Device.java
+++ b/chsm-common/src/main/java/com/sunyard/chsm/model/entity/Device.java
@@ -23,6 +23,10 @@ public class Device {
private String manufacturerModel;
private String accessCredentials;
+ private Boolean connected;
+ private LocalDateTime lastCheckTime;
+ private LocalDateTime lastConnectedTime;
+
private Long groupId;
private String groupName;
private Integer weight;
diff --git a/chsm-common/src/main/java/com/sunyard/chsm/model/entity/TmkInfo.java b/chsm-common/src/main/java/com/sunyard/chsm/model/entity/TmkInfo.java
new file mode 100644
index 0000000..3de73f9
--- /dev/null
+++ b/chsm-common/src/main/java/com/sunyard/chsm/model/entity/TmkInfo.java
@@ -0,0 +1,25 @@
+package com.sunyard.chsm.model.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author liulu
+ * @since 2024/11/4
+ */
+@Data
+@TableName("sp_tmk_info")
+public class TmkInfo {
+
+ private Long id;
+
+ private String deviceSerial;
+ private String encTmk;
+
+ private String remark;
+ private LocalDateTime createTime;
+ private LocalDateTime updateTime;
+
+}
diff --git a/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/JnaSdfAdaptor.java b/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/JnaSdfAdaptor.java
new file mode 100644
index 0000000..e547550
--- /dev/null
+++ b/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/JnaSdfAdaptor.java
@@ -0,0 +1,109 @@
+package com.sunyard.chsm.sdf.adapter;
+
+import com.sun.jna.Pointer;
+import com.sun.jna.ptr.PointerByReference;
+import com.sunyard.chsm.sdf.lib.SdfLibrary;
+import com.sunyard.chsm.sdf.model.DeviceInfo;
+import com.sunyard.chsm.sdf.model.EccKey;
+import com.sunyard.chsm.sdf.model.EccPubKey;
+import org.springframework.util.Assert;
+
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author liulu
+ * @since 2024/11/4
+ */
+public abstract class JnaSdfAdaptor implements SdfApiAdapter {
+
+ protected static final Map DEVICE_HANDLE_CONTEXT = new ConcurrentHashMap<>();
+ protected static final Map SESSION_HANDLE_CONTEXT = new ConcurrentHashMap<>();
+ protected static final Map KEY_HANDLE_CONTEXT = new ConcurrentHashMap<>();
+
+
+ protected abstract SdfLibrary getSdfLibrary();
+
+ protected abstract int getAlgId(String alg);
+
+ @Override
+ public String openDevice() {
+ PointerByReference phDeviceHandle = new PointerByReference();
+ getSdfLibrary().SDF_OpenDevice(phDeviceHandle);
+ String key = UUID.randomUUID().toString();
+ DEVICE_HANDLE_CONTEXT.put(key, phDeviceHandle.getValue());
+ return key;
+ }
+
+ @Override
+ public boolean closeDevice(String deviceHandle) {
+ Pointer pointer = DEVICE_HANDLE_CONTEXT.remove(deviceHandle);
+ if (pointer != null) {
+ int res = getSdfLibrary().SDF_CloseDevice(pointer);
+ return res == 0;
+ }
+ return true;
+ }
+
+ @Override
+ public String openSession(String deviceHandle) {
+ Pointer device = getDeviceHandle(deviceHandle);
+
+ PointerByReference phSessionHandle = new PointerByReference();
+ getSdfLibrary().SDF_OpenSession(device, phSessionHandle);
+
+ String key = UUID.randomUUID().toString();
+ SESSION_HANDLE_CONTEXT.put(key, phSessionHandle.getValue());
+ return key;
+ }
+
+ @Override
+ public void closeSession(String sessionHandle) {
+ Pointer pointer = SESSION_HANDLE_CONTEXT.remove(sessionHandle);
+ if (pointer != null) {
+ getSdfLibrary().SDF_CloseSession(pointer);
+ }
+ }
+
+ protected Pointer getDeviceHandle(String deviceHandle) {
+ Pointer pointer = DEVICE_HANDLE_CONTEXT.get(deviceHandle);
+ Assert.notNull(pointer, "设备句柄不存在");
+ return pointer;
+ }
+
+ protected Pointer getSessionHandle(String sessionHandle) {
+ Pointer pointer = SESSION_HANDLE_CONTEXT.get(sessionHandle);
+ Assert.notNull(pointer, "会话句柄不存在");
+ return pointer;
+ }
+
+ protected Pointer getKeyHandle(String keyHandle) {
+ Pointer pointer = KEY_HANDLE_CONTEXT.get(keyHandle);
+ Assert.notNull(pointer, "密钥句柄不存在");
+ return pointer;
+ }
+
+ @Override
+ public DeviceInfo getDeviceInfo(String sessionHandle) {
+ return null;
+ }
+
+ @Override
+ public byte[] generateRandom(String sessionHandle, int uiLength) {
+ byte[] pucRandom = new byte[uiLength];
+ Pointer hSessionHandle = getSessionHandle(sessionHandle);
+ getSdfLibrary().SDF_GenerateRandom(hSessionHandle, uiLength, pucRandom);
+ return pucRandom;
+ }
+
+ @Override
+ public EccPubKey exportEncPublicKeyECC(String sessionHandle, int uiKeyIndex) {
+ return null;
+ }
+
+ @Override
+ public EccKey generateKeyPairECC(String sessionHandle, String alg, int uiKeyBits) {
+ return null;
+ }
+}
diff --git a/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/SdfApiAdapter.java b/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/SdfApiAdapter.java
new file mode 100644
index 0000000..d03100e
--- /dev/null
+++ b/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/SdfApiAdapter.java
@@ -0,0 +1,71 @@
+package com.sunyard.chsm.sdf.adapter;
+
+import com.sunyard.chsm.sdf.model.DeviceInfo;
+import com.sunyard.chsm.sdf.model.EccKey;
+import com.sunyard.chsm.sdf.model.EccPubKey;
+
+/**
+ * @author liulu
+ * @since 2024/11/4
+ */
+public interface SdfApiAdapter {
+
+ /**
+ * 打开设备
+ *
+ * @return 设 备 句 柄
+ */
+ String openDevice();
+
+ /**
+ * 关闭设备
+ *
+ * @param deviceHandle 已打开的设备句柄
+ * @return 0 成功; 非0 失败,返回错误代码
+ */
+ boolean closeDevice(String deviceHandle);
+
+ /**
+ * 创建会话
+ *
+ * @return 与密码设备建立的新会话句柄
+ */
+ String openSession(String deviceHandle);
+
+ void closeSession(String sessionHandle);
+
+ /**
+ * 获取设备信息
+ *
+ * @param sessionHandle 与设备建立的会话句柄
+ * @return 设备能力描述信息,内容及格式见设备信息定义
+ */
+ DeviceInfo getDeviceInfo(String sessionHandle);
+
+ /**
+ * 产生随机数
+ *
+ * @param uiLength 随机数长度
+ * @return pucRandom 返回随机数
+ */
+ byte[] generateRandom(String sessionHandle, int uiLength);
+
+ /**
+ * 导出ECC加密公钥
+ *
+ * @param uiKeyIndex 密码设备存储的ECC密钥对索引值
+ * @return pucPublicKeyEcc 返回ECC加密公钥
+ */
+ EccPubKey exportEncPublicKeyECC(String sessionHandle, int uiKeyIndex);
+
+ /**
+ * 产生ECC密钥对并输出
+ *
+ * @param alg 指定算法标识 SGD_SM2_1
+ * @param uiKeyBits 指定密钥模长
+ * @return pucPublicKeyEcc 返回公钥 | pucPrivateKeyEcc 返回私钥
+ */
+ EccKey generateKeyPairECC(String sessionHandle, String alg, int uiKeyBits);
+
+
+}
diff --git a/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/SdfApiAdapterFactory.java b/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/SdfApiAdapterFactory.java
new file mode 100644
index 0000000..f118475
--- /dev/null
+++ b/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/SdfApiAdapterFactory.java
@@ -0,0 +1,34 @@
+package com.sunyard.chsm.sdf.adapter;
+
+import com.sunyard.chsm.enums.ManufacturerModelEnum;
+import com.sunyard.chsm.sdf.context.DeviceContext;
+import org.springframework.util.Assert;
+
+import java.util.Objects;
+
+/**
+ * @author liulu
+ * @since 2024/11/5
+ */
+public class SdfApiAdapterFactory {
+
+
+ public static SdfApiAdapter newInstance(DeviceContext device) {
+
+ Assert.hasText(device.getManufacturerModel(), "设备型号不能为空");
+ ManufacturerModelEnum model = ManufacturerModelEnum.of(device.getManufacturerModel());
+
+ if (Objects.isNull(model)) {
+ // bc adaptor
+ return null;
+ }
+ switch (model) {
+ case enc001:
+ return new SunyardJnaSdfAdaptor(device.getServiceIp(), device.getServicePort());
+ default:
+ return null;
+ }
+ }
+
+
+}
diff --git a/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/SunyardJnaSdfAdaptor.java b/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/SunyardJnaSdfAdaptor.java
new file mode 100644
index 0000000..0474e45
--- /dev/null
+++ b/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/SunyardJnaSdfAdaptor.java
@@ -0,0 +1,75 @@
+package com.sunyard.chsm.sdf.adapter;
+
+import com.sun.jna.Native;
+import com.sun.jna.ptr.PointerByReference;
+import com.sunyard.chsm.sdf.lib.SdfLibrary;
+import com.sunyard.chsm.sdf.lib.SunyardSdfLibrary;
+
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author liulu
+ * @since 2024/11/4
+ */
+public class SunyardJnaSdfAdaptor extends JnaSdfAdaptor {
+
+ private static final Map SDF_LIB_MAP = new ConcurrentHashMap<>();
+
+
+ private final String ip;
+ private final Integer port;
+ private final String libName;
+ private final Integer connTimeout;
+ private final Integer dealTimeout;
+
+ public SunyardJnaSdfAdaptor(String ip, int port) {
+ this(ip, port, "libsdf");
+ }
+
+ public SunyardJnaSdfAdaptor(String ip, int port, String libName) {
+ this(ip, port, libName, 3000, 3000);
+ }
+
+ public SunyardJnaSdfAdaptor(String ip, int port, String libName, int connTimeout, int dealTimeout) {
+ this.ip = ip;
+ this.port = port;
+ this.libName = libName;
+ this.connTimeout = connTimeout;
+ this.dealTimeout = dealTimeout;
+ SDF_LIB_MAP.computeIfAbsent(libName, k -> Native.load(libName, SunyardSdfLibrary.class));
+ }
+
+ @Override
+ public String openDevice() {
+ SunyardSdfLibrary sdfLibrary = (SunyardSdfLibrary) getSdfLibrary();
+ PointerByReference phDeviceHandle = new PointerByReference();
+ sdfLibrary.SDF_OpenDevice(phDeviceHandle, safeStringBytes(ip), port, connTimeout, dealTimeout, 0);
+ String key = UUID.randomUUID().toString();
+ DEVICE_HANDLE_CONTEXT.put(key, phDeviceHandle.getValue());
+ return key;
+ }
+
+ @Override
+ protected SdfLibrary getSdfLibrary() {
+ return SDF_LIB_MAP.get(libName);
+ }
+
+ @Override
+ protected int getAlgId(String alg) {
+
+ return 0;
+ }
+
+ public static byte[] safeStringBytes(String str) {
+ if (null == str || str.isEmpty()) {
+ return new byte[]{0x30};
+ }
+ int len = str.getBytes().length;
+ byte[] ret = new byte[len + 1];
+ System.arraycopy(str.getBytes(), 0, ret, 0, len);
+ return ret;
+ }
+
+}
diff --git a/chsm-common/src/main/java/com/sunyard/chsm/sdf/context/DeviceContext.java b/chsm-common/src/main/java/com/sunyard/chsm/sdf/context/DeviceContext.java
new file mode 100644
index 0000000..d133840
--- /dev/null
+++ b/chsm-common/src/main/java/com/sunyard/chsm/sdf/context/DeviceContext.java
@@ -0,0 +1,22 @@
+package com.sunyard.chsm.sdf.context;
+
+import lombok.Data;
+
+/**
+ * @author liulu
+ * @since 2024/11/5
+ */
+@Data
+public class DeviceContext {
+
+ private String serviceIp;
+ private Integer servicePort;
+ private String manufacturer;
+ private String manufacturerModel;
+ private String accessCredentials;
+ private String jnaLibName;
+
+ private Integer weight;
+
+
+}
diff --git a/chsm-common/src/main/java/com/sunyard/chsm/sdf/lib/SdfLibrary.java b/chsm-common/src/main/java/com/sunyard/chsm/sdf/lib/SdfLibrary.java
new file mode 100644
index 0000000..f8bf741
--- /dev/null
+++ b/chsm-common/src/main/java/com/sunyard/chsm/sdf/lib/SdfLibrary.java
@@ -0,0 +1,66 @@
+package com.sunyard.chsm.sdf.lib;
+
+import com.sun.jna.Library;
+import com.sun.jna.Pointer;
+import com.sun.jna.ptr.PointerByReference;
+
+/**
+ * @author liulu
+ * @since 2024/11/4
+ */
+public interface SdfLibrary extends Library {
+
+ /**
+ * 打开设备
+ *
+ * @param phDeviceHandle 返回设备句柄
+ * @return 0 成功; 非0 失败,返回错误代码
+ */
+ int SDF_OpenDevice(PointerByReference phDeviceHandle);
+
+ /**
+ * 关闭设备
+ *
+ * @param hDeviceHandle 已打开的设备句柄
+ * @return 0 成功; 非0 失败,返回错误代码
+ */
+ int SDF_CloseDevice(Pointer hDeviceHandle);
+
+ /**
+ * 创建会话
+ *
+ * @param hDeviceHandle 已打开的设备句柄
+ * @param phSessionHandle 返回与密码设备建立的新会话句柄
+ * @return 0 成功; 非0 失败,返回错误代码
+ */
+ int SDF_OpenSession(Pointer hDeviceHandle, PointerByReference phSessionHandle);
+
+ /**
+ * 关闭会话
+ *
+ * @param hSessionHandle 与密码设备已建立的会话句柄
+ * @return 0 成功; 非0 失败,返回错误代码
+ */
+ int SDF_CloseSession(Pointer hSessionHandle);
+
+ /**
+ * 获取设备信息
+ *
+ * @param hSessionHandle 与设备建立的会话句柄
+ * @param pstDeviceInfo 设备能力描述信息,内容及格式见设备信息定义
+ * @return 0 成功; 非0 失败,返回错误代码
+ */
+ int SDF_GetDeviceInfo(Pointer hSessionHandle, byte[] pstDeviceInfo);
+
+ /**
+ * 产生随机数
+ *
+ * @param hSessionHandle 与设备建立的会话句柄
+ * @param uiLength 欲获取的随机数长度
+ * @param pucRandom 缓冲区指针,用于存放获取的随机数
+ * @return 0 成功; 非0 失败,返回错误代码
+ */
+ int SDF_GenerateRandom(Pointer hSessionHandle, int uiLength, byte[] pucRandom);
+
+
+}
diff --git a/chsm-common/src/main/java/com/sunyard/chsm/sdf/lib/SunyardSdfLibrary.java b/chsm-common/src/main/java/com/sunyard/chsm/sdf/lib/SunyardSdfLibrary.java
new file mode 100644
index 0000000..3152f58
--- /dev/null
+++ b/chsm-common/src/main/java/com/sunyard/chsm/sdf/lib/SunyardSdfLibrary.java
@@ -0,0 +1,21 @@
+package com.sunyard.chsm.sdf.lib;
+
+import com.sun.jna.ptr.PointerByReference;
+
+/**
+ * @author liulu
+ * @since 2024/11/4
+ */
+public interface SunyardSdfLibrary extends SdfLibrary {
+
+
+ /**
+ * 打开设备
+ * @param phDeviceHandle 设备句柄
+ * @return int 响应码
+ */
+ int SDF_OpenDevice(PointerByReference phDeviceHandle, byte[] ip, int port, int connTimeout, int dealTimeout, int ipMode);
+
+
+
+}
diff --git a/chsm-web-manage/src/main/java/com/sunyard/chsm/service/impl/DeviceServiceImpl.java b/chsm-web-manage/src/main/java/com/sunyard/chsm/service/impl/DeviceServiceImpl.java
index 84e520c..012b858 100644
--- a/chsm-web-manage/src/main/java/com/sunyard/chsm/service/impl/DeviceServiceImpl.java
+++ b/chsm-web-manage/src/main/java/com/sunyard/chsm/service/impl/DeviceServiceImpl.java
@@ -123,6 +123,7 @@ public class DeviceServiceImpl implements DeviceService {
Assert.isTrue(manufacturerModel.getManufacturer() == manufacturer, "设备厂商和型号不匹配");
checkName(save.getName());
+ checkIpPort(save.getServiceIp(), save.getServicePort());
Device device = new Device();
BeanUtils.copyProperties(save, device);
@@ -136,6 +137,14 @@ public class DeviceServiceImpl implements DeviceService {
return device.getId();
}
+ private void checkIpPort(String serviceIp, Integer servicePort) {
+ LambdaQueryWrapper wrapper = new LambdaQueryWrapper()
+ .eq(Device::getServiceIp, serviceIp)
+ .eq(Device::getServicePort, servicePort);
+ Device exist = spDeviceMapper.selectOne(wrapper);
+ Assert.isNull(exist, "设备IP和端口已存在");
+ }
+
@Override
public void update(DeviceDTO.DeviceSave update) {
Assert.notNull(update.getId(), "id不能为空");
@@ -145,13 +154,16 @@ public class DeviceServiceImpl implements DeviceService {
if (!Objects.equals(exist.getName(), update.getName())) {
checkName(update.getName());
}
+ if (!Objects.equals(exist.getServiceIp(), update.getServiceIp())
+ || !Objects.equals(exist.getServicePort(), update.getServicePort())) {
+ checkIpPort(update.getServiceIp(), update.getServicePort());
+ }
Device up = new Device();
BeanUtils.copyProperties(update, up);
up.setUpdateTime(LocalDateTime.now());
spDeviceMapper.updateById(up);
-
}
@Override
diff --git a/chsm-web-manage/src/main/java/com/sunyard/chsm/task/DeviceTask.java b/chsm-web-manage/src/main/java/com/sunyard/chsm/task/DeviceTask.java
new file mode 100644
index 0000000..1a76244
--- /dev/null
+++ b/chsm-web-manage/src/main/java/com/sunyard/chsm/task/DeviceTask.java
@@ -0,0 +1,61 @@
+package com.sunyard.chsm.task;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.sunyard.chsm.mapper.SpDeviceMapper;
+import com.sunyard.chsm.model.entity.Device;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+
+import javax.annotation.Resource;
+import java.time.Duration;
+import java.util.List;
+
+/**
+ * @author liulu
+ * @since 2024/11/4
+ */
+@Component
+public class DeviceTask implements InitializingBean {
+
+ @Resource
+ private SpDeviceMapper spDeviceMapper;
+
+ @Resource
+ private ThreadPoolTaskScheduler threadPoolTaskScheduler;
+ @Resource
+ private ThreadPoolTaskExecutor threadPoolTaskExecutor;
+
+
+ private void checkDeviceStatus() {
+
+ for (int i = 1; i < 500; i++) {
+ Page devicePage = spDeviceMapper.selectPage(
+ new Page<>(i, 100L),
+ new LambdaQueryWrapper().orderByAsc(Device::getId)
+ );
+ List records = devicePage.getRecords();
+ if (CollectionUtils.isEmpty(records)) {
+ break;
+ }
+
+ for (Device record : records) {
+
+
+
+
+
+ }
+ }
+
+
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ threadPoolTaskScheduler.scheduleWithFixedDelay(this::checkDeviceStatus, Duration.ofMinutes(5L));
+ }
+}
diff --git a/doc/ssp_dm.sql b/doc/ssp_dm.sql
index 71028ca..424a6b9 100644
--- a/doc/ssp_dm.sql
+++ b/doc/ssp_dm.sql
@@ -11,6 +11,9 @@ CREATE TABLE sp_device (
manage_ip VARCHAR(30) COMMENT '管理ip',
manage_port INT COMMENT '管理端口',
access_credentials VARCHAR(1000) COMMENT '访问凭证',
+ connected TINYINT NOT NULL DEFAULT 0,
+ last_connected_time TIMESTAMP ,
+ last_check_time TIMESTAMP ,
status VARCHAR(25) DEFAULT '' COMMENT '设备状态',
group_id BIGINT NOT NULL DEFAULT 0 COMMENT '设备组id',
group_name VARCHAR(255) NOT NULL DEFAULT '' COMMENT '设备组名称',
@@ -21,6 +24,7 @@ CREATE TABLE sp_device (
PRIMARY KEY (id)
);
+-- 设备组
CREATE TABLE sp_device_group (
id BIGINT NOT NULL COMMENT 'id',
name VARCHAR(255) NOT NULL DEFAULT '' COMMENT '服务名称',
@@ -30,6 +34,18 @@ CREATE TABLE sp_device_group (
PRIMARY KEY (id)
);
+-- TMK
+CREATE TABLE sp_tmk_info (
+ id BIGINT NOT NULL COMMENT 'id',
+ device_serial VARCHAR(255) NOT NULL DEFAULT '' COMMENT '设备序列号',
+ enc_tmk VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'tmk密文',
+ pub_key VARCHAR(400) NOT NULL DEFAULT '' COMMENT '设备公钥',
+ remark VARCHAR(500) NOT NULL DEFAULT '' COMMENT '备注',
+ update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP(),
+ create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP(),
+ PRIMARY KEY (id)
+);
+
-- 密码服务
CREATE TABLE sp_crypto_service (
id BIGINT NOT NULL COMMENT 'id',