mirror of
https://github.com/1024-lab/smart-admin.git
synced 2026-06-21 21:24:41 +00:00
v3.6.0 三级等保重磅更新:1、【新增】双因子方式登录;2、【新增】定期修改密码;3、【新增】最大活跃时间;4、【新增】敏感数据脱敏;5、【新增】登录锁定配置;6、【新增】密码复杂度配置;7、【新增】三级等保可配置
This commit is contained in:
@@ -88,8 +88,8 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -263,6 +263,11 @@
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-yaml</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.1024lab</groupId>
|
||||
<artifactId>smartdb</artifactId>
|
||||
@@ -278,6 +283,27 @@
|
||||
<artifactId>redisson-spring-data-27</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.freemarker</groupId>
|
||||
<artifactId>freemarker</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
+4
-4
@@ -28,19 +28,19 @@ public class DictValueVoDeserializer extends JsonDeserializer<String> {
|
||||
|
||||
@Override
|
||||
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
|
||||
List<DictValueVO> list = new ArrayList<>();
|
||||
List<String> list = new ArrayList<>();
|
||||
ObjectCodec objectCodec = jsonParser.getCodec();
|
||||
JsonNode listOrObjectNode = objectCodec.readTree(jsonParser);
|
||||
String deserialize = "";
|
||||
try {
|
||||
if (listOrObjectNode.isArray()) {
|
||||
for (JsonNode node : listOrObjectNode) {
|
||||
list.add(objectCodec.treeToValue(node, DictValueVO.class));
|
||||
list.add(node.asText());
|
||||
}
|
||||
} else {
|
||||
list.add(objectCodec.treeToValue(listOrObjectNode, DictValueVO.class));
|
||||
list.add(listOrObjectNode.asText());
|
||||
}
|
||||
deserialize = list.stream().map(DictValueVO::getValueCode).collect(Collectors.joining(","));
|
||||
deserialize = String.join(",", list);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
deserialize = listOrObjectNode.asText();
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package net.lab1024.sa.base.common.json.serializer;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.BeanProperty;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
|
||||
import net.lab1024.sa.base.module.support.datamasking.DataMasking;
|
||||
import net.lab1024.sa.base.module.support.datamasking.DataMaskingTypeEnum;
|
||||
import net.lab1024.sa.base.module.support.datamasking.SmartDataMaskingUtil;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 脱敏序列化
|
||||
*
|
||||
* @author 罗伊
|
||||
* @description:
|
||||
* @date 2024/7/21 4:39 下午
|
||||
*/
|
||||
public class DataMaskingSerializer extends JsonSerializer<Object> implements ContextualSerializer {
|
||||
|
||||
private DataMaskingTypeEnum typeEnum;
|
||||
|
||||
@Override
|
||||
public void serialize(Object value, JsonGenerator jsonGenerator, SerializerProvider serializers) throws IOException {
|
||||
|
||||
if (ObjectUtils.isEmpty(value)) {
|
||||
jsonGenerator.writeObject(value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeEnum == null) {
|
||||
jsonGenerator.writeObject(SmartDataMaskingUtil.dataMasking(String.valueOf(value)));
|
||||
return;
|
||||
}
|
||||
|
||||
jsonGenerator.writeObject(SmartDataMaskingUtil.dataMasking(value, typeEnum));
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
|
||||
// 判断beanProperty是不是空
|
||||
if (null == property) {
|
||||
return prov.findNullValueSerializer(property);
|
||||
}
|
||||
|
||||
DataMasking annotation = property.getAnnotation(DataMasking.class);
|
||||
if (null == annotation) {
|
||||
return prov.findValueSerializer(property.getType(), property);
|
||||
}
|
||||
|
||||
typeEnum = annotation.value();
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
+4
-1
@@ -12,7 +12,6 @@ import org.apache.commons.collections4.CollectionUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 分页工具类
|
||||
@@ -32,6 +31,10 @@ public class SmartPageUtil {
|
||||
public static Page<?> convert2PageQuery(PageParam pageParam) {
|
||||
Page<?> page = new Page<>(pageParam.getPageNum(), pageParam.getPageSize());
|
||||
|
||||
if (pageParam.getSearchCount() != null) {
|
||||
page.setSearchCount(pageParam.getSearchCount());
|
||||
}
|
||||
|
||||
List<PageParam.SortItem> sortItemList = pageParam.getSortItemList();
|
||||
if (CollectionUtils.isEmpty(sortItemList)) {
|
||||
return page;
|
||||
|
||||
@@ -7,10 +7,12 @@ import com.alibaba.druid.support.http.StatViewServlet;
|
||||
import com.alibaba.druid.support.http.WebStatFilter;
|
||||
import com.alibaba.druid.support.spring.stat.DruidStatInterceptor;
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.core.config.GlobalConfig;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.lab1024.sa.base.common.domain.DataScopePlugin;
|
||||
import net.lab1024.sa.base.handler.MybatisPlusFillHandler;
|
||||
import org.apache.ibatis.plugin.Interceptor;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.springframework.aop.support.DefaultPointcutAdvisor;
|
||||
@@ -144,6 +146,8 @@ public class DataSourceConfig {
|
||||
pluginsList.add(dataScopePlugin);
|
||||
}
|
||||
factoryBean.setPlugins(pluginsList.toArray(new Interceptor[pluginsList.size()]));
|
||||
// 添加字段自动填充处理
|
||||
factoryBean.setGlobalConfig(new GlobalConfig().setBanner(false).setMetaObjectHandler(new MybatisPlusFillHandler()));
|
||||
|
||||
return factoryBean.getObject();
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -43,7 +44,8 @@ public class RedisConfig {
|
||||
.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||
|
||||
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
|
||||
// enableDefaultTyping 官方已弃用 所以改为 activateDefaultTyping
|
||||
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
|
||||
jackson2JsonRedisSerializer.setObjectMapper(om);
|
||||
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
||||
template.setConnectionFactory(factory);
|
||||
|
||||
@@ -28,7 +28,7 @@ import java.util.Optional;
|
||||
* springdoc-openapi 配置
|
||||
* nginx配置前缀时如果需要访问【/swagger-ui/index.html】需添加额外nginx配置
|
||||
* location /v3/api-docs/ {
|
||||
* proxy_pass http://127.0.0.1:11024/v3/api-docs/;
|
||||
* proxy_pass http://127.0.0.1:1024/v3/api-docs/;
|
||||
* }
|
||||
* @Author 1024创新实验室-主任: 卓大
|
||||
* @Date 2020-03-25 22:54:46
|
||||
@@ -43,7 +43,7 @@ public class SwaggerConfig {
|
||||
/**
|
||||
* 用于解决/swagger-ui/index.html页面ServersUrl 测试环境部署错误问题
|
||||
*/
|
||||
@Value("${springdoc.swagger-ui.server-base-url:''}")
|
||||
@Value("${springdoc.swagger-ui.server-base-url}")
|
||||
private String serverBaseUrl;
|
||||
|
||||
public static final String[] SWAGGER_WHITELIST = {
|
||||
|
||||
@@ -23,5 +23,7 @@ public class RedisKeyConst {
|
||||
|
||||
public static final String CAPTCHA = "captcha:";
|
||||
|
||||
public static final String LOGIN_VERIFICATION_CODE = "login:verification-code:";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -49,6 +49,8 @@ public class SwaggerTagConst {
|
||||
|
||||
public static final String PROTECT = "业务支撑-网络安全";
|
||||
|
||||
public static final String DATA_MASKING = "业务支撑-数据脱敏";
|
||||
|
||||
public static final String JOB = "业务支撑-定时任务";
|
||||
|
||||
public static final String MESSAGE = "业务支撑-消息";
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package net.lab1024.sa.base.handler;
|
||||
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* Mybatis Plus 插入或者更新时指定字段设置值
|
||||
*
|
||||
* @author zhoumingfa
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class MybatisPlusFillHandler implements MetaObjectHandler {
|
||||
|
||||
public static final String CREATE_TIME = "createTime";
|
||||
|
||||
public static final String UPDATE_TIME = "updateTime";
|
||||
|
||||
@Override
|
||||
public void insertFill(MetaObject metaObject) {
|
||||
if (metaObject.hasSetter(CREATE_TIME)) {
|
||||
this.fillStrategy(metaObject, CREATE_TIME, LocalDateTime.now());
|
||||
}
|
||||
if (metaObject.hasSetter(UPDATE_TIME)) {
|
||||
this.fillStrategy(metaObject, UPDATE_TIME, LocalDateTime.now());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFill(MetaObject metaObject) {
|
||||
if (metaObject.hasSetter(UPDATE_TIME)) {
|
||||
this.fillStrategy(metaObject, UPDATE_TIME, LocalDateTime.now());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+3
-1
@@ -42,8 +42,10 @@ public class CaptchaService {
|
||||
|
||||
@Resource
|
||||
private DefaultKaptcha defaultKaptcha;
|
||||
@Autowired
|
||||
|
||||
@Resource
|
||||
private SystemEnvironment systemEnvironment;
|
||||
|
||||
@Resource
|
||||
private RedisService redisService;
|
||||
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ public enum CodeFrontComponentEnum implements BaseEnum {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
-1
@@ -32,7 +32,6 @@ public class CodeGeneratorConfigForm {
|
||||
@Schema(description = "表名")
|
||||
private String tableName;
|
||||
|
||||
|
||||
@Valid
|
||||
@NotNull(message = "基础信息不能为空")
|
||||
@Schema(description = "基础信息")
|
||||
|
||||
+2
-2
@@ -39,8 +39,8 @@ public class CodeInsertAndUpdateField {
|
||||
@Schema(description = "更新标识")
|
||||
private Boolean updateFlag;
|
||||
|
||||
@SchemaEnum(value = CodeGeneratorPageTypeEnum.class)
|
||||
@CheckEnum(value = CodeFrontComponentEnum.class, message = "3.增加、修改 增加、修改 组件类型 枚举值错误", required = true)
|
||||
@SchemaEnum(value = CodeFrontComponentEnum.class)
|
||||
@CheckEnum(value = CodeFrontComponentEnum.class, message = "3.增加、修改 组件类型 枚举值错误", required = true)
|
||||
private String frontComponent;
|
||||
|
||||
}
|
||||
|
||||
+8
-8
@@ -13,16 +13,13 @@ import net.lab1024.sa.base.common.util.SmartStringUtil;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.domain.entity.CodeGeneratorConfigEntity;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.domain.form.CodeGeneratorConfigForm;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.domain.model.*;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.service.variable.backend.ControllerVariableService;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.service.variable.backend.DaoVariableService;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.service.variable.backend.ManagerVariableService;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.service.variable.backend.ServiceVariableService;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.service.variable.CodeGenerateBaseVariableService;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.service.variable.backend.*;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.service.variable.backend.domain.*;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.service.variable.front.ApiVariableService;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.service.variable.front.ConstVariableService;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.service.variable.front.FormVariableService;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.service.variable.front.ListVariableService;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.service.variable.CodeGenerateBaseVariableService;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.util.CodeGeneratorTool;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.velocity.Template;
|
||||
@@ -36,7 +33,7 @@ import javax.annotation.PostConstruct;
|
||||
import java.io.File;
|
||||
import java.io.OutputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -47,7 +44,7 @@ import java.util.stream.Collectors;
|
||||
* @Date 2022-06-30 22:15:38
|
||||
* @Wechat zhuoda1024
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a>
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a>
|
||||
*/
|
||||
|
||||
@Service
|
||||
@@ -70,6 +67,8 @@ public class CodeGeneratorTemplateService {
|
||||
map.put("java/manager/Manager.java", new ManagerVariableService());
|
||||
map.put("java/dao/Dao.java", new DaoVariableService());
|
||||
map.put("java/mapper/Mapper.xml", new MapperVariableService());
|
||||
// 菜单 SQL
|
||||
map.put("java/sql/Menu.sql", new MenuVariableService());
|
||||
// 前端
|
||||
map.put("js/api.js", new ApiVariableService());
|
||||
map.put("js/const.js", new ConstVariableService());
|
||||
@@ -94,6 +93,7 @@ public class CodeGeneratorTemplateService {
|
||||
String fileName = templateFile.startsWith("java") ? upperCamel + templateSplit[templateSplit.length - 1] : lowerHyphen + "-" + templateSplit[templateSplit.length - 1];
|
||||
String fullPathFileName = templateFile.replaceAll(templateSplit[templateSplit.length - 1], fileName);
|
||||
fullPathFileName = fullPathFileName.replaceAll("java/", "java/" + basic.getModuleName().toLowerCase() + "/");
|
||||
fullPathFileName = fullPathFileName.replaceAll("js/", "js/" + lowerHyphen + "/");
|
||||
|
||||
String fileContent = generate(tableName, templateFile, codeGeneratorConfigEntity);
|
||||
File file = new File(uuid + "/" + fullPathFileName);
|
||||
@@ -129,7 +129,7 @@ public class CodeGeneratorTemplateService {
|
||||
}
|
||||
|
||||
|
||||
ZipUtil.zip(outputStream, Charset.forName("utf-8"), false, null, dir);
|
||||
ZipUtil.zip(outputStream, StandardCharsets.UTF_8, false, null, dir);
|
||||
|
||||
FileUtil.del(dir);
|
||||
|
||||
|
||||
+18
-27
@@ -2,6 +2,7 @@ package net.lab1024.sa.base.module.support.codegenerator.service.variable;
|
||||
|
||||
import com.google.common.base.CaseFormat;
|
||||
import net.lab1024.sa.base.common.util.SmartStringUtil;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.constant.CodeFrontComponentEnum;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.domain.form.CodeGeneratorConfigForm;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeField;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeInsertAndUpdate;
|
||||
@@ -17,7 +18,7 @@ import java.util.stream.Collectors;
|
||||
* @Date 2022/9/29 17:20:41
|
||||
* @Wechat zhuoda1024
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a>
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a>
|
||||
*/
|
||||
public abstract class CodeGenerateBaseVariableService {
|
||||
|
||||
@@ -43,13 +44,13 @@ public abstract class CodeGenerateBaseVariableService {
|
||||
String upperCamelName = CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_CAMEL, form.getBasic().getModuleName());
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
|
||||
list.add("import " + form.getBasic().getJavaPackageName() + ".domain.entity." + upperCamelName + "Entity;" );
|
||||
list.add("import " + form.getBasic().getJavaPackageName() + ".domain.entity." + upperCamelName + "Entity;");
|
||||
|
||||
list.add("import " + form.getBasic().getJavaPackageName() + ".domain.form." + upperCamelName + "AddForm;" );
|
||||
list.add("import " + form.getBasic().getJavaPackageName() + ".domain.form." + upperCamelName + "UpdateForm;" );
|
||||
list.add("import " + form.getBasic().getJavaPackageName() + ".domain.form." + upperCamelName + "QueryForm;" );
|
||||
list.add("import " + form.getBasic().getJavaPackageName() + ".domain.form." + upperCamelName + "AddForm;");
|
||||
list.add("import " + form.getBasic().getJavaPackageName() + ".domain.form." + upperCamelName + "UpdateForm;");
|
||||
list.add("import " + form.getBasic().getJavaPackageName() + ".domain.form." + upperCamelName + "QueryForm;");
|
||||
|
||||
list.add("import " + form.getBasic().getJavaPackageName() + ".domain.vo." + upperCamelName + "VO;" );
|
||||
list.add("import " + form.getBasic().getJavaPackageName() + ".domain.vo." + upperCamelName + "VO;");
|
||||
return list;
|
||||
}
|
||||
|
||||
@@ -88,43 +89,33 @@ public abstract class CodeGenerateBaseVariableService {
|
||||
}
|
||||
|
||||
CodeInsertAndUpdateField field = first.get();
|
||||
return SmartStringUtil.contains(field.getFrontComponent(), "Upload" );
|
||||
return SmartStringUtil.equals(field.getFrontComponent(), CodeFrontComponentEnum.FILE_UPLOAD.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为 枚举
|
||||
* 是否为 字典
|
||||
*/
|
||||
protected boolean isDict(String columnName, CodeGeneratorConfigForm form) {
|
||||
List<CodeField> fields = form.getFields();
|
||||
if (CollectionUtils.isEmpty(fields)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Optional<CodeField> first = fields.stream().filter(e -> columnName.equals(e.getColumnName())).findFirst();
|
||||
if (first.isPresent()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CodeField codeField = first.get();
|
||||
return codeField.getDict() != null;
|
||||
CodeField codeField = getCodeField(columnName, form);
|
||||
return codeField != null && codeField.getDict() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为 枚举
|
||||
*/
|
||||
protected boolean isEnum(String columnName, CodeGeneratorConfigForm form) {
|
||||
CodeField codeField = getCodeField(columnName, form);
|
||||
return codeField != null && codeField.getEnumName() != null;
|
||||
}
|
||||
|
||||
private CodeField getCodeField(String columnName, CodeGeneratorConfigForm form) {
|
||||
List<CodeField> fields = form.getFields();
|
||||
if (CollectionUtils.isEmpty(fields)) {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
Optional<CodeField> first = fields.stream().filter(e -> columnName.equals(e.getColumnName())).findFirst();
|
||||
if (first.isPresent()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CodeField codeField = first.get();
|
||||
return codeField.getEnumName() != null;
|
||||
return first.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package net.lab1024.sa.base.module.support.codegenerator.service.variable.backend;
|
||||
|
||||
import net.lab1024.sa.base.module.support.codegenerator.domain.form.CodeGeneratorConfigForm;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.service.variable.CodeGenerateBaseVariableService;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 目前暂时没用到 这是一个空实现
|
||||
*
|
||||
* @author zhoumingfa
|
||||
* @date 2024/8/13
|
||||
*/
|
||||
public class MenuVariableService extends CodeGenerateBaseVariableService {
|
||||
|
||||
@Override
|
||||
public boolean isSupport(CodeGeneratorConfigForm form) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getInjectVariablesMap(CodeGeneratorConfigForm form) {
|
||||
return new HashMap<>(2);
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -2,6 +2,7 @@ package net.lab1024.sa.base.module.support.codegenerator.service.variable.backen
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import net.lab1024.sa.base.common.util.SmartStringUtil;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.constant.CodeFrontComponentEnum;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.domain.form.CodeGeneratorConfigForm;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeField;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeInsertAndUpdate;
|
||||
@@ -100,7 +101,6 @@ public class AddFormVariableService extends CodeGenerateBaseVariableService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//字典
|
||||
if (SmartStringUtil.isNotEmpty(codeField.getDict())) {
|
||||
finalFieldMap.put("dict", "\n @JsonDeserialize(using = DictValueVoDeserializer.class)");
|
||||
@@ -109,7 +109,7 @@ public class AddFormVariableService extends CodeGenerateBaseVariableService {
|
||||
}
|
||||
|
||||
//文件上传
|
||||
if (SmartStringUtil.contains(field.getFrontComponent(), "Upload")) {
|
||||
if (CodeFrontComponentEnum.FILE_UPLOAD.equalsValue(field.getFrontComponent())) {
|
||||
finalFieldMap.put("file", "\n @JsonDeserialize(using = FileKeyVoDeserializer.class)");
|
||||
packageList.add("import com.fasterxml.jackson.databind.annotation.JsonDeserialize;");
|
||||
packageList.add("import net.lab1024.sa.base.common.json.deserializer.FileKeyVoDeserializer;");
|
||||
|
||||
+10
-3
@@ -14,7 +14,7 @@ import java.util.stream.Collectors;
|
||||
* @Date 2022/9/29 17:20:41
|
||||
* @Wechat zhuoda1024
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a>
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a>
|
||||
*/
|
||||
|
||||
public class EntityVariableService extends CodeGenerateBaseVariableService {
|
||||
@@ -54,14 +54,21 @@ public class EntityVariableService extends CodeGenerateBaseVariableService {
|
||||
// mybatis plus
|
||||
result.add("import com.baomidou.mybatisplus.annotation.TableName;");
|
||||
|
||||
// 自动填充注解
|
||||
boolean existCreateAndUpdate = fields.stream().anyMatch(e -> "create_time".equals(e.getColumnName()) || "update_time".equals(e.getColumnName()));
|
||||
if (existCreateAndUpdate) {
|
||||
result.add("import com.baomidou.mybatisplus.annotation.FieldFill;");
|
||||
result.add("import com.baomidou.mybatisplus.annotation.TableField;");
|
||||
}
|
||||
|
||||
//主键
|
||||
boolean isExistPrimaryKey = fields.stream().filter(e -> e.getPrimaryKeyFlag() != null && e.getPrimaryKeyFlag()).findFirst().isPresent();
|
||||
boolean isExistPrimaryKey = fields.stream().anyMatch(e -> e.getPrimaryKeyFlag() != null && e.getPrimaryKeyFlag());
|
||||
if (isExistPrimaryKey) {
|
||||
result.add("import com.baomidou.mybatisplus.annotation.TableId;");
|
||||
}
|
||||
|
||||
//自增
|
||||
boolean isExistAutoIncrease = fields.stream().filter(e -> e.getAutoIncreaseFlag() != null && e.getAutoIncreaseFlag()).findFirst().isPresent();
|
||||
boolean isExistAutoIncrease = fields.stream().anyMatch(e -> e.getAutoIncreaseFlag() != null && e.getAutoIncreaseFlag());
|
||||
if (isExistAutoIncrease) {
|
||||
result.add("import com.baomidou.mybatisplus.annotation.IdType;");
|
||||
}
|
||||
|
||||
+29
-36
@@ -3,19 +3,20 @@ package net.lab1024.sa.base.module.support.codegenerator.service.variable.backen
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.constant.CodeQueryFieldQueryTypeEnum;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.domain.form.CodeGeneratorConfigForm;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeInsertAndUpdateField;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeQueryField;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.service.variable.CodeGenerateBaseVariableService;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @Author 1024创新实验室-主任:卓大
|
||||
* @Date 2022/9/29 17:20:41
|
||||
* @Wechat zhuoda1024
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a>
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a>
|
||||
*/
|
||||
|
||||
public class MapperVariableService extends CodeGenerateBaseVariableService {
|
||||
@@ -39,55 +40,47 @@ public class MapperVariableService extends CodeGenerateBaseVariableService {
|
||||
List<String> columnNameList = queryField.getColumnNameList();
|
||||
if (columnNameList.size() == 1) {
|
||||
// AND INSTR(t_notice.title,#{query.keywords})
|
||||
stringBuilder.append(" AND INSTR(" )
|
||||
.append(form.getTableName()).append("." ).append(queryField.getColumnNameList().get(0))
|
||||
.append(",#{queryForm." )
|
||||
stringBuilder.append(" AND INSTR(")
|
||||
.append(form.getTableName()).append(".").append(queryField.getColumnNameList().get(0))
|
||||
.append(",#{queryForm.")
|
||||
.append(queryField.getFieldName())
|
||||
.append("})" );
|
||||
.append("})");
|
||||
} else {
|
||||
for (int i = 0; i < columnNameList.size(); i++) {
|
||||
if (i == 0) {
|
||||
stringBuilder.append("AND ( INSTR(" )
|
||||
.append(form.getTableName()).append("." ).append(queryField.getColumnNameList().get(i))
|
||||
.append(",#{queryForm." )
|
||||
stringBuilder.append("AND ( INSTR(")
|
||||
.append(form.getTableName()).append(".").append(queryField.getColumnNameList().get(i))
|
||||
.append(",#{queryForm.")
|
||||
.append(queryField.getFieldName())
|
||||
.append("})" );
|
||||
.append("})");
|
||||
} else {
|
||||
// OR INSTR(t_notice.author,#{query.keywords})
|
||||
stringBuilder.append("\n OR INSTR(" )
|
||||
.append(form.getTableName()).append("." ).append(queryField.getColumnNameList().get(i))
|
||||
.append(",#{queryForm." )
|
||||
stringBuilder.append("\n OR INSTR(")
|
||||
.append(form.getTableName()).append(".").append(queryField.getColumnNameList().get(i))
|
||||
.append(",#{queryForm.")
|
||||
.append(queryField.getFieldName())
|
||||
.append("})" );
|
||||
.append("})");
|
||||
}
|
||||
}
|
||||
stringBuilder.append("\n )" );
|
||||
stringBuilder.append("\n )");
|
||||
}
|
||||
fieldMap.put("likeStr", stringBuilder.toString());
|
||||
}else{
|
||||
fieldMap.put("columnName",queryField.getColumnNameList().get(0));
|
||||
} else if (CodeQueryFieldQueryTypeEnum.DICT.equalsValue(queryField.getQueryTypeEnum())) {
|
||||
String stringBuilder = "AND INSTR(" +
|
||||
form.getTableName() + "." + queryField.getColumnNameList().get(0) +
|
||||
",#{queryForm." +
|
||||
queryField.getFieldName() +
|
||||
"})";
|
||||
fieldMap.put("likeStr", stringBuilder);
|
||||
}
|
||||
else {
|
||||
fieldMap.put("columnName", queryField.getColumnNameList().get(0));
|
||||
}
|
||||
}
|
||||
|
||||
variablesMap.put("queryFields", finalQueryFiledList);
|
||||
variablesMap.put("daoClassName", form.getBasic().getJavaPackageName() + ".dao." + form.getBasic().getModuleName() + "Dao" );
|
||||
variablesMap.put("daoClassName", form.getBasic().getJavaPackageName() + ".dao." + form.getBasic().getModuleName() + "Dao");
|
||||
return variablesMap;
|
||||
}
|
||||
|
||||
|
||||
public List<String> getPackageList(List<CodeInsertAndUpdateField> fields, CodeGeneratorConfigForm form) {
|
||||
if (CollectionUtils.isEmpty(fields)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
HashSet<String> packageList = new HashSet<>();
|
||||
|
||||
//1、javabean相关的包
|
||||
packageList.addAll(getJavaBeanImportClass(form));
|
||||
|
||||
//2、dao
|
||||
packageList.add("import " + form.getBasic().getJavaPackageName() + ".dao." + form.getBasic().getModuleName() + "Dao;" );
|
||||
return new ArrayList<>(packageList);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+13
-11
@@ -2,6 +2,7 @@ package net.lab1024.sa.base.module.support.codegenerator.service.variable.backen
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import net.lab1024.sa.base.common.util.SmartEnumUtil;
|
||||
import net.lab1024.sa.base.common.util.SmartStringUtil;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.constant.CodeQueryFieldQueryTypeEnum;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.domain.form.CodeGeneratorConfigForm;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeField;
|
||||
@@ -18,7 +19,7 @@ import java.util.stream.Collectors;
|
||||
* @Date 2022/9/29 17:20:41
|
||||
* @Wechat zhuoda1024
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a>
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a>
|
||||
*/
|
||||
|
||||
public class QueryFormVariableService extends CodeGenerateBaseVariableService {
|
||||
@@ -41,14 +42,11 @@ public class QueryFormVariableService extends CodeGenerateBaseVariableService {
|
||||
|
||||
|
||||
public ImmutablePair<List<String>, List<Map<String, Object>>> getPackageListAndFields(CodeGeneratorConfigForm form) {
|
||||
|
||||
List<CodeQueryField> fields = form.getQueryFields();
|
||||
if (CollectionUtils.isEmpty(fields)) {
|
||||
return ImmutablePair.of(new ArrayList<>(), new ArrayList<>());
|
||||
}
|
||||
|
||||
HashSet<String> packageList = new HashSet<>();
|
||||
|
||||
|
||||
/**
|
||||
* 1、LocalDate、LocalDateTime、BigDecimal 类型的包名
|
||||
* 2、排序
|
||||
@@ -75,9 +73,6 @@ public class QueryFormVariableService extends CodeGenerateBaseVariableService {
|
||||
CodeField codeField = null;
|
||||
|
||||
switch (queryTypeEnum) {
|
||||
case LIKE:
|
||||
finalFieldMap.put("javaType", "String");
|
||||
break;
|
||||
case EQUAL:
|
||||
codeField = getCodeFieldByColumnName(field.getColumnNameList().get(0), form);
|
||||
if (codeField == null) {
|
||||
@@ -109,6 +104,14 @@ public class QueryFormVariableService extends CodeGenerateBaseVariableService {
|
||||
|
||||
finalFieldMap.put("javaType", codeField.getJavaType());
|
||||
break;
|
||||
case DICT:
|
||||
codeField = getCodeFieldByColumnName(field.getColumnNameList().get(0), form);
|
||||
if (SmartStringUtil.isNotEmpty(codeField.getDict())) {
|
||||
finalFieldMap.put("dict", "\n @JsonDeserialize(using = DictValueVoDeserializer.class)");
|
||||
packageList.add("import com.fasterxml.jackson.databind.annotation.JsonDeserialize;");
|
||||
packageList.add("import net.lab1024.sa.base.common.json.deserializer.DictValueVoDeserializer;");
|
||||
}
|
||||
finalFieldMap.put("javaType", "String");
|
||||
default:
|
||||
finalFieldMap.put("javaType", "String");
|
||||
}
|
||||
@@ -116,12 +119,11 @@ public class QueryFormVariableService extends CodeGenerateBaseVariableService {
|
||||
finalFieldList.add(finalFieldMap);
|
||||
}
|
||||
|
||||
|
||||
// lombok
|
||||
packageList.add("import lombok.Data;");
|
||||
packageList.add("import lombok.EqualsAndHashCode;");
|
||||
|
||||
List<String> packageNameList = packageList.stream().filter(Objects::nonNull).collect(Collectors.toList());
|
||||
Collections.sort(packageNameList);
|
||||
List<String> packageNameList = packageList.stream().filter(Objects::nonNull).sorted().collect(Collectors.toList());
|
||||
return ImmutablePair.of(packageNameList, finalFieldList);
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -2,6 +2,7 @@ package net.lab1024.sa.base.module.support.codegenerator.service.variable.backen
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import net.lab1024.sa.base.common.util.SmartStringUtil;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.constant.CodeFrontComponentEnum;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.domain.form.CodeGeneratorConfigForm;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeField;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeInsertAndUpdate;
|
||||
@@ -18,7 +19,7 @@ import java.util.stream.Collectors;
|
||||
* @Date 2022/9/29 17:20:41
|
||||
* @Wechat zhuoda1024
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a>
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a>
|
||||
*/
|
||||
|
||||
public class UpdateFormVariableService extends CodeGenerateBaseVariableService {
|
||||
@@ -42,7 +43,7 @@ public class UpdateFormVariableService extends CodeGenerateBaseVariableService {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(Boolean.TRUE.equals(codeField.getPrimaryKeyFlag())){
|
||||
if (Boolean.TRUE.equals(codeField.getPrimaryKeyFlag())) {
|
||||
e.setRequiredFlag(true);
|
||||
}
|
||||
|
||||
@@ -123,7 +124,7 @@ public class UpdateFormVariableService extends CodeGenerateBaseVariableService {
|
||||
}
|
||||
|
||||
//文件上传
|
||||
if (SmartStringUtil.contains(field.getFrontComponent(), "Upload")) {
|
||||
if (CodeFrontComponentEnum.FILE_UPLOAD.equalsValue(field.getFrontComponent())) {
|
||||
finalFieldMap.put("file", "\n @JsonDeserialize(using = FileKeyVoDeserializer.class)");
|
||||
packageList.add("import com.fasterxml.jackson.databind.annotation.JsonDeserialize;");
|
||||
packageList.add("import net.lab1024.sa.base.common.json.deserializer.FileKeyVoDeserializer;");
|
||||
|
||||
+7
-7
@@ -17,7 +17,7 @@ import java.util.stream.Collectors;
|
||||
* @Date 2022/9/29 17:20:41
|
||||
* @Wechat zhuoda1024
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a>
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a>
|
||||
*/
|
||||
|
||||
public class VOVariableService extends CodeGenerateBaseVariableService {
|
||||
@@ -87,16 +87,16 @@ public class VOVariableService extends CodeGenerateBaseVariableService {
|
||||
|
||||
//字典
|
||||
if (isDict(field.getColumnName(), form)) {
|
||||
finalFieldMap.put("dict", "\n @JsonDeserialize(using = DictValueVoDeserializer.class)");
|
||||
packageList.add("import com.fasterxml.jackson.databind.annotation.JsonDeserialize;");
|
||||
packageList.add("import net.lab1024.sa.base.common.json.deserializer.DictValueVoDeserializer;");
|
||||
finalFieldMap.put("dict", "\n @JsonSerialize(using = DictValueVoSerializer.class)");
|
||||
packageList.add("import com.fasterxml.jackson.databind.annotation.JsonSerialize;");
|
||||
packageList.add("import net.lab1024.sa.base.common.json.serializer.DictValueVoSerializer;");
|
||||
}
|
||||
|
||||
//文件上传
|
||||
if (isFile(field.getColumnName(), form)) {
|
||||
finalFieldMap.put("file", "\n @JsonDeserialize(using = FileKeyVoDeserializer.class)");
|
||||
packageList.add("import com.fasterxml.jackson.databind.annotation.JsonDeserialize;");
|
||||
packageList.add("import net.lab1024.sa.base.common.json.deserializer.FileKeyVoDeserializer;");
|
||||
finalFieldMap.put("file", "\n @JsonSerialize(using = FileKeyVoSerializer.class)");
|
||||
packageList.add("import com.fasterxml.jackson.databind.annotation.JsonSerialize;");
|
||||
packageList.add("import net.lab1024.sa.base.common.json.serializer.FileKeyVoSerializer;");
|
||||
}
|
||||
|
||||
packageList.add(getJavaPackageName(codeField.getJavaType()));
|
||||
|
||||
+5
-4
@@ -57,19 +57,20 @@ public class FormVariableService extends CodeGenerateBaseVariableService {
|
||||
|
||||
fieldsVariableList.add(objectMap);
|
||||
|
||||
if (CodeFrontComponentEnum.ENUM_SELECT.getValue().equals(field.getFrontComponent())) {
|
||||
if (CodeFrontComponentEnum.ENUM_SELECT.equalsValue(field.getFrontComponent())) {
|
||||
frontImportSet.add("import SmartEnumSelect from '/@/components/framework/smart-enum-select/index.vue';");
|
||||
}
|
||||
|
||||
if (CodeFrontComponentEnum.BOOLEAN_SELECT.getValue().equals(field.getFrontComponent())) {
|
||||
if (CodeFrontComponentEnum.BOOLEAN_SELECT.equalsValue(field.getFrontComponent())) {
|
||||
frontImportSet.add("import BooleanSelect from '/@/components/framework/boolean-select/index.vue';");
|
||||
}
|
||||
|
||||
if (CodeFrontComponentEnum.DICT_SELECT.getValue().equals(field.getFrontComponent())) {
|
||||
if (CodeFrontComponentEnum.DICT_SELECT.equalsValue(field.getFrontComponent())) {
|
||||
frontImportSet.add("import DictSelect from '/@/components/support/dict-select/index.vue';");
|
||||
}
|
||||
|
||||
if (CodeFrontComponentEnum.FILE_UPLOAD.getValue().equals(field.getFrontComponent())) {
|
||||
if (CodeFrontComponentEnum.FILE_UPLOAD.equalsValue(field.getFrontComponent())) {
|
||||
frontImportSet.add("import { FILE_FOLDER_TYPE_ENUM } from '/@/constants/support/file-const';");
|
||||
frontImportSet.add("import FileUpload from '/@/components/support/file-upload/index.vue';");
|
||||
}
|
||||
}
|
||||
|
||||
+9
-4
@@ -4,6 +4,7 @@ import cn.hutool.core.bean.BeanUtil;
|
||||
import com.google.common.base.CaseFormat;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.constant.CodeQueryFieldQueryTypeEnum;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.domain.form.CodeGeneratorConfigForm;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeField;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeQueryField;
|
||||
import net.lab1024.sa.base.module.support.codegenerator.service.variable.CodeGenerateBaseVariableService;
|
||||
|
||||
@@ -35,19 +36,23 @@ public class ListVariableService extends CodeGenerateBaseVariableService {
|
||||
|
||||
for (CodeQueryField queryField : queryFields) {
|
||||
Map<String, Object> objectMap = BeanUtil.beanToMap(queryField);
|
||||
variableList.add(objectMap);
|
||||
|
||||
if("Enum".equals(queryField.getQueryTypeEnum())){
|
||||
CodeField codeField = getCodeFieldByColumnName(queryField.getColumnNameList().get(0), form);
|
||||
objectMap.put("frontEnumName", codeField.getEnumName());
|
||||
objectMap.put("dict", codeField.getDict());
|
||||
|
||||
if(CodeQueryFieldQueryTypeEnum.ENUM.equalsValue(queryField.getQueryTypeEnum())){
|
||||
frontImportSet.add("import SmartEnumSelect from '/@/components/framework/smart-enum-select/index.vue';");
|
||||
}
|
||||
|
||||
if("Dict".equals(queryField.getQueryTypeEnum())){
|
||||
if(CodeQueryFieldQueryTypeEnum.DICT.equalsValue(queryField.getQueryTypeEnum())){
|
||||
frontImportSet.add("import DictSelect from '/@/components/support/dict-select/index.vue';");
|
||||
}
|
||||
|
||||
if(CodeQueryFieldQueryTypeEnum.DATE_RANGE.getValue().equals(queryField.getQueryTypeEnum())){
|
||||
if(CodeQueryFieldQueryTypeEnum.DATE_RANGE.equalsValue(queryField.getQueryTypeEnum())){
|
||||
frontImportSet.add("import { defaultTimeRanges } from '/@/lib/default-time-ranges';");
|
||||
}
|
||||
variableList.add(objectMap);
|
||||
|
||||
}
|
||||
variablesMap.put("queryFields",variableList);
|
||||
|
||||
+1
@@ -22,6 +22,7 @@ public enum ConfigKeyEnum implements BaseEnum {
|
||||
*/
|
||||
SUPER_PASSWORD("super_password", "万能密码"),
|
||||
|
||||
LEVEL3_PROTECT_CONFIG("level3_protect_config", "三级等保配置"),
|
||||
;
|
||||
|
||||
private final String value;
|
||||
|
||||
+5
-4
@@ -109,7 +109,8 @@ public class ConfigService {
|
||||
*
|
||||
*/
|
||||
public String getConfigValue(ConfigKeyEnum configKey) {
|
||||
return this.getConfig(configKey).getConfigValue();
|
||||
ConfigVO config = this.getConfig(configKey);
|
||||
return config == null ? null : config.getConfigValue();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,12 +126,12 @@ public class ConfigService {
|
||||
* 添加系统配置
|
||||
*
|
||||
*/
|
||||
public ResponseDTO<String> add(ConfigAddForm configAddDTO) {
|
||||
ConfigEntity entity = configDao.selectByKey(configAddDTO.getConfigKey());
|
||||
public ResponseDTO<String> add(ConfigAddForm configAddForm) {
|
||||
ConfigEntity entity = configDao.selectByKey(configAddForm.getConfigKey());
|
||||
if (null != entity) {
|
||||
return ResponseDTO.error(UserErrorCode.ALREADY_EXIST);
|
||||
}
|
||||
entity = SmartBeanUtil.copy(configAddDTO, ConfigEntity.class);
|
||||
entity = SmartBeanUtil.copy(configAddForm, ConfigEntity.class);
|
||||
configDao.insert(entity);
|
||||
|
||||
// 刷新缓存
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package net.lab1024.sa.base.module.support.datamasking;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import net.lab1024.sa.base.common.json.serializer.DataMaskingSerializer;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 脱敏注解
|
||||
*
|
||||
* @author 罗伊
|
||||
* @description:
|
||||
* @date 2024/7/21 4:39 下午
|
||||
*/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@JacksonAnnotationsInside
|
||||
@JsonSerialize(using = DataMaskingSerializer.class, nullsUsing = DataMaskingSerializer.class)
|
||||
public @interface DataMasking {
|
||||
|
||||
DataMaskingTypeEnum value() default DataMaskingTypeEnum.COMMON;
|
||||
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package net.lab1024.sa.base.module.support.datamasking;
|
||||
|
||||
import cn.hutool.core.util.DesensitizedUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 脱敏数据类型
|
||||
*
|
||||
* @Author 1024创新实验室-创始人兼主任:卓大
|
||||
* @Date 2024/8/1
|
||||
* @Wechat zhuoda1024
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a> ,Since 2012
|
||||
*/
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum DataMaskingTypeEnum {
|
||||
|
||||
COMMON(null, "通用"),
|
||||
PHONE(DesensitizedUtil.DesensitizedType.MOBILE_PHONE, "手机号"),
|
||||
CHINESE_NAME(DesensitizedUtil.DesensitizedType.CHINESE_NAME, "中文名"),
|
||||
ID_CARD(DesensitizedUtil.DesensitizedType.ID_CARD, "身份证号"),
|
||||
FIXED_PHONE(DesensitizedUtil.DesensitizedType.FIXED_PHONE, "座机号"),
|
||||
ADDRESS(DesensitizedUtil.DesensitizedType.ADDRESS, "地址"),
|
||||
EMAIL(DesensitizedUtil.DesensitizedType.EMAIL, "电子邮件"),
|
||||
PASSWORD(DesensitizedUtil.DesensitizedType.PASSWORD, "密码"),
|
||||
CAR_LICENSE(DesensitizedUtil.DesensitizedType.CAR_LICENSE, "中国大陆车牌"),
|
||||
BANK_CARD(DesensitizedUtil.DesensitizedType.BANK_CARD, "银行卡"),
|
||||
USER_ID(DesensitizedUtil.DesensitizedType.USER_ID, "用户id");
|
||||
|
||||
|
||||
|
||||
private DesensitizedUtil.DesensitizedType type;
|
||||
|
||||
private String desc;
|
||||
|
||||
|
||||
}
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
package net.lab1024.sa.base.module.support.datamasking;
|
||||
|
||||
import cn.hutool.core.util.DesensitizedUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 脱敏工具类
|
||||
*
|
||||
* @Author 1024创新实验室-主任: 卓大
|
||||
* @Date 2024-07-23 21:38:52
|
||||
* @Wechat zhuoda1024
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a>
|
||||
*/
|
||||
public class SmartDataMaskingUtil {
|
||||
|
||||
/**
|
||||
* 类 加注解字段缓存
|
||||
*/
|
||||
private static final ConcurrentHashMap<Class<?>, List<Field>> fieldMap = new ConcurrentHashMap<>();
|
||||
|
||||
public static String dataMasking(String value) {
|
||||
if (StringUtils.isBlank(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value.length() < 4) {
|
||||
return StrUtil.hide(value, 0, value.length());
|
||||
}
|
||||
|
||||
int valueLength = value.length();
|
||||
int startHideIndex = getHideStartIndex(valueLength);
|
||||
int endHideIndex = getHideEndIndex(valueLength);
|
||||
return StrUtil.hide(value, startHideIndex, endHideIndex);
|
||||
}
|
||||
|
||||
public static Object dataMasking(Object value, DataMaskingTypeEnum dataType) {
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (dataType == null) {
|
||||
return dataMasking(String.valueOf(value));
|
||||
}
|
||||
|
||||
switch (dataType) {
|
||||
case PHONE:
|
||||
return DesensitizedUtil.mobilePhone(String.valueOf(value));
|
||||
case CHINESE_NAME:
|
||||
return DesensitizedUtil.chineseName(String.valueOf(value));
|
||||
case ID_CARD:
|
||||
return DesensitizedUtil.idCardNum(String.valueOf(value), 6, 2);
|
||||
case FIXED_PHONE:
|
||||
return DesensitizedUtil.fixedPhone(String.valueOf(value));
|
||||
case ADDRESS:
|
||||
return StrUtil.hide(String.valueOf(value), 6, String.valueOf(value).length() - 1);
|
||||
case EMAIL:
|
||||
return DesensitizedUtil.email(String.valueOf(value));
|
||||
case PASSWORD:
|
||||
return DesensitizedUtil.password(String.valueOf(value));
|
||||
case CAR_LICENSE:
|
||||
return DesensitizedUtil.carLicense(String.valueOf(value));
|
||||
case BANK_CARD:
|
||||
return DesensitizedUtil.bankCard(String.valueOf(value));
|
||||
case USER_ID:
|
||||
return DesensitizedUtil.userId();
|
||||
default:
|
||||
return dataMasking(String.valueOf(value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量脱敏
|
||||
*/
|
||||
public static <T> void dataMasking(Collection<T> objectList) throws IntrospectionException, InvocationTargetException, IllegalAccessException {
|
||||
if (CollectionUtils.isEmpty(objectList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (T object : objectList) {
|
||||
dataMasking(object);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 单个脱敏
|
||||
*/
|
||||
public static <T> void dataMasking(T object) throws IntrospectionException, InvocationTargetException, IllegalAccessException {
|
||||
Class<?> tClass = object.getClass();
|
||||
List<Field> fieldList = getField(object);
|
||||
for (Field field : fieldList) {
|
||||
field.setAccessible(true);
|
||||
String fieldValue = "";
|
||||
try {
|
||||
PropertyDescriptor pd = new PropertyDescriptor(field.getName(), tClass);
|
||||
Method getMethod = pd.getReadMethod();
|
||||
Object value = getMethod.invoke(object);
|
||||
if (value != null) {
|
||||
fieldValue = value.toString();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
if (StringUtils.isBlank(fieldValue)) {
|
||||
continue;
|
||||
}
|
||||
int valueLength = fieldValue.length();
|
||||
int startHideIndex = getHideStartIndex(valueLength);
|
||||
int endHideIndex = getHideEndIndex(valueLength);
|
||||
try {
|
||||
field.set(object, StrUtil.hide(fieldValue, startHideIndex, endHideIndex));
|
||||
} catch (Exception e1) {
|
||||
throw new RuntimeException(e1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int getHideStartIndex(int totalLength) {
|
||||
if (totalLength <= 4) {
|
||||
return 1;
|
||||
} else if (totalLength <= 6) {
|
||||
return 1;
|
||||
} else if (totalLength <= 10) {
|
||||
return 2;
|
||||
} else if (totalLength <= 18) {
|
||||
return 3;
|
||||
} else if (totalLength <= 27) {
|
||||
return 5;
|
||||
} else if (totalLength <= 34) {
|
||||
return 7;
|
||||
} else if (totalLength <= 41) {
|
||||
return 9;
|
||||
} else {
|
||||
return 15;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getHideEndIndex(int totalLength) {
|
||||
if (totalLength <= 4) {
|
||||
return totalLength - 1;
|
||||
} else if (totalLength <= 6) {
|
||||
return totalLength - 2;
|
||||
} else if (totalLength <= 10) {
|
||||
return totalLength - 2;
|
||||
} else if (totalLength <= 18) {
|
||||
return totalLength - 4;
|
||||
} else if (totalLength <= 27) {
|
||||
return totalLength - 6;
|
||||
} else if (totalLength <= 34) {
|
||||
return totalLength - 8;
|
||||
} else if (totalLength <= 41) {
|
||||
return totalLength - 10;
|
||||
} else {
|
||||
return totalLength - 16;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static List<Field> getField(Object object) throws IntrospectionException {
|
||||
// 从缓存中查询
|
||||
Class<?> tClass = object.getClass();
|
||||
List<Field> fieldList = fieldMap.get(tClass);
|
||||
if (null != fieldList) {
|
||||
return fieldList;
|
||||
}
|
||||
|
||||
// 这一段递归代码 是为了 从父类中获取属性
|
||||
Class<?> tempClass = tClass;
|
||||
fieldList = new ArrayList<>();
|
||||
while (tempClass != null) {
|
||||
Field[] declaredFields = tempClass.getDeclaredFields();
|
||||
for (Field field : declaredFields) {
|
||||
boolean stringField = false;
|
||||
try {
|
||||
PropertyDescriptor pd = new PropertyDescriptor(field.getName(), tClass);
|
||||
Method getMethod = pd.getReadMethod();
|
||||
Type returnType = getMethod.getGenericReturnType();
|
||||
stringField = "java.lang.String".equals(returnType.getTypeName());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
if (field.isAnnotationPresent(DataMasking.class) && stringField) {
|
||||
field.setAccessible(true);
|
||||
fieldList.add(field);
|
||||
}
|
||||
}
|
||||
tempClass = tempClass.getSuperclass();
|
||||
}
|
||||
fieldMap.put(tClass, fieldList);
|
||||
return fieldList;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(dataMasking("a", null));
|
||||
System.out.println(dataMasking("ab", null));
|
||||
System.out.println(dataMasking("abc", null));
|
||||
System.out.println(dataMasking("abcd", null));
|
||||
System.out.println(dataMasking("abcde", null));
|
||||
}
|
||||
|
||||
}
|
||||
+9
-11
@@ -19,9 +19,9 @@ import net.lab1024.sa.base.module.support.file.domain.vo.FileDownloadVO;
|
||||
import net.lab1024.sa.base.module.support.file.domain.vo.FileUploadVO;
|
||||
import net.lab1024.sa.base.module.support.file.domain.vo.FileVO;
|
||||
import net.lab1024.sa.base.module.support.redis.RedisService;
|
||||
import net.lab1024.sa.base.module.support.securityprotect.service.SecurityFileService;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@@ -39,7 +39,7 @@ import java.util.stream.Collectors;
|
||||
* @Date 2019年10月11日 15:34:47
|
||||
* @Wechat zhuoda1024
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a>
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a>
|
||||
*/
|
||||
@Service
|
||||
public class FileService {
|
||||
@@ -58,9 +58,8 @@ public class FileService {
|
||||
@Resource
|
||||
private RedisService redisService;
|
||||
|
||||
@Value("${spring.servlet.multipart.max-file-size}")
|
||||
private String maxFileSize;
|
||||
|
||||
@Resource
|
||||
private SecurityFileService securityFileService;
|
||||
|
||||
/**
|
||||
* 文件上传服务
|
||||
@@ -89,11 +88,10 @@ public class FileService {
|
||||
return ResponseDTO.userErrorParam("文件名称最大长度为:" + FILE_NAME_MAX_LENGTH);
|
||||
}
|
||||
|
||||
// 校验文件大小
|
||||
String maxSizeStr = maxFileSize.toLowerCase().replace("mb", "");
|
||||
long maxSize = Integer.parseInt(maxSizeStr) * 1024 * 1024L;
|
||||
if (file.getSize() > maxSize) {
|
||||
return ResponseDTO.userErrorParam("上传文件最大为:" + maxSize);
|
||||
// 校验文件大小以及安全性
|
||||
ResponseDTO<String> validateFile = securityFileService.checkFile(file);
|
||||
if (!validateFile.getOk()) {
|
||||
return ResponseDTO.error(validateFile);
|
||||
}
|
||||
|
||||
// 进行上传
|
||||
@@ -192,7 +190,7 @@ public class FileService {
|
||||
|
||||
// 根据文件服务类 获取对应文件服务 查询 url
|
||||
ResponseDTO<FileDownloadVO> download = fileStorageService.download(fileKey);
|
||||
if(download.getOk()){
|
||||
if (download.getOk()) {
|
||||
download.getData().getMetadata().setFileName(fileVO.getFileName());
|
||||
}
|
||||
return download;
|
||||
|
||||
+2
@@ -142,6 +142,8 @@ public class SmartJobExecutor implements Runnable {
|
||||
logEntity.setSuccessFlag(true);
|
||||
// 执行开始时间
|
||||
logEntity.setExecuteStartTime(executeTime);
|
||||
logEntity.setExecuteEndTime(executeTime);
|
||||
logEntity.setExecuteTimeMillis(0L);
|
||||
logEntity.setCreateName(executorName);
|
||||
logEntity.setIp(SmartIpUtil.getLocalFirstIp());
|
||||
logEntity.setProcessId(SmartJobUtil.getProcessId());
|
||||
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
package net.lab1024.sa.base.module.support.mail;
|
||||
|
||||
|
||||
import cn.hutool.core.lang.UUID;
|
||||
import freemarker.cache.StringTemplateLoader;
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.Template;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.lab1024.sa.base.common.domain.ResponseDTO;
|
||||
import net.lab1024.sa.base.common.domain.SystemEnvironment;
|
||||
import net.lab1024.sa.base.module.support.mail.constant.MailTemplateCodeEnum;
|
||||
import net.lab1024.sa.base.module.support.mail.constant.MailTemplateTypeEnum;
|
||||
import net.lab1024.sa.base.module.support.mail.domain.MailTemplateEntity;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.text.StringSubstitutor;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.mail.javamail.MimeMessageHelper;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
import java.io.File;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* 发生邮件:<br/>
|
||||
* 1、支持直接发送 <br/>
|
||||
* 2、支持使用邮件模板发送
|
||||
*
|
||||
* @Author 1024创新实验室-创始人兼主任:卓大
|
||||
* @Date 2024/8/5
|
||||
* @Wechat zhuoda1024
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a> ,Since 2012
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class MailService {
|
||||
|
||||
@Autowired
|
||||
private JavaMailSender javaMailSender;
|
||||
|
||||
@Resource
|
||||
private MailTemplateDao mailTemplateDao;
|
||||
|
||||
@Resource
|
||||
private SystemEnvironment systemEnvironment;
|
||||
|
||||
@Value("${spring.mail.username}")
|
||||
private String clientMail;
|
||||
|
||||
|
||||
/**
|
||||
* 使用模板发送邮件
|
||||
*/
|
||||
public ResponseDTO<String> sendMail(MailTemplateCodeEnum templateCode, Map<String, Object> templateParamsMap, List<String> receiverUserList, List<File> fileList) {
|
||||
|
||||
MailTemplateEntity mailTemplateEntity = mailTemplateDao.selectById(templateCode.name().toLowerCase());
|
||||
if (mailTemplateEntity == null) {
|
||||
return ResponseDTO.userErrorParam("模版不存在");
|
||||
}
|
||||
|
||||
if (mailTemplateEntity.getDisableFlag()) {
|
||||
return ResponseDTO.userErrorParam("模版已禁用,无法发送");
|
||||
}
|
||||
|
||||
String content = null;
|
||||
if (MailTemplateTypeEnum.FREEMARKER.name().equalsIgnoreCase(mailTemplateEntity.getTemplateType().trim())) {
|
||||
content = freemarkerResolverContent(mailTemplateEntity.getTemplateContent(), templateParamsMap);
|
||||
} else if (MailTemplateTypeEnum.STRING.name().equalsIgnoreCase(mailTemplateEntity.getTemplateType().trim())) {
|
||||
content = stringResolverContent(mailTemplateEntity.getTemplateContent(), templateParamsMap);
|
||||
} else {
|
||||
return ResponseDTO.userErrorParam("模版类型不存在");
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
this.sendMail(mailTemplateEntity.getTemplateSubject(), content, fileList, receiverUserList, true);
|
||||
|
||||
} catch (Throwable e) {
|
||||
log.error("邮件发送失败", e);
|
||||
return ResponseDTO.userErrorParam("邮件发送失败");
|
||||
}
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用模板发送邮件
|
||||
*/
|
||||
public ResponseDTO<String> sendMail(MailTemplateCodeEnum templateCode, Map<String, Object> templateParamsMap, List<String> receiverUserList) {
|
||||
return this.sendMail(templateCode, templateParamsMap, receiverUserList, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 发送邮件
|
||||
*
|
||||
* @param subject 主题
|
||||
* @param content 内容
|
||||
* @param fileList 文件
|
||||
* @param receiverUserList 接收方
|
||||
* @throws MessagingException
|
||||
*/
|
||||
public void sendMail(String subject, String content, List<File> fileList, List<String> receiverUserList, boolean isHtml) throws MessagingException {
|
||||
|
||||
if (CollectionUtils.isEmpty(receiverUserList)) {
|
||||
throw new RuntimeException("接收方不能为空");
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(content)) {
|
||||
throw new RuntimeException("邮件内容不能为空");
|
||||
}
|
||||
|
||||
if (!systemEnvironment.isProd()) {
|
||||
subject = "(测试)" + subject;
|
||||
}
|
||||
|
||||
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
|
||||
|
||||
//是否为多文件上传
|
||||
boolean multiparty = !CollectionUtils.isEmpty(fileList);
|
||||
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, multiparty);
|
||||
helper.setFrom(clientMail);
|
||||
helper.setTo(receiverUserList.toArray(new String[0]));
|
||||
helper.setSubject(subject);
|
||||
//发送html格式
|
||||
helper.setText(content, isHtml);
|
||||
|
||||
//附件
|
||||
if (multiparty) {
|
||||
for (File file : fileList) {
|
||||
helper.addAttachment(file.getName(), file);
|
||||
}
|
||||
}
|
||||
javaMailSender.send(mimeMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用字符串生成最终内容
|
||||
*/
|
||||
private String stringResolverContent(String stringTemplate, Map<String, Object> templateParamsMap) {
|
||||
StringSubstitutor stringSubstitutor = new StringSubstitutor(templateParamsMap);
|
||||
String contractHtml = stringSubstitutor.replace(stringTemplate);
|
||||
Document doc = Jsoup.parse(contractHtml);
|
||||
doc.outputSettings().syntax(Document.OutputSettings.Syntax.xml);
|
||||
return doc.outerHtml();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 使用 freemarker 生成最终内容
|
||||
*/
|
||||
private String freemarkerResolverContent(String htmlTemplate, Map<String, Object> templateParamsMap) {
|
||||
Configuration configuration = new Configuration(Configuration.VERSION_2_3_23);
|
||||
StringTemplateLoader stringLoader = new StringTemplateLoader();
|
||||
String templateName = UUID.fastUUID().toString(true);
|
||||
stringLoader.putTemplate(templateName, htmlTemplate);
|
||||
configuration.setTemplateLoader(stringLoader);
|
||||
try {
|
||||
Template template = configuration.getTemplate(templateName, "utf-8");
|
||||
Writer out = new StringWriter(2048);
|
||||
template.process(templateParamsMap, out);
|
||||
return out.toString();
|
||||
} catch (Throwable e) {
|
||||
log.error("freemarkerResolverContent error: ", e);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package net.lab1024.sa.base.module.support.mail;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import net.lab1024.sa.base.module.support.mail.domain.MailTemplateEntity;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 邮件模板
|
||||
*
|
||||
* @Author 1024创新实验室-创始人兼主任:卓大
|
||||
* @Date 2024/8/5
|
||||
* @Wechat zhuoda1024
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a> ,Since 2012
|
||||
*/
|
||||
@Mapper
|
||||
@Component
|
||||
public interface MailTemplateDao extends BaseMapper<MailTemplateEntity> {
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package net.lab1024.sa.base.module.support.mail.constant;
|
||||
|
||||
/**
|
||||
* 模版编码
|
||||
*
|
||||
* @Author 1024创新实验室-创始人兼主任:卓大
|
||||
* @Date 2024/8/5
|
||||
* @Wechat zhuoda1024
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a> ,Since 2012
|
||||
*/
|
||||
public enum MailTemplateCodeEnum {
|
||||
|
||||
/**
|
||||
* 登录验证码
|
||||
*/
|
||||
LOGIN_VERIFICATION_CODE
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package net.lab1024.sa.base.module.support.mail.constant;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import net.lab1024.sa.base.common.enumeration.BaseEnum;
|
||||
|
||||
/**
|
||||
* 邮件模板类型
|
||||
*
|
||||
* @Author 1024创新实验室-创始人兼主任:卓大
|
||||
* @Date 2024/8/5
|
||||
* @Wechat zhuoda1024
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a> ,Since 2012
|
||||
*/
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum MailTemplateTypeEnum implements BaseEnum {
|
||||
|
||||
STRING("string", "字符串替代器"),
|
||||
|
||||
FREEMARKER("freemarker", "freemarker模板引擎");
|
||||
|
||||
private String value;
|
||||
|
||||
private String desc;
|
||||
|
||||
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package net.lab1024.sa.base.module.support.mail.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
*
|
||||
* 邮件模板
|
||||
*
|
||||
* @Author 1024创新实验室-创始人兼主任:卓大
|
||||
* @Date 2024/8/5
|
||||
* @Wechat zhuoda1024
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a> ,Since 2012
|
||||
*/
|
||||
@Data
|
||||
@TableName("t_mail_template")
|
||||
public class MailTemplateEntity {
|
||||
|
||||
@TableId(type = IdType.NONE)
|
||||
private String templateCode;
|
||||
|
||||
/**
|
||||
* 主题
|
||||
*/
|
||||
private String templateSubject;
|
||||
|
||||
/**
|
||||
* 模板类型
|
||||
*/
|
||||
private String templateType;
|
||||
|
||||
/**
|
||||
* 模板内容
|
||||
*/
|
||||
private String templateContent;
|
||||
|
||||
/**
|
||||
* 禁用标识
|
||||
*/
|
||||
private Boolean disableFlag;
|
||||
|
||||
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
+6
@@ -22,6 +22,12 @@ public class OperateLogQueryForm extends PageParam {
|
||||
@Schema(description = "用户类型")
|
||||
private Integer operateUserType;
|
||||
|
||||
@Schema(description = "关键字:模块、操作内容")
|
||||
private String keywords;
|
||||
|
||||
@Schema(description = "请求关键字:请求地址、请求方法、请求参数")
|
||||
private String requestKeywords;
|
||||
|
||||
@Schema(description = "开始日期")
|
||||
private String startDate;
|
||||
|
||||
|
||||
-1
@@ -8,7 +8,6 @@ import net.lab1024.sa.base.module.support.reload.dao.ReloadItemDao;
|
||||
import net.lab1024.sa.base.module.support.reload.dao.ReloadResultDao;
|
||||
import net.lab1024.sa.base.module.support.reload.domain.ReloadItemEntity;
|
||||
import net.lab1024.sa.base.module.support.reload.domain.ReloadResultEntity;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package net.lab1024.sa.base.module.support.securityprotect.dao;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import net.lab1024.sa.base.module.support.securityprotect.domain.PasswordLogEntity;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
@Component
|
||||
public interface PasswordLogDao extends BaseMapper<PasswordLogEntity> {
|
||||
|
||||
/**
|
||||
* 查询最后一次修改密码记录
|
||||
*
|
||||
* @param userType
|
||||
* @param userId
|
||||
* @return
|
||||
*/
|
||||
PasswordLogEntity selectLastByUserTypeAndUserId(@Param("userType") Integer userType, @Param("userId") Long userId);
|
||||
|
||||
|
||||
/**
|
||||
* 查询最近几次修改后的密码
|
||||
*
|
||||
* @param userType
|
||||
* @param userId
|
||||
* @return
|
||||
*/
|
||||
List<String> selectOldPassword(@Param("userType") Integer userType, @Param("userId") Long userId, @Param("limit") int limit);
|
||||
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package net.lab1024.sa.base.module.support.securityprotect.domain;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 三级等保相关配置
|
||||
*
|
||||
* @Author 1024创新实验室-创始人兼主任:卓大
|
||||
* @Date 2024/7/30
|
||||
* @Wechat zhuoda1024
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a> ,Since 2012
|
||||
*/
|
||||
|
||||
@Data
|
||||
public class Level3ProtectConfigForm {
|
||||
|
||||
@Schema(description = "连续登录失败次数则锁定")
|
||||
@NotNull(message = "连续登录失败次数则锁定 不能为空")
|
||||
private Integer loginFailMaxTimes;
|
||||
|
||||
@Schema(description = "连续登录失败锁定时间(单位:分钟)")
|
||||
@NotNull(message = "连续登录失败锁定时间(单位:分钟) 不能为空")
|
||||
private Integer loginFailLockMinutes;
|
||||
|
||||
@Schema(description = "最低活跃时间(单位:分钟)")
|
||||
@NotNull(message = "最低活跃时间(单位:分钟) 不能为空")
|
||||
private Integer loginActiveTimeoutMinutes;
|
||||
|
||||
@Schema(description = "开启双因子登录")
|
||||
@NotNull(message = "开启双因子登录 不能为空")
|
||||
private Boolean twoFactorLoginEnabled;
|
||||
|
||||
@Schema(description = "密码复杂度 是否开启,默认:开启")
|
||||
@NotNull(message = "密码复杂度 是否开启 不能为空")
|
||||
private Boolean passwordComplexityEnabled;
|
||||
|
||||
@Schema(description = "定期修改密码时间间隔(默认:月)")
|
||||
@NotNull(message = "定期修改密码时间间隔(默认:月) 不能为空")
|
||||
private Integer regularChangePasswordMonths;
|
||||
|
||||
@Schema(description = "定期修改密码不允许重复次数,默认:3次以内密码不能相同(默认:次)")
|
||||
@NotNull(message = "定期修改密码不允许重复次数 不能为空")
|
||||
private Integer regularChangePasswordNotAllowRepeatTimes;
|
||||
|
||||
@Schema(description = "文件检测,默认:不开启")
|
||||
@NotNull(message = "文件检测 是否开启 不能为空")
|
||||
private Boolean fileDetectFlag;
|
||||
|
||||
@Schema(description = "文件大小限制,单位 mb ,(默认:50 mb)")
|
||||
@NotNull(message = "文件大小限制 不能为空")
|
||||
private Long maxUploadFileSizeMb;
|
||||
|
||||
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package net.lab1024.sa.base.module.support.securityprotect.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author yandy
|
||||
* @description:
|
||||
* @date 2024/7/15 1:39 下午
|
||||
*/
|
||||
@Data
|
||||
@TableName("t_password_log")
|
||||
public class PasswordLogEntity {
|
||||
|
||||
/**
|
||||
* 主键id
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
private Integer userType;
|
||||
|
||||
private Long userId;
|
||||
|
||||
private String oldPassword;
|
||||
|
||||
private String newPassword;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
package net.lab1024.sa.base.module.support.securityprotect.service;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.lab1024.sa.base.common.domain.ResponseDTO;
|
||||
import net.lab1024.sa.base.module.support.config.ConfigKeyEnum;
|
||||
import net.lab1024.sa.base.module.support.config.ConfigService;
|
||||
import net.lab1024.sa.base.module.support.securityprotect.domain.Level3ProtectConfigForm;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 三级等保配置
|
||||
*
|
||||
* @Author 1024创新实验室-创始人兼主任:卓大
|
||||
* @Date 2024/7/30
|
||||
* @Wechat zhuoda1024
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a> ,Since 2012
|
||||
*/
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class Level3ProtectConfigService {
|
||||
|
||||
/**
|
||||
* 开启双因子登录,默认:开启
|
||||
*/
|
||||
private boolean twoFactorLoginEnabled = false;
|
||||
|
||||
/**
|
||||
* 连续登录失败次数则锁定,-1表示不受限制,可以一直尝试登录
|
||||
*/
|
||||
private int loginFailMaxTimes = -1;
|
||||
|
||||
/**
|
||||
* 连续登录失败锁定时间(单位:秒),-1表示不锁定,建议锁定30分钟
|
||||
*/
|
||||
private int loginFailLockSeconds = 1800;
|
||||
|
||||
/**
|
||||
* 最低活跃时间(单位:秒),超过此时间没有操作系统就会被冻结,默认-1 代表不限制,永不冻结; 默认 30分钟
|
||||
*/
|
||||
private int loginActiveTimeoutSeconds = 1800;
|
||||
|
||||
/**
|
||||
* 密码复杂度 是否开启,默认:开启
|
||||
*/
|
||||
private boolean passwordComplexityEnabled = true;
|
||||
|
||||
/**
|
||||
* 定期修改密码时间间隔(默认:天),默认:建议90天更换密码
|
||||
*/
|
||||
private int regularChangePasswordDays = 90;
|
||||
|
||||
/**
|
||||
* 定期修改密码不允许相同次数,默认:3次以内密码不能相同
|
||||
*/
|
||||
private int regularChangePasswordNotAllowRepeatTimes = 3;
|
||||
|
||||
/**
|
||||
* 文件大小限制,单位 mb ,(默认:50 mb)
|
||||
*/
|
||||
private long maxUploadFileSizeMb = 50;
|
||||
|
||||
/**
|
||||
* 文件检测,默认:不开启
|
||||
*/
|
||||
private boolean fileDetectFlag = false;
|
||||
|
||||
|
||||
@Resource
|
||||
private ConfigService configService;
|
||||
|
||||
/**
|
||||
* 文件检测,默认:不开启
|
||||
*/
|
||||
public boolean isFileDetectFlag() {
|
||||
return fileDetectFlag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件大小限制,单位 mb ,(默认:50 mb)
|
||||
*/
|
||||
public long getMaxUploadFileSizeMb() {
|
||||
return maxUploadFileSizeMb;
|
||||
}
|
||||
|
||||
/**
|
||||
* 连续登录失败次数则锁定,-1表示不受限制,可以一直尝试登录
|
||||
*/
|
||||
public int getLoginFailMaxTimes() {
|
||||
return loginFailMaxTimes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 连续登录失败锁定时间(单位:秒),-1表示不锁定,建议锁定30分钟
|
||||
*/
|
||||
public int getLoginFailLockSeconds() {
|
||||
return loginFailLockSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 最低活跃时间(单位:秒),超过此时间没有操作系统就会被冻结,默认-1 代表不限制,永不冻结; 默认 30分钟
|
||||
*/
|
||||
public int getLoginActiveTimeoutSeconds() {
|
||||
return loginActiveTimeoutSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 定期修改密码时间间隔(默认:天),默认:建议90天更换密码
|
||||
*/
|
||||
public int getRegularChangePasswordDays() {
|
||||
return regularChangePasswordDays;
|
||||
}
|
||||
|
||||
/**
|
||||
* 开启双因子登录,默认:开启
|
||||
*/
|
||||
public boolean isTwoFactorLoginEnabled() {
|
||||
return twoFactorLoginEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* 密码复杂度 是否开启,默认:开启
|
||||
*/
|
||||
public boolean isPasswordComplexityEnabled() {
|
||||
return passwordComplexityEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* 定期修改密码不允许相同次数,默认:3次以内密码不能相同
|
||||
*/
|
||||
public int getRegularChangePasswordNotAllowRepeatTimes() {
|
||||
return regularChangePasswordNotAllowRepeatTimes;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
void init() {
|
||||
String configValue = configService.getConfigValue(ConfigKeyEnum.LEVEL3_PROTECT_CONFIG);
|
||||
if (StrUtil.isEmpty(configValue)) {
|
||||
throw new ExceptionInInitializerError("t_config 表 三级等保配置为空,请进行配置!");
|
||||
}
|
||||
Level3ProtectConfigForm level3ProtectConfigForm = JSON.parseObject(configValue, Level3ProtectConfigForm.class);
|
||||
setProp(level3ProtectConfigForm);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置属性
|
||||
*/
|
||||
private void setProp(Level3ProtectConfigForm configForm) {
|
||||
|
||||
if (configForm.getFileDetectFlag() != null) {
|
||||
this.fileDetectFlag = configForm.getFileDetectFlag();
|
||||
}
|
||||
|
||||
if (configForm.getMaxUploadFileSizeMb() != null) {
|
||||
this.maxUploadFileSizeMb = configForm.getMaxUploadFileSizeMb();
|
||||
}
|
||||
|
||||
if (configForm.getLoginFailLockMinutes() != null) {
|
||||
this.loginFailLockSeconds = configForm.getLoginFailLockMinutes() * 60;
|
||||
}
|
||||
|
||||
if (configForm.getLoginActiveTimeoutMinutes() != null) {
|
||||
this.loginActiveTimeoutSeconds = configForm.getLoginActiveTimeoutMinutes() * 60;
|
||||
}
|
||||
|
||||
if (configForm.getPasswordComplexityEnabled() != null) {
|
||||
this.passwordComplexityEnabled = configForm.getPasswordComplexityEnabled();
|
||||
}
|
||||
|
||||
if (configForm.getRegularChangePasswordMonths() != null) {
|
||||
this.regularChangePasswordDays = configForm.getRegularChangePasswordMonths() * 30;
|
||||
}
|
||||
|
||||
if (configForm.getTwoFactorLoginEnabled() != null) {
|
||||
this.twoFactorLoginEnabled = configForm.getTwoFactorLoginEnabled();
|
||||
}
|
||||
|
||||
if (configForm.getRegularChangePasswordNotAllowRepeatTimes() != null) {
|
||||
this.regularChangePasswordNotAllowRepeatTimes = configForm.getRegularChangePasswordNotAllowRepeatTimes();
|
||||
}
|
||||
|
||||
// 设置 最低活跃时间(单位:秒)
|
||||
if (this.loginActiveTimeoutSeconds > 0) {
|
||||
StpUtil.getStpLogic().getConfigOrGlobal().setActiveTimeout(getLoginActiveTimeoutSeconds());
|
||||
} else {
|
||||
StpUtil.getStpLogic().getConfigOrGlobal().setActiveTimeout(-1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新三级等保配置
|
||||
*/
|
||||
public ResponseDTO<String> updateLevel3Config(Level3ProtectConfigForm configForm) {
|
||||
// 设置属性
|
||||
setProp(configForm);
|
||||
// 保存数据库
|
||||
String configFormJsonString = JSON.toJSONString(configForm, true);
|
||||
return configService.updateValueByKey(ConfigKeyEnum.LEVEL3_PROTECT_CONFIG, configFormJsonString);
|
||||
}
|
||||
}
|
||||
-99
@@ -1,99 +0,0 @@
|
||||
package net.lab1024.sa.base.module.support.securityprotect.service;
|
||||
|
||||
import net.lab1024.sa.base.common.domain.ResponseDTO;
|
||||
import net.lab1024.sa.base.common.util.SmartStringUtil;
|
||||
import net.lab1024.sa.base.module.support.apiencrypt.service.ApiEncryptService;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 三级等保 密码 相关
|
||||
*
|
||||
* @Author 1024创新实验室-主任:卓大
|
||||
* @Date 2023/10/11 19:25:59
|
||||
* @Wechat zhuoda1024
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a>,Since 2012
|
||||
*/
|
||||
|
||||
@Service
|
||||
public class ProtectPasswordService {
|
||||
|
||||
/**
|
||||
* 密码长度8-20位且包含大写字母、小写字母、数字三种
|
||||
*/
|
||||
public static final String PASSWORD_PATTERN = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d]{8,20}$";
|
||||
|
||||
/**
|
||||
* 密码长度8-20位且包含大写字母、小写字母、数字三种
|
||||
*/
|
||||
public static final String PASSWORD_FORMAT_MSG = "密码必须为长度8-20位且包含大写字母、小写字母、数字三种";
|
||||
|
||||
|
||||
private static final int PASSWORD_LENGTH = 8;
|
||||
|
||||
|
||||
/**
|
||||
* 密码复杂度开启, 默认为true 开启,false 不开启
|
||||
*/
|
||||
@Value("${classified-protect.password-complexity-enabled}")
|
||||
private Boolean passwordComplexityEnabled;
|
||||
|
||||
|
||||
@Resource
|
||||
private ApiEncryptService apiEncryptService;
|
||||
|
||||
/**
|
||||
* 校验密码复杂度
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public ResponseDTO<String> validatePassComplexity(String password) {
|
||||
|
||||
// 无需校验
|
||||
if (!passwordComplexityEnabled) {
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
if (SmartStringUtil.isEmpty(password)) {
|
||||
return ResponseDTO.userErrorParam(PASSWORD_FORMAT_MSG);
|
||||
}
|
||||
|
||||
if (!password.matches(PASSWORD_PATTERN)) {
|
||||
return ResponseDTO.userErrorParam(PASSWORD_FORMAT_MSG);
|
||||
}
|
||||
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机生成密码
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String randomPassword() {
|
||||
// 未开启密码复杂度,则由8为数字构成
|
||||
if (passwordComplexityEnabled) {
|
||||
return RandomStringUtils.randomNumeric(PASSWORD_LENGTH);
|
||||
} else {
|
||||
// 3位大写字母,2位数字,3位小写字母
|
||||
return RandomStringUtils.randomAlphabetic(3).toUpperCase() + RandomStringUtils.randomNumeric(2) + RandomStringUtils.randomAlphabetic(3).toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解密 SM4 or AES 加密过的密码
|
||||
*
|
||||
* @param encryptedPassword
|
||||
* @return
|
||||
*/
|
||||
public String decryptPassword(String encryptedPassword) {
|
||||
return apiEncryptService.decrypt(encryptedPassword);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package net.lab1024.sa.base.module.support.securityprotect.service;
|
||||
|
||||
import net.lab1024.sa.base.common.domain.ResponseDTO;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* 三级等保 文件上传 相关
|
||||
*
|
||||
* @Author 1024创新实验室-主任:卓大
|
||||
* @Date 2024/08/22 19:25:59
|
||||
* @Wechat zhuoda1024
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a>,Since 2012
|
||||
*/
|
||||
|
||||
@Service
|
||||
public class SecurityFileService {
|
||||
|
||||
@Resource
|
||||
private Level3ProtectConfigService level3ProtectConfigService;
|
||||
|
||||
|
||||
/**
|
||||
* 检测文件安全类型
|
||||
*/
|
||||
public ResponseDTO<String> checkFile(MultipartFile file) {
|
||||
|
||||
// 检验文件大小
|
||||
if (level3ProtectConfigService.getMaxUploadFileSizeMb() > 0) {
|
||||
long maxSize = level3ProtectConfigService.getMaxUploadFileSizeMb() * 1024 * 1024;
|
||||
if (file.getSize() > maxSize) {
|
||||
return ResponseDTO.userErrorParam("上传文件最大为:" + level3ProtectConfigService.getMaxUploadFileSizeMb() + " mb");
|
||||
}
|
||||
}
|
||||
|
||||
// 文件类型安全检测
|
||||
if (!level3ProtectConfigService.isFileDetectFlag()) {
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
// 检测文件类型
|
||||
// .....
|
||||
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
}
|
||||
;
|
||||
+38
-46
@@ -12,7 +12,6 @@ import net.lab1024.sa.base.module.support.securityprotect.domain.LoginFailEntity
|
||||
import net.lab1024.sa.base.module.support.securityprotect.domain.LoginFailQueryForm;
|
||||
import net.lab1024.sa.base.module.support.securityprotect.domain.LoginFailVO;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
@@ -26,27 +25,18 @@ import java.util.List;
|
||||
* @Date 2023/10/11 19:25:59
|
||||
* @Wechat zhuoda1024
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a>,Since 2012
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a>,Since 2012
|
||||
*/
|
||||
|
||||
@Service
|
||||
public class ProtectLoginService {
|
||||
public class SecurityLoginService {
|
||||
|
||||
private static final String LOGIN_LOCK_MSG = "您已连续登录失败%s次,账号锁定%s分钟,解锁时间为:%s,请您耐心等待!";
|
||||
|
||||
private static final String LOGIN_FAIL_MSG = "登录名或密码错误!连续登录失败%s次,账号将锁定%s分钟!您还可以再尝试%s次!";
|
||||
|
||||
/**
|
||||
* 连续登录失败次数则锁定,-1表示不受限制,可以一直登录
|
||||
*/
|
||||
@Value("${classified-protect.login-max-fail-times}")
|
||||
private Integer loginMaxFailTimes;
|
||||
|
||||
/**
|
||||
* 连续登录失败锁定时间(单位:秒),-1表示不锁定
|
||||
*/
|
||||
@Value("${classified-protect.login-fail-locked-seconds}")
|
||||
private Integer loginFailLockedSeconds;
|
||||
@Resource
|
||||
private Level3ProtectConfigService level3ProtectConfigService;
|
||||
|
||||
@Resource
|
||||
private LoginFailDao loginFailDao;
|
||||
@@ -61,8 +51,8 @@ public class ProtectLoginService {
|
||||
*/
|
||||
public ResponseDTO<LoginFailEntity> checkLogin(Long userId, UserTypeEnum userType) {
|
||||
|
||||
// 无需校验
|
||||
if (loginMaxFailTimes < 1) {
|
||||
// 若登录最大失败次数小于1,无需校验
|
||||
if (level3ProtectConfigService.getLoginFailMaxTimes() < 1) {
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
@@ -72,19 +62,24 @@ public class ProtectLoginService {
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
// 校验次数
|
||||
if (loginFailEntity.getLoginFailCount() < loginMaxFailTimes) {
|
||||
// 校验登录失败次数
|
||||
if (loginFailEntity.getLoginFailCount() < level3ProtectConfigService.getLoginFailMaxTimes()) {
|
||||
return ResponseDTO.ok(loginFailEntity);
|
||||
}
|
||||
|
||||
// 校验是否锁定
|
||||
if (loginFailEntity.getLoginLockBeginTime() == null) {
|
||||
return ResponseDTO.ok(loginFailEntity);
|
||||
}
|
||||
|
||||
// 校验锁定时长
|
||||
if(loginFailEntity.getLoginLockBeginTime().plusSeconds(loginFailLockedSeconds).isBefore(LocalDateTime.now())){
|
||||
if (loginFailEntity.getLoginLockBeginTime().plusSeconds(level3ProtectConfigService.getLoginFailLockSeconds()).isBefore(LocalDateTime.now())) {
|
||||
// 过了锁定时间
|
||||
return ResponseDTO.ok(loginFailEntity);
|
||||
}
|
||||
|
||||
LocalDateTime unlockTime = loginFailEntity.getLoginLockBeginTime().plusSeconds(loginFailLockedSeconds);
|
||||
return ResponseDTO.error(UserErrorCode.LOGIN_FAIL_LOCK, String.format(LOGIN_LOCK_MSG, loginFailEntity.getLoginFailCount(), loginFailLockedSeconds / 60, LocalDateTimeUtil.formatNormal(unlockTime)));
|
||||
LocalDateTime unlockTime = loginFailEntity.getLoginLockBeginTime().plusSeconds(level3ProtectConfigService.getLoginFailLockSeconds());
|
||||
return ResponseDTO.error(UserErrorCode.LOGIN_FAIL_LOCK, String.format(LOGIN_LOCK_MSG, loginFailEntity.getLoginFailCount(), level3ProtectConfigService.getLoginFailLockSeconds() / 60, LocalDateTimeUtil.formatNormal(unlockTime)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,43 +91,40 @@ public class ProtectLoginService {
|
||||
*/
|
||||
public String recordLoginFail(Long userId, UserTypeEnum userType, String loginName, LoginFailEntity loginFailEntity) {
|
||||
|
||||
// 无需校验
|
||||
if (loginMaxFailTimes < 1) {
|
||||
// 若登录最大失败次数小于1,无需记录
|
||||
if (level3ProtectConfigService.getLoginFailMaxTimes() < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 登录失败
|
||||
int loginFailCount = loginFailEntity == null ? 1 : loginFailEntity.getLoginFailCount() + 1;
|
||||
boolean lockFlag = loginFailCount >= level3ProtectConfigService.getLoginFailMaxTimes();
|
||||
LocalDateTime lockBeginTime = lockFlag ? LocalDateTime.now() : null;
|
||||
|
||||
if (loginFailEntity == null) {
|
||||
loginFailEntity = LoginFailEntity.builder()
|
||||
.userId(userId)
|
||||
.userType(userType.getValue())
|
||||
.loginName(loginName)
|
||||
.loginFailCount(1)
|
||||
.lockFlag(false)
|
||||
.loginLockBeginTime(null).build();
|
||||
.loginFailCount(loginFailCount)
|
||||
.lockFlag(lockFlag)
|
||||
.loginLockBeginTime(lockBeginTime)
|
||||
.build();
|
||||
loginFailDao.insert(loginFailEntity);
|
||||
} else {
|
||||
|
||||
// 如果是已经锁定状态,则重新计算
|
||||
if(loginFailEntity.getLockFlag()){
|
||||
loginFailEntity.setLockFlag(false);
|
||||
loginFailEntity.setLoginFailCount(1);
|
||||
loginFailEntity.setLoginLockBeginTime(null);
|
||||
}else{
|
||||
loginFailEntity.setLoginLockBeginTime(LocalDateTime.now());
|
||||
loginFailEntity.setLoginFailCount(loginFailEntity.getLoginFailCount() + 1);
|
||||
loginFailEntity.setLockFlag(loginFailEntity.getLoginFailCount() >= loginMaxFailTimes);
|
||||
}
|
||||
|
||||
loginFailEntity.setLoginLockBeginTime(lockBeginTime);
|
||||
loginFailEntity.setLoginFailCount(loginFailCount);
|
||||
loginFailEntity.setLockFlag(lockFlag);
|
||||
loginFailEntity.setLoginName(loginName);
|
||||
loginFailDao.updateById(loginFailEntity);
|
||||
}
|
||||
|
||||
// 提示信息
|
||||
if (loginFailEntity.getLoginFailCount() >= loginMaxFailTimes) {
|
||||
LocalDateTime unlockTime = loginFailEntity.getLoginLockBeginTime().plusSeconds(loginFailLockedSeconds);
|
||||
return String.format(LOGIN_LOCK_MSG, loginFailEntity.getLoginFailCount(), loginFailLockedSeconds / 60, LocalDateTimeUtil.formatNormal(unlockTime));
|
||||
if (lockFlag) {
|
||||
LocalDateTime unlockTime = loginFailEntity.getLoginLockBeginTime().plusSeconds(level3ProtectConfigService.getLoginFailLockSeconds());
|
||||
return String.format(LOGIN_LOCK_MSG, loginFailEntity.getLoginFailCount(), level3ProtectConfigService.getLoginFailLockSeconds() / 60, LocalDateTimeUtil.formatNormal(unlockTime));
|
||||
} else {
|
||||
return String.format(LOGIN_FAIL_MSG, loginMaxFailTimes, loginFailLockedSeconds / 60, loginMaxFailTimes - loginFailEntity.getLoginFailCount());
|
||||
return String.format(LOGIN_FAIL_MSG, level3ProtectConfigService.getLoginFailMaxTimes(), level3ProtectConfigService.getLoginFailLockSeconds() / 60, level3ProtectConfigService.getLoginFailMaxTimes() - loginFailEntity.getLoginFailCount());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,8 +135,9 @@ public class ProtectLoginService {
|
||||
* @param userType
|
||||
*/
|
||||
public void removeLoginFail(Long userId, UserTypeEnum userType) {
|
||||
// 无需校验
|
||||
if (loginMaxFailTimes < 1) {
|
||||
|
||||
// 若登录最大失败次数小于1,无需校验
|
||||
if (level3ProtectConfigService.getLoginFailMaxTimes() < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -160,8 +153,7 @@ public class ProtectLoginService {
|
||||
public PageResult<LoginFailVO> queryPage(LoginFailQueryForm queryForm) {
|
||||
Page<?> page = SmartPageUtil.convert2PageQuery(queryForm);
|
||||
List<LoginFailVO> list = loginFailDao.queryPage(page, queryForm);
|
||||
PageResult<LoginFailVO> pageResult = SmartPageUtil.convert2PageResult(page, list);
|
||||
return pageResult;
|
||||
return SmartPageUtil.convert2PageResult(page, list);
|
||||
}
|
||||
|
||||
/**
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
package net.lab1024.sa.base.module.support.securityprotect.service;
|
||||
|
||||
import net.lab1024.sa.base.common.domain.RequestUser;
|
||||
import net.lab1024.sa.base.common.domain.ResponseDTO;
|
||||
import net.lab1024.sa.base.common.util.SmartStringUtil;
|
||||
import net.lab1024.sa.base.module.support.securityprotect.dao.PasswordLogDao;
|
||||
import net.lab1024.sa.base.module.support.securityprotect.domain.PasswordLogEntity;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* 三级等保 密码 相关
|
||||
*
|
||||
* @Author 1024创新实验室-主任:卓大
|
||||
* @Date 2023/10/11 19:25:59
|
||||
* @Wechat zhuoda1024
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright <a href="https://1024lab.net">1024创新实验室</a>,Since 2012
|
||||
*/
|
||||
|
||||
@Service
|
||||
public class SecurityPasswordService {
|
||||
|
||||
/**
|
||||
* 密码长度8-20位且包含大小写字母、数字、特殊符号三种及以上组合
|
||||
*/
|
||||
public static final String PASSWORD_PATTERN = "^(?![a-zA-Z]+$)(?![A-Z0-9]+$)(?![A-Z\\W_!@#$%^&*`~()-+=]+$)(?![a-z0-9]+$)(?![a-z\\W_!@#$%^&*`~()-+=]+$)(?![0-9\\W_!@#$%^&*`~()-+=]+$)[a-zA-Z0-9\\W_!@#$%^&*`~()-+=]*$";
|
||||
|
||||
public static final String PASSWORD_FORMAT_MSG = "密码必须为长度8-20位且必须包含大小写字母、数字、特殊符号(如:@#$%^&*()_+-=)等三种字符";
|
||||
|
||||
|
||||
private static final int PASSWORD_LENGTH = 8;
|
||||
|
||||
private static final String PASSWORD_SALT_FORMAT = "smart_%s_admin_$^&*";
|
||||
|
||||
|
||||
@Resource
|
||||
private PasswordLogDao passwordLogDao;
|
||||
|
||||
@Resource
|
||||
private Level3ProtectConfigService level3ProtectConfigService;
|
||||
|
||||
/**
|
||||
* 校验密码复杂度
|
||||
*/
|
||||
public ResponseDTO<String> validatePasswordComplexity(String password) {
|
||||
|
||||
if (SmartStringUtil.isEmpty(password)) {
|
||||
return ResponseDTO.userErrorParam(PASSWORD_FORMAT_MSG);
|
||||
}
|
||||
|
||||
// 密码长度必须大于等于8位
|
||||
if (password.length() < PASSWORD_LENGTH) {
|
||||
return ResponseDTO.userErrorParam(PASSWORD_FORMAT_MSG);
|
||||
}
|
||||
|
||||
// 无需校验 密码复杂度
|
||||
if (!level3ProtectConfigService.isPasswordComplexityEnabled()) {
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
if (!password.matches(PASSWORD_PATTERN)) {
|
||||
return ResponseDTO.userErrorParam(PASSWORD_FORMAT_MSG);
|
||||
}
|
||||
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验密码重复次数
|
||||
*/
|
||||
public ResponseDTO<String> validatePasswordRepeatTimes(RequestUser requestUser, String newPassword) {
|
||||
|
||||
// 密码重复次数小于1 无需校验
|
||||
if (level3ProtectConfigService.getRegularChangePasswordNotAllowRepeatTimes() < 1) {
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
// 检查最近几次是否有重复密码
|
||||
List<String> oldPasswords = passwordLogDao.selectOldPassword(requestUser.getUserType().getValue(), requestUser.getUserId(), level3ProtectConfigService.getRegularChangePasswordNotAllowRepeatTimes());
|
||||
if (oldPasswords != null && oldPasswords.contains(getEncryptPwd(newPassword))) {
|
||||
return ResponseDTO.userErrorParam(String.format("与前%s个历史密码重复,请换个密码!", level3ProtectConfigService.getRegularChangePasswordNotAllowRepeatTimes()));
|
||||
}
|
||||
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机生成密码
|
||||
*/
|
||||
public String randomPassword() {
|
||||
// 未开启密码复杂度,则由8为数字构成
|
||||
if (!level3ProtectConfigService.isPasswordComplexityEnabled()) {
|
||||
return RandomStringUtils.randomNumeric(PASSWORD_LENGTH);
|
||||
}
|
||||
|
||||
// 3位大写字母,2位数字,2位小写字母 + 1位特殊符号
|
||||
return RandomStringUtils.randomAlphabetic(3).toUpperCase()
|
||||
+ RandomStringUtils.randomNumeric(2)
|
||||
+ RandomStringUtils.randomAlphabetic(2).toLowerCase()
|
||||
+ (ThreadLocalRandom.current().nextBoolean() ? "#" : "@");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 保存修改密码
|
||||
*/
|
||||
public void saveUserChangePasswordLog(RequestUser requestUser, String newPassword, String oldPassword) {
|
||||
|
||||
PasswordLogEntity passwordLogEntity = new PasswordLogEntity();
|
||||
passwordLogEntity.setNewPassword(newPassword);
|
||||
passwordLogEntity.setOldPassword(oldPassword);
|
||||
passwordLogEntity.setUserId(requestUser.getUserId());
|
||||
passwordLogEntity.setUserType(requestUser.getUserType().getValue());
|
||||
passwordLogDao.insert(passwordLogEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否需要修改密码
|
||||
*/
|
||||
public boolean checkNeedChangePassword(Integer userType, Long userId) {
|
||||
|
||||
if (level3ProtectConfigService.getRegularChangePasswordDays() < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PasswordLogEntity passwordLogEntity = passwordLogDao.selectLastByUserTypeAndUserId(userType, userId);
|
||||
if (passwordLogEntity == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LocalDateTime nextUpdateTime = passwordLogEntity.getCreateTime().plusDays(level3ProtectConfigService.getRegularChangePasswordDays());
|
||||
return nextUpdateTime.isBefore(LocalDateTime.now());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 加密后 的密码
|
||||
*/
|
||||
public static String getEncryptPwd(String password) {
|
||||
return DigestUtils.md5Hex(String.format(PASSWORD_SALT_FORMAT, password));
|
||||
}
|
||||
|
||||
}
|
||||
+1
@@ -2,6 +2,7 @@ package ${packageName};
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import net.lab1024.sa.base.common.enumeration.BaseEnum;
|
||||
|
||||
/**
|
||||
* ${enumDesc}
|
||||
|
||||
+7
-1
@@ -3,6 +3,7 @@ package ${packageName};
|
||||
#foreach ($importClass in $importPackageList)
|
||||
$importClass
|
||||
#end
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import net.lab1024.sa.base.common.domain.ResponseDTO;
|
||||
import net.lab1024.sa.base.common.domain.PageResult;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@@ -23,7 +24,7 @@ import javax.validation.Valid;
|
||||
*/
|
||||
|
||||
@RestController
|
||||
@Tag(name = "")
|
||||
@Tag(name = "${basic.description}")
|
||||
public class ${name.upperCamel}Controller {
|
||||
|
||||
@Resource
|
||||
@@ -31,6 +32,7 @@ public class ${name.upperCamel}Controller {
|
||||
|
||||
@Operation(summary = "分页查询 @author ${basic.backendAuthor}")
|
||||
@PostMapping("/${name.lowerCamel}/queryPage")
|
||||
@SaCheckPermission("${name.lowerCamel}:query")
|
||||
public ResponseDTO<PageResult<${name.upperCamel}VO>> queryPage(@RequestBody @Valid ${name.upperCamel}QueryForm queryForm) {
|
||||
return ResponseDTO.ok(${name.lowerCamel}Service.queryPage(queryForm));
|
||||
}
|
||||
@@ -38,12 +40,14 @@ public class ${name.upperCamel}Controller {
|
||||
#if($insertAndUpdate.isSupportInsertAndUpdate)
|
||||
@Operation(summary = "添加 @author ${basic.backendAuthor}")
|
||||
@PostMapping("/${name.lowerCamel}/add")
|
||||
@SaCheckPermission("${name.lowerCamel}:add")
|
||||
public ResponseDTO<String> add(@RequestBody @Valid ${name.upperCamel}AddForm addForm) {
|
||||
return ${name.lowerCamel}Service.add(addForm);
|
||||
}
|
||||
|
||||
@Operation(summary = "更新 @author ${basic.backendAuthor}")
|
||||
@PostMapping("/${name.lowerCamel}/update")
|
||||
@SaCheckPermission("${name.lowerCamel}:update")
|
||||
public ResponseDTO<String> update(@RequestBody @Valid ${name.upperCamel}UpdateForm updateForm) {
|
||||
return ${name.lowerCamel}Service.update(updateForm);
|
||||
}
|
||||
@@ -53,6 +57,7 @@ public class ${name.upperCamel}Controller {
|
||||
#if($deleteInfo.deleteEnum == "Batch" || $deleteInfo.deleteEnum == "SingleAndBatch")
|
||||
@Operation(summary = "批量删除 @author ${basic.backendAuthor}")
|
||||
@PostMapping("/${name.lowerCamel}/batchDelete")
|
||||
@SaCheckPermission("${name.lowerCamel}:delete")
|
||||
public ResponseDTO<String> batchDelete(@RequestBody ValidateList<${primaryKeyJavaType}> idList) {
|
||||
return ${name.lowerCamel}Service.batchDelete(idList);
|
||||
}
|
||||
@@ -61,6 +66,7 @@ public class ${name.upperCamel}Controller {
|
||||
#if($deleteInfo.deleteEnum == "Single" || $deleteInfo.deleteEnum == "SingleAndBatch")
|
||||
@Operation(summary = "单个删除 @author ${basic.backendAuthor}")
|
||||
@GetMapping("/${name.lowerCamel}/delete/{${primaryKeyFieldName}}")
|
||||
@SaCheckPermission("${name.lowerCamel}:delete")
|
||||
public ResponseDTO<String> batchDelete(@PathVariable ${primaryKeyJavaType} ${primaryKeyFieldName}) {
|
||||
return ${name.lowerCamel}Service.delete(${primaryKeyFieldName});
|
||||
}
|
||||
|
||||
+4
-3
@@ -38,14 +38,15 @@ public interface ${name.upperCamel}Dao extends BaseMapper<${name.upperCamel}Enti
|
||||
* 更新删除状态
|
||||
*/
|
||||
long updateDeleted(@Param("${primaryKeyFieldName}")${primaryKeyJavaType} ${primaryKeyFieldName},@Param("${deletedFlag}")boolean deletedFlag);
|
||||
|
||||
#end
|
||||
#if($deleteInfo.deleteEnum == "Batch" || $deleteInfo.deleteEnum == "SingleAndBatch")
|
||||
/**
|
||||
* 批量更新删除状态
|
||||
*/
|
||||
void batchUpdateDeleted(@Param("idList")List<${primaryKeyJavaType}> idList,@Param("${deletedFlag}")boolean deletedFlag);
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
}
|
||||
|
||||
+11
-5
@@ -20,13 +20,19 @@ public class ${name.upperCamel}Entity {
|
||||
/**
|
||||
* $field.label
|
||||
*/
|
||||
#if($field.primaryKeyFlag && $field.autoIncreaseFlag)
|
||||
#if($field.primaryKeyFlag && $field.autoIncreaseFlag)
|
||||
@TableId(type = IdType.AUTO)
|
||||
#end
|
||||
#if($field.primaryKeyFlag && !$field.autoIncreaseFlag)
|
||||
#end
|
||||
#if($field.primaryKeyFlag && !$field.autoIncreaseFlag)
|
||||
@TableId
|
||||
#end
|
||||
#end
|
||||
#if($field.columnName == "create_time")
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
#end
|
||||
#if($field.columnName == "update_time")
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
#end
|
||||
private $field.javaType $field.fieldName;
|
||||
#end
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -14,7 +14,8 @@ $importClass
|
||||
*/
|
||||
|
||||
@Data
|
||||
public class ${name.upperCamel}QueryForm extends PageParam{
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class ${name.upperCamel}QueryForm extends PageParam {
|
||||
#foreach ($field in $fields)
|
||||
|
||||
#if($field.isEnum)
|
||||
@@ -35,4 +36,4 @@ public class ${name.upperCamel}QueryForm extends PageParam{
|
||||
#end
|
||||
#end
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+2
-8
@@ -17,14 +17,8 @@ public class ${name.upperCamel}VO {
|
||||
|
||||
#foreach ($field in $fields)
|
||||
|
||||
#if($field.isEnum)
|
||||
${field.apiModelProperty}
|
||||
${field.apiModelProperty}$!{field.notEmpty}$!{field.dict}$!{field.file}
|
||||
private $field.javaType $field.fieldName;
|
||||
#end
|
||||
#if(!$field.isEnum)
|
||||
${field.apiModelProperty}$!{field.dict}$!{field.file}
|
||||
private $field.javaType $field.fieldName;
|
||||
#end
|
||||
#end
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+19
-17
@@ -2,10 +2,17 @@
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="${daoClassName}">
|
||||
|
||||
<!-- 查询结果列 -->
|
||||
<sql id="base_columns">
|
||||
#foreach ($field in $fields)
|
||||
${tableName}.${field.columnName}#if($foreach.hasNext),#end
|
||||
#end
|
||||
</sql>
|
||||
|
||||
<!-- 分页查询 -->
|
||||
<select id="queryPage" resultType="${basic.javaPackageName}.domain.vo.${name.upperCamel}VO">
|
||||
SELECT
|
||||
*
|
||||
<include refid="base_columns"/>
|
||||
FROM ${tableName}
|
||||
#if($queryFields.size() > 0)
|
||||
<where>
|
||||
@@ -16,22 +23,27 @@
|
||||
${queryField.likeStr}
|
||||
</if>
|
||||
#end
|
||||
#if(${queryField.queryTypeEnum} == "Equal" || ${queryField.queryTypeEnum} == "Enum" || ${queryField.queryTypeEnum} == "Dict")
|
||||
#if(${queryField.queryTypeEnum} == "Dict")
|
||||
<if test="queryForm.${queryField.fieldName} != null and queryForm.${queryField.fieldName} != ''">
|
||||
${queryField.likeStr}
|
||||
</if>
|
||||
#end
|
||||
#if(${queryField.queryTypeEnum} == "Equal" || ${queryField.queryTypeEnum} == "Enum")
|
||||
<if test="queryForm.${queryField.fieldName} != null">
|
||||
AND ${tableName}.${queryField.columnName} = #{queryForm.${queryField.fieldName}}
|
||||
</if>
|
||||
#end
|
||||
#if(${queryField.queryTypeEnum} == "Date")
|
||||
<if test="queryForm.${queryField.fieldName} != null">
|
||||
AND DATE_FORMAT(${tableName}.${queryField.columnName}, '%Y-%m-%d') = #{queryForm.${queryField.fieldName}}
|
||||
AND ${tableName}.${queryField.columnName} = #{queryForm.${queryField.fieldName}}
|
||||
</if>
|
||||
#end
|
||||
#if(${queryField.queryTypeEnum} == "DateRange")
|
||||
<if test="queryForm.${queryField.fieldName}Begin != null">
|
||||
AND DATE_FORMAT(${tableName}.${queryField.columnName}, '%Y-%m-%d') >= #{queryForm.${queryField.fieldName}Begin}
|
||||
AND ${tableName}.${queryField.columnName} >= #{queryForm.${queryField.fieldName}Begin}
|
||||
</if>
|
||||
<if test="queryForm.${queryField.fieldName}End != null">
|
||||
AND DATE_FORMAT(${tableName}.${queryField.columnName}, '%Y-%m-%d') <= #{queryForm.${queryField.fieldName}End}
|
||||
AND ${tableName}.${queryField.columnName} <= #{queryForm.${queryField.fieldName}End}
|
||||
</if>
|
||||
#end
|
||||
#end
|
||||
@@ -39,21 +51,10 @@
|
||||
#end
|
||||
</select>
|
||||
|
||||
#if($dao.deletedFieldUpperName != $null)
|
||||
<update id="batchUpdate${dao.deletedFieldUpperName}">
|
||||
update ${mapper.tableName} set ${mapper.deletedColumnName} = #{deletedFlag}
|
||||
where ${mapper.mainKeyColumnName} in
|
||||
<foreach collection="idList" open="(" close=")" separator="," item="item">
|
||||
#{item}
|
||||
</foreach>
|
||||
</update>
|
||||
#end
|
||||
|
||||
#if($deleteInfo.isSupportDelete)
|
||||
### 假删除
|
||||
#if(!${deleteInfo.isPhysicallyDeleted})
|
||||
#if($deleteInfo.deleteEnum == "Batch" || $deleteInfo.deleteEnum == "SingleAndBatch")
|
||||
|
||||
<update id="batchUpdateDeleted">
|
||||
update ${tableName} set deleted_flag = #{deletedFlag}
|
||||
where ${primaryKeyColumnName} in
|
||||
@@ -71,4 +72,5 @@
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
</mapper>
|
||||
|
||||
</mapper>
|
||||
|
||||
+2
-2
@@ -64,7 +64,7 @@ public class ${name.upperCamel}Service {
|
||||
#end
|
||||
|
||||
#if($deleteInfo.isSupportDelete)
|
||||
#if($deleteInfo.deleteEnum == "BATCH" || $deleteInfo.deleteEnum == "SingleAndBatch")
|
||||
#if($deleteInfo.deleteEnum == "Batch" || $deleteInfo.deleteEnum == "SingleAndBatch")
|
||||
/**
|
||||
* 批量删除
|
||||
*
|
||||
@@ -97,7 +97,7 @@ public class ${name.upperCamel}Service {
|
||||
|
||||
### 真删除 or 假删除
|
||||
#if(!${deleteInfo.isPhysicallyDeleted})
|
||||
${name.lowerCamel}Dao.updateDeleted(${primaryKeyFieldName},true);
|
||||
${name.lowerCamel}Dao.updateDeleted(${primaryKeyFieldName}, true);
|
||||
#end
|
||||
#if(${deleteInfo.isPhysicallyDeleted})
|
||||
${name.lowerCamel}Dao.deleteById(${primaryKeyFieldName});
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
# 默认是按前端工程文件的 /views/business 文件夹的路径作为前端组件路径,如果你没把生成的 .vue 前端代码放在 /views/business 下,
|
||||
# 那就根据自己实际情况修改下面 SQL 的 path,component 字段值,避免执行 SQL 后菜单无法访问。
|
||||
# 如果你一切都是按照默认,那么下面的 SQL 基本不用改
|
||||
|
||||
INSERT INTO t_menu ( menu_name, menu_type, parent_id, path, component, frame_flag, cache_flag, visible_flag, disabled_flag, perms_type, create_user_id )
|
||||
VALUES ( '${basic.description}', 2, 0, '/${name.lowerHyphenCamel}/list', '/business/${name.lowerHyphenCamel}/${name.lowerHyphenCamel}-list.vue', false, false, true, false, 1, 1 );
|
||||
|
||||
# 按菜单名称查询该菜单的 menu_id 作为按钮权限的 父菜单ID 与 功能点关联菜单ID
|
||||
SET @parent_id = NULL;
|
||||
SELECT t_menu.menu_id INTO @parent_id FROM t_menu WHERE t_menu.menu_name = '${basic.description}';
|
||||
|
||||
INSERT INTO t_menu ( menu_name, menu_type, parent_id, frame_flag, cache_flag, visible_flag, disabled_flag, api_perms, perms_type, context_menu_id, create_user_id )
|
||||
VALUES ( '查询', 3, @parent_id, false, true, true, false, '${name.lowerCamel}:query', 1, @parent_id, 1 );
|
||||
|
||||
INSERT INTO t_menu ( menu_name, menu_type, parent_id, frame_flag, cache_flag, visible_flag, disabled_flag, api_perms, perms_type, context_menu_id, create_user_id )
|
||||
VALUES ( '添加', 3, @parent_id, false, true, true, false, '${name.lowerCamel}:add', 1, @parent_id, 1 );
|
||||
|
||||
INSERT INTO t_menu ( menu_name, menu_type, parent_id, frame_flag, cache_flag, visible_flag, disabled_flag, api_perms, perms_type, context_menu_id, create_user_id )
|
||||
VALUES ( '更新', 3, @parent_id, false, true, true, false, '${name.lowerCamel}:update', 1, @parent_id, 1 );
|
||||
|
||||
INSERT INTO t_menu ( menu_name, menu_type, parent_id, frame_flag, cache_flag, visible_flag, disabled_flag, api_perms, perms_type, context_menu_id, create_user_id )
|
||||
VALUES ( '删除', 3, @parent_id, false, true, true, false, '${name.lowerCamel}:delete', 1, @parent_id, 1 );
|
||||
+125
-125
@@ -8,7 +8,7 @@
|
||||
<template>
|
||||
<a-$!{insertAndUpdate.pageType}
|
||||
:title="form.$!{primaryKeyFieldName} ? '编辑' : '添加'"
|
||||
width="$!{insertAndUpdate.width}"
|
||||
:width="$!{insertAndUpdate.width}"
|
||||
:open="visibleFlag"
|
||||
#if($!{insertAndUpdate.pageType} == 'drawer')
|
||||
@close="onClose"
|
||||
@@ -19,124 +19,121 @@
|
||||
:destroyOnClose="true"
|
||||
>
|
||||
<a-form ref="formRef" :model="form" :rules="rules" :label-col="{ span: 5 }" >
|
||||
#if($insertAndUpdate.countPerLine == 1)
|
||||
<a-row>
|
||||
#foreach ($field in $formFields)
|
||||
#if($field.frontComponent == "Input")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<a-input style="width: 100%" v-model:value="form.${field.fieldName}" placeholder="$!{field.label}" />
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "InputNumber")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<a-input-number style="width: 100%" v-model:value="form.${field.fieldName}" placeholder="$!{field.label}" />
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "Textarea")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<a-textarea style="width: 100%" v-model:value="form.${field.fieldName}" placeholder="$!{field.label}" />
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "BooleanSelect")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<BooleanSelect v-model:value="form.${field.fieldName}" style="width: 100%" />
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "SmartEnumSelect")
|
||||
<a-form-item label="$codeGeneratorTool.removeEnumDesc($!{field.label})" name="${field.fieldName}">
|
||||
<SmartEnumSelect width="100%" v-model:value="form.${field.fieldName}" enumName="$!{field.upperUnderscoreEnum}" placeholder="$codeGeneratorTool.removeEnumDesc($!{field.label})"/>
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "DictSelect")
|
||||
<a-form-item label="$codeGeneratorTool.removeEnumDesc($!{field.label})" name="${field.fieldName}">
|
||||
<DictSelect width="100%" v-model:value="form.${field.fieldName}" keyCode="$!{field.dict}" placeholder="$!{field.label}"/>
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "Date")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<a-date-picker valueFormat="YYYY-MM-DD" v-model:value="form.$!{field.fieldName}" style="width: 100%" placeholder="$!{field.label}"/>
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "DateTime")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<a-date-picker show-time valueFormat="YYYY-MM-DD HH:mm:ss" v-model:value="form.$!{field.fieldName}" style="width: 100%" placeholder="$!{field.label}" />
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "Upload")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<FileUpload
|
||||
:defaultFileList="form.$!{field.fieldName}"
|
||||
:folder="FILE_FOLDER_TYPE_ENUM.COMMON.value"
|
||||
buttonText="上传 $!{field.label}"
|
||||
listType="text"
|
||||
@change="e => form.$!{field.fieldName} = e"
|
||||
/>
|
||||
</a-form-item>
|
||||
#end
|
||||
#end
|
||||
</a-row>
|
||||
#if($insertAndUpdate.countPerLine == 1)
|
||||
#foreach ($field in $formFields)
|
||||
#if($field.frontComponent == "Input")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<a-input style="width: 100%" v-model:value="form.${field.fieldName}" placeholder="$!{field.label}" />
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "InputNumber")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<a-input-number style="width: 100%" v-model:value="form.${field.fieldName}" placeholder="$!{field.label}" />
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "Textarea")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<a-textarea style="width: 100%" v-model:value="form.${field.fieldName}" placeholder="$!{field.label}" />
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "BooleanSelect")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<BooleanSelect v-model:value="form.${field.fieldName}" style="width: 100%" />
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "SmartEnumSelect")
|
||||
<a-form-item label="$codeGeneratorTool.removeEnumDesc($!{field.label})" name="${field.fieldName}">
|
||||
<SmartEnumSelect width="100%" v-model:value="form.${field.fieldName}" enumName="$!{field.upperUnderscoreEnum}" placeholder="$codeGeneratorTool.removeEnumDesc($!{field.label})"/>
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "DictSelect")
|
||||
<a-form-item label="$codeGeneratorTool.removeEnumDesc($!{field.label})" name="${field.fieldName}">
|
||||
<DictSelect width="100%" v-model:value="form.${field.fieldName}" keyCode="$!{field.dict}" placeholder="$!{field.label}"/>
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "Date")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<a-date-picker valueFormat="YYYY-MM-DD" v-model:value="form.$!{field.fieldName}" style="width: 100%" placeholder="$!{field.label}"/>
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "DateTime")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<a-date-picker show-time valueFormat="YYYY-MM-DD HH:mm:ss" v-model:value="form.$!{field.fieldName}" style="width: 100%" placeholder="$!{field.label}" />
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "FileUpload")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<FileUpload
|
||||
:defaultFileList="form.$!{field.fieldName}"
|
||||
:folder="FILE_FOLDER_TYPE_ENUM.COMMON.value"
|
||||
buttonText="上传 $!{field.label}"
|
||||
listType="text"
|
||||
@change="e => form.$!{field.fieldName} = e"
|
||||
/>
|
||||
</a-form-item>
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
#if($insertAndUpdate.countPerLine > 1)
|
||||
<a-row>
|
||||
#set($span=24 / $!insertAndUpdate.countPerLine )
|
||||
#foreach ($field in $formFields)
|
||||
<a-col :span="$!{span}">
|
||||
#if($field.frontComponent == "Input")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<a-input style="width: 100%" v-model:value="form.${field.fieldName}" placeholder="$!{field.label}" />
|
||||
</a-form-item>
|
||||
#end
|
||||
|
||||
#if($insertAndUpdate.countPerLine > 1)
|
||||
<a-row>
|
||||
#set($span=24 / $!insertAndUpdate.countPerLine )
|
||||
#foreach ($field in $formFields)
|
||||
<a-col :span="$!{span}">
|
||||
#if($field.frontComponent == "Input")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<a-input style="width: 100%" v-model:value="form.${field.fieldName}" placeholder="$!{field.label}" />
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "InputNumber")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<a-input-number style="width: 100%" v-model:value="form.${field.fieldName}" placeholder="$!{field.label}" />
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "Textarea")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<a-textarea style="width: 100%" v-model:value="form.${field.fieldName}" placeholder="$!{field.label}" />
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "BooleanSelect")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<BooleanSelect v-model:value="form.${field.fieldName}" style="width: 100%" />
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "SmartEnumSelect")
|
||||
<a-form-item label="$codeGeneratorTool.removeEnumDesc($!{field.label})" name="${field.fieldName}">
|
||||
<SmartEnumSelect width="100%" v-model:value="form.${field.fieldName}" enumName="$!{field.upperUnderscoreEnum}" placeholder="$codeGeneratorTool.removeEnumDesc($!{field.label})"/>
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "DictSelect")
|
||||
<a-form-item label="$codeGeneratorTool.removeEnumDesc($!{field.label})" name="${field.fieldName}">
|
||||
<DictSelect width="100%" v-model:value="form.${field.fieldName}" keyCode="$!{field.dict}" placeholder="$!{field.label}"/>
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "Date")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<a-date-picker valueFormat="YYYY-MM-DD" v-model:value="form.$!{field.fieldName}" style="width: 100%" placeholder="$!{field.label}"/>
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "DateTime")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<a-date-picker show-time valueFormat="YYYY-MM-DD HH:mm:ss" v-model:value="form.$!{field.fieldName}" style="width: 100%" placeholder="$!{field.label}" />
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "Upload")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<FileUpload
|
||||
:defaultFileList="form.$!{field.fieldName}"
|
||||
:folder="FILE_FOLDER_TYPE_ENUM.COMMON.value"
|
||||
buttonText="上传 $!{field.label}"
|
||||
listType="text"
|
||||
@change="e => form.$!{field.fieldName} = e"
|
||||
/>
|
||||
</a-form-item>
|
||||
#end
|
||||
</a-col>
|
||||
#end
|
||||
</a-row>
|
||||
#if($field.frontComponent == "InputNumber")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<a-input-number style="width: 100%" v-model:value="form.${field.fieldName}" placeholder="$!{field.label}" />
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "Textarea")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<a-textarea style="width: 100%" v-model:value="form.${field.fieldName}" placeholder="$!{field.label}" />
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "BooleanSelect")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<BooleanSelect v-model:value="form.${field.fieldName}" style="width: 100%" />
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "SmartEnumSelect")
|
||||
<a-form-item label="$codeGeneratorTool.removeEnumDesc($!{field.label})" name="${field.fieldName}">
|
||||
<SmartEnumSelect width="100%" v-model:value="form.${field.fieldName}" enumName="$!{field.upperUnderscoreEnum}" placeholder="$codeGeneratorTool.removeEnumDesc($!{field.label})"/>
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "DictSelect")
|
||||
<a-form-item label="$codeGeneratorTool.removeEnumDesc($!{field.label})" name="${field.fieldName}">
|
||||
<DictSelect width="100%" v-model:value="form.${field.fieldName}" keyCode="$!{field.dict}" placeholder="$!{field.label}"/>
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "Date")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<a-date-picker valueFormat="YYYY-MM-DD" v-model:value="form.$!{field.fieldName}" style="width: 100%" placeholder="$!{field.label}"/>
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "DateTime")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<a-date-picker show-time valueFormat="YYYY-MM-DD HH:mm:ss" v-model:value="form.$!{field.fieldName}" style="width: 100%" placeholder="$!{field.label}" />
|
||||
</a-form-item>
|
||||
#end
|
||||
#if($field.frontComponent == "FileUpload")
|
||||
<a-form-item label="$!{field.label}" name="${field.fieldName}">
|
||||
<FileUpload
|
||||
:defaultFileList="form.$!{field.fieldName}"
|
||||
:folder="FILE_FOLDER_TYPE_ENUM.COMMON.value"
|
||||
buttonText="上传 $!{field.label}"
|
||||
listType="text"
|
||||
@change="e => form.$!{field.fieldName} = e"
|
||||
/>
|
||||
</a-form-item>
|
||||
#end
|
||||
</a-col>
|
||||
#end
|
||||
</a-row>
|
||||
#end
|
||||
</a-form>
|
||||
|
||||
<template #footer>
|
||||
@@ -154,9 +151,9 @@
|
||||
import { SmartLoading } from '/@/components/framework/smart-loading';
|
||||
import { $!{name.lowerCamel}Api } from '/@/api/business/$!{name.lowerHyphenCamel}/$!{name.lowerHyphenCamel}-api';
|
||||
import { smartSentry } from '/@/lib/smart-sentry';
|
||||
#foreach ($import in $frontImportList)
|
||||
$!{import}
|
||||
#end
|
||||
#foreach ($import in $frontImportList)
|
||||
$!{import}
|
||||
#end
|
||||
|
||||
// ------------------------ 事件 ------------------------
|
||||
|
||||
@@ -171,6 +168,10 @@
|
||||
if (rowData && !_.isEmpty(rowData)) {
|
||||
Object.assign(form, rowData);
|
||||
}
|
||||
// 使用字典时把下面这注释修改成自己的字典字段 有多个字典字段就复制多份同理修改 不然打开表单时不显示字典初始值
|
||||
// if (form.status && form.status.length > 0) {
|
||||
// form.status = form.status.map((e) => e.valueCode);
|
||||
// }
|
||||
visibleFlag.value = true;
|
||||
nextTick(() => {
|
||||
formRef.value.clearValidate();
|
||||
@@ -188,9 +189,8 @@
|
||||
const formRef = ref();
|
||||
|
||||
const formDefault = {
|
||||
$!{primaryKeyFieldName}: undefined,
|
||||
#foreach ($field in $formFields)
|
||||
$!{field.fieldName}: undefined, //$!{field.label}
|
||||
$!{field.fieldName}: undefined, //$!{field.label}
|
||||
#end
|
||||
};
|
||||
|
||||
@@ -198,9 +198,9 @@
|
||||
|
||||
const rules = {
|
||||
#foreach ($field in $formFields)
|
||||
#if($field.requiredFlag)
|
||||
$!{field.fieldName}: [{ required: true, message: '$!{field.label} 必填' }],
|
||||
#end
|
||||
#if($field.requiredFlag)
|
||||
$!{field.fieldName}: [{ required: true, message: '$!{field.label} 必填' }],
|
||||
#end
|
||||
#end
|
||||
};
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
</a-button>
|
||||
#end
|
||||
#if($deleteInfo.isSupportDelete && ($deleteInfo.deleteEnum == "Batch"||$deleteInfo.deleteEnum == "SingleAndBatch"))
|
||||
<a-button @click="confirmBatchDelete" type="danger" size="small" :disabled="selectedRowKeyList.length == 0">
|
||||
<a-button @click="confirmBatchDelete" type="primary" danger size="small" :disabled="selectedRowKeyList.length == 0">
|
||||
<template #icon>
|
||||
<DeleteOutlined />
|
||||
</template>
|
||||
@@ -88,18 +88,32 @@
|
||||
|
||||
<!---------- 表格 begin ----------->
|
||||
<a-table
|
||||
size="small"
|
||||
:dataSource="tableData"
|
||||
:columns="columns"
|
||||
rowKey="$!{primaryKeyFieldName}"
|
||||
bordered
|
||||
:loading="tableLoading"
|
||||
:pagination="false"
|
||||
size="small"
|
||||
:dataSource="tableData"
|
||||
:columns="columns"
|
||||
rowKey="$!{primaryKeyFieldName}"
|
||||
bordered
|
||||
:loading="tableLoading"
|
||||
:pagination="false"
|
||||
#if($deleteInfo.isSupportDelete && ($deleteInfo.deleteEnum == "Batch"||$deleteInfo.deleteEnum == "SingleAndBatch"))
|
||||
:row-selection="{ selectedRowKeys: selectedRowKeyList, onChange: onSelectChange }"
|
||||
:row-selection="{ selectedRowKeys: selectedRowKeyList, onChange: onSelectChange }"
|
||||
#end
|
||||
>
|
||||
<template #bodyCell="{ text, record, column }">
|
||||
|
||||
<!-- 有图片预览时 注释解开并把下面的'picture'修改成自己的图片字段名即可 -->
|
||||
<!-- <template v-if="column.dataIndex === 'picture'">
|
||||
<FilePreview :fileList="text" type="picture" />
|
||||
</template> -->
|
||||
|
||||
<!-- 使用字典时 注释解开并把下面的'dict'修改成自己的字典字段名即可 有多个字典字段就复制多份同理修改 不然不显示字典 -->
|
||||
<!-- 方便修改tag的颜色 orange green purple success processing error default warning -->
|
||||
<!-- <template v-if="column.dataIndex === 'dict'">
|
||||
<a-tag color="cyan">
|
||||
{{ text && text.length > 0 ? text.map((e) => e.valueName).join(',') : '暂无' }}
|
||||
</a-tag>
|
||||
</template> -->
|
||||
|
||||
<template v-if="column.dataIndex === 'action'">
|
||||
<div class="smart-table-operate">
|
||||
#if($insertAndUpdate.isSupportInsertAndUpdate)
|
||||
@@ -116,17 +130,17 @@
|
||||
|
||||
<div class="smart-query-table-page">
|
||||
<a-pagination
|
||||
showSizeChanger
|
||||
showQuickJumper
|
||||
show-less-items
|
||||
:pageSizeOptions="PAGE_SIZE_OPTIONS"
|
||||
:defaultPageSize="queryForm.pageSize"
|
||||
v-model:current="queryForm.pageNum"
|
||||
v-model:pageSize="queryForm.pageSize"
|
||||
:total="total"
|
||||
@change="queryData"
|
||||
@showSizeChange="queryData"
|
||||
:show-total="(total) => `共${total}条`"
|
||||
showSizeChanger
|
||||
showQuickJumper
|
||||
show-less-items
|
||||
:pageSizeOptions="PAGE_SIZE_OPTIONS"
|
||||
:defaultPageSize="queryForm.pageSize"
|
||||
v-model:current="queryForm.pageNum"
|
||||
v-model:pageSize="queryForm.pageSize"
|
||||
:total="total"
|
||||
@change="queryData"
|
||||
@showSizeChange="queryData"
|
||||
:show-total="(total) => `共${total}条`"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -145,6 +159,8 @@
|
||||
#foreach ($import in $frontImportList)
|
||||
$!{import}
|
||||
#end
|
||||
//import FilePreview from '/@/components/support/file-preview/index.vue'; // 图片预览组件
|
||||
|
||||
// ---------------------------- 表格列 ----------------------------
|
||||
|
||||
const columns = ref([
|
||||
|
||||
@@ -3,14 +3,14 @@ spring:
|
||||
datasource:
|
||||
url: jdbc:p6spy:mysql://127.0.0.1:3306/smart_admin_v3?autoReconnect=true&useServerPreparedStmts=false&rewriteBatchedStatements=true&characterEncoding=UTF-8&useSSL=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai
|
||||
username: root
|
||||
password: Zhuoda1024lab
|
||||
password: SmartAdmin666
|
||||
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
|
||||
initial-size: 2
|
||||
min-idle: 2
|
||||
max-active: 10
|
||||
max-wait: 60000
|
||||
time-between-eviction-runs-millis: 60000
|
||||
min-evictable-idle-time-millis: 300000
|
||||
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
|
||||
filters: stat
|
||||
druid:
|
||||
username: druid
|
||||
@@ -34,11 +34,22 @@ spring:
|
||||
max-idle: 3
|
||||
max-wait: 30000ms
|
||||
|
||||
# 上传文件大小配置
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 30MB
|
||||
max-request-size: 30MB
|
||||
# 邮件,置以SSL的方式发送, 这个需要使用这种方式并且端口是465
|
||||
mail:
|
||||
host: smtp.163.com
|
||||
port: 465
|
||||
username: lab1024@163.com
|
||||
password: ROIASDFJKHQFTA
|
||||
properties:
|
||||
mail:
|
||||
smtp:
|
||||
auth: true
|
||||
ssl:
|
||||
enable: true
|
||||
socketFactory:
|
||||
class: com.sun.mail.util.MailSSLSocketFactory
|
||||
fallback: false
|
||||
debug: false
|
||||
|
||||
# json序列化相关配置
|
||||
jackson:
|
||||
@@ -87,6 +98,7 @@ springdoc:
|
||||
enabled: true # 开关
|
||||
doc-expansion: none #关闭展开
|
||||
tags-sorter: alpha
|
||||
server-base-url:
|
||||
api-docs:
|
||||
enabled: true # 开关
|
||||
knife4j:
|
||||
|
||||
@@ -22,6 +22,12 @@
|
||||
<if test="query.userName != null and query.userName != ''">
|
||||
AND INSTR(operate_user_name,#{query.userName})
|
||||
</if>
|
||||
<if test="query.keywords != null and query.keywords != ''">
|
||||
AND (INSTR(`module`,#{query.keywords}) OR INSTR(content,#{query.keywords}))
|
||||
</if>
|
||||
<if test="query.requestKeywords != null and query.requestKeywords != ''">
|
||||
AND (INSTR(`url`,#{query.requestKeywords}) OR INSTR(`method`,#{query.requestKeywords}) OR INSTR(`param`,#{query.requestKeywords}))
|
||||
</if>
|
||||
<if test="query.successFlag != null">
|
||||
AND success_flag = #{query.successFlag}
|
||||
</if>
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="net.lab1024.sa.base.module.support.securityprotect.dao.PasswordLogDao">
|
||||
|
||||
<select id="selectLastByUserTypeAndUserId"
|
||||
resultType="net.lab1024.sa.base.module.support.securityprotect.domain.PasswordLogEntity">
|
||||
select
|
||||
*
|
||||
from t_password_log
|
||||
where
|
||||
user_id = #{userId}
|
||||
and user_type = #{userType}
|
||||
order by id desc
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
<select id="selectOldPassword" resultType="java.lang.String">
|
||||
select
|
||||
new_password
|
||||
from t_password_log
|
||||
where
|
||||
user_id = #{userId}
|
||||
and user_type = #{userType}
|
||||
order by id desc
|
||||
limit #{limit}
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -3,14 +3,14 @@ spring:
|
||||
datasource:
|
||||
url: jdbc:p6spy:mysql://127.0.0.1:3306/smart_admin_v3?autoReconnect=true&useServerPreparedStmts=false&rewriteBatchedStatements=true&characterEncoding=UTF-8&useSSL=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai
|
||||
username: root
|
||||
password: Zhuoda1024lab
|
||||
password: SmartAdmin666
|
||||
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
|
||||
initial-size: 2
|
||||
min-idle: 2
|
||||
max-active: 10
|
||||
max-wait: 60000
|
||||
time-between-eviction-runs-millis: 60000
|
||||
min-evictable-idle-time-millis: 300000
|
||||
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
|
||||
filters: stat
|
||||
druid:
|
||||
username: druid
|
||||
@@ -34,11 +34,22 @@ spring:
|
||||
max-idle: 3
|
||||
max-wait: 30000ms
|
||||
|
||||
# 上传文件大小配置
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 30MB
|
||||
max-request-size: 30MB
|
||||
# 邮件,置以SSL的方式发送, 这个需要使用这种方式并且端口是465
|
||||
mail:
|
||||
host: smtp.163.com
|
||||
port: 465
|
||||
username: lab1024@163.com
|
||||
password: ROIASDFJKHQFTA
|
||||
properties:
|
||||
mail:
|
||||
smtp:
|
||||
auth: true
|
||||
ssl:
|
||||
enable: true
|
||||
socketFactory:
|
||||
class: com.sun.mail.util.MailSSLSocketFactory
|
||||
fallback: false
|
||||
debug: false
|
||||
|
||||
# json序列化相关配置
|
||||
jackson:
|
||||
@@ -65,7 +76,6 @@ server:
|
||||
max-days: 7
|
||||
pattern: "%t %{X-Forwarded-For}i %a %r %s (%D ms) %I (%B byte)"
|
||||
|
||||
|
||||
# 文件上传 配置
|
||||
file:
|
||||
storage:
|
||||
@@ -82,13 +92,13 @@ file:
|
||||
url-prefix: https://${file.storage.cloud.bucket-name}.${file.storage.cloud.endpoint}/
|
||||
private-url-expire-seconds: 3600
|
||||
|
||||
|
||||
# open api配置
|
||||
springdoc:
|
||||
swagger-ui:
|
||||
enabled: true # 开关
|
||||
doc-expansion: none #关闭展开
|
||||
tags-sorter: alpha
|
||||
server-base-url:
|
||||
api-docs:
|
||||
enabled: true # 开关
|
||||
knife4j:
|
||||
@@ -112,11 +122,11 @@ access-control-allow-origin: '*'
|
||||
|
||||
# 心跳配置
|
||||
heart-beat:
|
||||
interval-seconds: 60
|
||||
interval-seconds: 300
|
||||
|
||||
# 热加载配置
|
||||
reload:
|
||||
interval-seconds: 60
|
||||
interval-seconds: 300
|
||||
|
||||
# sa-token 配置
|
||||
sa-token:
|
||||
@@ -135,9 +145,9 @@ sa-token:
|
||||
# 是否打开自动续签 (如果此值为true,框架会在每次直接或间接调用 getLoginId() 时进行一次过期检查与续签操作)
|
||||
auto-renew: true
|
||||
# 是否输出操作日志
|
||||
is-log: false
|
||||
is-log: true
|
||||
# 日志等级(trace、debug、info、warn、error、fatal)
|
||||
log-level: warn
|
||||
log-level: debug
|
||||
# 启动时的字符画打印
|
||||
is-print: false
|
||||
# 是否从cookie读取token
|
||||
|
||||
@@ -3,14 +3,14 @@ spring:
|
||||
datasource:
|
||||
url: jdbc:mysql://127.0.0.1:3306/smart_admin_v3_dev?autoReconnect=true&useServerPreparedStmts=false&rewriteBatchedStatements=true&characterEncoding=UTF-8&useSSL=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai
|
||||
username: root
|
||||
password: Zhuoda#1024lab
|
||||
password: SmartAdmin666
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
initial-size: 10
|
||||
min-idle: 10
|
||||
max-active: 100
|
||||
max-wait: 60000
|
||||
time-between-eviction-runs-millis: 60000
|
||||
min-evictable-idle-time-millis: 300000
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
filters: stat
|
||||
druid:
|
||||
username: druid
|
||||
@@ -33,12 +33,22 @@ spring:
|
||||
min-idle: 10
|
||||
max-idle: 50
|
||||
max-wait: 30000ms
|
||||
|
||||
# 上传文件大小配置
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 30MB
|
||||
max-request-size: 30MB
|
||||
# 邮件,置以SSL的方式发送, 这个需要使用这种方式并且端口是465
|
||||
mail:
|
||||
host: smtp.163.com
|
||||
port: 465
|
||||
username: lab1024@163.com
|
||||
password: ROIASDFJKHQFTA
|
||||
properties:
|
||||
mail:
|
||||
smtp:
|
||||
auth: true
|
||||
ssl:
|
||||
enable: true
|
||||
socketFactory:
|
||||
class: com.sun.mail.util.MailSSLSocketFactory
|
||||
fallback: false
|
||||
debug: false
|
||||
|
||||
# json序列化相关配置
|
||||
jackson:
|
||||
@@ -74,9 +84,9 @@ file:
|
||||
upload-path: /home/smart_admin_v3/upload/ #文件上传目录
|
||||
url-prefix:
|
||||
cloud:
|
||||
region: oss-cn-qingdao
|
||||
endpoint: oss-cn-qingdao.aliyuncs.com
|
||||
bucket-name: common
|
||||
region: oss-cn-hangzhou
|
||||
endpoint: oss-cn-hangzhou.aliyuncs.com
|
||||
bucket-name: 1024lab-smart-admin
|
||||
access-key:
|
||||
secret-key:
|
||||
url-prefix: https://${file.storage.cloud.bucket-name}.${file.storage.cloud.endpoint}/
|
||||
|
||||
@@ -3,14 +3,14 @@ spring:
|
||||
datasource:
|
||||
url: jdbc:p6spy:mysql://127.0.0.1:3306/smart_admin_v3?autoReconnect=true&useServerPreparedStmts=false&rewriteBatchedStatements=true&characterEncoding=UTF-8&useSSL=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai
|
||||
username: root
|
||||
password: Zhuoda1024lab
|
||||
password: SmartAdmin666
|
||||
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
|
||||
initial-size: 2
|
||||
min-idle: 2
|
||||
max-active: 10
|
||||
max-wait: 60000
|
||||
time-between-eviction-runs-millis: 60000
|
||||
min-evictable-idle-time-millis: 300000
|
||||
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
|
||||
filters: stat
|
||||
druid:
|
||||
username: druid
|
||||
@@ -34,11 +34,22 @@ spring:
|
||||
max-idle: 3
|
||||
max-wait: 30000ms
|
||||
|
||||
# 上传文件大小配置
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 30MB
|
||||
max-request-size: 30MB
|
||||
# 邮件,置以SSL的方式发送, 这个需要使用这种方式并且端口是465
|
||||
mail:
|
||||
host: smtp.163.com
|
||||
port: 465
|
||||
username: lab1024@163.com
|
||||
password: ROIASDFJKHQFTA
|
||||
properties:
|
||||
mail:
|
||||
smtp:
|
||||
auth: true
|
||||
ssl:
|
||||
enable: true
|
||||
socketFactory:
|
||||
class: com.sun.mail.util.MailSSLSocketFactory
|
||||
fallback: false
|
||||
debug: false
|
||||
|
||||
# json序列化相关配置
|
||||
jackson:
|
||||
@@ -65,7 +76,6 @@ server:
|
||||
max-days: 7
|
||||
pattern: "%t %{X-Forwarded-For}i %a %r %s (%D ms) %I (%B byte)"
|
||||
|
||||
|
||||
# 文件上传 配置
|
||||
file:
|
||||
storage:
|
||||
@@ -82,20 +92,19 @@ file:
|
||||
url-prefix: https://${file.storage.cloud.bucket-name}.${file.storage.cloud.endpoint}/
|
||||
private-url-expire-seconds: 3600
|
||||
|
||||
|
||||
# open api配置
|
||||
springdoc:
|
||||
swagger-ui:
|
||||
enabled: true # 开关
|
||||
doc-expansion: none #关闭展开
|
||||
tags-sorter: alpha
|
||||
server-base-url: http://smartadmin.dev.1024lab.net/api/
|
||||
server-base-url:
|
||||
api-docs:
|
||||
enabled: true # 开关
|
||||
knife4j:
|
||||
enable: true
|
||||
basic:
|
||||
enable: true
|
||||
enable: false
|
||||
username: api # Basic认证用户名
|
||||
password: 1024 # Basic认证密码
|
||||
|
||||
|
||||
Reference in New Issue
Block a user