2.0 alpha

This commit is contained in:
zhuoda 2021-09-02 21:34:21 +08:00
parent c692d0e6d3
commit 959ef3dc4e
1223 changed files with 55849 additions and 73875 deletions

12
.gitignore vendored
View File

@ -29,3 +29,15 @@ build/
### VS Code ### ### VS Code ###
.vscode/ .vscode/
/admin-api/java-api/target/
/admin-web/typescript-ant-design-vue/node_modules/

View File

@ -1,4 +1,3 @@
### SmartAdmin 2.0 (即 vue3 的 js版本和ts版本 )正在火热开发中,欢迎持续关注
#### 简介 #### 简介
SmartAdmin由河南·洛阳 [1024创新实验室](https://www.1024lab.net/)团队研发的一套互联网企业级的通用型中后台解决方案使用最前沿的前后台技术栈SpringBoot和Vue前后端分离<font color="#DC143C">**我们开源一套漂亮的代码和一套整洁的代码规范**</font>让大家在这浮躁的代码世界里感受到一股把代码写好的清流同时又让开发者节省大量的时间减少加班快乐工作热爱生活。SmartAdmin 让你从认识到忘不了,绝对是你最想要的! SmartAdmin由河南·洛阳 [1024创新实验室](https://www.1024lab.net/)团队研发的一套互联网企业级的通用型中后台解决方案使用最前沿的前后台技术栈SpringBoot和Vue前后端分离<font color="#DC143C">**我们开源一套漂亮的代码和一套整洁的代码规范**</font>让大家在这浮躁的代码世界里感受到一股把代码写好的清流同时又让开发者节省大量的时间减少加班快乐工作热爱生活。SmartAdmin 让你从认识到忘不了,绝对是你最想要的!

401
admin-api/java-api/pom.xml Normal file
View File

@ -0,0 +1,401 @@
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.1</version>
</parent>
<groupId>net.1024lab</groupId>
<artifactId>smart-admin</artifactId>
<version>2.0.0</version>
<packaging>jar</packaging>
<name>smart-admin</name>
<description>smart-admin project</description>
<properties>
<java.version>1.8</java.version>
<jodconverter.version>4.3.0</jodconverter.version>
<libreoffice.version>6.4.3</libreoffice.version>
<mybatis-plus.version>3.4.1</mybatis-plus.version>
<p6spy.version>3.8.6</p6spy.version>
<swagger.version>2.7.0</swagger.version>
<fast-json.version>1.2.62</fast-json.version>
<druid.version>1.2.6</druid.version>
<jjwt.version>0.9.1</jjwt.version>
<easypoi.version>4.2.0</easypoi.version>
<okhttp.version>4.2.2</okhttp.version>
<kaptcha.version>2.3.2</kaptcha.version>
<commons-io.version>20030203.000550</commons-io.version>
<google-guava.version>30.1.1-jre</google-guava.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- spring security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<exclusions>
<exclusion>
<artifactId>error_prone_annotations</artifactId>
<groupId>com.google.errorprone</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-mock</artifactId>
<version>2.0.8</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>${p6spy.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
<exclusions>
<exclusion>
<artifactId>guava</artifactId>
<groupId>com.google.guava</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fast-json.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jjwt.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${google-guava.version}</version>
</dependency>
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>${kaptcha.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<!-- poi start -->
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-spring-boot-starter</artifactId>
<version>${easypoi.version}</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<version>${easypoi.version}</version>
<exclusions>
<exclusion>
<artifactId>javassist</artifactId>
<groupId>org.javassist</groupId>
</exclusion>
<exclusion>
<artifactId>guava</artifactId>
<groupId>com.google.guava</groupId>
</exclusion>
<exclusion>
<artifactId>xmlbeans</artifactId>
<groupId>org.apache.xmlbeans</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-web</artifactId>
<version>${easypoi.version}</version>
<exclusions>
<exclusion>
<artifactId>javassist</artifactId>
<groupId>org.javassist</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-annotation</artifactId>
<version>${easypoi.version}</version>
</dependency>
<!-- poi end -->
<dependency>
<groupId>com.googlecode.concurrentlinkedhashmap</groupId>
<artifactId>concurrentlinkedhashmap-lru</artifactId>
<version>1.4.2</version>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.11</version>
<exclusions>
<exclusion>
<artifactId>guava</artifactId>
<groupId>com.google.guava</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- 集合工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>1.11.842</version>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- sax 读取时候用到的 -->
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>4.1.1</version>
</dependency>
<!-- Word 需要使用 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>ooxml-schemas</artifactId>
<version>1.3</version>
</dependency>
<!--velocity begin-->
<dependency>
<artifactId>velocity</artifactId>
<groupId>org.apache.velocity</groupId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.0</version>
</dependency>
<!--velocity end-->
</dependencies>
<build>
<finalName>${profiles.active}-${project.name}</finalName>
<resources>
<resource>
<filtering>false</filtering>
<directory>src/main/resources</directory>
<excludes>
<exclude>dev/*</exclude>
<exclude>sit/*</exclude>
<exclude>pre/*</exclude>
<exclude>prod/*</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources/${profiles.active}</directory>
<filtering>true</filtering>
<includes>
<include>*.properties</include>
</includes>
</resource>
<resource>
<directory>src/main/resources/${profiles.active}</directory>
<filtering>false</filtering>
<includes>
<include>*.*</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<testFailureIgnore>true</testFailureIgnore>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>dev</id>
<properties>
<profiles.active>dev</profiles.active>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>sit</id>
<properties>
<profiles.active>sit</profiles.active>
</properties>
</profile>
<profile>
<id>pre</id>
<properties>
<profiles.active>pre</profiles.active>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<profiles.active>prod</profiles.active>
</properties>
</profile>
</profiles>
</project>

View File

@ -1,5 +1,7 @@
package net.lab1024.smartadmin; package net.lab1024.smartadmin.service;
import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.annotation.EnableCaching;
@ -9,21 +11,18 @@ import org.springframework.scheduling.annotation.EnableScheduling;
/** /**
* [ admin 项目启动类 ] * [ admin 项目启动类 ]
* *
* @author yandanyang * @author 罗伊
* @version 1.0
* @company 1024lab.net
* @copyright (c) 2019 1024lab.netInc. All rights reserved.
* @date
* @since JDK1.8
* *
*/ */
@SpringBootApplication(scanBasePackages = {"net.lab1024.smartadmin", "cn.afterturn.easypoi"}) @SpringBootApplication
@EnableCaching @EnableCaching
@EnableScheduling @EnableScheduling
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true) @EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
@MapperScan(value = "net.lab1024.smartadmin.service.*",basePackageClasses = Mapper.class)
public class SmartAdminApplication { public class SmartAdminApplication {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(SmartAdminApplication.class, args); SpringApplication.run(SmartAdminApplication.class, args);
System.out.println("####################### smart-admin-service start #######################");
} }
} }

View File

@ -1,4 +1,4 @@
package net.lab1024.smartadmin.common.reload.annotation; package net.lab1024.smartadmin.service.common.anno;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
@ -6,13 +6,12 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
/** /**
* 定义 SmartReload 注解 * [ ]
* *
* @author zhuoda * @author 罗伊
* @date 2020/8/21 14:55
*/ */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface SmartReload { @Target(ElementType.METHOD)
public @interface NoNeedLogin {
String value();
} }

View File

@ -0,0 +1,29 @@
package net.lab1024.smartadmin.service.common.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 标记 需要防止重复提交 的注解
*
* @author listen
* @date 2020年11月25日 10:56:58
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface NoRepeatSubmit {
/**
* 重复提交间隔时间/毫秒
*
* @return
*/
int value() default 1200;
/**
* 最长间隔30s
*/
int MAX_INTERVAL = 30000;
}

View File

@ -0,0 +1,14 @@
package net.lab1024.smartadmin.service.common.anno;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* [ ]
*
* @author 罗伊
* @date 2020/8/21 15:02
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface NoValidPrivilege {
}

View File

