Commit f1b93458 authored by Fei 张飞's avatar Fei 张飞

init

parent 00f3a5f7
/.idea/
/target/
/yst-dch-score.iml
/interface/target/
/interface/interface.iml
# Compiled class file # Compiled class file
*.class *.class
......
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**
!**/src/test/**
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
### VS Code ###
.vscode/
/.mvn/
/mvnw
/mvnw.cmd
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>yst-dch-score</artifactId>
<groupId>com.elitesland</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>core</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>core</name>
<description>Elitesland YST core framework</description>
<properties>
<java.version>11</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>exec</classifier>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<!--华为云仓库-->
<repository>
<id>huaweicloud</id>
<name>huaweicloud-maven</name>
<url>https://mirrors.huaweicloud.com/repository/maven/</url>
</repository>
<!--阿里云仓库-->
<repository>
<id>aliyun</id>
<name>aliyun-maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</repository>
<!-- 阿里云spring仓库 -->
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://maven.aliyun.com/repository/spring</url>
</repository>
<!-- 中央仓库 -->
<repository>
<id>central</id>
<name>maven-central</name>
<url>http://central.maven.org/maven2/</url>
</repository>
</repositories>
</project>
#! /bin/shell
# Copyright 2019-2029 elitesland(https://elitesland.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#======================================================================
# 项目重启shell脚本
# 先调用shutdown.sh停服
# 然后调用startup.sh启动服务
#
# author: Mir
# date: 2020-2-18
#======================================================================
# 项目名称
APPLICATION="yst-cms-svr"
# 停服
echo stop ${APPLICATION} Application...
sh shutdown.sh
# 启动服务
echo start ${APPLICATION} Application...
sh startup.sh
\ No newline at end of file
#! /bin/shell
# Copyright 2019-2029 elitesland(https://elitesland.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#======================================================================
# 项目停服shell脚本
# 通过项目名称查找到PID
# 然后kill -9 pid
#
# author: Mir
# date: 2020-2-18
#======================================================================
# 项目名称
APPLICATION="yst-cms-svr"
# 项目启动jar包名称
APPLICATION_JAR="${APPLICATION}.jar"
PID=$(ps -ef | grep ${APPLICATION_JAR} | grep -v grep | awk '{ print $2 }')
if [ -z "$PID" ]
then
echo ${APPLICATION} is already stopped
else
echo kill ${PID}
kill -9 ${PID}
echo ${APPLICATION} stopped successfully
fi
\ No newline at end of file
rem Copyright 2019-2029 elitesland(https://elitesland.com)
rem
rem Licensed under the Apache License, Version 2.0 (the "License");
rem you may not use this file except in compliance with the License.
rem You may obtain a copy of the License at
rem
rem http://www.apache.org/licenses/LICENSE-2.0
rem
rem Unless required by applicable law or agreed to in writing, software
rem distributed under the License is distributed on an "AS IS" BASIS,
rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
rem See the License for the specific language governing permissions and
rem limitations under the License.
rem ======================================================================
rem windows启动脚本
rem
rem author: Mir
rem date: 2020-2-18
rem ======================================================================
rem startup jar
java -jar ../lib/yst-cms-svr.jar --spring.config.location=../config/ --elbootplus.isEnableAnsi=false
pause
\ No newline at end of file
#! /bin/shell
# Copyright 2019-2029 elitesland(https://elitesland.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#======================================================================
# 项目启动shell脚本
# config目录: 配置文件目录
# logs目录: 项目运行日志目录
# logs/spring-boot-plus_startup.log: 记录启动日志
# logs/back目录: 项目运行日志备份目录
# nohup后台运行
#
# author: Mir
# date: 2020-2-18
#======================================================================
# 项目名称
APPLICATION="yst-cms-svr"
# 项目启动jar包名称
APPLICATION_JAR="${APPLICATION}.jar"
# bin目录绝对路径
BIN_PATH=$(cd $(dirname "$0"); pwd)
# 进入bin目录
cd $(dirname "$0")
# 返回到上一级项目根目录路径
cd ..
# 打印项目根目录绝对路径
# `pwd` 执行系统命令并获得结果
BASE_PATH=$(pwd)
# 外部配置文件绝对目录,如果是目录需要/结尾,也可以直接指定文件
# 如果指定的是目录,spring则会读取目录中的所有配置文件
CONFIG_DIR=${BASE_PATH}"/config/"
# 项目日志输出绝对路径
LOG_DIR=${BASE_PATH}"/logs"
LOG_FILE="${APPLICATION}.log"
LOG_PATH="${LOG_DIR}/${LOG_FILE}"
# 日志备份目录
LOG_BACK_DIR="${LOG_DIR}/back/"
# 项目启动日志输出绝对路径
LOG_STARTUP_PATH="${LOG_DIR}/${APPLICATION}_startup.log"
# 当前时间
NOW=$(date --date='0 days ago' "+%Y-%m-%d-%H-%M-%S")
NOW_PRETTY=$(date --date='0 days ago' "+%Y-%m-%d %H:%M:%S")
# 启动日志
STARTUP_LOG="================================================ ${NOW_PRETTY} ================================================\n"
# 如果logs文件夹不存在,则创建文件夹
if [ ! -d "${LOG_DIR}" ]; then
mkdir "${LOG_DIR}"
fi
# 如果logs/back文件夹不存在,则创建文件夹
if [ ! -d "${LOG_BACK_DIR}" ]; then
mkdir "${LOG_BACK_DIR}"
fi
# 如果项目运行日志存在,则重命名备份
if [ -f "${LOG_PATH}" ]; then
mv ${LOG_PATH} "${LOG_BACK_DIR}/${APPLICATION}_back_${NOW}.log"
fi
# 创建新的项目运行日志
echo "" > ${LOG_PATH}
# 如果项目启动日志不存在,则创建,否则追加
echo ${STARTUP_LOG} >> ${LOG_STARTUP_PATH}
#==========================================================================================
# JVM Configuration
# -Xmx1g:设置JVM最大可用内存为1G。
# -Xms1g:设置JVM初始内存41。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存
# -Xmn512m:设置年轻代大小为512m。整个JVM内存大小=年轻代大小 + 年老代大小 + 持久代大小。
# 持久代一般固定大小为64m,所以增大年轻代,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8
# -XX:MetaspaceSize=64m:存储class的内存大小,该值越大触发Metaspace GC的时机就越晚
# -XX:MaxMetaspaceSize=320m:限制Metaspace增长的上限,防止因为某些情况导致Metaspace无限的使用本地内存,影响到其他程序
# -XX:-OmitStackTraceInFastThrow:解决重复异常不打印堆栈信息问题
#==========================================================================================
JAVA_OPT="-server -Xms1g -Xmx1g -Xmn512m -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=256m"
JAVA_OPT="${JAVA_OPT} -XX:-OmitStackTraceInFastThrow"
#=======================================================
# 将命令启动相关日志追加到日志文件
#=======================================================
# 输出项目名称
STARTUP_LOG="${STARTUP_LOG}application name: ${APPLICATION}\n"
# 输出jar包名称
STARTUP_LOG="${STARTUP_LOG}application jar name: ${APPLICATION_JAR}\n"
# 输出项目bin路径
STARTUP_LOG="${STARTUP_LOG}application bin path: ${BIN_PATH}\n"
# 输出项目根目录
STARTUP_LOG="${STARTUP_LOG}application root path: ${BASE_PATH}\n"
# 打印日志路径
STARTUP_LOG="${STARTUP_LOG}application log path: ${LOG_PATH}\n"
# 打印JVM配置
STARTUP_LOG="${STARTUP_LOG}application JAVA_OPT : ${JAVA_OPT}\n"
# 打印启动命令
STARTUP_LOG="${STARTUP_LOG}application background startup command: nohup java ${JAVA_OPT} -jar ${BASE_PATH}/lib/${APPLICATION_JAR} --spring.config.location=${CONFIG_DIR} --logging.config=${CONFIG_DIR}logback.xml > ${LOG_PATH} 2>&1 &\n"
#======================================================================
# 执行启动命令:后台启动项目,并将日志输出到项目根目录下的logs文件夹下
#======================================================================
nohup java ${JAVA_OPT} -jar ${BASE_PATH}/lib/${APPLICATION_JAR} --spring.config.location=${CONFIG_DIR} --logging.config=${CONFIG_DIR}logback.xml > ${LOG_PATH} 2>&1 &
# 进程ID
PID=$(ps -ef | grep ${APPLICATION_JAR} | grep -v grep | awk '{ print $2 }')
STARTUP_LOG="${STARTUP_LOG}application pid: ${PID}\n"
# 启动日志追加到启动日志文件中
echo -e ${STARTUP_LOG} >> ${LOG_STARTUP_PATH}
# 打印启动日志
echo -e ${STARTUP_LOG}
# 打印项目日志
tail -f ${LOG_PATH}
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2019-2029 elitesland(https://elitesland.com)
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<assembly>
<!-- 可自定义,这里指定的是项目环境 -->
<!-- spring-boot-plus-server-1.0.1-RELEASE-local.tar.gz -->
<!-- <id>${project.version}-${profileActive}</id> -->
<id>assembly</id>
<!-- 打包的类型,如果有N个,将会打N个类型的包 -->
<formats>
<format>tar.gz</format>
<!-- <format>zip</format> -->
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<fileSets>
<!--
0755->即用户具有读/写/执行权限,组用户和其它用户具有读写权限;
0644->即用户具有读写权限,组用户和其它用户具有只读权限;
-->
<!-- 将src/bin目录下的所有文件输出到打包后的bin目录中 -->
<fileSet>
<directory>${basedir}/src/bin</directory>
<outputDirectory>bin</outputDirectory>
<fileMode>0755</fileMode>
<includes>
<include>**.sh</include>
<include>**.bat</include>
</includes>
</fileSet>
<!-- 指定输出target/classes中的配置文件到config目录中 -->
<fileSet>
<directory>${basedir}/target/classes/config</directory>
<outputDirectory>config</outputDirectory>
<fileMode>0644</fileMode>
<includes>
<include>*</include>
</includes>
</fileSet>
<!-- 将项目启动jar打包到lib目录中 -->
<fileSet>
<directory>${basedir}/target</directory>
<outputDirectory>lib</outputDirectory>
<fileMode>0755</fileMode>
<includes>
<include>${project.build.finalName}.jar</include>
</includes>
</fileSet>
<!-- 指定日志目录 -->
<fileSet>
<directory>${basedir}/src/logs</directory>
<outputDirectory>logs</outputDirectory>
<fileMode>0755</fileMode>
</fileSet>
<!-- 包含根目录下的文件 -->
<fileSet>
<directory>${basedir}</directory>
<includes>
<include>NOTICE</include>
<include>LICENSE</include>
</includes>
</fileSet>
</fileSets>
</assembly>
\ No newline at end of file
package com.elitesland.core;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class CoreApplication {
public static void main(String[] args) {
SpringApplication.run(CoreApplication.class, args);
}
}
package com.elitesland.core.base;
/**
* <p>
* REST API 响应码
* </p>
*
* @author Mir
* @date 2018-11-08
*/
public enum ApiCode {
/**
* Http response entity status code, corresponding to HTTP Status Code
*/
SUCCESS(200, "操作成功"),
UNAUTHORIZED(401, "请先登录"),
NOT_PERMISSION(403, "没有权限"),
NOT_FOUND(404, "你请求的资源不存在"),
FAIL(500, "操作失败"),
LOGIN_EXCEPTION(4000, "登录失败"),
SYSTEM_EXCEPTION(5000, "系统异常!"),
PARAMETER_EXCEPTION(5001, "请求参数校验异常"),
PARAMETER_PARSE_EXCEPTION(5002, "请求参数解析异常"),
HTTP_MEDIA_TYPE_EXCEPTION(5003, "HTTP Media 类型异常"),
SPRING_BOOT_PLUS_EXCEPTION(5100, "系统处理异常"),
BUSINESS_EXCEPTION(5101, "业务处理异常"),
DAO_EXCEPTION(5102, "数据库处理异常"),
VERIFICATION_CODE_EXCEPTION(5103, "验证码校验异常"),
AUTHENTICATION_EXCEPTION(5104, "登录授权异常"),
UNAUTHENTICATED_EXCEPTION(5105, "没有访问权限"),
UNAUTHORIZED_EXCEPTION(5106, "没有访问权限"),
UNAUTHORIZED_ANONYMOUS(5107, "匿名用户,未授权访问"),
NO_USER_FOUND_EXCEPTION(5108, "用户信息未找到,系统异常")
;
private final int code;
private final String msg;
ApiCode(final int code, final String msg) {
this.code = code;
this.msg = msg;
}
public static ApiCode getApiCode(int code) {
ApiCode[] ecs = ApiCode.values();
for (ApiCode ec : ecs) {
if (ec.getCode() == code) {
return ec;
}
}
return SUCCESS;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
package com.elitesland.core.base;
import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
/**
* <p>
* REST API 返回结果
* </p>
*
* @author Mir
* @date 2018-11-08
*/
@Data
@Accessors(chain = true)
@Builder
@AllArgsConstructor
public class ApiResult<T> implements Serializable {
private static final long serialVersionUID = 7722914707623525357L;
/**
* 响应码
*/
private int code;
/**
* 响应消息
*/
private String msg;
/**
* 是否成功
*/
private boolean success;
/**
* 响应数据
*/
private T data;
/**
* 响应时间
*/
// @JSONField(format = "yyyy-MM-ddTHH:mm:ss")
// @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime time = LocalDateTime.now();
public ApiResult() {
}
public static <T> ApiResult<T> result(boolean flag) {
if (flag) {
return ok();
}
return fail();
}
public static <T> ApiResult<T> result(ApiCode apiCode) {
return result(apiCode, null);
}
public static <T> ApiResult<T> result(ApiCode apiCode, T data) {
return result(apiCode, null, data);
}
public static <T> ApiResult<T> result(ApiCode apiCode, String msg, T data) {
boolean success = false;
if (apiCode.getCode() == ApiCode.SUCCESS.getCode()) {
success = true;
}
String message = apiCode.getMsg();
if (StrUtil.isNotBlank(msg)) {
message = msg;
}
return ApiResult.<T>builder()
.code(apiCode.getCode())
.msg(message)
.data(data)
.success(success)
.time(LocalDateTime.now())
.build();
}
public static <T> ApiResult<T> ok() {
return ok(null);
}
public static <T> ApiResult<T> ok(T data) {
return result(ApiCode.SUCCESS, data);
}
public static <T> ApiResult<T> ok(T data, String msg) {
return result(ApiCode.SUCCESS, msg, data);
}
public static <T> ApiResult<Map<String, T>> okMap(String key, T value) {
Map<String, T> map = new HashMap<>(1);
map.put(key, value);
return ok(map);
}
public static <T> ApiResult<T> fail(ApiCode apiCode) {
return result(apiCode, null);
}
public static <T> ApiResult<T> fail(String msg) {
return result(ApiCode.FAIL, msg, null);
}
public static <T> ApiResult<T> fail(ApiCode apiCode, T data) {
if (ApiCode.SUCCESS == apiCode) {
throw new RuntimeException("失败结果状态码不能为" + ApiCode.SUCCESS.getCode());
}
return result(apiCode, data);
}
public static <T> ApiResult<Map<String, T>> fail(String key, T value) {
Map<String, T> map = new HashMap<>(1);
map.put(key, value);
return result(ApiCode.FAIL, map);
}
public static <T> ApiResult<T> fail() {
return fail(ApiCode.FAIL);
}
}
\ No newline at end of file
package com.elitesland.core.base;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.hibernate.annotations.GenericGenerator;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import javax.validation.constraints.Null;
import java.time.LocalDateTime;
/**
* <pre>
* JPA数据实体类的基础类,用于唯一ID生成策略绑定
* 审计字段、版本和逻辑删除标记,
* 未来扩展租户ID信息
* </pre>
* @author Moz
* @date 3/17/2020
*/
@Data
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
public class BaseModel {
@Id
@GenericGenerator(name = "el-id", strategy = "com.elitesland.core.util.IdGenerator")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "el-id")
@JsonSerialize(using = ToStringSerializer.class)
@ApiModelProperty("账号唯一ID")
@Column(columnDefinition = "bigint default 0 comment '唯一编号ID'")
private Long id;
@CreatedBy
@Column(columnDefinition = "bigint default 0 comment '记录创建者ID'")
private Long creator;
@CreatedDate
@Column(columnDefinition = "timestamp default NULL comment '记录创建时间'")
private LocalDateTime created = LocalDateTime.now();
@LastModifiedBy
@Column(columnDefinition = "bigint default 0 comment '记录最后更新者ID'")
private Long updater;
@LastModifiedDate
@Column(columnDefinition = "timestamp default NULL comment '记录最后更新时间'")
private LocalDateTime updated = LocalDateTime.now();
@ApiModelProperty(value = "逻辑删除,0:未删除,1:已删除")
@Null(message = "逻辑删除不用传")
@Column(columnDefinition = "int default 0 comment '逻辑删除,0:未删除,1:已删除'")
private Integer deleted;
@ApiModelProperty(value = "版本")
@Null(message = "版本不用传")
@Column(columnDefinition = "int default 0 comment '版本信息,前端不用传'")
private Integer version;
}
package com.elitesland.core.base;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
/**
* @author Mir
* @date 2018-11-08
*/
@ApiModel("分页")
@Data
@Accessors(chain = true)
@Builder
public class PagingVO<T> implements Serializable {
private static final long serialVersionUID = -1683800405530086022L;
@ApiModelProperty("总行数")
@JsonProperty("total")
private Long total = 0L;
@ApiModelProperty("数据列表")
@JsonProperty("records")
private List<T> records = Collections.emptyList();
public PagingVO() {
}
public PagingVO(long total, List<T> records) {
this.total = total;
this.records = records;
}
public PagingVO(IPage<T> page) {
this.total = page.getTotal();
this.records = page.getRecords();
}
@Override
public String toString() {
return "Paging{" +
"total=" + total +
", records=" + records +
'}';
}
}
package com.elitesland.core.base.param;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.querydsl.core.types.Order;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.PathBuilder;
import com.querydsl.jpa.impl.JPAQuery;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.val;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.query.JpaQueryCreator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* 可排序查询参数对象
*
* @author Mir
* @date 2019-08-04
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("可排序查询参数对象")
public abstract class AbstractOrderQueryParam extends QueryParam {
private static final long serialVersionUID = 57714391204790143L;
@ApiModelProperty(value = "排序")
private List<OrderItem> orders;
public List<OrderItem> getOrders() {
return orders;
}
public void setOrders(List<OrderItem> orders) {
this.orders = orders;
}
public void defaultOrder(OrderItem orderItem) {
this.defaultOrders(Collections.singletonList(orderItem));
}
public void defaultOrders(List<OrderItem> orderItems) {
if (CollectionUtils.isEmpty(orderItems)) {
return;
}
this.orders = orderItems;
}
@JsonIgnore
public PageRequest getPageRequest() {
val orderBys = CollectionUtils.isEmpty(orders) ? new ArrayList<Sort.Order>() :
orders.stream().map(o -> new Sort.Order(
o.isAsc() ? Sort.Direction.ASC : Sort.Direction.DESC,
o.getColumn()
)).collect(Collectors.toList());
return PageRequest.of(getCurrent(), getSize(), Sort.by(orderBys));
}
@JsonIgnore
public void fillOrders(JPAQuery<?> query){
val pageRequest = getPageRequest();
pageRequest.getSort().forEach(s -> {
val orderbyExpression = new PathBuilder<>(Object.class, "object");
query.orderBy(new OrderSpecifier(s.isAscending() ?
Order.ASC : Order.DESC, orderbyExpression.get(s.getProperty())));
});
}
@JsonIgnore
public void setPaging(JPAQuery<?> query){
query.offset(getCurrent() * getSize());
query.limit(getSize());
}
}
package com.elitesland.core.base.param;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
/**
* @author Mir
* @date 2018-11-08
*/
@Data
@ApiModel("ID参数")
public class IdParam implements Serializable {
private static final long serialVersionUID = -5353973980674510450L;
@NotBlank(message = "ID不能为空")
private String id;
}
package com.elitesland.core.base.param;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
/**
* @author Mir
* @date 2018-11-08
*/
@ApiModel("主键状态VO")
public class IdStatusParam implements Serializable {
private static final long serialVersionUID = -7581307955242965701L;
@ApiModelProperty("主键ID")
private String id;
@ApiModelProperty("状态,1:启用 0:禁用")
private Integer status;
}
package com.elitesland.core.base.param;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
/**
* 名称参数
*
* @author Mir
* @date 2018-11-08
*/
@ApiModel("名称参数")
public class NameParam implements Serializable {
private static final long serialVersionUID = -3710501706034574149L;
@ApiModelProperty("名称")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "NameParam{" +
"name='" + name + '\'' +
'}';
}
}
package com.elitesland.core.base.param;
import com.elitesland.core.constant.CommonConstant;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 查询参数
*
* @author Mir
* @date 2018-11-08
*/
@Data
@ApiModel("查询参数对象")
public abstract class QueryParam implements Serializable {
private static final long serialVersionUID = -3263921252635611410L;
@ApiModelProperty(value = "页码,默认为1", example = "0")
private Integer current = CommonConstant.DEFAULT_PAGE_INDEX;
@ApiModelProperty(value = "页大小,默认为10", example = "10")
private Integer size = CommonConstant.DEFAULT_PAGE_SIZE;
@ApiModelProperty(value = "搜索字符串", example = "")
private String keyword;
public Integer getCurrent() {
return current;
}
public Integer getSize() {
return size;
}
public String getKeyword() {
return keyword;
}
public void setKeyword(String keyword) {
this.keyword = keyword;
}
public void setCurrent(Integer current) {
if (current == null || current < 0) {
this.current = CommonConstant.DEFAULT_PAGE_INDEX;
} else {
this.current = current;
}
}
public void setSize(Integer size) {
if (size == null || size <= 0) {
this.size = CommonConstant.DEFAULT_PAGE_SIZE;
} else {
this.size = size;
}
}
}
package com.elitesland.core.config;
import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.persistence.EntityManager;
/**
* <pre>
*
* </pre>
* @author Mir
* @date 2020-06-20
*/
@Configuration
public class QueryDSLConfig {
@Bean
public JPAQueryFactory jpaQueryFactory(EntityManager em) {
return new JPAQueryFactory(em);
}
}
package com.elitesland.core.config.json;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* @author Mir
*/
@Configuration
public class LocalDateTimeSerializerConfig {
@Value("${spring.jackson.date-format: yyyy-MM-dd HH:mm:ss}")
private String pattern;
private LocalDateTimeSerializer localDateTimeSerializer;
private LocalDateTimeDeserializer localDateTimeDeserializer;
@Autowired
public void setLocalDateTimeSerializer(LocalDateTimeSerializer localDateTimeSerializer) {
this.localDateTimeSerializer = localDateTimeSerializer;
}
@Autowired
public void setLocalDateTimeDeserializer(LocalDateTimeDeserializer localDateTimeDeserializer) {
this.localDateTimeDeserializer = localDateTimeDeserializer;
}
@Bean
public LocalDateTimeSerializer localDateTimeSerializer() {
return new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(pattern));
}
@Bean
public LocalDateTimeDeserializer localDateTimeDeserializer() {
return new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(pattern));
}
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return jacksonObjectMapperBuilder -> {
jacksonObjectMapperBuilder.serializerByType(LocalDateTime.class, localDateTimeSerializer);
jacksonObjectMapperBuilder.deserializerByType(LocalDateTime.class, localDateTimeDeserializer);
// jacksonObjectMapperBuilder.serializerByType(Long.class, ToStringSerializer.instance);
// jacksonObjectMapperBuilder.serializerByType(Long.TYPE, ToStringSerializer.instance);
};
}
}
package com.elitesland.core.constant;
public interface CacheKey {
}
package com.elitesland.core.constant;
/**
* 常量
*
* @author Mir
* @date 2018-11-08
*/
public interface CommonConstant {
/**
* 默认页码为1
*/
Integer DEFAULT_PAGE_INDEX = 0;
/**
* 默认页大小为10
*/
Integer DEFAULT_PAGE_SIZE = 10;
/**
* 登录用户
*/
String LOGIN_SYS_USER = "loginSysUser";
/**
* 登录token
*/
String JWT_DEFAULT_TOKEN_NAME = "token";
/**
* JWT用户名
*/
String JWT_USERNAME = "username";
/**
* JWT刷新新token响应状态码
*/
int JWT_REFRESH_TOKEN_CODE = 460;
/**
* JWT刷新新token响应状态码,
* Redis中不存在,但jwt未过期,不生成新的token,返回361状态码
*/
int JWT_INVALID_TOKEN_CODE = 461;
/**
* JWT Token默认密钥
*/
String JWT_DEFAULT_SECRET = "666666";
/**
* JWT 默认过期时间,3600L,单位秒
*/
Long JWT_DEFAULT_EXPIRE_SECOND = 3600L;
/**
* 初始密码
*/
String INIT_PWD = "123456";
/**
* 默认头像
*/
String DEFAULT_HEAD_URL = "";
/**
* 管理员角色名称
*/
String ADMIN_ROLE_NAME = "管理员";
String ADMIN_LOGIN = "adminLogin";
/**
* 验证码token
*/
String VERIFY_TOKEN = "verifyToken";
/**
* 图片
*/
String IMAGE = "image";
/**
* JPEG
*/
String JPEG = "JPEG";
/**
* base64前缀
*/
String BASE64_PREFIX = "data:image/png;base64,";
String AUDIT_CREATOR = "creator";
String AUDIT_CREATED = "created";
String AUDIT_UPDATER = "updater";
String AUDIT_UPDATED = "updated";
}
package com.elitesland.core.constant;
/**
* <p>
* redis key 常量
* </p>
*
* @author Mir
* @date 2019-05-23
**/
public interface CommonRedisKey {
/**
* 登录用户token信息key
*/
String LOGIN_TOKEN = "login:token:%s";
/**
* 登录用户信息key
*/
String LOGIN_USER = "login:user:%s";
/**
* 登录用户盐值信息key
*/
String LOGIN_SALT = "login:salt:%s";
/**
* 登录用户username token
*/
String LOGIN_USER_TOKEN = "login:user:token:%s:%s";
/**
* 验证码
*/
String VERIFY_CODE = "verify.code:%s";
}
package com.elitesland.core.constant;
/**
* <p>
* 日期格式常量
* </p>
*
* @author Mir
* @date 2018-11-08
*/
public interface DatePattern {
/**
* 年-月-日
*/
String yyyy_MM_dd = "yyyy-MM-dd";
/**
* 年-月-日 时:分
*/
String yyyy_MM_dd_HH_mm = "yyyy-MM-dd HH:mm";
/**
* 年-月-日 时:分:秒
*/
String yyyy_MM_dd_HH_mm_ss = "yyyy-MM-dd HH:mm:ss";
/**
* 年-月-日 时:分:秒:毫秒
*/
String yyyy_MM_dd_HH_mm_ss_S = "yyyy-MM-dd HH:mm:ss.S";
/**
* 时:分
*/
String HH_mm = "HH:mm";
/**
* 时:分:秒
*/
String HH_mm_ss = "HH:mm:ss";
/**
* 时:分:秒:毫秒
*/
String HH_mm_ss_S = "HH:mm:ss:S";
}
package com.elitesland.core.constant;
/**
* @author Mir
* @date 2020/03/18
*/
public interface HttpConstant {
String CHARSET_ENCODING_UTF8 = "UTF-8";
String CONTENT_TYPE_JSON = "application/json";
}
package com.elitesland.core.demo.service;
import org.springframework.stereotype.Service;
/**
* <p>
* 功能说明
* </p>
*
* @author Mir
* @date 2020/6/28
*/
@Service
public class DemoService {
public Integer add(int a, int b){
return a + b;
}
}
package com.elitesland.core.exception;
import org.springframework.security.core.AuthenticationException;
/**
* <pre>
* [功能说明]
* </pre>
*
* @author Mir
* @date 2020/6/24
*/
public class BadCaptchaException extends AuthenticationException {
private static final long serialVersionUID = 8337958032785176060L;
public BadCaptchaException(String msg) {
super(msg);
}
}
/*
* Copyright 2019-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.elitesland.core.exception;
/**
* <pre>
* 统一关于错误配置信息 异常
* </pre>
*
* @author Mir
* @date 2020-06-20
*/
public class BadConfigurationException extends RuntimeException {
private static final long serialVersionUID = -5628549105671311130L;
/**
* Constructs a new runtime exception with {@code null} as its
* detail message. The cause is not initialized, and may subsequently be
* initialized by a call to {@link #initCause}.
*/
public BadConfigurationException() {
super();
}
/**
* Constructs a new runtime exception with the specified detail message.
* The cause is not initialized, and may subsequently be initialized by a
* call to {@link #initCause}.
*
* @param message the detail message. The detail message is saved for
* later retrieval by the {@link #getMessage()} method.
*/
public BadConfigurationException(String message) {
super(message);
}
/**
* Constructs a new runtime exception with the specified detail message and
* cause. <p>Note that the detail message associated with
* {@code cause} is <i>not</i> automatically incorporated in
* this runtime exception's detail message.
*
* @param message the detail message (which is saved for later retrieval
* by the {@link #getMessage()} method).
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A {@code null} value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.4
*/
public BadConfigurationException(String message, Throwable cause) {
super(message, cause);
}
/**
* Constructs a new runtime exception with the specified cause and a
* detail message of {@code (cause==null ? null : cause.toString())}
* (which typically contains the class and detail message of
* {@code cause}). This constructor is useful for runtime exceptions
* that are little more than wrappers for other throwables.
*
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A {@code null} value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.4
*/
public BadConfigurationException(Throwable cause) {
super(cause);
}
/**
* Constructs a new runtime exception with the specified detail
* message, cause, suppression enabled or disabled, and writable
* stack trace enabled or disabled.
*
* @param message the detail message.
* @param cause the cause. (A {@code null} value is permitted,
* and indicates that the cause is nonexistent or unknown.)
* @param enableSuppression whether or not suppression is enabled
* or disabled
* @param writableStackTrace whether or not the stack trace should
* be writable
* @since 1.7
*/
protected BadConfigurationException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
package com.elitesland.core.exception;
import com.elitesland.core.base.ApiCode;
import lombok.Getter;
/**
* <pre>
* 业务异常
* </pre>
*
* @author Mir
* @date 2020/6/22
*/
@Getter
public class BusinessException extends RuntimeException {
private static final long serialVersionUID = -2205002357611194846L;
private ApiCode code;
public BusinessException() {
super();
}
public BusinessException(String message) {
super(message);
}
public BusinessException(ApiCode code, String message) {
super(message);
this.code = code;
}
public BusinessException(String message, Throwable cause) {
super(message, cause);
}
public BusinessException(Throwable cause) {
super(cause);
}
public BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
package com.elitesland.core.exception;
import org.springframework.security.core.AuthenticationException;
/**
* <pre>
* [功能说明]
* </pre>
*
* @author Mir
* @date 2020/6/24
*/
public class CaptchaExpiredException extends AuthenticationException {
private static final long serialVersionUID = -2209433381756411634L;
public CaptchaExpiredException(String msg) {
super(msg);
}
}
package com.elitesland.core.exception.handler;
import com.elitesland.core.base.ApiCode;
import com.elitesland.core.base.ApiResult;
import com.elitesland.core.exception.BusinessException;
import com.elitesland.core.util.ThrowableUtil;
import io.undertow.util.BadRequestException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* <pre>
* [功能说明]
* </pre>
*
* @author Mir
* @date 2020/6/22
*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BadRequestException.class)
public ApiResult<Object> handleBadRequestException(BadRequestException e) {
log.error(ThrowableUtil.getStackTrace(e));
return ApiResult.fail(ApiCode.VERIFICATION_CODE_EXCEPTION, ThrowableUtil.getStackTrace(e));
}
@ExceptionHandler(BusinessException.class)
public ApiResult<Object> handleBusinessException(BusinessException e) {
log.error(ThrowableUtil.getStackTrace(e));
return ApiResult.fail(e.getCode(), ThrowableUtil.getStackTrace(e));
}
}
package com.elitesland.core.exception.rest;
import lombok.val;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.Optional;
/**
* <pre>
* [功能说明]
* </pre>
*
* @author Mir
* @date 2020/6/24
*/
@RestController
public class FilterErrorController extends BasicErrorController {
public FilterErrorController() {
super(new DefaultErrorAttributes(), new ErrorProperties());
}
@Override
@RequestMapping(produces = {MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
val body = getErrorAttributes(request, ErrorAttributeOptions.of(
ErrorAttributeOptions.Include.MESSAGE,
ErrorAttributeOptions.Include.BINDING_ERRORS,
ErrorAttributeOptions.Include.STACK_TRACE,
ErrorAttributeOptions.Include.EXCEPTION));
val status = getStatus(request);
return ResponseEntity.of(Optional.of(body));
}
@Override
public String getErrorPath() {
return "error/error";
}
}
package com.elitesland.core.micro;
import java.sql.Timestamp;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
/**
* Clock Pool
* <p>
* 利用ScheduledExecutorService实现高并发场景下System.currentTimeMillis()的性能问题的优化.
*
* @author Mir
* @date 3/17/2020
*
* https://github.com/yu120/neural
*/
public enum ClockPool {
// ====
INSTANCE(1);
private final long period;
private final AtomicLong nowTime;
private boolean started = false;
private ScheduledExecutorService executorService;
ClockPool(long period) {
this.period = period;
this.nowTime = new AtomicLong(System.currentTimeMillis());
}
/**
* The initialize scheduled executor service
*/
public void initialize() {
if (started) {
return;
}
this.executorService = new ScheduledThreadPoolExecutor(1, r -> {
Thread thread = new Thread(r, "system-clock");
thread.setDaemon(true);
return thread;
});
executorService.scheduleAtFixedRate(() -> nowTime.set(System.currentTimeMillis()),
this.period, this.period, TimeUnit.MILLISECONDS);
Runtime.getRuntime().addShutdownHook(new Thread(this::destroy));
started = true;
}
/**
* The get current time milliseconds
*
* @return long time
*/
public long currentTimeMillis() {
return started ? nowTime.get() : System.currentTimeMillis();
}
/**
* The get string current time
*
* @return string time
*/
public String currentTime() {
return new Timestamp(currentTimeMillis()).toString();
}
/**
* The destroy of executor service
*/
public void destroy() {
if (executorService != null) {
executorService.shutdown();
}
}
}
package com.elitesland.core.micro;
import java.net.InetAddress;
import java.util.concurrent.ThreadLocalRandom;
/**
* 基于Twitter的Snowflake算法实现分布式高效有序ID生产黑科技(sequence)——升级版Snowflake
*
* <br>
* SnowFlake的结构如下(每部分用-分开):<br>
* <br>
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>
* <br>
* 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br>
* <br>
* 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
* 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下START_TIME属性)。
* 41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>
* <br>
* 10位的数据机器位,可以部署在1024个节点,包括5位dataCenterId和5位workerId<br>
* <br>
* 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号<br>
* <br>
* <br>
* 加起来刚好64位,为一个Long型。<br>
* SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),
* 并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
* <p>
* <p>
* 特性:
* 1.支持自定义允许时间回拨的范围<p>
* 2.解决跨毫秒起始值每次为0开始的情况(避免末尾必定为偶数,而不便于取余使用问题)<p>
* 3.解决高并发场景中获取时间戳性能问题<p>
* 4.支撑根据IP末尾数据作为workerId
* 5.时间回拨方案思考:1024个节点中分配10个点作为时间回拨序号(连续10次时间回拨的概率较小)
*
* @author Mir
* @date 3/17/2020
*
* https://github.com/yu120/neural
*/
public class Snowflake {
/**
* 起始时间戳
**/
private final static long START_TIME = 1519740777809L;
/**
* dataCenterId占用的位数:2
**/
private final static long DATA_CENTER_ID_BITS = 2L;
/**
* workerId占用的位数:8
**/
private final static long WORKER_ID_BITS = 8L;
/**
* 序列号占用的位数:12(表示只允许workId的范围为:0-4095)
**/
private final static long SEQUENCE_BITS = 12L;
/**
* workerId可以使用范围:0-255
**/
private final static long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
/**
* dataCenterId可以使用范围:0-3
**/
private final static long MAX_DATA_CENTER_ID = ~(-1L << DATA_CENTER_ID_BITS);
private final static long WORKER_ID_SHIFT = SEQUENCE_BITS;
private final static long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
private final static long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS;
/**
* 用mask防止溢出:位与运算保证计算的结果范围始终是 0-4095
**/
private final static long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);
private final long workerId;
private final long dataCenterId;
private long sequence = 0L;
private long lastTimestamp = -1L;
private static byte LAST_IP = 0;
private final boolean clock;
private final long timeOffset;
private final boolean randomSequence;
private final ThreadLocalRandom tlr = ThreadLocalRandom.current();
public Snowflake(long dataCenterId) {
this(dataCenterId, 0x000000FF & getLastIPAddress(), false, 5L, false);
}
public Snowflake(long dataCenterId, boolean clock, boolean randomSequence) {
this(dataCenterId, 0x000000FF & getLastIPAddress(), clock, 5L, randomSequence);
}
/**
* 基于Snowflake创建分布式ID生成器
*
* @param dataCenterId 数据中心ID,数据范围为0~255
* @param workerId 工作机器ID,数据范围为0~3
* @param clock true表示解决高并发下获取时间戳的性能问题
* @param timeOffset 允许时间回拨的毫秒量,建议5ms
* @param randomSequence true表示使用毫秒内的随机序列(超过范围则取余)
*/
public Snowflake(long dataCenterId, long workerId, boolean clock, long timeOffset, boolean randomSequence) {
if (dataCenterId > MAX_DATA_CENTER_ID || dataCenterId < 0) {
throw new IllegalArgumentException("Data Center Id can't be greater than " +
MAX_DATA_CENTER_ID + " or less than 0");
}
if (workerId > MAX_WORKER_ID || workerId < 0) {
throw new IllegalArgumentException("Worker Id can't be greater than " +
MAX_WORKER_ID + " or less than 0");
}
this.workerId = workerId;
this.dataCenterId = dataCenterId;
this.clock = clock;
this.timeOffset = timeOffset;
this.randomSequence = randomSequence;
}
/**
* 获取ID
*
* @return long
*/
public synchronized Long nextId() {
long currentTimestamp = this.timeGen();
// 闰秒:如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过,这个时候应当抛出异常
if (currentTimestamp < lastTimestamp) {
// 校验时间偏移回拨量
long offset = lastTimestamp - currentTimestamp;
if (offset > timeOffset) {
throw new RuntimeException("Clock moved backwards, refusing to generate id for [" + offset + "ms]");
}
try {
// 时间回退timeOffset毫秒内,则允许等待2倍的偏移量后重新获取,解决小范围的时间回拨问题
this.wait(offset << 1);
} catch (Exception e) {
throw new RuntimeException(e);
}
// 再次获取
currentTimestamp = this.timeGen();
// 再次校验
if (currentTimestamp < lastTimestamp) {
throw new RuntimeException("Clock moved backwards, refusing to generate id for [" + offset + "ms]");
}
}
// 同一毫秒内序列直接自增
if (lastTimestamp == currentTimestamp) {
// randomSequence为true表示随机生成允许范围内的序列起始值并取余数,否则毫秒内起始值为0L开始自增
long tempSequence = sequence + 1;
if (randomSequence && tempSequence > SEQUENCE_MASK) {
tempSequence = tempSequence % SEQUENCE_MASK;
}
// 通过位与运算保证计算的结果范围始终是 0-4095
sequence = tempSequence & SEQUENCE_MASK;
if (sequence == 0) {
currentTimestamp = this.tilNextMillis(lastTimestamp);
}
} else {
// randomSequence为true表示随机生成允许范围内的序列起始值,否则毫秒内起始值为0L开始自增
sequence = randomSequence ? tlr.nextLong(SEQUENCE_MASK + 1) : 0L;
}
lastTimestamp = currentTimestamp;
long currentOffsetTime = currentTimestamp - START_TIME;
/*
* 1.左移运算是为了将数值移动到对应的段(41、5、5,12那段因为本来就在最右,因此不用左移)
* 2.然后对每个左移后的值(la、lb、lc、sequence)做位或运算,是为了把各个短的数据合并起来,合并成一个二进制数
* 3.最后转换成10进制,就是最终生成的id
*/
return (currentOffsetTime << TIMESTAMP_LEFT_SHIFT) |
// 数据中心位
(dataCenterId << DATA_CENTER_ID_SHIFT) |
// 工作ID位
(workerId << WORKER_ID_SHIFT) |
// 毫秒序列化位
sequence;
}
/**
* 保证返回的毫秒数在参数之后(阻塞到下一个毫秒,直到获得新的时间戳)——CAS
*
* @param lastTimestamp last timestamp
* @return next millis
*/
private long tilNextMillis(long lastTimestamp) {
long timestamp = this.timeGen();
while (timestamp <= lastTimestamp) {
// 如果发现时间回拨,则自动重新获取(可能会处于无限循环中)
timestamp = this.timeGen();
}
return timestamp;
}
/**
* 获得系统当前毫秒时间戳
*
* @return timestamp 毫秒时间戳
*/
private long timeGen() {
return clock ? ClockPool.INSTANCE.currentTimeMillis() : System.currentTimeMillis();
}
/**
* 用IP地址最后几个字节标示
* <p>
* eg:192.168.1.30->30
*
* @return last IP
*/
private static byte getLastIPAddress() {
if (LAST_IP != 0) {
return LAST_IP;
}
try {
InetAddress inetAddress = InetAddress.getLocalHost();
byte[] addressByte = inetAddress.getAddress();
LAST_IP = addressByte[addressByte.length - 1];
} catch (Exception e) {
throw new RuntimeException("Unknown Host Exception", e);
}
return LAST_IP;
}
}
/*
* Copyright 2019-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.elitesland.core.util;
/**
* @author: liaojinlong
* @date: 2020/6/9 17:02
* @since: 1.0
* @see {@link SpringContextHolder}
* 针对某些初始化方法,在SpringContextHolder 初始化前时,<br>
* 可提交一个 提交回调任务。<br>
* 在SpringContextHolder 初始化后,进行回调使用
*/
public interface CallBack {
/**
* 回调执行方法
*/
void executor();
/**
* 本回调任务名称
* @return /
*/
default String getCallBackName() {
return Thread.currentThread().getId() + ":" + this.getClass().getName();
}
}
package com.elitesland.core.util;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import lombok.val;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.time.LocalDateTime;
/**
* @author Mir
* @date 2018-11-08
*/
public final class HttpServletResponseUtil {
private static String UTF8 = "UTF-8";
private static String CONTENT_TYPE = "application/json";
private HttpServletResponseUtil() {
throw new AssertionError();
}
public static void printJSON(HttpServletResponse response, Object object) throws Exception {
val mapper = new ObjectMapper();
val javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class, SpringContextHolder.getBean(LocalDateTimeSerializer.class));
javaTimeModule.addDeserializer(LocalDateTime.class, SpringContextHolder.getBean(LocalDateTimeDeserializer.class));
mapper.registerModule(javaTimeModule);
response.setCharacterEncoding(UTF8);
response.setContentType(CONTENT_TYPE);
PrintWriter printWriter = response.getWriter();
printWriter.write(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(object));
printWriter.flush();
printWriter.close();
}
}
package com.elitesland.core.util;
import com.elitesland.core.micro.Snowflake;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;
import java.io.Serializable;
/**
* 适用于JPA的基于Snowflake算法的ID生成器
*
* @author Moz
* @date 3/17/2020
*/
public class IdGenerator implements IdentifierGenerator {
private final Snowflake snowflake = new Snowflake(1);
@Override
public Serializable generate(SharedSessionContractImplementor sharedSessionContractImplementor, Object o) throws HibernateException {
return snowflake.nextId();
}
}
package com.elitesland.core.util;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
/**
* <pre>
* 封装请求,并替换getParameter从原有的Parameter获取值,为从Attribute中获取值
* </pre>
*
* @author Mir
* @date 2020/6/16
*/
public class ParameterRequestWrapper extends HttpServletRequestWrapper {
public ParameterRequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
public String getParameter(String name) {
return (String) super.getAttribute(name);
}
}
This diff is collapsed.
package com.elitesland.core.util;
import lombok.val;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
/**
* <pre>
* 将请求的body的二进制内容转为字符串
* </pre>
*
* @author Mir
* @date 2020/6/16
*/
public class RequestUtil {
private static final Logger log = LoggerFactory.getLogger(RequestUtil.class);
private RequestUtil() {
}
public static String obtainBody(ServletRequest request) {
BufferedReader br = null;
val sb = new StringBuilder();
try {
br = request.getReader();
String str;
while ((str = br.readLine()) != null) {
sb.append(str);
}
br.close();
} catch (IOException e) {
log.error("request body error");
} finally {
if (null != br) {
try {
br.close();
} catch (IOException e) {
log.error("request reader close error");
}
}
}
return sb.toString();
}
}
package com.elitesland.core.util;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* @author https://www.cnblogs.com/nihaorz/p/10690643.html
* @description Rsa 工具类,公钥私钥生成,加解密
* @date 2020-05-18
**/
public class RsaUtils {
private static final String SRC = "123456";
public static void main(String[] args) throws Exception {
System.out.println("\n");
RsaKeyPair keyPair = generateKeyPair();
System.out.println("公钥:" + keyPair.getPublicKey());
System.out.println("私钥:" + keyPair.getPrivateKey());
System.out.println("\n");
test1(keyPair);
System.out.println("\n");
test2(keyPair);
System.out.println("\n");
}
/**
* 公钥加密私钥解密
*/
private static void test1(RsaKeyPair keyPair) throws Exception {
System.out.println("***************** 公钥加密私钥解密开始 *****************");
String text1 = encryptByPublicKey(keyPair.getPublicKey(), RsaUtils.SRC);
String text2 = decryptByPrivateKey(keyPair.getPrivateKey(), text1);
System.out.println("加密前:" + RsaUtils.SRC);
System.out.println("加密后:" + text1);
System.out.println("解密后:" + text2);
if (RsaUtils.SRC.equals(text2)) {
System.out.println("解密字符串和原始字符串一致,解密成功");
} else {
System.out.println("解密字符串和原始字符串不一致,解密失败");
}
System.out.println("***************** 公钥加密私钥解密结束 *****************");
}
/**
* 私钥加密公钥解密
* @throws Exception /
*/
private static void test2(RsaKeyPair keyPair) throws Exception {
System.out.println("***************** 私钥加密公钥解密开始 *****************");
String text1 = encryptByPrivateKey(keyPair.getPrivateKey(), RsaUtils.SRC);
String text2 = decryptByPublicKey(keyPair.getPublicKey(), text1);
System.out.println("加密前:" + RsaUtils.SRC);
System.out.println("加密后:" + text1);
System.out.println("解密后:" + text2);
if (RsaUtils.SRC.equals(text2)) {
System.out.println("解密字符串和原始字符串一致,解密成功");
} else {
System.out.println("解密字符串和原始字符串不一致,解密失败");
}
System.out.println("***************** 私钥加密公钥解密结束 *****************");
}
/**
* 公钥解密
*
* @param publicKeyText 公钥
* @param text 待解密的信息
* @return /
* @throws Exception /
*/
public static String decryptByPublicKey(String publicKeyText, String text) throws Exception {
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] result = cipher.doFinal(Base64.decodeBase64(text));
return new String(result);
}
/**
* 私钥加密
*
* @param privateKeyText 私钥
* @param text 待加密的信息
* @return /
* @throws Exception /
*/
public static String encryptByPrivateKey(String privateKeyText, String text) throws Exception {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] result = cipher.doFinal(text.getBytes());
return Base64.encodeBase64String(result);
}
/**
* 私钥解密
*
* @param privateKeyText 私钥
* @param text 待解密的文本
* @return /
* @throws Exception /
*/
public static String decryptByPrivateKey(String privateKeyText, String text) throws Exception {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] result = cipher.doFinal(Base64.decodeBase64(text));
return new String(result);
}
/**
* 公钥加密
*
* @param publicKeyText 公钥
* @param text 待加密的文本
* @return /
*/
public static String encryptByPublicKey(String publicKeyText, String text) throws Exception {
X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec2);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] result = cipher.doFinal(text.getBytes());
return Base64.encodeBase64String(result);
}
/**
* 构建RSA密钥对
*
* @return /
* @throws NoSuchAlgorithmException /
*/
public static RsaKeyPair generateKeyPair() throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
String publicKeyString = Base64.encodeBase64String(rsaPublicKey.getEncoded());
String privateKeyString = Base64.encodeBase64String(rsaPrivateKey.getEncoded());
return new RsaKeyPair(publicKeyString, privateKeyString);
}
/**
* RSA密钥对对象
*/
public static class RsaKeyPair {
private final String publicKey;
private final String privateKey;
public RsaKeyPair(String publicKey, String privateKey) {
this.publicKey = publicKey;
this.privateKey = privateKey;
}
public String getPublicKey() {
return publicKey;
}
public String getPrivateKey() {
return privateKey;
}
}
}
/*
* Copyright 2019-2020 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.elitesland.core.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.env.Environment;
import java.util.ArrayList;
import java.util.List;
/**
* @author Jie
* @date 2019-01-07
*/
@Slf4j
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
private static ApplicationContext applicationContext = null;
private static final List<CallBack> CALL_BACKS = new ArrayList<>();
private static boolean addCallback = true;
/**
* 针对 某些初始化方法,在SpringContextHolder 未初始化时 提交回调方法。
* 在SpringContextHolder 初始化后,进行回调使用
*
* @param callBack 回调函数
*/
public synchronized static void addCallBacks(CallBack callBack) {
if (addCallback) {
SpringContextHolder.CALL_BACKS.add(callBack);
} else {
log.warn("CallBack:{} 已无法添加!立即执行", callBack.getCallBackName());
callBack.executor();
}
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
assertContextInjected();
return (T) applicationContext.getBean(name);
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
public static <T> T getBean(Class<T> requiredType) {
assertContextInjected();
return applicationContext.getBean(requiredType);
}
/**
* 获取SpringBoot 配置信息
*
* @param property 属性key
* @param defaultValue 默认值
* @param requiredType 返回类型
* @return /
*/
public static <T> T getProperties(String property, T defaultValue, Class<T> requiredType) {
T result = defaultValue;
try {
result = getBean(Environment.class).getProperty(property, requiredType);
} catch (Exception ignored) {}
return result;
}
/**
* 获取SpringBoot 配置信息
*
* @param property 属性key
* @return /
*/
public static String getProperties(String property) {
return getProperties(property, null, String.class);
}
/**
* 获取SpringBoot 配置信息
*
* @param property 属性key
* @param requiredType 返回类型
* @return /
*/
public static <T> T getProperties(String property, Class<T> requiredType) {
return getProperties(property, null, requiredType);
}
/**
* 检查ApplicationContext不为空.
*/
private static void assertContextInjected() {
if (applicationContext == null) {
throw new IllegalStateException("applicaitonContext属性未注入, 请在applicationContext" +
".xml中定义SpringContextHolder或在SpringBoot启动类中注册SpringContextHolder.");
}
}
/**
* 清除SpringContextHolder中的ApplicationContext为Null.
*/
private static void clearHolder() {
log.debug("清除SpringContextHolder中的ApplicationContext:"
+ applicationContext);
applicationContext = null;
}
@Override
public void destroy() {
SpringContextHolder.clearHolder();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (SpringContextHolder.applicationContext != null) {
log.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringContextHolder.applicationContext);
}
SpringContextHolder.applicationContext = applicationContext;
if (addCallback) {
for (CallBack callBack : SpringContextHolder.CALL_BACKS) {
callBack.executor();
}
CALL_BACKS.clear();
}
SpringContextHolder.addCallback = false;
}
}
package com.elitesland.core.util;
import lombok.val;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* <pre>
* 整理异常中的堆栈信息,返回信息字符串
* </pre>
*
* @author Mir
* @date 2020/6/22
*/
public class ThrowableUtil {
public static String getStackTrace(Throwable e) {
val sw = new StringWriter();
try (val pw = new PrintWriter(sw)) {
e.printStackTrace(pw);
return sw.toString();
}
}
}
This diff is collapsed.
This diff is collapsed.
package com.elitesland.core;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class CoreApplicationTests {
@Test
void contextLoads() {
}
}
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**
!**/src/test/**
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
### VS Code ###
.vscode/
/mvnw
/mvnw.cmd
/.mvn/
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>yst-dch-score</artifactId>
<groupId>com.elitesland</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.elitesland</groupId>
<artifactId>core</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.elitesland</groupId>
<artifactId>interface</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.elitesland</groupId>
<artifactId>system</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<!--华为云仓库-->
<repository>
<id>huaweicloud</id>
<name>huaweicloud-maven</name>
<url>https://mirrors.huaweicloud.com/repository/maven/</url>
</repository>
<!--阿里云仓库-->
<repository>
<id>aliyun</id>
<name>aliyun-maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</repository>
<!-- 阿里云spring仓库 -->
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://maven.aliyun.com/repository/spring</url>
</repository>
<!-- 中央仓库 -->
<repository>
<id>central</id>
<name>maven-central</name>
<url>http://central.maven.org/maven2/</url>
</repository>
</repositories>
</project>
package com.elitesland;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
package com.elitesland.demo.config;
import com.elitesland.security.config.bean.SecurityProperties;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
//import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
/**
* Swagger2全局配置
*
* @author Mir
* @date 2018-11-08
*/
@Configuration
@EnableSwagger2
@RequiredArgsConstructor
public class DemoSwagger2Config {
/**
* 标题
*/
@Value("${swagger.title}")
private String title;
/**
* 基本包
*/
@Value("${swagger.base.package}")
private String basePackage;
/**
* 描述
*/
@Value("${swagger.description}")
private String description;
/**
* URL
*/
@Value("${swagger.url}")
private String url;
/**
* 作者
*/
@Value("${swagger.contact.name}")
private String contactName;
/**
* 作者网址
*/
@Value("${swagger.contact.url}")
private String contactUrl;
/**
* 作者邮箱
*/
@Value("${swagger.contact.email}")
private String contactEmail;
/**
* 版本
*/
@Value("${swagger.version}")
private String version;
private final SecurityProperties securityProperties;
@Bean
public Docket createDemoRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("Demo模块API")
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.elitesland.demo"))
.paths(PathSelectors.any())
.build()
.globalOperationParameters(setHeaderToken())
;
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title(title)
.description(description)
.termsOfServiceUrl(url)
.contact(new Contact(contactName, contactUrl, contactEmail))
.version(version)
.build();
}
private List<Parameter> setHeaderToken() {
List<Parameter> pars = new ArrayList<>();
// token请求头
String testTokenValue = "";
ParameterBuilder tokenPar = new ParameterBuilder();
Parameter tokenParameter = tokenPar
.name(securityProperties.getHeader())
.description("JWT Token Request Header")
.modelRef(new ModelRef("string"))
.parameterType("Authorization")
.required(false)
.defaultValue(testTokenValue)
.build();
pars.add(tokenParameter);
return pars;
}
}
package com.elitesland.demo.controller;
import com.elitesland.core.demo.service.DemoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Optional;
/**
* <p>
* 功能说明
* </p>
*
* @author Mir
* @date 2020/6/28
*/
@RestController
@RequestMapping("/demo")
@RequiredArgsConstructor
@Api(value = "多模块服务注入", tags = {"多模块服务注入"})
public class DemoController {
private final DemoService demoService;
@GetMapping
@ApiOperation("服务注入测试")
public ResponseEntity<Object> getDemo() {
if(demoService == null){
return ResponseEntity.of(Optional.of("Oops!"));
}
return ResponseEntity.of(Optional.of(demoService.add(99, 2)));
}
}
package com.elitesland.demo.controller;
import com.elitesland.core.base.ApiResult;
import com.elitesland.demo.param.EmpCompanyQueryParam;
import com.elitesland.demo.service.EmployeeService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.val;
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;
/**
* <p>
* 功能说明
* </p>
*
* @author Mir
* @date 2020/6/29
*/
@RestController
@RequestMapping("/demo/employees")
@RequiredArgsConstructor
@Api(value = "多表关联演示", tags = {"多表关联演示"})
public class EmployeeController {
private final EmployeeService employeeService;
@PostMapping("/q")
@ApiOperation("以雇员公司关联查询为例")
public ApiResult<Object> search(@RequestBody EmpCompanyQueryParam param) {
val ret = employeeService.searchWithCompany(param);
return ApiResult.ok(ret);
}
}
package com.elitesland.demo.entity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import com.elitesland.core.base.BaseModel;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.Objects;
/**
* <p>
* 功能说明
* </p>
*
* @author Mir
* @date 2020/6/29
*/
@Entity
@Table(name = "tbl_company")
@org.hibernate.annotations.Table(appliesTo = "tbl_company", comment = "JPA演示-公司")
@Data
@Accessors(chain = true)
@ApiModel(value = "JPA演示-公司", description = "JPA演示-公司")
public class CompanyDO extends BaseModel implements Serializable {
private static final long serialVersionUID = 2480095046239564630L;
@ApiModelProperty(value = "公司名称")
@Column(columnDefinition = "varchar(32) default '' comment '公司名称'", nullable = false, unique = true)
private String companyName;
@ApiModelProperty(value = "执照编号")
@Column(columnDefinition = "varchar(32) default '' comment '执照编号'", nullable = false, unique = true)
private String businessLicense;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof CompanyDO)) {
return false;
}
if (!super.equals(o)) {
return false;
}
CompanyDO entity = (CompanyDO) o;
return getId().equals(entity.getId());
}
@Override
public int hashCode() {
return Objects.hash(getId());
}
}
package com.elitesland.demo.entity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import com.elitesland.core.base.BaseModel;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.Objects;
/**
* <p>
* 功能说明
* </p>
*
* @author Mir
* @date 2020/6/29
*/
@Entity
@Table(name = "tbl_employee")
@org.hibernate.annotations.Table(appliesTo = "tbl_employee", comment = "JPA演示-雇员")
@Data
@Accessors(chain = true)
@ApiModel(value = "JPA演示-雇员", description = "JPA演示-雇员")
public class EmployeeDO extends BaseModel implements Serializable {
private static final long serialVersionUID = 9070053158849073936L;
@ApiModelProperty(value = "所属公司")
@Column(columnDefinition = "bigint default 0 comment '所属公司'")
private Long companyId;
@ApiModelProperty(value = "姓名")
@Column(columnDefinition = "varchar(32) default '' comment '姓名'", nullable = false)
private String name;
@ApiModelProperty(value = "手机")
@Column(columnDefinition = "varchar(16) default '' comment '手机'", unique = true)
private String mobile;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof EmployeeDO)) {
return false;
}
if (!super.equals(o)) {
return false;
}
EmployeeDO entity = (EmployeeDO) o;
return getId().equals(entity.getId());
}
@Override
public int hashCode() {
return Objects.hash(getId());
}
}
package com.elitesland.demo.repo;
import com.elitesland.demo.entity.CompanyDO;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.stereotype.Repository;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* <p>
* 功能说明
* </p>
*
* @author Mir
* @date 2020/6/29
*/
@Repository
public interface CompanyRepo extends JpaRepository<CompanyDO, Long>, QuerydslPredicateExecutor<CompanyDO> {
}
package com.elitesland.demo.repo;
/**
* <p>
* 功能说明
* </p>
*
* @author Mir
* @date 2020/6/29
*/
public class CompanyRepoProc {
}
package com.elitesland.demo.repo;
import com.elitesland.demo.entity.EmployeeDO;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.stereotype.Repository;
/**
* <p>
* 功能说明
* </p>
*
* @author Mir
* @date 2020/6/29
*/
@Repository
public interface EmployeeRepo extends JpaRepository<EmployeeDO, Long>, QuerydslPredicateExecutor<EmployeeDO> {
}
package com.elitesland.demo.repo;
import com.elitesland.demo.entity.QCompanyDO;
import com.elitesland.demo.entity.QEmployeeDO;
import com.elitesland.demo.param.EmpCompanyQueryParam;
import com.elitesland.demo.vo.EmpCompanyVO;
import com.querydsl.core.Tuple;
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
/**
* <p>
* 功能说明
* </p>
*
* @author Mir
* @date 2020/6/29
*/
@Component
@RequiredArgsConstructor
public class EmployeeRepoProc {
private final JPAQueryFactory jpaQueryFactory;
public JPAQuery<EmpCompanyVO> select() {
val employee = QEmployeeDO.employeeDO;
val company = QCompanyDO.companyDO;
return jpaQueryFactory.select(
Projections.bean(
EmpCompanyVO.class,
employee.name,
company.companyName,
company.businessLicense
)
)
.from(employee)
.leftJoin(company).on(employee.companyId.eq(company.id));
}
public Predicate where(EmpCompanyQueryParam param){
val employee = QEmployeeDO.employeeDO;
val company = QCompanyDO.companyDO;
Predicate predicate = employee.isNotNull().and(employee.isNull());
predicate = StringUtils.isBlank(param.getEmployeeName()) ? predicate : ExpressionUtils.and(predicate, employee.name.like("%" + param.getEmployeeName() + "%"));
predicate = StringUtils.isBlank(param.getCompanyName()) ? predicate : ExpressionUtils.and(predicate, company.companyName.like("%" + param.getCompanyName() + "%"));
predicate = StringUtils.isBlank(param.getBusinessLicense()) ? predicate : ExpressionUtils.and(predicate, company.businessLicense.like("%" + param.getBusinessLicense() + "%"));
return predicate;
}
}
package com.elitesland.demo.service;
import com.elitesland.core.base.PagingVO;
import com.elitesland.demo.param.EmpCompanyQueryParam;
import com.elitesland.demo.vo.EmpCompanyVO;
/**
* <p>
* 功能说明
* </p>
*
* @author Mir
* @date 2020/6/29
*/
public interface EmployeeService {
PagingVO<EmpCompanyVO> searchWithCompany(EmpCompanyQueryParam param);
}
package com.elitesland.demo.service.impl;
import com.elitesland.core.base.PagingVO;
import com.elitesland.demo.param.EmpCompanyQueryParam;
import com.elitesland.demo.repo.EmployeeRepoProc;
import com.elitesland.demo.service.EmployeeService;
import com.elitesland.demo.vo.EmpCompanyVO;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* <p>
* 功能说明
* </p>
*
* @author Mir
* @date 2020/6/29
*/
@Service
@RequiredArgsConstructor
public class EmployeeServiceImpl implements EmployeeService {
private final EmployeeRepoProc employeeRepoProc;
@Override
public PagingVO<EmpCompanyVO> searchWithCompany(EmpCompanyQueryParam param) {
val emps = employeeRepoProc.select()
.where(employeeRepoProc.where(param));
param.fillOrders(emps);
param.setPaging(emps);
return PagingVO.<EmpCompanyVO>builder()
.total(emps.fetchCount())
.records(emps.fetch())
.build();
}
}
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/demo?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
username: demo
password: P@ssw0rd
jpa:
database: mysql
database-platform: org.hibernate.dialect.MySQL8Dialect
show-sql: true
hibernate:
ddl-auto: update
open-in-view: false
redis:
#数据库索引
database: 0
host: 127.0.0.1
port: 6379
password:
#连接超时时间
timeout: 5000
logging:
level:
org.springframework.security:
- debug
- info
org.springframework.web: error
org.hibernate.SQL: debug
org.hibernate.engine.QueryParameters: debug
org.hibernate.engine.query.HQLQueryPlan: debug
org.hibernate.type.descriptor.sql.BasicBinder: trace
# 登录相关配置
login:
# 是否限制单用户登录
single: false
# 验证码
login-code:
# 验证码类型配置 查看 LoginProperties 类
code-type: arithmetic
# 登录图形验证码有效时间/分钟
expiration: 2
# 验证码高度
width: 111
# 验证码宽度
heigth: 36
# 内容长度
length: 2
#jwt
jwt:
header: Authorization
# 令牌前缀
token-start-with: Bearer
# 必须使用最少88位的Base64对该令牌进行编码
base64-secret: ZmQ0ZGI5NjQ0MDQwY2I4MjMxY2Y3ZmI3MjdhN2ZmMjNhODViOTg1ZGE0NTBjMGM4NDA5NzYxMjdjOWMwYWRmZTBlZjlhNGY3ZTg4Y2U3YTE1ODVkZDU5Y2Y3OGYwZWE1NzUzNWQ2YjFjZDc0NGMxZWU2MmQ3MjY1NzJmNTE0MzI=
# 令牌过期时间 此处单位/毫秒 ,默认4小时,可在此网站生成 https://www.convertworld.com/zh-hans/time/milliseconds.html
token-validity-in-seconds: 14400000
# 在线用户key
online-key: online-token-
# 验证码
code-key: code-key-
# token 续期检查时间范围(默认30分钟,单位毫秒),在token即将过期的一段时间内用户操作了,则给用户的token续期
detect: 1800000
# 续期时间范围,默认1小时,单位毫秒
renew: 3600000
#################################### Swagger start #################################
# swagger配置
swagger:
base:
package: com.elitesland.core
contact:
email: Mir@qq.com
name: Mir
url: ''
description: ''
title: YST中台
url: ''
version: 1.0
#################################### Swagger end ####################################
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/demo?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
username: demo
password: P@ssw0rd
jpa:
database: mysql
database-platform: org.hibernate.dialect.MySQL8Dialect
show-sql: true
hibernate:
ddl-auto: update
open-in-view: false
redis:
#数据库索引
database: 0
host: 127.0.0.1
port: 6379
password:
#连接超时时间
timeout: 5000
main:
allow-bean-definition-overriding: true
logging:
level:
org.springframework.security:
- debug
- info
org.springframework.web: error
org.hibernate.SQL: debug
org.hibernate.engine.QueryParameters: debug
org.hibernate.engine.query.HQLQueryPlan: debug
org.hibernate.type.descriptor.sql.BasicBinder: trace
# 登录相关配置
login:
# 是否限制单用户登录
single: false
# 验证码
login-code:
# 验证码类型配置 查看 LoginProperties 类
code-type: arithmetic
# 登录图形验证码有效时间/分钟
expiration: 2
# 验证码高度
width: 111
# 验证码宽度
heigth: 36
# 内容长度
length: 2
#jwt
jwt:
header: Authorization
# 令牌前缀
token-start-with: Bearer
# 必须使用最少88位的Base64对该令牌进行编码
base64-secret: ZmQ0ZGI5NjQ0MDQwY2I4MjMxY2Y3ZmI3MjdhN2ZmMjNhODViOTg1ZGE0NTBjMGM4NDA5NzYxMjdjOWMwYWRmZTBlZjlhNGY3ZTg4Y2U3YTE1ODVkZDU5Y2Y3OGYwZWE1NzUzNWQ2YjFjZDc0NGMxZWU2MmQ3MjY1NzJmNTE0MzI=
# 令牌过期时间 此处单位/毫秒 ,默认4小时,可在此网站生成 https://www.convertworld.com/zh-hans/time/milliseconds.html
token-validity-in-seconds: 14400000
# 在线用户key
online-key: online-token-
# 验证码
code-key: code-key-
# token 续期检查时间范围(默认30分钟,单位毫秒),在token即将过期的一段时间内用户操作了,则给用户的token续期
detect: 1800000
# 续期时间范围,默认1小时,单位毫秒
renew: 3600000
#################################### Swagger start #################################
# swagger配置
swagger:
base:
package: com.elitesland.core
contact:
email: Mir@qq.com
name: Mir
url: ''
description: ''
title: YST中台
url: ''
version: 1.0
#################################### Swagger end ####################################
package com.elitesland.demo;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class DemoApplicationTests {
@Test
void contextLoads() {
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>yst-dch-score</artifactId>
<groupId>com.elitesland</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>interface</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>interface</name>
<description>Elitesland YST core framework</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.elitesland</groupId>
<artifactId>core</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>exec</classifier>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
/**
* <p>
* 功能说明
* </p>
*
* @author Mir
* @date 2020/6/29
*/
package com.elitesland.demo;
\ No newline at end of file
package com.elitesland.demo.param;
import com.elitesland.core.base.param.AbstractOrderQueryParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* <p>
* 功能说明
* </p>
*
* @author Mir
* @date 2020/6/29
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
public class EmpCompanyQueryParam extends AbstractOrderQueryParam {
private static final long serialVersionUID = 5509777465104717308L;
private String employeeName;
private String companyName;
private String businessLicense;
}
package com.elitesland.demo.vo;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* <p>
* 功能说明
* </p>
*
* @author Mir
* @date 2020/6/29
*/
@Data
@Accessors(chain = true)
public class EmpCompanyVO implements Serializable {
private static final long serialVersionUID = -9160358759934090460L;
private String employeeName;
private String companyName;
private String businessLicense;
}
/**
* <p>
* 功能说明
* </p>
*
* @author Mir
* @date 2020/6/28
*/
package com.elitesland;
\ No newline at end of file
package com.elitesland.system.param;
import com.elitesland.core.base.param.AbstractOrderQueryParam;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.util.List;
/**
* <pre>
* 分类码查询条件
* </pre>
*
* @author Mir
* @date 2020/6/25
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@ApiModel("分类码查询条件")
public class CatCodeQueryParam extends AbstractOrderQueryParam {
private static final long serialVersionUID = -3895354355224335248L;
@ApiModelProperty("系统码")
private String sys;
@ApiModelProperty("模块码")
private String mod;
@ApiModelProperty("分类码")
private String key;
@ApiModelProperty("分类码描述")
private String desc;
@ApiModelProperty("多个分类码字符串列表")
private List<String> keys;
}
package com.elitesland.system.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* <p>
* 功能说明
* </p>
*
* @author Mir
* @date 2020/6/28
*/
@Data
@Accessors(chain = true)
@ApiModel("用于下拉选择的分类码集合项")
public class CatCodeComboVo implements Serializable {
private static final long serialVersionUID = -3389966849168373797L;
@ApiModelProperty(value = "系统码")
private String sys;
@ApiModelProperty(value = "模块码")
private String mode;
@ApiModelProperty(value = "分类键值")
private String keycode;
@ApiModelProperty(value = "分类描述")
private String description;
}
package com.elitesland.system.vo;
import com.elitesland.core.base.BaseModel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* <pre>
* 系统用户信息,脱敏
* </pre>
*
* @author Mir
* @date 2020/6/16
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@ApiModel("系统用户信息,脱敏后")
public class UserVO extends BaseModel implements Serializable {
private static final long serialVersionUID = 5583360083063856158L;
@ApiModelProperty("用户账号")
private String username;
@ApiModelProperty("用户姓氏")
private String lastName;
@ApiModelProperty("用户名称")
private String firstName;
@ApiModelProperty("手机号")
private String mobile;
@ApiModelProperty("电子邮箱")
private String email;
}
This diff is collapsed.
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**
!**/src/test/**
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
### VS Code ###
.vscode/
/mvnw
/mvnw.cmd
/.mvn/
This diff is collapsed.
package com.elitesland.security;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
/**
* <pre>
* [功能说明]
* </pre>
*
* @author Mir
* @date 2020/6/16
*/
@Slf4j
public class JwtTokenFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
logger.debug("JWT token filter triggerred");
filterChain.doFilter(servletRequest, servletResponse);
}
}
package com.elitesland.security;
/**
* <pre>
* 登录方式分类
* </pre>
*
* @author Mir
* @date 2020/6/16
*/
public enum LoginTypeEnum {
//表单登录
FORM("表单登录"),
//JSON登录
JSON("JSON登录");
private final String desc;
LoginTypeEnum(String desc) {
this.desc = desc;
}
public String getDesc() {
return this.desc;
}
}
package com.elitesland.system.annotation;
import java.lang.annotation.*;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysCodeProc {
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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