旭锋集团运营平台v2
- 1 客户端初始化
- 1.1 创建客户端项目
- 1.2 安装并初始化UI组件
- 1.2.1 安装并初始化Antdv
- 1.2.2 安装并初始化 element-plus
- 1.2.3 安装echarts
- 1.2.4 引入所有UI
- 1.2.5 引入部分UI css
- 1.3安装并初始化 axios
- 1.3.1安装axios
- 1.3.2 初始化axios
- 2 服务器端初始化
- 2.1 创建统一父工程
- 2.2 创建网关工程
- 2.2.1 添加依赖坐标
- 2.2.2 工程配置
- 2.2.3 添加启动类
- 2.3 创建common 工程
- 2.3.1 依赖管理
- 2.3.2 定义响应封装类
- 2.3.3 定义异常信息封装enum
- 2.3.4 定义全局异常处理类
- 2.3.5 定义线程池工具类集
- 2.3.5.1 定义线程池默认属性类
- 2.3.5.2 定义线程池属性类
- 2.3.5.3 定义线程池配置类
- 2.3.5.4 定义启动线程池注解
- 2.3.6 定义redis工具类集
- 2.3.6.1 定义redis连接池默认属性类
- 2.3.6.2 定义redis连接池属性类
- 2.3.6.3 定义redis连接池配置类
- 2.3.6.4 定义redis模板工具类
- 2.3.6.5 定义redis工具类集启动注解
- 2.3.6.6 定义支持redis缓存操作的注解
- 2.3.6.7 定义实现redis缓存操作的切面类
- 2.3.7 定义响应JSON的vo类
- 2.3.8 定义封装类序列化与反序列化工具类
- 2.3.10 定义自定义异常
- 2.3.11 自定义异常信息的enum
- 2.3.13 所有模块公用全局异常处理
- 3 功能模块
- 3.1权限子系统
- 3.2业务子系统
1 客户端初始化
1.1 创建客户端项目
- 项目结构如下:
1.2 安装并初始化UI组件
1.2.1 安装并初始化Antdv
- 安装Antdv
npm i --save ant-design-vue- 初始化Andv :在/plugins/antdv.ts
import {App} from "vue";
import {
Row, Col, Button, Form, FormItem, Input, InputPassword, Checkbox, Tooltip
} from "ant-design-vue"
const elements = [
Row, Col, Button, Form , FormItem, Input, InputPassword, Checkbox, Tooltip
];
export function antdvElUse(app:App)
{
elements.forEach((it:any)=>{
app.use(it);
})
}1.2.2 安装并初始化 element-plus
- 安装 element-plus
npm install element-plus --save- 创建/plugins/elmnt.ts
import {App} from "vue";
import {
} from "element-plus";
const elmnt:any = [
];
export function useElElement(app: App)
{
elmnt.forEach((it: any) => {
app.use(it);
})
}1.2.3 安装echarts
- 安装echarts
npm install echarts --save1.2.4 引入所有UI
- 在main.ts中导入对应的UI,主要包括:antdv, element-plus, font-someawe图标css,完成实现为:
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import {antdvElUse} from "@/plugins/antdv";
import 'ant-design-vue/dist/antd.css'
import {useElElement} from "@/plugins/elmnt";
import "@/assets/fontawesome/css/all.min.css";
import "@/assets/css/global.css"
let app = createApp(App);
antdvElUse(app);
useElElement(app);
app.use(store).use(router).mount('#app');1.2.5 引入部分UI css
- 在public/index.html引用部分UI css,静态初始化,具体实现为:
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<link rel="stylesheet" href="//unpkg.com/element-plus/dist/index.css" />
<link rel="stylesheet" href="https://unpkg.com/view-ui-plus/dist/styles/viewuiplus.css">
<title>旭锋集团运营管理平台</title>
<style>
*{
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>1.3安装并初始化 axios
1.3.1安装axios
npm install axios1.3.2 初始化axios
创建
plugin/axios.ts,创建并导出初始化后实例,具体实现如下:
import axios from "axios";
import {sysData} from "@/plugins/api";
export const $axios = axios.create({
baseURL: "http://localhost:9000",
timeout: 3000,
headers: {"System-Type": sysData.sysId}
})
$axios.interceptors.request.use((config) =>{
/*校验是否存在token
* 如果存在token则携带token
* 如果不存在则不携带*/
return config;
})
$axios.interceptors.response.use(res =>{
return res;
}, error => {
return Promise.reject(error);
})2 服务器端初始化
- 系统开发环境版本
windows | jdk | springboot | springcloud | springcloudalibaba | nacos | sentinal | seata | rocketMq |
win11 | 1.8 | 2.6.3 | 2021.0.1 | 2021.0.1.0 | 2.0.3 | 1.8.3 | 1.4.2 | 4.9.2 |
2.1 创建统一父工程
该工程主要用于定义管理系统组件依赖版本。并引入各子系统共同的依赖。具体实现
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http:///POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http:///POM/4.0.0 http:///xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.wjk</groupId>
<artifactId>xfsy-server</artifactId>
<version>1.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>
<!--定义三在系统版本-->
<spring-boot-version>2.6.3</spring-boot-version>
<spring-cloud-version>2021.0.1</spring-cloud-version>
<spring-cloud-alibaba-version>2021.0.1.0</spring-cloud-alibaba-version>
</properties>
<!--管理依赖版本-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot-version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud-version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba-version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<!--引入公共依赖-->
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!--单元测试依赖,子工程中需要单元测试时,不需要再次引入此依赖了-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope><!--test表示只能在test目录下使用此依赖-->
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<!--安装maven插件-->
<build><!--项目构建配置,我们基于maven完成项目的编译,测试,打包等操作,
都是基于pom.xml完成这一列的操作,但是编译和打包的配置都是要写到build元素
内的,而具体的编译和打包配置,又需要plugin去实现,plugin元素不是必须的,maven
有默认的plugin配置,常用插件可去本地库进行查看-->
<plugins>
<!--通过maven-compiler-plugin插件设置项目
的统一的jdk编译和运行版本-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<!--假如本地库没有这个版本,这里会出现红色字体错误-->
<version>3.8.1</version>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>2.2 创建网关工程
- 该工程主要实现:
- 服务路由
- 跨域配置
- 负载均衡
- 项目结构
2.2.1 添加依赖坐标
- pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http:///POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http:///POM/4.0.0 http:///xsd/maven-4.0.0.xsd">
<parent>
<artifactId>xfsy-server</artifactId>
<groupId>org.wjk</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>xfsy-gateway</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
</project>2.2.2 工程配置
- bootstrap.ymal
# 指定运行端口号
server:
port: 9000
spring:
application:
name: xfsy-gateway
cloud:
nacos:
discovery:
server-addr: #######:8848
namespace: 1bd17696-8a91-430e-9e21-887e5c562edf
group: dev
config:
server-addr: ########:8848
namespace: 1bd17696-8a91-430e-9e21-887e5c562edf
group: dev
file-extension: yml
main:
banner-mode: off
logging:
level:
org.wjk: debug- nacos上的application.yml
spring:
cloud:
gateway:
# 实现跨域配置
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: http://localhost:8050, http://localhost:8060
allowedHeaders: "*"
allowedMethods: "*"
allowCredentials: false
# 请求路由
routes:
- id: xfsy_auth_router
# 负载均衡方式将请求路由到指定的服务上
uri: lb://xfsy-sso-server
predicates:
- Path=/auth/**
filters:
- StripPrefix=12.2.3 添加启动类
package org.wjk;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class XfsyGatewayApp
{
public static void main(String[] args)
{
SpringApplication.run(XfsyGatewayApp.class, args);
}
}2.3 创建common 工程
- 工程功能:
- 该工程用于定义两个或以上工程需要共同使用的类
- 利用依赖传递性管理其它工程都需要的引入的依赖
- 该工程将做为其它工程的一部分被其它工程引入而不需要独立运行,需要此该工程不需要启动类
- 项目结果
2.3.1 依赖管理
- pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http:///POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http:///POM/4.0.0 http:///xsd/maven-4.0.0.xsd">
<parent>
<artifactId>xfsy-server</artifactId>
<groupId>org.wjk</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>xfsy-common</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!--公共模块,利用依赖传递特性引入所有模块必须的依赖-->
<dependencies>
<!--rpc调用依赖openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--SpringWeb依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--lombok依赖,用于简化开发-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<!--<scope>compile</scope><!–provided 表示此依赖仅在全阶段有效–>-->
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-core</artifactId>
<version>3.5.2</version>
<!--<scope>provided</scope>-->
<optional>true</optional>
<!--
scope 指定依赖生效的阶段范围即传递性
java程序生命周期划分:
1. 测试;2. 编译;3.运行; 4.打包等四个阶段。
scope 取值对生命周期的影响:
compile: 默认取值,指定依赖在当前项目的测试、编译、运行、打包四个阶段。
provided: 指定依赖只存在于编译、运行、测试阶段,打包不用包进去。
runtime: 指定依赖只在运行、测试阶段有效,不参与编译、打包。
test: 指定依赖仅在测试代码编译与运行阶段有效。
system: 指定依赖是从本地系统指定路径中获取,该值需要systempath属性。
scope 取值对依赖传递的影响:
compile:有传递性,其它值的依赖无传递性。
-->
</dependency>
<!--注解@ConfigurationProperties生效-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
<!--option:可以理解为此功能可选,如果不需要某一功能,可以不引入指定的依赖
当该属性的值为true时,该依赖不会传递。
-->
</dependency>
<!--SSO技术方案:SpringSecurity+JWT+oauth2-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<!--AOP依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--redis原生客户端依赖-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<!--参数校验-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
</dependencies>
</project>2.3.2 定义响应封装类
- 具体实现
package org.wjk.entity.vo;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import org.wjk.exception.ExceptionSpecification;
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResponseResult<T>
{
private Integer resCode;
private String resMsg;
private T resData;
private ResponseResult()
{
}
private ResponseResult(Integer code, String msg, T data)
{
this.resCode = code;
this.resMsg = msg;
this.resData = data;
}
public static <T> ResponseResult<T> success()
{
return new ResponseResult<>(2000, "成功连接服务器", null);
}
public static <T> ResponseResult<T> success(String msg)
{
return new ResponseResult<>(2000, msg, null);
}
public static <T> ResponseResult<T> success(String msg, T data)
{
return new ResponseResult<>(2000, msg, data);
}
public static <T> ResponseResult<T> success(Boolean cry, String msg, T data)
{
if(cry)
return new ResponseResult<>(2001, msg, data);
return null;
}
public static <T> ResponseResult<T> failure(Integer code, String msg)
{
return new ResponseResult<>(code, msg, null);
}
public static <T> ResponseResult<T> failure(ExceptionSpecification spec)
{
return new ResponseResult<>(spec.getResCode(), spec.getResMsg(), null);
}
public static <T> ResponseResult<T> failure()
{
return failure(ExceptionSpecification.SYSTEM_ERROR);
}
public static <T> ResponseResult<T> failure(Integer code, String msg, T data)
{
return new ResponseResult<>(code, msg, data);
}
public static <T> ResponseResult<T> failure(ExceptionSpecification spec, T data)
{
return new ResponseResult<>(spec.getResCode(), spec.getResMsg(), data);
}
}2.3.3 定义异常信息封装enum
- 具体实现
package org.wjk.exception;
public enum ExceptionSpecification
{
SYSTEM_ERROR(5000, "系统内部错误,请稍后重试!"),
ILLEGAL_PARAMETER(5001, "您提供的信息无效!请修改后再重试!")
;
private final Integer resCode;
private final String resMsg;
ExceptionSpecification(Integer resCode, String resMsg)
{
this.resCode = resCode;
this.resMsg = resMsg;
}
public Integer getResCode()
{
return resCode;
}
public String getResMsg()
{
return resMsg;
}
}2.3.4 定义全局异常处理类
- 具体实现:
package org.wjk.exception;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.wjk.entity.vo.ResponseResult;
import javax.validation.ConstraintViolationException;
@RestControllerAdvice
public class GlobalExceptionHandler
{
@ExceptionHandler(ConstraintViolationException.class)
public ResponseResult xfConstraintViolationExceptionHandler(ConstraintViolationException e)
{
return ResponseResult.failure(ExceptionSpecification.ILLEGAL_PARAMETER);
}
}2.3.5 定义线程池工具类集
2.3.5.1 定义线程池默认属性类
- 具体实现
package org.wjk.utils.constant;
public class ThreadPoolDefaultProperties
{
public static final String DEFAULT_POOL_NAME="xfsy_thread_pool";
public static final String DEFAULT_NAME_PREFIX="xfsy_";
public static final int DEFAULT_MAX_SIZE=Integer.MAX_VALUE;
public static final int DEFAULT_CORE_SIZE=1;
public static final int DEFAULT_KEEP_ALIVE=60;
public static final int DEFAULT_QUEUE_CAPACITY=Integer.MAX_VALUE;
}2.3.5.2 定义线程池属性类
- 功能:完成yml文件字段与属性映射
- 具体实现
package org.wjk.properties;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.wjk.utils.constant.ThreadPoolDefaultProperties;
@ConfigurationProperties("thread")
@Data
@Accessors(chain = true)
@Slf4j
public class ThreadPoolProperties
{
private String namePrefix = ThreadPoolDefaultProperties.DEFAULT_NAME_PREFIX;
private int maxSize = ThreadPoolDefaultProperties.DEFAULT_MAX_SIZE;
private int coreSize = ThreadPoolDefaultProperties.DEFAULT_CORE_SIZE;
private int keepAlive = ThreadPoolDefaultProperties.DEFAULT_KEEP_ALIVE;
private int queueCapacity = ThreadPoolDefaultProperties.DEFAULT_QUEUE_CAPACITY;
}2.3.5.3 定义线程池配置类
package org.wjk.config;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.wjk.properties.ThreadPoolProperties;
import org.wjk.utils.constant.ThreadPoolDefaultProperties;
@RequiredArgsConstructor
@Slf4j
public class ThreadPoolConfiguration
{
private final ThreadPoolProperties properties;
@Bean(ThreadPoolDefaultProperties.DEFAULT_POOL_NAME)
public ThreadPoolTaskExecutor threadPoolTaskExecutor()
{
log.debug("当前线程池配置信息为:{},当前CPU工作核心数为:{}", properties, Runtime.getRuntime().availableProcessors());
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setThreadNamePrefix(properties.getNamePrefix());
executor.setCorePoolSize(properties.getCoreSize());
executor.setMaxPoolSize(properties.getMaxSize());
executor.setKeepAliveSeconds(properties.getKeepAlive());
executor.setQueueCapacity(properties.getQueueCapacity());
return executor;
}
}2.3.5.4 定义启动线程池注解
package org.wjk.annotation;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Import;
import org.wjk.config.ThreadPoolConfiguration;
import org.wjk.properties.ThreadPoolProperties;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@EnableConfigurationProperties(ThreadPoolProperties.class)
@Import(ThreadPoolConfiguration.class)
public @interface EnableThreadPool
{
}2.3.6 定义redis工具类集
2.3.6.1 定义redis连接池默认属性类
- 具体实现
package org.wjk.utils.constant;
public class JedisPoolDefaultProperties
{
public static final int DEFAULT_MAX_TOTAL = 8;
public static final int DEFAULT_MAX_IDLE = 8;
public static final int DEFAULT_MIN_IDLE = 0;
public static final String DEFAULT_HOST = "localhost";
public static final int DEFAULT_PORT = 6379;
public static final int DEFAULT_TIMEOUT = 3000;
public static final String DEFAULT_PASSWORD = "root";
}2.3.6.2 定义redis连接池属性类
- 完成yml文件字段与属性的映射
- 具体实现
package org.wjk.utils.constant;
public class JedisPoolDefaultProperties
{
public static final int DEFAULT_MAX_TOTAL = 8;
public static final int DEFAULT_MAX_IDLE = 8;
public static final int DEFAULT_MIN_IDLE = 0;
public static final String DEFAULT_HOST = "localhost";
public static final int DEFAULT_PORT = 6379;
public static final int DEFAULT_TIMEOUT = 3000;
public static final String DEFAULT_PASSWORD = "root";
}2.3.6.3 定义redis连接池配置类
package org.wjk.config;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.wjk.properties.JedisPoolProperties;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
@RequiredArgsConstructor
@Slf4j
public class JedisPoolConfiguration
{
private final JedisPoolProperties properties ;
@Bean
public JedisPool jedisPool()
{
log.debug("JedisPool配置信息为{}", properties);
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(properties.getMaxIdle());
jedisPoolConfig.setMaxTotal(properties.getMaxTotal());
jedisPoolConfig.setMinIdle(properties.getMinIdle());
return new JedisPool(jedisPoolConfig, properties.getHost(), properties.getPort(), properties.getTimeout(), properties.getPassword());
}
}2.3.6.4 定义redis模板工具类
package org.wjk.utils.method;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.scheduling.annotation.Async;
import org.wjk.exception.ExceptionSpec;
import org.wjk.exception.XfNonDbOperationException;
import org.wjk.properties.JedisConfigProperties;
import org.wjk.utils.constant.JedisStoreKey;
import org.wjk.utils.constant.ThreadPoolDefaultProperties;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Set;
@RequiredArgsConstructor
@Slf4j
public class JedisTemplate
{
private final JedisPool pool;
private final JedisConfigProperties properties;
@Async(ThreadPoolDefaultProperties.DEFAULT_POOL_NAME)
public void setEx(String key, String content, long expiredTime)
{
Jedis jedis = pool.getResource();
String res = jedis.setex(key, expiredTime, content);
log.debug("JedisTemplate::setEx() is invoked, invoked result is {}", res);
jedis.close();
}
public Boolean exist(String key)
{
Jedis jedis = pool.getResource();
Boolean exists = jedis.exists(key);
jedis.close();
return exists;
}
/*该方法当前无用*/
public Object get(String key, Class<?> clazz)
{
/*以下语句:实现了通过的Json字符向对象的反序列化*/
try(Jedis jedis = pool.getResource())
{
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return mapper.readValue(jedis.get(key), clazz);
} catch (JsonProcessingException e)
{
log.debug("JedisTemplate::get(String, Class<?>)方法反序列化结果抛出异常,具体信息为 {}", e.getMessage());
throw new XfNonDbOperationException(ExceptionSpec.SYSTEM_ERROR);
}
}
public Object get(String key)
{
try(Jedis jedis = pool.getResource())
{
return XfSerializeResult.deserialize(jedis.get(key.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e)
{
log.debug("JedisTemplate::get(String)方法反序列化时,抛出异常,具体信息为{}", e.getMessage());
throw new XfNonDbOperationException(ExceptionSpec.SYSTEM_ERROR);
}
}
public String getStringRes(String key)
{
Jedis jedis = pool.getResource();
String res = jedis.get(key);
jedis.close();
return res;
}
public boolean existFlush(String keyPrefix)
{
String [] childrenKeys = keyPrefix.split("::");
Jedis jedis = pool.getResource();
boolean res = Arrays.stream(childrenKeys).anyMatch((item) -> jedis.exists(item + JedisStoreKey.FLUSH_CACHE_SUFFIX));
res = res || jedis.exists(keyPrefix);
log.debug("校验当前key:{}对应的数据是否被刷新!, 结果为:{}", Arrays.toString(childrenKeys), res);
jedis.close();
return res;
}
@Async(ThreadPoolDefaultProperties.DEFAULT_POOL_NAME)
public void setEx(String key, Object result, long expiredTime, String keyPrefix)
{
if(!existFlush(keyPrefix))
{
expiredTime = expiredTime + RandomUtils.nextInt(0, 24 * 3600);
log.debug("当前缓存设置的过期时间为:{}", expiredTime);
/*以下语句实现了对象向Json串的序列化:*/
/*try(Jedis jedis = pool.getResource())
{
String res = jedis.setex(key, expiredTime, new ObjectMapper().writeValueAsString(result));
log.debug("JedisTemplate::setEx(String, Object, long, String) 被调用执行缓存,结果为{}", res);
} catch (JsonProcessingException e)
{
log.debug("JedisTemplate::setEx(String, Object, long, String)方法序列化Json时抛出异常,具体信息为 {}", e.getMessage());
throw new XfNonDbOperationException(ExceptionSpec.SYSTEM_ERROR);
}*/
try(Jedis jedis = pool.getResource())
{
String res = jedis.setex(key.getBytes(StandardCharsets.UTF_8), expiredTime, XfSerializeResult.serialize(result));
log.debug("JedisTemplate::setEx(String, Object, long, String) 被调用执行缓存,结果为{}", res);
} catch (IOException e)
{
log.debug("JedisTemplate::setEx(String, Object, long, String)方法序列化时抛出异常,具体信息为 {}", e.getMessage());
throw new XfNonDbOperationException(ExceptionSpec.SYSTEM_ERROR);
}
}
}
@Async(ThreadPoolDefaultProperties.DEFAULT_POOL_NAME)
public void deleteFailure(String username)
{
Jedis jedis = pool.getResource();
Long delRes = jedis.del(JedisStoreKey.FAILURE_COUNTS + "::" + username);
log.debug("删除用户 {} 登录失败次数,结果为 {}", username, delRes);
jedis.close();
}
public void increaseFailureCount(String key)
{
Jedis jedis = pool.getResource();
Long incr = jedis.incr(key);
log.debug("JedisTemplate::increaseFailureCount(String)被调用,当前用户 {} 登录失败次数增加1,结果为 {}", key, incr);
jedis.close();
}
public void deleteCache(String key)
{
key = key.substring(0, key.indexOf("::["));
Jedis jedis = pool.getResource();
Set<String> keys = jedis.keys("*" + key + "*");
Long del = jedis.del(keys.toArray(new String[0]));
log.debug("JedisTemplate::deleteCache(string)被调用,删除与{}相关的数据, 结果为{}", key, del);
jedis.setex(key + JedisStoreKey.FLUSH_CACHE_SUFFIX, properties.getKeyExpired(), String.valueOf(true));
jedis.close();
}
}2.3.6.5 定义redis工具类集启动注解
package org.wjk.annotation;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Import;
import org.wjk.config.JedisPoolConfiguration;
import org.wjk.properties.JedisPoolProperties;
import org.wjk.utils.method.JedisTemplate;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@EnableConfigurationProperties(JedisPoolProperties.class)
@Import({JedisPoolConfiguration.class, JedisTemplate.class})
public @interface EnableJedis
{
}2.3.6.6 定义支持redis缓存操作的注解
// 注解一:用于获取或保存缓存
package org.wjk.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheRequire
{
String value();
}
// 注解二:用于新增或删除数据后删除对应的缓存
package org.wjk.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheFlushSpecKeys
{
String value();
}
// 注解三:用于更新数据后删除对应的缓存
package org.wjk.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheFlushUpdated
{
String value();
}2.3.6.7 定义实现redis缓存操作的切面类
package org.wjk.aspect;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.wjk.annotation.CacheFlushSpecKeys;
import org.wjk.annotation.CacheRequire;
import org.wjk.utils.method.JedisTemplate;
import java.util.Arrays;
@Component
@Aspect
@Slf4j
@RequiredArgsConstructor
@Order(10)
public class CacheAspect
{
private final JedisTemplate jedisTemplate;
@Around("@annotation(rqCache)")
public Object getResult(ProceedingJoinPoint pjp, CacheRequire rqCache) throws Throwable
{
String key = rqCache.value() + "::" + Arrays.toString(pjp.getArgs());
if(jedisTemplate.exist(key))
{
/*以下方法体用于获取目标方法的返回值类型*/
/*Signature signature = pjp.getSignature();
Class<?> returnType = Map.class;
if(signature instanceof MethodSignature)
{
returnType = ((MethodSignature) signature).getMethod().getReturnType();
}
log.debug("目标方法的返回值类型为:{}", returnType);*/
log.debug("目录方法 {} 的返回结果缓存命中,从redis中获取相关缓存数据", pjp.getSignature().getName());
return jedisTemplate.get(key);
}
else
{
log.debug("目录方法 {} 的返回结果无缓存结果,从数据库获取相关数据", pjp.getSignature().getName());
Object res = pjp.proceed();
jedisTemplate.setEx(key, res, 30L * 3600 * 24, rqCache.value());
return res;
}
}
}2.3.7 定义响应JSON的vo类
package org.wjk.entity.vo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class XfReturnResponse
{
public static void resJson(HttpServletResponse response, ResponseResult<Object> res) throws IOException
{
response.setContentType("application/json;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print(new ObjectMapper().writeValueAsString(res));
writer.close();
}
}2.3.8 定义封装类序列化与反序列化工具类
package org.wjk.utils.method;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotNull;
import java.io.*;
@Validated
public final class XfSerializeResult
{
public static byte[] serialize(@NotNull Object source) throws IOException
{
try(ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oop = new ObjectOutputStream(baos))
{
oop.writeObject(source);
return baos.toByteArray();
}
}
public static Object deserialize(@NotNull byte[] source) throws Exception
{
try(ByteArrayInputStream bais = new ByteArrayInputStream(source); ObjectInputStream oips = new ObjectInputStream(bais))
{
return oips.readObject();
}
}
}2.3.10 定义自定义异常
- 数据库操作异常,该异常将导致数据库回滚
package org.wjk.exception;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Data
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
public class XfDbOperationException extends RuntimeException
{
private static final long serialVersionUID = -1753012888825608525L;
private Integer code;
public XfDbOperationException (ExceptionSpec spec)
{
super(spec.getResMsg());
this.code = spec.getResCode();
}
}- 非数据库操作异常
package org.wjk.exception;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Data
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
public class XfNonDbOperationException extends RuntimeException
{
private static final long serialVersionUID = -1095999755628794111L;
private Integer code;
public XfNonDbOperationException(String message, Integer code)
{
super(message);
this.code = code;
}
public XfNonDbOperationException(ExceptionSpec e)
{
super(e.getResMsg());
this.code = e.getResCode();
}
}2.3.11 自定义异常信息的enum
package org.wjk.exception;
public enum ExceptionSpec
{
SYSTEM_ERROR(5000, "系统内部错误,请稍后重试!"),
ILLEGAL_ARGUMENTS(5001, "您提供的信息无效!请修改后再重试!"),
ILLEGAL_ACCESS(5002, "您使用的客户端无效,请使用官方客户端!"),
USERNAME_NOT_FOUND(5002, "用户名或密码错误,请查证后重新登录!"),
PASSWORD_ERROR(5002, "用户名或密码错误,请查证后重新登录!"),
USER_IS_LOCK(5002, "当前用户已被锁定,请联系系统管理员!"),
USER_IS_DISABLE(5002, "当前用户尚未启用,请联系系统管理员!"),
CAPTCHA_EXPIRED(5002, "验证码错误或已过期,请点击刷新或重新输入!"),
ILLEGAL_ACCESS_TOKEN(5003, "你的登录无效,请使用官方客户端重新登录!"),
ILLEGAL_REFRESH_TOKEN(5004, "您的登录已过期,请重新登录!"),
NOT_HAS_PERMISSION(5005, "您无访问该资源的权限,请联系系统管理员!")
;
private final Integer resCode;
private final String resMsg;
ExceptionSpec(Integer resCode, String resMsg)
{
this.resCode = resCode;
this.resMsg = resMsg;
}
public Integer getResCode()
{
return resCode;
}
public String getResMsg()
{
return resMsg;
}
}2.3.13 所有模块公用全局异常处理
package org.wjk.exception;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.wjk.entity.vo.ResponseResult;
import javax.validation.ConstraintViolationException;
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler
{
@ExceptionHandler(ConstraintViolationException.class)
public ResponseResult<Object> xfConstraintViolationExceptionHandler(ConstraintViolationException e)
{
log.debug("程序执行期间ConstraintViolationException被抛出,异常信息为{}", e.getMessage());
return ResponseResult.failure(ExceptionSpec.ILLEGAL_ARGUMENTS);
}
@ExceptionHandler(XfNonDbOperationException.class)
public ResponseResult<Object> xfNonDbOperationExceptionHandler(XfNonDbOperationException e)
{
return ResponseResult.failure(e.getCode(), e.getMessage());
}
@ExceptionHandler(XfDbOperationException.class)
public ResponseResult<Object> xfIllegalAccessExceptionHandler(XfDbOperationException e)
{
return ResponseResult.failure(e.getCode(), e.getMessage());
}
@ExceptionHandler(IllegalArgumentException.class)
public ResponseResult<?> illegalArgumentExceptionHandler(IllegalArgumentException e)
{
return ResponseResult.failure(ExceptionSpec.ILLEGAL_ARGUMENTS);
}
}3 功能模块
3.1权限子系统
- 主要包括:
- 菜单管理模块
- 用户管理模块
- 权限管理模块
- 角色管理模块
- 认证模块
3.2业务子系统
- 主要包括:
- 行政事务模块
- 机构管理
- 岗位管理
- 人力资源模块
- 员工信息管理
















