02旭锋集团运营管理平台项目搭建

1 客户端初始化

1.1 创建客户端项目

  • 项目结构如下:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VB9cEhIa-1668743904830)(https://secure2.wostatic.cn/static/eNUiMxvxX2waSgq29eCKLT/image.png?auth_key=1668742182-5wHqTR37FMiTLHJokgjKSE-0-99774e7ae9de5a63f726f1b7e7e7a8db)]

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

1.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 axios

1.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 服务器端初始化

  1. 系统开发环境版本
windowsjdkspringbootspringcloudspringcloudalibabanacossentinalseatarocketMq
win111.82.6.32021.0.12021.0.1.02.0.31.8.31.4.24.9.2

2.1 创建统一父工程

该工程主要用于定义管理系统组件依赖版本。并引入各子系统共同的依赖。具体实现

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

    <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://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">
    <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=1

2.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://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">
    <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>&lt;!&ndash;provided 表示此依赖仅在全阶段有效&ndash;&gt;-->
        </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 定义自定义异常

  1. 数据库操作异常,该异常将导致数据库回滚
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();
    }
}

  1. 非数据库操作异常
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业务子系统

  • 主要包括:
    • 行政事务模块
      • 机构管理
      • 岗位管理
    • 人力资源模块
      • 员工信息管理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值