Commit 7bbe84e0 authored by liuyang's avatar liuyang

init project

parents
# Created by https://www.gitignore.io/api/eclipse,intellij,maven
### Eclipse ###
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders
# Eclipse Core
.project
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# PyDev specific (Python IDE for Eclipse)
*.pydevproject
# CDT-specific (C/C++ Development Tooling)
.cproject
# JDT-specific (Eclipse Java Development Tools)
.classpath
# Java annotation processor (APT)
.factorypath
# PDT-specific (PHP Development Tools)
.buildpath
# sbteclipse plugin
.target
# Tern plugin
.tern-project
# TeXlipse plugin
.texlipse
# STS (Spring Tool Suite)
.springBeans
# Code Recommenders
.recommenders/
### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
.idea/**/workspace.xml
.idea/**/tasks.xml
# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
# Gradle:
.idea/**/gradle.xml
.idea/**/libraries
# Mongo Explorer plugin:
.idea/**/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### Intellij Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
.DS_Store
.idea
*.iml
# modules.xml
# .idea/misc.xml
# *.ipr
### Maven ###
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
### redis ###
*.rdb
#*.jpg
#*.png
# Exclude maven wrapper
!/.mvn/wrapper/maven-wrapper.jar
# End of https://www.gitignore.io/api/eclipse,intellij,maven
*.iml
*.class
target/*
<?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>
<artifactId>online-edu-backend</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
</parent>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<mysql.version>5.1.47</mysql.version>
<lombok.version>1.18.2</lombok.version>
<mybatis.plus.version>3.3.1</mybatis.plus.version>
<druid.springboot.version>1.1.10</druid.springboot.version>
<redis.springboot.version>2.1.0.RELEASE</redis.springboot.version>
<thumbnailator.coobird.version>0.4.8</thumbnailator.coobird.version>
<mybatis.typehandlers.jsr.version>1.0.2</mybatis.typehandlers.jsr.version>
<commons-lang3.version>3.8.1</commons-lang3.version>
<commons-codec.version>1.11</commons-codec.version>
<auth0.jwt.version>3.4.1</auth0.jwt.version>
<jackson.jsr310.version>2.9.7</jackson.jsr310.version>
<guava.version>27.0-jre</guava.version>
<xstream.version>1.4.11.1</xstream.version>
<httpclient.version>4.5.6</httpclient.version>
<sanselan.version>0.97-incubator</sanselan.version>
<jedis.version>2.9.0</jedis.version>
<org.bouncycastle.version>1.60</org.bouncycastle.version>
<google.zxing.version>3.3.3</google.zxing.version>
<common.io.version>2.6</common.io.version>
<java.jwt.version>3.7.0</java.jwt.version>
<product.version>1.0.0-SNAPSHOT</product.version>
<docker.plugin.version>1.1.1</docker.plugin.version>
<baidu.aip.version>4.11.3</baidu.aip.version>
<shiro.version>1.4.0</shiro.version>
<fastjson.version>1.2.60</fastjson.version>
<weixin-java.version>3.6.0</weixin-java.version>
<hutool.version>5.3.5</hutool.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis.plus.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>${mybatis.plus.version}</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.springboot.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>${redis.springboot.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-typehandlers-jsr310</artifactId>
<version>${mybatis.typehandlers.jsr.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${common.io.version}</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>${auth0.jwt.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.jsr310.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>${xstream.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.sanselan/sanselan -->
<dependency>
<groupId>org.apache.sanselan</groupId>
<artifactId>sanselan</artifactId>
<version>${sanselan.version}</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>redis.clients</groupId>-->
<!-- <artifactId>jedis</artifactId>-->
<!-- <type>jar</type>-->
<!-- <version>${jedis.version}</version>-->
<!-- </dependency>-->
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.github.binarywang</groupId>-->
<!-- <artifactId>weixin-java-mp</artifactId>-->
<!-- <version>${weixin-java.version}</version>-->
<!-- </dependency>-->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-miniapp</artifactId>
<version>3.7.0</version>
<exclusions>
<exclusion>
<artifactId>bcpkix-jdk15on</artifactId>
<groupId>org.bouncycastle</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.61</version>
</dependency>
<!-- 二维码图片 -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.3.0</version>
</dependency>
<!-- excel tool -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
<version>1.65</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.1.3.RELEASE</version>
<configuration>
<layout>ZIP</layout>
<classifier>all</classifier>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>${docker.plugin.version}</version>
<configuration>
<imageName>${project.artifactId}:${project.version}</imageName>
<dockerDirectory>${project.basedir}/src/main/docker</dockerDirectory>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>*.jar</include>
</resource>
</resources>
<buildArgs>
<JAR_FILE>${project.build.finalName}-all.jar</JAR_FILE>
</buildArgs>
</configuration>
<executions>
<execution>
<phase>install</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
FROM anapsix/alpine-java:8u202b08_server-jre_unlimited
ARG JAR_FILE
ADD ${JAR_FILE} $WORKDIR
RUN mv ${JAR_FILE} app.jar
COPY docker-entrypoint.sh /usr/share/app/docker-entrypoint.sh
RUN chmod +x /usr/share/app/docker-entrypoint.sh
ENV MYSQL_URL tcp://mysql:3306
ENV WAIT_MYSQL_TIMEOUT 30s
ENV JAVA_OPTIONS "-Xms256m -Xmx512m -Dfile.encoding=UTF-8"
ENV OVERRIDE_PROP ""
ENTRYPOINT ["/usr/share/app/docker-entrypoint.sh"]
EXPOSE 80
#!/bin/bash
set -ex
java $JAVA_OPTIONS -jar app.jar $OVERRIDE_PROP
package com.qkdata;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.retry.annotation.EnableRetry;
@SpringBootApplication
@ComponentScan(basePackages = {
"com.qkdata"
})
@ServletComponentScan
@EnableCaching
@EnableRetry
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
package com.qkdata.biz.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
public enum AccountStatusEnum {
DISABLE(0, "禁用"), ENABLE(1, "启用"),UNACTIVATE(2,"未激活");
@EnumValue
private Integer value;
private String text;
AccountStatusEnum(Integer value, String text) {
this.value = value;
this.text = text;
}
}
package com.qkdata.biz.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
public enum MenuTypeEnum {
FOLDER(0,"目录"),MENU(1,"菜单"),BUTTON(3,"按扭");
MenuTypeEnum(Integer value,String text){
this.value = value;
this.text = text;
}
@EnumValue
private Integer value;
private String text;
}
package com.qkdata.biz.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
public enum SysConfigStatusEnum {
HIDDEN(0, "隐藏"), SHOW(1, "显示");
@EnumValue
private Integer value;
private String text;
SysConfigStatusEnum(Integer value, String text) {
this.value = value;
this.text = text;
}
}
package com.qkdata.biz.sys.controller;
import com.qkdata.biz.sys.entity.SysConfigPO;
import com.qkdata.biz.sys.service.SysConfigService;
import com.qkdata.biz.sys.vo.QueryConfigModel;
import com.qkdata.common.annotation.SysLog;
import com.qkdata.common.base.model.PageResult;
import com.qkdata.common.base.model.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
@Api(tags = "字典表")
@RestController
@RequestMapping("/api/sys/config")
public class SysConfigController {
@Autowired
private SysConfigService sysConfigService;
@ApiOperation("查询列表")
@PostMapping("/list")
public PageResult<SysConfigPO> list(@RequestBody QueryConfigModel queryConfigModel){
return sysConfigService.queryPageList(queryConfigModel);
}
@ApiOperation("获取单条信息")
@GetMapping("/info/{id}")
public Result<SysConfigPO> info(@PathVariable Long id){
return Result.succeed(sysConfigService.getById(id));
}
@SysLog("保存字典数据")
@ApiOperation("保存")
@PostMapping("/save")
public Result<String> save(@RequestBody SysConfigPO sysConfigPO){
sysConfigService.save(sysConfigPO);
return Result.succeed("ok");
}
@SysLog("修改字典数据")
@ApiOperation("更新")
@PostMapping("/update")
public Result<String> update(@RequestBody SysConfigPO sysConfigPO){
sysConfigService.updateById(sysConfigPO);
return Result.succeed("ok");
}
@SysLog("删除字典数据")
@ApiOperation("删除")
@PostMapping("/delete")
public Result<String> delete(@RequestBody Long[] ids){
sysConfigService.removeByIds(Arrays.asList(ids));
return Result.succeed("ok");
}
}
package com.qkdata.biz.sys.controller;
import com.qkdata.biz.sys.entity.SysLogPO;
import com.qkdata.biz.sys.service.SysLogService;
import com.qkdata.biz.sys.vo.QueryLogModel;
import com.qkdata.common.base.model.PageResult;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Api(tags = "审计日志")
@RestController
@RequestMapping("/api/sys/log")
public class SysLogController {
@Autowired
private SysLogService sysLogService;
@PostMapping("/list")
public PageResult<SysLogPO> list(@RequestBody QueryLogModel queryLogModel){
return sysLogService.queryPageList(queryLogModel);
}
}
package com.qkdata.biz.sys.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.qkdata.biz.sys.service.ShiroService;
import com.qkdata.biz.sys.vo.LoginModel;
import com.qkdata.biz.sys.vo.LoginUserInfo;
import com.qkdata.common.annotation.SysLog;
import com.qkdata.common.base.model.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@Api(tags = "系统登陆")
@RestController
@RequestMapping("/api/sys")
public class SysLoginController {
@Autowired
private ShiroService shiroService;
@ApiOperation("登陆")
@SysLog("登陆")
@PostMapping("/login")
public Result<LoginUserInfo> login(@RequestBody @Valid LoginModel loginModel) throws JsonProcessingException {
return Result.succeed(shiroService.login(loginModel.getUsername(),loginModel.getPassword()));
}
@ApiOperation("登出")
@SysLog("登出")
@GetMapping("/logout")
public Result<String> logout(){
SecurityUtils.getSubject().logout();
return Result.succeed("ok");
}
}
package com.qkdata.biz.sys.controller;
import com.qkdata.biz.sys.entity.SysMenuPO;
import com.qkdata.biz.sys.service.ShiroService;
import com.qkdata.biz.sys.service.SysMenuService;
import com.qkdata.biz.sys.vo.SysNavModel;
import com.qkdata.common.annotation.SysLog;
import com.qkdata.common.base.exception.BusinessException;
import com.qkdata.common.base.model.Result;
import com.qkdata.common.util.UserContext;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Set;
@Api(tags = "菜单管理")
@RestController
@RequestMapping("/api/sys/menu")
public class SysMenuController {
@Autowired
private SysMenuService sysMenuService;
@Autowired
private ShiroService shiroService;
@ApiOperation("获取当前用户的菜单")
@GetMapping("/nav")
public Result<SysNavModel> nav(){
List<SysMenuPO> menuList = sysMenuService.getUserMenuList(UserContext.getUserId());
Set<String> permissions = shiroService.getUserPermissions(UserContext.getUserId());
SysNavModel navDTO = new SysNavModel(menuList,permissions);
return Result.succeed(navDTO);
}
@ApiOperation("获取全部菜单列表")
@GetMapping("/list")
@RequiresPermissions("sys:menu:list")
public Result<List<SysMenuPO>> list(){
List<SysMenuPO> menuList = sysMenuService.list();
for (SysMenuPO menuPO : menuList){
SysMenuPO parentMenu = sysMenuService.getById(menuPO.getParentId());
if (parentMenu != null){
menuPO.setParentName(parentMenu.getName());
}
}
return Result.succeed(menuList);
}
@ApiOperation("获取不包含按扭的全部菜单")
@GetMapping("/select")
@RequiresPermissions("sys:menu:select")
public Result<List<SysMenuPO>> select(){
List<SysMenuPO> menuList = sysMenuService.queryNotButtonList();
//添加顶级菜单
SysMenuPO root = new SysMenuPO();
root.setId(0L);
root.setName("一级菜单");
root.setParentId(-1L);
root.setOpen(true);
menuList.add(root);
return Result.succeed(menuList);
}
@ApiOperation("获取单个菜单详细信息")
@GetMapping("/info/{id}")
@RequiresPermissions("sys:menu:info")
public Result<SysMenuPO> info(@PathVariable Long id){
SysMenuPO sysMenuPO = sysMenuService.getById(id);
return Result.succeed(sysMenuPO);
}
@ApiOperation("保存菜单信息")
@SysLog("保存菜单信息")
@PostMapping("/save")
@RequiresPermissions("sys:menu:save")
public Result<String> save(@RequestBody SysMenuPO sysMenuPO){
sysMenuService.save(sysMenuPO);
return Result.succeed("ok");
}
@ApiOperation("修改菜单信息")
@SysLog("修改菜单信息")
@PostMapping("/update")
@RequiresPermissions("sys:menu:update")
public Result<String> update(@RequestBody SysMenuPO sysMenuPO){
sysMenuService.updateById(sysMenuPO);
return Result.succeed("ok");
}
@ApiOperation("删除菜单")
@SysLog("删除菜单")
@GetMapping("/delete/{id}")
@RequiresPermissions("sys:menu:delete")
public Result<String> delete(@PathVariable Long id){
if (id <= 31){
throw new BusinessException("系统菜单不能删除");
}
//判断是否有子菜单或按钮
List<SysMenuPO> menuList = sysMenuService.queryListParentId(id);
if(menuList.size() > 0){
throw new BusinessException("请先删除子菜单或按钮");
}
sysMenuService.removeById(id);
return Result.succeed("ok");
}
}
package com.qkdata.biz.sys.controller;
import com.qkdata.biz.sys.entity.SysRolePO;
import com.qkdata.biz.sys.service.SysRoleService;
import com.qkdata.biz.sys.vo.QueryRoleModel;
import com.qkdata.biz.sys.vo.SysRoleModel;
import com.qkdata.common.annotation.SysLog;
import com.qkdata.common.base.model.PageResult;
import com.qkdata.common.base.model.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
@Api(tags = "角色管理")
@RestController
@RequestMapping("/api/sys/role")
public class SysRoleController {
@Autowired
private SysRoleService sysRoleService;
/**
* 角色列表
*/
@ApiOperation("获取全部角色列表")
@GetMapping("/select")
@RequiresPermissions("sys:role:select")
public List<SysRolePO> select(){
return sysRoleService.list();
}
@ApiOperation("查询角色列表")
@PostMapping("/list")
@RequiresPermissions("sys:role:list")
public PageResult<SysRolePO> list(@RequestBody QueryRoleModel queryRoleModel){
return sysRoleService.queryPageList(queryRoleModel);
}
@ApiOperation("获取某一角色信息")
@GetMapping("/info/{id}")
@RequiresPermissions("sys:role:info")
public Result<SysRoleModel> info(@PathVariable Long id){
return Result.succeed(sysRoleService.getRoleInfo(id));
}
@ApiOperation("保存某一角色信息")
@SysLog("保存某一角色信息")
@PostMapping("/save")
@RequiresPermissions("sys:role:save")
public Result<String> save(@RequestBody @Validated SysRoleModel sysRoleModel){
sysRoleService.saveRole(sysRoleModel);
return Result.succeed("ok");
}
@ApiOperation("修改某一角色信息")
@SysLog("修改某一角色信息")
@PostMapping("/update")
@RequiresPermissions("sys:role:update")
public Result<String> update(@RequestBody @Validated SysRoleModel sysRoleModel){
sysRoleService.updateRole(sysRoleModel);
return Result.succeed("ok");
}
@ApiOperation("删除角色信息")
@SysLog("删除角色信息")
@PostMapping("/delete")
@RequiresPermissions("sys:role:delete")
public Result<String> delete(@RequestBody Long[] ids){
sysRoleService.removeByIds(Arrays.asList(ids));
return Result.succeed("ok");
}
}
package com.qkdata.biz.sys.controller;
import com.qkdata.biz.sys.entity.SysUserPO;
import com.qkdata.biz.sys.service.SysUserService;
import com.qkdata.biz.sys.vo.PasswordModel;
import com.qkdata.biz.sys.vo.QueryUserModel;
import com.qkdata.biz.sys.vo.SysUserModel;
import com.qkdata.common.annotation.SysLog;
import com.qkdata.common.base.exception.BusinessException;
import com.qkdata.common.base.model.PageResult;
import com.qkdata.common.base.model.Result;
import com.qkdata.common.constants.AddGroup;
import com.qkdata.common.constants.UpdateGroup;
import com.qkdata.common.util.UserContext;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.Arrays;
@Api(tags = "用户管理")
@RestController
@RequestMapping("/api/sys/user")
public class SysUserController {
@Autowired
private SysUserService sysUserService;
@ApiOperation("查询用户列表")
@PostMapping("/list")
@RequiresPermissions("sys:user:list")
public PageResult<SysUserModel> list(@RequestBody QueryUserModel queryUserModel){
return sysUserService.queryPageList(queryUserModel);
}
@ApiOperation("获取当前用户信息")
@GetMapping("/info")
public Result<SysUserModel> info(){
SysUserPO userPO = UserContext.getUser();
SysUserModel dto = new SysUserModel();
BeanUtils.copyProperties(userPO,dto);
dto.setRoleIdList(sysUserService.queryRoleIdList(userPO.getId()));
return Result.succeed(dto);
}
@ApiOperation("获取某个用户信息")
@GetMapping("/info/{id}")
@RequiresPermissions("sys:user:info")
public Result<SysUserModel> infoById(@PathVariable Long id){
SysUserPO sysUserPO = sysUserService.getById(id);
SysUserModel dto = new SysUserModel();
BeanUtils.copyProperties(sysUserPO,dto);
dto.setRoleIdList(sysUserService.queryRoleIdList(id));
return Result.succeed(dto);
}
@ApiOperation("保存用户信息")
@SysLog("保存用户信息")
@PostMapping("/save")
@RequiresPermissions("sys:user:save")
public Result<String> save(@RequestBody @Validated(AddGroup.class) SysUserModel sysUserModel){
sysUserService.saveUser(sysUserModel);
return Result.succeed("ok");
}
@ApiOperation("修改用户信息")
@SysLog("修改用户信息")
@PostMapping("/update")
@RequiresPermissions("sys:user:update")
public Result<String> update(@RequestBody @Validated(UpdateGroup.class) SysUserModel sysUserModel){
sysUserService.updateUser(sysUserModel);
return Result.succeed("ok");
}
/**
* 修改登录用户密码
*/
@SysLog("修改当前用户密码")
@ApiOperation("修改当前用户密码")
@PostMapping("/password")
public Result<String> password(@RequestBody @Valid PasswordModel form){
//更新密码
sysUserService.updatePassword(UserContext.getUser(), form.getPassword(), form.getNewPassword());
return Result.succeed("ok");
}
/**
* 删除用户
*/
@ApiOperation("删除用户")
@SysLog("删除用户")
@PostMapping("/delete")
@RequiresPermissions("sys:user:delete")
public Result<String> delete(@RequestBody Long[] userIds){
if(ArrayUtils.contains(userIds, 1L)){
throw new BusinessException("系统管理员不能删除");
}
if(ArrayUtils.contains(userIds, UserContext.getUserId())){
throw new BusinessException("当前用户不能删除");
}
sysUserService.removeUsers(Arrays.asList(userIds));
return Result.succeed("ok");
}
}
package com.qkdata.biz.sys.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.qkdata.biz.enums.SysConfigStatusEnum;
import com.qkdata.common.base.entity.BasePO;
import lombok.Data;
@Data
@TableName("sys_config")
public class SysConfigPO extends BasePO {
private String paramKey;
private String paramValue;
private SysConfigStatusEnum status;
private String remark;
}
package com.qkdata.biz.sys.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.qkdata.common.base.entity.BasePO;
import lombok.Data;
@Data
@TableName("sys_log")
public class SysLogPO extends BasePO {
private String username;
private String operation;
private String method;
private String params;
private String ip;
private Long time;
}
package com.qkdata.biz.sys.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.qkdata.biz.enums.MenuTypeEnum;
import com.qkdata.common.base.entity.BasePO;
import lombok.Data;
import java.util.List;
@Data
@TableName("sys_menu")
public class SysMenuPO extends BasePO {
private Long parentId;
/**
* 非数据库中字段
*/
@TableField(exist = false)
private String parentName;
private String name;
private String url;
private String perms;
private MenuTypeEnum type;
private String icon;
private Integer orderNum;
/**
* ztree属性
*/
@TableField(exist = false)
private Boolean open;
@TableField(exist = false)
private List<?> list;
}
package com.qkdata.biz.sys.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("sys_role_menu")
public class SysRoleMenuPO {
@TableId
private Long id;
private Long roleId;
private Long menuId;
}
package com.qkdata.biz.sys.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.qkdata.common.base.entity.BasePO;
import lombok.Data;
@Data
@TableName("sys_role")
public class SysRolePO extends BasePO {
private String name;
private String code;
}
package com.qkdata.biz.sys.entity;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.qkdata.biz.enums.AccountStatusEnum;
import com.qkdata.common.base.entity.BasePO;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
@EqualsAndHashCode(callSuper = true)
@Data
@TableName( "sys_user")
public class SysUserPO extends BasePO {
private String username;
private String password;
private String salt;
private String nickName;
private Date lastLoginTime;
private Date activateTime;
private AccountStatusEnum status;
@TableLogic
private Boolean isDel;
}
package com.qkdata.biz.sys.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("sys_user_role")
public class SysUserRolePO {
@TableId
private Long id;
private Long userId;
private Long roleId;
}
package com.qkdata.biz.sys.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qkdata.biz.sys.entity.SysConfigPO;
import com.qkdata.biz.sys.vo.QueryConfigModel;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface SysConfigMapper extends BaseMapper<SysConfigPO> {
List<SysConfigPO> queryPageList(Page<SysConfigPO> page, @Param("p") QueryConfigModel queryConfigModel);
}
package com.qkdata.biz.sys.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qkdata.biz.sys.entity.SysLogPO;
import com.qkdata.biz.sys.vo.QueryLogModel;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface SysLogMapper extends BaseMapper<SysLogPO> {
List<SysLogPO> queryPageList(Page<SysLogPO> page, @Param("p") QueryLogModel queryLogModel);
}
package com.qkdata.biz.sys.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qkdata.biz.sys.entity.SysMenuPO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface SysMenuMapper extends BaseMapper<SysMenuPO> {
List<SysMenuPO> queryNotButtonList();
}
package com.qkdata.biz.sys.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qkdata.biz.sys.entity.SysRolePO;
import com.qkdata.biz.sys.vo.QueryRoleModel;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface SysRoleMapper extends BaseMapper<SysRolePO> {
List<SysRolePO> queryPageList(Page<SysRolePO> page, @Param("p") QueryRoleModel queryRoleModel);
}
package com.qkdata.biz.sys.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qkdata.biz.sys.entity.SysRoleMenuPO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface SysRoleMenuMapper extends BaseMapper<SysRoleMenuPO> {
List<Long> selectMenuIdsByRoleId(Long roleId);
}
package com.qkdata.biz.sys.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qkdata.biz.sys.entity.SysUserPO;
import com.qkdata.biz.sys.vo.QueryUserModel;
import com.qkdata.biz.sys.vo.SysUserModel;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface SysUserMapper extends BaseMapper<SysUserPO> {
List<String> queryAllPerms(Long userId);
List<SysUserModel> queryPageList(Page<SysUserModel> page, @Param("p") QueryUserModel queryUserModel);
List<Long> queryAllMenuId(Long userId);
}
package com.qkdata.biz.sys.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qkdata.biz.sys.entity.SysUserRolePO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface SysUserRoleMapper extends BaseMapper<SysUserRolePO> {
List<Long> queryRoleIdList(Long userId);
}
package com.qkdata.biz.sys.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import com.qkdata.biz.enums.AccountStatusEnum;
import com.qkdata.biz.sys.entity.SysMenuPO;
import com.qkdata.biz.sys.entity.SysUserPO;
import com.qkdata.biz.sys.vo.LoginUserInfo;
import com.qkdata.common.base.exception.BusinessException;
import com.qkdata.common.jwt.JWTService;
import com.qkdata.common.oauth.AuthorizedUser;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.*;
@Service
public class ShiroService {
@Autowired
private SysUserService sysUserService;
@Autowired
private SysMenuService sysMenuService;
@Autowired
private ObjectMapper objectMapper;
@Autowired
private JWTService jwtService;
public Set<String> getUserPermissions(Long userId) {
List<String> permsList;
//系统管理员,拥有最高权限
if(userId == 1){
List<SysMenuPO> menuList = sysMenuService.list();
permsList = new ArrayList<>(menuList.size());
for(SysMenuPO menu : menuList){
permsList.add(menu.getPerms());
}
}else{
permsList = sysUserService.queryAllPerms(userId);
}
//用户权限列表
Set<String> permsSet = new HashSet<>();
for(String perms : permsList){
if(StringUtils.isEmpty(perms)){
continue;
}
permsSet.addAll(Arrays.asList(perms.trim().split(",")));
}
return permsSet;
}
public LoginUserInfo login(String username, String password) throws JsonProcessingException {
SysUserPO userPO = getUserByUserName(username);
if(userPO == null || !userPO.getPassword().equals(new Sha256Hash(password, userPO.getSalt()).toHex())) {
throw new BusinessException("用户名或密码错误");
}
if (userPO.getStatus() == AccountStatusEnum.DISABLE){
throw new BusinessException("帐号已禁用");
}
String token = generatorToken(userPO);
LoginUserInfo loginUser = new LoginUserInfo();
BeanUtils.copyProperties(userPO,loginUser);
loginUser.setAuthorization(token);
return loginUser;
}
private String generatorToken(SysUserPO userPO) throws JsonProcessingException {
AuthorizedUser user = new AuthorizedUser();
user.setUserId(userPO.getId());
user.setUsername(userPO.getUsername());
String userJson = objectMapper.writeValueAsString(user);
Map<String,Object> userClaim = Maps.newConcurrentMap();
userClaim.put("user",userJson);
return jwtService.createJWT(userClaim);
}
public SysUserPO getUserByUserName(String username) {
return sysUserService.getByUsername(username);
}
}
package com.qkdata.biz.sys.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qkdata.biz.sys.entity.SysConfigPO;
import com.qkdata.biz.sys.mapper.SysConfigMapper;
import com.qkdata.biz.sys.vo.QueryConfigModel;
import com.qkdata.common.base.enums.CodeEnum;
import com.qkdata.common.base.model.PageResult;
import com.qkdata.common.base.service.impl.BaseServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class SysConfigService extends BaseServiceImpl<SysConfigMapper, SysConfigPO> {
public PageResult<SysConfigPO> queryPageList(QueryConfigModel queryConfigModel) {
Page<SysConfigPO> page = new Page<>(queryConfigModel.getPageIndex(),queryConfigModel.getPageSize());
List<SysConfigPO> pageList = baseMapper.queryPageList(page,queryConfigModel);
return PageResult.<SysConfigPO>builder().code(CodeEnum.SUCCESS.getCode()).count(page.getTotal()).data(pageList).build();
}
}
package com.qkdata.biz.sys.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qkdata.biz.sys.entity.SysLogPO;
import com.qkdata.biz.sys.mapper.SysLogMapper;
import com.qkdata.biz.sys.vo.QueryLogModel;
import com.qkdata.common.base.enums.CodeEnum;
import com.qkdata.common.base.model.PageResult;
import com.qkdata.common.base.service.impl.BaseServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class SysLogService extends BaseServiceImpl<SysLogMapper, SysLogPO> {
public PageResult<SysLogPO> queryPageList(QueryLogModel queryLogModel) {
Page<SysLogPO> page = new Page<>(queryLogModel.getPageIndex(),queryLogModel.getPageSize());
List<SysLogPO> pageList = baseMapper.queryPageList(page,queryLogModel);
return PageResult.<SysLogPO>builder().code(CodeEnum.SUCCESS.getCode()).count(page.getTotal()).data(pageList).build();
}
}
package com.qkdata.biz.sys.service;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.qkdata.biz.enums.MenuTypeEnum;
import com.qkdata.biz.sys.entity.SysMenuPO;
import com.qkdata.biz.sys.mapper.SysMenuMapper;
import com.qkdata.biz.sys.mapper.SysUserMapper;
import com.qkdata.common.base.service.impl.BaseServiceImpl;
import com.qkdata.common.constants.Constants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class SysMenuService extends BaseServiceImpl<SysMenuMapper, SysMenuPO> {
@Autowired
private SysUserMapper sysUserMapper;
public List<SysMenuPO> getUserMenuList(Long userId) {
List<SysMenuPO> userMenuList = Lists.newArrayList();
//系统管理员,拥有最高权限
if (userId == Constants.SYS_ADMIN_ID){
userMenuList = getAllMenuList(null);
}else {
//用户菜单列表
List<Long> menuIdList = sysUserMapper.queryAllMenuId(userId);
userMenuList = getAllMenuList(menuIdList);
}
return userMenuList;
}
private List<SysMenuPO> getAllMenuList(List<Long> menuIdList) {
//查询根菜单列表
List<SysMenuPO> menuList = queryListParentId(0L, menuIdList);
//递归获取子菜单
getMenuTreeList(menuList, menuIdList);
return menuList;
}
private List<SysMenuPO> getMenuTreeList(List<SysMenuPO> menuList, List<Long> menuIdList) {
List<SysMenuPO> subMenuList = Lists.newArrayList();
for(SysMenuPO entity : menuList){
//目录
if(entity.getType() == MenuTypeEnum.FOLDER){
entity.setList(getMenuTreeList(queryListParentId(entity.getId(), menuIdList), menuIdList));
}
subMenuList.add(entity);
}
return subMenuList;
}
private List<SysMenuPO> queryListParentId(Long parentId, List<Long> menuIdList) {
List<SysMenuPO> menuList = queryListParentId(parentId);
if(menuIdList == null){
return menuList;
}
List<SysMenuPO> userMenuList = new ArrayList<>();
for(SysMenuPO menu : menuList){
if(menuIdList.contains(menu.getId())){
userMenuList.add(menu);
}
}
return userMenuList;
}
public List<SysMenuPO> queryListParentId(Long parentId) {
return list(Wrappers.<SysMenuPO>lambdaQuery().eq(SysMenuPO::getParentId,parentId));
}
public List<SysMenuPO> queryNotButtonList() {
return baseMapper.queryNotButtonList();
}
}
package com.qkdata.biz.sys.service;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.qkdata.biz.sys.entity.SysRoleMenuPO;
import com.qkdata.biz.sys.mapper.SysRoleMenuMapper;
import com.qkdata.common.base.service.impl.BaseServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.List;
@Service
public class SysRoleMenuService extends BaseServiceImpl<SysRoleMenuMapper, SysRoleMenuPO> {
public List<Long> queryMenuIdList(Long roleId) {
return baseMapper.selectMenuIdsByRoleId(roleId);
}
public void saveOrUpdateRoleMenu(Long roleId, List<Long> menuIdList) {
deleteByRoleIds(new Long[]{roleId});
if (CollectionUtils.isEmpty(menuIdList)){
return;
}
for (Long menuId : menuIdList){
SysRoleMenuPO po = new SysRoleMenuPO();
po.setRoleId(roleId);
po.setMenuId(menuId);
save(po);
}
}
private void deleteByRoleIds(Long[] roleIds) {
baseMapper.delete(Wrappers.<SysRoleMenuPO>lambdaQuery().in(SysRoleMenuPO::getRoleId,roleIds));
}
}
package com.qkdata.biz.sys.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qkdata.biz.sys.entity.SysRolePO;
import com.qkdata.biz.sys.mapper.SysRoleMapper;
import com.qkdata.biz.sys.vo.QueryRoleModel;
import com.qkdata.biz.sys.vo.SysRoleModel;
import com.qkdata.common.base.enums.CodeEnum;
import com.qkdata.common.base.exception.BusinessException;
import com.qkdata.common.base.model.PageResult;
import com.qkdata.common.base.service.impl.BaseServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class SysRoleService extends BaseServiceImpl<SysRoleMapper, SysRolePO> {
@Autowired
private SysRoleMenuService sysRoleMenuService;
public PageResult<SysRolePO> queryPageList(QueryRoleModel queryRoleModel) {
Page<SysRolePO> page = new Page<>(queryRoleModel.getPageIndex(),queryRoleModel.getPageSize());
List<SysRolePO> pageList = baseMapper.queryPageList(page,queryRoleModel);
return PageResult.<SysRolePO>builder().code(CodeEnum.SUCCESS.getCode()).count(page.getTotal()).data(pageList).build();
}
public SysRoleModel getRoleInfo(Long id) {
SysRolePO po = getById(id);
if (po == null){
throw new BusinessException("请求错误,未找到对应角色");
}
List<Long> menuIds = sysRoleMenuService.queryMenuIdList(po.getId());
SysRoleModel model = new SysRoleModel();
BeanUtils.copyProperties(po,model);
model.setMenuIdList(menuIds);
return model;
}
@Transactional
public void saveRole(SysRoleModel sysRoleModel) {
SysRolePO po = new SysRolePO();
BeanUtils.copyProperties(sysRoleModel,po);
save(po);
sysRoleMenuService.saveOrUpdateRoleMenu(po.getId(),sysRoleModel.getMenuIdList());
}
@Transactional
public void updateRole(SysRoleModel sysRoleModel) {
SysRolePO po = getById(sysRoleModel.getId());
if (po == null){
throw new BusinessException("请求错误,角色不存在");
}
po.setName(sysRoleModel.getName());
po.setCode(sysRoleModel.getCode());
updateById(po);
sysRoleMenuService.saveOrUpdateRoleMenu(po.getId(),sysRoleModel.getMenuIdList());
}
}
package com.qkdata.biz.sys.service;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.qkdata.biz.sys.entity.SysUserRolePO;
import com.qkdata.biz.sys.mapper.SysUserRoleMapper;
import com.qkdata.common.base.service.impl.BaseServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.List;
@Service
public class SysUserRoleService extends BaseServiceImpl<SysUserRoleMapper, SysUserRolePO> {
public List<Long> queryRoleIdList(Long userId) {
return baseMapper.queryRoleIdList(userId);
}
public void saveOrUpdateUserRole(Long userId, List<Long> roleIdList) {
if (!CollectionUtils.isEmpty(roleIdList)){
//先删除用户与角色关系
deleteByUserId(userId);
//保存用户角色关系
for (Long roleId : roleIdList){
SysUserRolePO po = new SysUserRolePO();
po.setUserId(userId);
po.setRoleId(roleId);
baseMapper.insert(po);
}
}
}
public void deleteByUserId(Long userId){
baseMapper.delete(Wrappers.<SysUserRolePO>lambdaQuery().eq(SysUserRolePO::getUserId,userId));
}
public void deleteByUserIds(List<Long> userIds) {
baseMapper.delete(Wrappers.<SysUserRolePO>lambdaQuery().in(SysUserRolePO::getUserId,userIds));
}
}
package com.qkdata.biz.sys.service;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qkdata.biz.sys.entity.SysUserPO;
import com.qkdata.biz.sys.mapper.SysUserMapper;
import com.qkdata.biz.sys.vo.QueryUserModel;
import com.qkdata.biz.sys.vo.SysUserModel;
import com.qkdata.common.base.enums.CodeEnum;
import com.qkdata.common.base.exception.BusinessException;
import com.qkdata.common.base.model.PageResult;
import com.qkdata.common.base.service.impl.BaseServiceImpl;
import com.qkdata.common.util.UserContext;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.util.List;
@Service
public class SysUserService extends BaseServiceImpl<SysUserMapper, SysUserPO> {
@Autowired
private SysUserRoleService sysUserRoleService;
public SysUserPO getByUsername(String username) {
return baseMapper.selectOne(Wrappers.<SysUserPO>lambdaQuery().eq(SysUserPO::getUsername,username));
}
public List<String> queryAllPerms(Long userId) {
return baseMapper.queryAllPerms(userId);
}
public PageResult<SysUserModel> queryPageList(QueryUserModel queryUserModel) {
Page<SysUserModel> page = new Page<>(queryUserModel.getPageIndex(),queryUserModel.getPageSize());
List<SysUserModel> pageList = baseMapper.queryPageList(page,queryUserModel);
return PageResult.<SysUserModel>builder().code(CodeEnum.SUCCESS.getCode()).count(page.getTotal()).data(pageList).build();
}
public List<Long> queryRoleIdList(Long userId) {
return sysUserRoleService.queryRoleIdList(userId);
}
@Transactional
public void saveUser(SysUserModel sysUserModel) {
SysUserPO po = getByUsername(sysUserModel.getUsername());
if (po != null){
throw new BusinessException("帐号已存在");
}
po = new SysUserPO();
BeanUtils.copyProperties(sysUserModel,po);
po.setSalt(RandomStringUtils.randomAlphanumeric(20));
po.setPassword(new Sha256Hash(sysUserModel.getPassword(), po.getSalt()).toHex());
save(po);
sysUserRoleService.saveOrUpdateUserRole(po.getId(),sysUserModel.getRoleIdList());
}
@Transactional
public void updateUser(SysUserModel sysUserModel) {
SysUserPO po = getById(sysUserModel.getId());
if (po == null){
throw new BusinessException("请求错误,用户不存在");
}
po.setUsername(sysUserModel.getUsername());
if (!StringUtils.isEmpty(sysUserModel.getPassword())){
po.setPassword(new Sha256Hash(sysUserModel.getPassword(),po.getSalt()).toHex());
}
po.setStatus(sysUserModel.getStatus());
updateById(po);
sysUserRoleService.saveOrUpdateUserRole(po.getId(),sysUserModel.getRoleIdList());
}
public void updatePassword(SysUserPO user, String password, String newPassword) {
//sha256加密
String pwd = new Sha256Hash(password, UserContext.getUser().getSalt()).toHex();
if (!user.getPassword().equals(pwd)){
throw new BusinessException("密码错误");
}
//sha256加密
String newPwd = new Sha256Hash(newPassword, UserContext.getUser().getSalt()).toHex();
user.setPassword(newPwd);
updateById(user);
}
@Transactional
public void removeUsers(List<Long> ids) {
removeByIds(ids);
sysUserRoleService.deleteByUserIds(ids);
}
}
package com.qkdata.biz.sys.vo;
import lombok.Data;
import javax.validation.constraints.NotBlank;
@Data
public class LoginModel {
@NotBlank(message = "请填写登陆帐号")
private String username;
@NotBlank(message = "请填写密码")
private String password;
}
package com.qkdata.biz.sys.vo;
import lombok.Data;
@Data
public class LoginUserInfo {
private Long id;
private String username;
private String email;
private String mobile;
private String authorization;
}
package com.qkdata.biz.sys.vo;
import lombok.Data;
import javax.validation.constraints.NotBlank;
@Data
public class PasswordModel {
@NotBlank(message = "原密码不能为空")
private String password;
@NotBlank(message = "新密码不能为空")
private String newPassword;
}
package com.qkdata.biz.sys.vo;
import com.qkdata.common.constants.Constants;
import lombok.Data;
@Data
public class QueryConfigModel {
private Integer pageIndex = Constants.DEFAULT_PAGE;
private Integer pageSize = Constants.DEFAULT_PAGE_SIZE;
private String paramKey;
}
package com.qkdata.biz.sys.vo;
import com.qkdata.common.constants.Constants;
import lombok.Data;
@Data
public class QueryLogModel {
private Integer pageIndex = Constants.DEFAULT_PAGE;
private Integer pageSize = Constants.DEFAULT_PAGE_SIZE;
private String condition;
}
package com.qkdata.biz.sys.vo;
import com.qkdata.common.constants.Constants;
import lombok.Data;
@Data
public class QueryRoleModel {
private Integer pageIndex = Constants.DEFAULT_PAGE;
private Integer pageSize = Constants.DEFAULT_PAGE_SIZE;
private String name;
}
package com.qkdata.biz.sys.vo;
import com.qkdata.common.constants.Constants;
import lombok.Data;
@Data
public class QueryUserModel {
private Integer pageIndex = Constants.DEFAULT_PAGE;
private Integer pageSize = Constants.DEFAULT_PAGE_SIZE;
private String username;
}
package com.qkdata.biz.sys.vo;
import com.qkdata.biz.sys.entity.SysMenuPO;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.List;
import java.util.Set;
@Data
@AllArgsConstructor
public class SysNavModel {
private List<SysMenuPO> menuList;
private Set<String> permissions;
}
package com.qkdata.biz.sys.vo;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.util.List;
@Data
public class SysRoleModel {
private Long id;
@NotBlank(message = "角色名称不能为空")
private String name;
@NotBlank(message = "角色代码不能为空")
private String code;
private List<Long> menuIdList;
}
package com.qkdata.biz.sys.vo;
import com.qkdata.biz.enums.AccountStatusEnum;
import com.qkdata.common.constants.AddGroup;
import com.qkdata.common.constants.UpdateGroup;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.util.List;
@Data
public class SysUserModel {
private Long id;
@NotBlank(message = "用户名不能为空",groups = {AddGroup.class, UpdateGroup.class})
private String username;
@NotBlank(message = "密码不能为空",groups = AddGroup.class)
private String password;
private String email;
private String mobile;
private AccountStatusEnum status;
private List<Long> roleIdList;
}
package com.qkdata.common.annotation;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiLimit {
/**
* 限制某时间段内可以访问的次数,默认设置200
*/
int limitCounts() default 200;
/**
* @return
* @Description: 限制访问的某一个时间段,单位为秒,默认值10秒
*/
int timeSecond() default 10;
}
package com.qkdata.common.annotation;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
String value() default "";
boolean includeParam() default true;
}
package com.qkdata.common.aspect;
import com.qkdata.common.annotation.ApiLimit;
import com.qkdata.common.base.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
@Aspect
@Component
@Slf4j
public class ApiLimitAspect {
@Autowired
private RedisTemplate<String,Object> redisTemplate;
@Pointcut("@annotation(com.qkdata.common.annotation.ApiLimit)")
public void logPointCut() {
}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
ApiLimit apiLimit = getAnnotation(point);
return dealLimit(point,apiLimit,false);
}
private Object dealLimit(ProceedingJoinPoint point,ApiLimit apiLimit,boolean flag) throws Throwable {
String msgKey = getMsgKey(point);
ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
long methodCounts = valueOperations.increment(msgKey, 1);
// 如果该key不存在,则从0开始计算,并且当count为1的时候,设置过期时间
if (methodCounts == 1) {
redisTemplate.expire(msgKey, apiLimit.timeSecond(), TimeUnit.SECONDS);
}
// 如果redis中的count大于限制的次数,则等待10秒重试
if (methodCounts > apiLimit.limitCounts()) {
if (!flag) {
//等待10秒后,第一次重试
Thread.sleep(10 * 1000);
log.warn("等待10秒后,第一次重试...");
// 递归,再次请求业务方法
return dealLimit(point, apiLimit,true);
} else {
//如果第一次请求被限制了,等待10秒后重试,如果再次失败,则抛出异常
throw new BusinessException("请求超时");
}
}else {
return point.proceed();
}
}
private ApiLimit getAnnotation(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
return method.getAnnotation(ApiLimit.class);
}
private String getMsgKey(ProceedingJoinPoint point){
MethodSignature signature = (MethodSignature) point.getSignature();
String className = point.getTarget().getClass().getName();
String methodName = signature.getName();
String msgKey = className + "." + methodName;
return msgKey;
}
}
package com.qkdata.common.aspect;
import com.alibaba.fastjson.JSONArray;
import com.qkdata.biz.sys.entity.SysLogPO;
import com.qkdata.biz.sys.entity.SysUserPO;
import com.qkdata.biz.sys.service.SysLogService;
import com.qkdata.common.annotation.SysLog;
import com.qkdata.common.util.HttpContextUtils;
import com.qkdata.common.util.IPUtils;
import com.qkdata.common.util.UserContext;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
@Aspect
@Component
public class SysLogAspect {
@Autowired
private SysLogService sysLogService;
@Pointcut("@annotation(com.qkdata.common.annotation.SysLog)")
public void logPointCut() {
}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
long beginTime = System.currentTimeMillis();
//执行方法
Object result = null;
try {
result = point.proceed();
//执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
//保存日志
saveSysLog(point, time);
return result;
}catch (Throwable e){
long time = System.currentTimeMillis() - beginTime;
saveSysLog(point,time);
throw e;
}
}
private void saveSysLog(ProceedingJoinPoint joinPoint, long time) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
SysLogPO sysLog = new SysLogPO();
SysLog syslog = method.getAnnotation(SysLog.class);
if(syslog != null){
//注解上的描述
sysLog.setOperation(syslog.value());
}
//请求的方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
sysLog.setMethod(className + "." + methodName + "()");
if (syslog != null && syslog.includeParam()){
//请求的参数
Object[] args = joinPoint.getArgs();
try{
String params = JSONArray.toJSONString(args);
sysLog.setParams(params);
}catch (Exception e){
}
}
sysLog.setTime(time);
//获取request
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
//设置IP地址
sysLog.setIp(IPUtils.getIpAddr(request));
//用户名
SysUserPO user = UserContext.getUser();
if (user != null){
String username = UserContext.getUser().getUsername();
sysLog.setUsername(username);
}
//保存系统日志
sysLogService.save(sysLog);
}
}
package com.qkdata.common.base.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
public class BasePO<T extends Model<?>> extends Model<T> {
/**
* 主键ID
*/
@TableId
private Long id;
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
@Override
protected Serializable pkVal() {
return this.id;
}
}
package com.qkdata.common.base.enums;
public enum CodeEnum {
SUCCESS(200),
ERROR(1000);
private Integer code;
CodeEnum(Integer code){
this.code = code;
}
public Integer getCode() {
return code;
}
}
package com.qkdata.common.base.exception;
import lombok.Data;
/**
* 业务异常
*/
@Data
public class BusinessException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = 4650570427238258122L;
public BusinessException() {
super();
}
public BusinessException(String message) {
super(message);
}
public BusinessException(Throwable cause) {
super(cause);
}
public BusinessException(String message, Throwable cause) {
super(message, cause);
}
}
package com.qkdata.common.base.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
/**
* 分页实体类
*
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PageResult<T> implements Serializable {
private static final long serialVersionUID = -275582248840137389L;
/**
* 总数
*/
private Long count;
/**
* 是否成功:0 成功、1 失败
*/
private int code;
/**
* 当前页结果集
*/
private List<T> data;
}
package com.qkdata.common.base.model;
import com.qkdata.common.base.enums.CodeEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> implements Serializable {
private T datas;
private Integer resp_code;
private String resp_msg;
public static <T> Result<T> succeed(String msg) {
return of(null, CodeEnum.SUCCESS.getCode(), msg);
}
public static <T> Result<T> succeed(T model, String msg) {
return of(model, CodeEnum.SUCCESS.getCode(), msg);
}
public static <T> Result<T> succeed(T model) {
return of(model, CodeEnum.SUCCESS.getCode(), "");
}
public static <T> Result<T> of(T datas, Integer code, String msg) {
return new Result<>(datas, code, msg);
}
public static <T> Result<T> failed(String msg) {
return of(null, CodeEnum.ERROR.getCode(), msg);
}
public static <T> Result<T> failed(T model, String msg) {
return of(model, CodeEnum.ERROR.getCode(), msg);
}
}
package com.qkdata.common.base.service.impl;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
public abstract class BaseServiceImpl<M extends BaseMapper<T>,T> extends ServiceImpl<M,T> {
}
package com.qkdata.common.config;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.handlers.StrictFill;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.Date;
/**
* 自定义填充公共字段
*
*/
@Component
public class DateMetaObjectHandler implements MetaObjectHandler {
/**
* 插入填充,字段为空自动填充
*/
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", Date.class, DateUtil.date());
this.strictUpdateFill(metaObject,"updateTime",Date.class,DateUtil.date());
}
/**
* 更新填充
*/
@Override
public void updateFill(MetaObject metaObject) {
setFieldValByName("updateTime", new Date(), metaObject);
}
}
\ No newline at end of file
package com.qkdata.common.config;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean shiroFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new DelegatingFilterProxy("shiroFilter"));
//该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理
registration.addInitParameter("targetFilterLifecycle", "true");
registration.setEnabled(true);
registration.setOrder(Integer.MAX_VALUE - 1);
registration.addUrlPatterns("/api/*");
return registration;
}
}
package com.qkdata.common.config;
import com.qkdata.common.base.exception.BusinessException;
import com.qkdata.common.base.model.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;
import javax.validation.ConstraintViolationException;
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandle {
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public ResponseEntity<Result> bindExceptionHandler(MethodArgumentNotValidException e) {
BindingResult bindingResult = e.getBindingResult();
String message = bindingResult.getFieldError().getDefaultMessage();
log.warn("方法参数无效: {}", message);
return ResponseEntity.status(HttpStatus.OK).body(Result.failed(message));
}
@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
public ResponseEntity<Result> httpRequestMethodNotSupportedExceptionHandler(HttpRequestMethodNotSupportedException e) {
log.warn("不支持的Method: {}", e.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Result.failed("不支持当前请求方法"));
}
@ExceptionHandler(value = Exception.class)
public ResponseEntity<Result> handler(Exception e) {
log.error("服务器错误", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Result.failed("服务器处理错误"));
}
@ExceptionHandler(value = BusinessException.class)
public ResponseEntity<Result> businessExceptionhandler(BusinessException e) {
log.warn("业务异常, message: {}", e.getMessage());
return ResponseEntity.status(HttpStatus.OK).body(Result.failed(e.getMessage()));
}
@ExceptionHandler(value = HttpMessageNotReadableException.class)
public ResponseEntity<Result> httpMessageConvertExceptionHanlder(HttpMessageNotReadableException e) {
log.warn("无法解析请求参数: {}", e.getMessage());
return ResponseEntity.badRequest().body(Result.failed(e.getMessage()));
}
@ExceptionHandler(value = MissingPathVariableException.class)
public ResponseEntity<Result> missingPathVariableExceptionHanlder(MissingPathVariableException e) {
log.warn("请求链接缺少参数: {}", e.getMessage());
return ResponseEntity.badRequest().body(Result.failed(e.getMessage()));
}
@ExceptionHandler(value = MissingServletRequestParameterException.class)
public ResponseEntity<Result> requestParameterExceptionHandler(MissingServletRequestParameterException e) {
log.warn("缺少必需的请求参数: {}", e.getMessage());
return ResponseEntity.badRequest().body(Result.failed(e.getMessage()));
}
@ExceptionHandler(value = MethodArgumentTypeMismatchException.class)
public ResponseEntity<Result> methodArgumentTypeMismatchExceptionHandler(MethodArgumentTypeMismatchException e) {
log.warn("参数类别转换失败: {}", e.getMessage());
return ResponseEntity.badRequest().body(Result.failed(e.getMessage()));
}
@ExceptionHandler(value = ConstraintViolationException.class)
public ResponseEntity<Result> constraintViolationExceptionHandler(ConstraintViolationException e) {
log.warn("参数校验异常: {}", e.getMessage());
return ResponseEntity.badRequest().body(Result.failed(e.getMessage()));
}
@ExceptionHandler(value = HttpMediaTypeNotSupportedException.class)
public ResponseEntity<Result> httpMediaTypeNotSupportedExceptionHandler(HttpMediaTypeNotSupportedException e) {
log.warn("不支持MediaType: {}", e.getMessage());
return ResponseEntity.badRequest().body(Result.failed(e.getMessage()));
}
@ExceptionHandler(value = NoHandlerFoundException.class)
public ResponseEntity<Result> noHandlerFoundExceptionHandler(NoHandlerFoundException e) {
log.warn("no handler found: {}", e.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Result.failed(e.getMessage()));
}
}
package com.qkdata.common.config;
import org.springframework.http.HttpRequest;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import java.io.IOException;
import java.nio.charset.Charset;
public class HttpHeaderInterceptor implements ClientHttpRequestInterceptor {
private final String name;
private final String value;
public HttpHeaderInterceptor(String name, String value) {
// Assert.hasLength(name, "Name must not be empty");
// Assert.hasLength(value, "Value" + " must not be empty");
this.name = name;
this.value = value;
}
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
request.getHeaders().add(this.name, this.value);
charsetToUTF8(request);
return execution.execute(request, body);
}
private void charsetToUTF8(HttpRequest request) {
MediaType contentType = request.getHeaders().getContentType();
if(contentType.getCharset() == null || Charset.forName("UTF-8") != contentType.getCharset()) {
contentType = new MediaType(contentType, Charset.forName("UTF-8"));
request.getHeaders().setContentType(contentType);
}
}
}
package com.qkdata.common.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qkdata.common.trace.HttpTraceLogFilter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnWebApplication
public class HttpTraceConfiguration {
@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
static class ServletTraceFilterConfiguration {
@Bean
@ConditionalOnMissingBean
public HttpTraceLogFilter httpTraceLogFilter(ObjectMapper objectMapper) {
return new HttpTraceLogFilter(objectMapper);
}
}
}
package com.qkdata.common.config;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.qkdata.common.converter.*;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
@Configuration
public class JacksonConfiguration {
@Bean
public Jackson2ObjectMapperBuilderCustomizer objectMapper() {
return builder -> builder.failOnEmptyBeans(false)
.failOnUnknownProperties(false)
.createXmlMapper(false)
// .serializationInclusion(JsonInclude.Include.NON_EMPTY)
// .serializationInclusion(JsonInclude.Include.NON_NULL)
.serializerByType(Date.class,new DateSerializer())
.deserializerByType(Date.class,new DateDeserializer())
.serializerByType(LocalDate.class, new LocalDateSerializer())
.deserializerByType(LocalDate.class, new LocalDateDeserializer())
.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer())
.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer())
.featuresToDisable(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
}
package com.qkdata.common.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import java.io.IOException;
@Slf4j
public class LogClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
log.info("restTemplate请求日志, uri: {}, method: {}, methodValue: {}, headers: {}", request.getURI(),
request.getMethod(), request.getMethodValue(), request.getHeaders());
return execution.execute(request, body);
}
}
package com.qkdata.common.config;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* mybatis-plus自动配置
*
*/
@Configuration
public class MybatisPlusAutoConfigure {
/**
* 分页插件,自动识别数据库类型
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
// paginationInterceptor.setOverflow(false);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
// paginationInterceptor.setLimit(500);
// 开启 count 的 join 优化,只针对部分 left join
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return paginationInterceptor;
}
}
package com.qkdata.common.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.qkdata.common.converter.LocalDateTimeDeserializer;
import com.qkdata.common.converter.LocalDateTimeSerializer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
import java.time.LocalDateTime;
@Configuration
@Slf4j
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfig {
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(5 * 60))
.disableCachingNullValues();
return RedisCacheManager.builder(RedisCacheWriter.lockingRedisCacheWriter
(redisConnectionFactory)).cacheDefaults(defaultCacheConfig).transactionAware().build();
}
@Bean(value = "stringRedisTemplate")
public RedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
return redisTemplate;
}
@Bean(value = "objectRedisTemplate")
public RedisTemplate objectRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
SimpleModule module = new SimpleModule();
module.addDeserializer(LocalDateTime.class,new LocalDateTimeDeserializer());
module.addSerializer(LocalDateTime.class,new LocalDateTimeSerializer());
mapper.registerModule(module);
serializer.setObjectMapper(mapper);
template.setValueSerializer(serializer);
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
//订阅了一个叫chat 的通道
// container.addMessageListener(syncFriendListener(), new PatternTopic(Constants.topic_sync_friend));
// container.addMessageListener(syncRoomListener(),new PatternTopic(Constants.topic_sync_room));
// container.addMessageListener(syncMessageListener(),new PatternTopic(Constants.topic_sync_message));
//这个container 可以添加多个 messageListener
return container;
}
// @Bean
// public MessageListener syncMessageListener() {
//
// return new WxMessageListener();
// }
//
// @Bean
// public MessageListener syncRoomListener() {
// return new SyncRoomListener();
// }
//
// @Bean
// public MessageListener syncFriendListener() {
// return new SyncFriendListener();
// }
}
package com.qkdata.common.config;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.StandardCharsets;
@Configuration
public class RestTemplateConfig {
@Bean
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
RestTemplate restTemplate = new RestTemplate(factory);
restTemplate.getMessageConverters().set(1,
new StringHttpMessageConverter(StandardCharsets.UTF_8));
return restTemplate;
}
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setReadTimeout(10000);//单位为ms
factory.setConnectTimeout(5000);//单位为ms
return factory;
}
}
\ No newline at end of file
package com.qkdata.common.config;
import com.qkdata.common.jwt.JWTProperties;
import com.qkdata.common.oauth.OAuthFilter;
import com.qkdata.common.oauth.OAuthRealm;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
@EnableConfigurationProperties(JWTProperties.class)
public class ShiroConfig {
@Autowired
private JWTProperties jwtProperties;
@Bean("securityManager")
public SecurityManager securityManager(OAuthRealm oAuth2Realm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRememberMeManager(null);
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
securityManager.setSubjectDAO(subjectDAO);
securityManager.setRealm(oAuth2Realm);
return securityManager;
}
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
//oauth过滤
Map<String, Filter> filters = new HashMap<>();
filters.put("oauth", new OAuthFilter());
shiroFilter.setFilters(filters);
Map<String, String> filterMap = new LinkedHashMap<>();
for (String url : jwtProperties.getExcludeUrls()){
filterMap.put(url,"anon");
}
for (String url : jwtProperties.getIncludeUrls()){
filterMap.put(url, "oauth");
}
shiroFilter.setFilterChainDefinitionMap(filterMap);
return shiroFilter;
}
// @Bean("lifecycleBeanPostProcessor")
// public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
// return new LifecycleBeanPostProcessor();
// }
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
package com.qkdata.common.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
@Configuration
@EnableSwagger2
public class Swagger2Config {
/**
* @return
*/
@Bean
public Docket createRestApi() {
// ParameterBuilder ticketPar = new ParameterBuilder();
// List<Parameter> pars = new ArrayList<>();
// ticketPar.name(HttpHeaders.AUTHORIZATION).description("user token")
// .modelRef(new ModelRef("string")).parameterType("header")
// .required(false).build();
// pars.add(ticketPar.build());
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()
.apis(RequestHandlerSelectors.basePackage("com.qkdata"))
.paths(PathSelectors.any()).build()
.securitySchemes(securitySchemes()).securityContexts(securityContexts());
// .globalOperationParameters(pars);
}
private List<ApiKey> securitySchemes() {
List<ApiKey> apiKeys = new ArrayList<>(1);
ApiKey apiKey = new ApiKey("Authorization","Authorization","header");
apiKeys.add(apiKey);
return apiKeys;
}
private List<SecurityContext> securityContexts() {
List<SecurityContext> contexts = new ArrayList<>(1);
SecurityContext securityContext = SecurityContext.builder()
.securityReferences(defaultAuth())
//.forPaths(PathSelectors.regex("^(?!auth).*$"))
.build();
contexts.add(securityContext);
return contexts;
}
private List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
List<SecurityReference> references = new ArrayList<>(1);
references.add(new SecurityReference("Authorization", authorizationScopes));
return references;
}
/**
* @return
*/
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("backend service API").version("1.0").build();
}
}
package com.qkdata.common.config;
import com.qkdata.common.converter.StringToDateConverter;
import com.qkdata.common.converter.StringToLocalDateConverter;
import com.qkdata.common.converter.StringToLocalDateTimeConverter;
import com.qkdata.common.trace.TraceIdInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*")
.allowedOrigins("*")
.allowCredentials(true)
.maxAge(1800);
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(stringToLocalDateConverter());
registry.addConverter(stringToLocalDateTimeConverter());
registry.addConverter(stringToDateConverter());
}
private Converter stringToDateConverter() {
return new StringToDateConverter();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(traceIdInterceptor());
}
private HandlerInterceptor traceIdInterceptor() {
return new TraceIdInterceptor();
}
private Converter stringToLocalDateConverter() {
return new StringToLocalDateConverter();
}
private Converter stringToLocalDateTimeConverter() {
return new StringToLocalDateTimeConverter();
}
}
package com.qkdata.common.constants;
public interface AddGroup {
}
package com.qkdata.common.constants;
public final class Constants {
public static final String PSAAWORD = "123456";
public static final Long SYS_ADMIN_ID = 1L;
public static final Integer DEFAULT_PAGE = 1;
public static final Integer DEFAULT_PAGE_SIZE = 10;
}
package com.qkdata.common.constants;
public final class DateTimeFormatterPattern {
private DateTimeFormatterPattern() {
}
public static final String YYYY_MM_DD = "yyyy-MM-dd";
public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
}
package com.qkdata.common.constants;
/**
* 分页常量
*/
public final class Page {
private Page() {
}
public static final String CURRENT_PAGE_STR = "1";
public static final Integer CURRENT_PAGE_INT = 1;
public static final String PAGE_SIZE_STR = "10";
public static final Integer PAGE_SIZE_INT = 10;
}
package com.qkdata.common.constants;
public interface UpdateGroup {
}
package com.qkdata.common.constants;
public final class ValidatorPattern {
private ValidatorPattern(){}
public static final String MOBILE_PATTERN = "^(13|14|15|16|17|18|19)[0-9]{9}$";
}
package com.qkdata.common.converter;
import cn.hutool.core.date.DateUtil;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.qkdata.common.base.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.util.Date;
/**
* 用于@RequestBody中Date类型参数序列化与反序列化
*/
@Slf4j
public class DateDeserializer extends JsonDeserializer<Date> {
@Override
public Date deserialize(JsonParser p, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
try {
return DateUtil.parseDateTime(p.getValueAsString());
} catch (Exception e) {
log.warn("时间转换异常", e);
throw new BusinessException("时间转换异常");
}
}
}
package com.qkdata.common.converter;
import cn.hutool.core.date.DateUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.util.Date;
/**
* 用于@RequestBody中Date类型参数序列化与反序列化
*/
public class DateSerializer extends JsonSerializer<Date> {
@Override
public void serialize(Date value, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException {
if (value == null) {
return;
}
gen.writeString(DateUtil.formatDateTime(value));
}
}
package com.qkdata.common.converter;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.qkdata.common.base.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
/**
* 用于@RequestBody中LocalDate类型参数序列化与反序列化
*/
@Slf4j
public class LocalDateDeserializer extends JsonDeserializer<LocalDate> {
@Override
public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
try {
return Instant.ofEpochMilli(p.getLongValue()).atZone(ZoneId.systemDefault()).toLocalDate();
} catch (Exception e) {
log.warn("时间转换异常", e);
throw new BusinessException("时间转换异常");
}
}
}
package com.qkdata.common.converter;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.time.LocalDate;
import java.time.ZoneId;
/**
* 用于@RequestBody中LocalDate类型参数序列化与反序列化
*/
public class LocalDateSerializer extends JsonSerializer<LocalDate> {
@Override
public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (value == null) {
return;
}
gen.writeNumber(value.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli());
}
}
package com.qkdata.common.converter;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.qkdata.common.base.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
/**
* 用于@RequestBody中LocalDateTime参数序列化与反序列化
*/
@Slf4j
public class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
@Override
public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
try {
return Instant.ofEpochMilli(p.getLongValue()).atZone(ZoneId.systemDefault()).toLocalDateTime();
} catch (Exception e) {
log.warn("时间转换异常", e);
throw new BusinessException("时间转换异常");
}
}
}
package com.qkdata.common.converter;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
/**
* 用于@RequestBody中LocalDateTime参数序列化与反序列化
*/
public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
@Override
public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (value == null) {
return;
}
gen.writeNumber(value.toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
}
}
package com.qkdata.common.converter;
import cn.hutool.core.date.DateUtil;
import com.qkdata.common.base.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.StringUtils;
import java.util.Date;
/**
* 用于QUERY参数转换成Date
*/
@Slf4j
public class StringToDateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
if (StringUtils.isEmpty(source)) {
return null;
}
try {
return DateUtil.parseDateTime(source);
} catch (Exception e) {
log.warn("时间转换异常", e);
throw new BusinessException("时间转换异常");
}
}
}
package com.qkdata.common.converter;
import com.qkdata.common.base.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.StringUtils;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
/**
* 用于QUERY参数转换成LocalDate
*/
@Slf4j
public class StringToLocalDateConverter implements Converter<String, LocalDate> {
@Override
public LocalDate convert(String source) {
if (StringUtils.isEmpty(source)) {
return null;
}
try {
return Instant.ofEpochMilli(Long.valueOf(source)).atZone(ZoneId.systemDefault()).toLocalDate();
} catch (Exception e) {
log.warn("时间转换异常", e);
throw new BusinessException("时间转换异常");
}
}
}
package com.qkdata.common.converter;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.StringUtils;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
/**
* 用于QUERY参数转换成LocalDateTime
*/
public class StringToLocalDateTimeConverter implements Converter<String, LocalDateTime> {
@Override
public LocalDateTime convert(String source) {
if (StringUtils.isEmpty(source)) {
return null;
}
return Instant.ofEpochMilli(Long.valueOf(source)).atZone(ZoneId.systemDefault()).toLocalDateTime();
}
}
package com.qkdata.common.jwt;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.Set;
@Data
@ConfigurationProperties(prefix = "jwt")
public class JWTProperties {
private String typ = "JWT";
private String alg = "HS256";
private String secret;
private String iss = "qkdata";
private String aud;
private Integer exp = 2;
/**
* 是否启用拦截器
*/
private Boolean enableInterceptor = Boolean.FALSE;
/**
* 需要拦截的URL
*/
private Set<String> includeUrls;
/**
* 需要排除的URL
*/
private Set<String> excludeUrls;
}
package com.qkdata.common.jwt;
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.interfaces.DecodedJWT;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Component
@EnableConfigurationProperties(JWTProperties.class)
public class JWTService {
@Autowired
private JWTProperties jwtProperties;
/**
* 头部信息
*
* @return
*/
private void buildHeader(JWTCreator.Builder builder) {
Map<String, Object> header = new HashMap<>();
header.put("typ", jwtProperties.getTyp());
header.put("alg", jwtProperties.getAlg());
builder.withHeader(header);
}
/**
* 载荷信息
*/
private void buildPayload(JWTCreator.Builder builder, Map<String, Object> claims) {
LocalDateTime now = LocalDateTime.now();
builder.withIssuer(jwtProperties.getIss())
.withAudience(jwtProperties.getAud())
.withIssuedAt(issuedAt((now)))
.withExpiresAt(expiresAt(now, jwtProperties.getExp()));
if (claims != null) {
for (Map.Entry<String, Object> entry : claims.entrySet()) {
Object value = entry.getValue();
if (value instanceof Boolean) {
builder.withClaim(entry.getKey(), (Boolean) value);
} else if (value instanceof Integer) {
builder.withClaim(entry.getKey(), (Integer) value);
} else if (value instanceof Long) {
builder.withClaim(entry.getKey(), (Long) value);
} else if (value instanceof String) {
builder.withClaim(entry.getKey(), (String) value);
} else if (value instanceof Double) {
builder.withClaim(entry.getKey(), (Double) value);
} else if (value instanceof Date) {
builder.withClaim(entry.getKey(), (Date) value);
} else {
throw new IllegalStateException("jwt payload claims type mismatch");
}
}
}
}
/**
* 签名
*
* @param builder
* @return jwt字符串
*/
private String buildSignature(JWTCreator.Builder builder) {
return builder.sign(Algorithm.HMAC256(jwtProperties.getSecret()));
}
/**
* 创建JWT
*
* @param claims 业务参数
* @return
*/
public String createJWT(Map<String, Object> claims) {
JWTCreator.Builder builder = JWT.create();
buildHeader(builder);
buildPayload(builder, claims);
return buildSignature(builder);
}
public String createJWT() {
return createJWT(null);
}
/**
* 签发时间
*
* @param issuedAt
* @return
*/
private Date issuedAt(LocalDateTime issuedAt) {
return Date.from(issuedAt.atZone(ZoneId.systemDefault()).toInstant());
}
/**
* 过期时间
*
* @param issuedAt
* @param hours
* @return
*/
private Date expiresAt(LocalDateTime issuedAt, Integer hours) {
return Date.from(issuedAt.plusHours(hours).atZone(ZoneId.systemDefault()).toInstant());
}
/**
* 解码
*
* @param jwt
*/
public DecodedJWT decode(String jwt) {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(jwtProperties.getSecret())).build();
return verifier.verify(jwt);
}
}
package com.qkdata.common.oauth;
public enum AuthorizationResponseEnum{
MISSING_TOKEN("403", "HEADER中不存在TOKEN"),
INVALID_TOKEN("403", "校验TOKEN失败"),
EXPIRED_TOKEN("403", "TOKEN已过期"),
MISSING_CLAIMS("403", "不合法的TOKEN, 信息可能被篡改"),
INVALID_CLAIM("403", "不合法的TOKEN, 系统中不存在资源");
private String value;
private String text;
AuthorizationResponseEnum(String value, String text) {
this.value = value;
this.text = text;
}
public String value() {
return this.value;
}
public String text() {
return this.text;
}
}
package com.qkdata.common.oauth;
import lombok.Data;
@Data
public class AuthorizedUser {
private Long userId;
private String username;
}
package com.qkdata.common.oauth;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qkdata.common.base.model.Result;
import com.qkdata.common.util.HttpContextUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.springframework.http.HttpHeaders;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class OAuthFilter extends AuthenticatingFilter {
private ObjectMapper mapper = new ObjectMapper();
@Override
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
//获取请求token
String token = getRequestToken((HttpServletRequest) request);
if(StringUtils.isEmpty(token)){
return null;
}
return new OAuthToken(token);
}
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
if(((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())){
return true;
}
return false;
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
//获取请求token,如果token不存在,直接返回403
String token = getRequestToken((HttpServletRequest) request);
if(StringUtils.isEmpty(token)){
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setContentType("application/json;charset=utf-8");
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
Result responseData = Result.failed(AuthorizationResponseEnum.MISSING_TOKEN.text());
String json = mapper.writeValueAsString(responseData);
httpResponse.getWriter().print(json);
return false;
}
return executeLogin(request, response);
}
@Override
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setContentType("application/json;charset=utf-8");
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
try {
//处理登录失败的异常
Throwable throwable = e.getCause() == null ? e : e.getCause();
Result rd = Result.failed(AuthorizationResponseEnum.INVALID_TOKEN.value(),e.getMessage());
String json = mapper.writeValueAsString(rd);
httpResponse.getWriter().print(json);
} catch (IOException e1) {
}
return false;
}
/**
* 获取请求的token
*/
private String getRequestToken(HttpServletRequest httpRequest){
//从header中获取token
String token = httpRequest.getHeader(HttpHeaders.AUTHORIZATION);
//如果header中不存在token,则从参数中获取token
if(StringUtils.isEmpty(token)){
token = httpRequest.getParameter(HttpHeaders.AUTHORIZATION);
}
return token;
}
}
package com.qkdata.common.oauth;
import com.auth0.jwt.exceptions.*;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qkdata.biz.sys.entity.SysUserPO;
import com.qkdata.biz.enums.AccountStatusEnum;
import com.qkdata.biz.sys.service.ShiroService;
import com.qkdata.common.jwt.JWTService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
@Slf4j
@Component
public class OAuthRealm extends AuthorizingRealm {
@Autowired
private ShiroService shiroService;
@Autowired
private JWTService jwtService;
private ObjectMapper mapper = new ObjectMapper();
private DecodedJWT decodedJWT;
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof OAuthToken;
}
/**
* 授权(验证权限时调用)
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SysUserPO user = (SysUserPO) principals.getPrimaryPrincipal();
Long userId = user.getId();
//用户权限列表
Set<String> permsSet = new HashSet<>();
permsSet.add("all");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setStringPermissions(permsSet);
return info;
}
/**
* 认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String accessToken = (String) token.getPrincipal();
if (accessToken == null) {
throw new AuthenticationException(AuthorizationResponseEnum.MISSING_TOKEN.text());
}
try {
decodedJWT = jwtService.decode(accessToken);
String userJson = decodedJWT.getClaim("user").asString();
AuthorizedUser authUser = mapper.readValue(userJson,AuthorizedUser.class);
SysUserPO user = shiroService.getUserByUserName(authUser.getUsername());
if (user == null){
throw new AuthenticationException(AuthorizationResponseEnum.INVALID_CLAIM.text());
}
if (user.getStatus() == AccountStatusEnum.DISABLE){
throw new AuthenticationException("您已退出团队,不能登陆");
}
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, accessToken, getName());
return info;
} catch (JWTDecodeException | AlgorithmMismatchException | SignatureVerificationException | InvalidClaimException e) {
log.warn("校验TOKEN失败: {}, token: {}", e.getMessage(), token);
throw new AuthenticationException(AuthorizationResponseEnum.INVALID_TOKEN.text());
} catch (TokenExpiredException e) {
log.warn("TOKEN已过期: {}, token: {}", e.getMessage(), token);
throw new AuthenticationException(AuthorizationResponseEnum.EXPIRED_TOKEN.text());
} catch (IOException e) {
throw new AuthenticationException(AuthorizationResponseEnum.INVALID_CLAIM.text());
}
}
}
package com.qkdata.common.oauth;
import org.apache.shiro.authc.AuthenticationToken;
public class OAuthToken implements AuthenticationToken {
private String token;
public OAuthToken(String token){
this.token = token;
}
@Override
public String getPrincipal() {
return token;
}
@Override
public Object getCredentials() {
return token;
}
}
package com.qkdata.common.trace;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.slf4j.MDC;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;
import org.springframework.web.util.WebUtils;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* 记录请求参数、请求路径、响应数据、请求时间等信息
*
* @see org.springframework.boot.actuate.web.trace.servlet.HttpTraceFilter
*/
@Slf4j
public class HttpTraceLogFilter extends OncePerRequestFilter implements Ordered {
private int order = Ordered.LOWEST_PRECEDENCE - 10;
private ObjectMapper objectMapper;
public HttpTraceLogFilter(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public int getOrder() {
return this.order;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if (!isRequestValid(request)) {
filterChain.doFilter(request, response);
return;
}
// request的stream只能被消费一次
if (!(request instanceof ContentCachingRequestWrapper)) {
request = new ContentCachingRequestWrapper(request);
}
if (!(response instanceof ContentCachingResponseWrapper)) {
response = new ContentCachingResponseWrapper(response);
}
int status = HttpStatus.OK.value();
long startTime = Instant.now().toEpochMilli();
try {
filterChain.doFilter(request, response);
status = response.getStatus();
} finally {
long timeTaken = Instant.now().toEpochMilli() - startTime;
TraceLog traceLog = new TraceLog();
traceLog.setTraceId(MDC.get(TraceIdInterceptor.TRACE_ID_KEY));
traceLog.setTimeTaken(timeTaken);
traceLog.setCreateAt(LocalDateTime.now());
traceLog.setMethod(request.getMethod());
traceLog.setPath(request.getRequestURI());
traceLog.setStatus(status);
traceLog.setRequestBody(getRequestBody(request));
traceLog.setResponseBody(getResponseBody(response));
traceLog.setParameters(objectMapper.writeValueAsString(request.getParameterMap()));
traceLog.setHeaders(objectMapper.writeValueAsString(getHeaders(request)));
// log.info("Http Trace Log: {}", objectMapper.writeValueAsString(traceLog));
// 复原response,正常的返回数据
updateResponse(response);
MDC.remove(TraceIdInterceptor.TRACE_ID_KEY);
}
}
private Map<String, Object> getHeaders(HttpServletRequest request) {
Enumeration<String> headerNames = request.getHeaderNames();
Map<String, Object> heads = new HashMap<>();
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String value = request.getHeader(name);
heads.put(name, value);
}
return heads;
}
private boolean isRequestValid(HttpServletRequest request) {
try {
new URI(request.getRequestURL().toString());
return true;
} catch (URISyntaxException ex) {
return false;
}
}
private String getRequestBody(HttpServletRequest request) {
String requestBody = "";
ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
if (wrapper != null) {
try {
requestBody = IOUtils.toString(wrapper.getContentAsByteArray(), wrapper.getCharacterEncoding());
} catch (IOException e) {
// NOOP
}
}
return requestBody;
}
private String getResponseBody(HttpServletResponse response) {
String responseBody = "";
ContentCachingResponseWrapper wrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
if (wrapper != null) {
try {
responseBody = IOUtils.toString(wrapper.getContentAsByteArray(), wrapper.getCharacterEncoding());
} catch (IOException e) {
// NOOP
}
}
return responseBody;
}
private void updateResponse(HttpServletResponse response) throws IOException {
ContentCachingResponseWrapper responseWrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
Objects.requireNonNull(responseWrapper).copyBodyToResponse();
}
}
package com.qkdata.common.trace;
import org.slf4j.MDC;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.Map;
/**
* 这是{@link ThreadPoolTaskExecutor}的一个简单替换,可以在每个任务之前设置子线程的MDC数据。
* <p/>
* 在记录日志的时候,一般情况下我们会使用MDC来存储每个线程的特有参数,如身份信息等,以便更好的查询日志。
* 但是Logback在最新的版本中因为性能问题,不会自动的将MDC的内存传给子线程。所以Logback建议在执行异步线程前
* 先通过MDC.getCopyOfContextMap()方法将MDC内存获取出来,再传给线程。
* 并在子线程的执行的最开始调用MDC.setContextMap(context)方法将父线程的MDC内容传给子线程。
**/
public class MdcThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
/**
* 所有线程都会委托给这个execute方法,在这个方法中我们把父线程的MDC内容赋值给子线程
* https://logback.qos.ch/manual/mdc.html#managedThreads
*
* @param runnable
*/
@Override
public void execute(Runnable runnable) {
// 获取父线程MDC中的内容,必须在run方法之前,否则等异步线程执行的时候有可能MDC里面的值已经被清空了,这个时候就会返回null
Map<String, String> context = MDC.getCopyOfContextMap();
super.execute(() -> run(runnable, context));
}
/**
* 子线程委托的执行方法
*
* @param runnable {@link Runnable}
* @param context 父线程MDC内容
*/
private void run(Runnable runnable, Map<String, String> context) {
// 将父线程的MDC内容传给子线程
MDC.setContextMap(context);
try {
// 执行异步操作
runnable.run();
} finally {
// 清空MDC内容
MDC.clear();
}
}
}
package com.qkdata.common.trace;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;
@Slf4j
public class TraceIdInterceptor implements HandlerInterceptor {
public static final String TRACE_ID_KEY = "traceId";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String traceId = UUID.randomUUID().toString().replaceAll("-", "");
MDC.put(TRACE_ID_KEY, traceId);
return true;
}
}
package com.qkdata.common.trace;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
public class TraceLog implements Serializable {
/**
* traceId
*/
private String traceId;
/**
* 请求路径
*/
private String path;
/**
* 请求方法
*/
private String method;
/**
* 请求头信息
*/
private String headers;
/**
* query请求参数
*/
private String parameters;
/**
* 接口花费时间
*/
private Long timeTaken;
/**
* 日志创建时间
*/
private LocalDateTime createAt;
/**
* Response Status
*/
private Integer status;
/**
* 请求Body
*/
private String requestBody;
/**
* 响应Body
*/
private String responseBody;
}
package com.qkdata.common.tree;
import java.util.List;
/**
* @author liu_yang
* 构造树结构时内容提供者接口
*
*/
public interface IContentProvider {
/**
* 获取当前元素下的子元素.
*
* @param curElement 当前元素对象
* @param level 当前元素级别
* @param index 当前元素所在层级索引
* @return 子元素对象集合
* @throws ServiceException 抛出异常
*/
List<? extends Object> getChildren(Object curElement, final int level, final int index);
/**
* 获取根元素.
*
* @param treeData 树结构所需数据
* @param level 当前级别
* @param index 当前索引
* @return 根元素对象集合
*/
List<? extends Object> getRootElement(List<? extends Object> treeData, final int level, final int index);
/**
* 异步时需实现此方法,用于判断当前元素是否有子元素.
*
* @param curElement 当前元素对象
* @param level 当前元素级别
* @param index 当前元素所在层级索引
* @return true/false
*/
boolean hasChild(Object curElement, final int level, final int index);
}
package com.qkdata.common.tree;
import java.util.Map;
/**
* @author liu_yang
* 构造树结构时节点显示内容提供者接口
*
*/
public interface ILabelProvider {
/**
* 获得当前元素类型Normal,Checkbox,Radio.
*
* @param curElement 当前元素对象
* @param level 级别
* @param index 索引
* @return TreeTypeEnum枚举
*/
TreeTypeEnum getLabelType(final Object curElement, final int level, final int index);
/**
* 获取当前元素显示名称.
*
* @param curElement 当前元素对象
* @param level 级别
* @param index 索引
* @return 节点名称
*/
String getLabelName(final Object curElement, final int level, final int index);
/**
* 获得当前元素的ID.
*
* @param curElement 当前元素
* @param level 级别
* @param index 索引
* @return 节点ID
*/
String getLabelId(final Object curElement, final int level, final int index);
/**
* 获取当前元素显示图标.如果不显示图标,返回null
*
* @param curElement 当前元素
* @param level 级别
* @param index 索引
* @return 节 点图标名称
*/
String getIcon(final Object curElement, final int level, final int index);
/**
* 获取当前元素是否显示默认图标.
*
* @param curElement 当前元素
* @param level 级别
* @param index 索引
* @return true/false
*/
boolean isDefaultIcon(final Object curElement, final int level, final int index);
/**
* 获取当前元素是否可以点击操作
*
* @param curElement 当前元素
* @param level 级别
* @param index 索引
* @return true/false
*/
boolean isClick(final Object curElement, int level, int index);
/**
* 如是单选或多选情况下会调用该方法,判断当前节点是否被选中.
*
* @param curElement 当前元素
* @param level 级别
* @param index 索引
* @return true/false
*/
boolean isCheck(final Object curElement, int level, int index);
/**
* 如是单选或多选情况下会调用该方法,获取节点选中时设置的值.
*
* @param curElement 当前元素
* @param level 级别
* @param index 索引
* @return 节点值
*/
String getCheckId(final Object curElement, int level, int index);
/**
* 获得当前元素的自定义属性.
*
* @param curElement 当前元素
* @param level 级别
* @param index 索引
* @return 自定义属性MAP
*/
Map<String, String> getAttributes(final Object curElement, int level, int index);
}
package com.qkdata.common.tree;
import java.util.ArrayList;
import java.util.List;
/**
* @author liu_yang
* 树对象
*
*/
public class Tree {
/**
* <code>childs</code> - 树子节点.
*/
private List<TreeNode> m_roots = new ArrayList<TreeNode>();
/**
* 树ID
*/
private String m_id = "";
/**
* Constructors.
*
* @param id 树ID
*/
public Tree(final String id) {
this.m_id = id;
}
/**
* Constructors.
*
* @param id 树ID
* @param roots 子节点TreeNode
*/
public Tree(final String id, final List<TreeNode> roots) {
this.m_roots = roots;
this.m_id = id;
}
/**
* 添加子节点
*
* @param treeNode TreeNode节点对象
*/
public void addChild(final TreeNode treeNode) {
this.m_roots.add(treeNode);
}
/**
* 获取List<TreeNode>树
*
* @return List<TreeNode>
*/
public List<TreeNode> getRoots() {
return this.m_roots;
}
/**
* @return id - {return content description}
*/
public String getId() {
return m_id;
}
/**
* @param id - {parameter description}.
*/
public void setId(final String id) {
m_id = id;
}
}
package com.qkdata.common.tree;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author liu_yang
* 树节点类
*
*/
public class TreeNode {
/**
* <code>id</code> - 节点ID.
*/
private String m_id;
/**
* <code>m_lable</code> - 节点名称.
*/
private String m_name;
/**
* <code>m_icon</code> - 图片.
*/
private String m_icon;
/**
* <code>m_children</code> - 子节点.
*/
private List<TreeNode> m_children;
/**
* <code>m_isExpand</code> - 是否可展开.
*/
private boolean m_isExpand;
/**
* <code>m_isClick</code> - 节点是否可点击.
*/
private boolean m_isClick;
/**
* 节点是否被选中
*/
private boolean m_isCheck;
/**
* <code>m_type</code> - 节点类型.
*/
private String m_type;
/**
* <code>extProperty</code> - {description}.
*/
private Map<String, String> m_extProperty;
/**
* 构造方法.
*
* @param id 节点ID.
* @param values 节点属性集合.
*/
public TreeNode(final String id, final Map<String, String> values) {
this.m_id = id;
this.m_extProperty = values;
}
/**
* @return id - {return content description}
*/
public String getId() {
return m_id;
}
/**
* @param id - {parameter description}.
*/
public void setId(final String id) {
m_id = id;
}
/**
* @return name - {return content description}
*/
public String getName() {
return m_name;
}
/**
* @param name - {parameter description}.
*/
public void setName(final String name) {
m_name = name;
}
/**
* @return icon - {return content description}
*/
public String getIcon() {
return m_icon;
}
/**
* @param icon - {parameter description}.
*/
public void setIcon(final String icon) {
m_icon = icon;
}
/**
* @return children - {return content description}
*/
public List<TreeNode> getChildren() {
return m_children;
}
/**
* @param children - {parameter description}.
*/
public void setChilds(final List<TreeNode> children) {
m_children = children;
}
public boolean getIsCheck() {
return m_isCheck;
}
public void setIsCheck(boolean isCheck) {
this.m_isCheck = isCheck;
}
/**
* @return isExpand - {return content description}
*/
public boolean getIsExpand() {
return m_isExpand;
}
/**
* @param isExpand - {parameter description}.
*/
public void setIsExpand(final boolean isExpand) {
m_isExpand = isExpand;
}
/**
* @return isClick - {return content description}
*/
public boolean getIsClick() {
return m_isClick;
}
/**
* @param isClick - {parameter description}.
*/
public void setIsClick(final boolean isClick) {
m_isClick = isClick;
}
/**
* @return type - {return content description}
*/
public String getType() {
return m_type;
}
/**
* @param type - {parameter description}.
*/
public void setType(final String type) {
m_type = type;
}
/**
* @return extProperty - {return content description}
*/
public Map<String, String> getExtProperty() {
return m_extProperty;
}
/**
* @param extProperty - {parameter description}.
*/
public void setExtProperty(final Map<String, String> extProperty) {
m_extProperty = extProperty;
}
/**
* addChild.
*
* @param node node
*/
public void addChild(final TreeNode node) {
if (this.m_children == null) {
this.m_children = new ArrayList<TreeNode>();
}
m_children.add(node);
}
}
package com.qkdata.common.tree;
/**
* @author liu_yang
* 树类型枚举
*
*/
public enum TreeTypeEnum {
/**
* 树节点可多选
*/
CHECKBOX("checkbox"),
/**
* 树节点可单选
*/
RADIO("radio"),
/**
* 树节点为普通节点
*/
NORMAL("normal");
/**
* 类型值
*/
private String value;
/**
* @param value
*/
private TreeTypeEnum(final String value) {
this.value = value;
}
/**
* @return
*/
public String value() {
return this.value;
}
}
package com.qkdata.common.tree;
import java.util.ArrayList;
import java.util.List;
/**
* @author liu_yang 生成树方法
*
*/
public class TreeView {
/**
* <code>m_treeData</code> - 树内容原始对象.
*/
private List<? extends Object> m_treeData;
/**
* <code>m_contentProvider</code> - 内容提供者.
*/
private IContentProvider m_contentProvider;
/**
* <code>m_labelProvider</code> - 节点内容提供者.
*/
private ILabelProvider m_labelProvider;
/**
* 生成树对象的构造方法
* @param treeData 生成树结构的数据
* @param contentProvider 树节点内容提供者
* @param labelProvider 树节点label显示内容提供者
*/
public TreeView(List<? extends Object> treeData,IContentProvider contentProvider,ILabelProvider labelProvider){
this.m_treeData=treeData;
this.m_contentProvider=contentProvider;
this.m_labelProvider=labelProvider;
}
/**
* 构造树.
*
* @param treeId
* Tree ID
* @return Tree对象
* @throws ServiceException
* Service异常
*/
public Tree buildTree(final String treeId) {
List<TreeNode> t_rootList = new ArrayList<TreeNode>();
TreeNode t_treeNode = null;
List<? extends Object> t_rootObjectList = m_contentProvider.getRootElement(this.m_treeData, 0, 0);
if (t_rootObjectList != null) {
for (int t_i = 0; t_i < t_rootObjectList.size(); t_i++) {
Object t_rootObject = t_rootObjectList.get(t_i);
t_treeNode = createTreeNode(t_rootObject, 0, t_i);
recursionNodes(this.m_contentProvider.getChildren(t_rootObject, 0, t_i), t_treeNode, 0);
t_rootList.add(t_treeNode);
}
}
Tree t_tree = new Tree(treeId, t_rootList);
return t_tree;
}
/**
* 递归树节点.
*
* @param objectList
* 树原始对象集合
* @param parentTreeNode
* 父树节点
* @param level
* 树节点集合
* @throws ServiceException
* 异常
*/
private void recursionNodes(final List<? extends Object> objectList, final TreeNode parentTreeNode,
final int level) {
int t_level = level + 1;
TreeNode t_treeNode = null;
if (objectList != null) {
for (int t_i = 0; t_i < objectList.size(); t_i++) {
Object t_childObject = objectList.get(t_i);
t_treeNode = createTreeNode(t_childObject, t_level, t_i);
parentTreeNode.addChild(t_treeNode);
recursionNodes(this.m_contentProvider.getChildren(t_childObject, t_level, t_i), t_treeNode, t_level);
}
}
}
/**
* 创建树节点.
*
* @param o
* 节点原始对象
* @param level
* 节点级别
* @param index
* 节点同级索引
* @return 创建的树节点
*/
private TreeNode createTreeNode(final Object o, final int level, final int index) {
TreeNode t_treeNode = new TreeNode(this.m_labelProvider.getLabelId(o, level, index),
this.m_labelProvider.getAttributes(o, level, index));
if (this.m_contentProvider.hasChild(o, level, index)) {
t_treeNode.setIsExpand(true);
} else {
t_treeNode.setIsExpand(false);
}
t_treeNode.setName(this.m_labelProvider.getLabelName(o, level, index));
t_treeNode.setIcon(this.m_labelProvider.getIcon(o, level, index));
t_treeNode.setIsClick(this.m_labelProvider.isClick(o, level, index));
t_treeNode.setIsCheck(this.m_labelProvider.isCheck(o, level, index));
createTreeNodeType(o, level, index, t_treeNode);
return t_treeNode;
}
/**
* 创建树节点类型.
*
* @param o
* Object
* @param level
* 级别
* @param index
* 索引
* @param treeNode
* 树节点
*/
private void createTreeNodeType(final Object o, final int level, final int index, final TreeNode treeNode) {
TreeTypeEnum t_treeType = this.m_labelProvider.getLabelType(o, level, index);
treeNode.setType(t_treeType.toString());
}
}
package com.qkdata.common.util;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.Validate;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
public class EncryptUtil {
private static final String SHA1 = "SHA-1";
private static final String MD5 = "MD5";
private static final SecureRandom random = new SecureRandom();
public static String getSalt() {
byte[] saltByte = generateSalt(8);
String salt = encodeHex(saltByte);
return salt;
}
public static String encryptPassword(String password, String salt) throws NoSuchAlgorithmException {
byte[] hashPassword = md5(password.getBytes(), salt.getBytes(), 1024);
return encodeHex(hashPassword);
}
/**
* Hex编码.
*/
public static String encodeHex(byte[] input) {
return Hex.encodeHexString(input);
}
/**
* Hex解码.
* @throws DecoderException
*/
public static byte[] decodeHex(String input) throws DecoderException {
return Hex.decodeHex(input.toCharArray());
}
public static byte[] sha1(byte[] input, byte[] salt, int iterations) throws NoSuchAlgorithmException {
return digest(input, SHA1, salt, iterations);
}
public static byte[] md5(byte[] input, byte[] salt, int iterations) throws NoSuchAlgorithmException {
return digest(input, MD5, salt, iterations);
}
/**
* 对字符串进行散列, 支持md5与sha1算法.
* @throws NoSuchAlgorithmException
*/
private static byte[] digest(byte[] input, String algorithm, byte[] salt, int iterations) throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance(algorithm);
if (salt != null) {
digest.update(salt);
}
byte[] result = digest.digest(input);
for (int i = 1; i < iterations; i++) {
digest.reset();
result = digest.digest(result);
}
return result;
}
/**
* 生成随机的Byte[]作为salt.
*
* @param numBytes byte数组的大小
*/
private static byte[] generateSalt(int numBytes) {
Validate.isTrue(numBytes > 0, "numBytes argument must be a positive integer (1 or larger)", numBytes);
byte[] bytes = new byte[numBytes];
random.nextBytes(bytes);
return bytes;
}
}
/**
* Copyright (c) 2016-2019 人人开源 All rights reserved.
*
*
* 版权所有,侵权必究!
*/
package com.qkdata.common.util;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
public class HttpContextUtils {
public static HttpServletRequest getHttpServletRequest() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
}
public static String getDomain(){
HttpServletRequest request = getHttpServletRequest();
StringBuffer url = request.getRequestURL();
return url.delete(url.length() - request.getRequestURI().length(), url.length()).toString();
}
public static String getOrigin(){
HttpServletRequest request = getHttpServletRequest();
return request.getHeader("Origin");
}
}
/**
* Copyright (c) 2016-2019 人人开源 All rights reserved.
*
*
* 版权所有,侵权必究!
*/
package com.qkdata.common.util;
import com.alibaba.druid.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
public class IPUtils {
private static final Logger logger = LoggerFactory.getLogger(IPUtils.class);
/**
* 获取IP地址
*
* 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址
* 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址
*/
public static String getIpAddr(HttpServletRequest request) {
String ip = null;
try {
ip = request.getHeader("x-forwarded-for");
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
} catch (Exception e) {
logger.error("IPUtils ERROR ", e);
}
// //使用代理,则获取第一个IP地址
// if(StringUtils.isEmpty(ip) && ip.length() > 15) {
// if(ip.indexOf(",") > 0) {
// ip = ip.substring(0, ip.indexOf(","));
// }
// }
return ip;
}
}
package com.qkdata.common.util;
import java.util.Objects;
import java.util.function.BiConsumer;
/**
* 带索引的forEach方法
*/
public class Iterables {
public static <E> void forEach(Iterable<? extends E> elements, BiConsumer<Integer, ? super E> action) {
Objects.requireNonNull(elements);
Objects.requireNonNull(action);
int index = 0;
for (E element : elements) {
action.accept(index++, element);
}
}
}
package com.qkdata.common.util;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
/**
* @author songminghui
* @date 2018-12-17 下午6:00
* @email songminghui@shangweiec.com
* @description
*/
public class MD5Util {
public static String md5(String str) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(str.getBytes(StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100), 1, 3);
}
return sb.toString().toUpperCase();
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
}
package com.qkdata.common.util;
import java.util.concurrent.ThreadLocalRandom;
/**
* 随机数生成器
*/
public final class RandomDigitGenerator {
private RandomDigitGenerator() {
}
private static final int DEFAULT_COUNT = 6;
private static final ThreadLocalRandom RANDOM = ThreadLocalRandom.current();
private static final int ORIGIN = 0;
private static final int BOUND = 10;
/**
* 默认生成6位数字
*
* @return
*/
public static String generate() {
return generate(DEFAULT_COUNT);
}
/**
* 生成指定长度的数字
*
* @param count 长度
* @return
*/
public static String generate(int count) {
if (count <= 0 || count > 1000) {
return "";
}
return RANDOM.ints(ORIGIN, BOUND)
.limit(count)
.collect(StringBuilder::new, StringBuilder::append, StringBuilder::append)
.toString();
}
}
package com.qkdata.common.util;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.HttpMethod;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Map;
/**
* @author songminghui
* @date 2018-11-26 下午6:52
* @email songminghui@shangweiec.com
* @description
*/
public class RequestUtil {
private RequestUtil() {
}
public static HttpServletRequest getRequest() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
}
public static String getIpAddress() {
HttpServletRequest request = RequestUtil.getRequest();
String ip = request.getHeader("X-Real-IP");
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("x-forwarded-for");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
public static String getParam(HttpServletRequest request) {
BufferedReader br = null;
try {
String method = request.getMethod();
if (HttpMethod.GET.matches(method)) {
Map<String, String[]> parameterMap = request.getParameterMap();
return new ObjectMapper().writeValueAsString(parameterMap);
}
br = new BufferedReader(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8));
} catch (Exception e) {
}
String line = null;
StringBuilder sb = new StringBuilder();
try {
while ((line = br.readLine()) != null) {
sb.append(line);
}
} catch (Exception e) {
}
return sb.toString();
}
}
package com.qkdata.common.util;
import java.util.Random;
public class ShareCodeUtils {
/**
* 自定义进制(0,1没有加入,容易与o,l混淆),数组顺序可进行调整增加反推难度,A用来补位因此此数组不包含A,共31个字符。
*/
private static final char[] BASE = new char[]{'H', 'V', 'E', '8', 'S', '2', 'D', 'Z', 'X', '9', 'C', '7', 'P',
'5', 'I', 'K', '3', 'M', 'J', 'U', 'F', 'R', '4', 'W', 'Y', 'L', 'T', 'N', '6', 'B', 'G', 'Q'};
/**
* A补位字符,不能与自定义重复
*/
private static final char SUFFIX_CHAR = 'A';
/**
* 进制长度
*/
private static final int BIN_LEN = BASE.length;
/**
* 生成邀请码最小长度
*/
private static final int CODE_LEN = 6;
public static String mobileToCode(String mobile){
Long id = Long.parseLong(mobile);
return idToCode(id);
}
public static String codeToMobile(String code){
Long id = codeToId(code);
return String.valueOf(id);
}
/**
* ID转换为邀请码
*
* @param id
* @return
*/
private static String idToCode(Long id) {
char[] buf = new char[BIN_LEN];
int charPos = BIN_LEN;
// 当id除以数组长度结果大于0,则进行取模操作,并以取模的值作为数组的坐标获得对应的字符
while (id / BIN_LEN > 0) {
int index = (int) (id % BIN_LEN);
buf[--charPos] = BASE[index];
id /= BIN_LEN;
}
buf[--charPos] = BASE[(int) (id % BIN_LEN)];
// 将字符数组转化为字符串
String result = new String(buf, charPos, BIN_LEN - charPos);
// 长度不足指定长度则随机补全
int len = result.length();
if (len < CODE_LEN) {
StringBuilder sb = new StringBuilder();
sb.append(SUFFIX_CHAR);
Random random = new Random();
// 去除SUFFIX_CHAR本身占位之后需要补齐的位数
for (int i = 0; i < CODE_LEN - len - 1; i++) {
sb.append(BASE[random.nextInt(BIN_LEN)]);
}
result += sb.toString();
}
return result;
}
/**
* 邀请码解析出ID<br/>
* 基本操作思路恰好与idToCode反向操作。
*
* @param code
* @return
*/
private static Long codeToId(String code) {
char[] charArray = code.toCharArray();
long result = 0L;
for (int i = 0; i < charArray.length; i++) {
int index = 0;
for (int j = 0; j < BIN_LEN; j++) {
if (charArray[i] == BASE[j]) {
index = j;
break;
}
}
if (charArray[i] == SUFFIX_CHAR) {
break;
}
if (i > 0) {
result = result * BIN_LEN + index;
} else {
result = index;
}
}
return result;
}
public static void main(String[] args) {
String mobile = "18678057587";//Constants.ADMIN_MOBILE;
String code = mobileToCode(mobile);
System.out.println(code);
String str = codeToMobile("MZTLU8J");
System.out.println(str);
}
}
package com.qkdata.common.util;
import com.qkdata.biz.sys.entity.SysUserPO;
import org.apache.shiro.SecurityUtils;
public class UserContext {
public static SysUserPO getUser(){
SysUserPO user = (SysUserPO)SecurityUtils.getSubject().getPrincipal();
return user;
}
public static Long getUserId(){
return getUser().getId();
}
}
package com.qkdata.wx.miniapp.config;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage;
import cn.binarywang.wx.miniapp.bean.WxMaTemplateData;
import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage;
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import cn.binarywang.wx.miniapp.message.WxMaMessageHandler;
import cn.binarywang.wx.miniapp.message.WxMaMessageRouter;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Configuration
@EnableConfigurationProperties(WxMaProperties.class)
public class WxMaConfiguration {
private WxMaProperties properties;
private static Map<String, WxMaMessageRouter> routers = Maps.newHashMap();
private static Map<String, WxMaService> maServices = Maps.newHashMap();
@Autowired
public WxMaConfiguration(WxMaProperties properties) {
this.properties = properties;
}
public static WxMaService getMaService(String appid) {
WxMaService wxService = maServices.get(appid);
if (wxService == null) {
throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));
}
return wxService;
}
public static WxMaMessageRouter getRouter(String appid) {
return routers.get(appid);
}
@PostConstruct
public void init() {
List<WxMaProperties.Config> configs = this.properties.getConfigs();
if (configs == null) {
throw new RuntimeException("大哥,拜托先看下项目首页的说明(readme文件),添加下相关配置,注意别配错了!");
}
maServices = configs.stream()
.map(a -> {
WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
config.setAppid(a.getAppid());
config.setSecret(a.getSecret());
config.setToken(a.getToken());
config.setAesKey(a.getAesKey());
config.setMsgDataFormat(a.getMsgDataFormat());
WxMaService service = new WxMaServiceImpl();
service.setWxMaConfig(config);
routers.put(a.getAppid(), this.newRouter(service));
return service;
}).collect(Collectors.toMap(s -> s.getWxMaConfig().getAppid(), a -> a));
}
private WxMaMessageRouter newRouter(WxMaService service) {
final WxMaMessageRouter router = new WxMaMessageRouter(service);
router
.rule().handler(logHandler).next()
.rule().async(false).content("模板").handler(templateMsgHandler).end()
.rule().async(false).content("文本").handler(textHandler).end()
.rule().async(false).content("图片").handler(picHandler).end()
.rule().async(false).content("二维码").handler(qrcodeHandler).end();
return router;
}
private final WxMaMessageHandler templateMsgHandler = (wxMessage, context, service, sessionManager) -> {
service.getMsgService().sendTemplateMsg(WxMaTemplateMessage.builder()
.templateId("此处更换为自己的模板id")
.formId("自己替换可用的formid")
.data(Lists.newArrayList(
new WxMaTemplateData("keyword1", "339208499", "#173177")))
.toUser(wxMessage.getFromUser())
.build());
return null;
};
private final WxMaMessageHandler logHandler = (wxMessage, context, service, sessionManager) -> {
System.out.println("收到消息:" + wxMessage.toString());
service.getMsgService().sendKefuMsg(WxMaKefuMessage.newTextBuilder().content("收到信息为:" + wxMessage.toJson())
.toUser(wxMessage.getFromUser()).build());
return null;
};
private final WxMaMessageHandler textHandler = (wxMessage, context, service, sessionManager) -> {
service.getMsgService().sendKefuMsg(WxMaKefuMessage.newTextBuilder().content("回复文本消息")
.toUser(wxMessage.getFromUser()).build());
return null;
};
private final WxMaMessageHandler picHandler = (wxMessage, context, service, sessionManager) -> {
try {
WxMediaUploadResult uploadResult = service.getMediaService()
.uploadMedia("image", "png",
ClassLoader.getSystemResourceAsStream("tmp.png"));
service.getMsgService().sendKefuMsg(
WxMaKefuMessage
.newImageBuilder()
.mediaId(uploadResult.getMediaId())
.toUser(wxMessage.getFromUser())
.build());
} catch (WxErrorException e) {
e.printStackTrace();
}
return null;
};
private final WxMaMessageHandler qrcodeHandler = (wxMessage, context, service, sessionManager) -> {
try {
final File file = service.getQrcodeService().createQrcode("123", 430);
WxMediaUploadResult uploadResult = service.getMediaService().uploadMedia("image", file);
service.getMsgService().sendKefuMsg(
WxMaKefuMessage
.newImageBuilder()
.mediaId(uploadResult.getMediaId())
.toUser(wxMessage.getFromUser())
.build());
} catch (WxErrorException e) {
e.printStackTrace();
}
return null;
};
}
package com.qkdata.wx.miniapp.config;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import lombok.Data;
/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Data
@ConfigurationProperties(prefix = "wx.miniapp")
public class WxMaProperties {
private List<Config> configs;
@Data
public static class Config {
/**
* 设置微信小程序的appid
*/
private String appid;
/**
* 设置微信小程序的Secret
*/
private String secret;
/**
* 设置微信小程序消息服务器配置的token
*/
private String token;
/**
* 设置微信小程序消息服务器配置的EncodingAESKey
*/
private String aesKey;
/**
* 消息格式,XML或者JSON
*/
private String msgDataFormat;
}
}
package com.qkdata.wx.miniapp.controller;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaMessage;
import cn.binarywang.wx.miniapp.constant.WxMaConstants;
import com.qkdata.wx.miniapp.config.WxMaConfiguration;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;
import java.util.Objects;
/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@RestController
@RequestMapping("/wx/portal/{appid}")
public class WxPortalController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@GetMapping(produces = "text/plain;charset=utf-8")
public String authGet(@PathVariable String appid,
@RequestParam(name = "signature", required = false) String signature,
@RequestParam(name = "timestamp", required = false) String timestamp,
@RequestParam(name = "nonce", required = false) String nonce,
@RequestParam(name = "echostr", required = false) String echostr) {
this.logger.info("\n接收到来自微信服务器的认证消息:signature = [{}], timestamp = [{}], nonce = [{}], echostr = [{}]",
signature, timestamp, nonce, echostr);
if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) {
throw new IllegalArgumentException("请求参数非法,请核实!");
}
final WxMaService wxService = WxMaConfiguration.getMaService(appid);
if (wxService.checkSignature(timestamp, nonce, signature)) {
return echostr;
}
return "非法请求";
}
@PostMapping(produces = "application/xml; charset=UTF-8")
public String post(@PathVariable String appid,
@RequestBody String requestBody,
@RequestParam(name = "msg_signature", required = false) String msgSignature,
@RequestParam(name = "encrypt_type", required = false) String encryptType,
@RequestParam(name = "signature", required = false) String signature,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce) {
this.logger.info("\n接收微信请求:[msg_signature=[{}], encrypt_type=[{}], signature=[{}]," +
" timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
msgSignature, encryptType, signature, timestamp, nonce, requestBody);
final WxMaService wxService = WxMaConfiguration.getMaService(appid);
final boolean isJson = Objects.equals(wxService.getWxMaConfig().getMsgDataFormat(),
WxMaConstants.MsgDataFormat.JSON);
if (StringUtils.isBlank(encryptType)) {
// 明文传输的消息
WxMaMessage inMessage;
if (isJson) {
inMessage = WxMaMessage.fromJson(requestBody);
} else {//xml
inMessage = WxMaMessage.fromXml(requestBody);
}
this.route(inMessage, appid);
return "success";
}
if ("aes".equals(encryptType)) {
// 是aes加密的消息
WxMaMessage inMessage;
if (isJson) {
inMessage = WxMaMessage.fromEncryptedJson(requestBody, wxService.getWxMaConfig());
} else {//xml
inMessage = WxMaMessage.fromEncryptedXml(requestBody, wxService.getWxMaConfig(),
timestamp, nonce, msgSignature);
}
this.route(inMessage, appid);
return "success";
}
throw new RuntimeException("不可识别的加密类型:" + encryptType);
}
private void route(WxMaMessage message, String appid) {
try {
WxMaConfiguration.getRouter(appid).route(message);
} catch (Exception e) {
this.logger.error(e.getMessage(), e);
}
}
}
server:
port: 9090
spring:
datasource:
druid:
url: jdbc:mysql://localhost:3306/online-edu?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
username: root
password: 123456
redis:
host: localhost
port: 6379
log:
path: /Users/liuyang/work/argus_work/online-edu/data/logs
file:
path: /Users/liuyang/work/argus_work/online-edu/data/files #../data/files
server:
port: 80
servlet:
context-path: /online-edu-backend
multipart:
enable: true
max-file-size: 10M
# ssl:
# key-store: classpath:3275085_datahub.qiankundata.com.pfx
# key-store-password: cDU6D68N
connection-timeout: 15000
tomcat:
max-threads: 1000
management:
server:
port: 8080
ssl:
enabled: false
# endpoint:
# health:
# show-details: always
spring:
datasource:
druid:
url: jdbc:mysql://mysql:3306/framework?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
username: root
password: qkdata
driver-class-name: com.mysql.jdbc.Driver
initialSize: 50
minIdle: 10
maxActive: 200
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: false
maxPoolPreparedStatementPerConnectionSize: -1
filters: stat,slf4j,config
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMil=3000
useGlobalDataSourceStat: true
filter:
wall:
config:
multi-statement-allow: true
redis:
host: redis
port: 6379
database: 0
lettuce:
pool:
max-active: 200
max-idle: 10
min-idle: 0
mvc:
throw-exception-if-no-handler-found: true
resources:
add-mappings: true
main:
allow-bean-definition-overriding: true
mybatis-plus:
mapper-locations: classpath:/mappers/*Mapper.xml
#实体扫描,多个package用逗号或者分号分隔
typeAliasesPackage: com.qkdata.biz.sys.entity
typeEnumsPackage: com.qkdata.biz.enums
configuration:
map-underscore-to-camel-case: true
global-config:
db-config:
id-type: auto
log:
context: online-edu-backend
path: /data/logs
file:
path: /data/files
downloadPath: /download/ #前台获取静态资源时的context
frontend:
domain: https://qdytx888.cn #前端访问的域名
jwt:
secret: qkdata
enable-interceptor: true
include-urls:
- /api/**
exclude-urls:
- /api/wx/user/**
- /api/sys/login
aud: qkdata
exp: 720 #24*30 30天
wx:
miniapp:
configs:
- appid: wx4a74df8356c465a5 #微信小程序的appid
secret: 415f8d52311eeb0e5eef32a5e41758ae #微信小程序的Secret
token: #微信小程序消息服务器配置的token
aesKey: #微信小程序消息服务器配置的EncodingAESKey
msgDataFormat: JSON
sms:
api:
v3: http://sms-service/sms/api/v3
-- ----------------------------
-- Table structure for allow_org_course
-- ----------------------------
DROP TABLE IF EXISTS `allow_org_course`;
CREATE TABLE `allow_org_course` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`course_id` bigint(20) DEFAULT NULL COMMENT '课程ID',
`org_id` bigint(20) DEFAULT NULL COMMENT '企业ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='授权企业可见的课程';
-- ----------------------------
-- Table structure for course
-- ----------------------------
DROP TABLE IF EXISTS `course`;
CREATE TABLE `course` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`type` varchar(20) DEFAULT NULL COMMENT '课程类型(单集、系列)',
`name` varchar(255) DEFAULT NULL COMMENT '课程名称',
`introduce` text COMMENT '课程简介',
`logo_url` varchar(255) DEFAULT NULL COMMENT '课程封面url',
`status` varchar(10) DEFAULT NULL COMMENT '状态(上架、下架)',
`teacher_id` bigint(20) DEFAULT NULL COMMENT '讲师ID',
`charge_model` varchar(20) DEFAULT NULL COMMENT '收费模式(免费、会员免费、付费点播)',
`price` decimal(10,2) DEFAULT NULL COMMENT '付费点播普通用户价',
`vip_price` decimal(10,2) DEFAULT NULL COMMENT '付费点播会员价',
`valid_period` int(10) DEFAULT NULL COMMENT '有效时间(小时)',
`model` varchar(20) DEFAULT NULL COMMENT '课程形态(点播、直播、一对一)',
`series_id` bigint(20) DEFAULT NULL COMMENT '系列ID',
`allow` varchar(10) DEFAULT NULL COMMENT '可见范围(全部、指定企业)',
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
`is_del` tinyint(1) DEFAULT NULL COMMENT '删除标识',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='课程';
-- ----------------------------
-- Table structure for course_chapter
-- ----------------------------
DROP TABLE IF EXISTS `course_chapter`;
CREATE TABLE `course_chapter` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`course_id` bigint(20) DEFAULT NULL COMMENT '课程ID',
`resource_id` bigint(20) DEFAULT NULL COMMENT '视频ID',
`name` varchar(255) DEFAULT NULL COMMENT '章节标题',
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='课程章节';
-- ----------------------------
-- Table structure for course_consume_record
-- ----------------------------
DROP TABLE IF EXISTS `course_consume_record`;
CREATE TABLE `course_consume_record` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`user_id` bigint(20) DEFAULT NULL COMMENT '用户ID',
`watch_time` bigint(20) DEFAULT NULL COMMENT '观看时长(分钟)',
`course_id` bigint(20) DEFAULT NULL COMMENT '课程ID',
`chapter_id` bigint(20) DEFAULT NULL COMMENT '章节ID',
`video_id` bigint(20) DEFAULT NULL COMMENT '视频ID',
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='课程消耗记录';
-- ----------------------------
-- Table structure for course_series
-- ----------------------------
DROP TABLE IF EXISTS `course_series`;
CREATE TABLE `course_series` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(255) DEFAULT NULL COMMENT '系列名称',
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='课程系列';
-- ----------------------------
-- Table structure for course_tag
-- ----------------------------
DROP TABLE IF EXISTS `course_tag`;
CREATE TABLE `course_tag` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(255) DEFAULT NULL COMMENT '名称',
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='课程标签';
-- ----------------------------
-- Table structure for course_tag_rel
-- ----------------------------
DROP TABLE IF EXISTS `course_tag_rel`;
CREATE TABLE `course_tag_rel` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`course_id` bigint(20) DEFAULT NULL COMMENT '课程ID',
`tag_id` bigint(20) DEFAULT NULL COMMENT '标签ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='课程与标签关联表';
-- ----------------------------
-- Table structure for exam
-- ----------------------------
DROP TABLE IF EXISTS `exam`;
CREATE TABLE `exam` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(255) DEFAULT NULL COMMENT '考试名称',
`limit` int(11) DEFAULT NULL COMMENT '考试限时(分钟)',
`description` varchar(255) DEFAULT NULL COMMENT '考试简述',
`question_ids_radio` varchar(512) DEFAULT NULL COMMENT '单选题ID集合,以逗号分隔',
`question_ids_check` varchar(512) DEFAULT NULL COMMENT '多选题ID集合,以逗号分隔',
`question_ids_judge` varchar(512) DEFAULT NULL COMMENT '判断题ID集合,以逗号分隔',
`score_radio` int(11) DEFAULT NULL COMMENT '当前考试每个单选题的分数',
`score_check` int(11) DEFAULT NULL COMMENT '当前考试每个多选题的分数',
`score_judge` int(11) DEFAULT NULL COMMENT '当前考试每个判断题的分数',
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='考试信息';
-- ----------------------------
-- Table structure for exam_question
-- ----------------------------
DROP TABLE IF EXISTS `exam_question`;
CREATE TABLE `exam_question` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` text COMMENT '题目名称',
`type` varchar(20) DEFAULT NULL COMMENT '题目类型(单选、多选、判断)',
`option_ids` varchar(255) DEFAULT NULL COMMENT '选项ID集合,以逗号分隔',
`answer_ids` varchar(255) DEFAULT NULL COMMENT '签案ID集合,以逗号分隔',
`description` text COMMENT '题目解析',
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='题目信息';
-- ----------------------------
-- Table structure for exam_question_option
-- ----------------------------
DROP TABLE IF EXISTS `exam_question_option`;
CREATE TABLE `exam_question_option` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`选项内容` text COMMENT '选项内容',
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='题目选项';
-- ----------------------------
-- Table structure for exam_record
-- ----------------------------
DROP TABLE IF EXISTS `exam_record`;
CREATE TABLE `exam_record` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`user_id` bigint(20) DEFAULT NULL COMMENT '用户ID',
`exam_id` bigint(20) DEFAULT NULL COMMENT '考试ID',
`start_time` datetime DEFAULT NULL COMMENT '考试时间',
`end_time` datetime DEFAULT NULL COMMENT '完成时间',
`score` int(11) DEFAULT NULL COMMENT '得分',
`answer_ids` varchar(512) DEFAULT NULL COMMENT '答案ID集合,以逗号分隔',
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='考试记录';
-- ----------------------------
-- Table structure for org_buy_course_record
-- ----------------------------
DROP TABLE IF EXISTS `org_buy_course_record`;
CREATE TABLE `org_buy_course_record` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`org_id` bigint(20) DEFAULT NULL COMMENT '企业ID',
`course_id` bigint(20) DEFAULT NULL COMMENT '课程ID',
`count` int(10) DEFAULT NULL COMMENT '剩余份数',
`open` tinyint(1) DEFAULT NULL COMMENT '是否开放成员领取',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='企业购买会员记录';
-- ----------------------------
-- Table structure for org_buy_vip_record
-- ----------------------------
DROP TABLE IF EXISTS `org_buy_vip_record`;
CREATE TABLE `org_buy_vip_record` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`org_id` bigint(20) DEFAULT NULL COMMENT '企业ID',
`vip_count` int(11) DEFAULT NULL COMMENT '会员剩余个数',
`open` tinyint(1) DEFAULT NULL COMMENT '是否开放成员领取',
`limit` int(10) DEFAULT NULL COMMENT '限制领取时长',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='企业购买会员记录';
-- ----------------------------
-- Table structure for organization
-- ----------------------------
DROP TABLE IF EXISTS `organization`;
CREATE TABLE `organization` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(100) DEFAULT NULL COMMENT '组织名称',
`type` varchar(20) DEFAULT NULL COMMENT '组织类型(企业、培训机构)',
`contact_tel` varchar(20) DEFAULT NULL COMMENT '联系人电话',
`contact_name` varchar(20) DEFAULT NULL COMMENT '联系人名称',
`logo_url` varchar(1000) DEFAULT NULL COMMENT '企业LOG',
`province` varchar(255) DEFAULT NULL COMMENT '省份',
`city` varchar(255) DEFAULT NULL COMMENT '城市',
`address` varchar(1000) DEFAULT NULL COMMENT '详细地址',
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`is_del` tinyint(1) DEFAULT NULL COMMENT '删除标识',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='组织';
-- ----------------------------
-- Table structure for recommend_config
-- ----------------------------
DROP TABLE IF EXISTS `recommend_config`;
CREATE TABLE `recommend_config` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`course_id` bigint(20) DEFAULT NULL COMMENT '课程ID',
`sort_no` int(11) DEFAULT NULL COMMENT '排序号',
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='推荐课程配置';
-- ----------------------------
-- Table structure for resource
-- ----------------------------
DROP TABLE IF EXISTS `resource`;
CREATE TABLE `resource` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`oss_id` varchar(255) DEFAULT NULL COMMENT '第三方存储ID',
`status` varchar(20) DEFAULT NULL COMMENT '状态(待上传、上传失败、上传成功)',
`size` float DEFAULT NULL COMMENT '资源大小',
`video_duration` float DEFAULT NULL COMMENT '视频时长',
`url` varchar(255) DEFAULT NULL COMMENT '访问url',
`name` varchar(255) DEFAULT NULL COMMENT '名称',
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='资源';
-- ----------------------------
-- Table structure for swiper_config
-- ----------------------------
DROP TABLE IF EXISTS `swiper_config`;
CREATE TABLE `swiper_config` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`image_url` varchar(1000) DEFAULT NULL COMMENT '封面图片url',
`course_id` bigint(20) DEFAULT NULL COMMENT '课程ID',
`sort_no` int(11) DEFAULT NULL COMMENT '排序号',
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='轮播图配置';
-- ----------------------------
-- Table structure for sys_log
-- ----------------------------
DROP TABLE IF EXISTS `sys_log`;
CREATE TABLE `sys_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`username` varchar(20) DEFAULT NULL COMMENT '用户名',
`operation` varchar(255) DEFAULT NULL COMMENT '用户操作',
`method` varchar(255) DEFAULT NULL COMMENT '请求方法',
`params` text COMMENT '请求参数',
`time` bigint(20) DEFAULT NULL COMMENT '执行时长(毫秒)',
`ip` varchar(64) DEFAULT NULL COMMENT 'IP地址',
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统操作日志';
-- ----------------------------
-- Table structure for sys_menu
-- ----------------------------
DROP TABLE IF EXISTS `sys_menu`;
CREATE TABLE `sys_menu` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`parent_id` bigint(20) DEFAULT NULL COMMENT '父菜单ID,一级菜单为0',
`name` varchar(50) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '菜单名称',
`url` varchar(200) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '菜单URL',
`perms` varchar(500) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '授权(多个用逗号分隔,如:user:list,user:create)',
`type` int(11) DEFAULT NULL COMMENT '类型 0:目录 1:菜单 2:按钮',
`icon` varchar(50) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '菜单图标',
`order_num` int(11) DEFAULT NULL COMMENT '排序',
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=108 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC COMMENT='菜单管理';
-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`code` varchar(20) DEFAULT NULL COMMENT '角色代码',
`name` varchar(100) DEFAULT NULL COMMENT '角色名称',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COMMENT='角色表';
-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES (1, 'ADMIN', '系统管理员', '2021-04-25 16:10:06', '2021-04-25 16:10:06');
INSERT INTO `sys_role` VALUES (2, 'OPERATOR', '运营人员', '2021-04-25 16:10:06', '2021-04-25 16:10:06');
INSERT INTO `sys_role` VALUES (3, 'ENTERPRISE_ADMIN', '企业管理员', '2021-04-25 16:10:06', '2021-04-25 16:10:06');
INSERT INTO `sys_role` VALUES (4, 'INSTITUTION_ADMIN', '培训机构管理员', '2021-04-25 16:10:06', '2021-04-25 16:10:06');
INSERT INTO `sys_role` VALUES (5, 'USER', '普通用户', '2021-04-25 16:10:06', '2021-04-25 16:10:06');
-- ----------------------------
-- Table structure for sys_role_menu
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_menu`;
CREATE TABLE `sys_role_menu` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`role_id` bigint(20) DEFAULT NULL COMMENT '角色ID',
`menu_id` bigint(20) DEFAULT NULL COMMENT '菜单ID',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC COMMENT='角色与菜单对应关系';
-- ----------------------------
-- Table structure for sys_config
-- ----------------------------
DROP TABLE IF EXISTS `sys_config`;
CREATE TABLE `sys_config` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`param_key` varchar(50) COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'key',
`param_value` varchar(2000) COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'value',
`status` tinyint(4) DEFAULT '1' COMMENT '状态 0:隐藏 1:显示',
`remark` varchar(500) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '备注',
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `param_key` (`param_key`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC COMMENT='系统配置信息表';
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`username` varchar(20) NOT NULL COMMENT '手机号',
`password` varchar(100) DEFAULT NULL COMMENT '密码',
`salt` varchar(20) DEFAULT NULL COMMENT '盐',
`nick_name` varchar(50) DEFAULT NULL COMMENT '昵称',
`status` tinyint(1) DEFAULT NULL COMMENT '状态',
`create_time` datetime DEFAULT NULL COMMENT '注册时间',
`last_login_time` datetime DEFAULT NULL COMMENT '最后登录时间',
`activate_time` datetime DEFAULT NULL COMMENT '激活时间',
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
`is_del` tinyint(1) DEFAULT NULL COMMENT '删除标识',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES (1, '13100000000', 'cdac762d0ba79875489f6a8b430fa8b5dfe0cdd81da38b80f02f33328af7fd4a', 'YzcmCZNvbXocrsz9dm8e', '系管理员', 1, '2021-04-25 16:10:06', NULL, NULL, '2021-04-25 16:10:06', 0);
-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`role_id` bigint(20) NOT NULL COMMENT '角色ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='用户角色关联表';
-- ----------------------------
-- Table structure for teacher
-- ----------------------------
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`code` varchar(100) DEFAULT NULL COMMENT '编号',
`name` varchar(20) DEFAULT NULL COMMENT '姓名',
`mobile` varchar(20) DEFAULT NULL COMMENT '电话',
`org_id` bigint(20) DEFAULT NULL COMMENT '所属机构ID',
`introduce` text COMMENT '简介',
`avatar_url` varchar(1000) DEFAULT NULL COMMENT '头像',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`is_del` tinyint(1) DEFAULT NULL COMMENT '删除标识',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='讲师';
-- ----------------------------
-- Table structure for user_ext
-- ----------------------------
DROP TABLE IF EXISTS `user_ext`;
CREATE TABLE `user_ext` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`user_id` bigint(20) DEFAULT NULL COMMENT '用户ID',
`name` varchar(20) DEFAULT NULL COMMENT '姓名',
`sex` varchar(10) DEFAULT NULL COMMENT '性别',
`age` int(10) DEFAULT NULL COMMENT '年龄',
`avatar_url` varchar(1000) DEFAULT NULL COMMENT '头像url',
`type` varchar(20) DEFAULT NULL COMMENT '用户类型(普通用户、会员、企业会员)',
`enterprise_id` bigint(20) DEFAULT NULL COMMENT '企业ID',
`vip_start_time` datetime DEFAULT NULL COMMENT '会员生效时间',
`vip_end_time` datetime DEFAULT NULL COMMENT '会员结束时间',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='用户扩展表';
-- ----------------------------
-- Records of user_ext
-- ----------------------------
INSERT INTO `user_ext` VALUES (1, 1, '系统管理员', NULL, NULL, NULL, NULL, NULL, NULL, NULL, '2021-04-25 16:10:06', '2021-04-25 16:10:06');
-- ----------------------------
-- Table structure for user_favorites
-- ----------------------------
DROP TABLE IF EXISTS `user_favorites`;
CREATE TABLE `user_favorites` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`user_id` bigint(10) DEFAULT NULL COMMENT '用户ID',
`course_id` bigint(20) DEFAULT NULL COMMENT '课程ID',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户收藏课程';
-- ----------------------------
-- Table structure for user_get_vip_record
-- ----------------------------
DROP TABLE IF EXISTS `user_get_vip_record`;
CREATE TABLE `user_get_vip_record` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`create_time` datetime DEFAULT NULL COMMENT '领取时间',
`update_time` datetime DEFAULT NULL,
`user_id` bigint(20) DEFAULT NULL COMMENT '用户ID',
`type` varchar(20) DEFAULT NULL COMMENT '领取类型(月卡、付费点播课程)',
`course_id` bigint(20) DEFAULT NULL COMMENT '课程ID',
`org_id` bigint(20) DEFAULT NULL COMMENT '企业ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='企业会员领取记录';
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<springProperty scope="context" name="LOG_PATH" source="log.path"/>
<springProperty scope="context" name="LOG_CONTEXT" source="log.context"/>
<property name="PATTERN" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] [%X{traceId}] %-5level %logger{50} - %line %msg %n"/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<appender name="rollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${LOG_PATH}/${LOG_CONTEXT}.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/${LOG_CONTEXT}.log.%i.%d{yyyy-MM-dd}</fileNamePattern>
<maxHistory>30</maxHistory>
<TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>30MB</maxFileSize>
</TimeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<Pattern>${PATTERN}</Pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<appender name="sql" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${LOG_PATH}/${LOG_CONTEXT}-sql.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/${LOG_CONTEXT}-sql.log.%i.%d{yyyy-MM-dd}</fileNamePattern>
<maxHistory>30</maxHistory>
<TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>30MB</maxFileSize>
</TimeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<Pattern>${PATTERN}</Pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<logger name="com.qkdata" level="DEBUG" />
<!-- <logger name="com.qkdata.biz.sys.repository" level="ERROR" additivity="false">-->
<!-- <appender-ref ref="sql" />-->
<!-- </logger>-->
<root level="INFO">
<appender-ref ref="console" />
<appender-ref ref="rollingFile" />
</root>
</configuration>
\ No newline at end of file
<?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.qkdata.biz.sys.mapper.SysConfigMapper">
<select id="queryPageList" resultType="com.qkdata.biz.sys.entity.SysConfigPO">
select * from sys_config where 1=1
<if test="p.paramKey != null and p.paramKey != ''">
and param_key like concat('%',#{p.paramKey},'%')
</if>
</select>
</mapper>
\ No newline at end of file
<?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.qkdata.biz.sys.mapper.SysLogMapper">
<select id="queryPageList" resultType="com.qkdata.biz.sys.entity.SysLogPO">
select * from sys_log where 1=1
<if test="p.condition != null and p.condition != ''">
and username like concat('%',#{p.condition},'%') or operation like concat('%',#{p.condition},'%')
</if>
order by create_time desc
</select>
</mapper>
\ No newline at end of file
<?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.qkdata.biz.sys.mapper.SysMenuMapper">
<select id="queryNotButtonList" resultType="com.qkdata.biz.sys.entity.SysMenuPO">
select * from sys_menu where type != 2 order by order_num asc
</select>
</mapper>
\ No newline at end of file
<?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.qkdata.biz.sys.mapper.SysRoleMapper">
<select id="queryPageList" resultType="com.qkdata.biz.sys.entity.SysRolePO">
select *
from sys_role
where 1=1
<if test="p.name != null and p.name != ''">
and name like concat('%',#{p.name},'%')
</if>
order by id desc
</select>
</mapper>
\ No newline at end of file
<?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.qkdata.biz.sys.mapper.SysRoleMenuMapper">
<select id="selectMenuIdsByRoleId" resultType="java.lang.Long">
select menu_id from sys_role_menu where role_id = #{roleId}
</select>
</mapper>
\ No newline at end of file
<?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.qkdata.biz.sys.mapper.SysUserMapper">
<select id="queryAllPerms" resultType="java.lang.String">
select m.perms from sys_user_role ur
LEFT JOIN sys_role_menu rm on ur.role_id = rm.role_id
LEFT JOIN sys_menu m on rm.menu_id = m.id
where ur.user_id = #{userId}
</select>
<select id="queryPageList" resultType="com.qkdata.biz.sys.vo.SysUserModel">
select id,username,email,mobile,status
from sys_user
where 1=1
<if test="p.username != null and p.username != ''">
and username like concat('%',#{p.username},'%')
</if>
order by create_time desc
</select>
<select id="queryAllMenuId" resultType="java.lang.Long">
select distinct rm.menu_id from sys_user_role ur
LEFT JOIN sys_role_menu rm on ur.role_id = rm.role_id
where ur.user_id = #{userId}
</select>
</mapper>
\ No newline at end of file
<?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.qkdata.biz.sys.mapper.SysUserRoleMapper">
<select id="queryRoleIdList" resultType="java.lang.Long">
select role_id from sys_user_role where user_id = #{userId}
</select>
</mapper>
\ No newline at end of file
package com.qkdata.autogenerator;
import cn.hutool.core.util.ArrayUtil;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class CodeGenerator {
/**
* <p>
* 读取控制台内容
* </p>
*/
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
StringBuilder help = new StringBuilder();
help.append("请输入" + tip + ":");
System.out.println(help.toString());
if (scanner.hasNext()) {
String ipt = scanner.next();
if (StringUtils.isNotBlank(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "!");
}
public static void main(String[] args) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("liuyang");
gc.setOpen(false);
gc.setEntityName("%sPO");
gc.setServiceName("%sService");
// gc.setSwagger2(true); 实体属性 Swagger2 注解
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/online-edu?useUnicode=true&useSSL=false&characterEncoding=utf8");
// dsc.setSchemaName("public");
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName(scanner("模块名"));
pc.setParent("com.qkdata.biz");
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
}
};
// 如果模板引擎是 freemarker
String templatePath = "/templates/mapper.xml.ftl";
// 如果模板引擎是 velocity
// String templatePath = "/templates/mapper.xml.vm";
String serviceTemplatePath = "/templates/ftl/Service.java.ftl";
// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
// 自定义配置会被优先输出
focList.add(new FileOutConfig(templatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
return projectPath + "/src/main/resources/mappers/" + pc.getModuleName()
+ "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
focList.add(new FileOutConfig(serviceTemplatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
return projectPath + "src/main/java/" + pc.getModuleName()
+ "/service/" + tableInfo.getEntityName() + "Service" + StringPool.DOT_JAVA;
}
});
/*
cfg.setFileCreate(new IFileCreate() {
@Override
public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
// 判断自定义文件夹是否需要创建
checkDir("调用默认方法创建的目录,自定义目录用");
if (fileType == FileType.MAPPER) {
// 已经生成 mapper 文件判断存在,不想重新生成返回 false
return !new File(filePath).exists();
}
// 允许生成模板文件
return true;
}
});
*/
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
// 配置自定义输出模板
//指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
// templateConfig.setEntity("templates/entity2.java");
templateConfig.setService("templates/ftl/Service.java");
templateConfig.setServiceImpl(null);
// templateConfig.setController();
templateConfig.setXml(null);
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setSuperEntityClass("com.qkdata.common.base.entity.BasePO");
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
// 公共父类
// strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!");
// 写于父类中的公共字段
strategy.setSuperEntityColumns("id","create_time","update_time");
strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
strategy.setControllerMappingHyphenStyle(true);
strategy.setSuperServiceImplClass("com.qkdata.common.base.service.impl.BaseServiceImpl");
// strategy.setTablePrefix(pc.getModuleName() + "_");
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}
}
package com.qkdata.test;
import com.qkdata.Application;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles(value = "dev")
public class BaseTest {
}
package ${package.Service};
import ${package.Entity}.${entity};
import ${package.Mapper}.${table.mapperName};
import ${superServiceImplClassPackage};
import org.springframework.stereotype.Service;
/**
* <p>
* $!{table.comment} 服务类
* </p>
*
* @author ${author}
* @since ${date}
*/
@Service
public class ${table.serviceName} extends ${superServiceImplClass}<${table.mapperName}, ${entity}> {
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment