mirror of
https://gitee.com/durcframework/SOP.git
synced 2025-08-11 12:56:28 +08:00
5.0
This commit is contained in:
@@ -14,7 +14,7 @@ import java.util.stream.Stream;
|
||||
/**
|
||||
* 生成_sidebar.md文件,直接运行即可
|
||||
*
|
||||
* @author tanghc
|
||||
* @author 六如
|
||||
*/
|
||||
public class SidebarTest {
|
||||
|
||||
|
2
pom.xml
2
pom.xml
@@ -20,7 +20,7 @@
|
||||
<!-- <module>sop-common</module>-->
|
||||
<!-- <module>sop-auth</module>-->
|
||||
<module>sop-example</module>
|
||||
<!-- <module>sop-admin</module>-->
|
||||
<module>sop-admin</module>
|
||||
<!-- <module>sop-gateway</module>-->
|
||||
<module>sop-test</module>
|
||||
<module>sop-sdk</module>
|
||||
|
@@ -3,18 +3,13 @@
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<parent>
|
||||
<groupId>com.gitee.sop</groupId>
|
||||
<artifactId>sop-parent</artifactId>
|
||||
<version>5.0.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.gitee.sop</groupId>
|
||||
<artifactId>sop-admin</artifactId>
|
||||
<version>5.0.0-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>sop-admin-server</module>
|
||||
<module>sop-admin-backend</module>
|
||||
</modules>
|
||||
</project>
|
||||
|
33
sop-admin/sop-admin-backend/.gitignore
vendored
Normal file
33
sop-admin/sop-admin-backend/.gitignore
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
HELP.md
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
294
sop-admin/sop-admin-backend/pom.xml
Normal file
294
sop-admin/sop-admin-backend/pom.xml
Normal file
@@ -0,0 +1,294 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
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.6.15</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
<groupId>com.gitee.sop</groupId>
|
||||
<artifactId>sop-admin-backend</artifactId>
|
||||
<version>5.0.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
||||
<!-- springboot 版本-->
|
||||
<spring-boot.version>2.6.15</spring-boot.version>
|
||||
<!-- spring cloud 版本 -->
|
||||
<spring-cloud.version>2021.0.5</spring-cloud.version>
|
||||
<!-- spring cloud alibaba 版本 -->
|
||||
<!-- 具体版本对应关系见:https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E -->
|
||||
<spring-cloud-alibaba.version>2021.0.5.0</spring-cloud-alibaba.version>
|
||||
<!-- dubbo版本 -->
|
||||
<dubbo.version>3.2.10</dubbo.version>
|
||||
|
||||
<junit.version>4.11</junit.version>
|
||||
<commons-io.version>2.5</commons-io.version>
|
||||
<commons-fileupload.version>1.3.3</commons-fileupload.version>
|
||||
<commons-collection.version>3.2.2</commons-collection.version>
|
||||
<commons-lang3.version>3.8.1</commons-lang3.version>
|
||||
<commons-codec.version>1.11</commons-codec.version>
|
||||
<commons-logging.version>1.2</commons-logging.version>
|
||||
<validation-api.version>2.0.1.Final</validation-api.version>
|
||||
<hibernate-validator.version>6.0.13.Final</hibernate-validator.version>
|
||||
<fastmybatis.version>3.0.10</fastmybatis.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.gitee.sop</groupId>
|
||||
<artifactId>sop-service-support</artifactId>
|
||||
<version>5.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- nacos注册中心 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-nacos-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.gitee.durcframework</groupId>
|
||||
<artifactId>fastmybatis-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-fileupload</groupId>
|
||||
<artifactId>commons-fileupload</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-xml</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.auth0</groupId>
|
||||
<artifactId>java-jwt</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-crypto</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
<!-- test-->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- provided -->
|
||||
|
||||
<!-- 仅在开发中使用 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
|
||||
<version>${dubbo.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<!-- 加了这个就不需要加版本号了 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>${spring-cloud.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
|
||||
<version>${spring-cloud-alibaba.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-bom</artifactId>
|
||||
<version>${dubbo.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.gitee.durcframework</groupId>
|
||||
<artifactId>fastmybatis-spring-boot-starter</artifactId>
|
||||
<version>${fastmybatis.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>3.14.7</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.validation</groupId>
|
||||
<artifactId>validation-api</artifactId>
|
||||
<version>${validation-api.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
<version>${hibernate-validator.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- commons -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${commons-lang3.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-collections</groupId>
|
||||
<artifactId>commons-collections</artifactId>
|
||||
<version>${commons-collection.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>${commons-io.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>${commons-codec.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-fileupload</groupId>
|
||||
<artifactId>commons-fileupload</artifactId>
|
||||
<version>${commons-fileupload.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<version>${commons-logging.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.auth0</groupId>
|
||||
<artifactId>java-jwt</artifactId>
|
||||
<version>3.2.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.1.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>aliyun</id>
|
||||
<name>aliyun</name>
|
||||
<url>https://maven.aliyun.com/repository/public</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<!-- 打包时跳过测试 -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.12.4</version>
|
||||
<configuration>
|
||||
<skipTests>true</skipTests>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.1</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@@ -0,0 +1,15 @@
|
||||
package com.gitee.sop.adminbackend;
|
||||
|
||||
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableDubbo
|
||||
public class SopAdminBackendApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SopAdminBackendApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
package com.gitee.sop.adminbackend.common;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum ConfigKeyEnum {
|
||||
PASSWORD_SALT("admin.password-salt", ""),
|
||||
JWT_TIMEOUT_DAYS("admin.jwt-timeout-days", "365"),
|
||||
JWT_SECRET("admin.jwt.secret", ""),
|
||||
;
|
||||
|
||||
private final String key;
|
||||
|
||||
private final String defaultValue;
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
package com.gitee.sop.adminbackend.common;
|
||||
|
||||
public interface IConfig {
|
||||
|
||||
String getConfig(String key);
|
||||
|
||||
String getConfig(String key, String defaultValue);
|
||||
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
package com.gitee.sop.adminbackend.common;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author thc
|
||||
*/
|
||||
@Data
|
||||
public class Result<T> {
|
||||
|
||||
private static final Result<?> RESULT = new Result<>();
|
||||
|
||||
private String code = "0";
|
||||
private T data;
|
||||
private String msg = "success";
|
||||
|
||||
public static Result<?> ok() {
|
||||
return RESULT;
|
||||
}
|
||||
|
||||
public static <E> Result<E> ok(E obj) {
|
||||
Result<E> result = new Result<>();
|
||||
result.setData(obj);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static <E> Result<E> err(String msg) {
|
||||
Result<E> result = new Result<>();
|
||||
result.setCode("1");
|
||||
result.setMsg(msg);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static <E> Result<E> err(String code, String msg) {
|
||||
Result<E> result = new Result<>();
|
||||
result.setCode(code);
|
||||
result.setMsg(msg);
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean getSuccess() {
|
||||
return Objects.equals("0", code);
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
package com.gitee.sop.adminbackend.common;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public class SpringContext {
|
||||
|
||||
private static ApplicationContext ctx;
|
||||
|
||||
public static <T> T getBean(Class<T> clazz) {
|
||||
return ctx.getBean(clazz);
|
||||
}
|
||||
|
||||
public static Object getBean(String beanName) {
|
||||
return ctx.getBean(beanName);
|
||||
}
|
||||
|
||||
public static void setApplicationContext(ApplicationContext ctx) {
|
||||
SpringContext.ctx = ctx;
|
||||
}
|
||||
|
||||
public static ApplicationContext getApplicationContext() {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
public static void publishEvent(ApplicationEvent event) {
|
||||
ctx.publishEvent(event);
|
||||
}
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
package com.gitee.sop.adminbackend.common;
|
||||
|
||||
/**
|
||||
* 登录用户信息
|
||||
* @author 六如
|
||||
*/
|
||||
public interface User {
|
||||
|
||||
/**
|
||||
* 用户id
|
||||
* @return
|
||||
*/
|
||||
Long getUserId();
|
||||
|
||||
/**
|
||||
* 昵称
|
||||
* @return
|
||||
*/
|
||||
String getNickname();
|
||||
|
||||
Integer getStatus();
|
||||
|
||||
String getToken();
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
package com.gitee.sop.adminbackend.common.exception;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public class BizException extends RuntimeException {
|
||||
public BizException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
package com.gitee.sop.adminbackend.common.exception;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public enum ErrorCode {
|
||||
// 1000: 登录失败
|
||||
LOGIN_FAIL("1000", "login error"),
|
||||
JWT_CREATE("1000", "create token error"),
|
||||
JWT_ERROR("1000", "invalid token"),
|
||||
JWT_EXPIRED("1000", "token expired"),
|
||||
SET_PASSWORD("2000", "set password"),
|
||||
;
|
||||
|
||||
ErrorCode(String code, String msg) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
private final String code;
|
||||
private final String msg;
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
package com.gitee.sop.adminbackend.common.exception;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public class ErrorTokenException extends Exception {
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
package com.gitee.sop.adminbackend.common.exception;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public interface ExceptionCode {
|
||||
ErrorCode getCode();
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
package com.gitee.sop.adminbackend.common.exception;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public class JwtCreateException extends RuntimeException implements ExceptionCode {
|
||||
@Override
|
||||
public ErrorCode getCode() {
|
||||
return ErrorCode.JWT_CREATE;
|
||||
}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package com.gitee.sop.adminbackend.common.exception;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public class JwtErrorException extends Exception {
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "jwt verify error";
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
package com.gitee.sop.adminbackend.common.exception;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public class JwtExpiredException extends Exception {
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "jwt expired";
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
package com.gitee.sop.adminbackend.common.exception;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public class LoginFailureException extends RuntimeException implements ExceptionCode {
|
||||
@Override
|
||||
public ErrorCode getCode() {
|
||||
return ErrorCode.LOGIN_FAIL;
|
||||
}
|
||||
|
||||
public LoginFailureException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public LoginFailureException() {
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
package com.gitee.sop.adminbackend.common.exception;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public class SetPasswordException extends RuntimeException implements ExceptionCode {
|
||||
@Override
|
||||
public ErrorCode getCode() {
|
||||
return ErrorCode.SET_PASSWORD;
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package com.gitee.sop.adminbackend.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "admin")
|
||||
@Data
|
||||
public class AdminConfig {
|
||||
|
||||
private int jwtTimeoutDays;
|
||||
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
package com.gitee.sop.adminbackend.config;
|
||||
|
||||
import com.gitee.sop.adminbackend.common.ConfigKeyEnum;
|
||||
import com.gitee.sop.adminbackend.common.IConfig;
|
||||
import com.gitee.sop.adminbackend.common.SpringContext;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public class Configs {
|
||||
|
||||
/**
|
||||
* 获取配置参数
|
||||
*
|
||||
* @param keyGetter 配置key
|
||||
* @return 返回配参数,没有则返回null
|
||||
*/
|
||||
public static String getValue(ConfigKeyEnum keyGetter) {
|
||||
return getValue(keyGetter, keyGetter.getDefaultValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置参数
|
||||
*
|
||||
* @param keyGetter 配置key
|
||||
* @param defaultValue 默认值
|
||||
* @return 返回配参数,没有则返回默认值
|
||||
*/
|
||||
public static String getValue(ConfigKeyEnum keyGetter, String defaultValue) {
|
||||
return SpringContext.getBean(IConfig.class).getConfig(keyGetter.getKey(), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置参数
|
||||
*
|
||||
* @param keyGetter 配置key
|
||||
* @param defaultValue 默认值
|
||||
* @return 返回配参数,没有则返回默认值
|
||||
*/
|
||||
public static String getValue(ConfigKeyEnum keyGetter, Supplier<String> defaultValue) {
|
||||
return getValue(keyGetter, defaultValue.get());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
package com.gitee.sop.adminbackend.config;
|
||||
|
||||
import com.gitee.sop.adminbackend.common.SpringContext;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Configuration
|
||||
public class SopAdminConfiguration implements ApplicationContextAware {
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
SpringContext.setApplicationContext(applicationContext);
|
||||
}
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
package com.gitee.sop.adminbackend.controller;
|
||||
|
||||
import com.gitee.sop.adminbackend.common.Result;
|
||||
import com.gitee.sop.adminbackend.common.exception.BizException;
|
||||
import com.gitee.sop.adminbackend.common.exception.ErrorCode;
|
||||
import com.gitee.sop.adminbackend.common.exception.ExceptionCode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.support.DefaultMessageSourceResolvable;
|
||||
import org.springframework.validation.ObjectError;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 全局错误处理
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
@Slf4j
|
||||
public class ExceptionHandlerController {
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public Object exceptionHandler(HttpServletRequest request, Exception e) {
|
||||
if (e instanceof ExceptionCode) {
|
||||
ExceptionCode exceptionCode = (ExceptionCode) e;
|
||||
ErrorCode errorCode = exceptionCode.getCode();
|
||||
log.error("报错,code:{}, msg:{}", errorCode.getCode(), errorCode.getMsg(), e);
|
||||
return Result.err(errorCode.getCode(), errorCode.getMsg());
|
||||
}
|
||||
if (e instanceof BizException || e instanceof IllegalArgumentException) {
|
||||
RuntimeException bizException = (RuntimeException) e;
|
||||
return Result.err(bizException.getMessage());
|
||||
}
|
||||
// 处理JSR-303错误
|
||||
if (e instanceof MethodArgumentNotValidException) {
|
||||
MethodArgumentNotValidException exception = (MethodArgumentNotValidException) e;
|
||||
List<ObjectError> allErrors = exception.getBindingResult().getAllErrors();
|
||||
String msg = allErrors.stream()
|
||||
.map(DefaultMessageSourceResolvable::getDefaultMessage)
|
||||
.collect(Collectors.joining(", "));
|
||||
return Result.err(msg);
|
||||
}
|
||||
log.error("未知错误,URI:{},HttpMethod:{}", request.getRequestURI(), request.getMethod(), e);
|
||||
return Result.err("系统错误,请查看日志");
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
package com.gitee.sop.adminbackend.controller.sys;
|
||||
|
||||
import com.gitee.sop.adminbackend.common.Result;
|
||||
import com.gitee.sop.adminbackend.controller.sys.req.LoginParam;
|
||||
import com.gitee.sop.adminbackend.controller.sys.resp.LoginResultVO;
|
||||
import com.gitee.sop.adminbackend.service.sys.login.LoginService;
|
||||
import com.gitee.sop.adminbackend.service.sys.login.dto.LoginDTO;
|
||||
import com.gitee.sop.adminbackend.service.sys.login.dto.LoginUser;
|
||||
import com.gitee.sop.adminbackend.service.sys.login.enums.RegTypeEnum;
|
||||
import com.gitee.sop.adminbackend.util.CopyUtil;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 登录相关接口
|
||||
*
|
||||
* @author 六如
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("sys")
|
||||
public class LoginController {
|
||||
|
||||
@Resource
|
||||
private LoginService loginService;
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
*
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("login")
|
||||
public Result<LoginResultVO> login(@Validated @RequestBody LoginParam param) {
|
||||
LoginDTO loginDTO = new LoginDTO();
|
||||
loginDTO.setUsername(param.getUsername());
|
||||
loginDTO.setPassword(param.getPassword());
|
||||
loginDTO.setRegType(RegTypeEnum.BACKEND);
|
||||
LoginUser loginUser = loginService.login(loginDTO);
|
||||
LoginResultVO loginResult = CopyUtil.copyBean(loginUser, LoginResultVO::new);
|
||||
return Result.ok(loginResult);
|
||||
}
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
package com.gitee.sop.adminbackend.controller.sys;
|
||||
|
||||
import com.gitee.fastmybatis.core.PageInfo;
|
||||
import com.gitee.fastmybatis.core.query.Query;
|
||||
import com.gitee.fastmybatis.core.query.param.PageParam;
|
||||
import com.gitee.sop.adminbackend.common.Result;
|
||||
import com.gitee.sop.adminbackend.dao.entity.SysAdminUser;
|
||||
import com.gitee.sop.adminbackend.service.sys.SysAdminUserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("sys/adminuser")
|
||||
public class SysAdminUserController {
|
||||
|
||||
@Autowired
|
||||
private SysAdminUserService sysAdminUserService;
|
||||
|
||||
/**
|
||||
* 分页查询
|
||||
*
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/page")
|
||||
public Result<PageInfo<SysAdminUser>> page(PageParam param) {
|
||||
Query query = param.toQuery();
|
||||
PageInfo<SysAdminUser> pageInfo = sysAdminUserService.page(query);
|
||||
return Result.ok(pageInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增记录
|
||||
*
|
||||
* @param user
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/save")
|
||||
public Result<Long> save(SysAdminUser user) {
|
||||
sysAdminUserService.save(user);
|
||||
// 返回添加后的主键值
|
||||
return Result.ok(user.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改记录
|
||||
*
|
||||
* @param user 表单数据
|
||||
* @return
|
||||
*/
|
||||
@PutMapping("/update")
|
||||
public Result<Integer> update(SysAdminUser user) {
|
||||
return Result.ok(sysAdminUserService.update(user));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除记录
|
||||
*
|
||||
* @param id 主键id
|
||||
* @return
|
||||
*/
|
||||
@DeleteMapping("/delete")
|
||||
public Result<?> delete(Long id) {
|
||||
return Result.ok(sysAdminUserService.deleteById(id));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
package com.gitee.sop.adminbackend.controller.sys.req;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
@Data
|
||||
public class LoginParam {
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
private String username;
|
||||
|
||||
@NotBlank(message = "密码不能为空")
|
||||
private String password;
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
package com.gitee.sop.adminbackend.controller.sys.resp;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Data
|
||||
public class LoginResultVO {
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String nickname;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 头像
|
||||
*/
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 注册类型
|
||||
*/
|
||||
private Integer regType;
|
||||
|
||||
/**
|
||||
* 状态,1:启用,0:禁用
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
private Set<String> roles;
|
||||
|
||||
private Set<String> permissions;
|
||||
|
||||
private String accessToken;
|
||||
|
||||
private String refreshToken;
|
||||
|
||||
private String expires;
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
package com.gitee.sop.adminbackend.dao.entity;
|
||||
|
||||
import com.gitee.fastmybatis.annotation.Pk;
|
||||
import com.gitee.fastmybatis.annotation.PkStrategy;
|
||||
import com.gitee.fastmybatis.annotation.Table;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
|
||||
/**
|
||||
* 表名:api_info
|
||||
* 备注:接口信息表
|
||||
*
|
||||
* @author 六如
|
||||
*/
|
||||
@Table(name = "api_info", pk = @Pk(name = "id", strategy = PkStrategy.INCREMENT))
|
||||
@Data
|
||||
public class ApiInfo {
|
||||
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 应用名称
|
||||
*/
|
||||
private String application;
|
||||
|
||||
/**
|
||||
* 接口名称
|
||||
*/
|
||||
private String apiName;
|
||||
|
||||
/**
|
||||
* 版本号
|
||||
*/
|
||||
private String apiVersion;
|
||||
|
||||
/**
|
||||
* 接口class
|
||||
*/
|
||||
private String interfaceClassName;
|
||||
|
||||
/**
|
||||
* 方法名称
|
||||
*/
|
||||
private String methodName;
|
||||
|
||||
/**
|
||||
* 参数信息
|
||||
*/
|
||||
private String paramInfo;
|
||||
|
||||
/**
|
||||
* 接口是否需要授权访问
|
||||
*/
|
||||
private Integer isPermission;
|
||||
|
||||
/**
|
||||
* 是否需要appAuthToken
|
||||
*/
|
||||
private Integer isNeedToken;
|
||||
|
||||
/**
|
||||
* 状态,1-启用,0-禁用
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
private LocalDateTime addTime;
|
||||
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
package com.gitee.sop.adminbackend.dao.entity;
|
||||
|
||||
import com.gitee.fastmybatis.annotation.Pk;
|
||||
import com.gitee.fastmybatis.annotation.PkStrategy;
|
||||
import com.gitee.fastmybatis.annotation.Table;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
|
||||
/**
|
||||
* 表名:isv_info
|
||||
* 备注:isv信息表
|
||||
*
|
||||
* @author 六如
|
||||
*/
|
||||
@Table(name = "isv_info", pk = @Pk(name = "id", strategy = PkStrategy.INCREMENT))
|
||||
@Data
|
||||
public class IsvInfo {
|
||||
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* appKey
|
||||
*/
|
||||
private String appId;
|
||||
|
||||
/**
|
||||
* 1启用,2禁用
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
private LocalDateTime addTime;
|
||||
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
package com.gitee.sop.adminbackend.dao.entity;
|
||||
|
||||
import com.gitee.fastmybatis.annotation.Pk;
|
||||
import com.gitee.fastmybatis.annotation.PkStrategy;
|
||||
import com.gitee.fastmybatis.annotation.Table;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
|
||||
/**
|
||||
* 表名:isv_keys
|
||||
* 备注:ISV秘钥管理
|
||||
*
|
||||
* @author 六如
|
||||
*/
|
||||
@Table(name = "isv_keys", pk = @Pk(name = "id", strategy = PkStrategy.INCREMENT))
|
||||
@Data
|
||||
public class IsvKeys {
|
||||
|
||||
private Long id;
|
||||
|
||||
private String appId;
|
||||
|
||||
/**
|
||||
* 秘钥格式,1:PKCS8(JAVA适用),2:PKCS1(非JAVA适用)
|
||||
*/
|
||||
private Integer keyFormat;
|
||||
|
||||
/**
|
||||
* 开发者生成的公钥
|
||||
*/
|
||||
private String publicKeyIsv;
|
||||
|
||||
/**
|
||||
* 开发者生成的私钥(交给开发者)
|
||||
*/
|
||||
private String privateKeyIsv;
|
||||
|
||||
/**
|
||||
* 平台生成的公钥(交给开发者)
|
||||
*/
|
||||
private String publicKeyPlatform;
|
||||
|
||||
/**
|
||||
* 平台生成的私钥
|
||||
*/
|
||||
private String privateKeyPlatform;
|
||||
|
||||
private LocalDateTime addTime;
|
||||
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
package com.gitee.sop.adminbackend.dao.entity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import com.gitee.fastmybatis.annotation.Pk;
|
||||
import com.gitee.fastmybatis.annotation.PkStrategy;
|
||||
import com.gitee.fastmybatis.annotation.Table;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
/**
|
||||
* 表名:sys_admin_user
|
||||
* 备注:系统用户表
|
||||
*
|
||||
* @author 六如
|
||||
*/
|
||||
@Table(name = "sys_admin_user", pk = @Pk(name = "id", strategy = PkStrategy.INCREMENT))
|
||||
@Data
|
||||
public class SysAdminUser {
|
||||
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String nickname;
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 头像
|
||||
*/
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 状态,1:启用,0:禁用
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 注册类型
|
||||
*/
|
||||
private String regType;
|
||||
|
||||
private LocalDateTime addTime;
|
||||
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
package com.gitee.sop.adminbackend.dao.entity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import com.gitee.fastmybatis.annotation.Pk;
|
||||
import com.gitee.fastmybatis.annotation.PkStrategy;
|
||||
import com.gitee.fastmybatis.annotation.Table;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
/**
|
||||
* 表名:sys_config
|
||||
* 备注:系统配置表
|
||||
*
|
||||
* @author 六如
|
||||
*/
|
||||
@Table(name = "sys_config", pk = @Pk(name = "id", strategy = PkStrategy.INCREMENT))
|
||||
@Data
|
||||
public class SysConfig {
|
||||
|
||||
private Long id;
|
||||
|
||||
private String configKey;
|
||||
|
||||
private String configValue;
|
||||
|
||||
private String remark;
|
||||
|
||||
@com.gitee.fastmybatis.annotation.Column(logicDelete = true)
|
||||
private Integer isDeleted;
|
||||
|
||||
private LocalDateTime addTime;
|
||||
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package com.gitee.sop.adminbackend.dao.mapper;
|
||||
|
||||
|
||||
import com.gitee.fastmybatis.core.mapper.BaseMapper;
|
||||
import com.gitee.sop.adminbackend.dao.entity.ApiInfo;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public interface ApiInfoMapper extends BaseMapper<ApiInfo> {
|
||||
|
||||
default ApiInfo getByNameVersion(String apiName, String apiVersion) {
|
||||
return this.query()
|
||||
.eq(ApiInfo::getApiName, apiName)
|
||||
.eq(ApiInfo::getApiVersion, apiVersion)
|
||||
.get();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
package com.gitee.sop.adminbackend.dao.mapper;
|
||||
|
||||
import com.gitee.fastmybatis.core.mapper.BaseMapper;
|
||||
import com.gitee.sop.adminbackend.dao.entity.IsvInfo;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public interface IsvInfoMapper extends BaseMapper<IsvInfo> {
|
||||
|
||||
default IsvInfo getByAppId(String appId) {
|
||||
return this.get(IsvInfo::getAppId, appId);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
package com.gitee.sop.adminbackend.dao.mapper;
|
||||
|
||||
import com.gitee.fastmybatis.core.mapper.BaseMapper;
|
||||
import com.gitee.sop.adminbackend.dao.entity.IsvKeys;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public interface IsvKeysMapper extends BaseMapper<IsvKeys> {
|
||||
|
||||
default IsvKeys getByAppId(String appId) {
|
||||
return this.get(IsvKeys::getAppId, appId);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
package com.gitee.sop.adminbackend.dao.mapper;
|
||||
|
||||
import com.gitee.fastmybatis.core.mapper.BaseMapper;
|
||||
import com.gitee.sop.adminbackend.dao.entity.SysAdminUser;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public interface SysAdminUserMapper extends BaseMapper<SysAdminUser> {
|
||||
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
package com.gitee.sop.adminbackend.dao.mapper;
|
||||
|
||||
import com.gitee.fastmybatis.core.mapper.BaseMapper;
|
||||
import com.gitee.sop.adminbackend.dao.entity.SysConfig;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public interface SysConfigMapper extends BaseMapper<SysConfig> {
|
||||
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
package com.gitee.sop.adminbackend.dao.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Mapper
|
||||
public interface UpgradeMapper {
|
||||
|
||||
void runSql(@Param("sql") String sql);
|
||||
|
||||
/**
|
||||
* 查看MYSQL表字段信息
|
||||
* @param tableName 表名
|
||||
* @return 返回字段信息
|
||||
*/
|
||||
List<Map<String, Object>> listColumnInfo(@Param("tableName") String tableName);
|
||||
|
||||
List<String> listTableName();
|
||||
|
||||
List<Map<String, Object>> listTableIndex(@Param("tableName") String tableName);
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
package com.gitee.sop.adminbackend.service.sys;
|
||||
|
||||
import com.gitee.fastmybatis.core.support.LambdaService;
|
||||
import com.gitee.sop.adminbackend.common.ConfigKeyEnum;
|
||||
import com.gitee.sop.adminbackend.config.AdminConfig;
|
||||
import com.gitee.sop.adminbackend.config.Configs;
|
||||
import com.gitee.sop.adminbackend.dao.entity.SysAdminUser;
|
||||
import com.gitee.sop.adminbackend.dao.mapper.SysAdminUserMapper;
|
||||
import com.gitee.sop.adminbackend.util.GenerateUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Service
|
||||
public class SysAdminUserService implements LambdaService<SysAdminUser, SysAdminUserMapper> {
|
||||
|
||||
|
||||
public SysAdminUser getByUsername(String username) {
|
||||
return this.get(SysAdminUser::getUsername, username);
|
||||
}
|
||||
|
||||
public String getDbPassword(String username, String password) {
|
||||
return getDbPassword(username, password, getPasswordSalt());
|
||||
}
|
||||
|
||||
public String getDbPassword(String username) {
|
||||
return this.query().eq(SysAdminUser::getUsername, username).getValue(SysAdminUser::getPassword);
|
||||
}
|
||||
|
||||
public String getDbPassword(String username, String password, String salt) {
|
||||
return GenerateUtil.getUserPassword(username, password, salt);
|
||||
}
|
||||
|
||||
private String getPasswordSalt() {
|
||||
return Configs.getValue(ConfigKeyEnum.PASSWORD_SALT);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,102 @@
|
||||
package com.gitee.sop.adminbackend.service.sys;
|
||||
|
||||
import com.gitee.fastmybatis.core.support.BaseLambdaService;
|
||||
import com.gitee.sop.adminbackend.common.IConfig;
|
||||
import com.gitee.sop.adminbackend.dao.entity.SysConfig;
|
||||
import com.gitee.sop.adminbackend.dao.mapper.SysConfigMapper;
|
||||
import com.gitee.sop.adminbackend.service.sys.dto.SystemConfigDTO;
|
||||
import com.gitee.sop.adminbackend.util.CopyUtil;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Service
|
||||
public class SysConfigService extends BaseLambdaService<SysConfig, SysConfigMapper> implements IConfig {
|
||||
|
||||
@Resource
|
||||
private Environment environment;
|
||||
|
||||
// key: configKey, value: configValue
|
||||
private final LoadingCache<String, Optional<String>> configCache = CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(15, TimeUnit.MINUTES)
|
||||
.build(new CacheLoader<String, Optional<String>>() {
|
||||
@Override
|
||||
public Optional<String> load(String key) throws Exception {
|
||||
return Optional.ofNullable(getConfigValue(key, null));
|
||||
}
|
||||
});
|
||||
|
||||
public String getRawValue(String key) {
|
||||
return this.query()
|
||||
.eq(SysConfig::getConfigKey, key)
|
||||
.getValue(SysConfig::getConfigValue);
|
||||
}
|
||||
|
||||
public void setConfig(String key, String value) {
|
||||
setConfig(key, value, "");
|
||||
}
|
||||
|
||||
public void setConfig(String key, String value, String remark) {
|
||||
SystemConfigDTO systemConfigDTO = new SystemConfigDTO();
|
||||
systemConfigDTO.setConfigKey(key);
|
||||
systemConfigDTO.setConfigValue(value);
|
||||
systemConfigDTO.setRemark(remark);
|
||||
setConfig(systemConfigDTO);
|
||||
}
|
||||
|
||||
public void setConfig(SystemConfigDTO systemConfigDTO) {
|
||||
Objects.requireNonNull(systemConfigDTO.getConfigKey(), "need key");
|
||||
Objects.requireNonNull(systemConfigDTO.getConfigValue(), "need value");
|
||||
SysConfig systemConfig = get(SysConfig::getConfigKey, systemConfigDTO.getConfigKey());
|
||||
if (systemConfig == null) {
|
||||
systemConfig = CopyUtil.copyBean(systemConfigDTO, SysConfig::new);
|
||||
this.save(systemConfig);
|
||||
} else {
|
||||
CopyUtil.copyPropertiesIgnoreNull(systemConfigDTO, systemConfig);
|
||||
this.update(systemConfig);
|
||||
}
|
||||
configCache.invalidate(systemConfigDTO.getConfigKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置信息
|
||||
* <pre>
|
||||
* 优先级:
|
||||
* 数据库
|
||||
* Environment
|
||||
* 默认配置
|
||||
* </pre>
|
||||
*
|
||||
* @param key 配置key
|
||||
* @param defaultValue 没有获取到返回的默认值
|
||||
* @return 返回配置信息,如果没有获取到值,则返回默认值
|
||||
*/
|
||||
public String getConfigValue(String key, String defaultValue) {
|
||||
Objects.requireNonNull(key, "need key");
|
||||
SysConfig systemConfig = get(SysConfig::getConfigKey, key);
|
||||
return Optional.ofNullable(systemConfig)
|
||||
.map(SysConfig::getConfigValue)
|
||||
.orElseGet(() -> environment.getProperty(key, defaultValue));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConfig(String key) {
|
||||
return configCache.getUnchecked(key).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConfig(String key, String defaultValue) {
|
||||
return configCache.getUnchecked(key).orElse(defaultValue);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,67 @@
|
||||
package com.gitee.sop.adminbackend.service.sys;
|
||||
|
||||
import com.gitee.sop.adminbackend.common.ConfigKeyEnum;
|
||||
import com.gitee.sop.adminbackend.dao.entity.SysAdminUser;
|
||||
import com.gitee.sop.adminbackend.dao.mapper.UpgradeMapper;
|
||||
import com.gitee.sop.adminbackend.service.sys.login.enums.RegTypeEnum;
|
||||
import com.gitee.sop.adminbackend.util.PasswordUtil;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.security.crypto.bcrypt.BCrypt;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Service
|
||||
public class UpgradeService {
|
||||
|
||||
@Resource
|
||||
private SysConfigService sysConfigService;
|
||||
|
||||
@Resource
|
||||
private SysAdminUserService sysAdminUserService;
|
||||
|
||||
@Resource
|
||||
private UpgradeMapper upgradeMapper;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
this.initJwtSecret();
|
||||
this.insertAdmin();
|
||||
}
|
||||
|
||||
private void initJwtSecret() {
|
||||
String configKey = ConfigKeyEnum.JWT_SECRET.getKey();
|
||||
String value = sysConfigService.getRawValue(configKey);
|
||||
if (StringUtils.isBlank(value)) {
|
||||
value = PasswordUtil.getRandomSimplePassword(30);
|
||||
sysConfigService.setConfig(configKey, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void insertAdmin() {
|
||||
SysAdminUser userInfo = sysAdminUserService.getByUsername("admin");
|
||||
if (userInfo != null) {
|
||||
return;
|
||||
}
|
||||
String username = "admin";
|
||||
String tpl = "INSERT INTO `sys_admin_user` ( `username`, `password`, `nickname`, `reg_type`) VALUES \n" +
|
||||
"\t('%s','%s','%s','%s');";
|
||||
// 初始密码
|
||||
String defPassword = "123456";
|
||||
defPassword = DigestUtils.sha256Hex(defPassword);
|
||||
String encodedPassword = BCrypt.hashpw(defPassword, BCrypt.gensalt());
|
||||
String sql = String.format(tpl, username, encodedPassword, username, RegTypeEnum.BACKEND.getValue());
|
||||
runSql(sql);
|
||||
}
|
||||
|
||||
protected void runSql(String sql) {
|
||||
upgradeMapper.runSql(sql);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
package com.gitee.sop.adminbackend.service.sys.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Data
|
||||
public class SystemConfigDTO {
|
||||
/** 数据库字段:config_key */
|
||||
private String configKey;
|
||||
|
||||
/** 数据库字段:config_value */
|
||||
private String configValue;
|
||||
|
||||
/** 数据库字段:remark */
|
||||
private String remark;
|
||||
}
|
@@ -0,0 +1,153 @@
|
||||
package com.gitee.sop.adminbackend.service.sys.login;
|
||||
|
||||
import com.alibaba.nacos.shaded.com.google.common.collect.Sets;
|
||||
import com.gitee.fastmybatis.core.query.Query;
|
||||
import com.gitee.sop.adminbackend.common.ConfigKeyEnum;
|
||||
import com.gitee.sop.adminbackend.common.exception.BizException;
|
||||
import com.gitee.sop.adminbackend.config.Configs;
|
||||
import com.gitee.sop.adminbackend.dao.entity.SysAdminUser;
|
||||
import com.gitee.sop.adminbackend.service.sys.SysAdminUserService;
|
||||
import com.gitee.sop.adminbackend.service.sys.login.dto.LoginDTO;
|
||||
import com.gitee.sop.adminbackend.service.sys.login.dto.LoginForm;
|
||||
import com.gitee.sop.adminbackend.service.sys.login.dto.LoginResult;
|
||||
import com.gitee.sop.adminbackend.service.sys.login.dto.LoginUser;
|
||||
import com.gitee.sop.adminbackend.service.sys.login.enums.AdminUserStatusEnum;
|
||||
import com.gitee.sop.adminbackend.service.sys.login.enums.RegTypeEnum;
|
||||
import com.gitee.sop.adminbackend.util.CopyUtil;
|
||||
import com.gitee.sop.adminbackend.util.GenerateUtil;
|
||||
import com.gitee.sop.adminbackend.util.JwtUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
import org.springframework.security.crypto.bcrypt.BCrypt;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class LoginService {
|
||||
|
||||
@Resource
|
||||
private SysAdminUserService sysAdminUserService;
|
||||
|
||||
@Resource
|
||||
private UserCacheManager userCacheManager;
|
||||
|
||||
|
||||
public LoginUser login(LoginDTO loginDTO) {
|
||||
String username = loginDTO.getUsername();
|
||||
String password = loginDTO.getPassword();
|
||||
RegTypeEnum regType = loginDTO.getRegType();
|
||||
SysAdminUser userInfo;
|
||||
switch (regType) {
|
||||
case FORM:
|
||||
throw new UnsupportedOperationException("第三方登录暂未支持");
|
||||
case LDAP:
|
||||
// LDAP登录
|
||||
throw new UnsupportedOperationException("LDAP登录登录暂未支持");
|
||||
default: {
|
||||
// 默认注册账号登录
|
||||
userInfo = this.doDatabaseLogin(username, password);
|
||||
}
|
||||
}
|
||||
LoginUser loginUser = buildLoginUser(userInfo);
|
||||
// 保存到缓存
|
||||
userCacheManager.saveUser(loginUser);
|
||||
return loginUser;
|
||||
}
|
||||
|
||||
private LoginUser buildLoginUser(SysAdminUser userInfo) {
|
||||
if (AdminUserStatusEnum.of(userInfo.getStatus()) == AdminUserStatusEnum.DISABLED) {
|
||||
throw new BizException("账号已禁用,请联系管理员");
|
||||
}
|
||||
// 登录成功
|
||||
LoginUser loginUser = CopyUtil.copyBean(userInfo, LoginUser::new);
|
||||
// 创建token
|
||||
String token = this.createToken(userInfo.getId());
|
||||
loginUser.setAccessToken(token);
|
||||
if ("admin".equals(userInfo.getUsername())) {
|
||||
// ROLE
|
||||
loginUser.setRoles(Sets.newHashSet("admin"));
|
||||
// *:*:* 表示所有权限
|
||||
loginUser.setPermissions(Sets.newHashSet("*:*:*"));
|
||||
} else {
|
||||
// TODO:其它角色权限
|
||||
loginUser.setRoles(Sets.newHashSet());
|
||||
loginUser.setPermissions(Sets.newHashSet());
|
||||
}
|
||||
// 设置token过期时间
|
||||
String value = Configs.getValue(ConfigKeyEnum.JWT_TIMEOUT_DAYS);
|
||||
LocalDateTime expireDate = LocalDateTime.now().plusDays(NumberUtils.toInt(value));
|
||||
loginUser.setExpires(expireDate.format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss")));
|
||||
return loginUser;
|
||||
}
|
||||
|
||||
private String createToken(long userId) {
|
||||
Map<String, String> data = new HashMap<>(4);
|
||||
data.put("id", String.valueOf(userId));
|
||||
String value = Configs.getValue(ConfigKeyEnum.JWT_TIMEOUT_DAYS);
|
||||
return JwtUtil.createJwt(data, NumberUtils.toInt(value), getJwtSecret());
|
||||
}
|
||||
|
||||
public static String getJwtSecret() {
|
||||
return Configs.getValue(ConfigKeyEnum.JWT_SECRET);
|
||||
}
|
||||
|
||||
private SysAdminUser doThirdPartyLogin(ThirdPartyLoginManager thirdPartyLoginManager, String username, String password) {
|
||||
LoginForm loginForm = new LoginForm();
|
||||
loginForm.setUsername(username);
|
||||
loginForm.setPassword(password);
|
||||
LoginResult loginResult;
|
||||
try {
|
||||
loginResult = thirdPartyLoginManager.login(loginForm);
|
||||
} catch (Exception e) {
|
||||
log.error("第三方登录失败", e);
|
||||
throw new BizException(e.getMessage());
|
||||
}
|
||||
|
||||
SysAdminUser userInfo = sysAdminUserService.getByUsername(username);
|
||||
|
||||
// 用户第一次登录则插入到user_info表
|
||||
if (userInfo == null) {
|
||||
userInfo = new SysAdminUser();
|
||||
userInfo.setUsername(username);
|
||||
userInfo.setPassword(GenerateUtil.getUUID());
|
||||
userInfo.setNickname(loginResult.getNickname());
|
||||
userInfo.setAvatar("");
|
||||
userInfo.setStatus(AdminUserStatusEnum.ENABLE.getStatus());
|
||||
userInfo.setRegType(loginResult.getRegTypeEnum().getValue());
|
||||
userInfo.setEmail(loginResult.getEmail());
|
||||
sysAdminUserService.save(userInfo);
|
||||
} else {
|
||||
String email = loginResult.getEmail();
|
||||
// 如果更改了邮箱
|
||||
if (StringUtils.hasText(email) && !Objects.equals(email, userInfo.getEmail())) {
|
||||
userInfo.setEmail(email);
|
||||
sysAdminUserService.update(userInfo);
|
||||
}
|
||||
}
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
private SysAdminUser doDatabaseLogin(String username, String password) {
|
||||
SysAdminUser sysAdminUser = sysAdminUserService.getByUsername(username);
|
||||
Assert.notNull(sysAdminUser, () -> "用户名密码不正确");
|
||||
String encodedPasswordDb = sysAdminUser.getPassword();
|
||||
// 校验
|
||||
boolean flag = BCrypt.checkpw(password, encodedPasswordDb);
|
||||
Assert.isTrue(flag, () -> "用户名密码不正确");
|
||||
return sysAdminUser;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
package com.gitee.sop.adminbackend.service.sys.login;
|
||||
|
||||
/**
|
||||
* @author thc
|
||||
*/
|
||||
public class NotNullStringBuilder {
|
||||
|
||||
private final StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
public NotNullStringBuilder append(Number o) {
|
||||
stringBuilder.append(formatValue(o));
|
||||
return this;
|
||||
}
|
||||
|
||||
public NotNullStringBuilder append(String o) {
|
||||
stringBuilder.append(formatValue(o));
|
||||
return this;
|
||||
}
|
||||
|
||||
public NotNullStringBuilder append(String o, String defaultValue) {
|
||||
if (o == null || o.length() == 0) {
|
||||
o = defaultValue;
|
||||
}
|
||||
stringBuilder.append(formatValue(o));
|
||||
return this;
|
||||
}
|
||||
|
||||
private static String formatValue(Number o) {
|
||||
return o == null ? "0" : String.valueOf(o);
|
||||
}
|
||||
|
||||
private static String formatValue(String o) {
|
||||
return o == null ? "" : o;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package com.gitee.sop.adminbackend.service.sys.login;
|
||||
|
||||
import com.gitee.sop.adminbackend.service.sys.login.dto.LoginForm;
|
||||
import com.gitee.sop.adminbackend.service.sys.login.dto.LoginResult;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public interface ThirdPartyLoginManager {
|
||||
|
||||
/**
|
||||
* 第三方登录
|
||||
* @param loginForm 登录表单
|
||||
* @return 返回登录结果
|
||||
*/
|
||||
LoginResult login(LoginForm loginForm) throws Exception;
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
package com.gitee.sop.adminbackend.service.sys.login;
|
||||
|
||||
import com.gitee.sop.adminbackend.common.User;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public interface UserCacheManager {
|
||||
/**
|
||||
* 返回用户信息
|
||||
* @param userId 用户id
|
||||
* @return 查不到返回null
|
||||
*/
|
||||
User getUser(long userId);
|
||||
|
||||
/**
|
||||
* 保存用户
|
||||
* @param user 用户
|
||||
*/
|
||||
void saveUser(User user);
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
package com.gitee.sop.adminbackend.service.sys.login.dto;
|
||||
|
||||
import com.gitee.sop.adminbackend.service.sys.login.enums.RegTypeEnum;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class LoginDTO {
|
||||
private String username;
|
||||
private String password;
|
||||
private RegTypeEnum regType = RegTypeEnum.BACKEND;
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package com.gitee.sop.adminbackend.service.sys.login.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Data
|
||||
public class LoginForm {
|
||||
private String username;
|
||||
private String password;
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
package com.gitee.sop.adminbackend.service.sys.login.dto;
|
||||
|
||||
import com.gitee.sop.adminbackend.service.sys.login.enums.RegTypeEnum;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Data
|
||||
public class LoginResult {
|
||||
|
||||
private String username;
|
||||
|
||||
private String nickname;
|
||||
|
||||
private String email;
|
||||
|
||||
private RegTypeEnum regTypeEnum = RegTypeEnum.FORM;
|
||||
|
||||
}
|
@@ -0,0 +1,83 @@
|
||||
package com.gitee.sop.adminbackend.service.sys.login.dto;
|
||||
|
||||
import com.gitee.sop.adminbackend.common.User;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Data
|
||||
public class LoginUser implements User {
|
||||
|
||||
/*
|
||||
avatar: "https://avatars.githubusercontent.com/u/44761321",
|
||||
username: "admin",
|
||||
nickname: "小铭",
|
||||
// 一个用户可能有多个角色
|
||||
roles: ["admin"],
|
||||
// 按钮级别权限
|
||||
permissions: ["*:*:*"],
|
||||
accessToken: "eyJhbGciOiJIUzUxMiJ9.admin",
|
||||
refreshToken: "eyJhbGciOiJIUzUxMiJ9.adminRefresh",
|
||||
expires: "2030/10/30 00:00:00"
|
||||
*/
|
||||
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String nickname;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 头像
|
||||
*/
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 注册类型
|
||||
*/
|
||||
private Integer regType;
|
||||
|
||||
/**
|
||||
* 状态,1:启用,0:禁用
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
private Set<String> roles;
|
||||
|
||||
private Set<String> permissions;
|
||||
|
||||
private String accessToken;
|
||||
|
||||
private String refreshToken;
|
||||
|
||||
private String expires;
|
||||
|
||||
@Override
|
||||
public Long getUserId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToken() {
|
||||
return accessToken;
|
||||
}
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
package com.gitee.sop.adminbackend.service.sys.login.enums;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public enum AdminUserStatusEnum {
|
||||
DISABLED((byte)0),
|
||||
ENABLE((byte)1),
|
||||
SET_PWD((byte)2),
|
||||
;
|
||||
|
||||
private final int status;
|
||||
|
||||
public static AdminUserStatusEnum of(Integer value) {
|
||||
for (AdminUserStatusEnum adminUserStatusEnum : AdminUserStatusEnum.values()) {
|
||||
if (Objects.equals(adminUserStatusEnum.status, value)) {
|
||||
return adminUserStatusEnum;
|
||||
}
|
||||
}
|
||||
return DISABLED;
|
||||
}
|
||||
|
||||
AdminUserStatusEnum(byte style) {
|
||||
this.status = style;
|
||||
}
|
||||
|
||||
public int getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
package com.gitee.sop.adminbackend.service.sys.login.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum RegTypeEnum {
|
||||
BACKEND("backend"),
|
||||
FORM("form"),
|
||||
OAUTH("oauth"),
|
||||
LDAP("ldap"),
|
||||
;
|
||||
|
||||
public static RegTypeEnum of(String source) {
|
||||
for (RegTypeEnum value : RegTypeEnum.values()) {
|
||||
if (Objects.equals(source, value.value)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return RegTypeEnum.BACKEND;
|
||||
}
|
||||
|
||||
private final String value;
|
||||
}
|
@@ -0,0 +1,90 @@
|
||||
package com.gitee.sop.adminbackend.service.sys.login.impl;
|
||||
|
||||
import com.gitee.sop.adminbackend.common.User;
|
||||
import com.gitee.sop.adminbackend.dao.entity.SysAdminUser;
|
||||
import com.gitee.sop.adminbackend.service.sys.SysAdminUserService;
|
||||
import com.gitee.sop.adminbackend.service.sys.login.UserCacheManager;
|
||||
import com.gitee.sop.adminbackend.service.sys.login.dto.LoginUser;
|
||||
import com.gitee.sop.adminbackend.service.sys.login.enums.AdminUserStatusEnum;
|
||||
import com.gitee.sop.adminbackend.util.CopyUtil;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class DefaultUserCacheManager implements UserCacheManager, InitializingBean {
|
||||
|
||||
@Autowired
|
||||
private SysAdminUserService sysAdminUserService;
|
||||
|
||||
@Value("${admin.user-cache-timeout-minutes:15}")
|
||||
private int timeoutMinutes;
|
||||
|
||||
// key: userId
|
||||
private LoadingCache<Long, Optional<User>> userCache;
|
||||
|
||||
@Override
|
||||
public User getUser(long userId) {
|
||||
return userCache.getUnchecked(userId).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveUser(User user) {
|
||||
if (user == null) {
|
||||
return;
|
||||
}
|
||||
userCache.put(user.getUserId(), Optional.of(user));
|
||||
}
|
||||
|
||||
private LoadingCache<Long, Optional<User>> buildCache(int timeout) {
|
||||
if (timeout <= 0) {
|
||||
throw new IllegalArgumentException("timeout must be gt 0");
|
||||
}
|
||||
CacheBuilder<Object, Object> cacheBuilder = CacheBuilder.newBuilder();
|
||||
cacheBuilder.expireAfterAccess(timeout, TimeUnit.MINUTES);
|
||||
return cacheBuilder
|
||||
.build(new CacheLoader<Long, Optional<User>>() {
|
||||
@Override
|
||||
public Optional<User> load(Long id) throws Exception {
|
||||
User user = getLoginUser(id);
|
||||
return Optional.ofNullable(user);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登陆用户
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
private User getLoginUser(long id) {
|
||||
SysAdminUser userInfo = sysAdminUserService.getById(id);
|
||||
if (userInfo == null) {
|
||||
log.warn("登录用户不存在,userId:{}", id);
|
||||
return null;
|
||||
}
|
||||
if (userInfo.getStatus() == AdminUserStatusEnum.DISABLED.getStatus()) {
|
||||
log.warn("用户被禁用, userId:{}, username:{}, nickname:{}", userInfo.getId(), userInfo.getUsername(), userInfo.getNickname());
|
||||
return null;
|
||||
}
|
||||
return CopyUtil.copyBean(userInfo, LoginUser::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
userCache = buildCache(timeoutMinutes);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,344 @@
|
||||
package com.gitee.sop.adminbackend.util;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 属性拷贝工具类
|
||||
*
|
||||
* @author 六如
|
||||
*/
|
||||
public class CopyUtil extends BeanUtils {
|
||||
|
||||
/**
|
||||
* 属性拷贝,第一个参数中的属性值拷贝到第二个参数中<br>
|
||||
* 注意:当第一个参数中的属性有null值时,不会拷贝进去
|
||||
*
|
||||
* @param from 源对象
|
||||
* @param to 目标对象
|
||||
* @param ignoreProperties 忽略的字段
|
||||
* @throws BeansException
|
||||
*/
|
||||
public static void copyPropertiesIgnoreNull(Object from, Object to, String... ignoreProperties)
|
||||
throws BeansException {
|
||||
Assert.notNull(from, "Source must not be null");
|
||||
Assert.notNull(to, "Target must not be null");
|
||||
|
||||
Class<?> actualEditable = to.getClass();
|
||||
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
|
||||
List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : Collections.emptyList());
|
||||
|
||||
for (PropertyDescriptor targetPd : targetPds) {
|
||||
if (ignoreList.contains(targetPd.getName())) {
|
||||
continue;
|
||||
}
|
||||
Method writeMethod = targetPd.getWriteMethod();
|
||||
if (writeMethod != null) {
|
||||
PropertyDescriptor sourcePd = getPropertyDescriptor(from.getClass(), targetPd.getName());
|
||||
if (sourcePd != null) {
|
||||
Method readMethod = sourcePd.getReadMethod();
|
||||
if (readMethod != null &&
|
||||
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
|
||||
try {
|
||||
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
|
||||
readMethod.setAccessible(true);
|
||||
}
|
||||
Object value = readMethod.invoke(from);
|
||||
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
|
||||
writeMethod.setAccessible(true);
|
||||
}
|
||||
// 这里判断value是否为空 当然这里也能进行一些特殊要求的处理
|
||||
// 例如绑定时格式转换等等
|
||||
if (value != null) {
|
||||
writeMethod.invoke(to, value);
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
throw new FatalBeanException(
|
||||
"Could not copy property '" + targetPd.getName() + "' from source to target", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝指定的字段
|
||||
*
|
||||
* @param from 源对象
|
||||
* @param to 目标对象
|
||||
* @param includeFields 指定字段
|
||||
*/
|
||||
public static void copyPropertiesInclude(Object from, Object to, Set<String> includeFields) {
|
||||
Objects.requireNonNull(includeFields, "includeFields can not null");
|
||||
Assert.notNull(from, "Source must not be null");
|
||||
Assert.notNull(to, "Target must not be null");
|
||||
if (includeFields.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Class<?> actualEditable = to.getClass();
|
||||
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
|
||||
|
||||
for (PropertyDescriptor targetPd : targetPds) {
|
||||
if (!includeFields.contains(targetPd.getName())) {
|
||||
continue;
|
||||
}
|
||||
Method writeMethod = targetPd.getWriteMethod();
|
||||
if (writeMethod != null) {
|
||||
PropertyDescriptor sourcePd = getPropertyDescriptor(from.getClass(), targetPd.getName());
|
||||
if (sourcePd != null) {
|
||||
Method readMethod = sourcePd.getReadMethod();
|
||||
if (readMethod != null &&
|
||||
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
|
||||
try {
|
||||
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
|
||||
readMethod.setAccessible(true);
|
||||
}
|
||||
Object value = readMethod.invoke(from);
|
||||
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
|
||||
writeMethod.setAccessible(true);
|
||||
}
|
||||
writeMethod.invoke(to, value);
|
||||
} catch (Throwable ex) {
|
||||
throw new FatalBeanException(
|
||||
"Could not copy property '" + targetPd.getName() + "' from source to target", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝属性
|
||||
*
|
||||
* @param from 被拷贝类
|
||||
* @param to 目标类
|
||||
*/
|
||||
public static void copyProperties(Object from, Object to) {
|
||||
BeanUtils.copyProperties(from, to);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝bean成为一个新类
|
||||
*
|
||||
* @param from 被拷贝类
|
||||
* @param supplier 新的类获取回调
|
||||
* @param <T> 新的类
|
||||
* @return 返回新的类实例,from为null时,返回null
|
||||
*/
|
||||
public static <T> T copyBean(Object from, Supplier<T> supplier) {
|
||||
if (from == null) {
|
||||
return null;
|
||||
}
|
||||
T to = supplier.get();
|
||||
BeanUtils.copyProperties(from, to);
|
||||
return to;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝实例
|
||||
*
|
||||
* @param from 被拷贝类
|
||||
* @param supplier 新的类获取回调
|
||||
* @param after 对新的类最后续处理回调
|
||||
* @param <T> 新的类
|
||||
* @return 返回新的类
|
||||
*/
|
||||
public static <T> T copyBean(Object from, Supplier<T> supplier, Consumer<T> after) {
|
||||
if (from == null) {
|
||||
return null;
|
||||
}
|
||||
T to = supplier.get();
|
||||
BeanUtils.copyProperties(from, to);
|
||||
after.accept(to);
|
||||
return to;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝List,将list中的类转换成新的对象
|
||||
*
|
||||
* @param collection 被拷贝的集合
|
||||
* @param toElement List新元素
|
||||
* @param <T> 新元素类型
|
||||
* @return 返回新的List
|
||||
*/
|
||||
public static <T> List<T> copyList(Collection<?> collection, Supplier<T> toElement) {
|
||||
if (collection == null || collection.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return collection.stream()
|
||||
.map(source -> {
|
||||
T target = toElement.get();
|
||||
BeanUtils.copyProperties(source, target);
|
||||
return target;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static <E, R> List<R> copyList(Collection<E> fromList, Function<E, R> function) {
|
||||
if (fromList == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return fromList.stream()
|
||||
.map(source -> {
|
||||
R target = function.apply(source);
|
||||
BeanUtils.copyProperties(source, target);
|
||||
return target;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝List,并做后续处理
|
||||
*
|
||||
* @param fromList 被拷贝的list
|
||||
* @param toElement 新元素
|
||||
* @param after 对新元素做后续处理
|
||||
* @param <T> 新类型
|
||||
* @return 返回新的List
|
||||
*/
|
||||
public static <T> List<T> copyList(Collection<?> fromList, Supplier<T> toElement, Consumer<T> after) {
|
||||
if (fromList == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return fromList.stream()
|
||||
.map(source -> {
|
||||
T target = toElement.get();
|
||||
BeanUtils.copyProperties(source, target);
|
||||
after.accept(target);
|
||||
return target;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝List,并做后续处理
|
||||
*
|
||||
* @param fromList 被拷贝的list
|
||||
* @param toElement 新元素
|
||||
* @param after 对新元素做后续处理
|
||||
* @param <T> 新类型
|
||||
* @return 返回新的List
|
||||
*/
|
||||
public static <T, F> List<T> copyList(Collection<F> fromList, Supplier<T> toElement, CopyConsumer<F, T> after) {
|
||||
if (fromList == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return fromList.stream()
|
||||
.map(source -> {
|
||||
T target = toElement.get();
|
||||
BeanUtils.copyProperties(source, target);
|
||||
after.apply(source, target);
|
||||
return target;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 深层次拷贝,通过json转换的方式实现
|
||||
*
|
||||
* @param from 待转换的类
|
||||
* @param toClass 目标类class
|
||||
* @param <T> 目标类
|
||||
* @return 返回目标类
|
||||
*/
|
||||
public static <T> T deepCopy(Object from, Class<T> toClass) {
|
||||
String json = JSON.toJSONString(from);
|
||||
return JSON.parseObject(json, toClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 深层次拷贝,通过json转换的方式实现
|
||||
*
|
||||
* @param from 待转换的类
|
||||
* @param toClass 目标类class
|
||||
* @param <T> 目标类
|
||||
* @return 返回目标类
|
||||
*/
|
||||
public static <T> List<T> deepCopyList(Object from, Class<T> toClass) {
|
||||
String json = JSON.toJSONString(from);
|
||||
return JSON.parseArray(json, toClass);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 拷贝map
|
||||
*
|
||||
* @param srcMap 原map
|
||||
* @param valueGetter 值转换
|
||||
* @param <K> Key类型
|
||||
* @param <V> Value类型
|
||||
* @return 返回新map
|
||||
*/
|
||||
public static <K, V> Map<K, V> copyMap(Map<K, ?> srcMap, Supplier<V> valueGetter) {
|
||||
Map<K, V> ret = new LinkedHashMap<>(srcMap.size() * 2);
|
||||
for (Map.Entry<K, ?> entry : srcMap.entrySet()) {
|
||||
V value = copyBean(entry.getValue(), valueGetter);
|
||||
ret.put(entry.getKey(), value);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝map
|
||||
*
|
||||
* @param srcMap 原map
|
||||
* @param function 值转换
|
||||
* @param <K> Key类型
|
||||
* @param <V> Value类型
|
||||
* @return 返回新map
|
||||
*/
|
||||
public static <K, V, V0> Map<K, V> copyMap(Map<K, V0> srcMap, Function<V0, V> function) {
|
||||
Map<K, V> ret = new LinkedHashMap<>(srcMap.size() * 2);
|
||||
for (Map.Entry<K, V0> entry : srcMap.entrySet()) {
|
||||
V value = function.apply(entry.getValue());
|
||||
ret.put(entry.getKey(), value);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝map,value是list
|
||||
*
|
||||
* @param srcMap 原map
|
||||
* @param valueGetter 值转换
|
||||
* @param <K> Key类型
|
||||
* @param <V> Value类型
|
||||
* @return 返回新map
|
||||
*/
|
||||
public static <K, V, V2> Map<K, List<V2>> copyMapList(Map<K, List<V>> srcMap, Function<List<V>, List<V2>> valueGetter) {
|
||||
Map<K, List<V2>> ret = new LinkedHashMap<>(srcMap.size() * 2);
|
||||
for (Map.Entry<K, List<V>> entry : srcMap.entrySet()) {
|
||||
List<V2> value = valueGetter.apply(entry.getValue());
|
||||
ret.put(entry.getKey(), value);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public interface CopyConsumer<F, T> {
|
||||
void apply(F from, T to);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
package com.gitee.sop.adminbackend.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.DigestUtils;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Slf4j
|
||||
public class GenerateUtil {
|
||||
|
||||
public static String getUserPassword(String username, String password, String salt) {
|
||||
return DigestUtils.md5DigestAsHex((username + password + salt).getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public static String getUUID() {
|
||||
return IdGen.uuid();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,171 @@
|
||||
package com.gitee.sop.adminbackend.util;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class IdGen {
|
||||
private static long workId = 0;
|
||||
private static final SnowflakeIdWorker worker = new SnowflakeIdWorker(workId++, 0);
|
||||
|
||||
public static long genId() {
|
||||
return worker.nextId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成唯一id
|
||||
* @return
|
||||
*/
|
||||
public static String nextId() {
|
||||
return String.valueOf(genId());
|
||||
}
|
||||
|
||||
public static String uuid() {
|
||||
return UUID.randomUUID().toString().replace("-", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Twitter_Snowflake<br>
|
||||
* SnowFlake的结构如下(每部分用-分开):<br>
|
||||
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>
|
||||
* 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br>
|
||||
* 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
|
||||
* 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>
|
||||
* 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId<br>
|
||||
* 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号<br>
|
||||
* 加起来刚好64位,为一个Long型。<br>
|
||||
* SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
|
||||
*/
|
||||
public static class SnowflakeIdWorker {
|
||||
|
||||
// ==============================Fields===========================================
|
||||
/** 开始时间截 (2015-01-01) */
|
||||
private final long twepoch = 1420041100000L;
|
||||
|
||||
/** 机器id所占的位数 */
|
||||
private final long workerIdBits = 5L;
|
||||
|
||||
/** 数据标识id所占的位数 */
|
||||
private final long datacenterIdBits = 5L;
|
||||
|
||||
/** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
|
||||
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
|
||||
|
||||
/** 支持的最大数据标识id,结果是31 */
|
||||
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
|
||||
|
||||
/** 序列在id中占的位数 */
|
||||
private final long sequenceBits = 12L;
|
||||
|
||||
/** 机器ID向左移12位 */
|
||||
private final long workerIdShift = sequenceBits;
|
||||
|
||||
/** 数据标识id向左移17位(12+5) */
|
||||
private final long datacenterIdShift = sequenceBits + workerIdBits;
|
||||
|
||||
/** 时间截向左移22位(5+5+12) */
|
||||
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
|
||||
|
||||
/** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */
|
||||
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
|
||||
|
||||
/** 工作机器ID(0~31) */
|
||||
private long workerId;
|
||||
|
||||
/** 数据中心ID(0~31) */
|
||||
private long datacenterId;
|
||||
|
||||
/** 毫秒内序列(0~4095) */
|
||||
private long sequence = 0L;
|
||||
|
||||
/** 上次生成ID的时间截 */
|
||||
private long lastTimestamp = -1L;
|
||||
|
||||
//==============================Constructors=====================================
|
||||
/**
|
||||
* 构造函数
|
||||
* @param workerId 工作ID (0~31)
|
||||
* @param datacenterId 数据中心ID (0~31)
|
||||
*/
|
||||
public SnowflakeIdWorker(long workerId, long datacenterId) {
|
||||
if (workerId > maxWorkerId || workerId < 0) {
|
||||
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
|
||||
}
|
||||
if (datacenterId > maxDatacenterId || datacenterId < 0) {
|
||||
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
|
||||
}
|
||||
this.workerId = workerId;
|
||||
this.datacenterId = datacenterId;
|
||||
}
|
||||
|
||||
// ==============================Methods==========================================
|
||||
/**
|
||||
* 获得下一个ID (该方法是线程安全的)
|
||||
* @return SnowflakeId
|
||||
*/
|
||||
public synchronized long nextId() {
|
||||
long timestamp = timeGen();
|
||||
|
||||
//如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
|
||||
if (timestamp < lastTimestamp) {
|
||||
throw new RuntimeException(
|
||||
String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
|
||||
}
|
||||
|
||||
//如果是同一时间生成的,则进行毫秒内序列
|
||||
if (lastTimestamp == timestamp) {
|
||||
sequence = (sequence + 1) & sequenceMask;
|
||||
//毫秒内序列溢出
|
||||
if (sequence == 0) {
|
||||
//阻塞到下一个毫秒,获得新的时间戳
|
||||
timestamp = tilNextMillis(lastTimestamp);
|
||||
}
|
||||
}
|
||||
//时间戳改变,毫秒内序列重置
|
||||
else {
|
||||
sequence = 0L;
|
||||
}
|
||||
|
||||
//上次生成ID的时间截
|
||||
lastTimestamp = timestamp;
|
||||
|
||||
//移位并通过或运算拼到一起组成64位的ID
|
||||
return ((timestamp - twepoch) << timestampLeftShift)
|
||||
| (datacenterId << datacenterIdShift)
|
||||
| (workerId << workerIdShift)
|
||||
| sequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* 阻塞到下一个毫秒,直到获得新的时间戳
|
||||
* @param lastTimestamp 上次生成ID的时间截
|
||||
* @return 当前时间戳
|
||||
*/
|
||||
protected long tilNextMillis(long lastTimestamp) {
|
||||
long timestamp = timeGen();
|
||||
while (timestamp <= lastTimestamp) {
|
||||
timestamp = timeGen();
|
||||
}
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回以毫秒为单位的当前时间
|
||||
* @return 当前时间(毫秒)
|
||||
*/
|
||||
protected long timeGen() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
}
|
||||
/*//==============================Test=============================================
|
||||
*/
|
||||
/** 测试
|
||||
public static void main(String[] args) {
|
||||
SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
long id = idWorker.nextId();
|
||||
System.out.println("id:" + id);
|
||||
System.out.println("toBinaryString:" + Long.toBinaryString(id));
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
@@ -0,0 +1,97 @@
|
||||
package com.gitee.sop.adminbackend.util;
|
||||
|
||||
import com.auth0.jwt.JWT;
|
||||
import com.auth0.jwt.JWTCreator;
|
||||
import com.auth0.jwt.JWTVerifier;
|
||||
import com.auth0.jwt.algorithms.Algorithm;
|
||||
import com.auth0.jwt.exceptions.TokenExpiredException;
|
||||
import com.auth0.jwt.interfaces.Claim;
|
||||
import com.auth0.jwt.interfaces.DecodedJWT;
|
||||
import com.gitee.sop.adminbackend.common.exception.JwtCreateException;
|
||||
import com.gitee.sop.adminbackend.common.exception.JwtErrorException;
|
||||
import com.gitee.sop.adminbackend.common.exception.JwtExpiredException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Slf4j
|
||||
public class JwtUtil {
|
||||
|
||||
private static final Map<String, Object> headerClaims = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 登录成功后跳转页面
|
||||
*/
|
||||
private static final String SUCCESS_HTML = String.join("\n", Arrays.asList(
|
||||
"<html><head><script>",
|
||||
"localStorage.setItem('torna.token', '%s');",
|
||||
"location.href = '%s';",
|
||||
"</script></head><body></body></html>"
|
||||
));
|
||||
|
||||
static {
|
||||
headerClaims.put("typ", "JWT");
|
||||
headerClaims.put("alg", "HS256");
|
||||
}
|
||||
|
||||
public static String createJwt(Map<String, String> data, int timeoutDays, String secret) {
|
||||
JWTCreator.Builder builder = JWT.create().withHeader(headerClaims);
|
||||
Set<Map.Entry<String, String>> entrySet = data.entrySet();
|
||||
for (Map.Entry<String, String> entry : entrySet) {
|
||||
builder.withClaim(entry.getKey(), entry.getValue());
|
||||
}
|
||||
LocalDateTime expiredDay = LocalDateTime.now().plusDays(timeoutDays);
|
||||
Date expiredDate = Date.from(expiredDay.atZone(ZoneId.systemDefault()).toInstant());
|
||||
try {
|
||||
return builder
|
||||
// 过期时间
|
||||
.withExpiresAt(expiredDate)
|
||||
// 创建时间
|
||||
.withIssuedAt(new Date())
|
||||
// 签名
|
||||
.sign(Algorithm.HMAC256(secret));
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
throw new JwtCreateException();
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, Claim> verifyJwt(String token, String secret) throws JwtExpiredException, JwtErrorException {
|
||||
JWTVerifier verifier = null;
|
||||
try {
|
||||
verifier = JWT.require(Algorithm.HMAC256(secret)).build();
|
||||
} catch (Exception e) {
|
||||
log.error("验证jwt失败", e);
|
||||
throw new JwtErrorException();
|
||||
}
|
||||
|
||||
DecodedJWT jwt;
|
||||
try {
|
||||
jwt = verifier.verify(token);
|
||||
} catch (TokenExpiredException e) {
|
||||
throw new JwtExpiredException();
|
||||
} catch (Exception e) {
|
||||
log.error("验证jwt失败", e);
|
||||
throw new JwtErrorException();
|
||||
}
|
||||
|
||||
return jwt.getClaims();
|
||||
}
|
||||
|
||||
public static String getJumpPageHtml(String token) {
|
||||
return getJumpPageHtml(token, "/");
|
||||
}
|
||||
|
||||
public static String getJumpPageHtml(String token, String redirectUrl) {
|
||||
return String.format(SUCCESS_HTML, token, redirectUrl);
|
||||
}
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
package com.gitee.sop.adminbackend.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public class PasswordUtil {
|
||||
|
||||
private static final List<Character> CHARS;
|
||||
private static final List<Character> SIMPLE_CHARS;
|
||||
|
||||
static {
|
||||
CHARS = getCharList("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~!@#$%^&*-=,.<>");
|
||||
SIMPLE_CHARS = getCharList("abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789");
|
||||
}
|
||||
|
||||
private static List<Character> getCharList(String str) {
|
||||
char[] chars = str.toCharArray();
|
||||
List<Character> list = new ArrayList<>(chars.length);
|
||||
for (char c : chars) {
|
||||
list.add(c);
|
||||
}
|
||||
Collections.shuffle(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
String password = getRandomPassword(32);
|
||||
System.out.println(password);
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机密码生成,仅字母数字
|
||||
* @param len 密码长度,必须大于等于4
|
||||
*/
|
||||
public static String getRandomSimplePassword(int len) {
|
||||
if (len < 4) {
|
||||
throw new IllegalArgumentException("'len' must >= 4");
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Random r = new Random();
|
||||
for (int x = 0; x < len; ++x) {
|
||||
sb.append(SIMPLE_CHARS.get(r.nextInt(SIMPLE_CHARS.size())));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机密码生成
|
||||
* @param len 密码长度,必须大于等于6
|
||||
*/
|
||||
public static String getRandomPassword(int len) {
|
||||
if (len < 6) {
|
||||
throw new IllegalArgumentException("'len' must >= 6");
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Random r = new Random();
|
||||
for (int x = 0; x < len; ++x) {
|
||||
sb.append(CHARS.get(r.nextInt(CHARS.size())));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
dubbo.registry.address=zookeeper://localhost:2181
|
||||
|
||||
mybatis.print-sql=true
|
||||
|
||||
# mysql config
|
||||
mysql.host=127.0.0.1:3306
|
||||
mysql.username=root
|
||||
mysql.password=root
|
@@ -0,0 +1,2 @@
|
||||
|
||||
dubbo.registry.address=nacos://localhost:8848
|
@@ -0,0 +1,33 @@
|
||||
server.port=8082
|
||||
spring.profiles.active=dev
|
||||
|
||||
spring.application.name=sop-admin-backend
|
||||
|
||||
dubbo.protocol.name=dubbo
|
||||
dubbo.protocol.port=-1
|
||||
dubbo.application.qos-enable=false
|
||||
dubbo.registry.address=zookeeper://localhost:2181
|
||||
|
||||
####### mysql config #######
|
||||
mysql.host=127.0.0.1:3306
|
||||
mysql.username=
|
||||
mysql.password=
|
||||
mysql.db=sop
|
||||
|
||||
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
spring.datasource.url=jdbc:mysql://${mysql.host}/${mysql.db}?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
|
||||
spring.datasource.username=${mysql.username}
|
||||
spring.datasource.password=${mysql.password}
|
||||
|
||||
####### mybatis config #######
|
||||
mybatis.fill.com.gitee.fastmybatis.core.support.LocalDateTimeFillInsert=add_time
|
||||
mybatis.fill.com.gitee.fastmybatis.core.support.LocalDateTimeFillUpdate=update_time
|
||||
# mybatis config file
|
||||
mybatis.config-location=classpath:mybatis/mybatisConfig.xml
|
||||
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
|
||||
|
||||
|
||||
# print SQL
|
||||
logging.level.com.gitee.sop.adminbackend.dao=error
|
||||
logging.level.com.gitee.fastmybatis=info
|
||||
mybatis.print-sql=false
|
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
||||
<mapper namespace="com.gitee.sop.adminbackend.dao.mapper.UpgradeMapper">
|
||||
|
||||
<update id="runSql">
|
||||
${sql}
|
||||
</update>
|
||||
|
||||
<select id="listColumnInfo" resultType="java.util.Map">
|
||||
SHOW COLUMNS FROM ${tableName}
|
||||
</select>
|
||||
|
||||
|
||||
<select id="listTableName" resultType="String">
|
||||
SHOW TABLES
|
||||
</select>
|
||||
|
||||
<select id="listTableIndex" resultType="java.util.Map">
|
||||
show index from ${tableName}
|
||||
</select>
|
||||
|
||||
</mapper>
|
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
|
||||
<configuration>
|
||||
<settings>
|
||||
<!-- 全局映射器启用缓存 -->
|
||||
<setting name="cacheEnabled" value="true" />
|
||||
<!-- 查询时,关闭关联对象即时加载以提高性能 -->
|
||||
<setting name="lazyLoadingEnabled" value="true" />
|
||||
<!-- 对于未知的SQL查询,允许返回不同的结果集以达到通用的效果 -->
|
||||
<setting name="multipleResultSetsEnabled" value="true" />
|
||||
<!-- 允许使用列标签代替列名 -->
|
||||
<setting name="useColumnLabel" value="true" />
|
||||
<!-- 允许使用自定义的主键值(比如由程序生成的UUID 32位编码作为键值),数据表的PK生成策略将被覆盖 -->
|
||||
<setting name="useGeneratedKeys" value="false" />
|
||||
<!-- 对于批量更新操作缓存SQL以提高性能:BATCH -->
|
||||
<setting name="defaultExecutorType" value="SIMPLE" />
|
||||
<!-- 超时设置 -->
|
||||
<setting name="defaultStatementTimeout" value="25000" />
|
||||
</settings>
|
||||
|
||||
<plugins>
|
||||
<plugin interceptor="com.gitee.fastmybatis.core.support.plugin.SqlFormatterPlugin">
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
</configuration>
|
@@ -0,0 +1,13 @@
|
||||
package com.gitee.sop.adminbackend;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
public class BaseTest {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
package com.gitee.sop.adminbackend.service;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.gitee.sop.adminbackend.BaseTest;
|
||||
import com.gitee.sop.adminbackend.service.sys.login.LoginService;
|
||||
import com.gitee.sop.adminbackend.service.sys.login.dto.LoginDTO;
|
||||
import com.gitee.sop.adminbackend.service.sys.login.dto.LoginUser;
|
||||
import com.gitee.sop.adminbackend.service.sys.login.enums.RegTypeEnum;
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.security.crypto.bcrypt.BCrypt;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public class LoginServiceTest extends BaseTest {
|
||||
|
||||
@Resource
|
||||
LoginService loginService;
|
||||
|
||||
@Test
|
||||
public void login() {
|
||||
LoginDTO loginDTO = new LoginDTO();
|
||||
loginDTO.setUsername("admin");
|
||||
loginDTO.setPassword("123456");
|
||||
loginDTO.setRegType(RegTypeEnum.BACKEND);
|
||||
LoginUser loginUser = loginService.login(loginDTO);
|
||||
Assert.notNull(loginUser, "not null");
|
||||
System.out.println(JSON.toJSONString(loginUser));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resetAdminPwd() {
|
||||
// 初始密码
|
||||
String defPassword = "123456";
|
||||
defPassword = DigestUtils.sha256Hex(defPassword);
|
||||
String encodedPassword = BCrypt.hashpw(defPassword, BCrypt.gensalt());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
package com.gitee.sop.adminbackend.service;
|
||||
|
||||
import com.gitee.sop.adminbackend.BaseTest;
|
||||
import com.gitee.sop.adminbackend.dao.entity.SysAdminUser;
|
||||
import com.gitee.sop.adminbackend.service.sys.SysAdminUserService;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.security.crypto.bcrypt.BCrypt;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public class PasswordTest extends BaseTest {
|
||||
|
||||
@Resource
|
||||
SysAdminUserService sysAdminUserService;
|
||||
|
||||
/**
|
||||
* 重置admin密码
|
||||
*/
|
||||
@Test
|
||||
public void resetAdminPwd() {
|
||||
String username = "admin";
|
||||
String defPassword = "123456";
|
||||
defPassword = DigestUtils.sha256Hex(defPassword);
|
||||
String encodedPassword = BCrypt.hashpw(defPassword, BCrypt.gensalt());
|
||||
System.out.println("数据库保存:" + encodedPassword);
|
||||
sysAdminUserService.query()
|
||||
.eq(SysAdminUser::getUsername, username)
|
||||
.set(SysAdminUser::getPassword, encodedPassword)
|
||||
.update();
|
||||
}
|
||||
|
||||
}
|
4
sop-admin/sop-admin-frontend/.browserslistrc
Normal file
4
sop-admin/sop-admin-frontend/.browserslistrc
Normal file
@@ -0,0 +1,4 @@
|
||||
> 1%
|
||||
last 2 versions
|
||||
not dead
|
||||
not ie 11
|
21
sop-admin/sop-admin-frontend/.dockerignore
Normal file
21
sop-admin/sop-admin-frontend/.dockerignore
Normal file
@@ -0,0 +1,21 @@
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
.eslintcache
|
||||
report.html
|
||||
|
||||
yarn.lock
|
||||
npm-debug.log*
|
||||
.pnpm-error.log*
|
||||
.pnpm-debug.log
|
||||
tests/**/coverage/
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
tsconfig.tsbuildinfo
|
14
sop-admin/sop-admin-frontend/.editorconfig
Normal file
14
sop-admin/sop-admin-frontend/.editorconfig
Normal file
@@ -0,0 +1,14 @@
|
||||
# http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
insert_final_newline = false
|
||||
trim_trailing_whitespace = false
|
5
sop-admin/sop-admin-frontend/.env
Normal file
5
sop-admin/sop-admin-frontend/.env
Normal file
@@ -0,0 +1,5 @@
|
||||
# 平台本地运行端口号
|
||||
VITE_PORT = 9123
|
||||
|
||||
# 是否隐藏首页 隐藏 true 不隐藏 false (勿删除,VITE_HIDE_HOME只需在.env文件配置)
|
||||
VITE_HIDE_HOME = false
|
@@ -1 +1,8 @@
|
||||
VITE_API_BASE_URL= 'http://localhost:8080'
|
||||
# 平台本地运行端口号
|
||||
VITE_PORT = 9123
|
||||
|
||||
# 开发环境读取配置文件路径
|
||||
VITE_PUBLIC_PATH = /
|
||||
|
||||
# 开发环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数")
|
||||
VITE_ROUTER_HISTORY = "hash"
|
||||
|
@@ -0,0 +1,13 @@
|
||||
# 线上环境平台打包路径
|
||||
VITE_PUBLIC_PATH = /
|
||||
|
||||
# 线上环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数")
|
||||
VITE_ROUTER_HISTORY = "hash"
|
||||
|
||||
# 是否在打包时使用cdn替换本地库 替换 true 不替换 false
|
||||
VITE_CDN = false
|
||||
|
||||
# 是否启用gzip压缩或brotli压缩(分两种情况,删除原始文件和不删除原始文件)
|
||||
# 压缩时不删除原始文件的配置:gzip、brotli、both(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认)
|
||||
# 压缩时删除原始文件的配置:gzip-clear、brotli-clear、both-clear(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认)
|
||||
VITE_COMPRESSION = "none"
|
16
sop-admin/sop-admin-frontend/.env.staging
Normal file
16
sop-admin/sop-admin-frontend/.env.staging
Normal file
@@ -0,0 +1,16 @@
|
||||
# 预发布也需要生产环境的行为
|
||||
# https://cn.vitejs.dev/guide/env-and-mode.html#modes
|
||||
# NODE_ENV = development
|
||||
|
||||
VITE_PUBLIC_PATH = /
|
||||
|
||||
# 预发布环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数")
|
||||
VITE_ROUTER_HISTORY = "hash"
|
||||
|
||||
# 是否在打包时使用cdn替换本地库 替换 true 不替换 false
|
||||
VITE_CDN = true
|
||||
|
||||
# 是否启用gzip压缩或brotli压缩(分两种情况,删除原始文件和不删除原始文件)
|
||||
# 压缩时不删除原始文件的配置:gzip、brotli、both(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认)
|
||||
# 压缩时删除原始文件的配置:gzip-clear、brotli-clear、both-clear(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认)
|
||||
VITE_COMPRESSION = "none"
|
@@ -1,3 +0,0 @@
|
||||
/*.json
|
||||
/*.js
|
||||
dist
|
@@ -1,70 +0,0 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: 'vue-eslint-parser',
|
||||
parserOptions: {
|
||||
// Parser that checks the content of the <script> tag
|
||||
parser: '@typescript-eslint/parser',
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 2020,
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
env: {
|
||||
'browser': true,
|
||||
'node': true,
|
||||
'vue/setup-compiler-macros': true,
|
||||
},
|
||||
plugins: ['@typescript-eslint'],
|
||||
extends: [
|
||||
// Airbnb JavaScript Style Guide https://github.com/airbnb/javascript
|
||||
'airbnb-base',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:import/recommended',
|
||||
'plugin:import/typescript',
|
||||
'plugin:vue/vue3-recommended',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
settings: {
|
||||
'import/resolver': {
|
||||
typescript: {
|
||||
project: path.resolve(__dirname, './tsconfig.json'),
|
||||
},
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'prettier/prettier': 1,
|
||||
// Vue: Recommended rules to be closed or modify
|
||||
'vue/require-default-prop': 0,
|
||||
'vue/singleline-html-element-content-newline': 0,
|
||||
'vue/max-attributes-per-line': 0,
|
||||
// Vue: Add extra rules
|
||||
'vue/custom-event-name-casing': [2, 'camelCase'],
|
||||
'vue/no-v-text': 1,
|
||||
'vue/padding-line-between-blocks': 1,
|
||||
'vue/require-direct-export': 1,
|
||||
'vue/multi-word-component-names': 0,
|
||||
// Allow @ts-ignore comment
|
||||
'@typescript-eslint/ban-ts-comment': 0,
|
||||
'@typescript-eslint/no-unused-vars': 1,
|
||||
'@typescript-eslint/no-empty-function': 1,
|
||||
'@typescript-eslint/no-explicit-any': 0,
|
||||
'import/extensions': [
|
||||
2,
|
||||
'ignorePackages',
|
||||
{
|
||||
js: 'never',
|
||||
jsx: 'never',
|
||||
ts: 'never',
|
||||
tsx: 'never',
|
||||
},
|
||||
],
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
|
||||
'no-param-reassign': 0,
|
||||
'prefer-regex-literals': 0,
|
||||
'import/no-extraneous-dependencies': 0,
|
||||
},
|
||||
};
|
22
sop-admin/sop-admin-frontend/.gitignore
vendored
22
sop-admin/sop-admin-frontend/.gitignore
vendored
@@ -3,8 +3,20 @@ node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
.eslintcache
|
||||
report.html
|
||||
vite.config.*.timestamp*
|
||||
|
||||
yarn.lock
|
||||
npm-debug.log*
|
||||
.pnpm-error.log*
|
||||
.pnpm-debug.log
|
||||
tests/**/coverage/
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
tsconfig.tsbuildinfo
|
@@ -1,4 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
# shellcheck source=./_/husky.sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
pnpm commitlint --edit $1
|
||||
PATH="/usr/local/bin:$PATH"
|
||||
|
||||
npx --no-install commitlint --edit "$1"
|
9
sop-admin/sop-admin-frontend/.husky/common.sh
Normal file
9
sop-admin/sop-admin-frontend/.husky/common.sh
Normal file
@@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
command_exists () {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Workaround for Windows 10, Git Bash and Pnpm
|
||||
if command_exists winpty && test -t 1; then
|
||||
exec < /dev/tty
|
||||
fi
|
@@ -1,4 +1,10 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
. "$(dirname "$0")/common.sh"
|
||||
|
||||
npm run lint-staged
|
||||
[ -n "$CI" ] && exit 0
|
||||
|
||||
PATH="/usr/local/bin:$PATH"
|
||||
|
||||
# Perform lint check on files in the staging area through .lintstagedrc configuration
|
||||
pnpm exec lint-staged
|
20
sop-admin/sop-admin-frontend/.lintstagedrc
Normal file
20
sop-admin/sop-admin-frontend/.lintstagedrc
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"*.{js,jsx,ts,tsx}": [
|
||||
"prettier --cache --ignore-unknown --write",
|
||||
"eslint --cache --fix"
|
||||
],
|
||||
"{!(package)*.json,*.code-snippets,.!({browserslist,npm,nvm})*rc}": [
|
||||
"prettier --cache --write--parser json"
|
||||
],
|
||||
"package.json": ["prettier --cache --write"],
|
||||
"*.vue": [
|
||||
"prettier --write",
|
||||
"eslint --cache --fix",
|
||||
"stylelint --fix --allow-empty-input"
|
||||
],
|
||||
"*.{css,scss,html}": [
|
||||
"prettier --cache --ignore-unknown --write",
|
||||
"stylelint --fix --allow-empty-input"
|
||||
],
|
||||
"*.md": ["prettier --cache --ignore-unknown --write"]
|
||||
}
|
11
sop-admin/sop-admin-frontend/.markdownlint.json
Normal file
11
sop-admin/sop-admin-frontend/.markdownlint.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"default": true,
|
||||
"MD003": false,
|
||||
"MD033": false,
|
||||
"MD013": false,
|
||||
"MD001": false,
|
||||
"MD025": false,
|
||||
"MD024": false,
|
||||
"MD007": { "indent": 4 },
|
||||
"no-hard-tabs": false
|
||||
}
|
4
sop-admin/sop-admin-frontend/.npmrc
Normal file
4
sop-admin/sop-admin-frontend/.npmrc
Normal file
@@ -0,0 +1,4 @@
|
||||
shell-emulator=true
|
||||
shamefully-hoist=true
|
||||
enable-pre-post-scripts=false
|
||||
strict-peer-dependencies=false
|
1
sop-admin/sop-admin-frontend/.nvmrc
Normal file
1
sop-admin/sop-admin-frontend/.nvmrc
Normal file
@@ -0,0 +1 @@
|
||||
v20.15.0
|
@@ -1,7 +0,0 @@
|
||||
/dist/*
|
||||
.local
|
||||
.output.js
|
||||
/node_modules/**
|
||||
|
||||
**/*.svg
|
||||
**/*.sh
|
@@ -1,9 +1,9 @@
|
||||
module.exports = {
|
||||
tabWidth: 2,
|
||||
semi: true,
|
||||
printWidth: 80,
|
||||
singleQuote: true,
|
||||
quoteProps: 'consistent',
|
||||
htmlWhitespaceSensitivity: 'strict',
|
||||
vueIndentScriptAndStyle: true,
|
||||
// @ts-check
|
||||
|
||||
/** @type {import("prettier").Config} */
|
||||
export default {
|
||||
bracketSpacing: true,
|
||||
singleQuote: false,
|
||||
arrowParens: "avoid",
|
||||
trailingComma: "none"
|
||||
};
|
||||
|
4
sop-admin/sop-admin-frontend/.stylelintignore
Normal file
4
sop-admin/sop-admin-frontend/.stylelintignore
Normal file
@@ -0,0 +1,4 @@
|
||||
/dist/*
|
||||
/public/*
|
||||
public/*
|
||||
src/style/reset.scss
|
@@ -1,30 +0,0 @@
|
||||
module.exports = {
|
||||
extends: [
|
||||
'stylelint-config-standard',
|
||||
'stylelint-config-rational-order',
|
||||
'stylelint-config-prettier',
|
||||
'stylelint-config-recommended-vue',
|
||||
],
|
||||
defaultSeverity: 'warning',
|
||||
plugins: ['stylelint-order'],
|
||||
rules: {
|
||||
'at-rule-no-unknown': [
|
||||
true,
|
||||
{
|
||||
ignoreAtRules: ['plugin'],
|
||||
},
|
||||
],
|
||||
'rule-empty-line-before': [
|
||||
'always',
|
||||
{
|
||||
except: ['after-single-line-comment', 'first-nested'],
|
||||
},
|
||||
],
|
||||
'selector-pseudo-class-no-unknown': [
|
||||
true,
|
||||
{
|
||||
ignorePseudoClasses: ['deep'],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
19
sop-admin/sop-admin-frontend/.vscode/extensions.json
vendored
Normal file
19
sop-admin/sop-admin-frontend/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"christian-kohler.path-intellisense",
|
||||
"warmthsea.vscode-custom-code-color",
|
||||
"vscode-icons-team.vscode-icons",
|
||||
"davidanson.vscode-markdownlint",
|
||||
"ms-azuretools.vscode-docker",
|
||||
"stylelint.vscode-stylelint",
|
||||
"bradlc.vscode-tailwindcss",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"redhat.vscode-yaml",
|
||||
"csstools.postcss",
|
||||
"mikestead.dotenv",
|
||||
"eamodio.gitlens",
|
||||
"antfu.iconify",
|
||||
"Vue.volar"
|
||||
]
|
||||
}
|
43
sop-admin/sop-admin-frontend/.vscode/settings.json
vendored
Normal file
43
sop-admin/sop-admin-frontend/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"editor.formatOnType": true,
|
||||
"editor.formatOnSave": true,
|
||||
"[vue]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"editor.tabSize": 2,
|
||||
"editor.formatOnPaste": true,
|
||||
"editor.guides.bracketPairs": "active",
|
||||
"files.autoSave": "afterDelay",
|
||||
"git.confirmSync": false,
|
||||
"workbench.startupEditor": "newUntitledFile",
|
||||
"editor.suggestSelection": "first",
|
||||
"editor.acceptSuggestionOnCommitCharacter": false,
|
||||
"css.lint.propertyIgnoredDueToDisplay": "ignore",
|
||||
"editor.quickSuggestions": {
|
||||
"other": true,
|
||||
"comments": true,
|
||||
"strings": true
|
||||
},
|
||||
"files.associations": {
|
||||
"editor.snippetSuggestions": "top"
|
||||
},
|
||||
"[css]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
"iconify.excludes": [
|
||||
"el"
|
||||
],
|
||||
"vscodeCustomCodeColor.highlightValue": [
|
||||
"v-loading",
|
||||
"v-auth",
|
||||
"v-copy",
|
||||
"v-longpress",
|
||||
"v-optimize",
|
||||
"v-perms",
|
||||
"v-ripple"
|
||||
],
|
||||
"vscodeCustomCodeColor.highlightValueColor": "#b392f0",
|
||||
}
|
22
sop-admin/sop-admin-frontend/.vscode/vue3.0.code-snippets
vendored
Normal file
22
sop-admin/sop-admin-frontend/.vscode/vue3.0.code-snippets
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"Vue3.0快速生成模板": {
|
||||
"scope": "vue",
|
||||
"prefix": "Vue3.0",
|
||||
"body": [
|
||||
"<template>",
|
||||
"\t<div>test</div>",
|
||||
"</template>\n",
|
||||
"<script lang='ts'>",
|
||||
"export default {",
|
||||
"\tsetup() {",
|
||||
"\t\treturn {}",
|
||||
"\t}",
|
||||
"}",
|
||||
"</script>\n",
|
||||
"<style lang='scss' scoped>\n",
|
||||
"</style>",
|
||||
"$2"
|
||||
],
|
||||
"description": "Vue3.0"
|
||||
}
|
||||
}
|
17
sop-admin/sop-admin-frontend/.vscode/vue3.2.code-snippets
vendored
Normal file
17
sop-admin/sop-admin-frontend/.vscode/vue3.2.code-snippets
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"Vue3.2+快速生成模板": {
|
||||
"scope": "vue",
|
||||
"prefix": "Vue3.2+",
|
||||
"body": [
|
||||
"<script setup lang='ts'>",
|
||||
"</script>\n",
|
||||
"<template>",
|
||||
"\t<div>test</div>",
|
||||
"</template>\n",
|
||||
"<style lang='scss' scoped>\n",
|
||||
"</style>",
|
||||
"$2"
|
||||
],
|
||||
"description": "Vue3.2+"
|
||||
}
|
||||
}
|
20
sop-admin/sop-admin-frontend/.vscode/vue3.3.code-snippets
vendored
Normal file
20
sop-admin/sop-admin-frontend/.vscode/vue3.3.code-snippets
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"Vue3.3+defineOptions快速生成模板": {
|
||||
"scope": "vue",
|
||||
"prefix": "Vue3.3+",
|
||||
"body": [
|
||||
"<script setup lang='ts'>",
|
||||
"defineOptions({",
|
||||
"\tname: ''",
|
||||
"})",
|
||||
"</script>\n",
|
||||
"<template>",
|
||||
"\t<div>test</div>",
|
||||
"</template>\n",
|
||||
"<style lang='scss' scoped>\n",
|
||||
"</style>",
|
||||
"$2"
|
||||
],
|
||||
"description": "Vue3.3+defineOptions快速生成模板"
|
||||
}
|
||||
}
|
20
sop-admin/sop-admin-frontend/Dockerfile
Normal file
20
sop-admin/sop-admin-frontend/Dockerfile
Normal file
@@ -0,0 +1,20 @@
|
||||
FROM node:20-alpine as build-stage
|
||||
|
||||
WORKDIR /app
|
||||
RUN corepack enable
|
||||
RUN corepack prepare pnpm@latest --activate
|
||||
|
||||
RUN npm config set registry https://registry.npmmirror.com
|
||||
|
||||
COPY .npmrc package.json pnpm-lock.yaml ./
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
COPY . .
|
||||
RUN pnpm build
|
||||
|
||||
FROM nginx:stable-alpine as production-stage
|
||||
|
||||
COPY --from=build-stage /app/dist /usr/share/nginx/html
|
||||
EXPOSE 80
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
21
sop-admin/sop-admin-frontend/LICENSE
Normal file
21
sop-admin/sop-admin-frontend/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020-present, pure-admin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
51
sop-admin/sop-admin-frontend/README.md
Normal file
51
sop-admin/sop-admin-frontend/README.md
Normal file
@@ -0,0 +1,51 @@
|
||||
<h1>vue-pure-admin精简版(国际化版本)</h1>
|
||||
|
||||
[](LICENSE)
|
||||
|
||||
**中文** | [English](./README.en-US.md)
|
||||
|
||||
## 介绍
|
||||
|
||||
精简版是基于 [vue-pure-admin](https://github.com/pure-admin/vue-pure-admin) 提炼出的架子,包含主体功能,更适合实际项目开发,打包后的大小在全局引入 [element-plus](https://element-plus.org) 的情况下仍然低于 `2.3MB`,并且会永久同步完整版的代码。开启 `brotli` 压缩和 `cdn` 替换本地库模式后,打包大小低于 `350kb`
|
||||
|
||||
## 版本选择
|
||||
|
||||
当前是国际化版本,如果您需要非国际化版本 [请点击](https://github.com/pure-admin/pure-admin-thin)
|
||||
|
||||
## `js` 版本
|
||||
|
||||
[点我查看 js 版本](https://pure-admin.github.io/pure-admin-doc/pages/js/)
|
||||
|
||||
## `max` 版本
|
||||
|
||||
[点我查看 max 版本](https://github.com/pure-admin/vue-pure-admin-max)
|
||||
|
||||
## 配套视频
|
||||
|
||||
[点我查看 UI 设计](https://www.bilibili.com/video/BV17g411T7rq)
|
||||
[点我查看快速开发教程](https://www.bilibili.com/video/BV1kg411v7QT)
|
||||
|
||||
## 配套保姆级文档
|
||||
|
||||
[点我查看 vue-pure-admin 文档](https://pure-admin.github.io/pure-admin-doc)
|
||||
[点我查看 @pureadmin/utils 文档](https://pure-admin-utils.netlify.app)
|
||||
|
||||
## 优质服务、软件外包、赞助支持
|
||||
|
||||
[点我查看详情](https://pure-admin.github.io/pure-admin-doc/pages/service/)
|
||||
|
||||
## 预览
|
||||
|
||||
[查看预览](https://pure-admin-thin.netlify.app/#/login)
|
||||
|
||||
## 维护者
|
||||
|
||||
[xiaoxian521](https://github.com/xiaoxian521)
|
||||
|
||||
## ⚠️ 注意
|
||||
|
||||
精简版不接受任何 `issues` 和 `pr`,如果有问题请到完整版 [issues](https://github.com/pure-admin/vue-pure-admin/issues/new/choose) 去提,谢谢!
|
||||
|
||||
## 许可证
|
||||
|
||||
[MIT © 2020-present, pure-admin](./LICENSE)
|
@@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
plugins: ['@vue/babel-plugin-jsx'],
|
||||
};
|
60
sop-admin/sop-admin-frontend/build/cdn.ts
Normal file
60
sop-admin/sop-admin-frontend/build/cdn.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { Plugin as importToCDN } from "vite-plugin-cdn-import";
|
||||
|
||||
/**
|
||||
* @description 打包时采用`cdn`模式,仅限外网使用(默认不采用,如果需要采用cdn模式,请在 .env.production 文件,将 VITE_CDN 设置成true)
|
||||
* 平台采用国内cdn:https://www.bootcdn.cn,当然你也可以选择 https://unpkg.com 或者 https://www.jsdelivr.com
|
||||
* 注意:上面提到的仅限外网使用也不是完全肯定的,如果你们公司内网部署的有相关js、css文件,也可以将下面配置对应改一下,整一套内网版cdn
|
||||
*/
|
||||
export const cdn = importToCDN({
|
||||
//(prodUrl解释: name: 对应下面modules的name,version: 自动读取本地package.json中dependencies依赖中对应包的版本号,path: 对应下面modules的path,当然也可写完整路径,会替换prodUrl)
|
||||
prodUrl: "https://cdn.bootcdn.net/ajax/libs/{name}/{version}/{path}",
|
||||
modules: [
|
||||
{
|
||||
name: "vue",
|
||||
var: "Vue",
|
||||
path: "vue.global.prod.min.js"
|
||||
},
|
||||
{
|
||||
name: "vue-router",
|
||||
var: "VueRouter",
|
||||
path: "vue-router.global.min.js"
|
||||
},
|
||||
{
|
||||
name: "vue-i18n",
|
||||
var: "VueI18n",
|
||||
path: "vue-i18n.runtime.global.prod.min.js"
|
||||
},
|
||||
// 项目中没有直接安装vue-demi,但是pinia用到了,所以需要在引入pinia前引入vue-demi(https://github.com/vuejs/pinia/blob/v2/packages/pinia/package.json#L77)
|
||||
{
|
||||
name: "vue-demi",
|
||||
var: "VueDemi",
|
||||
path: "index.iife.min.js"
|
||||
},
|
||||
{
|
||||
name: "pinia",
|
||||
var: "Pinia",
|
||||
path: "pinia.iife.min.js"
|
||||
},
|
||||
{
|
||||
name: "element-plus",
|
||||
var: "ElementPlus",
|
||||
path: "index.full.min.js",
|
||||
css: "index.min.css"
|
||||
},
|
||||
{
|
||||
name: "axios",
|
||||
var: "axios",
|
||||
path: "axios.min.js"
|
||||
},
|
||||
{
|
||||
name: "dayjs",
|
||||
var: "dayjs",
|
||||
path: "dayjs.min.js"
|
||||
},
|
||||
{
|
||||
name: "echarts",
|
||||
var: "echarts",
|
||||
path: "echarts.min.js"
|
||||
}
|
||||
]
|
||||
});
|
63
sop-admin/sop-admin-frontend/build/compress.ts
Normal file
63
sop-admin/sop-admin-frontend/build/compress.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import type { Plugin } from "vite";
|
||||
import { isArray } from "@pureadmin/utils";
|
||||
import compressPlugin from "vite-plugin-compression";
|
||||
|
||||
export const configCompressPlugin = (
|
||||
compress: ViteCompression
|
||||
): Plugin | Plugin[] => {
|
||||
if (compress === "none") return null;
|
||||
|
||||
const gz = {
|
||||
// 生成的压缩包后缀
|
||||
ext: ".gz",
|
||||
// 体积大于threshold才会被压缩
|
||||
threshold: 0,
|
||||
// 默认压缩.js|mjs|json|css|html后缀文件,设置成true,压缩全部文件
|
||||
filter: () => true,
|
||||
// 压缩后是否删除原始文件
|
||||
deleteOriginFile: false
|
||||
};
|
||||
const br = {
|
||||
ext: ".br",
|
||||
algorithm: "brotliCompress",
|
||||
threshold: 0,
|
||||
filter: () => true,
|
||||
deleteOriginFile: false
|
||||
};
|
||||
|
||||
const codeList = [
|
||||
{ k: "gzip", v: gz },
|
||||
{ k: "brotli", v: br },
|
||||
{ k: "both", v: [gz, br] }
|
||||
];
|
||||
|
||||
const plugins: Plugin[] = [];
|
||||
|
||||
codeList.forEach(item => {
|
||||
if (compress.includes(item.k)) {
|
||||
if (compress.includes("clear")) {
|
||||
if (isArray(item.v)) {
|
||||
item.v.forEach(vItem => {
|
||||
plugins.push(
|
||||
compressPlugin(Object.assign(vItem, { deleteOriginFile: true }))
|
||||
);
|
||||
});
|
||||
} else {
|
||||
plugins.push(
|
||||
compressPlugin(Object.assign(item.v, { deleteOriginFile: true }))
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (isArray(item.v)) {
|
||||
item.v.forEach(vItem => {
|
||||
plugins.push(compressPlugin(vItem));
|
||||
});
|
||||
} else {
|
||||
plugins.push(compressPlugin(item.v));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return plugins;
|
||||
};
|
57
sop-admin/sop-admin-frontend/build/info.ts
Normal file
57
sop-admin/sop-admin-frontend/build/info.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import type { Plugin } from "vite";
|
||||
import { getPackageSize } from "./utils";
|
||||
import dayjs, { type Dayjs } from "dayjs";
|
||||
import duration from "dayjs/plugin/duration";
|
||||
import gradientString from "gradient-string";
|
||||
import boxen, { type Options as BoxenOptions } from "boxen";
|
||||
dayjs.extend(duration);
|
||||
|
||||
const welcomeMessage = gradientString("cyan", "magenta").multiline(
|
||||
`您好! 欢迎使用 pure-admin 开源项目\n我们为您精心准备了下面两个贴心的保姆级文档\nhttps://pure-admin.github.io/pure-admin-doc\nhttps://pure-admin-utils.netlify.app`
|
||||
);
|
||||
|
||||
const boxenOptions: BoxenOptions = {
|
||||
padding: 0.5,
|
||||
borderColor: "cyan",
|
||||
borderStyle: "round"
|
||||
};
|
||||
|
||||
export function viteBuildInfo(): Plugin {
|
||||
let config: { command: string };
|
||||
let startTime: Dayjs;
|
||||
let endTime: Dayjs;
|
||||
let outDir: string;
|
||||
return {
|
||||
name: "vite:buildInfo",
|
||||
configResolved(resolvedConfig) {
|
||||
config = resolvedConfig;
|
||||
outDir = resolvedConfig.build?.outDir ?? "dist";
|
||||
},
|
||||
buildStart() {
|
||||
console.log(boxen(welcomeMessage, boxenOptions));
|
||||
if (config.command === "build") {
|
||||
startTime = dayjs(new Date());
|
||||
}
|
||||
},
|
||||
closeBundle() {
|
||||
if (config.command === "build") {
|
||||
endTime = dayjs(new Date());
|
||||
getPackageSize({
|
||||
folder: outDir,
|
||||
callback: (size: string) => {
|
||||
console.log(
|
||||
boxen(
|
||||
gradientString("cyan", "magenta").multiline(
|
||||
`🎉 恭喜打包完成(总用时${dayjs
|
||||
.duration(endTime.diff(startTime))
|
||||
.format("mm分ss秒")},打包后的大小为${size})`
|
||||
),
|
||||
boxenOptions
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user