v3.6.0 三级等保重磅更新:1、【新增】双因子方式登录;2、【新增】定期修改密码;3、【新增】最大活跃时间;4、【新增】敏感数据脱敏;5、【新增】登录锁定配置;6、【新增】密码复杂度配置;7、【新增】三级等保可配置

This commit is contained in:
zhuoda
2024-09-03 22:36:38 +08:00
parent 50f5324ac0
commit 85c6087330
160 changed files with 4087 additions and 1533 deletions

View File

@@ -19,9 +19,10 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version> <java.version>1.8</java.version>
<springboot.version>2.7.5</springboot.version> <springboot.version>2.7.18</springboot.version>
<spring-mock.version>2.0.8</spring-mock.version> <spring-mock.version>2.0.8</spring-mock.version>
<mybatis-plus.version>3.5.2</mybatis-plus.version> <mybatis-plus.version>3.5.2</mybatis-plus.version>
<mysql-connector-j.version>8.0.33</mysql-connector-j.version>
<p6spy.version>3.9.1</p6spy.version> <p6spy.version>3.9.1</p6spy.version>
<springdoc-openapi-ui.version>1.7.0</springdoc-openapi-ui.version> <springdoc-openapi-ui.version>1.7.0</springdoc-openapi-ui.version>
<knife4j.version>4.3.0</knife4j.version> <knife4j.version>4.3.0</knife4j.version>
@@ -42,7 +43,7 @@
<poi.version>5.2.4</poi.version> <poi.version>5.2.4</poi.version>
<ooxml-schemas.version>1.4</ooxml-schemas.version> <ooxml-schemas.version>1.4</ooxml-schemas.version>
<aws-java-sdk.version>1.11.842</aws-java-sdk.version> <aws-java-sdk.version>1.11.842</aws-java-sdk.version>
<log4j-spring-boot.version>2.17.2</log4j-spring-boot.version> <log4j-spring-boot.version>2.23.1</log4j-spring-boot.version>
<hutool.version>5.7.22</hutool.version> <hutool.version>5.7.22</hutool.version>
<velocity-engine-core.version>2.3</velocity-engine-core.version> <velocity-engine-core.version>2.3</velocity-engine-core.version>
<jjwt.version>0.9.1</jjwt.version> <jjwt.version>0.9.1</jjwt.version>
@@ -52,8 +53,12 @@
<ip2region.version>2.7.0</ip2region.version> <ip2region.version>2.7.0</ip2region.version>
<bcprov.version>1.59</bcprov.version> <bcprov.version>1.59</bcprov.version>
<jackson-datatype-jsr310.version>2.13.4</jackson-datatype-jsr310.version> <jackson-datatype-jsr310.version>2.13.4</jackson-datatype-jsr310.version>
<jackson-dataformat-yaml.version>2.16.1</jackson-dataformat-yaml.version>
<smartdb.version>1.2.0</smartdb.version> <smartdb.version>1.2.0</smartdb.version>
<redisson.version>3.25.0</redisson.version> <redisson.version>3.25.0</redisson.version>
<snakeyaml.version>2.2</snakeyaml.version>
<freemarker.version>2.3.33</freemarker.version>
<jsoup.version>1.18.1</jsoup.version>
</properties> </properties>
<dependencyManagement> <dependencyManagement>
@@ -81,6 +86,12 @@
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql-connector-j.version}</version>
</dependency>
<dependency> <dependency>
<groupId>com.baomidou</groupId> <groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId> <artifactId>mybatis-plus-boot-starter</artifactId>
@@ -201,12 +212,6 @@
<version>${commons-text.version}</version> <version>${commons-text.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-spring-boot</artifactId>
<version>${log4j-spring-boot.version}</version>
</dependency>
<dependency> <dependency>
<groupId>cn.hutool</groupId> <groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId> <artifactId>hutool-all</artifactId>
@@ -309,6 +314,12 @@
<version>${jackson-datatype-jsr310.version}</version> <version>${jackson-datatype-jsr310.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>${jackson-dataformat-yaml.version}</version>
</dependency>
<dependency> <dependency>
<groupId>net.1024lab</groupId> <groupId>net.1024lab</groupId>
<artifactId>smartdb</artifactId> <artifactId>smartdb</artifactId>
@@ -341,6 +352,24 @@
<version>${redisson.version}</version> <version>${redisson.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>${snakeyaml.version}</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>${jsoup.version}</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>${freemarker.version}</version>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>

View File

@@ -50,9 +50,6 @@ public class AdminInterceptor implements HandlerInterceptor {
@Resource @Resource
private SystemEnvironment systemEnvironment; private SystemEnvironment systemEnvironment;
@Value("${sa-token.active-timeout}")
private long tokenActiveTimeout;
@Override @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
@@ -158,11 +155,6 @@ public class AdminInterceptor implements HandlerInterceptor {
return; return;
} }
// 小于1 ,也不需要检测
if (tokenActiveTimeout < 1) {
return;
}
StpUtil.checkActiveTimeout(); StpUtil.checkActiveTimeout();
StpUtil.updateLastActiveToNow(); StpUtil.updateLastActiveToNow();
} }

View File

@@ -1,9 +1,11 @@
package net.lab1024.sa.admin.module.business.goods.domain.form; package net.lab1024.sa.admin.module.business.goods.domain.form;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import net.lab1024.sa.admin.module.business.goods.constant.GoodsStatusEnum; import net.lab1024.sa.admin.module.business.goods.constant.GoodsStatusEnum;
import net.lab1024.sa.base.common.domain.PageParam; import net.lab1024.sa.base.common.domain.PageParam;
import net.lab1024.sa.base.common.json.deserializer.DictValueVoDeserializer;
import net.lab1024.sa.base.common.swagger.SchemaEnum; import net.lab1024.sa.base.common.swagger.SchemaEnum;
import net.lab1024.sa.base.common.validator.enumeration.CheckEnum; import net.lab1024.sa.base.common.validator.enumeration.CheckEnum;
import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.Length;
@@ -32,6 +34,7 @@ public class GoodsQueryForm extends PageParam {
private Integer goodsStatus; private Integer goodsStatus;
@Schema(description = "产地") @Schema(description = "产地")
@JsonDeserialize(using = DictValueVoDeserializer.class)
private String place; private String place;
@Schema(description = "上架状态") @Schema(description = "上架状态")

View File

@@ -199,7 +199,7 @@ public class GoodsService {
GoodsExcelVO.builder() GoodsExcelVO.builder()
.goodsStatus(SmartEnumUtil.getEnumDescByValue(e.getGoodsStatus(), GoodsStatusEnum.class)) .goodsStatus(SmartEnumUtil.getEnumDescByValue(e.getGoodsStatus(), GoodsStatusEnum.class))
.categoryName(categoryQueryService.queryCategoryName(e.getCategoryId())) .categoryName(categoryQueryService.queryCategoryName(e.getCategoryId()))
.place(dictCacheService.selectValueNameByValueCode(e.getPlace())) .place(Arrays.stream(e.getPlace().split(",")).map(code -> dictCacheService.selectValueNameByValueCode(code)).collect(Collectors.joining(",")))
.price(e.getPrice()) .price(e.getPrice())
.goodsName(e.getGoodsName()) .goodsName(e.getGoodsName())
.remark(e.getRemark()) .remark(e.getRemark())

View File

@@ -114,4 +114,14 @@ public interface NoticeDao extends BaseMapper<NoticeEntity> {
*/ */
void updateViewRecord(@Param("noticeId")Long noticeId, @Param("employeeId")Long requestEmployeeId,@Param("ip") String ip, @Param("userAgent")String userAgent); void updateViewRecord(@Param("noticeId")Long noticeId, @Param("employeeId")Long requestEmployeeId,@Param("ip") String ip, @Param("userAgent")String userAgent);
/**
* 更新 浏览量
*
* @param noticeId 通知 id
* @param pageViewCountIncrement 页面浏览量的增量
* @param userViewCountIncrement 用户浏览量的增量
*/
void updateViewCount(@Param("noticeId")Long noticeId,@Param("pageViewCountIncrement") Integer pageViewCountIncrement, @Param("userViewCountIncrement")Integer userViewCountIncrement);
} }

View File

@@ -52,10 +52,10 @@ public class NoticeEmployeeService {
public ResponseDTO<PageResult<NoticeEmployeeVO>> queryList(Long requestEmployeeId, NoticeEmployeeQueryForm noticeEmployeeQueryForm) { public ResponseDTO<PageResult<NoticeEmployeeVO>> queryList(Long requestEmployeeId, NoticeEmployeeQueryForm noticeEmployeeQueryForm) {
Page<?> page = SmartPageUtil.convert2PageQuery(noticeEmployeeQueryForm); Page<?> page = SmartPageUtil.convert2PageQuery(noticeEmployeeQueryForm);
//获取请求人的 部门及其子部门
List<Long> employeeDepartmentIdList = Lists.newArrayList(); List<Long> employeeDepartmentIdList = Lists.newArrayList();
EmployeeEntity employeeEntity = employeeService.getById(requestEmployeeId); EmployeeEntity employeeEntity = employeeService.getById(requestEmployeeId);
if (employeeEntity.getDepartmentId() != null) { // 如果不是管理员 则获取请求人的 部门及其子部门
if (!employeeEntity.getAdministratorFlag() && employeeEntity.getDepartmentId() != null) {
employeeDepartmentIdList = departmentService.selfAndChildrenIdList(employeeEntity.getDepartmentId()); employeeDepartmentIdList = departmentService.selfAndChildrenIdList(employeeEntity.getDepartmentId());
} }
@@ -106,8 +106,15 @@ public class NoticeEmployeeService {
long viewCount = noticeDao.viewRecordCount(noticeId, requestEmployeeId); long viewCount = noticeDao.viewRecordCount(noticeId, requestEmployeeId);
if (viewCount == 0) { if (viewCount == 0) {
noticeDao.insertViewRecord(noticeId, requestEmployeeId, ip, userAgent, 1); noticeDao.insertViewRecord(noticeId, requestEmployeeId, ip, userAgent, 1);
// 该员工对于这个通知是第一次查看 页面浏览量+1 用户浏览量+1
noticeDao.updateViewCount(noticeId, 1, 1);
noticeDetailVO.setPageViewCount(noticeDetailVO.getPageViewCount() + 1);
noticeDetailVO.setUserViewCount(noticeDetailVO.getUserViewCount() + 1);
} else { } else {
noticeDao.updateViewRecord(noticeId, requestEmployeeId, ip, userAgent); noticeDao.updateViewRecord(noticeId, requestEmployeeId, ip, userAgent);
// 该员工对于这个通知不是第一次查看 页面浏览量+1 用户浏览量+0
noticeDao.updateViewCount(noticeId, 1, 0);
noticeDetailVO.setPageViewCount(noticeDetailVO.getPageViewCount() + 1);
} }
return ResponseDTO.ok(noticeDetailVO); return ResponseDTO.ok(noticeDetailVO);

View File

@@ -141,13 +141,15 @@ public class DepartmentCacheManager {
return treeVOList; return treeVOList;
} }
/** /**
* 构建所有根节点的下级树形结构 * 构建所有根节点的下级树形结构
* * 返回值为层序遍历结果
* [由于departmentDao中listAll给出数据根据Sort降序 所以同一层中Sort值较大的优先遍历]
*/ */
private void recursiveBuildTree(List<DepartmentTreeVO> nodeList, List<DepartmentVO> allDepartmentList) { private List<Long> recursiveBuildTree(List<DepartmentTreeVO> nodeList, List<DepartmentVO> allDepartmentList) {
int nodeSize = nodeList.size(); int nodeSize = nodeList.size();
for (int i = 0; i < nodeSize; i++) { List<Long> childIdList = new ArrayList<>();
for(int i = 0; i < nodeSize; i++) {
int preIndex = i - 1; int preIndex = i - 1;
int nextIndex = i + 1; int nextIndex = i + 1;
DepartmentTreeVO node = nodeList.get(i); DepartmentTreeVO node = nodeList.get(i);
@@ -158,16 +160,34 @@ public class DepartmentCacheManager {
node.setNextId(nodeList.get(nextIndex).getDepartmentId()); node.setNextId(nodeList.get(nextIndex).getDepartmentId());
} }
ArrayList<Long> selfAndAllChildrenIdList = Lists.newArrayList();
selfAndAllChildrenIdList.add(node.getDepartmentId());
node.setSelfAndAllChildrenIdList(selfAndAllChildrenIdList);
List<DepartmentTreeVO> children = getChildren(node.getDepartmentId(), allDepartmentList); List<DepartmentTreeVO> children = getChildren(node.getDepartmentId(), allDepartmentList);
List<Long> tempChildIdList = new ArrayList<>();
if (CollectionUtils.isNotEmpty(children)) { if (CollectionUtils.isNotEmpty(children)) {
node.setChildren(children); node.setChildren(children);
this.recursiveBuildTree(children, allDepartmentList); tempChildIdList = this.recursiveBuildTree(children, allDepartmentList);
} }
if(CollectionUtils.isEmpty(node.getSelfAndAllChildrenIdList())) {
node.setSelfAndAllChildrenIdList(
new ArrayList<>()
);
}
node.getSelfAndAllChildrenIdList().add(node.getDepartmentId());
if(CollectionUtils.isNotEmpty(tempChildIdList)) {
node.getSelfAndAllChildrenIdList().addAll(tempChildIdList);
childIdList.addAll(tempChildIdList);
}
} }
// 保证本层遍历顺序
for(int i = nodeSize - 1; i >= 0; i--) {
childIdList.add(0, nodeList.get(i).getDepartmentId());
}
return childIdList;
} }

View File

@@ -92,7 +92,7 @@ public class DepartmentService {
} }
// 是否有未删除员工 // 是否有未删除员工
int employeeNum = employeeDao.countByDepartmentId(departmentId); int employeeNum = employeeDao.countByDepartmentId(departmentId, Boolean.FALSE);
if (employeeNum > 0) { if (employeeNum > 0) {
return ResponseDTO.userErrorParam("请先删除部门员工"); return ResponseDTO.userErrorParam("请先删除部门员工");
} }

View File

@@ -10,6 +10,8 @@ import net.lab1024.sa.admin.module.system.employee.service.EmployeeService;
import net.lab1024.sa.base.common.domain.PageResult; import net.lab1024.sa.base.common.domain.PageResult;
import net.lab1024.sa.base.common.domain.ResponseDTO; import net.lab1024.sa.base.common.domain.ResponseDTO;
import net.lab1024.sa.base.common.util.SmartRequestUtil; import net.lab1024.sa.base.common.util.SmartRequestUtil;
import net.lab1024.sa.base.module.support.apiencrypt.annotation.ApiDecrypt;
import net.lab1024.sa.base.module.support.securityprotect.service.Level3ProtectConfigService;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
@@ -23,7 +25,7 @@ import java.util.List;
* @Date 2021-12-09 22:57:49 * @Date 2021-12-09 22:57:49
* @Wechat zhuoda1024 * @Wechat zhuoda1024
* @Email lab1024@163.com * @Email lab1024@163.com
* @Copyright <a href="https://1024lab.net">1024创新实验室</a> * @Copyright <a href="https://1024lab.net">1024创新实验室</a>
*/ */
@RestController @RestController
@Tag(name = AdminSwaggerTagConst.System.SYSTEM_EMPLOYEE) @Tag(name = AdminSwaggerTagConst.System.SYSTEM_EMPLOYEE)
@@ -32,6 +34,9 @@ public class EmployeeController {
@Resource @Resource
private EmployeeService employeeService; private EmployeeService employeeService;
@Resource
private Level3ProtectConfigService level3ProtectConfigService;
@PostMapping("/employee/query") @PostMapping("/employee/query")
@Operation(summary = "员工管理查询 @author 卓大") @Operation(summary = "员工管理查询 @author 卓大")
public ResponseDTO<PageResult<EmployeeVO>> query(@Valid @RequestBody EmployeeQueryForm query) { public ResponseDTO<PageResult<EmployeeVO>> query(@Valid @RequestBody EmployeeQueryForm query) {
@@ -89,9 +94,17 @@ public class EmployeeController {
@Operation(summary = "修改密码 @author 卓大") @Operation(summary = "修改密码 @author 卓大")
@PostMapping("/employee/update/password") @PostMapping("/employee/update/password")
@ApiDecrypt
public ResponseDTO<String> updatePassword(@Valid @RequestBody EmployeeUpdatePasswordForm updatePasswordForm) { public ResponseDTO<String> updatePassword(@Valid @RequestBody EmployeeUpdatePasswordForm updatePasswordForm) {
updatePasswordForm.setEmployeeId(SmartRequestUtil.getRequestUserId()); updatePasswordForm.setEmployeeId(SmartRequestUtil.getRequestUserId());
return employeeService.updatePassword(updatePasswordForm); return employeeService.updatePassword(SmartRequestUtil.getRequestUser(), updatePasswordForm);
}
@Operation(summary = "获取密码复杂度 @author 卓大")
@GetMapping("/employee/getPasswordComplexityEnabled")
@ApiDecrypt
public ResponseDTO<Boolean> getPasswordComplexityEnabled() {
return ResponseDTO.ok(level3ProtectConfigService.isPasswordComplexityEnabled());
} }
@Operation(summary = "重置员工密码 @author 卓大") @Operation(summary = "重置员工密码 @author 卓大")

View File

@@ -69,7 +69,7 @@ public interface EmployeeDao extends BaseMapper<EmployeeEntity> {
* 获取某个部门员工数 * 获取某个部门员工数
* *
*/ */
Integer countByDepartmentId(@Param("departmentId") Long departmentId); Integer countByDepartmentId(@Param("departmentId") Long departmentId, @Param("deletedFlag") Boolean deletedFlag);
/** /**
* 获取一批员工 * 获取一批员工

View File

@@ -53,6 +53,11 @@ public class EmployeeEntity {
*/ */
private String phone; private String phone;
/**
* 邮箱
*/
private String email;
/** /**
* 部门id * 部门id
*/ */

View File

@@ -51,6 +51,9 @@ public class EmployeeAddForm {
@Pattern(regexp = SmartVerificationUtil.PHONE_REGEXP, message = "手机号格式不正确") @Pattern(regexp = SmartVerificationUtil.PHONE_REGEXP, message = "手机号格式不正确")
private String phone; private String phone;
@Schema(description = "邮箱")
private String email;
@Schema(description = "角色列表") @Schema(description = "角色列表")
private List<Long> roleIdList; private List<Long> roleIdList;

View File

@@ -24,11 +24,9 @@ public class EmployeeUpdatePasswordForm {
@Schema(description = "原密码") @Schema(description = "原密码")
@NotBlank(message = "原密码不能为空哦") @NotBlank(message = "原密码不能为空哦")
@Pattern(regexp = SmartVerificationUtil.PWD_REGEXP, message = "原密码请输入6-15位(数字|大小写字母|小数点)")
private String oldPassword; private String oldPassword;
@Schema(description = "新密码") @Schema(description = "新密码")
@NotBlank(message = "新密码不能为空哦") @NotBlank(message = "新密码不能为空哦")
@Pattern(regexp = SmartVerificationUtil.PWD_REGEXP, message = "新密码请输入6-15位(数字|大小写字母|小数点)")
private String newPassword; private String newPassword;
} }

View File

@@ -62,4 +62,7 @@ public class EmployeeVO {
@Schema(description = "职务名称") @Schema(description = "职务名称")
private String positionName; private String positionName;
@Schema(description = "邮箱")
private String email;
} }

View File

@@ -60,10 +60,14 @@ public class EmployeeManager extends ServiceImpl<EmployeeDao, EmployeeEntity> {
// 保存员工 获得id // 保存员工 获得id
employeeDao.updateById(employee); employeeDao.updateById(employee);
if (CollectionUtils.isNotEmpty(roleIdList)) { // 若为空,则删除所有角色
List<RoleEmployeeEntity> roleEmployeeList = roleIdList.stream().map(e -> new RoleEmployeeEntity(e, employee.getEmployeeId())).collect(Collectors.toList()); if (CollectionUtils.isEmpty(roleIdList)) {
this.updateEmployeeRole(employee.getEmployeeId(), roleEmployeeList); roleEmployeeDao.deleteByEmployeeId(employee.getEmployeeId());
return;
} }
List<RoleEmployeeEntity> roleEmployeeList = roleIdList.stream().map(e -> new RoleEmployeeEntity(e, employee.getEmployeeId())).collect(Collectors.toList());
this.updateEmployeeRole(employee.getEmployeeId(), roleEmployeeList);
} }
/** /**

View File

@@ -20,15 +20,16 @@ import net.lab1024.sa.admin.module.system.role.domain.vo.RoleEmployeeVO;
import net.lab1024.sa.base.common.code.UserErrorCode; import net.lab1024.sa.base.common.code.UserErrorCode;
import net.lab1024.sa.base.common.constant.StringConst; import net.lab1024.sa.base.common.constant.StringConst;
import net.lab1024.sa.base.common.domain.PageResult; import net.lab1024.sa.base.common.domain.PageResult;
import net.lab1024.sa.base.common.domain.RequestUser;
import net.lab1024.sa.base.common.domain.ResponseDTO; import net.lab1024.sa.base.common.domain.ResponseDTO;
import net.lab1024.sa.base.common.enumeration.UserTypeEnum; import net.lab1024.sa.base.common.enumeration.UserTypeEnum;
import net.lab1024.sa.base.common.util.SmartBeanUtil; import net.lab1024.sa.base.common.util.SmartBeanUtil;
import net.lab1024.sa.base.common.util.SmartPageUtil; import net.lab1024.sa.base.common.util.SmartPageUtil;
import net.lab1024.sa.base.module.support.securityprotect.service.ProtectPasswordService; import net.lab1024.sa.base.module.support.securityprotect.service.SecurityPasswordService;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.*; import java.util.*;
@@ -46,8 +47,6 @@ import java.util.stream.Collectors;
@Service @Service
public class EmployeeService { public class EmployeeService {
private static final String PASSWORD_SALT_FORMAT = "smart_%s_admin_$^&*";
@Resource @Resource
private EmployeeDao employeeDao; private EmployeeDao employeeDao;
@@ -64,7 +63,7 @@ public class EmployeeService {
private DepartmentService departmentService; private DepartmentService departmentService;
@Resource @Resource
private ProtectPasswordService protectPasswordService; private SecurityPasswordService securityPasswordService;
@Resource @Resource
@Lazy @Lazy
@@ -121,16 +120,11 @@ public class EmployeeService {
* 新增员工 * 新增员工
*/ */
public synchronized ResponseDTO<String> addEmployee(EmployeeAddForm employeeAddForm) { public synchronized ResponseDTO<String> addEmployee(EmployeeAddForm employeeAddForm) {
// 校验名是否重复 // 校验登录名是否重复
EmployeeEntity employeeEntity = employeeDao.getByLoginName(employeeAddForm.getLoginName(), null); EmployeeEntity employeeEntity = employeeDao.getByLoginName(employeeAddForm.getLoginName(), null);
if (null != employeeEntity) { if (null != employeeEntity) {
return ResponseDTO.userErrorParam("登录名重复"); return ResponseDTO.userErrorParam("登录名重复");
} }
// 校验姓名是否重复
employeeEntity = employeeDao.getByActualName(employeeAddForm.getActualName(), null);
if (null != employeeEntity) {
return ResponseDTO.userErrorParam("姓名重复");
}
// 校验电话是否存在 // 校验电话是否存在
employeeEntity = employeeDao.getByPhone(employeeAddForm.getPhone(), null); employeeEntity = employeeDao.getByPhone(employeeAddForm.getPhone(), null);
if (null != employeeEntity) { if (null != employeeEntity) {
@@ -146,8 +140,8 @@ public class EmployeeService {
EmployeeEntity entity = SmartBeanUtil.copy(employeeAddForm, EmployeeEntity.class); EmployeeEntity entity = SmartBeanUtil.copy(employeeAddForm, EmployeeEntity.class);
// 设置密码 默认密码 // 设置密码 默认密码
String password = protectPasswordService.randomPassword(); String password = securityPasswordService.randomPassword();
entity.setLoginPwd(getEncryptPwd(password)); entity.setLoginPwd(SecurityPasswordService.getEncryptPwd(password));
// 保存数据 // 保存数据
entity.setDeletedFlag(Boolean.FALSE); entity.setDeletedFlag(Boolean.FALSE);
@@ -185,11 +179,6 @@ public class EmployeeService {
return ResponseDTO.userErrorParam("手机号已存在"); return ResponseDTO.userErrorParam("手机号已存在");
} }
existEntity = employeeDao.getByActualName(employeeUpdateForm.getActualName(), null);
if (null != existEntity && !Objects.equals(existEntity.getEmployeeId(), employeeId)) {
return ResponseDTO.userErrorParam("姓名重复");
}
// 不更新密码 // 不更新密码
EmployeeEntity entity = SmartBeanUtil.copy(employeeUpdateForm, EmployeeEntity.class); EmployeeEntity entity = SmartBeanUtil.copy(employeeUpdateForm, EmployeeEntity.class);
entity.setLoginPwd(null); entity.setLoginPwd(null);
@@ -301,36 +290,46 @@ public class EmployeeService {
/** /**
* 更新密码 * 更新密码
*/ */
public ResponseDTO<String> updatePassword(EmployeeUpdatePasswordForm updatePasswordForm) { @Transactional(rollbackFor = Throwable.class)
public ResponseDTO<String> updatePassword(RequestUser requestUser, EmployeeUpdatePasswordForm updatePasswordForm) {
Long employeeId = updatePasswordForm.getEmployeeId(); Long employeeId = updatePasswordForm.getEmployeeId();
EmployeeEntity employeeEntity = employeeDao.selectById(employeeId); EmployeeEntity employeeEntity = employeeDao.selectById(employeeId);
if (employeeEntity == null) { if (employeeEntity == null) {
return ResponseDTO.error(UserErrorCode.DATA_NOT_EXIST); return ResponseDTO.error(UserErrorCode.DATA_NOT_EXIST);
} }
// 校验原始密码 // 校验原始密码
String encryptPwd = getEncryptPwd(updatePasswordForm.getOldPassword()); String oldPassword = SecurityPasswordService.getEncryptPwd(updatePasswordForm.getOldPassword());
if (!Objects.equals(encryptPwd, employeeEntity.getLoginPwd())) { if (!Objects.equals(oldPassword, employeeEntity.getLoginPwd())) {
return ResponseDTO.userErrorParam("原密码有误,请重新输入"); return ResponseDTO.userErrorParam("原密码有误,请重新输入");
} }
// 校验密码复杂度
ResponseDTO<String> validatePassComplexity = securityPasswordService.validatePasswordComplexity(updatePasswordForm.getNewPassword());
if (!validatePassComplexity.getOk()) {
return validatePassComplexity;
}
// 新旧密码相同 // 新旧密码相同
String newPassword = updatePasswordForm.getNewPassword(); String newPassword = SecurityPasswordService.getEncryptPwd(updatePasswordForm.getNewPassword());
if (Objects.equals(updatePasswordForm.getOldPassword(), newPassword)) { if (Objects.equals(oldPassword, newPassword)) {
return ResponseDTO.userErrorParam("新密码与原始密码相同,请重新输入"); return ResponseDTO.userErrorParam("新密码与原始密码相同,请重新输入");
} }
// 校验密码复杂度 // 根据三级等保规则,校验密码是否重复
ResponseDTO<String> validatePassComplexity = protectPasswordService.validatePassComplexity(newPassword); ResponseDTO<String> passwordRepeatTimes = securityPasswordService.validatePasswordRepeatTimes(requestUser, updatePasswordForm.getNewPassword());
if (!validatePassComplexity.getOk()) { if (!passwordRepeatTimes.getOk()) {
return validatePassComplexity; return ResponseDTO.error(passwordRepeatTimes);
} }
// 更新密码 // 更新密码
EmployeeEntity updateEntity = new EmployeeEntity(); EmployeeEntity updateEntity = new EmployeeEntity();
updateEntity.setEmployeeId(employeeId); updateEntity.setEmployeeId(employeeId);
updateEntity.setLoginPwd(getEncryptPwd(newPassword)); updateEntity.setLoginPwd(newPassword);
employeeDao.updateById(updateEntity); employeeDao.updateById(updateEntity);
// 保存修改密码密码记录
securityPasswordService.saveUserChangePasswordLog(requestUser, newPassword, oldPassword);
return ResponseDTO.ok(); return ResponseDTO.ok();
} }
@@ -364,18 +363,11 @@ public class EmployeeService {
* 重置密码 * 重置密码
*/ */
public ResponseDTO<String> resetPassword(Integer employeeId) { public ResponseDTO<String> resetPassword(Integer employeeId) {
String password = protectPasswordService.randomPassword(); String password = securityPasswordService.randomPassword();
employeeDao.updatePassword(employeeId, getEncryptPwd(password)); employeeDao.updatePassword(employeeId, SecurityPasswordService.getEncryptPwd(password));
return ResponseDTO.ok(password); return ResponseDTO.ok(password);
} }
/**
* 获取 加密后 的密码
*/
public static String getEncryptPwd(String password) {
return DigestUtils.md5Hex(String.format(PASSWORD_SALT_FORMAT, password));
}
/** /**
* 查询全部员工 * 查询全部员工

View File

@@ -2,8 +2,8 @@ package net.lab1024.sa.admin.module.system.login.controller;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.extra.servlet.ServletUtil; import cn.hutool.extra.servlet.ServletUtil;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import net.lab1024.sa.admin.constant.AdminSwaggerTagConst; import net.lab1024.sa.admin.constant.AdminSwaggerTagConst;
import net.lab1024.sa.admin.module.system.login.domain.LoginForm; import net.lab1024.sa.admin.module.system.login.domain.LoginForm;
import net.lab1024.sa.admin.module.system.login.domain.LoginResultVO; import net.lab1024.sa.admin.module.system.login.domain.LoginResultVO;
@@ -14,6 +14,7 @@ import net.lab1024.sa.base.common.constant.RequestHeaderConst;
import net.lab1024.sa.base.common.domain.ResponseDTO; import net.lab1024.sa.base.common.domain.ResponseDTO;
import net.lab1024.sa.base.common.util.SmartRequestUtil; import net.lab1024.sa.base.common.util.SmartRequestUtil;
import net.lab1024.sa.base.module.support.captcha.domain.CaptchaVO; import net.lab1024.sa.base.module.support.captcha.domain.CaptchaVO;
import net.lab1024.sa.base.module.support.securityprotect.service.Level3ProtectConfigService;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
@@ -27,7 +28,7 @@ import javax.validation.Valid;
* @Date 2021-12-15 21:05:46 * @Date 2021-12-15 21:05:46
* @Wechat zhuoda1024 * @Wechat zhuoda1024
* @Email lab1024@163.com * @Email lab1024@163.com
* @Copyright <a href="https://1024lab.net">1024创新实验室</a> * @Copyright <a href="https://1024lab.net">1024创新实验室</a>
*/ */
@RestController @RestController
@Tag(name = AdminSwaggerTagConst.System.SYSTEM_LOGIN) @Tag(name = AdminSwaggerTagConst.System.SYSTEM_LOGIN)
@@ -36,6 +37,9 @@ public class LoginController {
@Resource @Resource
private LoginService loginService; private LoginService loginService;
@Resource
private Level3ProtectConfigService level3ProtectConfigService;
@NoNeedLogin @NoNeedLogin
@PostMapping("/login") @PostMapping("/login")
@Operation(summary = "登录 @author 卓大") @Operation(summary = "登录 @author 卓大")
@@ -48,8 +52,8 @@ public class LoginController {
@GetMapping("/login/getLoginInfo") @GetMapping("/login/getLoginInfo")
@Operation(summary = "获取登录结果信息 @author 卓大") @Operation(summary = "获取登录结果信息 @author 卓大")
public ResponseDTO<LoginResultVO> getLoginInfo() { public ResponseDTO<LoginResultVO> getLoginInfo() {
LoginResultVO loginResult = loginService.getLoginResult(AdminRequestUtil.getRequestUser());
String tokenValue = StpUtil.getTokenValue(); String tokenValue = StpUtil.getTokenValue();
LoginResultVO loginResult = loginService.getLoginResult(AdminRequestUtil.getRequestUser(), tokenValue);
loginResult.setToken(tokenValue); loginResult.setToken(tokenValue);
return ResponseDTO.ok(loginResult); return ResponseDTO.ok(loginResult);
} }
@@ -67,4 +71,20 @@ public class LoginController {
return loginService.getCaptcha(); return loginService.getCaptcha();
} }
@NoNeedLogin
@GetMapping("/login/sendEmailCode/{loginName}")
@Operation(summary = "获取邮箱登录验证码 @author 卓大")
public ResponseDTO<String> sendEmailCode(@PathVariable String loginName) {
return loginService.sendEmailCode(loginName);
}
@NoNeedLogin
@GetMapping("/login/getTwoFactorLoginFlag")
@Operation(summary = "获取双因子登录标识 @author 卓大")
public ResponseDTO<Boolean> getTwoFactorLoginFlag() {
// 双因子登录
boolean twoFactorLoginEnabled = level3ProtectConfigService.isTwoFactorLoginEnabled();
return ResponseDTO.ok(twoFactorLoginEnabled);
}
} }

View File

@@ -34,4 +34,7 @@ public class LoginForm extends CaptchaForm {
@SchemaEnum(desc = "登录终端", value = LoginDeviceEnum.class) @SchemaEnum(desc = "登录终端", value = LoginDeviceEnum.class)
@CheckEnum(value = LoginDeviceEnum.class, required = true, message = "此终端不允许登录") @CheckEnum(value = LoginDeviceEnum.class, required = true, message = "此终端不允许登录")
private Integer loginDevice; private Integer loginDevice;
@Schema(description = "邮箱验证码")
private String emailCode;
} }

View File

@@ -26,6 +26,9 @@ public class LoginResultVO extends RequestEmployee {
@Schema(description = "菜单列表") @Schema(description = "菜单列表")
private List<MenuVO> menuList; private List<MenuVO> menuList;
@Schema(description = "是否需要修改密码")
private Boolean needUpdatePwdFlag;
@Schema(description = "上次登录ip") @Schema(description = "上次登录ip")
private String lastLoginIp; private String lastLoginIp;

View File

@@ -3,6 +3,8 @@ package net.lab1024.sa.admin.module.system.login.service;
import cn.dev33.satoken.stp.StpInterface; import cn.dev33.satoken.stp.StpInterface;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.lang.UUID; import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.extra.servlet.ServletUtil; import cn.hutool.extra.servlet.ServletUtil;
import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap; import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -27,7 +29,10 @@ import net.lab1024.sa.base.common.enumeration.UserTypeEnum;
import net.lab1024.sa.base.common.util.SmartBeanUtil; import net.lab1024.sa.base.common.util.SmartBeanUtil;
import net.lab1024.sa.base.common.util.SmartEnumUtil; import net.lab1024.sa.base.common.util.SmartEnumUtil;
import net.lab1024.sa.base.common.util.SmartIpUtil; import net.lab1024.sa.base.common.util.SmartIpUtil;
import net.lab1024.sa.base.common.util.SmartStringUtil;
import net.lab1024.sa.base.constant.LoginDeviceEnum; import net.lab1024.sa.base.constant.LoginDeviceEnum;
import net.lab1024.sa.base.constant.RedisKeyConst;
import net.lab1024.sa.base.module.support.apiencrypt.service.ApiEncryptService;
import net.lab1024.sa.base.module.support.captcha.CaptchaService; import net.lab1024.sa.base.module.support.captcha.CaptchaService;
import net.lab1024.sa.base.module.support.captcha.domain.CaptchaVO; import net.lab1024.sa.base.module.support.captcha.domain.CaptchaVO;
import net.lab1024.sa.base.module.support.config.ConfigKeyEnum; import net.lab1024.sa.base.module.support.config.ConfigKeyEnum;
@@ -37,9 +42,13 @@ import net.lab1024.sa.base.module.support.loginlog.LoginLogResultEnum;
import net.lab1024.sa.base.module.support.loginlog.LoginLogService; import net.lab1024.sa.base.module.support.loginlog.LoginLogService;
import net.lab1024.sa.base.module.support.loginlog.domain.LoginLogEntity; import net.lab1024.sa.base.module.support.loginlog.domain.LoginLogEntity;
import net.lab1024.sa.base.module.support.loginlog.domain.LoginLogVO; import net.lab1024.sa.base.module.support.loginlog.domain.LoginLogVO;
import net.lab1024.sa.base.module.support.mail.MailService;
import net.lab1024.sa.base.module.support.mail.constant.MailTemplateCodeEnum;
import net.lab1024.sa.base.module.support.redis.RedisService;
import net.lab1024.sa.base.module.support.securityprotect.domain.LoginFailEntity; import net.lab1024.sa.base.module.support.securityprotect.domain.LoginFailEntity;
import net.lab1024.sa.base.module.support.securityprotect.service.ProtectLoginService; import net.lab1024.sa.base.module.support.securityprotect.service.Level3ProtectConfigService;
import net.lab1024.sa.base.module.support.securityprotect.service.ProtectPasswordService; import net.lab1024.sa.base.module.support.securityprotect.service.SecurityLoginService;
import net.lab1024.sa.base.module.support.securityprotect.service.SecurityPasswordService;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -107,14 +116,26 @@ public class LoginService implements StpInterface {
private RoleMenuService roleMenuService; private RoleMenuService roleMenuService;
@Resource @Resource
private ProtectLoginService protectLoginService; private SecurityLoginService securityLoginService;
@Resource @Resource
private ProtectPasswordService profectPasswordService; private SecurityPasswordService protectPasswordService;
@Resource @Resource
private IFileStorageService fileStorageService; private IFileStorageService fileStorageService;
@Resource
private ApiEncryptService apiEncryptService;
@Resource
private Level3ProtectConfigService level3ProtectConfigService;
@Resource
private MailService mailService;
@Resource
private RedisService redisService;
/** /**
* 获取验证码 * 获取验证码
*/ */
@@ -153,12 +174,18 @@ public class LoginService implements StpInterface {
} }
// 解密前端加密的密码 // 解密前端加密的密码
String requestPassword = profectPasswordService.decryptPassword(loginForm.getPassword()); String requestPassword = apiEncryptService.decrypt(loginForm.getPassword());
// 验证密码 是否为万能密码 // 验证密码 是否为万能密码
String superPassword = configService.getConfigValue(ConfigKeyEnum.SUPER_PASSWORD); String superPassword = configService.getConfigValue(ConfigKeyEnum.SUPER_PASSWORD);
boolean superPasswordFlag = superPassword.equals(requestPassword); boolean superPasswordFlag = superPassword.equals(requestPassword);
// 校验双因子登录
ResponseDTO<String> validateEmailCode = validateEmailCode(loginForm, employeeEntity, superPasswordFlag);
if (!validateEmailCode.getOk()) {
return ResponseDTO.error(validateEmailCode);
}
// 万能密码特殊操作 // 万能密码特殊操作
if (superPasswordFlag) { if (superPasswordFlag) {
@@ -170,23 +197,27 @@ public class LoginService implements StpInterface {
} else { } else {
// 按照等保登录要求,进行登录失败次数校验 // 按照等保登录要求,进行登录失败次数校验
ResponseDTO<LoginFailEntity> loginFailEntityResponseDTO = protectLoginService.checkLogin(employeeEntity.getEmployeeId(), UserTypeEnum.ADMIN_EMPLOYEE); ResponseDTO<LoginFailEntity> loginFailEntityResponseDTO = securityLoginService.checkLogin(employeeEntity.getEmployeeId(), UserTypeEnum.ADMIN_EMPLOYEE);
if (!loginFailEntityResponseDTO.getOk()) { if (!loginFailEntityResponseDTO.getOk()) {
return ResponseDTO.error(loginFailEntityResponseDTO); return ResponseDTO.error(loginFailEntityResponseDTO);
} }
// 密码错误 // 密码错误
if (!employeeEntity.getLoginPwd().equals(EmployeeService.getEncryptPwd(requestPassword))) { if (!employeeEntity.getLoginPwd().equals(SecurityPasswordService.getEncryptPwd(requestPassword))) {
// 记录登录失败 // 记录登录失败
saveLoginLog(employeeEntity, ip, userAgent, "密码错误", LoginLogResultEnum.LOGIN_FAIL); saveLoginLog(employeeEntity, ip, userAgent, "密码错误", LoginLogResultEnum.LOGIN_FAIL);
// 记录等级保护次数 // 记录等级保护次数
String msg = protectLoginService.recordLoginFail(employeeEntity.getEmployeeId(), UserTypeEnum.ADMIN_EMPLOYEE, employeeEntity.getLoginName(), loginFailEntityResponseDTO.getData()); String msg = securityLoginService.recordLoginFail(employeeEntity.getEmployeeId(), UserTypeEnum.ADMIN_EMPLOYEE, employeeEntity.getLoginName(), loginFailEntityResponseDTO.getData());
return msg == null ? ResponseDTO.userErrorParam("登录名或密码错误!") : ResponseDTO.error(UserErrorCode.LOGIN_FAIL_WILL_LOCK, msg); return msg == null ? ResponseDTO.userErrorParam("登录名或密码错误!") : ResponseDTO.error(UserErrorCode.LOGIN_FAIL_WILL_LOCK, msg);
} }
String saTokenLoginId = UserTypeEnum.ADMIN_EMPLOYEE.getValue() + StringConst.COLON + employeeEntity.getEmployeeId(); String saTokenLoginId = UserTypeEnum.ADMIN_EMPLOYEE.getValue() + StringConst.COLON + employeeEntity.getEmployeeId();
// 登录 // 登录
StpUtil.login(saTokenLoginId, String.valueOf(loginDeviceEnum.getDesc())); StpUtil.login(saTokenLoginId, String.valueOf(loginDeviceEnum.getDesc()));
// 移除邮箱验证码
deleteEmailCode(employeeEntity.getEmployeeId());
} }
// 获取员工信息 // 获取员工信息
@@ -196,16 +227,17 @@ public class LoginService implements StpInterface {
loginEmployeeCache.put(employeeEntity.getEmployeeId(), requestEmployee); loginEmployeeCache.put(employeeEntity.getEmployeeId(), requestEmployee);
// 移除登录失败 // 移除登录失败
protectLoginService.removeLoginFail(employeeEntity.getEmployeeId(), UserTypeEnum.ADMIN_EMPLOYEE); securityLoginService.removeLoginFail(employeeEntity.getEmployeeId(), UserTypeEnum.ADMIN_EMPLOYEE);
// 获取登录结果信息 // 获取登录结果信息
LoginResultVO loginResultVO = getLoginResult(requestEmployee); String token = StpUtil.getTokenValue();
LoginResultVO loginResultVO = getLoginResult(requestEmployee, token);
//保存登录记录 //保存登录记录
saveLoginLog(employeeEntity, ip, userAgent, superPasswordFlag ? "万能密码登录" : loginDeviceEnum.getDesc(), LoginLogResultEnum.LOGIN_SUCCESS); saveLoginLog(employeeEntity, ip, userAgent, superPasswordFlag ? "万能密码登录" : loginDeviceEnum.getDesc(), LoginLogResultEnum.LOGIN_SUCCESS);
// 设置 token // 设置 token
loginResultVO.setToken(StpUtil.getTokenValue()); loginResultVO.setToken(token);
// 清除权限缓存 // 清除权限缓存
permissionCache.remove(employeeEntity.getEmployeeId()); permissionCache.remove(employeeEntity.getEmployeeId());
@@ -217,7 +249,7 @@ public class LoginService implements StpInterface {
/** /**
* 获取登录结果信息 * 获取登录结果信息
*/ */
public LoginResultVO getLoginResult(RequestEmployee requestEmployee) { public LoginResultVO getLoginResult(RequestEmployee requestEmployee, String token) {
// 基础信息 // 基础信息
LoginResultVO loginResultVO = SmartBeanUtil.copy(requestEmployee, LoginResultVO.class); LoginResultVO loginResultVO = SmartBeanUtil.copy(requestEmployee, LoginResultVO.class);
@@ -240,6 +272,16 @@ public class LoginService implements StpInterface {
loginResultVO.setLastLoginUserAgent(loginLogVO.getUserAgent()); loginResultVO.setLastLoginUserAgent(loginLogVO.getUserAgent());
} }
// 是否需要强制修改密码
boolean needChangePasswordFlag = protectPasswordService.checkNeedChangePassword(requestEmployee.getUserType().getValue(), requestEmployee.getUserId());
loginResultVO.setNeedUpdatePwdFlag(needChangePasswordFlag);
// 万能密码登录,则不需要设置强制修改密码
String loginIdByToken = (String) StpUtil.getLoginIdByToken(token);
if (loginIdByToken != null && loginIdByToken.startsWith(SUPER_PASSWORD_LOGIN_ID_PREFIX)) {
loginResultVO.setNeedUpdatePwdFlag(false);
}
return loginResultVO; return loginResultVO;
} }
@@ -259,9 +301,9 @@ public class LoginService implements StpInterface {
// 头像信息 // 头像信息
String avatar = employeeEntity.getAvatar(); String avatar = employeeEntity.getAvatar();
if(StringUtils.isNotBlank(avatar)){ if (StringUtils.isNotBlank(avatar)) {
ResponseDTO<String> getFileUrl = fileStorageService.getFileUrl(avatar); ResponseDTO<String> getFileUrl = fileStorageService.getFileUrl(avatar);
if(BooleanUtils.isTrue(getFileUrl.getOk())){ if (BooleanUtils.isTrue(getFileUrl.getOk())) {
requestEmployee.setAvatar(getFileUrl.getData()); requestEmployee.setAvatar(getFileUrl.getData());
} }
} }
@@ -357,9 +399,8 @@ public class LoginService implements StpInterface {
/** /**
* 清除员工登录缓存 * 清除员工登录缓存
* @param employeeId
*/ */
public void clearLoginEmployeeCache(Long employeeId){ public void clearLoginEmployeeCache(Long employeeId) {
// 清空登录信息缓存 // 清空登录信息缓存
loginEmployeeCache.remove(employeeId); loginEmployeeCache.remove(employeeId);
} }
@@ -451,4 +492,94 @@ public class LoginService implements StpInterface {
return userPermission; return userPermission;
} }
/**
* 发送 邮箱 验证码
*/
public ResponseDTO<String> sendEmailCode(String loginName) {
// 开启双因子登录
if (!level3ProtectConfigService.isTwoFactorLoginEnabled()) {
return ResponseDTO.userErrorParam("无需使用邮箱验证码");
}
// 验证登录名
EmployeeEntity employeeEntity = employeeService.getByLoginName(loginName);
if (null == employeeEntity) {
return ResponseDTO.userErrorParam("登录名不存在!");
}
// 验证账号状态
if (employeeEntity.getDisabledFlag()) {
return ResponseDTO.userErrorParam("您的账号已被禁用,请联系工作人员!");
}
String mail = employeeEntity.getEmail();
if (SmartStringUtil.isBlank(mail)) {
return ResponseDTO.userErrorParam("您暂未配置邮箱地址,请联系管理员配置邮箱");
}
// 校验验证码发送时间60秒内不能重复发生
String redisVerificationCodeKey = redisService.generateRedisKey(RedisKeyConst.Support.LOGIN_VERIFICATION_CODE, UserTypeEnum.ADMIN_EMPLOYEE.getValue() + RedisKeyConst.SEPARATOR + employeeEntity.getEmployeeId());
String emailCode = redisService.get(redisVerificationCodeKey);
long sendCodeTimeMills = -1;
if (!SmartStringUtil.isEmpty(emailCode)) {
sendCodeTimeMills = NumberUtil.parseLong(emailCode.split(StringConst.UNDERLINE)[1]);
}
if (System.currentTimeMillis() - sendCodeTimeMills < 60 * 1000) {
return ResponseDTO.userErrorParam("邮箱验证码已发送,一分钟内请勿重复发送");
}
//生成验证码
long currentTimeMillis = System.currentTimeMillis();
String verificationCode = RandomUtil.randomNumbers(4);
redisService.set(redisVerificationCodeKey, verificationCode + StringConst.UNDERLINE + currentTimeMillis, 300);
// 发送邮件验证码
HashMap<String, Object> mailParams = new HashMap<>();
mailParams.put("code", verificationCode);
return mailService.sendMail(MailTemplateCodeEnum.LOGIN_VERIFICATION_CODE, mailParams, Collections.singletonList(employeeEntity.getEmail()));
}
/**
* 校验邮箱验证码
*/
private ResponseDTO<String> validateEmailCode(LoginForm loginForm, EmployeeEntity employeeEntity, boolean superPasswordFlag) {
// 万能密码则不校验
if (superPasswordFlag) {
return ResponseDTO.ok();
}
// 未开启双因子登录
if (!level3ProtectConfigService.isTwoFactorLoginEnabled()) {
return ResponseDTO.ok();
}
if (SmartStringUtil.isEmpty(loginForm.getEmailCode())) {
return ResponseDTO.userErrorParam("请输入邮箱验证码");
}
// 校验验证码
String redisVerificationCodeKey = redisService.generateRedisKey(RedisKeyConst.Support.LOGIN_VERIFICATION_CODE, UserTypeEnum.ADMIN_EMPLOYEE.getValue() + RedisKeyConst.SEPARATOR + employeeEntity.getEmployeeId());
String emailCode = redisService.get(redisVerificationCodeKey);
if (SmartStringUtil.isEmpty(emailCode)) {
return ResponseDTO.userErrorParam("邮箱验证码已失效,请重新发送");
}
if (!emailCode.split(StringConst.UNDERLINE)[0].equals(loginForm.getEmailCode().trim())) {
return ResponseDTO.userErrorParam("邮箱验证码错误,请重新填写");
}
return ResponseDTO.ok();
}
/**
* 移除邮箱验证码
*/
private void deleteEmailCode(Long employeeId) {
String redisVerificationCodeKey = redisService.generateRedisKey(RedisKeyConst.Support.LOGIN_VERIFICATION_CODE, UserTypeEnum.ADMIN_EMPLOYEE.getValue() + RedisKeyConst.SEPARATOR + employeeId);
redisService.delete(redisVerificationCodeKey);
}
} }

View File

@@ -12,6 +12,7 @@ import net.lab1024.sa.admin.module.system.role.domain.form.RoleEmployeeQueryForm
import net.lab1024.sa.admin.module.system.role.domain.vo.RoleEmployeeVO; import net.lab1024.sa.admin.module.system.role.domain.vo.RoleEmployeeVO;
import java.util.List; import java.util.List;
import java.util.Set;
/** /**
@@ -50,7 +51,7 @@ public interface RoleEmployeeDao extends BaseMapper<RoleEmployeeEntity> {
/** /**
* 查询角色下的人员id * 查询角色下的人员id
*/ */
List<Long> selectEmployeeIdByRoleIdList(@Param("roleIdList") List<Long> roleIdList); Set<Long> selectEmployeeIdByRoleIdList(@Param("roleIdList") List<Long> roleIdList);
/** /**
* *
@@ -79,5 +80,10 @@ public interface RoleEmployeeDao extends BaseMapper<RoleEmployeeEntity> {
/** /**
* 批量删除某个角色下的某批用户的关联关系 * 批量删除某个角色下的某批用户的关联关系
*/ */
void batchDeleteEmployeeRole(@Param("roleId") Long roleId,@Param("employeeIds")List<Long> employeeIds); void batchDeleteEmployeeRole(@Param("roleId") Long roleId, @Param("employeeIds") Set<Long> employeeIds);
/**
* 判断某个角色下是否存在用户
*/
Integer existsByRoleId(@Param("roleId") Long roleId);
} }

View File

@@ -5,7 +5,7 @@ import lombok.Data;
import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.util.List; import java.util.Set;
/** /**
* 角色的员工更新 * 角色的员工更新
@@ -25,6 +25,6 @@ public class RoleEmployeeUpdateForm {
@Schema(description = "员工id集合") @Schema(description = "员工id集合")
@NotEmpty(message = "员工id不能为空") @NotEmpty(message = "员工id不能为空")
protected List<Long> employeeIdList; protected Set<Long> employeeIdList;
} }

View File

@@ -3,11 +3,7 @@ package net.lab1024.sa.admin.module.system.role.manager;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.lab1024.sa.admin.module.system.role.dao.RoleEmployeeDao; import net.lab1024.sa.admin.module.system.role.dao.RoleEmployeeDao;
import net.lab1024.sa.admin.module.system.role.domain.entity.RoleEmployeeEntity; import net.lab1024.sa.admin.module.system.role.domain.entity.RoleEmployeeEntity;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/** /**
* 角色员工 manager * 角色员工 manager
@@ -16,20 +12,9 @@ import java.util.List;
* @Date 2022-04-08 21:53:04 * @Date 2022-04-08 21:53:04
* @Wechat zhuoda1024 * @Wechat zhuoda1024
* @Email lab1024@163.com * @Email lab1024@163.com
* @Copyright <a href="https://1024lab.net">1024创新实验室</a> * @Copyright <a href="https://1024lab.net">1024创新实验室</a>
*/ */
@Service @Service
public class RoleEmployeeManager extends ServiceImpl<RoleEmployeeDao, RoleEmployeeEntity> { public class RoleEmployeeManager extends ServiceImpl<RoleEmployeeDao, RoleEmployeeEntity> {
/**
* 保存 角色员工
*
*/
@Transactional(rollbackFor = Throwable.class)
public void saveRoleEmployee(Long roleId, List<RoleEmployeeEntity> roleEmployeeList) {
this.getBaseMapper().deleteByRoleId(roleId);
if (CollectionUtils.isNotEmpty(roleEmployeeList)) {
this.saveBatch(roleEmployeeList);
}
}
} }

View File

@@ -1,6 +1,7 @@
package net.lab1024.sa.admin.module.system.role.service; package net.lab1024.sa.admin.module.system.role.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import net.lab1024.sa.admin.module.system.department.dao.DepartmentDao; import net.lab1024.sa.admin.module.system.department.dao.DepartmentDao;
import net.lab1024.sa.admin.module.system.department.domain.entity.DepartmentEntity; import net.lab1024.sa.admin.module.system.department.domain.entity.DepartmentEntity;
import net.lab1024.sa.admin.module.system.employee.domain.vo.EmployeeVO; import net.lab1024.sa.admin.module.system.employee.domain.vo.EmployeeVO;
@@ -20,12 +21,12 @@ import net.lab1024.sa.base.common.util.SmartBeanUtil;
import net.lab1024.sa.base.common.util.SmartPageUtil; import net.lab1024.sa.base.common.util.SmartPageUtil;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@@ -88,7 +89,6 @@ public class RoleEmployeeService {
* 移除员工角色 * 移除员工角色
* *
*/ */
@Transactional(rollbackFor = Exception.class)
public ResponseDTO<String> removeRoleEmployee(Long employeeId, Long roleId) { public ResponseDTO<String> removeRoleEmployee(Long employeeId, Long roleId) {
if (null == employeeId || null == roleId) { if (null == employeeId || null == roleId) {
return ResponseDTO.userErrorParam(); return ResponseDTO.userErrorParam();
@@ -112,16 +112,21 @@ public class RoleEmployeeService {
*/ */
public ResponseDTO<String> batchAddRoleEmployee(RoleEmployeeUpdateForm roleEmployeeUpdateForm) { public ResponseDTO<String> batchAddRoleEmployee(RoleEmployeeUpdateForm roleEmployeeUpdateForm) {
Long roleId = roleEmployeeUpdateForm.getRoleId(); Long roleId = roleEmployeeUpdateForm.getRoleId();
List<Long> employeeIdList = roleEmployeeUpdateForm.getEmployeeIdList();
// 保存新的角色员工 // 已选择的员工id列表
List<RoleEmployeeEntity> roleEmployeeList = null; Set<Long> selectedEmployeeIdList = roleEmployeeUpdateForm.getEmployeeIdList();
if (CollectionUtils.isNotEmpty(employeeIdList)) { // 数据库里已有的员工id列表
roleEmployeeList = employeeIdList.stream() Set<Long> dbEmployeeIdList = roleEmployeeDao.selectEmployeeIdByRoleIdList(Lists.newArrayList(roleId));
// 从已选择的员工id列表里 过滤数据库里不存在的 即需要添加的员工 id
Set<Long> addEmployeeIdList = selectedEmployeeIdList.stream().filter(id -> !dbEmployeeIdList.contains(id)).collect(Collectors.toSet());
// 添加角色员工
if (CollectionUtils.isNotEmpty(addEmployeeIdList)) {
List<RoleEmployeeEntity> roleEmployeeList = addEmployeeIdList.stream()
.map(employeeId -> new RoleEmployeeEntity(roleId, employeeId)) .map(employeeId -> new RoleEmployeeEntity(roleId, employeeId))
.collect(Collectors.toList()); .collect(Collectors.toList());
roleEmployeeManager.saveBatch(roleEmployeeList);
} }
// 保存数据
roleEmployeeManager.saveRoleEmployee(roleId, roleEmployeeList);
return ResponseDTO.ok(); return ResponseDTO.ok();
} }

View File

@@ -65,6 +65,11 @@ public class RoleService {
if (null == roleEntity) { if (null == roleEntity) {
return ResponseDTO.error(UserErrorCode.DATA_NOT_EXIST); return ResponseDTO.error(UserErrorCode.DATA_NOT_EXIST);
} }
// 当没有员工绑定这个角色时才可以删除
Integer exists = roleEmployeeDao.existsByRoleId(roleId);
if (exists != null) {
return ResponseDTO.error(UserErrorCode.ALREADY_EXIST, "该角色下存在员工,无法删除");
}
roleDao.deleteById(roleId); roleDao.deleteById(roleId);
roleMenuDao.deleteByRoleId(roleId); roleMenuDao.deleteByRoleId(roleId);
roleEmployeeDao.deleteByRoleId(roleId); roleEmployeeDao.deleteByRoleId(roleId);
@@ -86,7 +91,7 @@ public class RoleService {
} }
existRoleEntity = roleDao.getByRoleCode(roleUpdateForm.getRoleCode()); existRoleEntity = roleDao.getByRoleCode(roleUpdateForm.getRoleCode());
if (null != existRoleEntity) { if (null != existRoleEntity && !existRoleEntity.getRoleId().equals(roleUpdateForm.getRoleId())) {
return ResponseDTO.userErrorParam("角色编码重复,重复的角色为:" + existRoleEntity.getRoleName()); return ResponseDTO.userErrorParam("角色编码重复,重复的角色为:" + existRoleEntity.getRoleName());
} }

View File

@@ -0,0 +1,88 @@
package net.lab1024.sa.admin.module.system.support;
import cn.hutool.core.util.RandomUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.Data;
import net.lab1024.sa.base.common.controller.SupportBaseController;
import net.lab1024.sa.base.common.domain.ResponseDTO;
import net.lab1024.sa.base.constant.SwaggerTagConst;
import net.lab1024.sa.base.module.support.datamasking.DataMasking;
import net.lab1024.sa.base.module.support.datamasking.DataMaskingTypeEnum;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
* 数据脱敏demo
*
* @Author 1024创新实验室-主任:卓大
* @Date 2024/08/01 22:07:27
* @Wechat zhuoda1024
* @Email lab1024@163.com
* @Copyright <a href="https://1024lab.net">1024创新实验室</a>Since 2012
*/
@RestController
@Tag(name = SwaggerTagConst.Support.DATA_MASKING)
public class AdminDataMaskingDemoController extends SupportBaseController {
@Operation(summary = "数据脱敏demo @author 1024创新实验室-主任-卓大")
@GetMapping("/dataMasking/demo/query")
public ResponseDTO<List<DataVO>> query() {
List<DataVO> list = new ArrayList<>();
for (int i = 0; i < RandomUtil.randomInt(10,16); i++) {
DataVO data = new DataVO();
data.setUserId(RandomUtil.randomLong(1328479238, 83274298347982L));
data.setPhone("1" + RandomUtil.randomNumbers(10));
data.setIdCard("410" + RandomUtil.randomNumbers(3) + RandomUtil.randomInt(1980, 2010) + RandomUtil.randomInt(10, 12) + RandomUtil.randomInt(10, 30) + RandomUtil.randomNumbers(4));
data.setAddress(RandomUtil.randomBoolean() ? "河南省洛阳市洛龙区一零二四大街1024号" : "河南省郑州市高新区六边形大街六边形大楼");
data.setPassword(RandomUtil.randomString(10));
data.setEmail(RandomUtil.randomString(RandomUtil.randomInt(6, 10)) + "@" + RandomUtil.randomString(2) + ".com");
data.setCarLicense("" + RandomStringUtils.randomAlphabetic(1).toUpperCase()+" " + RandomStringUtils.randomAlphanumeric(5).toUpperCase());
data.setBankCard("6225" + RandomStringUtils.randomNumeric(14));
data.setOther(RandomStringUtils.randomAlphanumeric(1, 12));
list.add(data);
}
return ResponseDTO.ok(list);
}
@Data
public static class DataVO {
@DataMasking(DataMaskingTypeEnum.USER_ID)
private Long userId;
@DataMasking(DataMaskingTypeEnum.PHONE)
private String phone;
@DataMasking(DataMaskingTypeEnum.ID_CARD)
private String idCard;
@DataMasking(DataMaskingTypeEnum.ADDRESS)
private String address;
@DataMasking(DataMaskingTypeEnum.PASSWORD)
private String password;
@DataMasking(DataMaskingTypeEnum.EMAIL)
private String email;
@DataMasking(DataMaskingTypeEnum.CAR_LICENSE)
private String carLicense;
@DataMasking(DataMaskingTypeEnum.BANK_CARD)
private String bankCard;
@DataMasking
private String other;
}
}

View File

@@ -1,15 +1,20 @@
package net.lab1024.sa.admin.module.system.support; package net.lab1024.sa.admin.module.system.support;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import net.lab1024.sa.base.common.controller.SupportBaseController; import net.lab1024.sa.base.common.controller.SupportBaseController;
import net.lab1024.sa.base.common.domain.PageResult; import net.lab1024.sa.base.common.domain.PageResult;
import net.lab1024.sa.base.common.domain.ResponseDTO; import net.lab1024.sa.base.common.domain.ResponseDTO;
import net.lab1024.sa.base.common.domain.ValidateList; import net.lab1024.sa.base.common.domain.ValidateList;
import net.lab1024.sa.base.constant.SwaggerTagConst; import net.lab1024.sa.base.constant.SwaggerTagConst;
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 net.lab1024.sa.base.module.support.securityprotect.domain.LoginFailQueryForm; import net.lab1024.sa.base.module.support.securityprotect.domain.LoginFailQueryForm;
import net.lab1024.sa.base.module.support.securityprotect.domain.LoginFailVO; import net.lab1024.sa.base.module.support.securityprotect.domain.LoginFailVO;
import net.lab1024.sa.base.module.support.securityprotect.service.ProtectLoginService; import net.lab1024.sa.base.module.support.securityprotect.service.Level3ProtectConfigService;
import net.lab1024.sa.base.module.support.securityprotect.service.SecurityLoginService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@@ -18,14 +23,13 @@ import javax.annotation.Resource;
import javax.validation.Valid; import javax.validation.Valid;
/** /**
*
* 网络安全 * 网络安全
* *
* @Author 1024创新实验室-主任:卓大 * @Author 1024创新实验室-主任:卓大
* @Date 2023/10/17 19:07:27 * @Date 2023/10/17 19:07:27
* @Wechat zhuoda1024 * @Wechat zhuoda1024
* @Email lab1024@163.com * @Email lab1024@163.com
* @Copyright <a href="https://1024lab.net">1024创新实验室</a>Since 2012 * @Copyright <a href="https://1024lab.net">1024创新实验室</a>Since 2012
*/ */
@RestController @RestController
@@ -33,20 +37,37 @@ import javax.validation.Valid;
public class AdminProtectController extends SupportBaseController { public class AdminProtectController extends SupportBaseController {
@Resource @Resource
private ProtectLoginService protectLoginService; private SecurityLoginService securityLoginService;
@Resource
private Level3ProtectConfigService level3ProtectConfigService;
@Resource
private ConfigService configService;
@Operation(summary = "分页查询 @author 1024创新实验室-主任-卓大") @Operation(summary = "分页查询 @author 1024创新实验室-主任-卓大")
@PostMapping("/protect/loginFail/queryPage") @PostMapping("/protect/loginFail/queryPage")
public ResponseDTO<PageResult<LoginFailVO>> queryPage(@RequestBody @Valid LoginFailQueryForm queryForm) { public ResponseDTO<PageResult<LoginFailVO>> queryPage(@RequestBody @Valid LoginFailQueryForm queryForm) {
return ResponseDTO.ok(protectLoginService.queryPage(queryForm)); return ResponseDTO.ok(securityLoginService.queryPage(queryForm));
} }
@Operation(summary = "批量删除 @author 1024创新实验室-主任-卓大") @Operation(summary = "批量删除 @author 1024创新实验室-主任-卓大")
@PostMapping("/protect/loginFail/batchDelete") @PostMapping("/protect/loginFail/batchDelete")
public ResponseDTO<String> batchDelete(@RequestBody ValidateList<Long> idList) { public ResponseDTO<String> batchDelete(@RequestBody ValidateList<Long> idList) {
return protectLoginService.batchDelete(idList); return securityLoginService.batchDelete(idList);
} }
@Operation(summary = "更新三级等保配置 @author 1024创新实验室-主任-卓大")
@PostMapping("/protect/level3protect/updateConfig")
public ResponseDTO<String> updateConfig(@RequestBody @Valid Level3ProtectConfigForm configForm) {
return level3ProtectConfigService.updateLevel3Config(configForm);
}
@Operation(summary = "查询 三级等保配置 @author 1024创新实验室-主任-卓大")
@GetMapping("/protect/level3protect/getConfig")
public ResponseDTO<String> getConfig() {
return ResponseDTO.ok(configService.getConfigValue(ConfigKeyEnum.LEVEL3_PROTECT_CONFIG));
}
} }

View File

@@ -19,27 +19,4 @@ server:
# 环境 # 环境
spring: spring:
profiles: profiles:
active: '@profiles.active@' active: '@profiles.active@'
####################################### 安全等级保护 相关配置 ##################################################
# #
# 建议开启 "三级等保" 所要求的配置,具体如下: #
# 1连续登录失败 5 次锁定账户 30 分钟, #
# 2登录超时时长建议为 30分钟超过此时间没有访问系统会重新要求登录 #
# 3密码复杂度至少三种字符最小 8 位 #
# #
#############################################################################################################
classified-protect:
# 连续登录失败次数则锁定,-1表示不受限制可以一直尝试登录
login-max-fail-times: 5
# 连续登录失败锁定时间(单位:秒),-1表示不锁定建议锁定30分钟
login-fail-locked-seconds: 1800
# 密码复杂度开启(默认复杂度为:至少三种字符,最小 8 位), true 开启false 不开启,建议开启
password-complexity-enabled: true
sa-token:
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
active-timeout: 1800
# token 有效期(单位:秒) 1天86400秒-1 代表永久有效
timeout: 86400

View File

@@ -18,7 +18,7 @@
INSTR(goods_name,#{query.searchWord}) INSTR(goods_name,#{query.searchWord})
</if> </if>
<if test="query.place != null"> <if test="query.place != null">
AND place = #{query.place} AND INSTR(place,#{query.place})
</if> </if>
<if test="query.goodsStatus != null"> <if test="query.goodsStatus != null">
AND goods_status = #{query.goodsStatus} AND goods_status = #{query.goodsStatus}

View File

@@ -1,69 +0,0 @@
<?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.admin.module.business.notice.NoticeDao">
<update id="updateWatchAmount">
UPDATE t_notice
SET watch_amount = watch_amount + 1
WHERE notice_id = #{noticeId}
</update>
<update id="batchDeleteNotice">
UPDATE t_notice
SET deleted_flag = #{deletedFlag}
WHERE notice_id IN
<foreach collection="noticeIdList" open="(" close=")" separator="," item="item">
#{item}
</foreach>
</update>
<update id="updateNotice">
UPDATE t_notice
SET notice_type = #{noticeType},
notice_belong_type = #{noticeBelongType},
notice_title = #{noticeTitle},
notice_content = #{noticeContent},
link_address = #{linkAddress},
cover_file_key = #{coverFileKey},
accessory_file_keys = #{accessoryFileKeys},
top_flag = #{topFlag},
publish_time = #{publishTime},
disabled_flag = #{disabledFlag}
WHERE notice_id = #{noticeId}
</update>
<select id="queryPage" resultType="net.lab1024.sa.admin.module.business.oa.notice.domain.vo.NoticeVO">
SELECT t_notice.*,
t_employee.actual_name AS createUserName
FROM t_notice
LEFT JOIN t_employee ON t_notice.create_user_id = t_employee.employee_id
<where>
t_notice.deleted_flag = #{queryForm.deletedFlag}
<if test="queryForm.keywords != null and queryForm.keywords != ''">
AND (INSTR(t_notice.notice_title,#{queryForm.keywords}) OR INSTR(t_employee.actual_name,#{queryForm.keywords}))
</if>
<if test="queryForm.noticeType != null">
AND t_notice.notice_type = #{queryForm.noticeType}
</if>
<if test="queryForm.noticeBelongType != null">
AND t_notice.notice_belong_type = #{queryForm.noticeBelongType}
</if>
<if test="queryForm.startTime != null">
AND DATE_FORMAT(t_notice.publish_time, '%Y-%m-%d') &gt;= #{queryForm.startTime}
</if>
<if test="queryForm.endTime != null">
AND DATE_FORMAT(t_notice.publish_time, '%Y-%m-%d') &lt;= #{queryForm.endTime}
</if>
<if test="queryForm.disabledFlag != null">
AND t_notice.disabled_flag = #{queryForm.disabledFlag}
</if>
</where>
<if test="queryForm.sortItemList == null or queryForm.sortItemList.size == 0">
ORDER BY t_notice.top_flag DESC,t_notice.publish_time DESC
</if>
</select>
<select id="getDetail" resultType="net.lab1024.sa.admin.module.business.oa.notice.domain.vo.NoticeVO">
SELECT tn.*,
e.actual_name AS createUserName
FROM t_notice tn
LEFT JOIN t_employee e ON tn.create_user_id = e.employee_id
WHERE tn.notice_id = #{noticeId}
AND tn.deleted_flag = #{deletedFlag}
</select>
</mapper>

View File

@@ -242,5 +242,11 @@
where notice_id = #{noticeId} where notice_id = #{noticeId}
and employee_id = #{employeeId} and employee_id = #{employeeId}
</update> </update>
<update id="updateViewCount">
update t_notice
set page_view_count = page_view_count + #{pageViewCountIncrement},
user_view_count = user_view_count + #{userViewCountIncrement}
where notice_id = #{noticeId}
</update>
</mapper> </mapper>

View File

@@ -85,7 +85,7 @@
<select id="countByDepartmentId" resultType="integer"> <select id="countByDepartmentId" resultType="integer">
SELECT count(1) FROM t_employee SELECT count(1) FROM t_employee
WHERE WHERE
department_id = #{departmentId} department_id = #{departmentId} AND deleted_flag = #{deletedFlag}
</select> </select>

View File

@@ -138,4 +138,12 @@
#{item} #{item}
</foreach> </foreach>
</delete> </delete>
<select id="existsByRoleId" resultType="java.lang.Integer">
SELECT 1
FROM t_role_employee er
WHERE er.role_id = #{roleId}
LIMIT 1
</select>
</mapper> </mapper>

View File

@@ -19,27 +19,4 @@ server:
# 环境 # 环境
spring: spring:
profiles: profiles:
active: '@profiles.active@' active: '@profiles.active@'
####################################### 安全等级保护 相关配置 ##################################################
# #
# 建议开启 "三级等保" 所要求的配置,具体如下: #
# 1连续登录失败 5 次锁定账户 30 分钟, #
# 2登录超时时长建议为 30分钟超过此时间没有访问系统会重新要求登录 #
# 3密码复杂度至少三种字符最小 8 位 #
# #
#############################################################################################################
classified-protect:
# 连续登录失败次数则锁定,-1表示不受限制可以一直尝试登录
login-max-fail-times: 5
# 连续登录失败锁定时间(单位:秒),-1表示不锁定建议锁定30分钟
login-fail-locked-seconds: 1800
# 密码复杂度开启(默认复杂度为:至少三种字符,最小 8 位), true 开启false 不开启,建议开启
password-complexity-enabled: true
sa-token:
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
active-timeout: 1800
# token 有效期(单位:秒) 1天86400秒-1 代表永久有效
timeout: 86400

View File

@@ -19,27 +19,4 @@ server:
# 环境 # 环境
spring: spring:
profiles: profiles:
active: '@profiles.active@' active: '@profiles.active@'
####################################### 安全等级保护 相关配置 ##################################################
# #
# 建议开启 "三级等保" 所要求的配置,具体如下: #
# 1连续登录失败 5 次锁定账户 30 分钟, #
# 2登录超时时长建议为 30分钟超过此时间没有访问系统会重新要求登录 #
# 3密码复杂度至少三种字符最小 8 位 #
# #
#############################################################################################################
classified-protect:
# 连续登录失败次数则锁定,-1表示不受限制可以一直尝试登录
login-max-fail-times: 5
# 连续登录失败锁定时间(单位:秒),-1表示不锁定建议锁定30分钟
login-fail-locked-seconds: 1800
# 密码复杂度开启(默认复杂度为:至少三种字符,最小 8 位), true 开启false 不开启,建议开启
password-complexity-enabled: true
sa-token:
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
active-timeout: 1800
# token 有效期(单位:秒) 1天86400秒-1 代表永久有效
timeout: 86400

View File

@@ -8,7 +8,7 @@
# 项目配置: 名称、日志目录 # 项目配置: 名称、日志目录
project: project:
name: sa-admin name: sa-admin
log-directory: /home/project/smartadmin/sit/log log-directory: /home/project/smartadmin/test/log
# 项目端口和url根路径 # 项目端口和url根路径
server: server:
@@ -19,27 +19,4 @@ server:
# 环境 # 环境
spring: spring:
profiles: profiles:
active: '@profiles.active@' active: '@profiles.active@'
####################################### 安全等级保护 相关配置 ##################################################
# #
# 建议开启 "三级等保" 所要求的配置,具体如下: #
# 1连续登录失败 5 次锁定账户 30 分钟, #
# 2登录超时时长建议为 30分钟超过此时间没有访问系统会重新要求登录 #
# 3密码复杂度至少三种字符最小 8 位 #
# #
#############################################################################################################
classified-protect:
# 连续登录失败次数则锁定,-1表示不受限制可以一直尝试登录
login-max-fail-times: 5
# 连续登录失败锁定时间(单位:秒),-1表示不锁定建议锁定30分钟
login-fail-locked-seconds: 1800
# 密码复杂度开启(默认复杂度为:至少三种字符,最小 8 位), true 开启false 不开启,建议开启
password-complexity-enabled: true
sa-token:
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
active-timeout: 1800
# token 有效期(单位:秒) 1天86400秒-1 代表永久有效
timeout: 86400

View File

@@ -88,8 +88,8 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>com.mysql</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>mysql-connector-j</artifactId>
</dependency> </dependency>
<dependency> <dependency>
@@ -263,6 +263,11 @@
<artifactId>jackson-datatype-jsr310</artifactId> <artifactId>jackson-datatype-jsr310</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
</dependency>
<dependency> <dependency>
<groupId>net.1024lab</groupId> <groupId>net.1024lab</groupId>
<artifactId>smartdb</artifactId> <artifactId>smartdb</artifactId>
@@ -278,6 +283,27 @@
<artifactId>redisson-spring-data-27</artifactId> <artifactId>redisson-spring-data-27</artifactId>
</dependency> </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> </dependencies>

View File

@@ -28,19 +28,19 @@ public class DictValueVoDeserializer extends JsonDeserializer<String> {
@Override @Override
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
List<DictValueVO> list = new ArrayList<>(); List<String> list = new ArrayList<>();
ObjectCodec objectCodec = jsonParser.getCodec(); ObjectCodec objectCodec = jsonParser.getCodec();
JsonNode listOrObjectNode = objectCodec.readTree(jsonParser); JsonNode listOrObjectNode = objectCodec.readTree(jsonParser);
String deserialize = ""; String deserialize = "";
try { try {
if (listOrObjectNode.isArray()) { if (listOrObjectNode.isArray()) {
for (JsonNode node : listOrObjectNode) { for (JsonNode node : listOrObjectNode) {
list.add(objectCodec.treeToValue(node, DictValueVO.class)); list.add(node.asText());
} }
} else { } 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) { } catch (Exception e) {
log.error(e.getMessage(), e); log.error(e.getMessage(), e);
deserialize = listOrObjectNode.asText(); deserialize = listOrObjectNode.asText();

View File

@@ -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;
}
}

View File

@@ -12,7 +12,6 @@ import org.apache.commons.collections4.CollectionUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
/** /**
* 分页工具类 * 分页工具类
@@ -32,6 +31,10 @@ public class SmartPageUtil {
public static Page<?> convert2PageQuery(PageParam pageParam) { public static Page<?> convert2PageQuery(PageParam pageParam) {
Page<?> page = new Page<>(pageParam.getPageNum(), pageParam.getPageSize()); Page<?> page = new Page<>(pageParam.getPageNum(), pageParam.getPageSize());
if (pageParam.getSearchCount() != null) {
page.setSearchCount(pageParam.getSearchCount());
}
List<PageParam.SortItem> sortItemList = pageParam.getSortItemList(); List<PageParam.SortItem> sortItemList = pageParam.getSortItemList();
if (CollectionUtils.isEmpty(sortItemList)) { if (CollectionUtils.isEmpty(sortItemList)) {
return page; return page;

View File

@@ -7,10 +7,12 @@ import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter; import com.alibaba.druid.support.http.WebStatFilter;
import com.alibaba.druid.support.spring.stat.DruidStatInterceptor; import com.alibaba.druid.support.spring.stat.DruidStatInterceptor;
import com.baomidou.mybatisplus.annotation.DbType; 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.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.lab1024.sa.base.common.domain.DataScopePlugin; 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.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor; import org.springframework.aop.support.DefaultPointcutAdvisor;
@@ -144,6 +146,8 @@ public class DataSourceConfig {
pluginsList.add(dataScopePlugin); pluginsList.add(dataScopePlugin);
} }
factoryBean.setPlugins(pluginsList.toArray(new Interceptor[pluginsList.size()])); factoryBean.setPlugins(pluginsList.toArray(new Interceptor[pluginsList.size()]));
// 添加字段自动填充处理
factoryBean.setGlobalConfig(new GlobalConfig().setBanner(false).setMetaObjectHandler(new MybatisPlusFillHandler()));
return factoryBean.getObject(); return factoryBean.getObject();
} }

View File

@@ -6,6 +6,7 @@ import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@@ -43,7 +44,8 @@ public class RedisConfig {
.setSerializationInclusion(JsonInclude.Include.NON_NULL); .setSerializationInclusion(JsonInclude.Include.NON_NULL);
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); 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); jackson2JsonRedisSerializer.setObjectMapper(om);
RedisTemplate<String, Object> template = new RedisTemplate<>(); RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory); template.setConnectionFactory(factory);

View File

@@ -28,7 +28,7 @@ import java.util.Optional;
* springdoc-openapi 配置 * springdoc-openapi 配置
* nginx配置前缀时如果需要访问【/swagger-ui/index.html】需添加额外nginx配置 * nginx配置前缀时如果需要访问【/swagger-ui/index.html】需添加额外nginx配置
* location /v3/api-docs/ { * 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创新实验室-主任: 卓大 * @Author 1024创新实验室-主任: 卓大
* @Date 2020-03-25 22:54:46 * @Date 2020-03-25 22:54:46
@@ -43,7 +43,7 @@ public class SwaggerConfig {
/** /**
* 用于解决/swagger-ui/index.html页面ServersUrl 测试环境部署错误问题 * 用于解决/swagger-ui/index.html页面ServersUrl 测试环境部署错误问题
*/ */
@Value("${springdoc.swagger-ui.server-base-url:''}") @Value("${springdoc.swagger-ui.server-base-url}")
private String serverBaseUrl; private String serverBaseUrl;
public static final String[] SWAGGER_WHITELIST = { public static final String[] SWAGGER_WHITELIST = {

View File

@@ -23,5 +23,7 @@ public class RedisKeyConst {
public static final String CAPTCHA = "captcha:"; public static final String CAPTCHA = "captcha:";
public static final String LOGIN_VERIFICATION_CODE = "login:verification-code:";
} }
} }

View File

@@ -49,6 +49,8 @@ public class SwaggerTagConst {
public static final String PROTECT = "业务支撑-网络安全"; public static final String PROTECT = "业务支撑-网络安全";
public static final String DATA_MASKING = "业务支撑-数据脱敏";
public static final String JOB = "业务支撑-定时任务"; public static final String JOB = "业务支撑-定时任务";
public static final String MESSAGE = "业务支撑-消息"; public static final String MESSAGE = "业务支撑-消息";

View File

@@ -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());
}
}
}

View File

@@ -42,8 +42,10 @@ public class CaptchaService {
@Resource @Resource
private DefaultKaptcha defaultKaptcha; private DefaultKaptcha defaultKaptcha;
@Autowired
@Resource
private SystemEnvironment systemEnvironment; private SystemEnvironment systemEnvironment;
@Resource @Resource
private RedisService redisService; private RedisService redisService;

View File

@@ -41,7 +41,7 @@ public enum CodeFrontComponentEnum implements BaseEnum {
} }
@Override @Override
public Object getValue() { public String getValue() {
return value; return value;
} }

View File

@@ -32,7 +32,6 @@ public class CodeGeneratorConfigForm {
@Schema(description = "表名") @Schema(description = "表名")
private String tableName; private String tableName;
@Valid @Valid
@NotNull(message = "基础信息不能为空") @NotNull(message = "基础信息不能为空")
@Schema(description = "基础信息") @Schema(description = "基础信息")

View File

@@ -39,8 +39,8 @@ public class CodeInsertAndUpdateField {
@Schema(description = "更新标识") @Schema(description = "更新标识")
private Boolean updateFlag; private Boolean updateFlag;
@SchemaEnum(value = CodeGeneratorPageTypeEnum.class) @SchemaEnum(value = CodeFrontComponentEnum.class)
@CheckEnum(value = CodeFrontComponentEnum.class, message = "3.增加、修改 增加、修改 组件类型 枚举值错误", required = true) @CheckEnum(value = CodeFrontComponentEnum.class, message = "3.增加、修改 组件类型 枚举值错误", required = true)
private String frontComponent; private String frontComponent;
} }

View File

@@ -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.entity.CodeGeneratorConfigEntity;
import net.lab1024.sa.base.module.support.codegenerator.domain.form.CodeGeneratorConfigForm; 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.domain.model.*;
import net.lab1024.sa.base.module.support.codegenerator.service.variable.backend.ControllerVariableService; import net.lab1024.sa.base.module.support.codegenerator.service.variable.CodeGenerateBaseVariableService;
import net.lab1024.sa.base.module.support.codegenerator.service.variable.backend.DaoVariableService; import net.lab1024.sa.base.module.support.codegenerator.service.variable.backend.*;
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.backend.domain.*; 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.ApiVariableService;
import net.lab1024.sa.base.module.support.codegenerator.service.variable.front.ConstVariableService; 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.FormVariableService;
import net.lab1024.sa.base.module.support.codegenerator.service.variable.front.ListVariableService; 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 net.lab1024.sa.base.module.support.codegenerator.util.CodeGeneratorTool;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.velocity.Template; import org.apache.velocity.Template;
@@ -36,7 +33,7 @@ import javax.annotation.PostConstruct;
import java.io.File; import java.io.File;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.StringWriter; import java.io.StringWriter;
import java.nio.charset.Charset; import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -47,7 +44,7 @@ import java.util.stream.Collectors;
* @Date 2022-06-30 22:15:38 * @Date 2022-06-30 22:15:38
* @Wechat zhuoda1024 * @Wechat zhuoda1024
* @Email lab1024@163.com * @Email lab1024@163.com
* @Copyright <a href="https://1024lab.net">1024创新实验室</a> * @Copyright <a href="https://1024lab.net">1024创新实验室</a>
*/ */
@Service @Service
@@ -70,6 +67,8 @@ public class CodeGeneratorTemplateService {
map.put("java/manager/Manager.java", new ManagerVariableService()); map.put("java/manager/Manager.java", new ManagerVariableService());
map.put("java/dao/Dao.java", new DaoVariableService()); map.put("java/dao/Dao.java", new DaoVariableService());
map.put("java/mapper/Mapper.xml", new MapperVariableService()); 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/api.js", new ApiVariableService());
map.put("js/const.js", new ConstVariableService()); 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 fileName = templateFile.startsWith("java") ? upperCamel + templateSplit[templateSplit.length - 1] : lowerHyphen + "-" + templateSplit[templateSplit.length - 1];
String fullPathFileName = templateFile.replaceAll(templateSplit[templateSplit.length - 1], fileName); String fullPathFileName = templateFile.replaceAll(templateSplit[templateSplit.length - 1], fileName);
fullPathFileName = fullPathFileName.replaceAll("java/", "java/" + basic.getModuleName().toLowerCase() + "/"); fullPathFileName = fullPathFileName.replaceAll("java/", "java/" + basic.getModuleName().toLowerCase() + "/");
fullPathFileName = fullPathFileName.replaceAll("js/", "js/" + lowerHyphen + "/");
String fileContent = generate(tableName, templateFile, codeGeneratorConfigEntity); String fileContent = generate(tableName, templateFile, codeGeneratorConfigEntity);
File file = new File(uuid + "/" + fullPathFileName); 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); FileUtil.del(dir);

View File

@@ -2,6 +2,7 @@ package net.lab1024.sa.base.module.support.codegenerator.service.variable;
import com.google.common.base.CaseFormat; import com.google.common.base.CaseFormat;
import net.lab1024.sa.base.common.util.SmartStringUtil; 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.form.CodeGeneratorConfigForm;
import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeField; import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeField;
import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeInsertAndUpdate; 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 * @Date 2022/9/29 17:20:41
* @Wechat zhuoda1024 * @Wechat zhuoda1024
* @Email lab1024@163.com * @Email lab1024@163.com
* @Copyright <a href="https://1024lab.net">1024创新实验室</a> * @Copyright <a href="https://1024lab.net">1024创新实验室</a>
*/ */
public abstract class CodeGenerateBaseVariableService { public abstract class CodeGenerateBaseVariableService {
@@ -43,13 +44,13 @@ public abstract class CodeGenerateBaseVariableService {
String upperCamelName = CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_CAMEL, form.getBasic().getModuleName()); String upperCamelName = CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_CAMEL, form.getBasic().getModuleName());
ArrayList<String> list = new ArrayList<>(); 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 + "AddForm;");
list.add("import " + form.getBasic().getJavaPackageName() + ".domain.form." + upperCamelName + "UpdateForm;" ); 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 + "QueryForm;");
list.add("import " + form.getBasic().getJavaPackageName() + ".domain.vo." + upperCamelName + "VO;" ); list.add("import " + form.getBasic().getJavaPackageName() + ".domain.vo." + upperCamelName + "VO;");
return list; return list;
} }
@@ -88,43 +89,33 @@ public abstract class CodeGenerateBaseVariableService {
} }
CodeInsertAndUpdateField field = first.get(); 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) { protected boolean isDict(String columnName, CodeGeneratorConfigForm form) {
List<CodeField> fields = form.getFields(); CodeField codeField = getCodeField(columnName, form);
if (CollectionUtils.isEmpty(fields)) { return codeField != null && codeField.getDict() != null;
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;
} }
/** /**
* 是否为 枚举 * 是否为 枚举
*/ */
protected boolean isEnum(String columnName, CodeGeneratorConfigForm form) { 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(); List<CodeField> fields = form.getFields();
if (CollectionUtils.isEmpty(fields)) { if (CollectionUtils.isEmpty(fields)) {
return false; return null;
} }
Optional<CodeField> first = fields.stream().filter(e -> columnName.equals(e.getColumnName())).findFirst(); Optional<CodeField> first = fields.stream().filter(e -> columnName.equals(e.getColumnName())).findFirst();
if (first.isPresent()) { return first.orElse(null);
return false;
}
CodeField codeField = first.get();
return codeField.getEnumName() != null;
} }
/** /**

View File

@@ -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);
}
}

View File

@@ -2,6 +2,7 @@ package net.lab1024.sa.base.module.support.codegenerator.service.variable.backen
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import net.lab1024.sa.base.common.util.SmartStringUtil; 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.form.CodeGeneratorConfigForm;
import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeField; import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeField;
import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeInsertAndUpdate; 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())) { if (SmartStringUtil.isNotEmpty(codeField.getDict())) {
finalFieldMap.put("dict", "\n @JsonDeserialize(using = DictValueVoDeserializer.class)"); 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)"); finalFieldMap.put("file", "\n @JsonDeserialize(using = FileKeyVoDeserializer.class)");
packageList.add("import com.fasterxml.jackson.databind.annotation.JsonDeserialize;"); packageList.add("import com.fasterxml.jackson.databind.annotation.JsonDeserialize;");
packageList.add("import net.lab1024.sa.base.common.json.deserializer.FileKeyVoDeserializer;"); packageList.add("import net.lab1024.sa.base.common.json.deserializer.FileKeyVoDeserializer;");

View File

@@ -14,7 +14,7 @@ import java.util.stream.Collectors;
* @Date 2022/9/29 17:20:41 * @Date 2022/9/29 17:20:41
* @Wechat zhuoda1024 * @Wechat zhuoda1024
* @Email lab1024@163.com * @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 { public class EntityVariableService extends CodeGenerateBaseVariableService {
@@ -54,14 +54,21 @@ public class EntityVariableService extends CodeGenerateBaseVariableService {
// mybatis plus // mybatis plus
result.add("import com.baomidou.mybatisplus.annotation.TableName;"); 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) { if (isExistPrimaryKey) {
result.add("import com.baomidou.mybatisplus.annotation.TableId;"); 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) { if (isExistAutoIncrease) {
result.add("import com.baomidou.mybatisplus.annotation.IdType;"); result.add("import com.baomidou.mybatisplus.annotation.IdType;");
} }

View File

@@ -3,19 +3,20 @@ package net.lab1024.sa.base.module.support.codegenerator.service.variable.backen
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import net.lab1024.sa.base.module.support.codegenerator.constant.CodeQueryFieldQueryTypeEnum; 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.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.domain.model.CodeQueryField;
import net.lab1024.sa.base.module.support.codegenerator.service.variable.CodeGenerateBaseVariableService; 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创新实验室-主任:卓大 * @Author 1024创新实验室-主任:卓大
* @Date 2022/9/29 17:20:41 * @Date 2022/9/29 17:20:41
* @Wechat zhuoda1024 * @Wechat zhuoda1024
* @Email lab1024@163.com * @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 { public class MapperVariableService extends CodeGenerateBaseVariableService {
@@ -39,55 +40,47 @@ public class MapperVariableService extends CodeGenerateBaseVariableService {
List<String> columnNameList = queryField.getColumnNameList(); List<String> columnNameList = queryField.getColumnNameList();
if (columnNameList.size() == 1) { if (columnNameList.size() == 1) {
// AND INSTR(t_notice.title,#{query.keywords}) // AND INSTR(t_notice.title,#{query.keywords})
stringBuilder.append(" AND INSTR(" ) stringBuilder.append(" AND INSTR(")
.append(form.getTableName()).append("." ).append(queryField.getColumnNameList().get(0)) .append(form.getTableName()).append(".").append(queryField.getColumnNameList().get(0))
.append(",#{queryForm." ) .append(",#{queryForm.")
.append(queryField.getFieldName()) .append(queryField.getFieldName())
.append("})" ); .append("})");
} else { } else {
for (int i = 0; i < columnNameList.size(); i++) { for (int i = 0; i < columnNameList.size(); i++) {
if (i == 0) { if (i == 0) {
stringBuilder.append("AND ( INSTR(" ) stringBuilder.append("AND ( INSTR(")
.append(form.getTableName()).append("." ).append(queryField.getColumnNameList().get(i)) .append(form.getTableName()).append(".").append(queryField.getColumnNameList().get(i))
.append(",#{queryForm." ) .append(",#{queryForm.")
.append(queryField.getFieldName()) .append(queryField.getFieldName())
.append("})" ); .append("})");
} else { } else {
// OR INSTR(t_notice.author,#{query.keywords}) // OR INSTR(t_notice.author,#{query.keywords})
stringBuilder.append("\n OR INSTR(" ) stringBuilder.append("\n OR INSTR(")
.append(form.getTableName()).append("." ).append(queryField.getColumnNameList().get(i)) .append(form.getTableName()).append(".").append(queryField.getColumnNameList().get(i))
.append(",#{queryForm." ) .append(",#{queryForm.")
.append(queryField.getFieldName()) .append(queryField.getFieldName())
.append("})" ); .append("})");
} }
} }
stringBuilder.append("\n )" ); stringBuilder.append("\n )");
} }
fieldMap.put("likeStr", stringBuilder.toString()); fieldMap.put("likeStr", stringBuilder.toString());
}else{ } else if (CodeQueryFieldQueryTypeEnum.DICT.equalsValue(queryField.getQueryTypeEnum())) {
fieldMap.put("columnName",queryField.getColumnNameList().get(0)); 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("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; 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);
}
} }

View File

@@ -2,6 +2,7 @@ package net.lab1024.sa.base.module.support.codegenerator.service.variable.backen
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import net.lab1024.sa.base.common.util.SmartEnumUtil; 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.constant.CodeQueryFieldQueryTypeEnum;
import net.lab1024.sa.base.module.support.codegenerator.domain.form.CodeGeneratorConfigForm; 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.CodeField;
@@ -18,7 +19,7 @@ import java.util.stream.Collectors;
* @Date 2022/9/29 17:20:41 * @Date 2022/9/29 17:20:41
* @Wechat zhuoda1024 * @Wechat zhuoda1024
* @Email lab1024@163.com * @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 { 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) { public ImmutablePair<List<String>, List<Map<String, Object>>> getPackageListAndFields(CodeGeneratorConfigForm form) {
List<CodeQueryField> fields = form.getQueryFields(); List<CodeQueryField> fields = form.getQueryFields();
if (CollectionUtils.isEmpty(fields)) {
return ImmutablePair.of(new ArrayList<>(), new ArrayList<>());
}
HashSet<String> packageList = new HashSet<>(); HashSet<String> packageList = new HashSet<>();
/** /**
* 1、LocalDate、LocalDateTime、BigDecimal 类型的包名 * 1、LocalDate、LocalDateTime、BigDecimal 类型的包名
* 2、排序 * 2、排序
@@ -75,9 +73,6 @@ public class QueryFormVariableService extends CodeGenerateBaseVariableService {
CodeField codeField = null; CodeField codeField = null;
switch (queryTypeEnum) { switch (queryTypeEnum) {
case LIKE:
finalFieldMap.put("javaType", "String");
break;
case EQUAL: case EQUAL:
codeField = getCodeFieldByColumnName(field.getColumnNameList().get(0), form); codeField = getCodeFieldByColumnName(field.getColumnNameList().get(0), form);
if (codeField == null) { if (codeField == null) {
@@ -109,6 +104,14 @@ public class QueryFormVariableService extends CodeGenerateBaseVariableService {
finalFieldMap.put("javaType", codeField.getJavaType()); finalFieldMap.put("javaType", codeField.getJavaType());
break; 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: default:
finalFieldMap.put("javaType", "String"); finalFieldMap.put("javaType", "String");
} }
@@ -116,12 +119,11 @@ public class QueryFormVariableService extends CodeGenerateBaseVariableService {
finalFieldList.add(finalFieldMap); finalFieldList.add(finalFieldMap);
} }
// lombok // lombok
packageList.add("import lombok.Data;"); packageList.add("import lombok.Data;");
packageList.add("import lombok.EqualsAndHashCode;");
List<String> packageNameList = packageList.stream().filter(Objects::nonNull).collect(Collectors.toList()); List<String> packageNameList = packageList.stream().filter(Objects::nonNull).sorted().collect(Collectors.toList());
Collections.sort(packageNameList);
return ImmutablePair.of(packageNameList, finalFieldList); return ImmutablePair.of(packageNameList, finalFieldList);
} }

View File

@@ -2,6 +2,7 @@ package net.lab1024.sa.base.module.support.codegenerator.service.variable.backen
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import net.lab1024.sa.base.common.util.SmartStringUtil; 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.form.CodeGeneratorConfigForm;
import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeField; import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeField;
import net.lab1024.sa.base.module.support.codegenerator.domain.model.CodeInsertAndUpdate; 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 * @Date 2022/9/29 17:20:41
* @Wechat zhuoda1024 * @Wechat zhuoda1024
* @Email lab1024@163.com * @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 { public class UpdateFormVariableService extends CodeGenerateBaseVariableService {
@@ -42,7 +43,7 @@ public class UpdateFormVariableService extends CodeGenerateBaseVariableService {
return false; return false;
} }
if(Boolean.TRUE.equals(codeField.getPrimaryKeyFlag())){ if (Boolean.TRUE.equals(codeField.getPrimaryKeyFlag())) {
e.setRequiredFlag(true); 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)"); finalFieldMap.put("file", "\n @JsonDeserialize(using = FileKeyVoDeserializer.class)");
packageList.add("import com.fasterxml.jackson.databind.annotation.JsonDeserialize;"); packageList.add("import com.fasterxml.jackson.databind.annotation.JsonDeserialize;");
packageList.add("import net.lab1024.sa.base.common.json.deserializer.FileKeyVoDeserializer;"); packageList.add("import net.lab1024.sa.base.common.json.deserializer.FileKeyVoDeserializer;");

View File

@@ -17,7 +17,7 @@ import java.util.stream.Collectors;
* @Date 2022/9/29 17:20:41 * @Date 2022/9/29 17:20:41
* @Wechat zhuoda1024 * @Wechat zhuoda1024
* @Email lab1024@163.com * @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 { public class VOVariableService extends CodeGenerateBaseVariableService {
@@ -87,16 +87,16 @@ public class VOVariableService extends CodeGenerateBaseVariableService {
//字典 //字典
if (isDict(field.getColumnName(), form)) { if (isDict(field.getColumnName(), form)) {
finalFieldMap.put("dict", "\n @JsonDeserialize(using = DictValueVoDeserializer.class)"); finalFieldMap.put("dict", "\n @JsonSerialize(using = DictValueVoSerializer.class)");
packageList.add("import com.fasterxml.jackson.databind.annotation.JsonDeserialize;"); packageList.add("import com.fasterxml.jackson.databind.annotation.JsonSerialize;");
packageList.add("import net.lab1024.sa.base.common.json.deserializer.DictValueVoDeserializer;"); packageList.add("import net.lab1024.sa.base.common.json.serializer.DictValueVoSerializer;");
} }
//文件上传 //文件上传
if (isFile(field.getColumnName(), form)) { if (isFile(field.getColumnName(), form)) {
finalFieldMap.put("file", "\n @JsonDeserialize(using = FileKeyVoDeserializer.class)"); finalFieldMap.put("file", "\n @JsonSerialize(using = FileKeyVoSerializer.class)");
packageList.add("import com.fasterxml.jackson.databind.annotation.JsonDeserialize;"); packageList.add("import com.fasterxml.jackson.databind.annotation.JsonSerialize;");
packageList.add("import net.lab1024.sa.base.common.json.deserializer.FileKeyVoDeserializer;"); packageList.add("import net.lab1024.sa.base.common.json.serializer.FileKeyVoSerializer;");
} }
packageList.add(getJavaPackageName(codeField.getJavaType())); packageList.add(getJavaPackageName(codeField.getJavaType()));

View File

@@ -57,19 +57,20 @@ public class FormVariableService extends CodeGenerateBaseVariableService {
fieldsVariableList.add(objectMap); 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';"); 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';"); 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';"); 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';"); frontImportSet.add("import FileUpload from '/@/components/support/file-upload/index.vue';");
} }
} }

View File

@@ -4,6 +4,7 @@ import cn.hutool.core.bean.BeanUtil;
import com.google.common.base.CaseFormat; import com.google.common.base.CaseFormat;
import net.lab1024.sa.base.module.support.codegenerator.constant.CodeQueryFieldQueryTypeEnum; 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.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.domain.model.CodeQueryField;
import net.lab1024.sa.base.module.support.codegenerator.service.variable.CodeGenerateBaseVariableService; import net.lab1024.sa.base.module.support.codegenerator.service.variable.CodeGenerateBaseVariableService;
@@ -35,19 +36,23 @@ public class ListVariableService extends CodeGenerateBaseVariableService {
for (CodeQueryField queryField : queryFields) { for (CodeQueryField queryField : queryFields) {
Map<String, Object> objectMap = BeanUtil.beanToMap(queryField); 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';"); 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';"); 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';"); frontImportSet.add("import { defaultTimeRanges } from '/@/lib/default-time-ranges';");
} }
variableList.add(objectMap);
} }
variablesMap.put("queryFields",variableList); variablesMap.put("queryFields",variableList);

View File

@@ -22,6 +22,7 @@ public enum ConfigKeyEnum implements BaseEnum {
*/ */
SUPER_PASSWORD("super_password", "万能密码"), SUPER_PASSWORD("super_password", "万能密码"),
LEVEL3_PROTECT_CONFIG("level3_protect_config", "三级等保配置"),
; ;
private final String value; private final String value;

View File

@@ -109,7 +109,8 @@ public class ConfigService {
* *
*/ */
public String getConfigValue(ConfigKeyEnum configKey) { 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) { public ResponseDTO<String> add(ConfigAddForm configAddForm) {
ConfigEntity entity = configDao.selectByKey(configAddDTO.getConfigKey()); ConfigEntity entity = configDao.selectByKey(configAddForm.getConfigKey());
if (null != entity) { if (null != entity) {
return ResponseDTO.error(UserErrorCode.ALREADY_EXIST); return ResponseDTO.error(UserErrorCode.ALREADY_EXIST);
} }
entity = SmartBeanUtil.copy(configAddDTO, ConfigEntity.class); entity = SmartBeanUtil.copy(configAddForm, ConfigEntity.class);
configDao.insert(entity); configDao.insert(entity);
// 刷新缓存 // 刷新缓存

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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));
}
}

View File

@@ -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.FileUploadVO;
import net.lab1024.sa.base.module.support.file.domain.vo.FileVO; 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.redis.RedisService;
import net.lab1024.sa.base.module.support.securityprotect.service.SecurityFileService;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@@ -39,7 +39,7 @@ import java.util.stream.Collectors;
* @Date 2019年10月11日 15:34:47 * @Date 2019年10月11日 15:34:47
* @Wechat zhuoda1024 * @Wechat zhuoda1024
* @Email lab1024@163.com * @Email lab1024@163.com
* @Copyright <a href="https://1024lab.net">1024创新实验室</a> * @Copyright <a href="https://1024lab.net">1024创新实验室</a>
*/ */
@Service @Service
public class FileService { public class FileService {
@@ -58,9 +58,8 @@ public class FileService {
@Resource @Resource
private RedisService redisService; private RedisService redisService;
@Value("${spring.servlet.multipart.max-file-size}") @Resource
private String maxFileSize; private SecurityFileService securityFileService;
/** /**
* 文件上传服务 * 文件上传服务
@@ -89,11 +88,10 @@ public class FileService {
return ResponseDTO.userErrorParam("文件名称最大长度为:" + FILE_NAME_MAX_LENGTH); return ResponseDTO.userErrorParam("文件名称最大长度为:" + FILE_NAME_MAX_LENGTH);
} }
// 校验文件大小 // 校验文件大小以及安全性
String maxSizeStr = maxFileSize.toLowerCase().replace("mb", ""); ResponseDTO<String> validateFile = securityFileService.checkFile(file);
long maxSize = Integer.parseInt(maxSizeStr) * 1024 * 1024L; if (!validateFile.getOk()) {
if (file.getSize() > maxSize) { return ResponseDTO.error(validateFile);
return ResponseDTO.userErrorParam("上传文件最大为:" + maxSize);
} }
// 进行上传 // 进行上传
@@ -192,7 +190,7 @@ public class FileService {
// 根据文件服务类 获取对应文件服务 查询 url // 根据文件服务类 获取对应文件服务 查询 url
ResponseDTO<FileDownloadVO> download = fileStorageService.download(fileKey); ResponseDTO<FileDownloadVO> download = fileStorageService.download(fileKey);
if(download.getOk()){ if (download.getOk()) {
download.getData().getMetadata().setFileName(fileVO.getFileName()); download.getData().getMetadata().setFileName(fileVO.getFileName());
} }
return download; return download;

View File

@@ -142,6 +142,8 @@ public class SmartJobExecutor implements Runnable {
logEntity.setSuccessFlag(true); logEntity.setSuccessFlag(true);
// 执行开始时间 // 执行开始时间
logEntity.setExecuteStartTime(executeTime); logEntity.setExecuteStartTime(executeTime);
logEntity.setExecuteEndTime(executeTime);
logEntity.setExecuteTimeMillis(0L);
logEntity.setCreateName(executorName); logEntity.setCreateName(executorName);
logEntity.setIp(SmartIpUtil.getLocalFirstIp()); logEntity.setIp(SmartIpUtil.getLocalFirstIp());
logEntity.setProcessId(SmartJobUtil.getProcessId()); logEntity.setProcessId(SmartJobUtil.getProcessId());

View File

@@ -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 "";
}
}

View File

@@ -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> {
}

View File

@@ -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
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -22,6 +22,12 @@ public class OperateLogQueryForm extends PageParam {
@Schema(description = "用户类型") @Schema(description = "用户类型")
private Integer operateUserType; private Integer operateUserType;
@Schema(description = "关键字:模块、操作内容")
private String keywords;
@Schema(description = "请求关键字:请求地址、请求方法、请求参数")
private String requestKeywords;
@Schema(description = "开始日期") @Schema(description = "开始日期")
private String startDate; private String startDate;

View File

@@ -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.dao.ReloadResultDao;
import net.lab1024.sa.base.module.support.reload.domain.ReloadItemEntity; import net.lab1024.sa.base.module.support.reload.domain.ReloadItemEntity;
import net.lab1024.sa.base.module.support.reload.domain.ReloadResultEntity; import net.lab1024.sa.base.module.support.reload.domain.ReloadResultEntity;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}
;

View File

@@ -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.LoginFailQueryForm;
import net.lab1024.sa.base.module.support.securityprotect.domain.LoginFailVO; import net.lab1024.sa.base.module.support.securityprotect.domain.LoginFailVO;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
@@ -26,27 +25,18 @@ import java.util.List;
* @Date 2023/10/11 19:25:59 * @Date 2023/10/11 19:25:59
* @Wechat zhuoda1024 * @Wechat zhuoda1024
* @Email lab1024@163.com * @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 @Service
public class ProtectLoginService { public class SecurityLoginService {
private static final String LOGIN_LOCK_MSG = "您已连续登录失败%s次账号锁定%s分钟解锁时间为%s请您耐心等待"; private static final String LOGIN_LOCK_MSG = "您已连续登录失败%s次账号锁定%s分钟解锁时间为%s请您耐心等待";
private static final String LOGIN_FAIL_MSG = "登录名或密码错误!连续登录失败%s次账号将锁定%s分钟您还可以再尝试%s次"; private static final String LOGIN_FAIL_MSG = "登录名或密码错误!连续登录失败%s次账号将锁定%s分钟您还可以再尝试%s次";
/** @Resource
* 连续登录失败次数则锁定-1表示不受限制可以一直登录 private Level3ProtectConfigService level3ProtectConfigService;
*/
@Value("${classified-protect.login-max-fail-times}")
private Integer loginMaxFailTimes;
/**
* 连续登录失败锁定时间单位-1表示不锁定
*/
@Value("${classified-protect.login-fail-locked-seconds}")
private Integer loginFailLockedSeconds;
@Resource @Resource
private LoginFailDao loginFailDao; private LoginFailDao loginFailDao;
@@ -61,8 +51,8 @@ public class ProtectLoginService {
*/ */
public ResponseDTO<LoginFailEntity> checkLogin(Long userId, UserTypeEnum userType) { public ResponseDTO<LoginFailEntity> checkLogin(Long userId, UserTypeEnum userType) {
// 无需校验 // 若登录最大失败次数小于1无需校验
if (loginMaxFailTimes < 1) { if (level3ProtectConfigService.getLoginFailMaxTimes() < 1) {
return ResponseDTO.ok(); return ResponseDTO.ok();
} }
@@ -72,19 +62,24 @@ public class ProtectLoginService {
return ResponseDTO.ok(); return ResponseDTO.ok();
} }
// 校验次数 // 校验登录失败次数
if (loginFailEntity.getLoginFailCount() < loginMaxFailTimes) { if (loginFailEntity.getLoginFailCount() < level3ProtectConfigService.getLoginFailMaxTimes()) {
return ResponseDTO.ok(loginFailEntity);
}
// 校验是否锁定
if (loginFailEntity.getLoginLockBeginTime() == null) {
return ResponseDTO.ok(loginFailEntity); 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); return ResponseDTO.ok(loginFailEntity);
} }
LocalDateTime unlockTime = loginFailEntity.getLoginLockBeginTime().plusSeconds(loginFailLockedSeconds); LocalDateTime unlockTime = loginFailEntity.getLoginLockBeginTime().plusSeconds(level3ProtectConfigService.getLoginFailLockSeconds());
return ResponseDTO.error(UserErrorCode.LOGIN_FAIL_LOCK, String.format(LOGIN_LOCK_MSG, loginFailEntity.getLoginFailCount(), loginFailLockedSeconds / 60, LocalDateTimeUtil.formatNormal(unlockTime))); 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) { public String recordLoginFail(Long userId, UserTypeEnum userType, String loginName, LoginFailEntity loginFailEntity) {
// 无需校验 // 若登录最大失败次数小于1无需记录
if (loginMaxFailTimes < 1) { if (level3ProtectConfigService.getLoginFailMaxTimes() < 1) {
return null; return null;
} }
// 登录失败
int loginFailCount = loginFailEntity == null ? 1 : loginFailEntity.getLoginFailCount() + 1;
boolean lockFlag = loginFailCount >= level3ProtectConfigService.getLoginFailMaxTimes();
LocalDateTime lockBeginTime = lockFlag ? LocalDateTime.now() : null;
if (loginFailEntity == null) { if (loginFailEntity == null) {
loginFailEntity = LoginFailEntity.builder() loginFailEntity = LoginFailEntity.builder()
.userId(userId) .userId(userId)
.userType(userType.getValue()) .userType(userType.getValue())
.loginName(loginName) .loginName(loginName)
.loginFailCount(1) .loginFailCount(loginFailCount)
.lockFlag(false) .lockFlag(lockFlag)
.loginLockBeginTime(null).build(); .loginLockBeginTime(lockBeginTime)
.build();
loginFailDao.insert(loginFailEntity); loginFailDao.insert(loginFailEntity);
} else { } else {
loginFailEntity.setLoginLockBeginTime(lockBeginTime);
// 如果是已经锁定状态则重新计算 loginFailEntity.setLoginFailCount(loginFailCount);
if(loginFailEntity.getLockFlag()){ loginFailEntity.setLockFlag(lockFlag);
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.setLoginName(loginName); loginFailEntity.setLoginName(loginName);
loginFailDao.updateById(loginFailEntity); loginFailDao.updateById(loginFailEntity);
} }
// 提示信息 // 提示信息
if (loginFailEntity.getLoginFailCount() >= loginMaxFailTimes) { if (lockFlag) {
LocalDateTime unlockTime = loginFailEntity.getLoginLockBeginTime().plusSeconds(loginFailLockedSeconds); LocalDateTime unlockTime = loginFailEntity.getLoginLockBeginTime().plusSeconds(level3ProtectConfigService.getLoginFailLockSeconds());
return String.format(LOGIN_LOCK_MSG, loginFailEntity.getLoginFailCount(), loginFailLockedSeconds / 60, LocalDateTimeUtil.formatNormal(unlockTime)); return String.format(LOGIN_LOCK_MSG, loginFailEntity.getLoginFailCount(), level3ProtectConfigService.getLoginFailLockSeconds() / 60, LocalDateTimeUtil.formatNormal(unlockTime));
} else { } 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 * @param userType
*/ */
public void removeLoginFail(Long userId, UserTypeEnum userType) { public void removeLoginFail(Long userId, UserTypeEnum userType) {
// 无需校验
if (loginMaxFailTimes < 1) { // 若登录最大失败次数小于1无需校验
if (level3ProtectConfigService.getLoginFailMaxTimes() < 1) {
return; return;
} }
@@ -160,8 +153,7 @@ public class ProtectLoginService {
public PageResult<LoginFailVO> queryPage(LoginFailQueryForm queryForm) { public PageResult<LoginFailVO> queryPage(LoginFailQueryForm queryForm) {
Page<?> page = SmartPageUtil.convert2PageQuery(queryForm); Page<?> page = SmartPageUtil.convert2PageQuery(queryForm);
List<LoginFailVO> list = loginFailDao.queryPage(page, queryForm); List<LoginFailVO> list = loginFailDao.queryPage(page, queryForm);
PageResult<LoginFailVO> pageResult = SmartPageUtil.convert2PageResult(page, list); return SmartPageUtil.convert2PageResult(page, list);
return pageResult;
} }
/** /**

View File

@@ -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));
}
}

View File

@@ -2,6 +2,7 @@ package ${packageName};
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import net.lab1024.sa.base.common.enumeration.BaseEnum;
/** /**
* ${enumDesc} * ${enumDesc}

View File

@@ -3,6 +3,7 @@ package ${packageName};
#foreach ($importClass in $importPackageList) #foreach ($importClass in $importPackageList)
$importClass $importClass
#end #end
import cn.dev33.satoken.annotation.SaCheckPermission;
import net.lab1024.sa.base.common.domain.ResponseDTO; import net.lab1024.sa.base.common.domain.ResponseDTO;
import net.lab1024.sa.base.common.domain.PageResult; import net.lab1024.sa.base.common.domain.PageResult;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@@ -23,7 +24,7 @@ import javax.validation.Valid;
*/ */
@RestController @RestController
@Tag(name = "") @Tag(name = "${basic.description}")
public class ${name.upperCamel}Controller { public class ${name.upperCamel}Controller {
@Resource @Resource
@@ -31,6 +32,7 @@ public class ${name.upperCamel}Controller {
@Operation(summary = "分页查询 @author ${basic.backendAuthor}") @Operation(summary = "分页查询 @author ${basic.backendAuthor}")
@PostMapping("/${name.lowerCamel}/queryPage") @PostMapping("/${name.lowerCamel}/queryPage")
@SaCheckPermission("${name.lowerCamel}:query")
public ResponseDTO<PageResult<${name.upperCamel}VO>> queryPage(@RequestBody @Valid ${name.upperCamel}QueryForm queryForm) { public ResponseDTO<PageResult<${name.upperCamel}VO>> queryPage(@RequestBody @Valid ${name.upperCamel}QueryForm queryForm) {
return ResponseDTO.ok(${name.lowerCamel}Service.queryPage(queryForm)); return ResponseDTO.ok(${name.lowerCamel}Service.queryPage(queryForm));
} }
@@ -38,12 +40,14 @@ public class ${name.upperCamel}Controller {
#if($insertAndUpdate.isSupportInsertAndUpdate) #if($insertAndUpdate.isSupportInsertAndUpdate)
@Operation(summary = "添加 @author ${basic.backendAuthor}") @Operation(summary = "添加 @author ${basic.backendAuthor}")
@PostMapping("/${name.lowerCamel}/add") @PostMapping("/${name.lowerCamel}/add")
@SaCheckPermission("${name.lowerCamel}:add")
public ResponseDTO<String> add(@RequestBody @Valid ${name.upperCamel}AddForm addForm) { public ResponseDTO<String> add(@RequestBody @Valid ${name.upperCamel}AddForm addForm) {
return ${name.lowerCamel}Service.add(addForm); return ${name.lowerCamel}Service.add(addForm);
} }
@Operation(summary = "更新 @author ${basic.backendAuthor}") @Operation(summary = "更新 @author ${basic.backendAuthor}")
@PostMapping("/${name.lowerCamel}/update") @PostMapping("/${name.lowerCamel}/update")
@SaCheckPermission("${name.lowerCamel}:update")
public ResponseDTO<String> update(@RequestBody @Valid ${name.upperCamel}UpdateForm updateForm) { public ResponseDTO<String> update(@RequestBody @Valid ${name.upperCamel}UpdateForm updateForm) {
return ${name.lowerCamel}Service.update(updateForm); return ${name.lowerCamel}Service.update(updateForm);
} }
@@ -53,6 +57,7 @@ public class ${name.upperCamel}Controller {
#if($deleteInfo.deleteEnum == "Batch" || $deleteInfo.deleteEnum == "SingleAndBatch") #if($deleteInfo.deleteEnum == "Batch" || $deleteInfo.deleteEnum == "SingleAndBatch")
@Operation(summary = "批量删除 @author ${basic.backendAuthor}") @Operation(summary = "批量删除 @author ${basic.backendAuthor}")
@PostMapping("/${name.lowerCamel}/batchDelete") @PostMapping("/${name.lowerCamel}/batchDelete")
@SaCheckPermission("${name.lowerCamel}:delete")
public ResponseDTO<String> batchDelete(@RequestBody ValidateList<${primaryKeyJavaType}> idList) { public ResponseDTO<String> batchDelete(@RequestBody ValidateList<${primaryKeyJavaType}> idList) {
return ${name.lowerCamel}Service.batchDelete(idList); return ${name.lowerCamel}Service.batchDelete(idList);
} }
@@ -61,6 +66,7 @@ public class ${name.upperCamel}Controller {
#if($deleteInfo.deleteEnum == "Single" || $deleteInfo.deleteEnum == "SingleAndBatch") #if($deleteInfo.deleteEnum == "Single" || $deleteInfo.deleteEnum == "SingleAndBatch")
@Operation(summary = "单个删除 @author ${basic.backendAuthor}") @Operation(summary = "单个删除 @author ${basic.backendAuthor}")
@GetMapping("/${name.lowerCamel}/delete/{${primaryKeyFieldName}}") @GetMapping("/${name.lowerCamel}/delete/{${primaryKeyFieldName}}")
@SaCheckPermission("${name.lowerCamel}:delete")
public ResponseDTO<String> batchDelete(@PathVariable ${primaryKeyJavaType} ${primaryKeyFieldName}) { public ResponseDTO<String> batchDelete(@PathVariable ${primaryKeyJavaType} ${primaryKeyFieldName}) {
return ${name.lowerCamel}Service.delete(${primaryKeyFieldName}); return ${name.lowerCamel}Service.delete(${primaryKeyFieldName});
} }

View File

@@ -38,14 +38,15 @@ public interface ${name.upperCamel}Dao extends BaseMapper<${name.upperCamel}Enti
* 更新删除状态 * 更新删除状态
*/ */
long updateDeleted(@Param("${primaryKeyFieldName}")${primaryKeyJavaType} ${primaryKeyFieldName},@Param("${deletedFlag}")boolean deletedFlag); long updateDeleted(@Param("${primaryKeyFieldName}")${primaryKeyJavaType} ${primaryKeyFieldName},@Param("${deletedFlag}")boolean deletedFlag);
#end #end
#if($deleteInfo.deleteEnum == "Batch" || $deleteInfo.deleteEnum == "SingleAndBatch") #if($deleteInfo.deleteEnum == "Batch" || $deleteInfo.deleteEnum == "SingleAndBatch")
/** /**
* 批量更新删除状态 * 批量更新删除状态
*/ */
void batchUpdateDeleted(@Param("idList")List<${primaryKeyJavaType}> idList,@Param("${deletedFlag}")boolean deletedFlag); void batchUpdateDeleted(@Param("idList")List<${primaryKeyJavaType}> idList,@Param("${deletedFlag}")boolean deletedFlag);
#end
#end
#end
#end
#end
#end
} }

View File

@@ -20,13 +20,19 @@ public class ${name.upperCamel}Entity {
/** /**
* $field.label * $field.label
*/ */
#if($field.primaryKeyFlag && $field.autoIncreaseFlag) #if($field.primaryKeyFlag && $field.autoIncreaseFlag)
@TableId(type = IdType.AUTO) @TableId(type = IdType.AUTO)
#end #end
#if($field.primaryKeyFlag && !$field.autoIncreaseFlag) #if($field.primaryKeyFlag && !$field.autoIncreaseFlag)
@TableId @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; private $field.javaType $field.fieldName;
#end #end
} }

View File

@@ -14,7 +14,8 @@ $importClass
*/ */
@Data @Data
public class ${name.upperCamel}QueryForm extends PageParam{ @EqualsAndHashCode(callSuper = false)
public class ${name.upperCamel}QueryForm extends PageParam {
#foreach ($field in $fields) #foreach ($field in $fields)
#if($field.isEnum) #if($field.isEnum)
@@ -35,4 +36,4 @@ public class ${name.upperCamel}QueryForm extends PageParam{
#end #end
#end #end
} }

View File

@@ -17,14 +17,8 @@ public class ${name.upperCamel}VO {
#foreach ($field in $fields) #foreach ($field in $fields)
#if($field.isEnum) ${field.apiModelProperty}$!{field.notEmpty}$!{field.dict}$!{field.file}
${field.apiModelProperty}
private $field.javaType $field.fieldName; private $field.javaType $field.fieldName;
#end #end
#if(!$field.isEnum)
${field.apiModelProperty}$!{field.dict}$!{field.file}
private $field.javaType $field.fieldName;
#end
#end
} }

View File

@@ -2,10 +2,17 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="${daoClassName}"> <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 id="queryPage" resultType="${basic.javaPackageName}.domain.vo.${name.upperCamel}VO">
SELECT SELECT
* <include refid="base_columns"/>
FROM ${tableName} FROM ${tableName}
#if($queryFields.size() > 0) #if($queryFields.size() > 0)
<where> <where>
@@ -16,22 +23,27 @@
${queryField.likeStr} ${queryField.likeStr}
</if> </if>
#end #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"> <if test="queryForm.${queryField.fieldName} != null">
AND ${tableName}.${queryField.columnName} = #{queryForm.${queryField.fieldName}} AND ${tableName}.${queryField.columnName} = #{queryForm.${queryField.fieldName}}
</if> </if>
#end #end
#if(${queryField.queryTypeEnum} == "Date") #if(${queryField.queryTypeEnum} == "Date")
<if test="queryForm.${queryField.fieldName} != null"> <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> </if>
#end #end
#if(${queryField.queryTypeEnum} == "DateRange") #if(${queryField.queryTypeEnum} == "DateRange")
<if test="queryForm.${queryField.fieldName}Begin != null"> <if test="queryForm.${queryField.fieldName}Begin != null">
AND DATE_FORMAT(${tableName}.${queryField.columnName}, '%Y-%m-%d') &gt;= #{queryForm.${queryField.fieldName}Begin} AND ${tableName}.${queryField.columnName} &gt;= #{queryForm.${queryField.fieldName}Begin}
</if> </if>
<if test="queryForm.${queryField.fieldName}End != null"> <if test="queryForm.${queryField.fieldName}End != null">
AND DATE_FORMAT(${tableName}.${queryField.columnName}, '%Y-%m-%d') &lt;= #{queryForm.${queryField.fieldName}End} AND ${tableName}.${queryField.columnName} &lt;= #{queryForm.${queryField.fieldName}End}
</if> </if>
#end #end
#end #end
@@ -39,21 +51,10 @@
#end #end
</select> </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.isSupportDelete)
### 假删除 ### 假删除
#if(!${deleteInfo.isPhysicallyDeleted}) #if(!${deleteInfo.isPhysicallyDeleted})
#if($deleteInfo.deleteEnum == "Batch" || $deleteInfo.deleteEnum == "SingleAndBatch") #if($deleteInfo.deleteEnum == "Batch" || $deleteInfo.deleteEnum == "SingleAndBatch")
<update id="batchUpdateDeleted"> <update id="batchUpdateDeleted">
update ${tableName} set deleted_flag = #{deletedFlag} update ${tableName} set deleted_flag = #{deletedFlag}
where ${primaryKeyColumnName} in where ${primaryKeyColumnName} in
@@ -71,4 +72,5 @@
#end #end
#end #end
#end #end
</mapper>
</mapper>

View File

@@ -64,7 +64,7 @@ public class ${name.upperCamel}Service {
#end #end
#if($deleteInfo.isSupportDelete) #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 假删除 ### 真删除 or 假删除
#if(!${deleteInfo.isPhysicallyDeleted}) #if(!${deleteInfo.isPhysicallyDeleted})
${name.lowerCamel}Dao.updateDeleted(${primaryKeyFieldName},true); ${name.lowerCamel}Dao.updateDeleted(${primaryKeyFieldName}, true);
#end #end
#if(${deleteInfo.isPhysicallyDeleted}) #if(${deleteInfo.isPhysicallyDeleted})
${name.lowerCamel}Dao.deleteById(${primaryKeyFieldName}); ${name.lowerCamel}Dao.deleteById(${primaryKeyFieldName});

View File

@@ -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 );

View File

@@ -8,7 +8,7 @@
<template> <template>
<a-$!{insertAndUpdate.pageType} <a-$!{insertAndUpdate.pageType}
:title="form.$!{primaryKeyFieldName} ? '编辑' : '添加'" :title="form.$!{primaryKeyFieldName} ? '编辑' : '添加'"
width="$!{insertAndUpdate.width}" :width="$!{insertAndUpdate.width}"
:open="visibleFlag" :open="visibleFlag"
#if($!{insertAndUpdate.pageType} == 'drawer') #if($!{insertAndUpdate.pageType} == 'drawer')
@close="onClose" @close="onClose"
@@ -19,124 +19,121 @@
:destroyOnClose="true" :destroyOnClose="true"
> >
<a-form ref="formRef" :model="form" :rules="rules" :label-col="{ span: 5 }" > <a-form ref="formRef" :model="form" :rules="rules" :label-col="{ span: 5 }" >
#if($insertAndUpdate.countPerLine == 1) #if($insertAndUpdate.countPerLine == 1)
<a-row> #foreach ($field in $formFields)
#foreach ($field in $formFields) #if($field.frontComponent == "Input")
#if($field.frontComponent == "Input") <a-form-item label="$!{field.label}" name="${field.fieldName}">
<a-form-item label="$!{field.label}" name="${field.fieldName}"> <a-input style="width: 100%" v-model:value="form.${field.fieldName}" placeholder="$!{field.label}" />
<a-input style="width: 100%" v-model:value="form.${field.fieldName}" placeholder="$!{field.label}" /> </a-form-item>
</a-form-item> #end
#end #if($field.frontComponent == "InputNumber")
#if($field.frontComponent == "InputNumber") <a-form-item label="$!{field.label}" name="${field.fieldName}">
<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-input-number style="width: 100%" v-model:value="form.${field.fieldName}" placeholder="$!{field.label}" /> </a-form-item>
</a-form-item> #end
#end #if($field.frontComponent == "Textarea")
#if($field.frontComponent == "Textarea") <a-form-item label="$!{field.label}" name="${field.fieldName}">
<a-form-item label="$!{field.label}" name="${field.fieldName}"> <a-textarea style="width: 100%" v-model:value="form.${field.fieldName}" placeholder="$!{field.label}" />
<a-textarea style="width: 100%" v-model:value="form.${field.fieldName}" placeholder="$!{field.label}" /> </a-form-item>
</a-form-item> #end
#end #if($field.frontComponent == "BooleanSelect")
#if($field.frontComponent == "BooleanSelect") <a-form-item label="$!{field.label}" name="${field.fieldName}">
<a-form-item label="$!{field.label}" name="${field.fieldName}"> <BooleanSelect v-model:value="form.${field.fieldName}" style="width: 100%" />
<BooleanSelect v-model:value="form.${field.fieldName}" style="width: 100%" /> </a-form-item>
</a-form-item> #end
#end #if($field.frontComponent == "SmartEnumSelect")
#if($field.frontComponent == "SmartEnumSelect") <a-form-item label="$codeGeneratorTool.removeEnumDesc($!{field.label})" name="${field.fieldName}">
<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})"/>
<SmartEnumSelect width="100%" v-model:value="form.${field.fieldName}" enumName="$!{field.upperUnderscoreEnum}" placeholder="$codeGeneratorTool.removeEnumDesc($!{field.label})"/> </a-form-item>
</a-form-item> #end
#end #if($field.frontComponent == "DictSelect")
#if($field.frontComponent == "DictSelect") <a-form-item label="$codeGeneratorTool.removeEnumDesc($!{field.label})" name="${field.fieldName}">
<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}"/>
<DictSelect width="100%" v-model:value="form.${field.fieldName}" keyCode="$!{field.dict}" placeholder="$!{field.label}"/> </a-form-item>
</a-form-item> #end
#end #if($field.frontComponent == "Date")
#if($field.frontComponent == "Date") <a-form-item label="$!{field.label}" name="${field.fieldName}">
<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-date-picker valueFormat="YYYY-MM-DD" v-model:value="form.$!{field.fieldName}" style="width: 100%" placeholder="$!{field.label}"/> </a-form-item>
</a-form-item> #end
#end #if($field.frontComponent == "DateTime")
#if($field.frontComponent == "DateTime") <a-form-item label="$!{field.label}" name="${field.fieldName}">
<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-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>
</a-form-item> #end
#end #if($field.frontComponent == "FileUpload")
#if($field.frontComponent == "Upload") <a-form-item label="$!{field.label}" name="${field.fieldName}">
<a-form-item label="$!{field.label}" name="${field.fieldName}"> <FileUpload
<FileUpload :defaultFileList="form.$!{field.fieldName}"
:defaultFileList="form.$!{field.fieldName}" :folder="FILE_FOLDER_TYPE_ENUM.COMMON.value"
:folder="FILE_FOLDER_TYPE_ENUM.COMMON.value" buttonText="上传 $!{field.label}"
buttonText="上传 $!{field.label}" listType="text"
listType="text" @change="e => form.$!{field.fieldName} = e"
@change="e => form.$!{field.fieldName} = e" />
/> </a-form-item>
</a-form-item> #end
#end #end
#end #end
</a-row> #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 #end
#if($field.frontComponent == "InputNumber")
#if($insertAndUpdate.countPerLine > 1) <a-form-item label="$!{field.label}" name="${field.fieldName}">
<a-row> <a-input-number style="width: 100%" v-model:value="form.${field.fieldName}" placeholder="$!{field.label}" />
#set($span=24 / $!insertAndUpdate.countPerLine ) </a-form-item>
#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>
#end #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> </a-form>
<template #footer> <template #footer>
@@ -154,9 +151,9 @@
import { SmartLoading } from '/@/components/framework/smart-loading'; import { SmartLoading } from '/@/components/framework/smart-loading';
import { $!{name.lowerCamel}Api } from '/@/api/business/$!{name.lowerHyphenCamel}/$!{name.lowerHyphenCamel}-api'; import { $!{name.lowerCamel}Api } from '/@/api/business/$!{name.lowerHyphenCamel}/$!{name.lowerHyphenCamel}-api';
import { smartSentry } from '/@/lib/smart-sentry'; import { smartSentry } from '/@/lib/smart-sentry';
#foreach ($import in $frontImportList) #foreach ($import in $frontImportList)
$!{import} $!{import}
#end #end
// ------------------------ 事件 ------------------------ // ------------------------ 事件 ------------------------
@@ -171,6 +168,10 @@
if (rowData && !_.isEmpty(rowData)) { if (rowData && !_.isEmpty(rowData)) {
Object.assign(form, rowData); Object.assign(form, rowData);
} }
// 使用字典时把下面这注释修改成自己的字典字段 有多个字典字段就复制多份同理修改 不然打开表单时不显示字典初始值
// if (form.status && form.status.length > 0) {
// form.status = form.status.map((e) => e.valueCode);
// }
visibleFlag.value = true; visibleFlag.value = true;
nextTick(() => { nextTick(() => {
formRef.value.clearValidate(); formRef.value.clearValidate();
@@ -188,9 +189,8 @@
const formRef = ref(); const formRef = ref();
const formDefault = { const formDefault = {
$!{primaryKeyFieldName}: undefined,
#foreach ($field in $formFields) #foreach ($field in $formFields)
$!{field.fieldName}: undefined, //$!{field.label} $!{field.fieldName}: undefined, //$!{field.label}
#end #end
}; };
@@ -198,9 +198,9 @@
const rules = { const rules = {
#foreach ($field in $formFields) #foreach ($field in $formFields)
#if($field.requiredFlag) #if($field.requiredFlag)
$!{field.fieldName}: [{ required: true, message: '$!{field.label} 必填' }], $!{field.fieldName}: [{ required: true, message: '$!{field.label} 必填' }],
#end #end
#end #end
}; };

View File

@@ -72,7 +72,7 @@
</a-button> </a-button>
#end #end
#if($deleteInfo.isSupportDelete && ($deleteInfo.deleteEnum == "Batch"||$deleteInfo.deleteEnum == "SingleAndBatch")) #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> <template #icon>
<DeleteOutlined /> <DeleteOutlined />
</template> </template>
@@ -88,18 +88,32 @@
<!---------- 表格 begin -----------> <!---------- 表格 begin ----------->
<a-table <a-table
size="small" size="small"
:dataSource="tableData" :dataSource="tableData"
:columns="columns" :columns="columns"
rowKey="$!{primaryKeyFieldName}" rowKey="$!{primaryKeyFieldName}"
bordered bordered
:loading="tableLoading" :loading="tableLoading"
:pagination="false" :pagination="false"
#if($deleteInfo.isSupportDelete && ($deleteInfo.deleteEnum == "Batch"||$deleteInfo.deleteEnum == "SingleAndBatch")) #if($deleteInfo.isSupportDelete && ($deleteInfo.deleteEnum == "Batch"||$deleteInfo.deleteEnum == "SingleAndBatch"))
:row-selection="{ selectedRowKeys: selectedRowKeyList, onChange: onSelectChange }" :row-selection="{ selectedRowKeys: selectedRowKeyList, onChange: onSelectChange }"
#end #end
> >
<template #bodyCell="{ text, record, column }"> <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'"> <template v-if="column.dataIndex === 'action'">
<div class="smart-table-operate"> <div class="smart-table-operate">
#if($insertAndUpdate.isSupportInsertAndUpdate) #if($insertAndUpdate.isSupportInsertAndUpdate)
@@ -116,17 +130,17 @@
<div class="smart-query-table-page"> <div class="smart-query-table-page">
<a-pagination <a-pagination
showSizeChanger showSizeChanger
showQuickJumper showQuickJumper
show-less-items show-less-items
:pageSizeOptions="PAGE_SIZE_OPTIONS" :pageSizeOptions="PAGE_SIZE_OPTIONS"
:defaultPageSize="queryForm.pageSize" :defaultPageSize="queryForm.pageSize"
v-model:current="queryForm.pageNum" v-model:current="queryForm.pageNum"
v-model:pageSize="queryForm.pageSize" v-model:pageSize="queryForm.pageSize"
:total="total" :total="total"
@change="queryData" @change="queryData"
@showSizeChange="queryData" @showSizeChange="queryData"
:show-total="(total) => `共${total}条`" :show-total="(total) => `共${total}条`"
/> />
</div> </div>
@@ -145,6 +159,8 @@
#foreach ($import in $frontImportList) #foreach ($import in $frontImportList)
$!{import} $!{import}
#end #end
//import FilePreview from '/@/components/support/file-preview/index.vue'; // 图片预览组件
// ---------------------------- 表格列 ---------------------------- // ---------------------------- 表格列 ----------------------------
const columns = ref([ const columns = ref([

View File

@@ -3,14 +3,14 @@ spring:
datasource: 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 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 username: root
password: Zhuoda1024lab password: SmartAdmin666
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
initial-size: 2 initial-size: 2
min-idle: 2 min-idle: 2
max-active: 10 max-active: 10
max-wait: 60000 max-wait: 60000
time-between-eviction-runs-millis: 60000 time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000 min-evictable-idle-time-millis: 300000
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
filters: stat filters: stat
druid: druid:
username: druid username: druid
@@ -34,11 +34,22 @@ spring:
max-idle: 3 max-idle: 3
max-wait: 30000ms max-wait: 30000ms
# 上传文件大小配置 # 邮件置以SSL的方式发送, 这个需要使用这种方式并且端口是465
servlet: mail:
multipart: host: smtp.163.com
max-file-size: 30MB port: 465
max-request-size: 30MB 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序列化相关配置 # json序列化相关配置
jackson: jackson:
@@ -87,6 +98,7 @@ springdoc:
enabled: true # 开关 enabled: true # 开关
doc-expansion: none #关闭展开 doc-expansion: none #关闭展开
tags-sorter: alpha tags-sorter: alpha
server-base-url:
api-docs: api-docs:
enabled: true # 开关 enabled: true # 开关
knife4j: knife4j:

View File

@@ -22,6 +22,12 @@
<if test="query.userName != null and query.userName != ''"> <if test="query.userName != null and query.userName != ''">
AND INSTR(operate_user_name,#{query.userName}) AND INSTR(operate_user_name,#{query.userName})
</if> </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"> <if test="query.successFlag != null">
AND success_flag = #{query.successFlag} AND success_flag = #{query.successFlag}
</if> </if>

View File

@@ -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>

View File

@@ -3,14 +3,14 @@ spring:
datasource: 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 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 username: root
password: Zhuoda1024lab password: SmartAdmin666
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
initial-size: 2 initial-size: 2
min-idle: 2 min-idle: 2
max-active: 10 max-active: 10
max-wait: 60000 max-wait: 60000
time-between-eviction-runs-millis: 60000 time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000 min-evictable-idle-time-millis: 300000
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
filters: stat filters: stat
druid: druid:
username: druid username: druid
@@ -34,11 +34,22 @@ spring:
max-idle: 3 max-idle: 3
max-wait: 30000ms max-wait: 30000ms
# 上传文件大小配置 # 邮件置以SSL的方式发送, 这个需要使用这种方式并且端口是465
servlet: mail:
multipart: host: smtp.163.com
max-file-size: 30MB port: 465
max-request-size: 30MB 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序列化相关配置 # json序列化相关配置
jackson: jackson:
@@ -65,7 +76,6 @@ server:
max-days: 7 max-days: 7
pattern: "%t %{X-Forwarded-For}i %a %r %s (%D ms) %I (%B byte)" pattern: "%t %{X-Forwarded-For}i %a %r %s (%D ms) %I (%B byte)"
# 文件上传 配置 # 文件上传 配置
file: file:
storage: storage:
@@ -82,13 +92,13 @@ file:
url-prefix: https://${file.storage.cloud.bucket-name}.${file.storage.cloud.endpoint}/ url-prefix: https://${file.storage.cloud.bucket-name}.${file.storage.cloud.endpoint}/
private-url-expire-seconds: 3600 private-url-expire-seconds: 3600
# open api配置 # open api配置
springdoc: springdoc:
swagger-ui: swagger-ui:
enabled: true # 开关 enabled: true # 开关
doc-expansion: none #关闭展开 doc-expansion: none #关闭展开
tags-sorter: alpha tags-sorter: alpha
server-base-url:
api-docs: api-docs:
enabled: true # 开关 enabled: true # 开关
knife4j: knife4j:
@@ -112,11 +122,11 @@ access-control-allow-origin: '*'
# 心跳配置 # 心跳配置
heart-beat: heart-beat:
interval-seconds: 60 interval-seconds: 300
# 热加载配置 # 热加载配置
reload: reload:
interval-seconds: 60 interval-seconds: 300
# sa-token 配置 # sa-token 配置
sa-token: sa-token:
@@ -135,9 +145,9 @@ sa-token:
# 是否打开自动续签 如果此值为true框架会在每次直接或间接调用 getLoginId() 时进行一次过期检查与续签操作) # 是否打开自动续签 如果此值为true框架会在每次直接或间接调用 getLoginId() 时进行一次过期检查与续签操作)
auto-renew: true auto-renew: true
# 是否输出操作日志 # 是否输出操作日志
is-log: false is-log: true
# 日志等级trace、debug、info、warn、error、fatal # 日志等级trace、debug、info、warn、error、fatal
log-level: warn log-level: debug
# 启动时的字符画打印 # 启动时的字符画打印
is-print: false is-print: false
# 是否从cookie读取token # 是否从cookie读取token

View File

@@ -3,14 +3,14 @@ spring:
datasource: 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 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 username: root
password: Zhuoda#1024lab password: SmartAdmin666
driver-class-name: com.mysql.cj.jdbc.Driver
initial-size: 10 initial-size: 10
min-idle: 10 min-idle: 10
max-active: 100 max-active: 100
max-wait: 60000 max-wait: 60000
time-between-eviction-runs-millis: 60000 time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000 min-evictable-idle-time-millis: 300000
driver-class-name: com.mysql.cj.jdbc.Driver
filters: stat filters: stat
druid: druid:
username: druid username: druid
@@ -33,12 +33,22 @@ spring:
min-idle: 10 min-idle: 10
max-idle: 50 max-idle: 50
max-wait: 30000ms max-wait: 30000ms
# 邮件置以SSL的方式发送, 这个需要使用这种方式并且端口是465
# 上传文件大小配置 mail:
servlet: host: smtp.163.com
multipart: port: 465
max-file-size: 30MB username: lab1024@163.com
max-request-size: 30MB password: ROIASDFJKHQFTA
properties:
mail:
smtp:
auth: true
ssl:
enable: true
socketFactory:
class: com.sun.mail.util.MailSSLSocketFactory
fallback: false
debug: false
# json序列化相关配置 # json序列化相关配置
jackson: jackson:
@@ -74,9 +84,9 @@ file:
upload-path: /home/smart_admin_v3/upload/ #文件上传目录 upload-path: /home/smart_admin_v3/upload/ #文件上传目录
url-prefix: url-prefix:
cloud: cloud:
region: oss-cn-qingdao region: oss-cn-hangzhou
endpoint: oss-cn-qingdao.aliyuncs.com endpoint: oss-cn-hangzhou.aliyuncs.com
bucket-name: common bucket-name: 1024lab-smart-admin
access-key: access-key:
secret-key: secret-key:
url-prefix: https://${file.storage.cloud.bucket-name}.${file.storage.cloud.endpoint}/ url-prefix: https://${file.storage.cloud.bucket-name}.${file.storage.cloud.endpoint}/

View File

@@ -3,14 +3,14 @@ spring:
datasource: 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 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 username: root
password: Zhuoda1024lab password: SmartAdmin666
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
initial-size: 2 initial-size: 2
min-idle: 2 min-idle: 2
max-active: 10 max-active: 10
max-wait: 60000 max-wait: 60000
time-between-eviction-runs-millis: 60000 time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000 min-evictable-idle-time-millis: 300000
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
filters: stat filters: stat
druid: druid:
username: druid username: druid
@@ -34,11 +34,22 @@ spring:
max-idle: 3 max-idle: 3
max-wait: 30000ms max-wait: 30000ms
# 上传文件大小配置 # 邮件置以SSL的方式发送, 这个需要使用这种方式并且端口是465
servlet: mail:
multipart: host: smtp.163.com
max-file-size: 30MB port: 465
max-request-size: 30MB 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序列化相关配置 # json序列化相关配置
jackson: jackson:
@@ -65,7 +76,6 @@ server:
max-days: 7 max-days: 7
pattern: "%t %{X-Forwarded-For}i %a %r %s (%D ms) %I (%B byte)" pattern: "%t %{X-Forwarded-For}i %a %r %s (%D ms) %I (%B byte)"
# 文件上传 配置 # 文件上传 配置
file: file:
storage: storage:
@@ -82,20 +92,19 @@ file:
url-prefix: https://${file.storage.cloud.bucket-name}.${file.storage.cloud.endpoint}/ url-prefix: https://${file.storage.cloud.bucket-name}.${file.storage.cloud.endpoint}/
private-url-expire-seconds: 3600 private-url-expire-seconds: 3600
# open api配置 # open api配置
springdoc: springdoc:
swagger-ui: swagger-ui:
enabled: true # 开关 enabled: true # 开关
doc-expansion: none #关闭展开 doc-expansion: none #关闭展开
tags-sorter: alpha tags-sorter: alpha
server-base-url: http://smartadmin.dev.1024lab.net/api/ server-base-url:
api-docs: api-docs:
enabled: true # 开关 enabled: true # 开关
knife4j: knife4j:
enable: true enable: true
basic: basic:
enable: true enable: false
username: api # Basic认证用户名 username: api # Basic认证用户名
password: 1024 # Basic认证密码 password: 1024 # Basic认证密码

Some files were not shown because too many files have changed in this diff Show More