@ -1,6 +1,4 @@
package net.lab1024.smartadmin.module.system.department; package net.lab1024.smartadmin.service.common.codeconst;
import net.lab1024.smartadmin.common.constant.ResponseCodeConst;
/** /**
* 部门返回信息常量类 * 部门返回信息常量类

View File

@ -1,12 +1,10 @@
package net.lab1024.smartadmin.module.system.employee.constant; package net.lab1024.smartadmin.service.common.codeconst;
import net.lab1024.smartadmin.common.constant.ResponseCodeConst;
/** /**
* 员工常量类 * 员工常量类
* 3001-3999 * 3001-3999
* *
* @author lidoudou * @author 开云
* @date 2017年12月19日下午19:04:52 * @date 2017年12月19日下午19:04:52
*/ */
public class EmployeeResponseCodeConst extends ResponseCodeConst { public class EmployeeResponseCodeConst extends ResponseCodeConst {
@ -34,16 +32,18 @@ public class EmployeeResponseCodeConst extends ResponseCodeConst {
/** /**
* 您的账号已被禁用不得登录系统 * 您的账号已被禁用不得登录系统
*/ */
public static final EmployeeResponseCodeConst IS_DISABLED = new EmployeeResponseCodeConst(3005, "您的账号已被禁用,不得登录系统!"); public static final EmployeeResponseCodeConst IS_DISABLED = new EmployeeResponseCodeConst(3005, "您的账号已被禁用");
/** /**
* 登录名已存在 * 登录名已存在
*/ */
public static final EmployeeResponseCodeConst LOGIN_NAME_EXISTS = new EmployeeResponseCodeConst(3006, "登录名已存在!"); public static final EmployeeResponseCodeConst LOGIN_NAME_EXISTS = new EmployeeResponseCodeConst(3006, "登录名已存在!");
/** /**
* 密码输入有误请重新输入 10115 * 密码输入有误请重新输入
*/ */
public static final EmployeeResponseCodeConst PASSWORD_ERROR = new EmployeeResponseCodeConst(3007, "密码输入有误,请重新输入"); public static final EmployeeResponseCodeConst PASSWORD_ERROR = new EmployeeResponseCodeConst(3007, "密码错误");
/** /**
* 手机号已存在 * 手机号已存在
*/ */
@ -53,7 +53,12 @@ public class EmployeeResponseCodeConst extends ResponseCodeConst {
public static final EmployeeResponseCodeConst BIRTHDAY_ERROR = new EmployeeResponseCodeConst(3010, "生日格式不正确"); public static final EmployeeResponseCodeConst BIRTHDAY_ERROR = new EmployeeResponseCodeConst(3010, "生日格式不正确");
public static final EmployeeResponseCodeConst VERIFICATION_CODE_INVALID = new EmployeeResponseCodeConst(3011, "验证码无效"); public static final EmployeeResponseCodeConst VERIFICATION_CODE_INVALID = new EmployeeResponseCodeConst(3011, "请输入正确的验证码");
/**
* 用户名或密码错误 多次错误用于前端判断展示验证码
*/
public static final EmployeeResponseCodeConst LOGIN_FAILED_REPEATEDLY = new EmployeeResponseCodeConst(3012, "用户名或密码错误!");
public EmployeeResponseCodeConst(int code, String msg) { public EmployeeResponseCodeConst(int code, String msg) {
super(code, msg); super(code, msg);

View File

@ -0,0 +1,47 @@
package net.lab1024.smartadmin.service.common.codeconst;
/**
* [ ]
*
* @author 罗伊
* @date 2020/8/25 11:57
*/
public class FileResponseCodeConst extends ResponseCodeConst {
/**
* 4001 -4999
*/
public static final FileResponseCodeConst FILE_EMPTY = new FileResponseCodeConst(4001, "上传文件不能为空");
public static final FileResponseCodeConst FILE_SIZE_ERROR = new FileResponseCodeConst(4002, "上传文件超过%s请重新上传");
public static final FileResponseCodeConst UNKNOWN_FILE_TYPE = new FileResponseCodeConst(4003, "未知的文件类型!");
public static final FileResponseCodeConst LOCAL_UPDATE_PREFIX_ERROR = new FileResponseCodeConst(4004, "文件本地上传缺少URL前缀配置[local_upload_url_prefix]");
public static final FileResponseCodeConst UPLOAD_ERROR = new FileResponseCodeConst(4005, "上传失败");
public static final FileResponseCodeConst URL_ERROR = new FileResponseCodeConst(4006, "获取URL失败");
public static final FileResponseCodeConst FILE_MODULE_ERROR = new FileResponseCodeConst(4007, "文件目录类型错误");
public static final FileResponseCodeConst FILE_NOT_EXIST = new FileResponseCodeConst(4008, "文件不存在");
public static final FileResponseCodeConst DOWNLOAD_ERROR = new FileResponseCodeConst(4009, "文件下载失败");
public static final FileResponseCodeConst VOD_SERVICE_ERROR = new FileResponseCodeConst(4010, "VOD服务错误");
public static final FileResponseCodeConst VOD_FILE_ERROR = new FileResponseCodeConst(4011, "请上传正确的音/视频格式");
public static final FileResponseCodeConst VOD_FILE_NOT_EXIST = new FileResponseCodeConst(4012, "视频文件不存在");
public static final FileResponseCodeConst FILE_NAME_ERROR = new FileResponseCodeConst(4013, "文件名称必须1-100个字符");
public static final FileResponseCodeConst VOD_TOKEN_ERROR = new FileResponseCodeConst(4014, "视频文件TOKEN失效");
public static final FileResponseCodeConst VOD_CIPHER_TEXT_ERROR = new FileResponseCodeConst(4015, "视频文件CIPHER_TEXT无效");
public FileResponseCodeConst(int code, String msg) {
super(code, msg);
}
}

View File

@ -1,12 +1,10 @@
package net.lab1024.smartadmin.module.system.login; package net.lab1024.smartadmin.service.common.codeconst;
import net.lab1024.smartadmin.common.constant.ResponseCodeConst;
/** /**
* 员工常量类 * 员工常量类
* 1001-1999 * 1001-1999
* *
* @author lidoudou * @author 开云
* @date 2017年12月19日下午19:04:52 * @date 2017年12月19日下午19:04:52
*/ */
public class LoginResponseCodeConst extends ResponseCodeConst { public class LoginResponseCodeConst extends ResponseCodeConst {

View File

@ -1,9 +1,7 @@
package net.lab1024.smartadmin.module.system.position; package net.lab1024.smartadmin.service.common.codeconst;
import net.lab1024.smartadmin.common.constant.ResponseCodeConst;
/** /**
* @author zzr * @author
*/ */
public class PositionResponseCodeConst extends ResponseCodeConst { public class PositionResponseCodeConst extends ResponseCodeConst {

View File

@ -1,16 +1,8 @@
package net.lab1024.smartadmin.module.system.privilege.constant; package net.lab1024.smartadmin.service.common.codeconst;
import net.lab1024.smartadmin.common.constant.ResponseCodeConst;
/** /**
* * @author 罗伊
* @author yandanyang
* @version 1.0
* @company 1024lab.net
* @copyright (c) 2019 1024lab.netInc. All rights reserved.
* @date
* @since JDK1.8
*/ */
public class PrivilegeResponseCodeConst extends ResponseCodeConst { public class PrivilegeResponseCodeConst extends ResponseCodeConst {

View File

@ -0,0 +1,111 @@
package net.lab1024.smartadmin.service.common.codeconst;
import lombok.extern.slf4j.Slf4j;
/**
* 1 表示成功
* 10 - 100 表示 系统异常即java报错了
* 100 以上 业务
* <p>
* 根据实际业务设置范围值
* 正常100就够了
*
* @author Administrator
*/
@Slf4j
public class ResponseCodeConst {
public static final ResponseCodeConst SUCCESS = new ResponseCodeConst(1, "操作成功!", true);
public static final ResponseCodeConst SYSTEM_ERROR = new ResponseCodeConst(11, "系统繁忙,请稍后重试");
public static final ResponseCodeConst ERROR_PARAM = new ResponseCodeConst(101, "参数异常");
public static final ResponseCodeConst ERROR_PARAM_ANY = new ResponseCodeConst(102, "%s参数异常");
public static final ResponseCodeConst DEVELOPMENT = new ResponseCodeConst(112, "此功能正在开发中");
public static final ResponseCodeConst NOT_EXISTS = new ResponseCodeConst(113, "数据不存在");
public static final ResponseCodeConst REQUEST_METHOD_ERROR = new ResponseCodeConst(114, "请求方式错误");
public static final ResponseCodeConst JSON_FORMAT_ERROR = new ResponseCodeConst(115, "请求前端参数格式错误");
public static final ResponseCodeConst PERMISSION_DENIED = new ResponseCodeConst(116, "您没有权限修改数据");
public static final ResponseCodeConst ALREADY_EXIST = new ResponseCodeConst(117, "数据已存在");
public static final ResponseCodeConst STATUS_ERROR = new ResponseCodeConst(118, "数据状态异常");
public static final ResponseCodeConst AREA_ERROR = new ResponseCodeConst(119, "地区数据错误");
public static final ResponseCodeConst REQUEST_ERROR = new ResponseCodeConst(120, "请求异常");
public static final ResponseCodeConst TOKEN_ERROR = new ResponseCodeConst(121, "登录失效,请重新登录");
public static final ResponseCodeConst BUSINESS_HANDING = new ResponseCodeConst(122, "业务正在繁忙处理中,请稍后再试");
public static final ResponseCodeConst NOT_SUPPORT = new ResponseCodeConst(123, "暂不支持");
public static final ResponseCodeConst REPEAT_SUBMIT = new ResponseCodeConst(125, "太...太快了,请您稍后重试~");
protected int code;
protected String msg;
protected boolean success;
public ResponseCodeConst() {
}
protected ResponseCodeConst(int code, String msg) {
super();
this.code = code;
this.msg = msg;
ResponseCodeContainer.put(this);
}
protected ResponseCodeConst(int code, String msg, boolean success) {
super();
this.code = code;
this.msg = msg;
this.success = success;
ResponseCodeContainer.put(this);
}
protected ResponseCodeConst(int code) {
super();
this.code = code;
ResponseCodeContainer.put(this);
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public boolean issucc() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public static void init() {
log.info("-------------- ResponseCodeConst init -------------");
}
}

View File

@ -0,0 +1,73 @@
package net.lab1024.smartadmin.service.common.codeconst;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
* [ ]
*
* @author 罗伊
* @date 2021/1/8 11:28
*/
@Slf4j
public class ResponseCodeContainer {
private static final Map<Integer, ResponseCodeConst> RESPONSE_CODE_MAP = new HashMap<>();
private static final Map<Class<? extends ResponseCodeConst>, int[]> RESPONSE_CODE_RANGE_MAP = new HashMap<>();
/**
* id的范围[start, end]左闭右闭
*
* @param clazz
* @param start
* @param end
*/
public static void register(Class<? extends ResponseCodeConst> clazz, int start, int end) {
if (start > end) {
throw new IllegalArgumentException("<ResponseDTO> start > end!");
}
if (RESPONSE_CODE_RANGE_MAP.containsKey(clazz)) {
throw new IllegalArgumentException(String.format("<ResponseDTO> Class:%s already exist!", clazz.getSimpleName()));
}
RESPONSE_CODE_RANGE_MAP.forEach((k, v) -> {
if ((start >= v[0] && start <= v[1]) || (end >= v[0] && end <= v[1])) {
throw new IllegalArgumentException(String.format("<ResponseDTO> Class:%s 's id range[%d,%d] has " + "intersection with " + "class:%s", clazz.getSimpleName(), start, end,
k.getSimpleName()));
}
});
RESPONSE_CODE_RANGE_MAP.put(clazz, new int[]{start, end});
// 提前初始化static变量进行范围检测
Field[] fields = clazz.getFields();
if (fields.length != 0) {
try {
fields[0].get(clazz);
} catch (IllegalArgumentException | IllegalAccessException e) {
log.error("", e);
}
}
}
public static void put(ResponseCodeConst codeConst) {
int[] idRange = RESPONSE_CODE_RANGE_MAP.get(codeConst.getClass());
if (idRange == null) {
throw new IllegalArgumentException(String.format("<ResponseDTO> Class:%s has not been registered!", codeConst.getClass().getSimpleName()));
}
int code = codeConst.code;
if (code < idRange[0] || code > idRange[1]) {
throw new IllegalArgumentException(String.format("<ResponseDTO> Id(%d) out of range[%d,%d], " + "class:%s", code, idRange[0], idRange[1], codeConst.getClass().getSimpleName()));
}
if (RESPONSE_CODE_MAP.keySet().contains(code)) {
log.error(String.format("<ResponseDTO> Id(%d) out of range[%d,%d], " + "class:%s code is repeat!", code, idRange[0], idRange[1], codeConst.getClass().getSimpleName()));
System.exit(0);
}
RESPONSE_CODE_MAP.put(code, codeConst);
}
}

View File

@ -0,0 +1,30 @@
package net.lab1024.smartadmin.service.common.codeconst;
import lombok.extern.slf4j.Slf4j;
/**
* [ ]
*
* @author 罗伊
* @date 2021/1/8 15:59
*/
@Slf4j
public class ResponseCodeRegister {
// 范围声明
static {
// 系统功能从0开始step=1000
ResponseCodeContainer.register(ResponseCodeConst.class, 0, 1000);
ResponseCodeContainer.register(LoginResponseCodeConst.class, 1001, 1999);
ResponseCodeContainer.register(DepartmentResponseCodeConst.class, 2001, 2999);
ResponseCodeContainer.register(EmployeeResponseCodeConst.class, 3001, 3999);
ResponseCodeContainer.register(FileResponseCodeConst.class, 4001, 5000);
ResponseCodeContainer.register(RoleResponseCodeConst.class, 6001, 6999);
ResponseCodeContainer.register(PrivilegeResponseCodeConst.class, 7001, 7999);
ResponseCodeContainer.register(PositionResponseCodeConst.class, 13000, 13999);
}
public static void init() {
log.info("-------------- ResponseCodeConst init -------------");
}
}

View File

@ -1,9 +1,7 @@
package net.lab1024.smartadmin.module.system.role.basic; package net.lab1024.smartadmin.service.common.codeconst;
import net.lab1024.smartadmin.common.constant.ResponseCodeConst;
/** /**
* * @author 罗伊
* @author yandanyang
* 角色业务状态码 6001 - 6999 * 角色业务状态码 6001 - 6999
*/ */
public class RoleResponseCodeConst extends ResponseCodeConst { public class RoleResponseCodeConst extends ResponseCodeConst {

View File

@ -1,4 +1,4 @@
package net.lab1024.smartadmin.common.domain; package net.lab1024.smartadmin.service.common.constant;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONAware; import com.alibaba.fastjson.JSONAware;
@ -10,6 +10,8 @@ import java.util.LinkedHashMap;
import java.util.Objects; import java.util.Objects;
/** /**
* 枚举类接口
*
* @author listen * @author listen
* @date 2018-07-17 下午 3:52 * @date 2018-07-17 下午 3:52
*/ */
@ -18,7 +20,7 @@ public interface BaseEnum {
/** /**
* 获取枚举类的值 * 获取枚举类的值
* *
* @return Object * @return
*/ */
Object getValue(); Object getValue();
@ -67,24 +69,21 @@ public interface BaseEnum {
String enumJson = JSON.toJSONString(json, true); String enumJson = JSON.toJSONString(json, true);
enumJson = enumJson.replaceAll("\"", ""); enumJson = enumJson.replaceAll("\"", "");
enumJson= enumJson.replaceAll("\t","&nbsp;&nbsp;"); enumJson = enumJson.replaceAll("\t", "&nbsp;&nbsp;");
enumJson = enumJson.replaceAll("\n","<br>"); enumJson = enumJson.replaceAll("\n", "<br>");
String prefix = " <br> export const <br> " + CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, clazz.getSimpleName() + " = <br> "); String prefix = " <br> export const <br> " + CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, clazz.getSimpleName() + " = <br> ");
return prefix + "" + enumJson + " <br>"; return prefix + "" + enumJson + " <br>";
} }
@Data @Data
class DeletedQuotationAware implements JSONAware { class DeletedQuotationAware implements JSONAware {
private String value; private String value;
public DeletedQuotationAware(Object value) { public DeletedQuotationAware(Object value) {
if(value == null){ if (value instanceof String) {
this.value = "";
}else if (value instanceof String) {
this.value = "'" + value + "'"; this.value = "'" + value + "'";
}else { } else {
this.value = value.toString(); this.value = value.toString();
} }
} }

View File

@ -0,0 +1,63 @@
package net.lab1024.smartadmin.service.common.constant;
/**
* @author 罗伊
* @date 2021-01-31 0:00
*/
public class CacheModuleBaseConst {
public static class Employee {
/**
* 某个部门下的员工缓存
*/
public static final String DEPARTMENT_EMPLOYEE_CACHE = "department_employee_cache";
/**
* 单个员工的缓存
*/
public static final String SINGLE_EMPLOYEE_CACHE = "single_employee_cache";
/**
* 单个员工角色
*/
public static final String SINGLE_EMPLOYEE_ROLE_CACHE = "single_employee_role_cache";
}
public static class Department {
/**
* 部门树
*/
public static final String DEPARTMENT_CACHE = "department_cache";
/**
* 部门树
*/
public static final String DEPARTMENT_TREE_CACHE = "department_tree_cache";
/**
* 某个部门以及下级的id列表
*/
public static final String DEPARTMENT_TREE_ID_CACHE = "department_tree_id_cache";
}
public static class Category {
/**
* 类目
*/
public static final String CATEGORY = "category_cache";
/**
* 类目子级
*/
public static final String CATEGORY_SUB = "category_sub_cache";
/**
* 类目层级树 缓存
*/
public static final String CATEGORY_TREE = "category_tree_cache";
}
}

View File

@ -0,0 +1,150 @@
package net.lab1024.smartadmin.service.common.constant;
import com.google.common.collect.ImmutableSet;
import org.springframework.util.CollectionUtils;
import java.util.*;
/**
* [ ]
*
* @author 罗伊
* @date 2020/9/14 15:48
*/
public class CommonConst {
/**
* 全局通用分隔符
*/
public static final String SEPARATOR = ",";
/**
* 全局通用分隔符 逗号
*/
public static final Character SEPARATOR_CHAR = ',';
/**
* 全局通用分隔符 斜杠
*/
public static final String SEPARATOR_SLASH = "/";
/**
* 全局通用分隔符 下划线
*/
public static final String UNDERLINE = "_";
/**
* 空字符串
*/
public static final String EMPTY_STR = "";
/**
* MaP
* 注意放入元素会抛异常
*/
public static final Map EMPTY_MAP = Collections.unmodifiableMap(new HashMap<>(0));
/**
* list
* 注意放入元素会抛异常
*/
public static final List EMPTY_LIST = Collections.unmodifiableList(new ArrayList<>(0));
/**
* 空字符串
*/
public static final long DEFAULT_PARENT_ID = 0L;
/**
* 空字符串
*/
public static final int ZERO = 0;
public static final class Token {
public static final String DEFAULT_TOKEN = "default-token-";
public static final String INNER_TOKEN_NAME = "inner-token";
public static final String OUTER_TOKEN_NAME = "x-access-token";
}
public static final class FileFolderConst {
/**
* 公用读取文件夹 public
*/
public static final String FOLDER_PUBLIC = "pu";
/**
* 私有读取文件夹 private
*/
public static final String FOLDER_PRIVATE = "pr";
/**
* 文件夹格式
*/
public static final String FOLDER_FORMAT = "folder";
}
public static final class System {
/**
* oss url redis 过期时间
*/
public static final int FILE_URL_EXPIRE_SECOND = 3600;
public static final int FILE_VO_EXPIRE_SECOND = 86400;
}
/**
* 长度类常量
*/
public static final class NumberLimit {
/**
* 文件名称长度
*/
public static final int FILE_NAME = 100;
}
public static final class ApiUrl {
/**
* 基础 api 前缀
*/
public static final String API_PREFIX_SUPPORT = "/support";
/**
* 后管 api 前缀
*/
public static final String API_PREFIX_ADMIN = "/admin";
}
public static final class Password {
public static final String DEFAULT = "123456";
public static final String SALT_FORMAT = "smart_%s_admin";
}
public static final class CommonCollection {
public static final Set<String> IGNORE_URL = ImmutableSet.of("/swagger", "Excel", "/h5/api");
public static Boolean contain(Set<String> ignores, String uri) {
if (CollectionUtils.isEmpty(ignores)) {
return false;
}
for (String ignoreUrl : ignores) {
if (uri.startsWith(ignoreUrl)) {
return true;
}
}
return false;
}
}
}

View File

@ -0,0 +1,52 @@
package net.lab1024.smartadmin.service.common.constant;
/**
* 性别枚举类
*
* @author listen
* @date 2019/09/24 16:50
*/
public enum GenderEnum implements BaseEnum {
/**
* 0 未知
*/
UNKNOWN(0, "未知"),
/**
* 1 奇数为阳
*/
MAN(1, ""),
/**
* 2 偶数为阴
*/
WOMAN(2, "");
private final Integer gender;
private final String desc;
GenderEnum(Integer gender, String desc) {
this.gender = gender;
this.desc = desc;
}
/**
* 获取枚举类的值
*
* @return Integer
*/
@Override
public Integer getValue() {
return gender;
}
/**
* 获取枚举类的说明
*
* @return String
*/
@Override
public String getDesc() {
return desc;
}
}

View File

@ -0,0 +1,27 @@
package net.lab1024.smartadmin.service.common.constant;
/**
* redis key 常量类
*
* @author listen
* @date 2019/09/23 20:48
*/
public class RedisKeyConst {
public class Base {
private static final String PROJECT = "base:";
public static final String FILE_URL = PROJECT + "file:";
public static final String FILE_VO = PROJECT + "fileVO:";
public static final String LOCK = PROJECT + "lock:";
public static final String ID_GENERATOR = LOCK + "id:";
public static final String CAPTCHA = PROJECT + "captcha:";
}
}

View File

@ -0,0 +1,55 @@
package net.lab1024.smartadmin.service.common.constant;
/**
* [ ]
*
* @author 罗伊
* @date 2021/1/9 11:45
*/
public class SwaggerTagConst {
public static class Support {
public static final String FILE = "基础-文件服务";
public static final String CACHE = "基础-缓存";
public static final String SYSTEM_CONFIG = "基础-系统参数";
public static final String ID_GENERATOR = "基础-ID生成器";
public static final String SMART_RELOAD = "基础-Reload";
public static final String TASK_SCHEDULER = "基础-任务调度";
public static final String USER_OPERATE_LOG = "基础-用户操作日志";
public static final String HEART_BEAT = "基础-心跳日志";
public static final String CAPTCHA = "基础-图形验证码";
}
public static class Admin {
public static final String MANAGER_EMPLOYEE = "管理端-员工";
public static final String MANAGER_EMPLOYEE_LOGIN = "管理端-员工登录";
public static final String MANAGER_DEPARTMENT = "管理端-部门";
public static final String MANAGER_ROLE = "管理端-角色";
public static final String MANAGER_NOTICE = "管理端-系统通知";
public static final String MANAGER_MENU = "管理端-菜单";
public static final String MANAGER_ROLE_MENU = "管理端-角色-菜单";
public static final String MANAGER_DATA_TRACER = "管理端-数据变动跟踪";
public static final String MANAGER_CATEGORY = "管理端-分类";
public static final String MANAGER_GOODS = "管理端-商品业务";
}
}

View File

@ -1,6 +1,4 @@
package net.lab1024.smartadmin.constant; package net.lab1024.smartadmin.service.common.constant;
import net.lab1024.smartadmin.common.domain.BaseEnum;
/** /**
* 系统环境枚举类 * 系统环境枚举类
@ -10,7 +8,6 @@ import net.lab1024.smartadmin.common.domain.BaseEnum;
*/ */
public enum SystemEnvironmentEnum implements BaseEnum { public enum SystemEnvironmentEnum implements BaseEnum {
/** /**
* dev * dev
*/ */
@ -31,19 +28,14 @@ public enum SystemEnvironmentEnum implements BaseEnum {
*/ */
PROD("prod", "生产环境"); PROD("prod", "生产环境");
private final String value;
public static final String DEV_ENV = "dev"; private final String desc;
private String value;
private String desc;
SystemEnvironmentEnum(String value, String desc) { SystemEnvironmentEnum(String value, String desc) {
this.value = value; this.value = value;
this.desc = desc; this.desc = desc;
} }
/** /**
* 获取定义枚举value值 * 获取定义枚举value值
* *

View File

@ -0,0 +1,15 @@
package net.lab1024.smartadmin.service.common.controller;
import lombok.extern.slf4j.Slf4j;
import net.lab1024.smartadmin.service.common.constant.CommonConst;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author: 卓大
* @create: 2020-03-31 08:54 PM from win10
*/
@Slf4j
@RequestMapping(CommonConst.ApiUrl.API_PREFIX_ADMIN)
public class AdminBaseController {
}

View File

@ -0,0 +1,14 @@
package net.lab1024.smartadmin.service.common.controller;
import net.lab1024.smartadmin.service.common.constant.CommonConst;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 通用业务路由基类
*
* @author 胡克
* @date 2019/10/18 11:51
*/
@RequestMapping(CommonConst.ApiUrl.API_PREFIX_SUPPORT)
public class SupportBaseController {
}

View File

@ -0,0 +1,52 @@
package net.lab1024.smartadmin.service.common.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.Valid;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.List;
/**
* 分页基础参数
*
* @author 善逸
* @Date Created in 2017/10/28 16:19
*/
@Data
public class PageBaseDTO {
@ApiModelProperty(value = "页码(不能为空)", required = true, example = "1")
@NotNull(message = "分页参数不能为空")
private Integer pageNum;
@ApiModelProperty(value = "每页数量(不能为空)", required = true, example = "10")
@NotNull(message = "每页数量不能为空")
@Max(value = 200, message = "每页最大为200")
private Integer pageSize;
@ApiModelProperty("排序字段集合")
@Size(max = 10, message = "排序字段最多10")
@Valid
private List<SortItemDTO> sortItemList;
/**
* 排序DTO类
*/
@Data
public static class SortItemDTO {
@ApiModelProperty("true正序|false倒序")
@NotNull(message = "排序规则不能为空")
private Boolean isAsc;
@ApiModelProperty(value = "排序字段")
@NotBlank(message = "排序字段不能为空")
@Length(max = 30, message = "排序字段最多30")
private String column;
}
}

View File

@ -1,4 +1,4 @@
package net.lab1024.smartadmin.common.domain; package net.lab1024.smartadmin.service.common.domain;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@ -8,7 +8,7 @@ import java.util.List;
/** /**
* Page返回对象 * Page返回对象
* *
* @Author lihaifan * @Author 善逸
* @Date Created in 2017/10/31 15:05 * @Date Created in 2017/10/31 15:05
*/ */
@Data @Data
@ -44,4 +44,7 @@ public class PageResultDTO<T> {
@ApiModelProperty(value = "结果集") @ApiModelProperty(value = "结果集")
private List<T> list; private List<T> list;
@ApiModelProperty("是否为空")
private Boolean emptyFlag;
} }

View File

@ -1,13 +1,13 @@
package net.lab1024.smartadmin.common.domain; package net.lab1024.smartadmin.service.common.domain;
import net.lab1024.smartadmin.common.constant.ResponseCodeConst; import net.lab1024.smartadmin.service.common.codeconst.ResponseCodeConst;
/** /**
* 返回类 * 返回类
* *
* @param <T> * @param <T>
* @author zhuoda * @author 1024lab
*/ */
public class ResponseDTO<T> { public class ResponseDTO<T> {
@ -25,7 +25,7 @@ public class ResponseDTO<T> {
public ResponseDTO(ResponseCodeConst responseCodeConst, String msg) { public ResponseDTO(ResponseCodeConst responseCodeConst, String msg) {
this.code = responseCodeConst.getCode(); this.code = responseCodeConst.getCode();
this.msg = msg; this.msg = msg;
this.success = responseCodeConst.isSuccess(); this.success = responseCodeConst.issucc();
} }
public ResponseDTO(ResponseCodeConst responseCodeConst, T data) { public ResponseDTO(ResponseCodeConst responseCodeConst, T data) {
@ -33,7 +33,7 @@ public class ResponseDTO<T> {
this.code = responseCodeConst.getCode(); this.code = responseCodeConst.getCode();
this.msg = responseCodeConst.getMsg(); this.msg = responseCodeConst.getMsg();
this.data = data; this.data = data;
this.success = responseCodeConst.isSuccess(); this.success = responseCodeConst.issucc();
} }
public ResponseDTO(ResponseCodeConst responseCodeConst, T data, String msg) { public ResponseDTO(ResponseCodeConst responseCodeConst, T data, String msg) {
@ -41,13 +41,13 @@ public class ResponseDTO<T> {
this.code = responseCodeConst.getCode(); this.code = responseCodeConst.getCode();
this.msg = msg; this.msg = msg;
this.data = data; this.data = data;
this.success = responseCodeConst.isSuccess(); this.success = responseCodeConst.issucc();
} }
private ResponseDTO(ResponseCodeConst responseCodeConst) { private ResponseDTO(ResponseCodeConst responseCodeConst) {
this.code = responseCodeConst.getCode(); this.code = responseCodeConst.getCode();
this.msg = responseCodeConst.getMsg(); this.msg = responseCodeConst.getMsg();
this.success = responseCodeConst.isSuccess(); this.success = responseCodeConst.issucc();
} }
public ResponseDTO(ResponseDTO responseDTO) { public ResponseDTO(ResponseDTO responseDTO) {
@ -56,6 +56,18 @@ public class ResponseDTO<T> {
this.success = responseDTO.isSuccess(); this.success = responseDTO.isSuccess();
} }
public ResponseDTO(ResponseCodeConst codeConst, boolean isSuccess) {
this.code = codeConst.getCode();
this.msg = codeConst.getMsg();
this.success = isSuccess;
}
public ResponseDTO(int code, String msg, boolean isSuccess) {
this.code = code;
this.msg = msg;
this.success = isSuccess;
}
public static <T> ResponseDTO<T> succ() { public static <T> ResponseDTO<T> succ() {
return new ResponseDTO(ResponseCodeConst.SUCCESS); return new ResponseDTO(ResponseCodeConst.SUCCESS);
} }
@ -68,27 +80,34 @@ public class ResponseDTO<T> {
return new ResponseDTO(ResponseCodeConst.SUCCESS, data); return new ResponseDTO(ResponseCodeConst.SUCCESS, data);
} }
public static <T> ResponseDTO succMsg(String msg) { public static ResponseDTO succMsg(String msg) {
return new ResponseDTO(ResponseCodeConst.SUCCESS, msg); return new ResponseDTO(ResponseCodeConst.SUCCESS, msg);
} }
public static <T> ResponseDTO<T> wrap(ResponseCodeConst codeConst) { public static <T> ResponseDTO<T> wrap(ResponseCodeConst codeConst) {
return new ResponseDTO<>(codeConst); return new ResponseDTO<>(codeConst);
} }
public static <T> ResponseDTO<T> wrap(ResponseCodeConst codeConst, T t) { public static <T> ResponseDTO<T> wrap(ResponseCodeConst codeConst, boolean isSuccess) {
return new ResponseDTO<T>(codeConst, t); return new ResponseDTO<>(codeConst, isSuccess);
} }
public static <T> ResponseDTO<T> wrap(ResponseCodeConst codeConst, String msg) { public static ResponseDTO wrap(ResponseDTO responseDTO) {
return new ResponseDTO<T>(codeConst, msg); return new ResponseDTO<>(responseDTO.getCode(), responseDTO.getMsg(), responseDTO.isSuccess());
}
public static <T> ResponseDTO<T> wrapData(ResponseCodeConst codeConst, T t) {
return new ResponseDTO<T>(codeConst, t);
} }
public static <T> ResponseDTO<T> wrapMsg(ResponseCodeConst codeConst, String msg) { public static <T> ResponseDTO<T> wrapMsg(ResponseCodeConst codeConst, String msg) {
return new ResponseDTO<T>(codeConst, msg); return new ResponseDTO<T>(codeConst, msg);
} }
public static <T> ResponseDTO<T> wrapDataMsg(ResponseCodeConst codeConst, T t, String msg) {
return new ResponseDTO<T>(codeConst, t, msg);
}
public String getMsg() { public String getMsg() {
return msg; return msg;
} }
@ -126,7 +145,6 @@ public class ResponseDTO<T> {
@Override @Override
public String toString() { public String toString() {
return "ResponseDTO{" + "code=" + code + ", msg='" + msg + '\'' + ", success=" + success + ", data=" + data + return "ResponseDTO{" + "code=" + code + ", msg='" + msg + '\'' + ", success=" + success + ", data=" + data + '}';
'}';
} }
} }

View File

@ -0,0 +1,183 @@
package net.lab1024.smartadmin.service.common.excel;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.afterturn.easypoi.excel.entity.params.ExcelForEachParams;
import cn.afterturn.easypoi.excel.export.styler.IExcelExportStyler;
import org.apache.poi.ss.usermodel.*;
/**
* [ ]
*
* @author 罗伊
* @date 2020/9/25 19:52
*/
public class ExcelStyle implements IExcelExportStyler {
private static final short STRING_FORMAT = (short) BuiltinFormats.getBuiltinFormat("TEXT");
private static final short FONT_SIZE_TEN = 10;
private static final short FONT_SIZE_ELEVEN = 11;
private static final short FONT_SIZE_TWELVE = 12;
/**
* 大标题样式
*/
private CellStyle headerStyle;
/**
* 每列标题样式
*/
private CellStyle titleStyle;
/**
* 数据行样式
*/
private CellStyle styles;
public ExcelStyle(Workbook workbook) {
this.init(workbook);
}
/**
* 初始化样式
*
* @param workbook
*/
private void init(Workbook workbook) {
this.headerStyle = initHeaderStyle(workbook);
this.titleStyle = initTitleStyle(workbook);
this.styles = initStyles(workbook);
}
/**
* 大标题样式
*
* @param color
* @return
*/
@Override
public CellStyle getHeaderStyle(short color) {
return headerStyle;
}
/**
* 每列标题样式
*
* @param color
* @return
*/
@Override
public CellStyle getTitleStyle(short color) {
return titleStyle;
}
/**
* 数据行样式
*
* @param parity 可以用来表示奇偶行
* @param entity 数据内容
* @return 样式
*/
@Override
public CellStyle getStyles(boolean parity, ExcelExportEntity entity) {
return styles;
}
/**
* 获取样式方法
*
* @param dataRow 数据行
* @param obj 对象
* @param data 数据
*/
@Override
public CellStyle getStyles(Cell cell, int dataRow, ExcelExportEntity entity, Object obj, Object data) {
return getStyles(true, entity);
}
/**
* 模板使用的样式设置
*/
@Override
public CellStyle getTemplateStyles(boolean isSingle, ExcelForEachParams excelForEachParams) {
return null;
}
/**
* 初始化--大标题样式
*
* @param workbook
* @return
*/
private CellStyle initHeaderStyle(Workbook workbook) {
CellStyle style = getBaseCellStyle(workbook);
style.setFont(getFont(workbook, FONT_SIZE_TWELVE, true));
return style;
}
/**
* 初始化--每列标题样式
*
* @param workbook
* @return
*/
private CellStyle initTitleStyle(Workbook workbook) {
CellStyle style = getBaseCellStyle(workbook);
style.setFont(getFont(workbook, FONT_SIZE_ELEVEN, false));
//背景色
style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
return style;
}
/**
* 初始化--数据行样式
*
* @param workbook
* @return
*/
private CellStyle initStyles(Workbook workbook) {
CellStyle style = getBaseCellStyle(workbook);
style.setFont(getFont(workbook, FONT_SIZE_TEN, false));
style.setDataFormat(STRING_FORMAT);
style.setAlignment(HorizontalAlignment.LEFT);
return style;
}
/**
* 基础样式
*
* @return
*/
private CellStyle getBaseCellStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
//下边框
style.setBorderBottom(BorderStyle.THIN);
//左边框
style.setBorderLeft(BorderStyle.THIN);
//上边框
style.setBorderTop(BorderStyle.THIN);
//右边框
style.setBorderRight(BorderStyle.THIN);
//水平居中
style.setAlignment(HorizontalAlignment.CENTER);
//上下居中
style.setVerticalAlignment(VerticalAlignment.CENTER);
//设置自动换行
style.setWrapText(true);
return style;
}
/**
* 字体样式
*
* @param size 字体大小
* @param isBold 是否加粗
* @return
*/
private Font getFont(Workbook workbook, short size, boolean isBold) {
Font font = workbook.createFont();
//字体样式
font.setFontName("宋体");
//是否加粗
font.setBold(isBold);
//字体大小
font.setFontHeightInPoints(size);
return font;
}
}

View File

@ -1,20 +1,22 @@
package net.lab1024.smartadmin.common.exception; package net.lab1024.smartadmin.service.common.exception;
import net.lab1024.smartadmin.service.common.codeconst.ResponseCodeConst;
/** /**
*
* [ 业务逻辑异常,全局异常拦截后统一返回ResponseCodeConst.SYSTEM_ERROR ] * [ 业务逻辑异常,全局异常拦截后统一返回ResponseCodeConst.SYSTEM_ERROR ]
* *
* @version 1.0 * @author 罗伊
* @since JDK1.8 * @date 2020/8/25 11:57
* @author yandanyang
* @company 1024lab.net
* @copyright (c) 2019 1024lab.netInc. All rights reserved.
* @date
*/ */
public class SmartBusinessException extends RuntimeException { public class SmartBusinessException extends RuntimeException {
public SmartBusinessException() { public SmartBusinessException() {
} }
public SmartBusinessException(ResponseCodeConst responseCodeConst) {
super(responseCodeConst.getMsg());
}
public SmartBusinessException(String message) { public SmartBusinessException(String message) {
super(message); super(message);
} }

View File

@ -0,0 +1,26 @@
package net.lab1024.smartadmin.service.common.json;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.math.BigDecimal;
/**
* [ ]
*
* @author 罗伊
* @date 2020/8/20 16:04
*/
public class BigDecimalNullZeroSerializer extends JsonSerializer<BigDecimal> {
@Override
public void serialize(BigDecimal value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (value == null) {
jsonGenerator.writeNumber(BigDecimal.ZERO);
return;
}
jsonGenerator.writeNumber(value);
}
}

View File

@ -0,0 +1,42 @@
package net.lab1024.smartadmin.service.common.json;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import net.lab1024.smartadmin.service.common.domain.ResponseDTO;
import net.lab1024.smartadmin.service.module.support.file.service.FileService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
/**
* [ ]
*
* @author 罗伊
* @date 2020/8/15 15:06
*/
public class FileKeySerializer extends JsonSerializer<String> {
@Autowired
private FileService fileService;
@Override
public void serialize(String value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (StringUtils.isEmpty(value)) {
jsonGenerator.writeString(value);
return;
}
if(fileService == null){
jsonGenerator.writeString(value);
return;
}
ResponseDTO<String> responseDTO = fileService.getFileUrl(value);
if(responseDTO.isSuccess()){
jsonGenerator.writeString(responseDTO.getData());
return;
}
jsonGenerator.writeString(value);
}
}

View File

@ -0,0 +1,43 @@
package net.lab1024.smartadmin.service.common.json;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.google.common.collect.Lists;
import net.lab1024.smartadmin.service.module.support.file.domain.vo.FileVO;
import net.lab1024.smartadmin.service.module.support.file.service.FileService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/**
* [ ]
*
* @author 罗伊
* @date 2020/8/15 15:06
*/
public class FileKeyVoSerializer extends JsonSerializer<String> {
@Autowired
private FileService fileService;
@Override
public void serialize(String value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (StringUtils.isEmpty(value)) {
jsonGenerator.writeObject(Lists.newArrayList());
return;
}
if(fileService == null){
jsonGenerator.writeString(value);
return;
}
String[] fileKeyArray = value.split(",");
List<String> fileKeyList = Arrays.asList(fileKeyArray);
List<FileVO> fileKeyVOList = fileService.getFileList(fileKeyList);
jsonGenerator.writeObject(fileKeyVOList);
}
}

View File

@ -0,0 +1,114 @@
package net.lab1024.smartadmin.service.common.security;
import net.lab1024.smartadmin.service.common.anno.NoValidPrivilege;
import net.lab1024.smartadmin.service.util.SmartSecurityUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.prepost.*;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* 此类用于默认给所有接口添加权限 @privilegeCheck.checkPermission('%s')
* %s 为类名.方法名
* 和使用@PreAuthorize("@privilegeCheck.checkPermission('%s')") 效果一致
* 避免所有接口都添加一遍 减轻工作量
*
* @author 罗伊
* @date 2021-08-30 23:08
*/
public class SmartSecurityMetadataSource extends PrePostAnnotationSecurityMetadataSource {
public static final String PRIVILEGE_CHECK_NAME = "privilegeCheck";
private static String EXPRESSION_FORMAT = "@privilegeCheck.checkPermission('%s')";
private final PrePostInvocationAttributeFactory attributeFactory;
private String projectModule;
private List<String> ignoreUrlList;
public SmartSecurityMetadataSource(PrePostInvocationAttributeFactory attributeFactory, List<String> ignoreUrlList, String projectModule) {
super(attributeFactory);
this.attributeFactory = attributeFactory;
this.projectModule = projectModule;
this.ignoreUrlList = ignoreUrlList;
}
@Override
public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
//只对固定的包的所有接口进行控制
if (!targetClass.getName().startsWith(projectModule)) {
return super.getAttributes(method, targetClass);
}
//是否需要权限
NoValidPrivilege methodNoValidPrivilege = method.getAnnotation(NoValidPrivilege.class);
if (methodNoValidPrivilege != null) {
return Collections.emptyList();
}
NoValidPrivilege classNoValidPrivilege = targetClass.getAnnotation(NoValidPrivilege.class);
if (classNoValidPrivilege != null) {
return Collections.emptyList();
}
//是否添加security原有注解
PreAuthorize preAuthorize = method.getAnnotation(PreAuthorize.class);
if (preAuthorize != null) {
return super.getAttributes(method, targetClass);
}
PostAuthorize postAuthorize = method.getAnnotation(PostAuthorize.class);
if (postAuthorize != null) {
return super.getAttributes(method, targetClass);
}
//自己的控制
GetMapping getMapping = method.getAnnotation(GetMapping.class);
PostMapping postMapping = method.getAnnotation(PostMapping.class);
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
if (getMapping == null && postMapping == null && requestMapping == null) {
return super.getAttributes(method, targetClass);
}
//获取注解值
String uriPrefix = SmartSecurityUtil.getUriPrefix(method);
List<String> annotationValueList = SmartSecurityUtil.getAnnotationValueList(method, uriPrefix);
//判断是否被忽略
if (this.contain(ignoreUrlList, annotationValueList)) {
return super.getAttributes(method, targetClass);
}
ArrayList<ConfigAttribute> configAttributes = new ArrayList(1);
String classFullName = targetClass.getName();
String methodName = method.getName();
String[] classNameArray = StringUtils.split(classFullName, "\\.");
String controllerName = classNameArray[classNameArray.length - 1];
String privilegeName = controllerName + "." + methodName;
String preAuthorizeAttribute = String.format(EXPRESSION_FORMAT, privilegeName);
PreInvocationAttribute pre = this.attributeFactory.createPreInvocationAttribute(null, null, preAuthorizeAttribute);
if (pre != null) {
configAttributes.add(pre);
}
return configAttributes;
}
public Boolean contain(List<String> ignores, List<String> valueList) {
if (CollectionUtils.isEmpty(ignores)) {
return false;
}
for (String ignoreUrl : ignores) {
for (String uri : valueList) {
if (uri.contains(ignoreUrl)) {
return true;
}
}
}
return false;
}
}

View File

@ -0,0 +1,72 @@
package net.lab1024.smartadmin.service.common.security;
import com.google.common.collect.Lists;
import net.lab1024.smartadmin.service.common.anno.NoNeedLogin;
import net.lab1024.smartadmin.service.util.SmartSecurityUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.reflections.Reflections;
import org.reflections.scanners.MethodAnnotationsScanner;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.reflections.util.ConfigurationBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;
/**
* [ ]
*
* @author 罗伊
* @date 2021/8/31 10:20
*/
public class SmartSecurityNoLoginUrl {
private List<String> noLoginUrlList;
public SmartSecurityNoLoginUrl(String scanPath){
this.noLoginUrlList = this.initNoLoginUrlList(scanPath);
}
public List<String> getNoLoginUrlList(){
return noLoginUrlList;
}
/**
* 获取无需登录的url信息
*
* @return
*/
private List<String> initNoLoginUrlList(String scanPath) {
List<String> noLoginUrlList = Lists.newArrayList();
//一些常量uri
noLoginUrlList.add("/swagger-ui.html");
noLoginUrlList.add("/swagger-resources/**");
noLoginUrlList.add("/webjars/**");
noLoginUrlList.add("/*/api-docs");
//添加无需登录注解的uri
Reflections reflections = new Reflections(new ConfigurationBuilder().forPackages(scanPath).addScanners(new MethodAnnotationsScanner(), new TypeAnnotationsScanner()));
Set<Method> methodSet = reflections.getMethodsAnnotatedWith(NoNeedLogin.class);
Set<Class<?>> classSet = reflections.getTypesAnnotatedWith(NoNeedLogin.class);
//方法级别无需登录
for (Method method : methodSet) {
String uriPrefix = SmartSecurityUtil.getUriPrefix(method);
List<String> valueList = SmartSecurityUtil.getAnnotationValueList(method, uriPrefix);
noLoginUrlList.addAll(valueList);
}
//类级别无需登录
for (Class clazz : classSet) {
Method[] methods = clazz.getMethods();
for (Method method : methods) {
String uriPrefix = SmartSecurityUtil.getUriPrefix(method);
List<String> valueList = SmartSecurityUtil.getAnnotationValueList(method, uriPrefix);
noLoginUrlList.addAll(valueList);
}
}
return noLoginUrlList;
}
}

View File

@ -1,6 +1,6 @@
package net.lab1024.smartadmin.common.anno; package net.lab1024.smartadmin.service.common.swagger;
import net.lab1024.smartadmin.common.domain.BaseEnum; import net.lab1024.smartadmin.service.common.constant.BaseEnum;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
@ -8,7 +8,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
/** /**
* 枚举类字段属性的注解 * 枚举类字段属性的 自定义 swagger 注解
* *
* @author listen * @author listen
* @date 2019/05/16 15:18 * @date 2019/05/16 15:18
@ -26,22 +26,12 @@ public @interface ApiModelPropertyEnum {
String example() default ""; String example() default "";
/**
* 是否隐藏
*
* @return
*/
boolean hidden() default false; boolean hidden() default false;
/**
* 是否必须
*
* @return
*/
boolean required() default true; boolean required() default true;
String dataType() default ""; String dataType() default "";
String enumDesc() default ""; String desc() default "";
} }

View File

@ -1,30 +1,12 @@
/* package net.lab1024.smartadmin.service.common.swagger;
*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*
*/
package net.lab1024.smartadmin.common.swagger;
import net.lab1024.smartadmin.common.anno.ApiModelPropertyEnum;
import net.lab1024.smartadmin.common.domain.BaseEnum;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import net.lab1024.smartadmin.service.common.constant.BaseEnum;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin; import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin;
import springfox.documentation.spi.schema.contexts.ModelPropertyContext; import springfox.documentation.spi.schema.contexts.ModelPropertyContext;
@ -41,26 +23,28 @@ import static springfox.documentation.schema.Annotations.findPropertyAnnotation;
* @author listen * @author listen
* @date 2019年5月16日 15:36:56 * @date 2019年5月16日 15:36:56
*/ */
public class SmartSwaggerApiModelEnumPlugin implements ModelPropertyBuilderPlugin { @Component
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1)
public class ApiModelPropertyEnumPlugin implements ModelPropertyBuilderPlugin {
@Override @Override
public void apply(ModelPropertyContext context) { public void apply(ModelPropertyContext context) {
Optional<ApiModelPropertyEnum> enumOptional = Optional.absent(); Optional<ApiModelPropertyEnum> annotation = Optional.absent();
if (context.getAnnotatedElement().isPresent()) { if (context.getAnnotatedElement().isPresent()) {
enumOptional = enumOptional.or(findApiModePropertyAnnotation(context.getAnnotatedElement().get())); annotation = annotation.or(findApiModePropertyAnnotation(context.getAnnotatedElement().get()));
} }
if (context.getBeanPropertyDefinition().isPresent()) { if (context.getBeanPropertyDefinition().isPresent()) {
enumOptional = enumOptional.or(findPropertyAnnotation(context.getBeanPropertyDefinition().get(), ApiModelPropertyEnum.class)); annotation = annotation.or(findPropertyAnnotation(context.getBeanPropertyDefinition().get(), ApiModelPropertyEnum.class));
} }
if (enumOptional.isPresent()) { if (annotation.isPresent()) {
ApiModelPropertyEnum anEnum = enumOptional.get(); ApiModelPropertyEnum anEnum = annotation.get();
String enumInfo = BaseEnum.getInfo(anEnum.value()); String enumInfo = BaseEnum.getInfo(anEnum.value());
context.getBuilder() context.getBuilder()
.required(enumOptional.transform(toIsRequired()).or(false)) .required(annotation.transform(toIsRequired()).or(false))
.description(anEnum.enumDesc() + ":" + enumInfo) .description(anEnum.desc() + ":" + enumInfo)
.example(enumOptional.transform(toExample()).orNull()) .example(annotation.transform(toExample()).orNull())
.isHidden(anEnum.hidden()); .isHidden(anEnum.hidden());
} }
} }

View File

@ -1,4 +1,4 @@
package net.lab1024.smartadmin.common.validator.bigdecimal; package net.lab1024.smartadmin.service.common.validator;
/** /**
* 比较符枚举类 * 比较符枚举类
@ -6,7 +6,7 @@ package net.lab1024.smartadmin.common.validator.bigdecimal;
* @author listen * @author listen
* @date 2018/03/20 14:01 * @date 2018/03/20 14:01
*/ */
public enum ComparisonSymbolEnum { public enum BigDecimalSymbolEnum {
/** /**
* 等于 * 等于

View File

@ -1,6 +1,6 @@
package net.lab1024.smartadmin.common.validator.bigdecimal; package net.lab1024.smartadmin.service.common.validator;
import net.lab1024.smartadmin.util.SmartBigDecimalUtil; import net.lab1024.smartadmin.service.util.SmartBigDecimalUtil;
import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext; import javax.validation.ConstraintValidatorContext;
@ -22,7 +22,7 @@ public class BigDecimalValidator implements ConstraintValidator<CheckBigDecimal,
/** /**
* 获取比较符 * 获取比较符
*/ */
private ComparisonSymbolEnum symbolEnum; private BigDecimalSymbolEnum symbolEnum;
/** /**
* 是否必须 * 是否必须

View File

@ -1,4 +1,4 @@
package net.lab1024.smartadmin.common.validator.bigdecimal; package net.lab1024.smartadmin.service.common.validator;
import javax.validation.Constraint; import javax.validation.Constraint;
import javax.validation.Payload; import javax.validation.Payload;
@ -30,7 +30,7 @@ public @interface CheckBigDecimal {
* *
* @return * @return
*/ */
ComparisonSymbolEnum symbolEnum(); BigDecimalSymbolEnum symbolEnum();
/** /**
* 默认的错误提示信息 * 默认的错误提示信息

View File

@ -1,7 +1,6 @@
package net.lab1024.smartadmin.common.validator.en; package net.lab1024.smartadmin.service.common.validator;
import net.lab1024.smartadmin.service.common.constant.BaseEnum;
import net.lab1024.smartadmin.common.domain.BaseEnum;
import javax.validation.Constraint; import javax.validation.Constraint;
import javax.validation.Payload; import javax.validation.Payload;
@ -19,7 +18,7 @@ import java.lang.annotation.Target;
*/ */
@Target(ElementType.FIELD) @Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EnumValidator.class)// 自定义验证的处理类 @Constraint(validatedBy = CheckEnumValidator.class)// 自定义验证的处理类
public @interface CheckEnum { public @interface CheckEnum {
/** /**

View File

@ -1,6 +1,6 @@
package net.lab1024.smartadmin.common.validator.en; package net.lab1024.smartadmin.service.common.validator;
import net.lab1024.smartadmin.common.domain.BaseEnum; import net.lab1024.smartadmin.service.common.constant.BaseEnum;
import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext; import javax.validation.ConstraintValidatorContext;
@ -13,8 +13,9 @@ import java.util.stream.Stream;
* *
* @author listen * @author listen
* @date 2017/11/11 15:34 * @date 2017/11/11 15:34
* @update 2021年1月20日 15:32:16
*/ */
public class EnumValidator implements ConstraintValidator<CheckEnum, Object> { public class CheckEnumValidator implements ConstraintValidator<CheckEnum, Object> {
/** /**
* 枚举类实例集合 * 枚举类实例集合

View File

@ -1,11 +1,11 @@
package net.lab1024.smartadmin.common.domain; package net.lab1024.smartadmin.service.common.validator;
import javax.validation.Valid; import javax.validation.Valid;
import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotEmpty;
import java.util.*; import java.util.*;
/** /**
* @author: zhuoda * @author: 卓大
* @create: 2020-02-03 17:37 PM from win10 * @create: 2020-02-03 17:37 PM from win10
*/ */
public class ValidateList<E> implements List<E> { public class ValidateList<E> implements List<E> {

View File

@ -0,0 +1,113 @@
package net.lab1024.smartadmin.service.config;
import net.lab1024.smartadmin.service.common.security.SmartSecurityNoLoginUrl;
import net.lab1024.smartadmin.service.filters.SmartTokenFilter;
import net.lab1024.smartadmin.service.handler.AuthenticationFailHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import java.util.List;
/**
* Spring Security
*
* @author 善逸
* @date 2021/8/3 17:50
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${access-control-allow-origin}")
private String accessControlAllowOrigin;
/**
* 认证失败处理类
*/
@Autowired
private AuthenticationFailHandler authenticationFailHandler;
/**
* 无需登录的url
*/
@Autowired
private SmartSecurityNoLoginUrl smartSecurityNoLoginUrl;
/**
* token过滤器
*/
@Autowired
private SmartTokenFilter smartTokenFilter;
/**
* 跨域配置
*
* @return
*/
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
// 设置访问源地址
config.addAllowedOriginPattern(accessControlAllowOrigin);
// 设置访问源请求头
config.addAllowedHeader("*");
// 设置访问源请求方法
config.addAllowedMethod("*");
// 对接口配置跨域设置
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
/**
* anyRequest | 匹配所有请求路径
* access | SpringEl表达式结果为true时可以访问
* anonymous | 匿名可以访问
* denyAll | 用户不能访问
* fullyAuthenticated | 用户完全认证可以访问非remember-me下自动登录
* hasAnyAuthority | 如果有参数参数表示权限则其中任何一个权限可以访问
* hasAnyRole | 如果有参数参数表示角色则其中任何一个角色可以访问
* hasAuthority | 如果有参数参数表示权限则其权限可以访问
* hasIpAddress | 如果有参数参数表示IP地址如果用户IP和参数匹配则可以访问
* hasRole | 如果有参数参数表示角色则其角色可以访问
* permitAll | 用户可以任意访问
* rememberMe | 允许通过remember-me登录的用户访问
* authenticated | 用户登录后可访问
*/
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
// CSRF禁用因为不使用session
.csrf().disable()
// 认证失败处理类
.exceptionHandling().authenticationEntryPoint(authenticationFailHandler).and()
// 基于token所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
// 过滤请求
.authorizeRequests()
.antMatchers("/admin/**").authenticated();
httpSecurity.addFilterBefore(smartTokenFilter, UsernamePasswordAuthenticationFilter.class);
httpSecurity.addFilterBefore(corsFilter(), SmartTokenFilter.class);
}
@Override
public void configure(WebSecurity web) {
// 忽略url
WebSecurity.IgnoredRequestConfigurer ignoring = web.ignoring();
List<String> noLoginUrlList = smartSecurityNoLoginUrl.getNoLoginUrlList();
for (String url : noLoginUrlList) {
ignoring.antMatchers(url);
}
}
}

View File

@ -0,0 +1,35 @@
package net.lab1024.smartadmin.service.config;
import net.lab1024.smartadmin.service.common.security.SmartSecurityMetadataSource;
import net.lab1024.smartadmin.service.common.security.SmartSecurityNoLoginUrl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.expression.method.ExpressionBasedAnnotationAttributeFactory;
import org.springframework.security.access.method.MethodSecurityMetadataSource;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
/**
* 此类用于注入自己的 method校验
* SmartSecurityMetadataSource
* @author 罗伊
* @date 2021-08-31 0:01
*/
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityMethodConfig extends GlobalMethodSecurityConfiguration {
@Value("${project.module}")
private String projectModule;
/**
* 无需登录的url
*/
@Autowired
private SmartSecurityNoLoginUrl smartSecurityNoLoginUrl;
@Override
public MethodSecurityMetadataSource customMethodSecurityMetadataSource(){
ExpressionBasedAnnotationAttributeFactory attributeFactory = new ExpressionBasedAnnotationAttributeFactory(this.getExpressionHandler());
return new SmartSecurityMetadataSource(attributeFactory, smartSecurityNoLoginUrl.getNoLoginUrlList(),projectModule);
}
}

View File

@ -0,0 +1,24 @@
package net.lab1024.smartadmin.service.config;
import net.lab1024.smartadmin.service.common.security.SmartSecurityNoLoginUrl;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* [ ]
*
* @author 罗伊
* @date 2021/9/1 21:40
*/
@Configuration
public class SecurityNoLoginUrlConfig {
@Value("${project.module}")
private String projectModule;
@Bean
public SmartSecurityNoLoginUrl securityNoLoginUrl(){
return new SmartSecurityNoLoginUrl(projectModule);
}
}

View File

@ -0,0 +1,66 @@
package net.lab1024.smartadmin.service.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* 异步调用线程配置
*
* @author 胡克
* @date 2019/12/26 11:54
*/
@Slf4j
@Configuration
public class SmartAsyncConfig {
/**
* 线程池 配置bean名称
*/
public static final String ASYNC_EXECUTOR = "asyncExecutor";
/**
* 配置线程池
*
* @return
*/
@Bean(name = ASYNC_EXECUTOR)
public AsyncTaskExecutor executor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
// 核心线程数量
taskExecutor.setCorePoolSize(Runtime.getRuntime().availableProcessors());
// 最大线程数量
taskExecutor.setMaxPoolSize(Runtime.getRuntime().availableProcessors());
taskExecutor.setThreadNamePrefix(ASYNC_EXECUTOR);
taskExecutor.initialize();
return taskExecutor;
}
/**
* spring 异步任务 异常配置
*/
@Configuration
public static class AsyncExceptionConfig implements AsyncConfigurer {
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}
}
/**
* 自定义异常处理
*/
public static class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
log.error("异步任务发生异常:{}, 参数:{}, ", method.getDeclaringClass().getSimpleName() + "." + method.getName(), Arrays.toString(objects), throwable);
}
}
}

View File

@ -0,0 +1,63 @@
package net.lab1024.smartadmin.service.config;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.collect.Maps;
import net.lab1024.smartadmin.service.module.support.beancache.cache.AbstractCaffeineCache;
import net.lab1024.smartadmin.service.module.support.beancache.cache.AbstractDisableCache;
import net.lab1024.smartadmin.service.module.support.beancache.cache.IBeanCache;
import net.lab1024.smartadmin.service.module.support.beancache.domain.CacheData;
import net.lab1024.smartadmin.service.module.support.beancache.load.CacheLoad;
import net.lab1024.smartadmin.service.module.support.beancache.load.CacheLoadMethod;
import org.reflections.Reflections;
import org.reflections.scanners.MethodAnnotationsScanner;
import org.reflections.util.ConfigurationBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
/**
* [ ]
*
* @author 罗伊
* @date 2020/9/6 16:01
*/
@Configuration
public class SmartCacheConfig {
@Value("${cache.maximumSize:5000}")
private Integer cacheMaximumSize;
@Value("${cache.expireDays:5}")
private Integer expireDays;
@Value("${cache.scanPath}")
private String scanPath;
@Bean
@Primary
public IBeanCache beanCache() {
return new AbstractCaffeineCache() {
LoadingCache<String, CacheData> cache = this.initCache(cacheMaximumSize,expireDays, scanPath);
@Override
public LoadingCache getCache() {
return cache;
}
};
}
@Bean
@ConditionalOnMissingBean(IBeanCache.class)
public IBeanCache beanDisableCache() {
return new AbstractDisableCache(scanPath);
}
}

View File

@ -1,9 +1,9 @@
package net.lab1024.smartadmin.config; package net.lab1024.smartadmin.service.config;
import net.lab1024.smartadmin.common.kaptcha.KaptchaNoise;
import net.lab1024.smartadmin.common.kaptcha.KaptchaWordRenderer;
import com.google.code.kaptcha.impl.DefaultKaptcha; import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config; import com.google.code.kaptcha.util.Config;
import net.lab1024.smartadmin.service.module.support.captcha.render.CaptchaNoise;
import net.lab1024.smartadmin.service.module.support.captcha.render.CaptchaWordRenderer;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -12,36 +12,38 @@ import java.util.Properties;
/** /**
* [ 验证码配置 ] * [ 验证码配置 ]
* *
* @author yandanyang * @author 罗伊
* @version 1.0 * @version 1.0
* @company 1024lab.net
* @copyright (c) 2018 1024lab.netInc. All rights reserved.
* @date 2019/7/4 0004 上午 9:40
* @since JDK1.8 * @since JDK1.8
*/ */
@Configuration @Configuration
public class SmartKaptchaConfig { public class SmartCaptchaConfig {
/**
* 图形验证码配置
*
* @return
*/
@Bean @Bean
public DefaultKaptcha getDefaultKaptcha(){ public DefaultKaptcha getDefaultCaptcha() {
DefaultKaptcha defaultKaptcha=new DefaultKaptcha(); DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
Properties properties=new Properties(); Properties properties = new Properties();
properties.setProperty("kaptcha.border", "no"); properties.setProperty("kaptcha.border", "no" );
properties.setProperty("kaptcha.border.color", "34,114,200"); properties.setProperty("kaptcha.border.color", "34,114,200" );
properties.setProperty("kaptcha.image.width", "125"); properties.setProperty("kaptcha.image.width", "125" );
properties.setProperty("kaptcha.image.height", "45"); properties.setProperty("kaptcha.image.height", "45" );
properties.setProperty("kaptcha.textproducer.char.string", "0123456789"); properties.setProperty("kaptcha.textproducer.char.string", "123456789" );
properties.setProperty("kaptcha.textproducer.char.length", "4"); properties.setProperty("kaptcha.textproducer.char.length", "4" );
properties.setProperty("kaptcha.textproducer.font.names", "Arial,Arial Narrow,Serif,Helvetica,Tahoma,Times New Roman,Verdana"); properties.setProperty("kaptcha.textproducer.font.names", "Arial,Arial Narrow,Serif,Helvetica,Tahoma,Times New Roman,Verdana" );
properties.setProperty("kaptcha.textproducer.font.size", "38"); properties.setProperty("kaptcha.textproducer.font.size", "38" );
properties.setProperty("kaptcha.background.clear.from", "white"); properties.setProperty("kaptcha.background.clear.from", "white" );
properties.setProperty("kaptcha.background.clear.to", "white"); properties.setProperty("kaptcha.background.clear.to", "white" );
properties.setProperty("kaptcha.word.impl", KaptchaWordRenderer.class.getName()); properties.setProperty("kaptcha.word.impl", CaptchaWordRenderer.class.getName());
properties.setProperty("kaptcha.noise.impl", KaptchaNoise.class.getName()); properties.setProperty("kaptcha.noise.impl", CaptchaNoise.class.getName());
Config config=new Config(properties); Config config = new Config(properties);
defaultKaptcha.setConfig(config); defaultKaptcha.setConfig(config);
return defaultKaptcha; return defaultKaptcha;
} }

View File

@ -0,0 +1,34 @@
package net.lab1024.smartadmin.service.config;
import net.lab1024.smartadmin.service.util.date.SmartDateFormatterEnum;
import net.lab1024.smartadmin.service.util.date.SmartLocalDateUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import java.time.LocalDate;
import java.time.format.DateTimeParseException;
/**
* string 转为 LocalDate 配置类
*
* @author 胡克
* @date 2020/3/6 14:34
*/
@Configuration
public class SmartConverterStringToLocalDate implements Converter<String, LocalDate> {
@Override
public LocalDate convert(String str) {
if (StringUtils.isBlank(str)) {
return null;
}
LocalDate localDate;
try {
localDate = SmartLocalDateUtil.parseDate(str, SmartDateFormatterEnum.YMD);
} catch (DateTimeParseException e) {
throw new RuntimeException("请输入正确的日期格式yyyy-MM-dd");
}
return localDate;
}
}

View File

@ -0,0 +1,37 @@
package net.lab1024.smartadmin.service.config;
import net.lab1024.smartadmin.service.util.date.SmartDateFormatterEnum;
import net.lab1024.smartadmin.service.util.date.SmartLocalDateUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import java.time.LocalDateTime;
import java.time.format.DateTimeParseException;
/**
* string 转为 LocalDate 配置类
*
* @author 胡克
* @date 2020/3/6 14:34
*/
@Configuration
public class SmartConverterStringToLocalDateTime implements Converter<String, LocalDateTime> {
@Override
public LocalDateTime convert(String str) {
if (StringUtils.isBlank(str)) {
return null;
}
LocalDateTime localDateTime;
try {
localDateTime = SmartLocalDateUtil.parse(str, SmartDateFormatterEnum.YMD_HMS);
} catch (DateTimeParseException e) {
throw new RuntimeException("请输入正确的日期格式yyyy-MM-dd HH:mm:ss");
}
return localDateTime;
}
}

View File

@ -1,4 +1,4 @@
package net.lab1024.smartadmin.config; package net.lab1024.smartadmin.service.config;
import com.alibaba.druid.filter.Filter; import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.filter.stat.StatFilter; import com.alibaba.druid.filter.stat.StatFilter;
@ -6,17 +6,25 @@ import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet; 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.alibaba.druid.util.JdbcConstants; import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor; import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.JdkRegexpMethodPointcut; import org.springframework.aop.support.JdkRegexpMethodPointcut;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource; import javax.sql.DataSource;
import java.sql.SQLException; import java.sql.SQLException;
@ -25,60 +33,59 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
* [ 数据源配置 ] * [ ]
* *
* @author yandanyang * @author 罗伊
* @version 1.0 * @date 2020/8/25 11:57
* @company 1024lab.net
* @copyright (c) 2019 1024lab.netInc. All rights reserved.
* @date
* @since JDK1.8
*/ */
@Slf4j @Slf4j
@Configuration @Configuration
public class SmartDruidDataSourceConfig { public class SmartDruidDataSourceConfig {
@Value("${spring.datasource.driver-class-name}") @Value("${spring.datasource.driver-class-name}")
String driver; private String driver;
@Value("${spring.datasource.url}") @Value("${spring.datasource.url}")
String url; private String url;
@Value("${spring.datasource.username}") @Value("${spring.datasource.username}")
String username; private String username;
@Value("${spring.datasource.password}") @Value("${spring.datasource.password}")
String password; private String password;
@Value("${spring.datasource.initial-size}") @Value("${spring.datasource.initial-size}")
int initialSize; private int initialSize;
@Value("${spring.datasource.min-idle}") @Value("${spring.datasource.min-idle}")
int minIdle; private int minIdle;
@Value("${spring.datasource.max-active}") @Value("${spring.datasource.max-active}")
int maxActive; private int maxActive;
@Value("${spring.datasource.max-wait}") @Value("${spring.datasource.max-wait}")
long maxWait; private long maxWait;
@Value("${spring.datasource.time-between-eviction-runs-millis}") @Value("${spring.datasource.time-between-eviction-runs-millis}")
long timeBetweenEvictionRunsMillis; private long timeBetweenEvictionRunsMillis;
@Value("${spring.datasource.min-evictable-idle-time-millis}") @Value("${spring.datasource.min-evictable-idle-time-millis}")
long minEvictableIdleTimeMillis; private long minEvictableIdleTimeMillis;
@Value("${spring.datasource.filters}") @Value("${spring.datasource.filters}")
String filters; private String filters;
@Value("${spring.datasource.druid.username}") @Value("${spring.datasource.druid.username}")
String druidUserName; private String druidUserName;
@Value("${spring.datasource.druid.password}") @Value("${spring.datasource.druid.password}")
String druidPassword; private String druidPassword;
@Value("${spring.datasource.druid.login.enabled}") @Value("${spring.datasource.druid.login.enabled}")
boolean druidLoginEnable; private boolean druidLoginEnable;
@Value("${spring.datasource.druid.service.scanner}")
private String serviceScanner;
@Autowired @Autowired
private StatFilter logSlowSql; private StatFilter logSlowSql;
@ -86,11 +93,14 @@ public class SmartDruidDataSourceConfig {
@Autowired @Autowired
private DruidStatInterceptor druidStatInterceptor; private DruidStatInterceptor druidStatInterceptor;
@Autowired
private MybatisPlusInterceptor mybatisPlusInterceptor;
@Bean @Bean
@Primary @Primary
public DataSource druidDataSource() { public DataSource druidDataSource() {
DruidDataSource druidDataSource = new DruidDataSource(); DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDbType(JdbcConstants.MYSQL); druidDataSource.setDbType(DbType.MYSQL.getDb());
druidDataSource.setDriverClassName(driver); druidDataSource.setDriverClassName(driver);
druidDataSource.setUrl(url); druidDataSource.setUrl(url);
druidDataSource.setUsername(username); druidDataSource.setUsername(username);
@ -101,6 +111,7 @@ public class SmartDruidDataSourceConfig {
druidDataSource.setMaxWait(maxWait); druidDataSource.setMaxWait(maxWait);
druidDataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); druidDataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
druidDataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); druidDataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
druidDataSource.setValidationQuery("SELECT 1");
try { try {
druidDataSource.setFilters(filters); druidDataSource.setFilters(filters);
ArrayList<Filter> arrayList = new ArrayList<>(); ArrayList<Filter> arrayList = new ArrayList<>();
@ -115,12 +126,33 @@ public class SmartDruidDataSourceConfig {
} }
@Bean @Bean
public ServletRegistrationBean druidServlet() { public SqlSessionFactory sqlSessionFactory() throws Exception {
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(); MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
factoryBean.setDataSource(druidDataSource());
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("classpath*:/mapper/**/*.xml");
factoryBean.setMapperLocations(resources);
// 设置 MyBatis-Plus 分页插件
Interceptor[] plugins = {mybatisPlusInterceptor};
factoryBean.setPlugins(plugins);
return factoryBean.getObject();
}
/**
* 非正式环境 加载
*
* @return
*/
@Bean
@Conditional(SmartSystemEnvNotProdCondition.class)
public ServletRegistrationBean<StatViewServlet> druidServlet() {
ServletRegistrationBean<StatViewServlet> servletRegistrationBean = new ServletRegistrationBean<StatViewServlet>();
servletRegistrationBean.setServlet(new StatViewServlet()); servletRegistrationBean.setServlet(new StatViewServlet());
servletRegistrationBean.addUrlMappings("/druid/*"); servletRegistrationBean.addUrlMappings("/druid/*");
Map<String, String> initParameters = new HashMap<String, String>(); Map<String, String> initParameters = new HashMap<String, String>();
//不设置用户名密码可以直接通过druid/index.html访问 // 不设置用户名密码可以直接通过druid/index.html访问
if (druidLoginEnable) { if (druidLoginEnable) {
initParameters.put("loginUsername", druidUserName); initParameters.put("loginUsername", druidUserName);
initParameters.put("loginPassword", druidPassword); initParameters.put("loginPassword", druidPassword);
@ -131,11 +163,11 @@ public class SmartDruidDataSourceConfig {
} }
@Bean @Bean
public FilterRegistrationBean filterRegistrationBean() { public FilterRegistrationBean<WebStatFilter> filterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); FilterRegistrationBean<WebStatFilter> filterRegistrationBean = new FilterRegistrationBean<WebStatFilter>();
filterRegistrationBean.setFilter(new WebStatFilter()); filterRegistrationBean.setFilter(new WebStatFilter());
filterRegistrationBean.addUrlPatterns("/*"); filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"); filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/*");
return filterRegistrationBean; return filterRegistrationBean;
} }
@ -143,21 +175,20 @@ public class SmartDruidDataSourceConfig {
public StatFilter logSlowSql() { public StatFilter logSlowSql() {
StatFilter statFilter = new StatFilter(); StatFilter statFilter = new StatFilter();
statFilter.setMergeSql(true); statFilter.setMergeSql(true);
statFilter.setSlowSqlMillis(300); statFilter.setSlowSqlMillis(500);
statFilter.setLogSlowSql(true); statFilter.setLogSlowSql(true);
return statFilter; return statFilter;
} }
@Bean(name = "druid-stat-interceptor") @Bean(name = "druid-stat-interceptor")
public DruidStatInterceptor druidStatInterceptor() { public DruidStatInterceptor druidStatInterceptor() {
DruidStatInterceptor dsInterceptor = new DruidStatInterceptor(); return new DruidStatInterceptor();
return dsInterceptor;
} }
@Bean @Bean
public JdkRegexpMethodPointcut jdkRegexpMethodPointcut() { public JdkRegexpMethodPointcut jdkRegexpMethodPointcut() {
JdkRegexpMethodPointcut jdkRegexpMethodPointcut = new JdkRegexpMethodPointcut(); JdkRegexpMethodPointcut jdkRegexpMethodPointcut = new JdkRegexpMethodPointcut();
jdkRegexpMethodPointcut.setPatterns("net.lab1024.smartadmin.module..*Service.*"); jdkRegexpMethodPointcut.setPatterns(serviceScanner);
return jdkRegexpMethodPointcut; return jdkRegexpMethodPointcut;
} }

View File

@ -0,0 +1,40 @@
package net.lab1024.smartadmin.service.config;
import net.lab1024.smartadmin.service.module.support.heartbeat.HeartBeatRecordHandler;
import net.lab1024.smartadmin.service.module.support.heartbeat.core.HeartBeatManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* [ 服务心跳配置 ]
*
* @author 罗伊
* @date 2021/1/9 14:13
*/
@Configuration
public class SmartHeartBeatConfig {
/**
* 延迟执行时间
*/
@Value("${heart-beat.delayHandlerTime}")
private Long delayHandlerTime;
/**
* 间隔执行时间
*/
@Value("${heart-beat.intervalTime}")
private Long intervalTime;
@Autowired
private HeartBeatRecordHandler heartBeatRecordHandler;
@Bean
public HeartBeatManager heartBeatManager(){
return new HeartBeatManager(delayHandlerTime,intervalTime,heartBeatRecordHandler);
}
}

View File

@ -0,0 +1,30 @@
package net.lab1024.smartadmin.service.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* MybatisPlus 配置
*
* @author listen
* @date 2021/06/06 19:30
*/
@Configuration
@EnableTransactionManagement
public class SmartMybatisPlusConfig {
/**
* 分页插件
*/
@Bean
public MybatisPlusInterceptor paginationInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}

View File

@ -1,4 +1,4 @@
package net.lab1024.smartadmin.config; package net.lab1024.smartadmin.service.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.annotation.PropertyAccessor;
@ -14,12 +14,8 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
/** /**
* [ redis配置 ] * [ redis配置 ]
* *
* @author yandanyang * @author 罗伊
* @version 1.0 * @date 2020/8/25 11:57
* @company 1024lab.net
* @copyright (c) 2019 1024lab.netInc. All rights reserved.
* @date
* @since JDK1.8
*/ */
@Configuration @Configuration
public class SmartRedisConfig { public class SmartRedisConfig {
@ -70,5 +66,4 @@ public class SmartRedisConfig {
return redisTemplate.opsForZSet(); return redisTemplate.opsForZSet();
} }
} }

View File

@ -0,0 +1,68 @@
package net.lab1024.smartadmin.service.config;
import lombok.extern.slf4j.Slf4j;
import net.lab1024.smartadmin.service.module.support.redismq.RedisMqTopicEnum;
import net.lab1024.smartadmin.service.module.support.redismq.RedisMsgHandler;
import net.lab1024.smartadmin.service.util.SmartBaseEnumUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
/**
* [ ]
*
* @author 罗伊
* @date
*/
@Slf4j
@Configuration
public class SmartRedisMqConfig {
@Value("${redis.mq.topic:SmartAdmin}")
private String topic;
@Value("${redis.mq.scanPath:net.lab1024.smartadmin.service}")
private String scanPath;
/**
* redis topic
* @param connectionFactory
* @param listenerAdapter
* @return
*/
@Bean
public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
boolean checkEnum = SmartBaseEnumUtil.checkEnum(topic, RedisMqTopicEnum.class);
if (!checkEnum) {
log.error("topic:{},系统暂未定义", topic);
throw new RuntimeException("无效的redis topic");
}
container.addMessageListener(listenerAdapter, new PatternTopic(topic));
return container;
}
/**
* redis消息处理类
* @return
*/
@Bean
public RedisMsgHandler redisMsgHandler() {
return new RedisMsgHandler(scanPath);
}
/**
* redis消息处理方法
* @param receiver
* @return
*/
@Bean
public MessageListenerAdapter listenerAdapter(RedisMsgHandler receiver) {
return new MessageListenerAdapter(receiver, RedisMsgHandler.METHOD_NAME);
}
}

View File

@ -0,0 +1,43 @@
package net.lab1024.smartadmin.service.config;
import com.alibaba.fastjson.JSON;
import net.lab1024.smartadmin.service.module.support.repeatsubmit.SmartRepeatSubmitAspect;
import net.lab1024.smartadmin.service.module.support.repeatsubmit.SmartRepeatSubmitUserDTO;
import net.lab1024.smartadmin.service.module.system.login.domain.EmployeeLoginInfoDTO;
import net.lab1024.smartadmin.service.util.SmartEmployeeTokenUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.http.HttpServletRequest;
/**
* [ 接口重复提交配置 ]
*
* @author 罗伊
* @date 2021/1/27 11:22
*/
@Configuration
public class SmartRepeatSubmitAspectConfig {
@Bean
public SmartRepeatSubmitAspect repeatSubmitAspect() {
return new SmartRepeatSubmitAspect(this::userFunction);
}
/**
* 请求用户信息
*
* @return
*/
private SmartRepeatSubmitUserDTO userFunction(HttpServletRequest request) {
EmployeeLoginInfoDTO requestEmployee = SmartEmployeeTokenUtil.getRequestEmployee();
if(requestEmployee == null){
return null;
}
SmartRepeatSubmitUserDTO repeatSubmitUserDTO = new SmartRepeatSubmitUserDTO();
repeatSubmitUserDTO.setUserId(requestEmployee.getEmployeeId());
repeatSubmitUserDTO.setUserName(requestEmployee.getActualName());
repeatSubmitUserDTO.setExtData(JSON.toJSONString(requestEmployee));
return repeatSubmitUserDTO;
}
}

View File

@ -0,0 +1,45 @@
package net.lab1024.smartadmin.service.config;
import com.alibaba.fastjson.JSON;
import net.lab1024.smartadmin.service.module.support.responseencrypt.ResponseEncryptDecryptAspect;
import net.lab1024.smartadmin.service.module.support.responseencrypt.ResponseEncryptDecryptUserDTO;
import net.lab1024.smartadmin.service.module.system.login.domain.EmployeeLoginInfoDTO;
import net.lab1024.smartadmin.service.util.SmartEmployeeTokenUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.http.HttpServletRequest;
/**
* [ 接口加解密配置 ]
*
* @author 罗伊
* @date 2021/1/27 11:22
*/
@Configuration
public class SmartResponseEncryptAspectConfig {
@Bean
public ResponseEncryptDecryptAspect responseEncryptAspect() {
return new ResponseEncryptDecryptAspect(this::employeeFunction);
}
/**
* 请求用户信息
* @param request
* @return
*/
private ResponseEncryptDecryptUserDTO employeeFunction(HttpServletRequest request){
EmployeeLoginInfoDTO employeeLoginInfoDTO = SmartEmployeeTokenUtil.getRequestEmployee();
if(employeeLoginInfoDTO == null){
return null;
}
ResponseEncryptDecryptUserDTO responseEncryptDecryptUserDTO = new ResponseEncryptDecryptUserDTO();
responseEncryptDecryptUserDTO.setUserId(employeeLoginInfoDTO.getEmployeeId());
responseEncryptDecryptUserDTO.setUserName(employeeLoginInfoDTO.getActualName());
responseEncryptDecryptUserDTO.setExtData(JSON.toJSONString(employeeLoginInfoDTO));
return responseEncryptDecryptUserDTO;
}
}

View File

@ -0,0 +1,126 @@
package net.lab1024.smartadmin.service.config;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* [ ]
*
* @author 罗伊
* @date 2020/8/25 11:57
*/
@Configuration
public class SmartRestTemplateConfig {
@Value("${http.pool.max-total}")
private Integer maxTotal;
@Value("${http.pool.connect-timeout}")
private Integer connectTimeout;
@Value("${http.pool.read-timeout}")
private Integer readTimeout;
@Value("${http.pool.write-timeout}")
private Integer writeTimeout;
@Value("${http.pool.keep-alive}")
private Integer keepAlive;
@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(this.clientHttpRequestFactory());
List<HttpMessageConverter<?>> messageConverterList = restTemplate.getMessageConverters();
messageConverterList.add(0, new StringHttpMessageConverter(StandardCharsets.UTF_8));
messageConverterList.addAll(converters());
return restTemplate;
}
public List<HttpMessageConverter<?>> converters() {
List<HttpMessageConverter<?>> converters = new ArrayList<>();
HttpMessageConverter<?> converter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
List<MediaType> fastMediaTypes = new ArrayList<>();
fastMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
fastMediaTypes.add(MediaType.APPLICATION_JSON);
fastConverter.setSupportedMediaTypes(fastMediaTypes);
converters.add(converter);
converters.add(fastConverter);
return converters;
}
public OkHttp3ClientHttpRequestFactory clientHttpRequestFactory() {
return new OkHttp3ClientHttpRequestFactory(httpClientBuilder());
}
public OkHttpClient httpClientBuilder() {
return new OkHttpClient.Builder()
.retryOnConnectionFailure(false)
.connectionPool(this.pool())
.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
.readTimeout(readTimeout,TimeUnit.MILLISECONDS)
.writeTimeout(writeTimeout,TimeUnit.MILLISECONDS)
.build();
}
public ConnectionPool pool() {
return new ConnectionPool(maxTotal, keepAlive, TimeUnit.MILLISECONDS);
}
@Bean
public X509TrustManager x509TrustManager() {
return new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
};
}
@Bean
public SSLSocketFactory sslSocketFactory() {
try {
//信任任何链接
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{x509TrustManager()}, new SecureRandom());
return sslContext.getSocketFactory();
} catch (NoSuchAlgorithmException | KeyManagementException e) {
e.printStackTrace();
}
return null;
}
}

View File

@ -0,0 +1,51 @@
package net.lab1024.smartadmin.service.config;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.config.Task;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author 卓大
* @Date 2020/5/22
*/
@Slf4j
@Configuration
public class SmartSchedulingConfig implements SchedulingConfigurer {
private ScheduledTaskRegistrar taskRegistrar;
public ScheduledTaskRegistrar getTaskRegistrar() {
return taskRegistrar;
}
/**
* 结束定时任务
*/
public void destroy() {
List<Task> taskList = new ArrayList<>();
taskList.addAll(taskRegistrar.getCronTaskList());
taskList.addAll(taskRegistrar.getTriggerTaskList());
taskList.addAll(taskRegistrar.getFixedDelayTaskList());
taskList.addAll(taskRegistrar.getFixedRateTaskList());
List<String> taskName = taskList.stream().map(Task::toString).collect(Collectors.toList());
taskRegistrar.destroy();
log.warn("已结束定时任务:\n{}", Strings.join(taskName, '\n'));
}
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
this.taskRegistrar = taskRegistrar;
}
}

View File

@ -0,0 +1,31 @@
package net.lab1024.smartadmin.service.config;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import net.lab1024.smartadmin.service.util.date.SmartDateFormatterEnum;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* java8 localDate 时间类格式化配置
*
* @author listen
* @date 2021年8月31日 21:19
*/
@Configuration
public class SmartSerializerLocalDateTimeConfig {
@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() {
return builder -> {
builder.deserializers(new LocalDateDeserializer(SmartDateFormatterEnum.YMD.getFormatter()));
builder.deserializers(new LocalDateTimeDeserializer(SmartDateFormatterEnum.YMD_HMS.getFormatter()));
builder.serializers(new LocalDateSerializer(SmartDateFormatterEnum.YMD.getFormatter()));
builder.serializers(new LocalDateTimeSerializer(SmartDateFormatterEnum.YMD_HMS.getFormatter()));
};
}
}

View File

@ -0,0 +1,64 @@
package net.lab1024.smartadmin.service.config;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.Protocol;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Administrator
*/
@Data
@Configuration
@ConditionalOnProperty(prefix = "file.storage", name = {"mode"}, havingValue = "cloud")
public class SmartStorageCloudConfig {
@Value("${file.storage.cloud.region}")
private String region;
@Value("${file.storage.cloud.endpoint}")
private String endpoint;
@Value("${file.storage.cloud.bucket-name}")
private String bucketName;
@Value("${file.storage.cloud.access-key}")
private String accessKey;
@Value("${file.storage.cloud.secret-key}")
private String secretKey;
@Value("${file.storage.cloud.url.expire}")
private Long urlExpire;
@Value("${file.storage.cloud.url.public}")
private String publicUrl;
/**
* 初始化 云oss client 配置
*
* @return
*/
@Bean
public AmazonS3 initAmazonS3() {
ClientConfiguration clientConfig = new ClientConfiguration();
clientConfig.setProtocol(Protocol.HTTPS);
AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, secretKey)))
.withClientConfiguration(clientConfig)
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endpoint, region))
.withPathStyleAccessEnabled(false)
.build();
return s3Client;
}
}

View File

@ -1,6 +1,5 @@
package net.lab1024.smartadmin.config; package net.lab1024.smartadmin.service.config;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
@ -8,8 +7,8 @@ import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.lab1024.smartadmin.constant.SwaggerTagConst; import net.lab1024.smartadmin.service.common.constant.CommonConst;
import net.lab1024.smartadmin.interceptor.SmartAuthenticationInterceptor; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@ -17,10 +16,10 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.EnvironmentAware; import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile; import org.springframework.context.annotation.Profile;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.RequestHandler; import springfox.documentation.RequestHandler;
import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.ApiInfoBuilder;
@ -28,41 +27,29 @@ import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef; import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.*; import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2; import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* [ 根据SwaggerTagConst内部类动态生成Swagger group ] * [ 根据SwaggerTagConst内部类动态生成Swagger ]
* *
* @author yandanyang * @author 罗伊
* @version 1.0 * @date 2020/8/25 11:57
* @company 1024lab.net
* @copyright (c) 2018 1024lab.netInc. All rights reserved.
* @date 2019/8/7 0007 下午 19:20
* @since JDK1.8
*/ */
@Slf4j @Slf4j
@EnableSwagger2 @EnableSwagger2
@EnableKnife4j
@Configuration @Configuration
@Profile({"dev", "sit", "pre", "prod"}) @Conditional(SmartSystemEnvNotProdCondition.class)
public class SmartSwaggerDynamicGroupConfig implements EnvironmentAware, BeanDefinitionRegistryPostProcessor { public class SmartSwaggerDynamicGroupConfig implements EnvironmentAware, BeanDefinitionRegistryPostProcessor {
/**
* 分组名称
*/
private String apiGroupName;
/** /**
* 文档标题 * 文档标题
*/ */
@ -88,6 +75,16 @@ public class SmartSwaggerDynamicGroupConfig implements EnvironmentAware, BeanDef
*/ */
private String packAge; private String packAge;
/**
* host
*/
private String host;
/**
* 接口Tag类
*/
private String swaggerTagClass;
private int groupIndex = 0; private int groupIndex = 0;
private String groupName = "default"; private String groupName = "default";
@ -98,12 +95,13 @@ public class SmartSwaggerDynamicGroupConfig implements EnvironmentAware, BeanDef
@Override @Override
public void setEnvironment(Environment environment) { public void setEnvironment(Environment environment) {
this.apiGroupName = environment.getProperty("swagger.apiGroupName"); this.title = environment.getProperty("swagger.title" );
this.title = environment.getProperty("swagger.title"); this.description = environment.getProperty("swagger.description" );
this.description = environment.getProperty("swagger.description"); this.version = environment.getProperty("swagger.version" );
this.version = environment.getProperty("swagger.version"); this.serviceUrl = environment.getProperty("swagger.serviceUrl" );
this.serviceUrl = environment.getProperty("swagger.serviceUrl"); this.packAge = environment.getProperty("swagger.packAge" );
this.packAge = environment.getProperty("swagger.packAge"); this.host = environment.getProperty("swagger.host" );
this.swaggerTagClass = environment.getProperty("swagger.tagClass" );
} }
@Override @Override
@ -118,8 +116,15 @@ public class SmartSwaggerDynamicGroupConfig implements EnvironmentAware, BeanDef
} }
private void groupBuild() { private void groupBuild() {
Class clazz = SwaggerTagConst.class; Class clazz = null;
Class[] innerClazz = clazz.getDeclaredClasses(); try {
clazz = Class.forName(swaggerTagClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
log.error("swaggerTagClass unfounded :{}", swaggerTagClass);
return;
}
Class[] innerClazz = clazz.getClasses();
for (Class cls : innerClazz) { for (Class cls : innerClazz) {
String group = cls.getSimpleName(); String group = cls.getSimpleName();
List<String> apiTags = Lists.newArrayList(); List<String> apiTags = Lists.newArrayList();
@ -140,57 +145,33 @@ public class SmartSwaggerDynamicGroupConfig implements EnvironmentAware, BeanDef
} }
private Docket baseDocket() { private Docket baseDocket() {
// 配置全局参数 token // 配置全局参数 token header
ParameterBuilder tokenPar = new ParameterBuilder(); ParameterBuilder tokenPar = new ParameterBuilder();
Parameter parameter = tokenPar.name(SmartAuthenticationInterceptor.TOKEN_NAME) tokenPar.name(CommonConst.Token.OUTER_TOKEN_NAME)
.description("token") .description("token" )
.modelRef(new ModelRef("string")) .modelRef(new ModelRef("string" ))
.parameterType("header") .parameterType("header" ).defaultValue("1" )
.defaultValue("")
.required(false) .required(false)
.build(); .build();
// 请求类型过滤规则 // 此行必须放在配置前面执行 因为要初始化groupName
Predicate<RequestHandler> controllerPredicate = getControllerPredicate(); Predicate<RequestHandler> controllerPredicate = this.getControllerPredicate();
// controller 包路径 // swagger配置
Predicate<RequestHandler> controllerPackage = RequestHandlerSelectors.basePackage(packAge); Docket docket = new Docket(DocumentationType.SWAGGER_2)
return new Docket(DocumentationType.SWAGGER_2)
.groupName(groupName) .groupName(groupName)
.forCodeGeneration(true) .forCodeGeneration(true)
.select() .select()
.apis(controllerPackage) .apis(RequestHandlerSelectors.basePackage(packAge))
.apis(controllerPredicate) .apis(controllerPredicate)
.paths(PathSelectors.any()) .paths(PathSelectors.any())
.build() .build().apiInfo(this.serviceApiInfo())
.apiInfo(this.serviceApiInfo()) .globalOperationParameters(Lists.newArrayList(tokenPar.build()));
.securitySchemes(securitySchemes())
.securityContexts(securityContexts())
.globalOperationParameters(Lists.newArrayList(parameter));
}
private List<ApiKey> securitySchemes() { if (StringUtils.isNotBlank(host)) {
List<ApiKey> apiKeyList = new ArrayList<>(); docket = docket.host(host);
apiKeyList.add(new ApiKey("x-access-token", "x-access-token", "header")); }
return apiKeyList;
}
private List<SecurityContext> securityContexts() { return docket;
List<SecurityContext> securityContexts = new ArrayList<>();
securityContexts.add(
SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.any())
.build());
return securityContexts;
}
List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
List<SecurityReference> securityReferences = new ArrayList<>();
securityReferences.add(new SecurityReference("x-access-token", authorizationScopes));
return securityReferences;
} }
private Predicate<RequestHandler> getControllerPredicate() { private Predicate<RequestHandler> getControllerPredicate() {
@ -203,34 +184,18 @@ public class SmartSwaggerDynamicGroupConfig implements EnvironmentAware, BeanDef
api = apiOptional.get(); api = apiOptional.get();
} }
List<String> tags = Arrays.asList(api.tags()); List<String> tags = Arrays.asList(api.tags());
if (api != null && apiTags.containsAll(tags)) { return apiTags.containsAll(tags);
return true;
}
return false;
}; };
groupIndex++; groupIndex++;
return Predicates.or( return Predicates.and(RequestHandlerSelectors.withClassAnnotation(RestController.class), methodPredicate);
Predicates.and(RequestHandlerSelectors.withClassAnnotation(RestController.class), methodPredicate),
Predicates.and(
RequestHandlerSelectors.withMethodAnnotation(ResponseBody.class), methodPredicate)
);
} }
private ApiInfo serviceApiInfo() { private ApiInfo serviceApiInfo() {
return new ApiInfoBuilder() return new ApiInfoBuilder().title(title).description(description).version(version).termsOfServiceUrl(serviceUrl).build();
.title(title)
.description(description)
.version(version)
.license("Apache License Version 2.0")
.contact(new Contact("1024创新实验室", "http://www.1024lab.net", ""))
.termsOfServiceUrl(serviceUrl)
.build();
} }
@Override @Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
} }
} }

View File

@ -0,0 +1,35 @@
package net.lab1024.smartadmin.service.config;
import net.lab1024.smartadmin.service.common.constant.SystemEnvironmentEnum;
import net.lab1024.smartadmin.service.util.SmartBaseEnumUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* 当前系统环境
*
* @author listen
* @date 2019/08/27 08:56
*/
@Configuration
public class SmartSystemEnvNotProdCondition implements Condition {
@Value("${spring.profiles.active}")
private String systemEnvironment;
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
String property = conditionContext.getEnvironment().getProperty("spring.profiles.active");
return StringUtils.isNotBlank(property) && !SystemEnvironmentEnum.PROD.equalsValue(property);
}
@Bean
public SystemEnvironmentEnum initEnvironment() {
return SmartBaseEnumUtil.getEnumByValue(systemEnvironment, SystemEnvironmentEnum.class);
}
}

View File

@ -0,0 +1,48 @@
package net.lab1024.smartadmin.service.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.Map;
import java.util.Map.Entry;
/**
* @Description
* @Author 善逸
* @Date Created in 2017/10/24 13:48
*/
@Configuration
public class SmartWebAppConfig implements WebMvcConfigurer {
@Autowired
private Map<String, HandlerInterceptor> interceptorMp;
@Value("${file.storage.local.path}")
private String localPath;
@Override
public void addInterceptors(InterceptorRegistry registry) {
for(Entry<String, HandlerInterceptor> entry : interceptorMp.entrySet()){
registry.addInterceptor(entry.getValue()).addPathPatterns(entry.getKey() + "/**");
}
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/druidMonitor").setViewName("redirect:druid/index.html");
registry.addViewController("/swaggerApi").setViewName("redirect:swagger-ui.html");
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/META-INF/resources/","classpath:/resources/","classpath:/static/","file:" + localPath);
}
}

View File

@ -0,0 +1,66 @@
package net.lab1024.smartadmin.service.filters;
import net.lab1024.smartadmin.service.common.constant.CommonConst;
import net.lab1024.smartadmin.service.module.system.login.EmployeeLoginTokenService;
import net.lab1024.smartadmin.service.module.system.login.domain.EmployeeLoginBO;
import net.lab1024.smartadmin.service.module.system.login.domain.EmployeeLoginInfoDTO;
import net.lab1024.smartadmin.service.util.SmartBeanUtil;
import net.lab1024.smartadmin.service.util.SmartEmployeeTokenUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* token过滤器
*/
@Component
public class SmartTokenFilter extends OncePerRequestFilter {
private static final String TOKEN_NAME = "x-access-token";
@Autowired
private EmployeeLoginTokenService loginTokenService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
//需要做token校验, 消息头的token优先于请求query参数的token
String xHeaderToken = request.getHeader(TOKEN_NAME);
String xRequestToken = request.getParameter(TOKEN_NAME);
String xAccessToken = null != xHeaderToken ? xHeaderToken : xRequestToken;
if (StringUtils.isBlank(xAccessToken)) {
// 若未给予spring security上下文用户授权 则会授权失败 进入AuthenticationEntryPointImpl
chain.doFilter(request, response);
return;
}
// 先清理spring security上下文
SecurityContextHolder.clearContext();
// 判断请求分组
String requestURI = request.getRequestURI();
if (StringUtils.startsWithIgnoreCase(requestURI, CommonConst.ApiUrl.API_PREFIX_ADMIN)) {
// 后管 获取用户信息
EmployeeLoginBO loginBO = loginTokenService.getEmployeeLoginBO(xAccessToken);
// 若获取到了登陆信息 则把用户信息设置到上下文中
if (null != loginBO) {
SmartEmployeeTokenUtil.setUser(SmartBeanUtil.copy(loginBO, EmployeeLoginInfoDTO.class));
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginBO, null, loginBO.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
// 若未给予spring security上下文用户授权 则会授权失败 进入AuthenticationEntryPointImpl
chain.doFilter(request, response);
}
}

View File

@ -0,0 +1,39 @@
package net.lab1024.smartadmin.service.handler;
import com.alibaba.fastjson.JSONObject;
import net.lab1024.smartadmin.service.common.codeconst.LoginResponseCodeConst;
import net.lab1024.smartadmin.service.common.codeconst.ResponseCodeConst;
import net.lab1024.smartadmin.service.common.domain.ResponseDTO;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 认证失败处理
*/
@Component
public class AuthenticationFailHandler implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException {
this.outputResult(response, LoginResponseCodeConst.LOGIN_ERROR);
}
/**
* 输出
*
* @param response
* @param respCode
* @throws IOException
*/
private void outputResult(HttpServletResponse response, ResponseCodeConst respCode) throws IOException {
String msg = JSONObject.toJSONString(ResponseDTO.wrap(respCode));
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(msg);
response.flushBuffer();
}
}

View File

@ -0,0 +1,104 @@
package net.lab1024.smartadmin.service.handler;
import lombok.extern.slf4j.Slf4j;
import net.lab1024.smartadmin.service.common.codeconst.ResponseCodeConst;
import net.lab1024.smartadmin.service.common.constant.SystemEnvironmentEnum;
import net.lab1024.smartadmin.service.common.domain.ResponseDTO;
import net.lab1024.smartadmin.service.common.exception.SmartBusinessException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.util.List;
import java.util.stream.Collectors;
/**
* [ 全局异常拦截 ]
*
* @author 罗伊
* @date 2020/8/25 11:57
*/
@Slf4j
@ControllerAdvice
public class SmartGlobalExceptionHandler {
@Autowired
private SystemEnvironmentEnum systemEnvironmentEnum;
/**
* 添加全局异常处理流程
*
* @param e
* @return
* @throws Exception
*/
@ResponseBody
@ExceptionHandler(Exception.class)
public ResponseDTO exceptionHandler(Exception e) {
// json 格式错误
if (e instanceof HttpMessageNotReadableException) {
return ResponseDTO.wrap(ResponseCodeConst.JSON_FORMAT_ERROR);
}
String uri = null;
RequestAttributes request = RequestContextHolder.getRequestAttributes();
if (null != request) {
ServletRequestAttributes servletRequest = (ServletRequestAttributes) request;
uri = servletRequest.getRequest().getRequestURI();
}
// http 请求方式错误
if (e instanceof HttpRequestMethodNotSupportedException) {
return ResponseDTO.wrap(ResponseCodeConst.REQUEST_METHOD_ERROR);
}
// 参数类型错误
if (e instanceof TypeMismatchException) {
return ResponseDTO.wrap(ResponseCodeConst.ERROR_PARAM);
}
// 参数校验未通过
if (e instanceof MethodArgumentNotValidException) {
List<FieldError> fieldErrors = ((MethodArgumentNotValidException) e).getBindingResult().getFieldErrors();
List<String> msgList = fieldErrors.stream().map(FieldError::getDefaultMessage).collect(Collectors.toList());
return ResponseDTO.wrapMsg(ResponseCodeConst.ERROR_PARAM, String.join(",", msgList));
}
// 参数绑定错误
if (e instanceof BindException) {
List<FieldError> fieldErrors = ((BindException) e).getFieldErrors();
List<String> error = fieldErrors.stream().map(field -> field.getField() + ":" + field.getRejectedValue()).collect(Collectors.toList());
String errorMsg = ResponseCodeConst.ERROR_PARAM.getMsg() + ":" + error.toString();
return ResponseDTO.wrapMsg(ResponseCodeConst.ERROR_PARAM, errorMsg);
}
if (e instanceof SmartBusinessException) {
return ResponseDTO.wrapMsg(ResponseCodeConst.SYSTEM_ERROR, e.getMessage());
}
if (e instanceof AccessDeniedException) {
return ResponseDTO.wrapMsg(ResponseCodeConst.SYSTEM_ERROR, "您暂无权限");
}
log.error("捕获全局异常,URL:{}", uri, e);
// 正式环境 不返回错误信息
if (SystemEnvironmentEnum.PROD == systemEnvironmentEnum) {
return ResponseDTO.wrap(ResponseCodeConst.SYSTEM_ERROR);
}
return ResponseDTO.wrapMsg(ResponseCodeConst.SYSTEM_ERROR, e.toString());
}
}

View File

@ -0,0 +1,60 @@
package net.lab1024.smartadmin.service.interceptor;
import net.lab1024.smartadmin.service.common.constant.CommonConst;
import net.lab1024.smartadmin.service.util.SmartEmployeeTokenUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* [ 登录拦截器 ]
*
* @author 罗伊
*/
@Component(CommonConst.ApiUrl.API_PREFIX_ADMIN)
public class BusinessAuthorityInterceptor implements HandlerInterceptor {
@Value("${access-control-allow-origin}")
private String accessControlAllowOrigin;
/**
* 拦截服务器端响应处理ajax请求返回结果
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//跨域设置
this.crossDomainConfig(response);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
SmartEmployeeTokenUtil.remove();
}
/**
* 配置跨域
*
* @param response
*/
private void crossDomainConfig(HttpServletResponse response) {
response.setHeader("Access-Control-Allow-Origin", accessControlAllowOrigin);
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE, PATCH");
response.setHeader("Access-Control-Expose-Headers", "*");
response.setHeader("Access-Control-Allow-Headers", "Authentication,Origin, X-Requested-With, Content-Type, " + "Accept, x-access-token");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.setHeader("Expires ", "-1");
}
}

View File

@ -0,0 +1,54 @@
package net.lab1024.smartadmin.service.interceptor;
import net.lab1024.smartadmin.service.common.constant.CommonConst;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 公用api 拦截器
*
* @author Administrator
*/
@Component(CommonConst.ApiUrl.API_PREFIX_SUPPORT)
public class SupportAuthorityInterceptor implements HandlerInterceptor {
@Value("${access-control-allow-origin}")
private String accessControlAllowOrigin;
/**
* 拦截服务器端响应处理ajax请求返回结果
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//跨域设置
this.crossDomainConfig(response);
return true;
}
/**
* 配置跨域
*
* @param response
*/
private void crossDomainConfig(HttpServletResponse response) {
response.setHeader("Access-Control-Allow-Origin", accessControlAllowOrigin);
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE, PATCH");
response.setHeader("Access-Control-Expose-Headers", "*");
response.setHeader("Access-Control-Allow-Headers", "Authentication,Origin, X-Requested-With, Content-Type, " + "Accept, x-access-token");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.setHeader("Expires ", "-1");
}
}

View File

@ -0,0 +1,29 @@
package net.lab1024.smartadmin.service.listener;
import lombok.extern.slf4j.Slf4j;
import net.lab1024.smartadmin.service.common.codeconst.ResponseCodeRegister;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
/**
* 应用启动加载
*
* @author zhuo
* @version 1.0
* @since JDK1.8
*/
@Slf4j
@Component
public class SmartAdminStartupRunner implements CommandLineRunner {
@Override
public void run(String... args) {
log.info("###################### init start ######################");
// 初始化状态码
ResponseCodeRegister.init();
log.info("###################### init complete ######################");
}
}

View File

@ -0,0 +1,57 @@
package net.lab1024.smartadmin.service.module.business.category;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import net.lab1024.smartadmin.service.common.constant.SwaggerTagConst;
import net.lab1024.smartadmin.service.common.controller.AdminBaseController;
import net.lab1024.smartadmin.service.common.domain.ResponseDTO;
import net.lab1024.smartadmin.service.module.business.category.domain.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
/**
* 类目 路由
*
* @author 胡克
* @date 2021/1/21 9:10
*/
@Api(tags = SwaggerTagConst.Admin.MANAGER_CATEGORY)
@RestController
public class CategoryController extends AdminBaseController {
@Autowired
private CategoryService categoryService;
@ApiOperation("添加类目 by listen")
@PostMapping("/category/add")
public ResponseDTO<String> add(@RequestBody @Valid CategoryAddDTO addDTO) {
return categoryService.add(addDTO);
}
@ApiOperation("更新类目 by listen")
@PostMapping("/category/update")
public ResponseDTO<String> update(@RequestBody @Valid CategoryUpdateDTO updateDTO) {
return categoryService.update(updateDTO);
}
@ApiOperation("查询类目详情 by listen")
@GetMapping("/category/{categoryId}")
public ResponseDTO<CategoryVO> queryDetail(@PathVariable Long categoryId) {
return categoryService.queryDetail(categoryId);
}
@ApiOperation("查询类目层级树 by listen")
@PostMapping("/category/tree")
public ResponseDTO<List<CategoryTreeVO>> queryTree(@RequestBody @Valid CategoryTreeQueryDTO queryDTO) {
return categoryService.queryTree(queryDTO);
}
@ApiOperation("删除类目 by listen")
@GetMapping("/category/del/{categoryId}")
public ResponseDTO<String> delete(@PathVariable Long categoryId) {
return categoryService.delete(categoryId);
}
}

View File

@ -0,0 +1,50 @@
package net.lab1024.smartadmin.service.module.business.category;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import net.lab1024.smartadmin.service.module.business.category.constant.CategoryTypeEnum;
import net.lab1024.smartadmin.service.module.business.category.domain.CategoryEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 类目 dao
*
* @author 胡克
* @date 2021/1/20 16:29
*/
@Component
@Mapper
public interface CategoryDao extends BaseMapper<CategoryEntity> {
/**
* 根据父级id 类型 查询子类
*
* @param parentIdList
* @param categoryType {@link CategoryTypeEnum}
* @param deletedFlag
* @return
*/
List<CategoryEntity> queryByParentId(@Param("parentIdList") List<Long> parentIdList,
@Param("categoryType") Integer categoryType,
@Param("deletedFlag") Boolean deletedFlag);
/**
* 根据类型和id查询
* @param categoryType
* @param categoryId
* @return
*/
CategoryEntity selectByTypeAndId(@Param("categoryType") Integer categoryType, @Param("categoryId") Long categoryId);
/**
* 查看类目 具体条件 看sql
*
* @param entity
* @return
*/
CategoryEntity selectOne(CategoryEntity entity);
}

View File

@ -0,0 +1,333 @@
package net.lab1024.smartadmin.service.module.business.category;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import net.lab1024.smartadmin.service.common.constant.CacheModuleBaseConst;
import net.lab1024.smartadmin.service.common.constant.CommonConst;
import net.lab1024.smartadmin.service.module.business.category.domain.CategoryEntity;
import net.lab1024.smartadmin.service.module.business.category.domain.CategorySimpleDTO;
import net.lab1024.smartadmin.service.module.business.category.domain.CategoryTreeQueryDTO;
import net.lab1024.smartadmin.service.module.business.category.domain.CategoryTreeVO;
import net.lab1024.smartadmin.service.module.support.beancache.cache.IBeanCache;
import net.lab1024.smartadmin.service.module.support.beancache.key.CacheKey;
import net.lab1024.smartadmin.service.module.support.beancache.load.CacheLoad;
import net.lab1024.smartadmin.service.util.SmartBeanUtil;
import net.lab1024.smartadmin.service.util.SmartStringUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 类目 查询 业务类
*
* @author 胡克
* @date 2021/1/20 16:26
*/
@Service
@Slf4j
public class CategoryQueryService {
@Autowired
private CategoryDao categoryDao;
@Autowired
private IBeanCache cache;
/**
* 查詢类目
*
* @param cacheKey
* @return
*/
@CacheLoad(CacheModuleBaseConst.Category.CATEGORY)
public CategoryEntity queryCategory(String cacheKey) {
String businessId = CacheKey.getBusinessIdByCacheKey(cacheKey);
return categoryDao.selectById(businessId);
}
/**
* 查询类目 子级
*
* @param cacheKey
* @return
*/
@CacheLoad(CacheModuleBaseConst.Category.CATEGORY_SUB)
public List<CategoryEntity> querySubCategory(String cacheKey) {
/**
* 下划线 分隔 key
* 左边 categoryId 右边 type
*/
String businessId = CacheKey.getBusinessIdByCacheKey(cacheKey);
String[] split = businessId.split(CommonConst.UNDERLINE);
Integer categoryType = split.length > 1 ? Integer.valueOf(split[1]) : null;
return categoryDao.queryByParentId(Lists.newArrayList(Long.valueOf(split[0])), categoryType, false);
}
/**
* 类目id+下划线+类型 作为缓存key
*
* @param categoryId
* @param categoryType
* @return
*/
private static String getCacheId(Long categoryId, Integer categoryType) {
return categoryId + CommonConst.UNDERLINE + categoryType;
}
/**
* 批量查询类目 子级
*
* @param categoryIdList
* @return
*/
public Map<Long, List<CategoryEntity>> querySubCategoryFromCache(List<Long> categoryIdList) {
return categoryIdList.stream().collect(Collectors.toMap(Function.identity(), e -> {
String cacheKey = CacheKey.cacheKey(CacheModuleBaseConst.Category.CATEGORY_SUB, e.toString());
return cache.get(cacheKey);
}));
}
/**
* 根据 id 查询未删除的类目
*
* @param categoryId
* @return 可能 null
*/
public Optional<CategoryEntity> queryCategory(Long categoryId) {
if (null == categoryId) {
return Optional.empty();
}
String cacheKey = CacheKey.cacheKey(CacheModuleBaseConst.Category.CATEGORY, categoryId.toString());
CategoryEntity entity = cache.get(cacheKey);
if (null == entity || entity.getDeletedFlag()) {
return Optional.empty();
}
return Optional.of(entity);
}
/**
* 根据 类目id 查询未删除的子类
*
* @param categoryId
* @return 没有返回空集合
*/
public List<CategoryEntity> queryCategoryByParent(Long categoryId, Integer categoryType) {
if (null == categoryId) {
return CommonConst.EMPTY_LIST;
}
String cacheKey = CacheKey.cacheKey(CacheModuleBaseConst.Category.CATEGORY_SUB, getCacheId(categoryId, categoryType));
return cache.get(cacheKey);
}
/**
* 根据 类目id集合 查询未删除的类目集合
*
* @param categoryIdList
* @return
*/
public Map<Long, CategoryEntity> queryCategoryList(List<Long> categoryIdList) {
if (CollectionUtils.isEmpty(categoryIdList)) {
return CommonConst.EMPTY_MAP;
}
categoryIdList = categoryIdList.stream().distinct().collect(Collectors.toList());
return categoryIdList.stream().collect(Collectors.toMap(Function.identity(), e -> {
String cacheKey = CacheKey.cacheKey(CacheModuleBaseConst.Category.CATEGORY, e.toString());
return cache.get(cacheKey);
}));
}
/**
* 根据类目id 移除缓存
*/
public void removeCache() {
cache.removeByModule(CacheModuleBaseConst.Category.CATEGORY);
cache.removeByModule(CacheModuleBaseConst.Category.CATEGORY_SUB);
// 移除整个类目树缓存
cache.removeByModule(CacheModuleBaseConst.Category.CATEGORY_TREE);
}
/**
* 根据类目id 递归查询该id的所有子类id 递归查询
* 同时存入缓存
* 注意查询出来的集合 不包含传递的父类参数
*
* @param categoryIdList
*/
public List<Long> queryCategorySubId(List<Long> categoryIdList) {
if (CollectionUtils.isEmpty(categoryIdList)) {
return CommonConst.EMPTY_LIST;
}
// 查询所有子类
Map<Long, List<CategoryEntity>> subTypeMap = this.querySubCategoryFromCache(categoryIdList);
if (MapUtils.isEmpty(subTypeMap)) {
return Lists.newArrayList();
}
// 递归查询子类
categoryIdList = subTypeMap.values().stream().flatMap(Collection::stream).map(CategoryEntity::getCategoryId).distinct().collect(Collectors.toList());
if (CollectionUtils.isEmpty(categoryIdList)) {
return Lists.newArrayList();
}
categoryIdList.addAll(this.queryCategorySubId(categoryIdList));
return categoryIdList;
}
/**
* 查询自身以及所有子节点
* @param categoryIdList
* @return
*/
public List<Long> queryCategorySelfAndSubId(List<Long> categoryIdList) {
List<Long> subIdList = this.queryCategorySubId(categoryIdList);
subIdList.addAll(categoryIdList);
return subIdList;
}
/**
* 查询类目 层级树
* 优先查询缓存
*
* @return
*/
public List<CategoryTreeVO> queryCategoryTree(CategoryTreeQueryDTO queryDTO) {
// 查询缓存
Long parentId = queryDTO.getParentId();
Integer categoryType = queryDTO.getCategoryType();
String cacheKey = CacheKey.cacheKey(CacheModuleBaseConst.Category.CATEGORY_TREE, getCacheId(parentId, categoryType));
List<CategoryTreeVO> treeList = cache.get(cacheKey);
if (null != treeList) {
return treeList;
}
// 查询一级类目
List<CategoryEntity> categoryEntityList = this.queryCategoryByParent(parentId, categoryType);
treeList = SmartBeanUtil.copyList(categoryEntityList, CategoryTreeVO.class);
treeList.forEach(e -> {
e.setLabel(e.getCategoryName());
e.setValue(e.getCategoryId());
e.setCategoryFullName(e.getCategoryName());
});
// 递归设置子类
this.queryAndSetSubCategory(treeList);
// 放入缓存
cache.put(cacheKey, treeList);
return treeList;
}
/**
* 递归查询设置类目子类
* 从缓存查询子类
*
* @param treeList
*/
private void queryAndSetSubCategory(List<CategoryTreeVO> treeList) {
if (CollectionUtils.isEmpty(treeList)) {
return;
}
List<Long> parentIdList = treeList.stream().map(CategoryTreeVO::getValue).collect(Collectors.toList());
Map<Long, List<CategoryEntity>> categorySubMap = this.querySubCategoryFromCache(parentIdList);
treeList.forEach(e -> {
List<CategoryEntity> childrenEntityList = categorySubMap.getOrDefault(e.getValue(), Lists.newArrayList());
List<CategoryTreeVO> childrenVOList = SmartBeanUtil.copyList(childrenEntityList, CategoryTreeVO.class);
childrenVOList.forEach(item -> {
item.setLabel(item.getCategoryName());
item.setValue(item.getCategoryId());
item.setCategoryFullName(e.getCategoryFullName() + CommonConst.SEPARATOR_SLASH + item.getCategoryName());
});
// 递归查询
this.queryAndSetSubCategory(childrenVOList);
e.setChildren(childrenVOList);
});
}
/**
* 根据类目id 查询类目详情 包含类目全称 医考/医师资格/临床执业
*
* @param categoryId
* @return
*/
public CategorySimpleDTO queryCategoryInfo(Long categoryId) {
String cacheKey = CacheKey.cacheKey(CacheModuleBaseConst.Category.CATEGORY, categoryId.toString());
CategoryEntity categoryEntity = cache.get(cacheKey);
if (null == categoryEntity || categoryEntity.getDeletedFlag()) {
return null;
}
// 递归查询分类和所有父级类目
List<CategoryEntity> parentCategoryList = this.queryCategoryAndParent(categoryId);
// 拼接父级类目名称 斜杠分隔返回
List<String> nameList = parentCategoryList.stream().map(CategoryEntity::getCategoryName).collect(Collectors.toList());
// 返回DTO
CategorySimpleDTO categoryDTO = new CategorySimpleDTO();
categoryDTO.setCategoryId(categoryId);
categoryDTO.setCategoryName(categoryEntity.getCategoryName());
categoryDTO.setCategoryFullName(SmartStringUtil.join(nameList, CommonConst.SEPARATOR_SLASH));
categoryDTO.setParentId(categoryEntity.getParentId());
return categoryDTO;
}
/**
* 递归查询分类和所有父级类目 ps:特别注意返回的集合中 包含自己
*
* @param categoryId
* @return
*/
public List<CategoryEntity> queryCategoryAndParent(Long categoryId) {
List<CategoryEntity> parentCategoryList = Lists.newArrayList();
String cacheKey = CacheKey.cacheKey(CacheModuleBaseConst.Category.CATEGORY, categoryId.toString());
CategoryEntity categoryEntity = cache.get(cacheKey);
if (null == categoryEntity || categoryEntity.getDeletedFlag()) {
return parentCategoryList;
}
// 父级始终放在第一位
parentCategoryList.add(0, categoryEntity);
Long parentId = categoryEntity.getParentId();
if (Objects.equals(CommonConst.DEFAULT_PARENT_ID, parentId)) {
return parentCategoryList;
}
parentCategoryList.addAll(0, this.queryCategoryAndParent(parentId));
return parentCategoryList;
}
/**
* 处理类目名称
*
* @param categoryIdList
*/
public List<String> queryCategoryName(List<Long> categoryIdList) {
if (CollectionUtils.isEmpty(categoryIdList)) {
return null;
}
Map<Long, CategoryEntity> categoryMap = this.queryCategoryList(categoryIdList);
List<String> categoryNameList = Lists.newArrayList();
categoryIdList.forEach(e -> {
CategoryEntity categoryEntity = categoryMap.get(e);
if (categoryEntity != null) {
categoryNameList.add(categoryMap.get(e).getCategoryName());
}
});
return categoryNameList;
}
/**
* 根据类目id 查询类目名称
*
* @param categoryId
* @return
*/
public String queryCategoryName(Long categoryId) {
String cacheKey = CacheKey.cacheKey(CacheModuleBaseConst.Category.CATEGORY, categoryId.toString());
CategoryEntity categoryEntity = cache.get(cacheKey);
if (null == categoryEntity || categoryEntity.getDeletedFlag()) {
return null;
}
return categoryEntity.getCategoryName();
}
}

View File

@ -0,0 +1,207 @@
package net.lab1024.smartadmin.service.module.business.category;
import com.google.common.collect.Lists;
import net.lab1024.smartadmin.service.common.codeconst.ResponseCodeConst;
import net.lab1024.smartadmin.service.common.constant.CommonConst;
import net.lab1024.smartadmin.service.common.domain.ResponseDTO;
import net.lab1024.smartadmin.service.module.business.category.domain.*;
import net.lab1024.smartadmin.service.util.SmartBeanUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
/**
* 类目 业务
*
* @author 胡克
* @date 2021/1/20 16:26
*/
@Service
public class CategoryService {
@Autowired
private CategoryDao categoryDao;
@Autowired
private CategoryQueryService categoryQueryService;
/**
* 添加类目
*
* @author 胡克
* @date 2021/1/20 17:17
*/
public ResponseDTO<String> add(CategoryAddDTO addDTO) {
// 校验类目
CategoryEntity categoryEntity = SmartBeanUtil.copy(addDTO, CategoryEntity.class);
ResponseDTO<String> res = this.checkCategory(categoryEntity, false);
if (!res.isSuccess()) {
return res;
}
// 没有父类则使用默认父类
Long parentId = null == addDTO.getParentId() ? CommonConst.DEFAULT_PARENT_ID : addDTO.getParentId();
categoryEntity.setParentId(parentId);
categoryEntity.setSort(null == addDTO.getSort() ? CommonConst.ZERO : addDTO.getSort());
categoryEntity.setDeletedFlag(false);
// 保存数据
categoryDao.insert(categoryEntity);
// 更新缓存
categoryQueryService.removeCache();
return ResponseDTO.succ();
}
/**
* 更新类目
* 不能更新父级类目
*
* @author 胡克
* @date 2021/1/20 17:17
*/
public ResponseDTO<String> update(CategoryUpdateDTO updateDTO) {
// 校验类目
Long categoryId = updateDTO.getCategoryId();
Optional<CategoryEntity> optional = categoryQueryService.queryCategory(categoryId);
if (!optional.isPresent()) {
return ResponseDTO.wrap(ResponseCodeConst.NOT_EXISTS);
}
CategoryEntity categoryEntity = SmartBeanUtil.copy(updateDTO, CategoryEntity.class);
/**
* 不更新类目类型
* 不更新父类id
*/
Integer categoryType = optional.get().getCategoryType();
categoryEntity.setCategoryType(categoryType);
categoryEntity.setParentId(optional.get().getParentId());
ResponseDTO<String> responseDTO = this.checkCategory(categoryEntity, true);
if (!responseDTO.isSuccess()) {
return responseDTO;
}
categoryDao.updateById(categoryEntity);
// 更新缓存
categoryQueryService.removeCache();
return ResponseDTO.succ();
}
/**
* 新增/更新 类目时的 校验
*
* @param categoryEntity
* @param isUpdate
* @return
*/
private ResponseDTO<String> checkCategory(CategoryEntity categoryEntity, boolean isUpdate) {
// 校验父级是否存在
Long parentId = categoryEntity.getParentId();
Integer categoryType = categoryEntity.getCategoryType();
if (null != parentId) {
if (Objects.equals(categoryEntity.getCategoryId(), parentId)) {
return ResponseDTO.wrapMsg(ResponseCodeConst.ERROR_PARAM, "父级类目怎么和自己相同了");
}
if (!Objects.equals(parentId, CommonConst.DEFAULT_PARENT_ID)) {
Optional<CategoryEntity> optional = categoryQueryService.queryCategory(parentId);
if (!optional.isPresent()) {
return ResponseDTO.wrapMsg(ResponseCodeConst.NOT_EXISTS, "父级类目不存在~");
}
CategoryEntity parent = optional.get();
if (!Objects.equals(categoryType, parent.getCategoryType())) {
return ResponseDTO.wrapMsg(ResponseCodeConst.ERROR_PARAM, "与父级类目类型不一致");
}
}
} else {
// 如果没有父类 使用默认父类
parentId = CommonConst.DEFAULT_PARENT_ID;
}
// 校验同父类下 名称是否重复
CategoryEntity queryEntity = new CategoryEntity();
queryEntity.setParentId(parentId);
queryEntity.setCategoryType(categoryType);
queryEntity.setCategoryName(categoryEntity.getCategoryName());
queryEntity.setDeletedFlag(false);
queryEntity = categoryDao.selectOne(queryEntity);
if (null != queryEntity) {
if (isUpdate) {
if (!Objects.equals(queryEntity.getCategoryId(), categoryEntity.getCategoryId())) {
return ResponseDTO.wrapMsg(ResponseCodeConst.ERROR_PARAM, "同级下已存在相同类目~");
}
} else {
return ResponseDTO.wrapMsg(ResponseCodeConst.ERROR_PARAM, "同级下已存在相同类目~");
}
}
return ResponseDTO.succ();
}
/**
* 查询 类目详情
*
* @param categoryId
* @return
*/
public ResponseDTO<CategoryVO> queryDetail(Long categoryId) {
Optional<CategoryEntity> optional = categoryQueryService.queryCategory(categoryId);
if (!optional.isPresent()) {
return ResponseDTO.wrap(ResponseCodeConst.NOT_EXISTS);
}
CategoryVO adminVO = SmartBeanUtil.copy(optional.get(), CategoryVO.class);
return ResponseDTO.succData(adminVO);
}
/**
* 根据父级id 查询所有子类 返回层级树
* 如果父类id 为空 返回所有类目层级
*
* @param queryDTO
* @return
*/
public ResponseDTO<List<CategoryTreeVO>> queryTree(CategoryTreeQueryDTO queryDTO) {
if (null == queryDTO.getParentId()) {
if (null == queryDTO.getCategoryType()) {
return ResponseDTO.wrapMsg(ResponseCodeConst.ERROR_PARAM, "类目类型不能为空");
}
queryDTO.setParentId(CommonConst.DEFAULT_PARENT_ID);
}
List<CategoryTreeVO> treeList = categoryQueryService.queryCategoryTree(queryDTO);
return ResponseDTO.succData(treeList);
}
/**
* 删除类目
* 如果有未删除的子类 则无法删除
*
* @param categoryId
* @return
*/
public ResponseDTO<String> delete(Long categoryId) {
Optional<CategoryEntity> optional = categoryQueryService.queryCategory(categoryId);
if (!optional.isPresent()) {
return ResponseDTO.wrap(ResponseCodeConst.NOT_EXISTS);
}
List<Long> categorySubId = categoryQueryService.queryCategorySubId(Lists.newArrayList(categoryId));
if (CollectionUtils.isNotEmpty(categorySubId)) {
return ResponseDTO.wrapMsg(ResponseCodeConst.ERROR_PARAM, "请先删除子级类目");
}
// 更新数据
CategoryEntity categoryEntity = new CategoryEntity();
categoryEntity.setCategoryId(categoryId);
categoryEntity.setDeletedFlag(true);
categoryDao.updateById(categoryEntity);
// 更新缓存
categoryQueryService.removeCache();
return ResponseDTO.succ();
}
}

View File

@ -0,0 +1,54 @@
package net.lab1024.smartadmin.service.module.business.category.constant;
import net.lab1024.smartadmin.service.common.constant.BaseEnum;
/**
* 分类类型 枚举
*
* @author listen
* @date 2021/08/05 15:26
*/
public enum CategoryTypeEnum implements BaseEnum {
/**
* 1 商品
*/
GOODS(1, "商品"),
/**
* 2 测试分类
*/
DEMO(2, "测试分类"),
;
private final Integer type;
private final String desc;
CategoryTypeEnum(Integer type, String desc) {
this.type = type;
this.desc = desc;
}
/**
* 获取枚举类的值
*
* @return Integer
*/
@Override
public Integer getValue() {
return type;
}
/**
* 获取枚举类的说明
*
* @return String
*/
@Override
public String getDesc() {
return desc;
}
}

View File

@ -0,0 +1,14 @@
package net.lab1024.smartadmin.service.module.business.category.domain;
import lombok.Data;
/**
* 类目 添加 DTO
*
* @author 胡克
* @date 2021/1/20 16:24
*/
@Data
public class CategoryAddDTO extends CategoryBaseDTO {
}

View File

@ -0,0 +1,44 @@
package net.lab1024.smartadmin.service.module.business.category.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import net.lab1024.smartadmin.service.common.swagger.ApiModelPropertyEnum;
import net.lab1024.smartadmin.service.common.validator.CheckEnum;
import net.lab1024.smartadmin.service.module.business.category.constant.CategoryTypeEnum;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* 类目 基础属性 DTO
*
* @author 胡克
* @date 2021/1/20 16:17
*/
@Data
public class CategoryBaseDTO {
@ApiModelProperty(value = "类目名称", required = true)
@NotBlank(message = "类目名称不能为空")
@Length(max = 20, message = "类目名称最多20字符")
private String categoryName;
@ApiModelPropertyEnum(desc = "分类类型", value = CategoryTypeEnum.class)
@CheckEnum(enumClazz = CategoryTypeEnum.class, required = true, message = "分类类型错误")
private Integer categoryType;
@ApiModelProperty("父级类目id|可选")
private Long parentId;
@ApiModelProperty("排序|可选")
private Integer sort;
@ApiModelProperty("备注|可选")
@Length(max = 200, message = "备注最多200字符")
private String remark;
@ApiModelProperty("禁用状态")
@NotNull(message = "禁用状态不能为空")
private Boolean disabledFlag;
}

View File

@ -0,0 +1,63 @@
package net.lab1024.smartadmin.service.module.business.category.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 胡克
* @date 2021/8/6 9:45
*/
@Data
@TableName("t_category")
public class CategoryEntity {
@TableId(type = IdType.AUTO)
private Long categoryId;
/**
* 类目名称
*/
private String categoryName;
/**
* 类目 类型
*
* @see CategoryTypeEnum
*/
private Integer categoryType;
/**
* 父级类目id
*/
private Long parentId;
/**
* 是否禁用
*/
private Boolean disabledFlag;
/**
* 排序
*/
private Integer sort;
/**
* 删除状态
*/
private Boolean deletedFlag;
/**
* 备注
*/
private String remark;
private LocalDateTime updateTime;
private LocalDateTime createTime;
}

View File

@ -0,0 +1,26 @@
package net.lab1024.smartadmin.service.module.business.category.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 类目 基础属性 DTO
*
* @author 胡克
* @date 2021/1/20 16:17
*/
@Data
public class CategorySimpleDTO {
@ApiModelProperty("类目id")
private Long categoryId;
@ApiModelProperty("类目名称")
private String categoryName;
@ApiModelProperty("类目层级全称")
private String categoryFullName;
@ApiModelProperty("父级id")
private Long parentId;
}

View File

@ -0,0 +1,24 @@
package net.lab1024.smartadmin.service.module.business.category.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import net.lab1024.smartadmin.service.common.swagger.ApiModelPropertyEnum;
import net.lab1024.smartadmin.service.common.validator.CheckEnum;
import net.lab1024.smartadmin.service.module.business.category.constant.CategoryTypeEnum;
/**
* 类目 层级树查询 DTO
*
* @author 胡克
* @date 2021/1/20 16:17
*/
@Data
public class CategoryTreeQueryDTO {
@ApiModelPropertyEnum(desc = "分类类型|可选", value = CategoryTypeEnum.class)
@CheckEnum(enumClazz = CategoryTypeEnum.class, message = "分类类型错误")
private Integer categoryType;
@ApiModelProperty("父级类目id|可选")
private Long parentId;
}

View File

@ -0,0 +1,25 @@
package net.lab1024.smartadmin.service.module.business.category.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
* 类目 层级树 vo
*
* @author listen
* @date 2021/01/21 17:03
*/
@Data
public class CategoryTreeVO extends CategorySimpleDTO {
@ApiModelProperty("类目id")
private Long value;
@ApiModelProperty("类目名称")
private String label;
@ApiModelProperty("子类")
private List<CategoryTreeVO> children;
}

View File

@ -0,0 +1,20 @@
package net.lab1024.smartadmin.service.module.business.category.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* 类目 更新 DTO
*
* @author 胡克
* @date 2021/1/20 16:24
*/
@Data
public class CategoryUpdateDTO extends CategoryBaseDTO {
@ApiModelProperty("类目id")
@NotNull(message = "类目id不能为空")
private Long categoryId;
}

View File

@ -0,0 +1,23 @@
package net.lab1024.smartadmin.service.module.business.category.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 类目 VO
*
* @author 胡克
* @date 2021/1/20 16:24
*/
@Data
public class CategoryVO extends CategoryBaseDTO {
@ApiModelProperty("类目id")
private Long categoryId;
private LocalDateTime updateTime;
private LocalDateTime createTime;
}

View File

@ -0,0 +1,5 @@
/**
* 类目 业务
* 包含 商品 等类型
*/
package net.lab1024.smartadmin.service.module.business.category;

View File

@ -0,0 +1,64 @@
package net.lab1024.smartadmin.service.module.business.goods;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import net.lab1024.smartadmin.service.common.constant.SwaggerTagConst;
import net.lab1024.smartadmin.service.common.controller.AdminBaseController;
import net.lab1024.smartadmin.service.common.domain.PageResultDTO;
import net.lab1024.smartadmin.service.common.domain.ResponseDTO;
import net.lab1024.smartadmin.service.module.business.goods.domain.*;
import net.lab1024.smartadmin.service.module.system.login.domain.EmployeeLoginInfoDTO;
import net.lab1024.smartadmin.service.util.SmartEmployeeTokenUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
/**
* 商品业务 路由
*
* @author 胡克
* @date 2021/08/21 19:10
*/
@Api(tags = SwaggerTagConst.Admin.MANAGER_GOODS)
@RestController
public class GoodsController extends AdminBaseController {
@Autowired
private GoodsService goodsService;
@ApiOperation("添加商品 by listen")
@PostMapping("/goods/add")
public ResponseDTO<String> add(@RequestBody @Valid GoodsAddDTO addDTO) {
EmployeeLoginInfoDTO employee = SmartEmployeeTokenUtil.getRequestEmployee();
addDTO.setUpdateId(employee.getEmployeeId());
addDTO.setUpdateName(employee.getActualName());
return goodsService.add(addDTO);
}
@ApiOperation("更新商品 by listen")
@PostMapping("/goods/update")
public ResponseDTO<String> update(@RequestBody @Valid GoodsUpdateDTO updateDTO) {
EmployeeLoginInfoDTO employee = SmartEmployeeTokenUtil.getRequestEmployee();
updateDTO.setUpdateId(employee.getEmployeeId());
updateDTO.setUpdateName(employee.getActualName());
return goodsService.update(updateDTO);
}
@ApiOperation("删除 by listen")
@PostMapping("/goods/del")
public ResponseDTO<String> del(@RequestBody @Valid GoodsDelDTO delDTO) {
EmployeeLoginInfoDTO employee = SmartEmployeeTokenUtil.getRequestEmployee();
delDTO.setUpdateId(employee.getEmployeeId());
delDTO.setUpdateName(employee.getActualName());
return goodsService.del(delDTO);
}
@ApiOperation("分页查询 by listen")
@PostMapping("/goods/query")
public ResponseDTO<PageResultDTO<GoodsAdminVO>> query(@RequestBody @Valid GoodsQueryDTO queryDTO) {
return goodsService.query(queryDTO);
}
}

View File

@ -0,0 +1,39 @@
package net.lab1024.smartadmin.service.module.business.goods;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.lab1024.smartadmin.service.module.business.goods.domain.GoodsAdminVO;
import net.lab1024.smartadmin.service.module.business.goods.domain.GoodsBO;
import net.lab1024.smartadmin.service.module.business.goods.domain.GoodsEntity;
import net.lab1024.smartadmin.service.module.business.goods.domain.GoodsQueryDTO;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 商品 dao
*
* @author 胡克
* @date 2021/8/6 15:26
*/
@Component
public interface GoodsDao extends BaseMapper<GoodsEntity> {
/**
* 查询1个商品 具体条件看sql
*
* @param goodsBO
* @return
*/
GoodsEntity selectOne(GoodsBO goodsBO);
/**
* 分页 查询商品
*
* @param page
* @param queryDTO
* @return
*/
List<GoodsAdminVO> query(Page page, @Param("query") GoodsQueryDTO queryDTO);
}

View File

@ -0,0 +1,16 @@
package net.lab1024.smartadmin.service.module.business.goods;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.lab1024.smartadmin.service.module.business.goods.domain.GoodsEntity;
import org.springframework.stereotype.Service;
/**
* 商品 manager
*
* @author 胡克
* @date 2021/8/6 15:26
*/
@Service
public class GoodsManager extends ServiceImpl<GoodsDao, GoodsEntity> {
}

View File

@ -0,0 +1,146 @@
package net.lab1024.smartadmin.service.module.business.goods;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.lab1024.smartadmin.service.common.codeconst.ResponseCodeConst;
import net.lab1024.smartadmin.service.common.domain.PageResultDTO;
import net.lab1024.smartadmin.service.common.domain.ResponseDTO;
import net.lab1024.smartadmin.service.module.business.category.CategoryQueryService;
import net.lab1024.smartadmin.service.module.business.category.constant.CategoryTypeEnum;
import net.lab1024.smartadmin.service.module.business.category.domain.CategoryEntity;
import net.lab1024.smartadmin.service.module.business.goods.domain.*;
import net.lab1024.smartadmin.service.util.SmartBeanUtil;
import net.lab1024.smartadmin.service.util.SmartPageUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* 商品 业务
*
* @author 胡克
* @date 2021/8/6 15:27
*/
@Service
public class GoodsService {
@Autowired
private GoodsDao goodsDao;
@Autowired
private GoodsManager goodsManager;
@Autowired
private CategoryQueryService categoryQueryService;
/**
* 添加商品
*
* @param addDTO
* @return
*/
public ResponseDTO<String> add(GoodsAddDTO addDTO) {
// 商品校验
ResponseDTO<String> res = this.checkGoods(addDTO, null);
if (!res.isSuccess()) {
return res;
}
GoodsEntity goodsEntity = SmartBeanUtil.copy(addDTO, GoodsEntity.class);
goodsDao.insert(goodsEntity);
return ResponseDTO.succ();
}
/**
* 更新商品
*
* @param updateDTO
* @return
*/
public ResponseDTO<String> update(GoodsUpdateDTO updateDTO) {
// 商品校验
ResponseDTO<String> res = this.checkGoods(updateDTO, updateDTO.getGoodsId());
if (!res.isSuccess()) {
return res;
}
GoodsEntity goodsEntity = SmartBeanUtil.copy(updateDTO, GoodsEntity.class);
goodsDao.updateById(goodsEntity);
return ResponseDTO.succ();
}
/**
* 添加/更新 商品校验
*
* @param addDTO
* @param goodsId 不为空 代表更新商品
* @return
*/
private ResponseDTO<String> checkGoods(GoodsAddDTO addDTO, Long goodsId) {
// 校验商品名称重复
Long categoryId = addDTO.getCategoryId();
GoodsBO goodsBO = new GoodsBO();
goodsBO.setGoodsName(addDTO.getGoodsName());
goodsBO.setGoodsType(addDTO.getGoodsType());
goodsBO.setCategoryId(categoryId);
goodsBO.setDeletedFlag(false);
GoodsEntity goodsEntity = goodsDao.selectOne(goodsBO);
if (null != goodsEntity) {
if (null == goodsId || !Objects.equals(goodsEntity.getGoodsId(), goodsId)) {
return ResponseDTO.wrapMsg(ResponseCodeConst.ALREADY_EXIST, "商品名称不能重复~");
}
}
// 校验类目id
Optional<CategoryEntity> optional = categoryQueryService.queryCategory(categoryId);
if (!optional.isPresent() || !CategoryTypeEnum.GOODS.equalsValue(optional.get().getCategoryType())) {
return ResponseDTO.wrapMsg(ResponseCodeConst.NOT_EXISTS, "商品类目不存在~");
}
return ResponseDTO.succ();
}
/**
* 批量删除
*
* @param delDTO
* @return
*/
public ResponseDTO<String> del(GoodsDelDTO delDTO) {
// 批量更新删除状态
List<GoodsEntity> goodsList = delDTO.getGoodsIdList().stream().map(id -> {
GoodsEntity goodsEntity = new GoodsEntity();
goodsEntity.setGoodsId(id);
goodsEntity.setDeletedFlag(true);
return goodsEntity;
}).collect(Collectors.toList());
goodsManager.updateBatchById(goodsList);
return ResponseDTO.succ();
}
/**
* 分页查询
*
* @param queryDTO
* @return
*/
public ResponseDTO<PageResultDTO<GoodsAdminVO>> query(GoodsQueryDTO queryDTO) {
queryDTO.setDeletedFlag(false);
Page page = SmartPageUtil.convert2PageQuery(queryDTO);
List<GoodsAdminVO> list = goodsDao.query(page, queryDTO);
PageResultDTO<GoodsAdminVO> pageResult = SmartPageUtil.convert2PageResult(page, list);
if (pageResult.getEmptyFlag()) {
return ResponseDTO.succData(pageResult);
}
// 查询分类名称
List<Long> categoryIdList = list.stream().map(GoodsAdminVO::getCategoryId).distinct().collect(Collectors.toList());
Map<Long, CategoryEntity> categoryMap = categoryQueryService.queryCategoryList(categoryIdList);
list.forEach(e -> e.setCategoryName(categoryMap.get(e.getCategoryId()).getCategoryName()));
return ResponseDTO.succData(pageResult);
}
}

View File

@ -0,0 +1,54 @@
package net.lab1024.smartadmin.service.module.business.goods.constant;
import net.lab1024.smartadmin.service.common.constant.BaseEnum;
/**
* 商品类型 枚举
*
* @author listen
* @date 2021/08/05 15:26
*/
public enum GoodsTypeEnum implements BaseEnum {
/**
* 1 图书
*/
BOOK(1, "图书"),
/**
* 2 课程
*/
COURSE(2, "课程"),
;
private final Integer type;
private final String desc;
GoodsTypeEnum(Integer type, String desc) {
this.type = type;
this.desc = desc;
}
/**
* 获取枚举类的值
*
* @return Integer
*/
@Override
public Integer getValue() {
return type;
}
/**
* 获取枚举类的说明
*
* @return String
*/
@Override
public String getDesc() {
return desc;
}
}

View File

@ -0,0 +1,20 @@
package net.lab1024.smartadmin.service.module.business.goods.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 商品 添加 DTO
*
* @author 胡克
* @date 2021/8/5 14:42
*/
@Data
public class GoodsAddDTO extends GoodsBaseDTO {
@ApiModelProperty(hidden = true)
private Long updateId;
@ApiModelProperty(hidden = true)
private String updateName;
}

View File

@ -0,0 +1,26 @@
package net.lab1024.smartadmin.service.module.business.goods.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 商品 添加 DTO
*
* @author 胡克
* @date 2021/8/5 14:42
*/
@Data
public class GoodsAdminVO extends GoodsBaseDTO {
@ApiModelProperty("商品id")
private Long goodsId;
@ApiModelProperty("商品分类")
private String categoryName;
private LocalDateTime updateTime;
private LocalDateTime createTime;
}

View File

@ -0,0 +1,36 @@
package net.lab1024.smartadmin.service.module.business.goods.domain;
import lombok.Data;
import net.lab1024.smartadmin.service.module.business.goods.constant.GoodsTypeEnum;
/**
* 商品
*
* @author 胡克
* @date 2021/8/5 14:42
*/
@Data
public class GoodsBO {
/**
* 商品类型
*
* @see GoodsTypeEnum
*/
private Integer goodsType;
/**
* 商品分类
*/
private Long categoryId;
/**
* 商品名称
*/
private String goodsName;
/**
* 删除状态
*/
private Boolean deletedFlag;
}

View File

@ -0,0 +1,60 @@
package net.lab1024.smartadmin.service.module.business.goods.domain;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import net.lab1024.smartadmin.service.common.json.FileKeySerializer;
import net.lab1024.smartadmin.service.common.swagger.ApiModelPropertyEnum;
import net.lab1024.smartadmin.service.common.validator.CheckEnum;
import net.lab1024.smartadmin.service.module.business.goods.constant.GoodsTypeEnum;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
/**
* 商品 基础属性 DTO
*
* @author 胡克
* @date 2021/8/5 14:42
*/
@Data
public class GoodsBaseDTO {
@ApiModelPropertyEnum(desc = "商品类型", value = GoodsTypeEnum.class)
@CheckEnum(enumClazz = GoodsTypeEnum.class, message = "商品类型错误")
private Integer goodsType;
@ApiModelProperty("商品分类")
@NotNull(message = "商品分类不能为空")
private Long categoryId;
@ApiModelProperty("商品名称")
@NotBlank(message = "商品名称不能为空")
@Length(max = 200, message = "商品名称最多50字符")
private String goodsName;
@ApiModelProperty("商品简介")
@Length(max = 200, message = "商品简介最多200字符")
private String goodsIntro;
@ApiModelProperty("商品价格")
@NotNull(message = "商品价格不能为空")
@DecimalMin(value = "0", message = "商品价格最低0")
private BigDecimal price;
@ApiModelProperty("商品封面")
@Length(max = 250, message = "商品封面最多250字符")
@JsonSerialize(using = FileKeySerializer.class)
private String coverPic;
@ApiModelProperty("上架状态")
@NotNull(message = "上架状态不能为空")
private Boolean shelvesFlag;
@ApiModelProperty("备注|可选")
@Length(max = 200, message = "备注最多200字符")
private String remark;
}

View File

@ -0,0 +1,29 @@
package net.lab1024.smartadmin.service.module.business.goods.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;
import java.util.List;
/**
* 商品 删除 DTO
*
* @author 胡克
* @date 2021/8/5 14:42
*/
@Data
public class GoodsDelDTO {
@ApiModelProperty("商品id集合")
@NotEmpty(message = "商品id不能为空")
@Size(max = 99, message = "一次最多删除99")
private List<Long> goodsIdList;
@ApiModelProperty(hidden = true)
private Long updateId;
@ApiModelProperty(hidden = true)
private String updateName;
}

View File

@ -0,0 +1,77 @@
package net.lab1024.smartadmin.service.module.business.goods.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import net.lab1024.smartadmin.service.module.business.goods.constant.GoodsTypeEnum;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 商品 实体类
*
* @author 胡克
* @date 2021/8/5 14:42
*/
@Data
@TableName("t_goods")
public class GoodsEntity {
@TableId(type = IdType.AUTO)
private Long goodsId;
/**
* 商品类型
*
* @see GoodsTypeEnum
*/
private Integer goodsType;
/**
* 第三方商品id
*/
private Long thirdGoodsId;
/**
* 商品分类
*/
private Long categoryId;
/**
* 商品名称
*/
private String goodsName;
/**
* 商品简介
*/
private String goodsIntro;
/**
* 商品价格
*/
private BigDecimal price;
/**
* 商品封面
*/
private String coverPic;
/**
* 上架状态
*/
private Boolean shelvesFlag;
/**
* 删除状态
*/
private Boolean deletedFlag;
private String remark;
private LocalDateTime updateTime;
private LocalDateTime createTime;
}

View File

@ -0,0 +1,36 @@
package net.lab1024.smartadmin.service.module.business.goods.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import net.lab1024.smartadmin.service.common.domain.PageBaseDTO;
import net.lab1024.smartadmin.service.common.swagger.ApiModelPropertyEnum;
import net.lab1024.smartadmin.service.common.validator.CheckEnum;
import net.lab1024.smartadmin.service.module.business.goods.constant.GoodsTypeEnum;
import org.hibernate.validator.constraints.Length;
/**
* 商品 添加 DTO
*
* @author 胡克
* @date 2021/8/5 14:42
*/
@Data
public class GoodsQueryDTO extends PageBaseDTO {
@ApiModelPropertyEnum(desc = "商品类型|可选", value = GoodsTypeEnum.class)
@CheckEnum(enumClazz = GoodsTypeEnum.class, message = "商品类型错误")
private Integer goodsType;
@ApiModelProperty("商品分类")
private Integer categoryId;
@ApiModelProperty("搜索词")
@Length(max = 30, message = "搜索词最多30字符")
private String searchWord;
@ApiModelProperty("上架状态")
private Boolean shelvesFlag;
@ApiModelProperty(hidden = true)
private Boolean deletedFlag;
}

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