commit message
This commit is contained in:
391
framework/pom.xml
Normal file
391
framework/pom.xml
Normal file
@@ -0,0 +1,391 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.lili</groupId>
|
||||
<artifactId>lili-shop-parent</artifactId>
|
||||
<version>1.0.1</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>framework</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<swagger-bootstrap-ui-version>1.9.6</swagger-bootstrap-ui-version>
|
||||
<alipay-sdk-version>4.13.40.ALL</alipay-sdk-version>
|
||||
<mysql-connector-version>5.1.48</mysql-connector-version>
|
||||
<mybatis-plus-version>3.3.1.tmp</mybatis-plus-version>
|
||||
<Hutool-version>5.5.8</Hutool-version>
|
||||
<TinyPinyin-verions>2.0.3.RELEASE</TinyPinyin-verions>
|
||||
<jasypt-version>3.0.0</jasypt-version>
|
||||
<neetl-version>2.9.10</neetl-version>
|
||||
<lombok-version>1.18.10</lombok-version>
|
||||
<minio-version>6.0.11</minio-version>
|
||||
<aliyun-version>4.5.18</aliyun-version>
|
||||
<aliyun-sdk-oss-version>3.11.1</aliyun-sdk-oss-version>
|
||||
<aliyun-sdk-dysms-version>2.0.1</aliyun-sdk-dysms-version>
|
||||
<rocketmq-version>2.1.1</rocketmq-version>
|
||||
<jwt-version>0.10.7</jwt-version>
|
||||
<antlr4-runtime-version>4.7.2</antlr4-runtime-version>
|
||||
<sharding-jdbc-version>4.0.0</sharding-jdbc-version>
|
||||
<druid-version>1.1.20</druid-version>
|
||||
<simple-http-version>1.0.3</simple-http-version>
|
||||
<antlr4-version>4.7.2</antlr4-version>
|
||||
<okhttp-version>4.4.1</okhttp-version>
|
||||
<httpclient-version>4.5.12</httpclient-version>
|
||||
<beetl-version>2.9.10</beetl-version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<java.version>1.8</java.version>
|
||||
<skipTests>true</skipTests>
|
||||
<knife4j.version>2.0.8</knife4j.version>
|
||||
<de.codecentric>2.3.1</de.codecentric>
|
||||
<userAgentUtils>1.21</userAgentUtils>
|
||||
<interceptor-api>1.2</interceptor-api>
|
||||
</properties>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>javax.interceptor</groupId>
|
||||
<artifactId>javax.interceptor-api</artifactId>
|
||||
<version>${interceptor-api}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.codecentric</groupId>
|
||||
<artifactId>spring-boot-admin-starter-client</artifactId>
|
||||
<version>${de.codecentric}</version>
|
||||
</dependency>
|
||||
<!-- 开源多维码生成工具 -->
|
||||
<dependency>
|
||||
<groupId>com.google.zxing</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>3.4.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.zxing</groupId>
|
||||
<artifactId>javase</artifactId>
|
||||
<version>3.4.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.28</version>
|
||||
</dependency>
|
||||
<!-- 开源多维码生成工具 -->
|
||||
<dependency>
|
||||
<groupId>com.github.xkzhangsan</groupId>
|
||||
<artifactId>xk-time</artifactId>
|
||||
<version>2.2.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.springframework.boot</groupId>-->
|
||||
<!-- <artifactId>spring-boot-starter-thymeleaf</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-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</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-aop</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>HdrHistogram</artifactId>
|
||||
<groupId>org.hdrhistogram</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context-support</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
<!--mongodb依赖配置-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-mongodb</artifactId>
|
||||
</dependency>
|
||||
<!-- Thymeleaf -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
<!-- JPA -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<!-- Websocket -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
<!-- MybatisPlus -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>${mybatis-plus-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mybatis.spring.boot</groupId>
|
||||
<artifactId>mybatis-spring-boot-starter-test</artifactId>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
<!-- Mysql Connector -->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>${mysql-connector-version}</version>
|
||||
</dependency>
|
||||
<!-- Redis-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<!-- Swagger API文档 -->
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-spring-boot-starter</artifactId>
|
||||
<version>${knife4j.version}</version>
|
||||
</dependency>
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>io.springfox</groupId>-->
|
||||
<!-- <artifactId>springfox-swagger-ui</artifactId>-->
|
||||
<!-- <version>${swagger-version}</version>-->
|
||||
<!-- <exclusions>-->
|
||||
<!-- <exclusion>-->
|
||||
<!-- <artifactId>guava</artifactId>-->
|
||||
<!-- <groupId>com.google.guava</groupId>-->
|
||||
<!-- </exclusion>-->
|
||||
<!-- <exclusion>-->
|
||||
<!-- <artifactId>javassist</artifactId>-->
|
||||
<!-- <groupId>org.javassist</groupId>-->
|
||||
<!-- </exclusion>-->
|
||||
<!-- </exclusions>-->
|
||||
<!-- </dependency>-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>io.springfox</groupId>-->
|
||||
<!-- <artifactId>springfox-swagger2</artifactId>-->
|
||||
<!-- <version>${swagger-version}</version>-->
|
||||
<!-- </dependency>-->
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>swagger-bootstrap-ui</artifactId>
|
||||
<version>${swagger-bootstrap-ui-version}</version>
|
||||
</dependency>
|
||||
<!-- Hutool工具包 -->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>${Hutool-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.biezhi</groupId>
|
||||
<artifactId>TinyPinyin</artifactId>
|
||||
<version>${TinyPinyin-verions}</version>
|
||||
</dependency>
|
||||
<!-- Lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok-version}</version>
|
||||
</dependency>
|
||||
<!-- Jasypt加密 -->
|
||||
<dependency>
|
||||
<groupId>com.github.ulisesbocchio</groupId>
|
||||
<artifactId>jasypt-spring-boot-starter</artifactId>
|
||||
<version>${jasypt-version}</version>
|
||||
</dependency>
|
||||
<!-- 模板引擎 -->
|
||||
<dependency>
|
||||
<groupId>com.ibeetl</groupId>
|
||||
<artifactId>beetl</artifactId>
|
||||
<version>${beetl-version}</version>
|
||||
</dependency>
|
||||
<!-- Minio -->
|
||||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio</artifactId>
|
||||
<version>${minio-version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>jsr305</artifactId>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>guava</artifactId>
|
||||
<groupId>com.google.guava</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<!-- 阿里云核心包-->
|
||||
<dependency>
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>aliyun-java-sdk-core</artifactId>
|
||||
<version>${aliyun-version}</version>
|
||||
</dependency>
|
||||
<!-- 阿里云OSS -->
|
||||
<dependency>
|
||||
<groupId>com.aliyun.oss</groupId>
|
||||
<artifactId>aliyun-sdk-oss</artifactId>
|
||||
<version>${aliyun-sdk-oss-version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>aliyun-java-sdk-core</artifactId>
|
||||
<groupId>com.aliyun</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>dysmsapi20170525</artifactId>
|
||||
<version>${aliyun-sdk-dysms-version}</version>
|
||||
</dependency>
|
||||
<!--脚本编程-->
|
||||
<dependency>
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
<artifactId>groovy</artifactId>
|
||||
</dependency>
|
||||
<!-- rocketmq-spring-boot-starter -->
|
||||
<dependency>
|
||||
<groupId>org.apache.rocketmq</groupId>
|
||||
<artifactId>rocketmq-spring-boot-starter</artifactId>
|
||||
<version>${rocketmq-version}</version>
|
||||
</dependency>
|
||||
<!-- token加密 -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
<version>${jwt-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<version>${jwt-version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<version>${jwt-version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<!--test-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<!-- <scope>test</scope>-->
|
||||
<!-- <exclusions>-->
|
||||
<!-- <exclusion>-->
|
||||
<!-- <groupId>org.junit.vintage</groupId>-->
|
||||
<!-- <artifactId>junit-vintage-engine</artifactId>-->
|
||||
<!-- </exclusion>-->
|
||||
<!-- </exclusions>-->
|
||||
</dependency>
|
||||
|
||||
<!-- 解决版本提示问题 -->
|
||||
<dependency>
|
||||
<groupId>org.antlr</groupId>
|
||||
<artifactId>antlr4</artifactId>
|
||||
<version>${antlr4-version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!--sharding jdbc springboot-->
|
||||
<dependency>
|
||||
<groupId>org.apache.shardingsphere</groupId>
|
||||
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
|
||||
<version>${sharding-jdbc-version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>groovy</artifactId>
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.shardingsphere</groupId>
|
||||
<artifactId>sharding-jdbc-spring-namespace</artifactId>
|
||||
<version>${sharding-jdbc-version}</version>
|
||||
</dependency>
|
||||
<!--druid-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid</artifactId>
|
||||
<version>${druid-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.xkcoding.http</groupId>
|
||||
<artifactId>simple-http</artifactId>
|
||||
<version>${simple-http-version}</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.antlr/antlr4-runtime -->
|
||||
<dependency>
|
||||
<groupId>org.antlr</groupId>
|
||||
<artifactId>antlr4-runtime</artifactId>
|
||||
<version>${antlr4-runtime-version}</version>
|
||||
</dependency>
|
||||
<!-- http client -->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.apache.httpcomponents</groupId>-->
|
||||
<!-- <artifactId>httpclient</artifactId>-->
|
||||
<!-- <version>${httpclient-version}</version>-->
|
||||
<!-- </dependency>-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>com.squareup.okhttp3</groupId>-->
|
||||
<!-- <artifactId>okhttp</artifactId>-->
|
||||
<!-- <version>${okhttp-version}</version>-->
|
||||
<!-- </dependency>-->
|
||||
<dependency>
|
||||
<groupId>com.alipay.sdk</groupId>
|
||||
<artifactId>alipay-sdk-java</artifactId>
|
||||
<version>${alipay-sdk-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!--用户端类型处理-->
|
||||
<dependency>
|
||||
<groupId>eu.bitwalker</groupId>
|
||||
<artifactId>UserAgentUtils</artifactId>
|
||||
<version>${userAgentUtils}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
81
framework/src/main/java/cn/lili/base/BaseEntity.java
Normal file
81
framework/src/main/java/cn/lili/base/BaseEntity.java
Normal file
@@ -0,0 +1,81 @@
|
||||
package cn.lili.base;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
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 org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.EntityListeners;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
/**
|
||||
* 数据库基础实体类
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v1.0
|
||||
* @since 2020/8/20 14:34
|
||||
*/
|
||||
@Data
|
||||
@MappedSuperclass
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
@JsonIgnoreProperties(value = {"hibernateLazyInitializer", "handler", "fieldHandler"})
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public abstract class BaseEntity implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
@Id
|
||||
@TableId
|
||||
@TableField
|
||||
@Column(columnDefinition = "bigint(20)")
|
||||
@ApiModelProperty(value = "唯一标识", hidden = true)
|
||||
private String id;
|
||||
|
||||
|
||||
@CreatedBy
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@ApiModelProperty(value = "创建者", hidden = true)
|
||||
private String createBy;
|
||||
|
||||
@CreatedDate
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@ApiModelProperty(value = "创建时间", hidden = true)
|
||||
private Date createTime;
|
||||
|
||||
@LastModifiedBy
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
@ApiModelProperty(value = "更新者", hidden = true)
|
||||
private String updateBy;
|
||||
|
||||
@LastModifiedDate
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
@ApiModelProperty(value = "更新时间", hidden = true)
|
||||
private Date updateTime;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@ApiModelProperty(value = "删除标志 true/false 删除/未删除", hidden = true)
|
||||
private Boolean deleteFlag ;
|
||||
|
||||
}
|
||||
45
framework/src/main/java/cn/lili/base/IdEntity.java
Normal file
45
framework/src/main/java/cn/lili/base/IdEntity.java
Normal file
@@ -0,0 +1,45 @@
|
||||
package cn.lili.base;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.EntityListeners;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
/**
|
||||
* 数据库基础实体类
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v1.0
|
||||
* @since 2020/8/20 14:34
|
||||
*/
|
||||
@Data
|
||||
@MappedSuperclass
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
@JsonIgnoreProperties(value = {"hibernateLazyInitializer", "handler", "fieldHandler"})
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public abstract class IdEntity implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
@Id
|
||||
@TableId
|
||||
@TableField
|
||||
@Column(columnDefinition = "bigint(20)")
|
||||
@ApiModelProperty(value = "唯一标识", hidden = true)
|
||||
private String id;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package cn.lili.base.mybatisplus;
|
||||
|
||||
import cn.lili.common.security.AuthUser;
|
||||
import cn.lili.common.security.context.UserContext;
|
||||
import cn.lili.common.utils.SnowFlake;
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 字段填充审计
|
||||
*
|
||||
* @author lili
|
||||
*/
|
||||
@Component
|
||||
public class MyMetaObjectHandler implements MetaObjectHandler {
|
||||
|
||||
@Override
|
||||
public void insertFill(MetaObject metaObject) {
|
||||
AuthUser authUser = UserContext.getCurrentUser();
|
||||
if (authUser != null) {
|
||||
this.setFieldValByName("createBy", authUser.getUsername(), metaObject);
|
||||
}
|
||||
this.setFieldValByName("createTime", new Date(), metaObject);
|
||||
//有值,则写入
|
||||
if (metaObject.hasGetter("deleteFlag")) {
|
||||
this.setFieldValByName("deleteFlag", false, metaObject);
|
||||
}
|
||||
if (metaObject.hasGetter("id")) {
|
||||
this.setFieldValByName("id", String.valueOf(SnowFlake.getId()), metaObject);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFill(MetaObject metaObject) {
|
||||
|
||||
AuthUser authUser = UserContext.getCurrentUser();
|
||||
if (authUser != null) {
|
||||
this.setFieldValByName("updateBy", authUser.getUsername(), metaObject);
|
||||
}
|
||||
this.setFieldValByName("updateTime", new Date(), metaObject);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package cn.lili.base.mybatisplus;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Chopper
|
||||
*/
|
||||
@Configuration
|
||||
@MapperScan({"cn.lili.modules.*.*.mapper","cn.lili.modules.*.mapper"})
|
||||
public class MybatisPlusConfig {
|
||||
/**
|
||||
* 分页插件,自动识别数据库类型
|
||||
*/
|
||||
@Bean
|
||||
public PaginationInterceptor paginationInterceptor() {
|
||||
return new PaginationInterceptor();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package cn.lili.common.aop.limiter;
|
||||
|
||||
import cn.lili.common.aop.limiter.annotation.LimitPoint;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.script.DefaultRedisScript;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* 流量拦截
|
||||
* @author Chopper
|
||||
*/
|
||||
@Aspect
|
||||
@Configuration
|
||||
@Slf4j
|
||||
public class LimitInterceptor {
|
||||
private RedisTemplate<String, Serializable> redisTemplate;
|
||||
|
||||
private DefaultRedisScript<Number> limitScript;
|
||||
|
||||
@Autowired
|
||||
public void setRedisTemplate(RedisTemplate redisTemplate) {
|
||||
this.redisTemplate = redisTemplate;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setLimitScript(DefaultRedisScript<Number> limitScript) {
|
||||
this.limitScript = limitScript;
|
||||
}
|
||||
|
||||
@Around("execution(public * *(..)) && @annotation(cn.lili.common.aop.limiter.annotation.LimitPoint)")
|
||||
public Object interceptor(ProceedingJoinPoint pjp) {
|
||||
MethodSignature signature = (MethodSignature) pjp.getSignature();
|
||||
Method method = signature.getMethod();
|
||||
LimitPoint limitPointAnnotation = method.getAnnotation(LimitPoint.class);
|
||||
LimitType limitType = limitPointAnnotation.limitType();
|
||||
String name = limitPointAnnotation.name();
|
||||
String key;
|
||||
int limitPeriod = limitPointAnnotation.period();
|
||||
int limitCount = limitPointAnnotation.limit();
|
||||
switch (limitType) {
|
||||
case IP:
|
||||
key = limitPointAnnotation.key() + getIpAddress();
|
||||
break;
|
||||
case CUSTOMER:
|
||||
key = limitPointAnnotation.key();
|
||||
break;
|
||||
default:
|
||||
key = StringUtils.upperCase(method.getName());
|
||||
}
|
||||
ImmutableList<String> keys = ImmutableList.of(StringUtils.join(limitPointAnnotation.prefix(), key));
|
||||
try {
|
||||
Number count = redisTemplate.execute(limitScript, keys, limitCount, limitPeriod);
|
||||
log.info("Access try count is {} for name={} and key = {}", count, name, key);
|
||||
if (count != null && count.intValue() <= limitCount) {
|
||||
return pjp.proceed();
|
||||
} else {
|
||||
throw new RuntimeException("访问过于频繁,请稍后再试");
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
if (e instanceof RuntimeException) {
|
||||
throw new RuntimeException(e.getLocalizedMessage());
|
||||
}
|
||||
throw new RuntimeException("服务器异常,请稍后再试");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final String UNKNOWN = "unknown";
|
||||
|
||||
public String getIpAddress() {
|
||||
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
|
||||
String ip = request.getHeader("x-forwarded-for");
|
||||
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
|
||||
ip = request.getRemoteAddr();
|
||||
}
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package cn.lili.common.aop.limiter;
|
||||
|
||||
|
||||
/**
|
||||
* redis 限流类型
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2018年2月02日 下午4:58:52
|
||||
*/
|
||||
|
||||
public enum LimitType {
|
||||
/**
|
||||
* 自定义key(即全局限流)
|
||||
*/
|
||||
CUSTOMER,
|
||||
/**
|
||||
* 根据请求者IP(IP限流)
|
||||
*/
|
||||
IP
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package cn.lili.common.aop.limiter.annotation;
|
||||
|
||||
|
||||
import cn.lili.common.aop.limiter.LimitType;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 限流注解
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2018-02-05
|
||||
*/
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface LimitPoint {
|
||||
/**
|
||||
* 资源的名字 无实际意义,但是可以用于排除异常
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String name() default "";
|
||||
|
||||
/**
|
||||
* 资源的key
|
||||
* <p>
|
||||
* 如果下方 limitType 值为自定义,那么全局限流参数来自于此
|
||||
* 如果limitType 为ip,则限流条件 为 key+ip
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String key() default "";
|
||||
|
||||
/**
|
||||
* Key的prefix redis前缀,可选
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String prefix() default "";
|
||||
|
||||
/**
|
||||
* 给定的时间段 单位秒
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int period() default 60;
|
||||
|
||||
/**
|
||||
* 最多的访问限制次数
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int limit() default 10;
|
||||
|
||||
/**
|
||||
* 类型 ip限制 还是自定义key值限制
|
||||
* 建议使用ip,自定义key属于全局限制,ip则是某节点设置,通常情况使用IP
|
||||
*
|
||||
* @return LimitType
|
||||
*/
|
||||
LimitType limitType() default LimitType.IP;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package cn.lili.common.aop.syslog.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 系统日志AOP注解
|
||||
*
|
||||
* @author Chopper
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface SystemLogPoint {
|
||||
|
||||
/**
|
||||
* 日志名称
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String description() default "";
|
||||
|
||||
/**
|
||||
* 自定义日志内容
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String customerLog() default "";
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
package cn.lili.common.aop.syslog.interceptor;
|
||||
|
||||
import cn.lili.common.aop.syslog.annotation.SystemLogPoint;
|
||||
import cn.lili.common.security.AuthUser;
|
||||
import cn.lili.common.security.context.UserContext;
|
||||
import cn.lili.common.security.enums.UserEnums;
|
||||
import cn.lili.common.utils.IpHelper;
|
||||
import cn.lili.common.utils.SpelUtil;
|
||||
import cn.lili.common.utils.ThreadPoolUtil;
|
||||
import cn.lili.modules.base.entity.systemlog.SystemLogVO;
|
||||
import cn.lili.modules.connect.util.IpUtils;
|
||||
import cn.lili.modules.permission.service.SystemLogService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.AfterReturning;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.NamedThreadLocal;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Spring AOP实现日志管理
|
||||
*
|
||||
* @author Chopper
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class SystemLogAspect {
|
||||
|
||||
/**
|
||||
* 启动线程异步记录日志
|
||||
*/
|
||||
private static final ThreadLocal<Date> beginTimeThreadLocal = new NamedThreadLocal<>("SYSTEM-LOG");
|
||||
|
||||
|
||||
private final SystemLogService systemLogService;
|
||||
|
||||
private final HttpServletRequest request;
|
||||
|
||||
private final IpHelper ipHelper;
|
||||
|
||||
/**
|
||||
* Controller层切点,注解方式
|
||||
*/
|
||||
@Pointcut("@annotation(cn.lili.common.aop.syslog.annotation.SystemLogPoint)")
|
||||
public void controllerAspect() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 前置通知 (在方法执行之前返回)用于拦截Controller层记录用户的操作的开始时间
|
||||
*/
|
||||
@Before("controllerAspect()")
|
||||
public void doBefore() {
|
||||
beginTimeThreadLocal.set(new Date());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 后置通知(在方法执行之后并返回数据) 用于拦截Controller层无异常的操作
|
||||
*
|
||||
* @param joinPoint 切点
|
||||
*/
|
||||
@AfterReturning(returning = "rvt", pointcut = "controllerAspect()")
|
||||
public void after(JoinPoint joinPoint, Object rvt) {
|
||||
try {
|
||||
Map map = spelFormat(joinPoint, rvt);
|
||||
String description = map.get("description").toString();
|
||||
String customerLog = map.get("customerLog").toString();
|
||||
|
||||
Map<String, String[]> logParams = request.getParameterMap();
|
||||
AuthUser authUser = UserContext.getCurrentUser();
|
||||
SystemLogVO systemLogVO = new SystemLogVO();
|
||||
|
||||
if (authUser == null) {
|
||||
//如果是商家则记录商家id,否则记录-1,代表平台id
|
||||
systemLogVO.setStoreId(-2L);
|
||||
//请求用户
|
||||
systemLogVO.setUsername("游客");
|
||||
} else {
|
||||
//如果是商家则记录商家id,否则记录-1,代表平台id
|
||||
systemLogVO.setStoreId(authUser.getRole().equals(UserEnums.STORE) ? Long.parseLong(authUser.getStoreId()) : -1);
|
||||
//请求用户
|
||||
systemLogVO.setUsername(authUser.getUsername());
|
||||
}
|
||||
|
||||
//日志标题
|
||||
systemLogVO.setName(description);
|
||||
//日志请求url
|
||||
systemLogVO.setRequestUrl(request.getRequestURI());
|
||||
//请求方式
|
||||
systemLogVO.setRequestType(request.getMethod());
|
||||
//请求参数
|
||||
systemLogVO.setMapToParams(logParams);
|
||||
//响应参数 此处数据太大了,所以先注释掉
|
||||
// systemLogVO.setResponseBody(JSONUtil.toJsonStr(rvt));
|
||||
//请求IP
|
||||
systemLogVO.setIp(IpUtils.getIpAddress(request));
|
||||
//IP地址
|
||||
systemLogVO.setIpInfo(ipHelper.getIpCity(request));
|
||||
//写入自定义日志内容
|
||||
systemLogVO.setCustomerLog(customerLog);
|
||||
//请求开始时间
|
||||
long beginTime = beginTimeThreadLocal.get().getTime();
|
||||
long endTime = System.currentTimeMillis();
|
||||
//请求耗时
|
||||
Long usedTime = endTime - beginTime;
|
||||
systemLogVO.setCostTime(usedTime.intValue());
|
||||
//调用线程保存
|
||||
ThreadPoolUtil.getPool().execute(new SaveSystemLogThread(systemLogVO, systemLogService));
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("系统日志保存异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存日志
|
||||
*/
|
||||
private static class SaveSystemLogThread implements Runnable {
|
||||
|
||||
private final SystemLogVO systemLogVO;
|
||||
private final SystemLogService systemLogService;
|
||||
|
||||
public SaveSystemLogThread(SystemLogVO systemLogVO, SystemLogService systemLogService) {
|
||||
this.systemLogVO = systemLogVO;
|
||||
this.systemLogService = systemLogService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
systemLogService.saveLog(systemLogVO);
|
||||
} catch (Exception e) {
|
||||
log.error("系统日志保存异常,内容{}:", systemLogVO, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取注解中对方法的描述信息 用于Controller层注解
|
||||
*
|
||||
* @param joinPoint 切点
|
||||
* @return 方法描述
|
||||
* @throws Exception
|
||||
*/
|
||||
private static Map<String, String> spelFormat(JoinPoint joinPoint, Object rvt) {
|
||||
|
||||
Map<String, String> result = new HashMap<>();
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
SystemLogPoint systemLogPoint = signature.getMethod().getAnnotation(SystemLogPoint.class);
|
||||
String description = systemLogPoint.description();
|
||||
String customerLog = SpelUtil.compileParams(joinPoint, rvt, systemLogPoint.customerLog());
|
||||
|
||||
result.put("description", description);
|
||||
result.put("customerLog", customerLog);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
235
framework/src/main/java/cn/lili/common/cache/Cache.java
vendored
Normal file
235
framework/src/main/java/cn/lili/common/cache/Cache.java
vendored
Normal file
@@ -0,0 +1,235 @@
|
||||
package cn.lili.common.cache;
|
||||
|
||||
|
||||
import org.springframework.data.redis.core.ZSetOperations;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 缓存接口
|
||||
*
|
||||
* @author Chopper
|
||||
*/
|
||||
public interface Cache<T> {
|
||||
|
||||
/**
|
||||
* Get an item from the cache, nontransactionally
|
||||
*
|
||||
* @param key
|
||||
* @return the cached object or <tt>null</tt>
|
||||
*/
|
||||
T get(Object key);
|
||||
|
||||
/**
|
||||
* Get an item from the cache, nontransactionally
|
||||
*
|
||||
* @param key
|
||||
* @return the cached object or <tt>null</tt>
|
||||
*/
|
||||
String getString(Object key);
|
||||
|
||||
|
||||
/**
|
||||
* multiGet
|
||||
*
|
||||
* @param keys 要查询的key集合
|
||||
* @return
|
||||
*/
|
||||
List multiGet(Collection keys);
|
||||
|
||||
/**
|
||||
* 批量set
|
||||
*
|
||||
* @param map
|
||||
*/
|
||||
void multiSet(Map map);
|
||||
|
||||
|
||||
/**
|
||||
* 批量删除
|
||||
*
|
||||
* @param keys 要删除的key集合
|
||||
*/
|
||||
void multiDel(Collection keys);
|
||||
|
||||
/**
|
||||
* Add an item to the cache, nontransactionally, with
|
||||
* failfast semantics
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
void put(Object key, T value);
|
||||
|
||||
/**
|
||||
* 往缓存中写入内容
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @param exp 超时时间,单位为秒
|
||||
*/
|
||||
void put(Object key, T value, Long exp);
|
||||
|
||||
/**
|
||||
* 往缓存中写入内容
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @param exp
|
||||
* @param timeUnit 写入单位
|
||||
*/
|
||||
void put(Object key, T value, Long exp, TimeUnit timeUnit);
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*
|
||||
* @param key
|
||||
*/
|
||||
void remove(Object key);
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*
|
||||
* @param key
|
||||
*/
|
||||
void vagueDel(Object key);
|
||||
|
||||
/**
|
||||
* Clear the cache
|
||||
*/
|
||||
void clear();
|
||||
|
||||
|
||||
/**
|
||||
* 往缓存中写入内容
|
||||
*
|
||||
* @param key 缓存key
|
||||
* @param hashKey 缓存中hashKey
|
||||
* @param hashValue hash值
|
||||
*/
|
||||
void putHash(Object key, Object hashKey, Object hashValue);
|
||||
|
||||
/**
|
||||
* 玩缓存中写入内容
|
||||
*
|
||||
* @param key
|
||||
* @param map
|
||||
*/
|
||||
void putAllHash(Object key, Map map);
|
||||
|
||||
/**
|
||||
* 读取缓存值
|
||||
*
|
||||
* @param key
|
||||
* @param hashKey
|
||||
* @return
|
||||
*/
|
||||
T getHash(Object key, Object hashKey);
|
||||
|
||||
/**
|
||||
* 读取缓存值
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
Map<Object, Object> getHash(Object key);
|
||||
|
||||
/**
|
||||
* 是否包含
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
boolean hasKey(Object key);
|
||||
|
||||
|
||||
/**
|
||||
* 模糊匹配key
|
||||
*
|
||||
* @param pattern
|
||||
* @return
|
||||
*/
|
||||
List<String> keys(String pattern);
|
||||
|
||||
|
||||
//-----------------------------------------------用于特殊场景,redis去重计数---------------------------------------------
|
||||
|
||||
/**
|
||||
* 累计数目
|
||||
* 效率较高的 计数器
|
||||
* 如需清零,按照普通key 移除即可
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
Long cumulative(Object key, Object value);
|
||||
|
||||
/**
|
||||
* 计数器结果
|
||||
* <p>
|
||||
* 效率较高的 计数器 统计返回
|
||||
* 如需清零,按照普通key 移除即可
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
Long counter(Object key);
|
||||
|
||||
/**
|
||||
* 批量计数
|
||||
*
|
||||
* @param keys 要查询的key集合
|
||||
* @return
|
||||
*/
|
||||
List multiCounter(Collection keys);
|
||||
|
||||
/**
|
||||
* 计数器结果
|
||||
* <p>
|
||||
* 效率较高的 计数器 统计返回
|
||||
* 如需清零,按照普通key 移除即可
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
Long mergeCounter(Object... key);
|
||||
//---------------------------------------------------用于特殊场景,redis去重统计-----------------------------------------
|
||||
|
||||
//-----------------------------------------------redis计数---------------------------------------------
|
||||
|
||||
/**
|
||||
* redis 计数器 累加
|
||||
* 注:到达liveTime之后,该次增加取消,即自动-1,而不是redis值为空
|
||||
*
|
||||
* @param key 为累计的key,同一key每次调用则值 +1
|
||||
* @param liveTime 单位秒后失效
|
||||
* @return
|
||||
*/
|
||||
Long incr(String key, long liveTime);
|
||||
//-----------------------------------------------redis计数---------------------------------------------
|
||||
|
||||
/**
|
||||
* 使用Sorted Set记录keyword
|
||||
* zincrby命令,对于一个Sorted Set,存在的就把分数加x(x可自行设定),不存在就创建一个分数为1的成员
|
||||
*
|
||||
* @param sortedSetName sortedSetName的Sorted Set不用预先创建,不存在会自动创建,存在则向里添加数据
|
||||
* @param keyword 关键词
|
||||
*/
|
||||
void incrementScore(String sortedSetName, String keyword);
|
||||
|
||||
/**
|
||||
* zrevrange命令, 查询Sorted Set中指定范围的值
|
||||
* 返回的有序集合中,score大的在前面
|
||||
* zrevrange方法无需担心用于指定范围的start和end出现越界报错问题
|
||||
*
|
||||
* @param sortedSetName sortedSetName
|
||||
* @param start 查询范围开始位置
|
||||
* @param end 查询范围结束位置
|
||||
* @return
|
||||
*/
|
||||
Set<ZSetOperations.TypedTuple<Object>> reverseRangeWithScores(String sortedSetName, Integer start, Integer end);
|
||||
}
|
||||
474
framework/src/main/java/cn/lili/common/cache/CachePrefix.java
vendored
Normal file
474
framework/src/main/java/cn/lili/common/cache/CachePrefix.java
vendored
Normal file
@@ -0,0 +1,474 @@
|
||||
package cn.lili.common.cache;
|
||||
|
||||
import cn.lili.common.security.enums.UserEnums;
|
||||
import cn.lili.modules.promotion.entity.enums.PromotionTypeEnum;
|
||||
|
||||
/**
|
||||
* 缓存前缀
|
||||
*
|
||||
* @author pikachu
|
||||
* @version 4.1
|
||||
* @since 1.0
|
||||
* 2018/3/19
|
||||
*/
|
||||
public enum CachePrefix {
|
||||
|
||||
|
||||
/**
|
||||
* nonce
|
||||
*/
|
||||
NONCE,
|
||||
|
||||
/**
|
||||
* 在线人数
|
||||
*/
|
||||
ONLINE_NUM,
|
||||
|
||||
|
||||
/**
|
||||
* 会员分布数据
|
||||
*/
|
||||
MEMBER_DISTRIBUTION,
|
||||
|
||||
/**
|
||||
* 在线会员统计
|
||||
*/
|
||||
ONLINE_MEMBER,
|
||||
|
||||
/**
|
||||
* token 信息
|
||||
*/
|
||||
ACCESS_TOKEN,
|
||||
/**
|
||||
* token 信息
|
||||
*/
|
||||
REFRESH_TOKEN,
|
||||
/**
|
||||
* 联合登录响应
|
||||
*/
|
||||
CONNECT_RESULT,
|
||||
/**
|
||||
* 微信联合登陆 session key
|
||||
*/
|
||||
SESSION_KEY,
|
||||
/**
|
||||
* 权限
|
||||
*/
|
||||
PERMISSION_LIST,
|
||||
/**
|
||||
* 部门id列表
|
||||
*/
|
||||
DEPARTMENT_IDS,
|
||||
|
||||
/**
|
||||
* 用户错误登录限制
|
||||
*/
|
||||
LOGIN_TIME_LIMIT,
|
||||
/**
|
||||
* 系统设置
|
||||
*/
|
||||
SETTING,
|
||||
|
||||
/**
|
||||
* 验证码滑块源
|
||||
*/
|
||||
VERIFICATION,
|
||||
|
||||
/**
|
||||
* 验证码滑块源
|
||||
*/
|
||||
VERIFICATION_IMAGE,
|
||||
|
||||
/**
|
||||
* 快递平台
|
||||
*/
|
||||
EXPRESS,
|
||||
|
||||
/**
|
||||
* 图片验证码
|
||||
*/
|
||||
CAPTCHA,
|
||||
|
||||
/**
|
||||
* 商品
|
||||
*/
|
||||
GOODS,
|
||||
|
||||
/**
|
||||
* 商品SKU
|
||||
*/
|
||||
GOODS_SKU,
|
||||
|
||||
/**
|
||||
* 运费模板脚本
|
||||
*/
|
||||
SHIP_SCRIPT,
|
||||
|
||||
/**
|
||||
* 商品sku
|
||||
*/
|
||||
SKU,
|
||||
|
||||
/**
|
||||
* sku库存
|
||||
*/
|
||||
SKU_STOCK,
|
||||
|
||||
/**
|
||||
* 促销商品sku库存
|
||||
*/
|
||||
PROMOTION_GOODS_STOCK,
|
||||
|
||||
/**
|
||||
* 商品库存
|
||||
*/
|
||||
GOODS_STOCK,
|
||||
|
||||
/**
|
||||
* 商品分类
|
||||
*/
|
||||
CATEGORY,
|
||||
/**
|
||||
* 浏览次数
|
||||
*/
|
||||
VISIT_COUNT,
|
||||
/**
|
||||
* 存储方案
|
||||
*/
|
||||
UPLOADER,
|
||||
/**
|
||||
* 地区
|
||||
*/
|
||||
REGION,
|
||||
|
||||
/**
|
||||
* 短信网关
|
||||
*/
|
||||
SPlATFORM,
|
||||
/**
|
||||
* 短信验证码前缀
|
||||
*/
|
||||
_CODE_PREFIX,
|
||||
/**
|
||||
* smtp
|
||||
*/
|
||||
SMTP,
|
||||
/**
|
||||
* 系统设置
|
||||
*/
|
||||
SETTINGS,
|
||||
/**
|
||||
* 电子面单
|
||||
*/
|
||||
WAYBILL,
|
||||
/**
|
||||
* 短信验证码
|
||||
*/
|
||||
SMS_CODE,
|
||||
/**
|
||||
* 邮箱验证码
|
||||
*/
|
||||
EMAIL_CODE,
|
||||
/**
|
||||
* 管理员角色权限对照表
|
||||
*/
|
||||
ADMIN_URL_ROLE,
|
||||
|
||||
/**
|
||||
* 店铺管理员角色权限对照表
|
||||
*/
|
||||
SHOP_URL_ROLE,
|
||||
|
||||
/**
|
||||
* 手机验证标识
|
||||
*/
|
||||
MOBILE_VALIDATE,
|
||||
|
||||
/**
|
||||
* 邮箱验证标识
|
||||
*/
|
||||
EMAIL_VALIDATE,
|
||||
|
||||
/**
|
||||
* 店铺运费模版列表
|
||||
*/
|
||||
SHIP_TEMPLATE,
|
||||
|
||||
/**
|
||||
* 店铺中某个运费模版
|
||||
*/
|
||||
SHIP_TEMPLATE_ONE,
|
||||
|
||||
//================促销=================
|
||||
/**
|
||||
* 促销活动
|
||||
*/
|
||||
PROMOTION,
|
||||
/**
|
||||
* 促销活动
|
||||
*/
|
||||
PROMOTION_GOODS,
|
||||
|
||||
/*** 单品立减 */
|
||||
STORE_ID_MINUS,
|
||||
|
||||
/*** 第二件半价 */
|
||||
STORE_ID_HALF_PRICE,
|
||||
|
||||
/*** 满优惠 */
|
||||
STORE_ID_FULL_DISCOUNT,
|
||||
|
||||
/**
|
||||
* 限时抢购活动缓存key前缀
|
||||
*/
|
||||
STORE_ID_SECKILL,
|
||||
|
||||
/**
|
||||
* 团购活动缓存key前缀
|
||||
*/
|
||||
STORE_ID_GROUP_BUY,
|
||||
|
||||
/**
|
||||
* 积分商品缓存key前缀
|
||||
*/
|
||||
STORE_ID_EXCHANGE,
|
||||
|
||||
|
||||
//================交易=================
|
||||
|
||||
/**
|
||||
* 购物车原始数据
|
||||
*/
|
||||
CART_ORIGIN_DATA_PREFIX,
|
||||
|
||||
/**
|
||||
* 立即购买原始数据
|
||||
*/
|
||||
BUY_NOW_ORIGIN_DATA_PREFIX,
|
||||
|
||||
/**
|
||||
* 交易原始数据
|
||||
*/
|
||||
TRADE_ORIGIN_DATA_PREFIX,
|
||||
|
||||
/**
|
||||
* 立即购买sku
|
||||
*/
|
||||
CART_SKU_PREFIX,
|
||||
|
||||
/**
|
||||
* 购物车视图
|
||||
*/
|
||||
CART_MEMBER_ID_PREFIX,
|
||||
|
||||
/**
|
||||
* 购物车,用户选择的促销信息
|
||||
*/
|
||||
CART_PROMOTION_PREFIX,
|
||||
|
||||
|
||||
/**
|
||||
* 交易_交易价格的前缀
|
||||
*/
|
||||
PRICE_SESSION_ID_PREFIX,
|
||||
|
||||
/**
|
||||
* 交易_交易单
|
||||
*/
|
||||
TRADE_SESSION_ID_PREFIX,
|
||||
|
||||
|
||||
/**
|
||||
* 结算参数
|
||||
*/
|
||||
CHECKOUT_PARAM_ID_PREFIX,
|
||||
|
||||
/**
|
||||
* 交易单号前缀
|
||||
*/
|
||||
TRADE_SN_CACHE_PREFIX,
|
||||
|
||||
/**
|
||||
* 订单编号前缀
|
||||
*/
|
||||
ORDER_SN_CACHE_PREFIX,
|
||||
/**
|
||||
* 订单编号标记
|
||||
*/
|
||||
ORDER_SN_SIGN_CACHE_PREFIX,
|
||||
/**
|
||||
* 订单编号前缀
|
||||
*/
|
||||
PAY_LOG_SN_CACHE_PREFIX,
|
||||
|
||||
/**
|
||||
* 合同编号
|
||||
*/
|
||||
CONTRACT_SN_CACHE_PREFIX,
|
||||
|
||||
|
||||
/**
|
||||
* 零钱
|
||||
*/
|
||||
SMALL_CHANGE_CACHE_PREFIX,
|
||||
|
||||
/**
|
||||
* 售后服务单号前缀
|
||||
*/
|
||||
AFTER_SALE_SERVICE_PREFIX,
|
||||
|
||||
/**
|
||||
* 交易
|
||||
*/
|
||||
TRADE,
|
||||
|
||||
/**
|
||||
* 站点导航栏
|
||||
*/
|
||||
SITE_NAVIGATION,
|
||||
|
||||
/**
|
||||
* 支付参数
|
||||
*/
|
||||
PAYMENT_CONFIG,
|
||||
|
||||
/**
|
||||
* 流程控制
|
||||
*/
|
||||
FLOW,
|
||||
|
||||
/**
|
||||
* 热门搜索
|
||||
*/
|
||||
HOT_WORD,
|
||||
|
||||
/**
|
||||
* 会员积分
|
||||
*/
|
||||
MEMBER_POINT,
|
||||
|
||||
/**
|
||||
* 会员积分
|
||||
*/
|
||||
POINT_ORDER,
|
||||
|
||||
|
||||
/**
|
||||
* 微博登录
|
||||
*/
|
||||
WEIBO_STATE,
|
||||
/**
|
||||
* 微博登录
|
||||
*/
|
||||
QQ_STATE,
|
||||
/**
|
||||
* 微博登录
|
||||
*/
|
||||
GITHUB_STATE,
|
||||
/**
|
||||
* 验证码key
|
||||
*/
|
||||
VERIFICATION_KEY,
|
||||
/**
|
||||
* 验证码验证结果
|
||||
*/
|
||||
VERIFICATION_RESULT,
|
||||
/**
|
||||
* 微信CGItoken
|
||||
*/
|
||||
WECHAT_CGI_ACCESS_TOKEN,
|
||||
/**
|
||||
* 微信JSApitoken
|
||||
*/
|
||||
WECHAT_JS_API_TOKEN,
|
||||
/**
|
||||
* 微信会话信息
|
||||
*/
|
||||
WECHAT_SESSION_PARAMS,
|
||||
/**
|
||||
* 第三方用户权限
|
||||
*/
|
||||
ALIPAY_CONFIG,
|
||||
/**
|
||||
* 微信支付配置
|
||||
*/
|
||||
WECHAT_PAY_CONFIG,
|
||||
/**
|
||||
* 微信支付平台证书配置
|
||||
*/
|
||||
WECHAT_PLAT_FORM_CERT,
|
||||
/**
|
||||
* 第三方用户权限
|
||||
*/
|
||||
CONNECT_AUTH,
|
||||
/**
|
||||
* 平台PageView 统计
|
||||
*/
|
||||
PV,
|
||||
/**
|
||||
* 平台UserView 统计
|
||||
*/
|
||||
UV,
|
||||
/**
|
||||
* 平台 商品PV 统计
|
||||
*/
|
||||
GOODS_PV,
|
||||
/**
|
||||
* 平台 商品UV 统计
|
||||
*/
|
||||
GOODS_UV,
|
||||
/**
|
||||
* 店铺PageView 统计
|
||||
*/
|
||||
STORE_PV,
|
||||
/**
|
||||
* 店铺UserView 统计
|
||||
*/
|
||||
STORE_UV,
|
||||
/**
|
||||
* 店铺 商品PV 统计
|
||||
*/
|
||||
STORE_GOODS_PV,
|
||||
/**
|
||||
* 店铺 商品UV 统计
|
||||
*/
|
||||
STORE_GOODS_UV,
|
||||
/**
|
||||
* 分销员
|
||||
*/
|
||||
DISTRIBUTION,
|
||||
|
||||
/**
|
||||
* 找回手机
|
||||
*/
|
||||
FIND_MOBILE,
|
||||
/**
|
||||
* 文章分类
|
||||
*/
|
||||
ARTICLE_CATEGORY,
|
||||
/**
|
||||
* 文章
|
||||
*/
|
||||
ARTICLE_CACHE
|
||||
;
|
||||
|
||||
|
||||
public static String removePrefix(String str) {
|
||||
return str.substring(str.lastIndexOf("}_") + 2);
|
||||
}
|
||||
|
||||
//通用获取缓存key值
|
||||
public String getPrefix() {
|
||||
return "{" + this.name() + "}_";
|
||||
}
|
||||
|
||||
//通用获取缓存key值
|
||||
public String getPrefix(PromotionTypeEnum typeEnum) {
|
||||
return "{" + this.name() + "_" + typeEnum.name() + "}_";
|
||||
}
|
||||
|
||||
//获取缓存key值 + 用户端,例如:三端都有用户体系,需要分别登录,如果用户名一致,则redis中的权限可能会冲突出错
|
||||
public String getPrefix(UserEnums user) {
|
||||
return "{" + this.name() + "_" + user.name() + "}_";
|
||||
}
|
||||
}
|
||||
101
framework/src/main/java/cn/lili/common/cache/MybatisRedisCache.java
vendored
Normal file
101
framework/src/main/java/cn/lili/common/cache/MybatisRedisCache.java
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
package cn.lili.common.cache;
|
||||
|
||||
import cn.lili.common.utils.SpringContextUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.cache.Cache;
|
||||
import org.springframework.data.redis.connection.RedisServerCommands;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
/**
|
||||
* MybatisRedisCache
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v1.0
|
||||
* @since
|
||||
* 2020-04-01 2:59 下午
|
||||
* 不赞成使用此方式注解,统一使用Cacheable 更为合适
|
||||
*
|
||||
* 使用方法 @CacheNamespace(implementation= MybatisRedisCache.class,eviction=MybatisRedisCache.class)
|
||||
*/
|
||||
@Deprecated
|
||||
@Slf4j
|
||||
public class MybatisRedisCache implements Cache {
|
||||
|
||||
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
|
||||
|
||||
|
||||
private RedisTemplate<Object, Object> getRedisTemplate() {
|
||||
return (RedisTemplate<Object, Object>) SpringContextUtil.getBean("redisTemplate");
|
||||
}
|
||||
|
||||
private final String id;
|
||||
|
||||
public MybatisRedisCache(final String id) {
|
||||
if (id == null) {
|
||||
throw new IllegalArgumentException("Cache instances require an ID");
|
||||
}
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putObject(Object key, Object value) {
|
||||
try {
|
||||
if (value != null) {
|
||||
log.info("写入缓存:" + key.toString()+"----"+value.toString());
|
||||
getRedisTemplate().opsForValue().set(key.toString(), value);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("写入mybatis缓存异常 ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject(Object key) {
|
||||
try {
|
||||
if (key != null) {
|
||||
log.info("获取缓存:" + key);
|
||||
return getRedisTemplate().opsForValue().get(key.toString());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("mybatis缓存获取异常 ", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object removeObject(Object key) {
|
||||
if (key != null) {
|
||||
getRedisTemplate().delete(key.toString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
Set<Object> keys = getRedisTemplate().keys("*:" + this.id + "*");
|
||||
if (!CollectionUtils.isEmpty(keys)) {
|
||||
getRedisTemplate().delete(keys);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
Long size = getRedisTemplate().execute(RedisServerCommands::dbSize);
|
||||
return size.intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadWriteLock getReadWriteLock() {
|
||||
return this.readWriteLock;
|
||||
}
|
||||
}
|
||||
243
framework/src/main/java/cn/lili/common/cache/impl/RedisCache.java
vendored
Normal file
243
framework/src/main/java/cn/lili/common/cache/impl/RedisCache.java
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
package cn.lili.common.cache.impl;
|
||||
|
||||
import cn.lili.common.cache.Cache;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.core.*;
|
||||
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* redis 缓存实现
|
||||
*
|
||||
* @author Chopepr
|
||||
*/
|
||||
@Component
|
||||
public class RedisCache implements Cache {
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<Object, Object> redisTemplate;
|
||||
|
||||
public RedisCache() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(Object key) {
|
||||
|
||||
return redisTemplate.opsForValue().get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(Object key) {
|
||||
try {
|
||||
return redisTemplate.opsForValue().get(key).toString();
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List multiGet(Collection keys) {
|
||||
return redisTemplate.opsForValue().multiGet(keys);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void multiSet(Map map) {
|
||||
redisTemplate.opsForValue().multiSet(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void multiDel(Collection keys) {
|
||||
redisTemplate.delete(keys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(Object key, Object value) {
|
||||
redisTemplate.opsForValue().set(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(Object key, Object value, Long exp) {
|
||||
put(key, value, exp, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(Object key, Object value, Long exp, TimeUnit timeUnit) {
|
||||
redisTemplate.opsForValue().set(key, value, exp, timeUnit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Object key) {
|
||||
|
||||
redisTemplate.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*
|
||||
* @param key
|
||||
*/
|
||||
@Override
|
||||
public void vagueDel(Object key) {
|
||||
Set<Object> keys = redisTemplate.keys(key + "*");
|
||||
redisTemplate.delete(keys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
|
||||
Set keys = redisTemplate.keys("*");
|
||||
redisTemplate.delete(keys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putHash(Object key, Object hashKey, Object hashValue) {
|
||||
redisTemplate.opsForHash().put(key, hashKey, hashValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAllHash(Object key, Map map) {
|
||||
redisTemplate.opsForHash().putAll(key, map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getHash(Object key, Object hashKey) {
|
||||
return redisTemplate.opsForHash().get(key, hashKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Object, Object> getHash(Object key) {
|
||||
return this.redisTemplate.opsForHash().entries(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasKey(Object key) {
|
||||
return this.redisTemplate.opsForValue().get(key) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取符合条件的key
|
||||
*
|
||||
* @param pattern 表达式
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public List<String> keys(String pattern) {
|
||||
List<String> keys = new ArrayList<>();
|
||||
this.scan(pattern, item -> {
|
||||
//符合条件的key
|
||||
String key = new String(item, StandardCharsets.UTF_8);
|
||||
keys.add(key);
|
||||
});
|
||||
return keys;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* scan 实现
|
||||
*
|
||||
* @param pattern 表达式
|
||||
* @param consumer 对迭代到的key进行操作
|
||||
*/
|
||||
private void scan(String pattern, Consumer<byte[]> consumer) {
|
||||
this.redisTemplate.execute((RedisConnection connection) -> {
|
||||
try (Cursor<byte[]> cursor =
|
||||
connection.scan(ScanOptions.scanOptions()
|
||||
.count(Long.MAX_VALUE)
|
||||
.match(pattern).build())) {
|
||||
cursor.forEachRemaining(consumer);
|
||||
return null;
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Long cumulative(Object key, Object value) {
|
||||
HyperLogLogOperations<Object, Object> operations = redisTemplate.opsForHyperLogLog();
|
||||
// add 方法对应 PFADD 命令
|
||||
return operations.add(key, value);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long counter(Object key) {
|
||||
HyperLogLogOperations<Object, Object> operations = redisTemplate.opsForHyperLogLog();
|
||||
|
||||
// add 方法对应 PFADD 命令
|
||||
return operations.size(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List multiCounter(Collection keys) {
|
||||
if (keys == null) {
|
||||
return new ArrayList();
|
||||
}
|
||||
List<Long> result = new ArrayList<>();
|
||||
for (Object key : keys) {
|
||||
result.add(counter(key));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long mergeCounter(Object... key) {
|
||||
HyperLogLogOperations<Object, Object> operations = redisTemplate.opsForHyperLogLog();
|
||||
// 计数器合并累加
|
||||
return operations.union(key[0], key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long incr(String key, long liveTime) {
|
||||
RedisAtomicLong entityIdCounter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
|
||||
Long increment = entityIdCounter.getAndIncrement();
|
||||
|
||||
if ((null == increment || increment.longValue() == 0) && liveTime > 0) {//初始设置过期时间
|
||||
entityIdCounter.expire(liveTime, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
return increment;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用Sorted Set记录keyword
|
||||
* zincrby命令,对于一个Sorted Set,存在的就把分数加x(x可自行设定),不存在就创建一个分数为1的成员
|
||||
*
|
||||
* @param sortedSetName sortedSetName的Sorted Set不用预先创建,不存在会自动创建,存在则向里添加数据
|
||||
* @param keyword 关键词
|
||||
*/
|
||||
@Override
|
||||
public void incrementScore(String sortedSetName, String keyword) {
|
||||
// x 的含义请见本方法的注释
|
||||
double x = 1.0;
|
||||
this.redisTemplate.opsForZSet().incrementScore(sortedSetName, keyword, x);
|
||||
}
|
||||
|
||||
/**
|
||||
* zrevrange命令, 查询Sorted Set中指定范围的值
|
||||
* 返回的有序集合中,score大的在前面
|
||||
* zrevrange方法无需担心用于指定范围的start和end出现越界报错问题
|
||||
*
|
||||
* @param sortedSetName sortedSetName
|
||||
* @param start 查询范围开始位置
|
||||
* @param end 查询范围结束位置
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Set<ZSetOperations.TypedTuple<Object>> reverseRangeWithScores(String sortedSetName, Integer start, Integer end) {
|
||||
return this.redisTemplate.opsForZSet().reverseRangeWithScores(sortedSetName, start, end);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package cn.lili.common.delayqueue;
|
||||
|
||||
import cn.lili.common.utils.RedisUtil;
|
||||
import cn.lili.common.utils.ThreadPoolUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.DefaultTypedTuple;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.Calendar;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 延时队列工厂
|
||||
*
|
||||
* @author paulG
|
||||
* @since 2020/11/7
|
||||
**/
|
||||
@Slf4j
|
||||
public abstract class AbstractDelayQueueMachineFactory {
|
||||
|
||||
@Autowired
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
/**
|
||||
* 插入任务id
|
||||
*
|
||||
* @param jobId 任务id(队列内唯一)
|
||||
* @param time 延时时间(单位 :秒)
|
||||
* @return 是否插入成功
|
||||
*/
|
||||
public boolean addJobId(String jobId, Integer time) {
|
||||
Calendar instance = Calendar.getInstance();
|
||||
instance.add(Calendar.SECOND, time);
|
||||
long delaySeconds = instance.getTimeInMillis() / 1000;
|
||||
return redisUtil.zadd(setDelayQueueName(), delaySeconds, jobId);
|
||||
|
||||
}
|
||||
|
||||
private void startDelayQueueMachine() {
|
||||
log.info(String.format("延时队列机器{%s}开始运作", setDelayQueueName()));
|
||||
|
||||
// 发生异常捕获并且继续不能让战斗停下来
|
||||
while (true) {
|
||||
try {
|
||||
// 获取当前时间的时间戳
|
||||
long now = System.currentTimeMillis() / 1000;
|
||||
// 获取当前时间前的任务列表
|
||||
Set<DefaultTypedTuple> tuples = redisUtil.zrangeByScoreWithScores(setDelayQueueName(), 0, now);
|
||||
// 如果不为空则遍历判断其是否满足取消要求
|
||||
if (!CollectionUtils.isEmpty(tuples)) {
|
||||
for (DefaultTypedTuple tuple : tuples) {
|
||||
|
||||
String jobId = (String) tuple.getValue();
|
||||
Long num = redisUtil.zremove(setDelayQueueName(), jobId);
|
||||
// 如果移除成功, 则执行
|
||||
if (num > 0) {
|
||||
ThreadPoolUtil.execute(() -> invoke(jobId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
// log.warn(String.format("处理延时任务发生异常,异常原因为{%s}", e.getMessage()), e);
|
||||
} finally {
|
||||
// 间隔一秒钟搞一次
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(1L);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 最终执行的任务方法
|
||||
*
|
||||
* @param jobId 任务id
|
||||
*/
|
||||
public abstract void invoke(String jobId);
|
||||
|
||||
|
||||
/**
|
||||
* 要实现延时队列的名字
|
||||
*/
|
||||
public abstract String setDelayQueueName();
|
||||
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
new Thread(this::startDelayQueueMachine).start();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package cn.lili.common.delayqueue;
|
||||
|
||||
/**
|
||||
* 延时任务工具类
|
||||
*
|
||||
* @author paulG
|
||||
* @since 2021/5/7
|
||||
**/
|
||||
public class DelayQueueTools {
|
||||
|
||||
/**
|
||||
* 组装延时任务唯一键
|
||||
*
|
||||
* @param type 延时任务类型
|
||||
* @param id id
|
||||
* @return 唯一键
|
||||
*/
|
||||
public static String wrapperUniqueKey(DelayQueueType type, String id) {
|
||||
return "{TIME_TRIGGER_" + type.name() + "}_" + id;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package cn.lili.common.delayqueue;
|
||||
|
||||
/**
|
||||
* 延时任务类型
|
||||
*
|
||||
* @author paulG
|
||||
* @since 2021/5/7
|
||||
**/
|
||||
public enum DelayQueueType {
|
||||
|
||||
/**
|
||||
* 促销活动
|
||||
*/
|
||||
PROMOTION("促销活动"),
|
||||
/**
|
||||
* 拼团订单
|
||||
*/
|
||||
PINTUAN_ORDER("拼团订单");
|
||||
|
||||
private final String description;
|
||||
|
||||
DelayQueueType(String des) {
|
||||
this.description = des;
|
||||
}
|
||||
|
||||
public String description() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package cn.lili.common.delayqueue;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 拼团订单延时任务信息
|
||||
*
|
||||
* @author paulG
|
||||
* @since 2021/5/7
|
||||
**/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class PintuanOrderMessage {
|
||||
|
||||
/**
|
||||
* 拼团活动id
|
||||
*/
|
||||
private String pintuanId;
|
||||
|
||||
/**
|
||||
* 父拼团订单sn
|
||||
*/
|
||||
private String orderSn;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package cn.lili.common.delayqueue;
|
||||
|
||||
import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 信息队列传输促销信息实体
|
||||
*
|
||||
* @author paulG
|
||||
* @date 2020/10/30
|
||||
**/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class PromotionMessage {
|
||||
|
||||
/**
|
||||
* 促销id
|
||||
*/
|
||||
private String promotionId;
|
||||
/**
|
||||
* 促销类型
|
||||
*/
|
||||
private String promotionType;
|
||||
|
||||
/**
|
||||
* 促销状态
|
||||
*/
|
||||
private String promotionStatus;
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private Date startTime;
|
||||
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
private Date endTime;
|
||||
|
||||
public <T> UpdateWrapper<T> updateWrapper() {
|
||||
UpdateWrapper<T> updateWrapper = new UpdateWrapper<>();
|
||||
updateWrapper.eq("id", promotionId);
|
||||
updateWrapper.set("promotion_status", PromotionStatusEnum.valueOf(promotionStatus));
|
||||
return updateWrapper;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package cn.lili.common.elasticsearch;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.lili.config.elasticsearch.ElasticsearchProperties;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
|
||||
import org.elasticsearch.action.delete.DeleteRequest;
|
||||
import org.elasticsearch.action.index.IndexRequest;
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.update.UpdateRequest;
|
||||
import org.elasticsearch.client.HttpAsyncResponseConsumerFactory;
|
||||
import org.elasticsearch.client.RequestOptions;
|
||||
import org.elasticsearch.client.RestHighLevelClient;
|
||||
import org.elasticsearch.client.indices.CreateIndexRequest;
|
||||
import org.elasticsearch.client.indices.CreateIndexResponse;
|
||||
import org.elasticsearch.client.indices.GetIndexRequest;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author paulG
|
||||
* @since 2020/10/14
|
||||
**/
|
||||
@Slf4j
|
||||
public abstract class BaseElasticsearchService {
|
||||
|
||||
protected static final RequestOptions COMMON_OPTIONS;
|
||||
|
||||
static {
|
||||
RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
|
||||
|
||||
// 默认缓冲限制为100MB,此处修改为30MB。
|
||||
builder.setHttpAsyncResponseConsumerFactory(new HttpAsyncResponseConsumerFactory.HeapBufferedResponseConsumerFactory(30 * 1024 * 1024));
|
||||
COMMON_OPTIONS = builder.build();
|
||||
}
|
||||
|
||||
@Autowired
|
||||
@Qualifier("elasticsearchClient")
|
||||
protected RestHighLevelClient client;
|
||||
|
||||
@Autowired
|
||||
private ElasticsearchProperties elasticsearchProperties;
|
||||
|
||||
/**
|
||||
* build DeleteIndexRequest
|
||||
*
|
||||
* @param index elasticsearch index name
|
||||
* @author fxbin
|
||||
*/
|
||||
private static DeleteIndexRequest buildDeleteIndexRequest(String index) {
|
||||
return new DeleteIndexRequest(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* build IndexRequest
|
||||
*
|
||||
* @param index elasticsearch index name
|
||||
* @param id request object id
|
||||
* @param object request object
|
||||
* @return {@link IndexRequest}
|
||||
* @author fxbin
|
||||
*/
|
||||
protected static IndexRequest buildIndexRequest(String index, String id, Object object) {
|
||||
return new IndexRequest(index).id(id).source(BeanUtil.beanToMap(object), XContentType.JSON);
|
||||
}
|
||||
|
||||
/**
|
||||
* create elasticsearch index (asyc)
|
||||
*
|
||||
* @param index elasticsearch index
|
||||
* @author fxbin
|
||||
*/
|
||||
protected void createIndexRequest(String index) {
|
||||
try {
|
||||
CreateIndexRequest request = new CreateIndexRequest(index);
|
||||
// Settings for this index
|
||||
request.settings(Settings.builder().put("index.number_of_shards", elasticsearchProperties.getIndex().getNumberOfShards()).put("index.number_of_replicas", elasticsearchProperties.getIndex().getNumberOfReplicas()));
|
||||
|
||||
CreateIndexResponse createIndexResponse = client.indices().create(request, COMMON_OPTIONS);
|
||||
|
||||
log.info(" whether all of the nodes have acknowledged the request : {}", createIndexResponse.isAcknowledged());
|
||||
log.info(" Indicates whether the requisite number of shard copies were started for each shard in the index before timing out :{}", createIndexResponse.isShardsAcknowledged());
|
||||
} catch (Exception e) {
|
||||
throw new ElasticsearchException("创建索引 {" + index + "} 失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Description: 判断某个index是否存在
|
||||
*
|
||||
* @param index index名
|
||||
* @return boolean
|
||||
* @author fanxb
|
||||
* @date 2019/7/24 14:57
|
||||
*/
|
||||
public boolean indexExist(String index) {
|
||||
try {
|
||||
GetIndexRequest request = new GetIndexRequest(index);
|
||||
request.local(false);
|
||||
request.humanReadable(true);
|
||||
request.includeDefaults(false);
|
||||
return client.indices().exists(request, RequestOptions.DEFAULT);
|
||||
} catch (Exception e) {
|
||||
throw new ElasticsearchException("获取索引 {" + index + "} 是否存在失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* delete elasticsearch index
|
||||
*
|
||||
* @param index elasticsearch index name
|
||||
* @author fxbin
|
||||
*/
|
||||
protected void deleteIndexRequest(String index) {
|
||||
DeleteIndexRequest deleteIndexRequest = buildDeleteIndexRequest(index);
|
||||
try {
|
||||
client.indices().delete(deleteIndexRequest, COMMON_OPTIONS);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("删除索引 {" + index + "} 失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* exec updateRequest
|
||||
*
|
||||
* @param index elasticsearch index name
|
||||
* @param id Document id
|
||||
* @param object request object
|
||||
* @author fxbin
|
||||
*/
|
||||
protected void updateRequest(String index, String id, Object object) {
|
||||
try {
|
||||
UpdateRequest updateRequest = new UpdateRequest(index, id).doc(BeanUtil.beanToMap(object), XContentType.JSON);
|
||||
client.update(updateRequest, COMMON_OPTIONS);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("更新索引 {" + index + "} 数据 {" + object + "} 失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* exec deleteRequest
|
||||
*
|
||||
* @param index elasticsearch index name
|
||||
* @param id Document id
|
||||
* @author fxbin
|
||||
*/
|
||||
protected void deleteRequest(String index, String id) {
|
||||
try {
|
||||
DeleteRequest deleteRequest = new DeleteRequest(index, id);
|
||||
client.delete(deleteRequest, COMMON_OPTIONS);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("删除索引 {" + index + "} 数据id {" + id + "} 失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* search all
|
||||
*
|
||||
* @param index elasticsearch index name
|
||||
* @return {@link SearchResponse}
|
||||
* @author fxbin
|
||||
*/
|
||||
protected SearchResponse search(String index) {
|
||||
SearchRequest searchRequest = new SearchRequest(index);
|
||||
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
|
||||
searchSourceBuilder.query(QueryBuilders.matchAllQuery());
|
||||
searchRequest.source(searchSourceBuilder);
|
||||
SearchResponse searchResponse = null;
|
||||
try {
|
||||
searchResponse = client.search(searchRequest, COMMON_OPTIONS);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return searchResponse;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package cn.lili.common.elasticsearch;
|
||||
|
||||
/**
|
||||
* elasticsearch 索引后缀
|
||||
*
|
||||
* @author paulG
|
||||
* @since 2020/10/13
|
||||
**/
|
||||
public class EsSuffix {
|
||||
|
||||
/**
|
||||
* 商品索引后缀
|
||||
*/
|
||||
public static final String GOODS_INDEX_NAME = "goods";
|
||||
|
||||
}
|
||||
166
framework/src/main/java/cn/lili/common/enums/MessageCode.java
Normal file
166
framework/src/main/java/cn/lili/common/enums/MessageCode.java
Normal file
@@ -0,0 +1,166 @@
|
||||
package cn.lili.common.enums;
|
||||
|
||||
/**
|
||||
* @author Chopper
|
||||
* @version v4.1
|
||||
* @Description: 错误码 5位
|
||||
* 第一位 1:商品;2:会员;3:订单,4:店铺,5:页面,6:其他
|
||||
* @since 2020/11/17 4:44 下午
|
||||
*/
|
||||
public enum MessageCode {
|
||||
|
||||
E401("认证过期"),
|
||||
|
||||
E10001("商品ID不存在"),
|
||||
E10002("商品名称不正确,名称应为2-50字符"),
|
||||
E10003("此规格已绑定分类不允许删除"),
|
||||
E10004("该分类名称已存在"),
|
||||
E10005("父分类不存在"),
|
||||
E10006("最多为三级分类,添加失败"),
|
||||
E10007("此类别下存在子类别不能删除"),
|
||||
E10008("此类别下存在商品不能删除"),
|
||||
E10009("通过categoryId获取分类失败"),
|
||||
E10010("不能重复添加分销商品"),
|
||||
E10011("无法将此商品放入回收站"),
|
||||
E10012("无法彻底删除从此商品"),
|
||||
E10013("无法下架此商品"),
|
||||
E10014("无法还原此商品"),
|
||||
|
||||
|
||||
E20001("用户名密码不能为空"),
|
||||
E20002("该用户名已被注册"),
|
||||
E20003("该手机号已被注册"),
|
||||
E20004("用户不存在"),
|
||||
E20005("修改资料成功"),
|
||||
E20006("修改资料失败"),
|
||||
E20007("手机号不存在"),
|
||||
E20008("重置密码成功"),
|
||||
E20009("修改手机号成功"),
|
||||
E20010("旧密码不正确"),
|
||||
E20011("修改密码成功"),
|
||||
E20012("用户未登录"),
|
||||
E20013("发布评价成功"),
|
||||
E20014("领取优惠券成功"),
|
||||
E20015("分销商不存在"),
|
||||
E20016("分销商已申请,无需重复提交"),
|
||||
E20017("密码错误"),
|
||||
E20018("分销商清退失败"),
|
||||
E20019("审核分销商失败"),
|
||||
E20020("用户名或密码错误"),
|
||||
E20021("订单状态不允许申请售后,请联系平台或商家"),
|
||||
E20022("无权操作此数据!"),
|
||||
E20023("此用户已禁用!"),
|
||||
E20024("未对当前用户做手机号码校验!"),
|
||||
E20025("可提现金额不足!"),
|
||||
E20026("零钱提现失败!"),
|
||||
E20027("请填写审核备注!"),
|
||||
E20028("请勿重复签到"),
|
||||
E20029("分销商不存在或已被清退"),
|
||||
E20030("分销商提现记录不存在"),
|
||||
E20031("分销商提现状态不正确,无法提现"),
|
||||
E20032("余额账户不存在,无法操作"),
|
||||
E20033("验证码错误,请重新输入"),
|
||||
E20034("最多可以设置10个角色"),
|
||||
E20035("订单已支付,请勿反复支付"),
|
||||
E20036("该订单已部分支付,请前往订单中心进行支付"),
|
||||
E20037("余额充值已支付成功,请勿重复支付"),
|
||||
E20038("余额不足以支付订单,请充值"),
|
||||
E20039("支付业务异常,请稍后重试"),
|
||||
E20040("当前订单不需要付款,请返回订单列表重新操作"),
|
||||
E20041("非当前用户的手机号"),
|
||||
E20101("联合第三方登录,未绑定会员"),
|
||||
E20102("联合第三方登录,授权信息错误"),
|
||||
E20103("会员发票信息重复"),
|
||||
E20104("会员发票信息不存在"),
|
||||
E20105("权限不足"),
|
||||
|
||||
E30001("购物车添加成功"),
|
||||
E30002("购物车数量更新成功"),
|
||||
E30003("购物车选中更新成功"),
|
||||
E30004("清空购物车成功"),
|
||||
E30005("删除购物车中的一个或多个产品成功"),
|
||||
E30006("读取结算页的购物车异常"),
|
||||
E30007("创建订单异常,请稍后重试"),
|
||||
E30008("订单状态错误,无法进行确认收货"),
|
||||
E30009("订单确定收货成功"),
|
||||
E30010("订单不存在"),
|
||||
E30011("已支付的订单不能修改金额"),
|
||||
E30013("物流错误"),
|
||||
E30014("当前订单不能发货"),
|
||||
E30015("非当前会员的订单"),
|
||||
E30016("无法重复提交评价"),
|
||||
E30017("付款金额和应付金额不一致"),
|
||||
E30018("售后已审核,无法重复操作"),
|
||||
E30019("物流公司错误,请重新选择"),
|
||||
E30020("请选择要投诉的商品"),
|
||||
E30021("当前订单无法核销"),
|
||||
E30022("当前售后单无法取消"),
|
||||
E30023("当前订单未支付,不能申请售后"),
|
||||
E30024("订单已支付,不能再次进行支付"),
|
||||
E30025("收银台信息获取错误"),
|
||||
E30026("积分不足,不能兑换"),
|
||||
E30027("优惠券已使用/已过期,不能使用"),
|
||||
E30028("投诉异常,请稍后重试"),
|
||||
|
||||
E40001("非当前店铺数据,无法操作"),
|
||||
E40002("此店铺不存在"),
|
||||
E40003("店铺名称已存在!"),
|
||||
E40004("参数有误!"),
|
||||
E40005("已有店铺,无需重复申请"),
|
||||
E40006("只有已出账结算单可以核对"),
|
||||
E40007("只有已核对结算单可以审核"),
|
||||
E40008("只有已审核结算单可以支付"),
|
||||
E40009("只有商家可以操作结算单核对"),
|
||||
E40010("只有管理可以操作结算单核对"),
|
||||
E40011("存在不属于此店铺的商品"),
|
||||
|
||||
E50001("该分类名称已存在"),
|
||||
E50002("父分类不存在"),
|
||||
E50003("最多为三级分类,添加失败"),
|
||||
E50004("最多为三级分类,修改失败"),
|
||||
E50005("文章分类名称重复,修改失败"),
|
||||
E50006("该文章分类下存在子分类,不能删除"),
|
||||
E50007("专题自动发布,无需操作发布"),
|
||||
E50008("当前页面为开启状态,无法删除"),
|
||||
E50009("当前页面为唯一页面,无法删除"),
|
||||
E50010("页面已发布,无需重复提交"),
|
||||
E50011("需传入查询ID"),
|
||||
E50012("当前消息模板不存在"),
|
||||
E50013("短信签名已存在"),
|
||||
E50014("该文章分类下存在文章,不能删除"),
|
||||
|
||||
E60000("请先登录再访问此接口"),
|
||||
E60001("操作失败"),
|
||||
E60002("非法请求"),
|
||||
E60003("请求正在处理,请稍后再试"),
|
||||
E60004("验证码为空或已过期,请重新获取"),
|
||||
E60005("验证失败"),
|
||||
E60007("OSS未配置"),
|
||||
E60008("支付暂不支持"),
|
||||
E60009("错误的客户端"),
|
||||
|
||||
E60010("角色已绑定部门,请逐个删除"),
|
||||
E60011("角色已绑定管理员,请逐个删除"),
|
||||
E60012("菜单已绑定角色,请先删除或编辑角色"),
|
||||
E60013("部门已经绑定管理员,请先删除或编辑管理员"),
|
||||
|
||||
S21001("分销商清退成功"),
|
||||
S21002("审核分销商成功"),
|
||||
|
||||
S44000("操作成功"),
|
||||
S50001("删除文章分类成功");
|
||||
|
||||
private final String value;
|
||||
|
||||
MessageCode(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Integer getCode() {
|
||||
return Integer.parseInt(this.name().replace("E", "").replace("S", ""));
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
284
framework/src/main/java/cn/lili/common/enums/ResultCode.java
Normal file
284
framework/src/main/java/cn/lili/common/enums/ResultCode.java
Normal file
@@ -0,0 +1,284 @@
|
||||
package cn.lili.common.enums;
|
||||
|
||||
/**
|
||||
* 返回状态码
|
||||
* 第一位 1:商品;2:用户;3:交易,4:促销,5:店铺,6:页面,7:设置,8:其他
|
||||
* @author Chopper
|
||||
* @date 2020/4/8 1:36 下午
|
||||
*/
|
||||
public enum ResultCode {
|
||||
|
||||
/**
|
||||
* 成功状态码
|
||||
*/
|
||||
SUCCESS(200, "成功"),
|
||||
|
||||
/**
|
||||
* 失败返回码
|
||||
*/
|
||||
ERROR(400,"失败"),
|
||||
/**
|
||||
* 失败返回码
|
||||
*/
|
||||
DEMO_SITE_EXCEPTION(400,"演示站点禁止使用"),
|
||||
|
||||
/**
|
||||
* 分类
|
||||
*/
|
||||
CATEGORY_NOT_EXIST(10001,"商品分类不存在"),
|
||||
CATEGORY_NAME_IS_EXIST(10002,"该分类名称已存在"),
|
||||
CATEGORY_PARENT_NOT_EXIST(10003,"该分类名称已存在"),
|
||||
CATEGORY_BEYOND_THREE(10004,"最多为三级分类,添加失败"),
|
||||
CATEGORY_HAS_CHILDREN(10005,"此类别下存在子类别不能删除"),
|
||||
CATEGORY_HAS_GOODS(10006,"此类别下存在商品不能删除"),
|
||||
CATEGORY_SAVE_ERROR(10007,"此类别下存在商品不能删除"),
|
||||
CATEGORY_PARAMETER_SAVE_ERROR(10008,"分类绑定参数组添加失败"),
|
||||
CATEGORY_PARAMETER_UPDATE_ERROR(10009,"分类绑定参数组添加失败"),
|
||||
|
||||
/**
|
||||
* 商品
|
||||
*/
|
||||
GOODS_NOT_EXIST(11001,"商品已下架"),
|
||||
GOODS_NAME_ERROR(11002,"商品名称不正确,名称应为2-50字符"),
|
||||
GOODS_UNDER_ERROR(11003,"商品下架失败"),
|
||||
GOODS_UPPER_ERROR(11004,"商品上架失败"),
|
||||
GOODS_AUTH_ERROR(11005,"商品审核失败"),
|
||||
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
PARAMETER_SAVE_ERROR(12001,"参数添加失败"),
|
||||
PARAMETER_UPDATE_ERROR(12002,"参数编辑失败"),
|
||||
|
||||
/**
|
||||
* 规格
|
||||
*/
|
||||
SPEC_SAVE_ERROR(13001,"规格修改失败"),
|
||||
SPEC_UPDATE_ERROR(13002,"规格修改失败"),
|
||||
SPEC_DELETE_ERROR(13003,"此规格已绑定分类不允许删除"),
|
||||
|
||||
/**
|
||||
* 品牌
|
||||
*/
|
||||
BRAND_SAVE_ERROR(14001,"品牌添加失败"),
|
||||
BRAND_UPDATE_ERROR(14002,"品牌修改失败"),
|
||||
BRAND_DISABLE_ERROR(14003,"品牌禁用失败"),
|
||||
BRAND_DELETE_ERROR(14004,"品牌删除失败"),
|
||||
|
||||
/**
|
||||
* 用户
|
||||
*/
|
||||
USER_EDIT_SUCCESS(20001,"用户修改成功"),
|
||||
USER_NOT_EXIST(20002, "用户不存在"),
|
||||
USER_NOT_LOGIN(20003, "用户未登录"),
|
||||
USER_AUTH_EXPIRED(20004, "认证过期"),
|
||||
USER_AUTHORITY_ERROR(20005,"权限不足"),
|
||||
USER_CONNECT_LOGIN_ERROR(20006,"未找到登录信息"),
|
||||
USER_NAME_EXIST(20007, "该用户名已被注册"),
|
||||
USER_PHONE_EXIST(20008, "该手机号已被注册"),
|
||||
USER_PHONE_NOT_EXIST(20009, "手机号不存在"),
|
||||
USER_PASSWORD_ERROR(20010, "密码不正确"),
|
||||
USER_NOT_PHONE(20011,"非当前用户的手机号"),
|
||||
USER_CONNECT_ERROR(20012,"联合第三方登录,授权信息错误"),
|
||||
USER_RECEIPT_REPEAT_ERROR(20013,"会员发票信息重复"),
|
||||
USER_RECEIPT_NOT_EXIST(20014,"会员发票信息不存在"),
|
||||
USER_EDIT_ERROR(20015,"用户修改失败"),
|
||||
USER_OLD_PASSWORD_ERROR(20016, "旧密码不正确"),
|
||||
USER_COLLECTION_EXIST(2001,"无法重复收藏"),
|
||||
|
||||
/**
|
||||
* 权限
|
||||
*/
|
||||
PERMISSION_DEPARTMENT_ROLE_ERROR(21001,"角色已绑定部门,请逐个删除"),
|
||||
PERMISSION_USER_ROLE_ERROR(21002,"角色已绑定管理员,请逐个删除"),
|
||||
PERMISSION_MENU_ROLE_ERROR(21003,"菜单已绑定角色,请先删除或编辑角色"),
|
||||
PERMISSION_DEPARTMENT_DELETE_ERROR(21004,"部门已经绑定管理员,请先删除或编辑管理员"),
|
||||
PERMISSION_BEYOND_TEN(21005,"最多可以设置10个角色"),
|
||||
|
||||
/**
|
||||
* 分销
|
||||
*/
|
||||
DISTRIBUTION_CLOSE(22000, "分销功能关闭"),
|
||||
DISTRIBUTION_NOT_EXIST(22001, "分销员不存在"),
|
||||
DISTRIBUTION_IS_APPLY(22002, "分销员已申请,无需重复提交"),
|
||||
DISTRIBUTION_AUDIT_ERROR(22003, "审核分销员失败"),
|
||||
DISTRIBUTION_RETREAT_ERROR(22004, "分销员清退失败"),
|
||||
DISTRIBUTION_CASH_NOT_EXIST(22005, "分销员提现记录不存在"),
|
||||
DISTRIBUTION_GOODS_DOUBLE(22006, "不能重复添加分销商品"),
|
||||
|
||||
/**
|
||||
* 购物车
|
||||
*/
|
||||
CART_ERROR(30001, "读取结算页的购物车异常"),
|
||||
|
||||
SHIPPING_NOT_APPLY(30005, "当前选择地址暂不支持配送!"),
|
||||
/**
|
||||
* 订单
|
||||
*/
|
||||
ORDER_ERROR(31001, "创建订单异常,请稍后重试"),
|
||||
ORDER_NOT_EXIST(31002, "订单不存在"),
|
||||
ORDER_DELIVERED_ERROR(31003, "订单状态错误,无法进行确认收货"),
|
||||
ORDER_UPDATE_PRICE_ERROR(31004, "已支付的订单不能修改金额"),
|
||||
ORDER_LOGISTICS_ERROR(31005, "物流错误"),
|
||||
ORDER_DELIVER_ERROR(31006, "物流错误"),
|
||||
ORDER_NOT_USER(31007, "非当前会员的订单"),
|
||||
ORDER_TAKE_ERROR(31008,"当前订单无法核销"),
|
||||
|
||||
/**
|
||||
* 支付
|
||||
*/
|
||||
PAY_SUCCESS(32001,"支付成功"),
|
||||
PAY_INCONSISTENT_ERROR(32002,"付款金额和应付金额不一致"),
|
||||
PAY_DOUBLE_ERROR(32003,"订单已支付,不能再次进行支付"),
|
||||
PAY_CASHIER_ERROR(32004,"收银台信息获取错误"),
|
||||
PAY_ERROR(32005,"支付业务异常,请稍后重试"),
|
||||
PAY_BAN(32006,"当前订单不需要付款,请返回订单列表重新操作"),
|
||||
PAY_PARTIAL_ERROR(32007,"该订单已部分支付,请前往订单中心进行支付"),
|
||||
PAY_NOT_SUPPORT(32008,"支付暂不支持"),
|
||||
PAY_CLIENT_TYPE_ERROR(32009,"错误的客户端"),
|
||||
PAY_POINT_ENOUGH(32010,"积分不足,不能兑换"),
|
||||
PAY_NOT_EXIST_ORDER(32011,"支付订单不存在"),
|
||||
|
||||
/**
|
||||
* 售后
|
||||
*/
|
||||
AFTER_SALES_NOT_PAY_ERROR(33001,"当前订单未支付,不能申请售后"),
|
||||
AFTER_SALES_CANCEL_ERROR(33002,"当前售后单无法取消"),
|
||||
AFTER_SALES_BAN(33003,"订单状态不允许申请售后,请联系平台或商家"),
|
||||
AFTER_SALES_DOUBLE_ERROR(33004,"售后已审核,无法重复操作"),
|
||||
AFTER_SALES_LOGISTICS_ERROR(33005,"物流公司错误,请重新选择"),
|
||||
|
||||
/**
|
||||
* 投诉
|
||||
*/
|
||||
COMPLAINT_ORDER_ITEM_EMPTY_ERROR(33100,"订单不存在"),
|
||||
COMPLAINT_SKU_EMPTY_ERROR(33101,"商品已下架,如需投诉请联系平台客服"),
|
||||
COMPLAINT_ERROR(33102,"投诉异常,请稍后重试"),
|
||||
|
||||
/**
|
||||
* 余额
|
||||
*/
|
||||
WALLET_INSUFFICIENT(34001,"余额不足以支付订单,请充值!"),
|
||||
WALLET_WITHDRAWAL_INSUFFICIENT(34002,"可提现金额不足!"),
|
||||
WALLET_ERROR_INSUFFICIENT(34003,"零钱提现失败!"),
|
||||
WALLET_REMARK_ERROR(34004,"请填写审核备注!"),
|
||||
|
||||
/**
|
||||
* 评价
|
||||
*/
|
||||
EVALUATION_DOUBLE_ERROR(35001,"无法重复提交评价"),
|
||||
|
||||
/**
|
||||
* 签到
|
||||
*/
|
||||
MEMBER_SIGN_REPEAT(40001,"请勿重复签到"),
|
||||
|
||||
/**
|
||||
* 优惠券
|
||||
*/
|
||||
COUPON_EDIT_STATUS_SUCCESS(41001,"修改状态成功!"),
|
||||
COUPON_CANCELLATION_SUCCESS(41002,"会员优惠券作废成功"),
|
||||
COUPON_EXPIRED(41003,"优惠券已使用/已过期,不能使用"),
|
||||
COUPON_EDIT_STATUS_ERROR(41004,"优惠券修改状态失败!"),
|
||||
|
||||
/**
|
||||
* 拼团
|
||||
*/
|
||||
PINTUAN_MANUAL_OPEN_SUCCESS(42001,"手动开启拼团活动成功"),
|
||||
PINTUAN_MANUAL_CLOSE_SUCCESS(42002,"手动关闭拼团活动成功"),
|
||||
PINTUAN_ADD_SUCCESS(42003,"添加拼团活动成功"),
|
||||
PINTUAN_EDIT_SUCCESS(42004,"修改拼团活动成功"),
|
||||
PINTUAN_DELETE_SUCCESS(42005,"删除拼团活动成功"),
|
||||
PINTUAN_MANUAL_OPEN_ERROR(42006,"手动开启拼团活动失败"),
|
||||
PINTUAN_MANUAL_CLOSE_ERROR(42007,"手动关闭拼团活动失败"),
|
||||
PINTUAN_ADD_ERROR(42008,"添加拼团活动失败"),
|
||||
PINTUAN_EDIT_ERROR(42009,"修改拼团活动失败"),
|
||||
PINTUAN_DELETE_ERROR(42010,"删除拼团活动失败"),
|
||||
|
||||
/**
|
||||
* 满额活动
|
||||
*/
|
||||
FULL_DISCOUNT_EDIT_SUCCESS(43001,"修改满优惠活动成功"),
|
||||
FULL_DISCOUNT_EDIT_DELETE(43002,"删除满优惠活动成功"),
|
||||
|
||||
/**
|
||||
* 店铺
|
||||
*/
|
||||
STORE_NOT_EXIST(50001,"此店铺不存在"),
|
||||
STORE_NAME_EXIST_ERROR(50002,"店铺名称已存在!"),
|
||||
STORE_APPLY_DOUBLE_ERROR(50003,"已有店铺,无需重复申请!"),
|
||||
|
||||
/**
|
||||
* 结算单
|
||||
*/
|
||||
BILL_CHECK_ERROR(51001,"只有已出账结算单可以核对"),
|
||||
BILL_COMPLETE_ERROR(51002,"只有已审核结算单可以支付"),
|
||||
|
||||
/**
|
||||
* 文章
|
||||
*/
|
||||
ARTICLE_CATEGORY_NAME_EXIST(60001,"文章分类名称已存在"),
|
||||
ARTICLE_CATEGORY_PARENT_NOT_EXIST(60002,"文章分类父分类不存在"),
|
||||
ARTICLE_CATEGORY_BEYOND_TWO(60003,"最多为二级分类,操作失败"),
|
||||
ARTICLE_CATEGORY_DELETE_ERROR(60004,"该文章分类下存在子分类,不能删除"),
|
||||
ARTICLE_CATEGORY_HAS_ARTICLE(60005,"该文章分类下存在文章,不能删除"),
|
||||
ARTICLE_CATEGORY_NO_DELETION(60007,"默认文章分类不能进行删除"),
|
||||
ARTICLE_NO_DELETION(60008,"默认文章不能进行删除"),
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 页面
|
||||
*/
|
||||
PAGE_NOT_EXIST(61001,"页面不存在"),
|
||||
PAGE_OPEN_DELETE_ERROR(61002,"当前页面为开启状态,无法删除"),
|
||||
PAGE_DELETE_ERROR(61003,"当前页面为唯一页面,无法删除"),
|
||||
PAGE_RELEASE_ERROR(61004,"页面已发布,无需重复提交"),
|
||||
|
||||
/**
|
||||
* 设置
|
||||
*/
|
||||
SETTING_NOT_TO_SET(70001,"该参数不需要设置"),
|
||||
|
||||
/**
|
||||
* 短信
|
||||
*/
|
||||
SMS_SIGN_EXIST_ERROR(80001,"短信签名已存在"),
|
||||
|
||||
/**
|
||||
* 站内信
|
||||
*/
|
||||
NOTICE_NOT_EXIST(80101,"当前消息模板不存在"),
|
||||
|
||||
/**
|
||||
* OSS
|
||||
*/
|
||||
OSS_NOT_EXIST(80201,"OSS未配置"),
|
||||
|
||||
/**
|
||||
* 验证码
|
||||
*/
|
||||
VERIFICATION_SEND_SUCCESS(80301,"短信验证码,发送成功"),
|
||||
VERIFICATION_ERROR(80302,"验证失败"),
|
||||
VERIFICATION_SMS_ERROR(80303,"短信验证码错误,请重新校验"),
|
||||
VERIFICATION_SMS_EXPIRED_ERROR(80304,"验证码已失效,请重新校验")
|
||||
|
||||
;
|
||||
private final Integer code;
|
||||
private final String message;
|
||||
|
||||
|
||||
ResultCode(Integer code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Integer code() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
public String message() {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
}
|
||||
129
framework/src/main/java/cn/lili/common/enums/StoreLogType.java
Normal file
129
framework/src/main/java/cn/lili/common/enums/StoreLogType.java
Normal file
@@ -0,0 +1,129 @@
|
||||
package cn.lili.common.enums;
|
||||
|
||||
/**
|
||||
* 日志枚举
|
||||
*
|
||||
* @author Chopper
|
||||
*/
|
||||
public enum StoreLogType {
|
||||
|
||||
/**
|
||||
* 默认0操作
|
||||
*/
|
||||
OPERATION,
|
||||
|
||||
/**
|
||||
* 1登录
|
||||
*/
|
||||
LOGIN,
|
||||
|
||||
/**
|
||||
* 商品
|
||||
*/
|
||||
|
||||
//商品上架
|
||||
|
||||
//商品下架
|
||||
|
||||
//发布商品
|
||||
|
||||
//修改商品
|
||||
|
||||
//删除商品
|
||||
|
||||
//添加商品模板
|
||||
|
||||
//编辑商品库存
|
||||
|
||||
//增加店铺商品分类
|
||||
|
||||
//编辑店铺商品分类
|
||||
|
||||
/**
|
||||
* 订单
|
||||
*/
|
||||
|
||||
//发货
|
||||
|
||||
//修改收件信息
|
||||
|
||||
/**
|
||||
* 售后
|
||||
*/
|
||||
|
||||
//审核退货单
|
||||
|
||||
//确定收货
|
||||
|
||||
//拒绝收货
|
||||
|
||||
//退款
|
||||
|
||||
//拒绝退款
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 营销
|
||||
*/
|
||||
|
||||
//添加分销商品
|
||||
|
||||
//下架分销商品
|
||||
|
||||
//编辑分销商品
|
||||
|
||||
//创建满减活动
|
||||
|
||||
//编辑满减活动
|
||||
|
||||
//下架满减活动
|
||||
|
||||
//上架满减活动
|
||||
|
||||
//删除满减活动
|
||||
|
||||
//添加优惠券
|
||||
|
||||
//编辑优惠券
|
||||
|
||||
//关闭优惠券
|
||||
|
||||
//参与秒杀活动
|
||||
|
||||
//编辑秒杀活动
|
||||
|
||||
//下架秒杀活动
|
||||
|
||||
//上架秒杀活动
|
||||
|
||||
//删除秒杀活动
|
||||
|
||||
|
||||
/**
|
||||
* 设置
|
||||
*/
|
||||
|
||||
//选择物流公司
|
||||
|
||||
//取消物流公司
|
||||
|
||||
//添加运费模板
|
||||
|
||||
//编辑运费模板
|
||||
|
||||
//删除运费模板
|
||||
|
||||
//设置店铺信息
|
||||
|
||||
//修改店铺退货地址
|
||||
|
||||
//新增自提点
|
||||
|
||||
//编辑自提点
|
||||
|
||||
//删除自提点
|
||||
|
||||
//发布店铺首页
|
||||
|
||||
}
|
||||
25
framework/src/main/java/cn/lili/common/enums/SwitchEnum.java
Normal file
25
framework/src/main/java/cn/lili/common/enums/SwitchEnum.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package cn.lili.common.enums;
|
||||
|
||||
/**
|
||||
* 开关枚举
|
||||
* @author Chopper
|
||||
*/
|
||||
public enum SwitchEnum {
|
||||
|
||||
/**
|
||||
* 开关
|
||||
*/
|
||||
OPEN("开启"), CLOSE("关闭");
|
||||
|
||||
private String description;
|
||||
|
||||
SwitchEnum(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String description() {
|
||||
return description;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package cn.lili.common.exception;
|
||||
|
||||
import cn.lili.common.enums.ResultCode;
|
||||
import cn.lili.common.utils.ResultUtil;
|
||||
import cn.lili.common.vo.ResultMessage;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 全局异常异常处理
|
||||
*
|
||||
* @author Chopper
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
@Slf4j
|
||||
public class GlobalControllerExceptionHandler {
|
||||
|
||||
/**
|
||||
* 如果超过长度,则前后段交互体验不佳,使用默认错误消息
|
||||
*/
|
||||
static Integer MAX_LENGTH = 200;
|
||||
|
||||
/**
|
||||
* 自定义异常
|
||||
*
|
||||
* @param e
|
||||
* @return
|
||||
*/
|
||||
@ExceptionHandler(ServiceException.class)
|
||||
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
|
||||
public ResultMessage<Object> handleServiceException(HttpServletRequest request, final Exception e, HttpServletResponse response) {
|
||||
|
||||
log.error("全局异常[ServiceException]:", e);
|
||||
|
||||
//如果是自定义异常,则获取异常,返回自定义错误消息
|
||||
if (e instanceof ServiceException) {
|
||||
ResultCode resultCode=((ServiceException) e).getResultCode();
|
||||
if (resultCode != null) {
|
||||
return ResultUtil.error(resultCode.code(), resultCode.message());
|
||||
}
|
||||
}
|
||||
|
||||
//默认错误消息
|
||||
String errorMsg = "服务器异常,请稍后重试";
|
||||
if (e != null && e.getMessage() != null && e.getMessage().length() < MAX_LENGTH) {
|
||||
errorMsg = e.getMessage();
|
||||
}
|
||||
return ResultUtil.error(400, errorMsg);
|
||||
}
|
||||
|
||||
@ExceptionHandler(RuntimeException.class)
|
||||
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
|
||||
public ResultMessage<Object> runtimeExceptionHandler(HttpServletRequest request, final Exception e, HttpServletResponse response) {
|
||||
|
||||
log.error("全局异常[RuntimeException]:", e);
|
||||
|
||||
return ResultUtil.error(400, "服务器异常,请稍后重试");
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 通用的接口映射异常处理方
|
||||
// */
|
||||
// @Override
|
||||
// protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
|
||||
// if (ex instanceof MethodArgumentNotValidException) {
|
||||
// MethodArgumentNotValidException exception = (MethodArgumentNotValidException) ex;
|
||||
// return new ResponseEntity<>(new ResultUtil<>().setErrorMsg(exception.getBindingResult().getAllErrors().get(0).getDefaultMessage()), status);
|
||||
// }
|
||||
// if (ex instanceof MethodArgumentTypeMismatchException) {
|
||||
// MethodArgumentTypeMismatchException exception = (MethodArgumentTypeMismatchException) ex;
|
||||
// logger.error("参数转换失败,方法:" + exception.getParameter().getMethod().getName() + ",参数:" + exception.getName()
|
||||
// + ",信息:" + exception.getLocalizedMessage());
|
||||
// return new ResponseEntity<>(new ResultUtil<>().setErrorMsg("参数转换失败"), status);
|
||||
// }
|
||||
// ex.printStackTrace();
|
||||
// return new ResponseEntity<>(new ResultUtil<>().setErrorMsg("未知异常,请联系管理员"), status);
|
||||
// }
|
||||
|
||||
/**
|
||||
* bean校验未通过异常
|
||||
*
|
||||
* @see javax.validation.Valid
|
||||
* @see org.springframework.validation.Validator
|
||||
* @see org.springframework.validation.DataBinder
|
||||
*/
|
||||
@ExceptionHandler(BindException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
@ResponseBody
|
||||
public ResultMessage<Object> validExceptionHandler(HttpServletRequest request, final Exception e, HttpServletResponse response) {
|
||||
|
||||
BindException exception = (BindException) e;
|
||||
List<FieldError> fieldErrors = exception.getBindingResult().getFieldErrors();
|
||||
for (FieldError error : fieldErrors) {
|
||||
return ResultUtil.error(400,error.getDefaultMessage());
|
||||
}
|
||||
return ResultUtil.error(ResultCode.ERROR);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package cn.lili.common.exception;
|
||||
|
||||
import cn.lili.common.enums.ResultCode;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author Chopper
|
||||
*/
|
||||
@Data
|
||||
public class ServiceException extends RuntimeException {
|
||||
|
||||
private String msg;
|
||||
|
||||
private ResultCode resultCode;
|
||||
|
||||
public ServiceException(String msg) {
|
||||
super(msg);
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public ServiceException() {
|
||||
super("网络错误,请稍后重试!");
|
||||
this.msg = "网络错误,请稍后重试!";
|
||||
}
|
||||
|
||||
public ServiceException(ResultCode resultCode) {
|
||||
this.resultCode = resultCode;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package cn.lili.common.heath;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
|
||||
import org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator;
|
||||
import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 数据库检验工具
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v4.0
|
||||
* @Description:
|
||||
* @since 2020/12/24 19:31
|
||||
*/
|
||||
|
||||
@Configuration
|
||||
public class DataSourceHealthConfig extends DataSourceHealthContributorAutoConfiguration {
|
||||
|
||||
@Value("${spring.datasource.dbcp2.validation-query:select 1}")
|
||||
private String defaultQuery;
|
||||
|
||||
|
||||
public DataSourceHealthConfig(Map<String, DataSource> dataSources, ObjectProvider<DataSourcePoolMetadataProvider> metadataProviders) {
|
||||
super(dataSources, metadataProviders);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractHealthIndicator createIndicator(DataSource source) {
|
||||
DataSourceHealthIndicator indicator = (DataSourceHealthIndicator) super.createIndicator(source);
|
||||
if (!StringUtils.hasText(indicator.getQuery())) {
|
||||
indicator.setQuery(defaultQuery);
|
||||
}
|
||||
return indicator;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package cn.lili.common.rocketmq;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.rocketmq.client.producer.SendCallback;
|
||||
import org.apache.rocketmq.client.producer.SendResult;
|
||||
|
||||
/**
|
||||
* @author paulG
|
||||
* @since 2020/11/4
|
||||
**/
|
||||
@Slf4j
|
||||
public class RocketmqSendCallback implements SendCallback {
|
||||
|
||||
@Override
|
||||
public void onSuccess(SendResult sendResult) {
|
||||
log.info("async onSuccess SendResult={}", sendResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onException(Throwable throwable) {
|
||||
log.error("async onException Throwable", throwable);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package cn.lili.common.rocketmq;
|
||||
|
||||
/**
|
||||
* @author paulG
|
||||
* @since 2020/11/4
|
||||
**/
|
||||
public class RocketmqSendCallbackBuilder {
|
||||
|
||||
|
||||
public static RocketmqSendCallback commonCallback() {
|
||||
return new RocketmqSendCallback();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package cn.lili.common.rocketmq.tags;
|
||||
|
||||
/**
|
||||
* @author paulG
|
||||
* @since 2020/12/9
|
||||
**/
|
||||
public enum AfterSaleTagsEnum {
|
||||
|
||||
REFUND("售后退款"),
|
||||
AFTER_SALE_STATUS_CHANGE("售后单状态改变");
|
||||
|
||||
private final String description;
|
||||
|
||||
AfterSaleTagsEnum(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String description() {
|
||||
return description;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package cn.lili.common.rocketmq.tags;
|
||||
|
||||
/**
|
||||
* @author paulG
|
||||
* @since 2020/12/9
|
||||
**/
|
||||
public enum GoodsTagsEnum {
|
||||
|
||||
GENERATOR_GOODS_INDEX("生成商品索引"),
|
||||
GOODS_DELETE("删除商品"),
|
||||
GOODS_AUDIT("审核商品"),
|
||||
GOODS_COLLECTION("收藏商品"),
|
||||
BUY_GOODS_COMPLETE("购买商品完成"),
|
||||
SKU_DELETE("删除商品SKU"),
|
||||
VIEW_GOODS("查看商品"),
|
||||
GOODS_COMMENT_COMPLETE("商品评价");
|
||||
|
||||
private final String description;
|
||||
|
||||
GoodsTagsEnum(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String description() {
|
||||
return description;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package cn.lili.common.rocketmq.tags;
|
||||
|
||||
/**
|
||||
* @author paulG
|
||||
* @since 2020/12/9
|
||||
**/
|
||||
public enum MemberTagsEnum {
|
||||
|
||||
MEMBER_REGISTER("会员注册"),
|
||||
MEMBER_SING("会员签到"),
|
||||
MEMBER_WITHDRAWAL("会员提现"),
|
||||
MEMBER_POINT_CHANGE("会员积分变动");
|
||||
|
||||
private final String description;
|
||||
|
||||
MemberTagsEnum(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String description() {
|
||||
return description;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package cn.lili.common.rocketmq.tags;
|
||||
|
||||
/**
|
||||
* @author paulG
|
||||
* @since 2020/12/9
|
||||
**/
|
||||
public enum MqOrderTagsEnum {
|
||||
|
||||
|
||||
ORDER_CREATE("订单创建"),
|
||||
STATUS_CHANGE("订单状态改变");
|
||||
|
||||
|
||||
|
||||
|
||||
private final String description;
|
||||
|
||||
MqOrderTagsEnum(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String description() {
|
||||
return description;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package cn.lili.common.rocketmq.tags;
|
||||
|
||||
/**
|
||||
* @author paulG
|
||||
* @since 2020/12/9
|
||||
**/
|
||||
public enum OtherTagsEnum {
|
||||
|
||||
MESSAGE("站内消息提醒"),
|
||||
SMS("短信消息提醒");
|
||||
|
||||
private final String description;
|
||||
|
||||
OtherTagsEnum(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String description() {
|
||||
return description;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package cn.lili.common.security;
|
||||
|
||||
import cn.lili.common.security.enums.UserEnums;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author Chopper
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class AuthUser implements Serializable {
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
private String nickName;
|
||||
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 长期有效(用于手机app登录场景或者信任场景等)
|
||||
*/
|
||||
private Boolean longTerm = false;
|
||||
|
||||
/**
|
||||
* @see UserEnums
|
||||
* 角色
|
||||
*/
|
||||
private UserEnums role;
|
||||
|
||||
/**
|
||||
* 如果角色是商家,则存在此店铺id字段
|
||||
* storeId
|
||||
*/
|
||||
private String storeId;
|
||||
|
||||
/**
|
||||
* 如果角色是商家,则存在此店铺名称字段
|
||||
* storeName
|
||||
*/
|
||||
private String storeName;
|
||||
|
||||
/**
|
||||
* 是否是超级管理员
|
||||
*/
|
||||
private Boolean isSuper = false;
|
||||
|
||||
public AuthUser(String username, String id, String nickName, UserEnums role) {
|
||||
this.username = username;
|
||||
this.id = id;
|
||||
this.role = role;
|
||||
this.nickName = nickName;
|
||||
}
|
||||
|
||||
public AuthUser(String username, String id, UserEnums manager, String nickName, Boolean isSuper) {
|
||||
this.username = username;
|
||||
this.id = id;
|
||||
this.role = manager;
|
||||
this.isSuper = isSuper;
|
||||
this.nickName = nickName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package cn.lili.common.security;
|
||||
|
||||
|
||||
import cn.lili.common.utils.ResponseUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* 自定义 拒绝访问响应
|
||||
*
|
||||
* @author Chopper
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
|
||||
|
||||
@Override
|
||||
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) {
|
||||
ResponseUtil.output(response, ResponseUtil.resultMap(false, 401, "抱歉,您没有访问权限"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package cn.lili.common.security;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
|
||||
/**
|
||||
* SecurityBean
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v1.0
|
||||
* 2020-11-14 15:03
|
||||
*/
|
||||
@Configuration
|
||||
public class SecurityBean {
|
||||
|
||||
@Bean
|
||||
public BCryptPasswordEncoder passwordEncoder() {
|
||||
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
|
||||
return bCryptPasswordEncoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 定义跨域配置
|
||||
*
|
||||
* @return bean
|
||||
*/
|
||||
@Bean
|
||||
CorsConfigurationSource corsConfigurationSource() {
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
CorsConfiguration config = new CorsConfiguration();
|
||||
config.setAllowCredentials(true);
|
||||
config.addAllowedOrigin(CorsConfiguration.ALL);
|
||||
config.addAllowedHeader(CorsConfiguration.ALL);
|
||||
config.addAllowedMethod(CorsConfiguration.ALL);
|
||||
source.registerCorsConfiguration("/**", config);
|
||||
return source;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package cn.lili.common.security.context;
|
||||
|
||||
import cn.lili.common.security.AuthUser;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 获取用户信息 处理
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v4.0
|
||||
* @Description:
|
||||
* @since 2020/11/14 20:35
|
||||
*/
|
||||
@Component
|
||||
public class AuthenticationHandler {
|
||||
|
||||
/**
|
||||
* 获取当前用户信息
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public AuthUser getAuthUser() {
|
||||
//获取spring security 权限信息,如果token有权限,在这里就会得到内容
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (authentication == null) {
|
||||
return null;
|
||||
}
|
||||
Object object = authentication.getDetails();
|
||||
if (object instanceof AuthUser) {
|
||||
return (AuthUser) authentication.getDetails();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package cn.lili.common.security.context;
|
||||
|
||||
import cn.lili.common.cache.Cache;
|
||||
import cn.lili.common.enums.MessageCode;
|
||||
import cn.lili.common.enums.ResultCode;
|
||||
import cn.lili.common.exception.ServiceException;
|
||||
import cn.lili.common.security.AuthUser;
|
||||
import cn.lili.common.security.enums.SecurityEnum;
|
||||
import cn.lili.common.token.SecretKeyUtil;
|
||||
import com.google.gson.Gson;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
|
||||
/**
|
||||
* 用户上下文
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v4.0
|
||||
* @Description:
|
||||
* @since 2020/11/14 20:27
|
||||
*/
|
||||
public class UserContext {
|
||||
|
||||
private static AuthenticationHandler authenticationHandler;
|
||||
|
||||
public static void setHolder(AuthenticationHandler authenticationHandler) {
|
||||
UserContext.authenticationHandler = authenticationHandler;
|
||||
}
|
||||
|
||||
|
||||
public static AuthUser getCurrentUser() {
|
||||
return authenticationHandler.getAuthUser();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据jwt获取token重的用户信息
|
||||
*
|
||||
* @param cache 缓存
|
||||
* @param accessToken token
|
||||
* @return
|
||||
*/
|
||||
public static AuthUser getAuthUser(Cache cache, String accessToken) {
|
||||
try {
|
||||
if (cache.keys("*" + accessToken).size() == 0) {
|
||||
throw new ServiceException(ResultCode.USER_AUTHORITY_ERROR);
|
||||
}
|
||||
//获取token的信息
|
||||
Claims claims
|
||||
= Jwts.parser()
|
||||
.setSigningKey(SecretKeyUtil.generalKeyByDecoders())
|
||||
.parseClaimsJws(accessToken).getBody();
|
||||
//获取存储在claims中的用户信息
|
||||
String json = claims.get(SecurityEnum.USER_CONTEXT.getValue()).toString();
|
||||
return new Gson().fromJson(json, AuthUser.class);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package cn.lili.common.security.context;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 给予用户上下文,初始化参数
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v4.0
|
||||
* @Description:
|
||||
* @since 2020/11/14 20:30
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class UserContextInit implements ApplicationRunner {
|
||||
|
||||
/**
|
||||
* 用户信息holder,认证信息的获取者
|
||||
*/
|
||||
private final AuthenticationHandler authenticationHandler;
|
||||
|
||||
/**
|
||||
* 在项目加载时指定认证信息获取者
|
||||
* 默认是由spring 安全上下文中获取
|
||||
*
|
||||
* @param args
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
UserContext.setHolder(authenticationHandler);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package cn.lili.common.security.enums;
|
||||
|
||||
/**
|
||||
* 安全相关常量
|
||||
*
|
||||
* @author Chopper
|
||||
*/
|
||||
public enum SecurityEnum {
|
||||
|
||||
/**
|
||||
* 存在与header中的token参数头 名
|
||||
*/
|
||||
HEADER_TOKEN("accessToken"), USER_CONTEXT("userContext"), JWT_SECRET("secret");
|
||||
|
||||
String value;
|
||||
|
||||
SecurityEnum(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package cn.lili.common.security.enums;
|
||||
|
||||
/**
|
||||
* token角色类型
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v1.0
|
||||
* @Description:
|
||||
* @since 2020/8/18 15:23
|
||||
*/
|
||||
public enum UserEnums {
|
||||
/**
|
||||
* 角色
|
||||
*/
|
||||
MEMBER("会员"),
|
||||
STORE("商家"),
|
||||
MANAGER("管理员"),
|
||||
SYSTEM("系统");
|
||||
private final String role;
|
||||
|
||||
UserEnums(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
}
|
||||
76
framework/src/main/java/cn/lili/common/sms/AliSmsUtil.java
Normal file
76
framework/src/main/java/cn/lili/common/sms/AliSmsUtil.java
Normal file
@@ -0,0 +1,76 @@
|
||||
package cn.lili.common.sms;
|
||||
|
||||
import cn.lili.modules.message.entity.dos.SmsSign;
|
||||
import cn.lili.modules.message.entity.dos.SmsTemplate;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Chopper
|
||||
* @version v4.1
|
||||
* @Description:
|
||||
* @since 2021/2/1 6:05 下午
|
||||
*/
|
||||
public interface AliSmsUtil {
|
||||
/**
|
||||
* 申请短信签名
|
||||
*
|
||||
* @param smsSign 短信签名
|
||||
*/
|
||||
void addSmsSign(SmsSign smsSign) throws Exception;
|
||||
|
||||
|
||||
/**
|
||||
* 删除短信签名
|
||||
*
|
||||
* @param signName 签名名称
|
||||
*/
|
||||
void deleteSmsSign(String signName) throws Exception;
|
||||
|
||||
/**
|
||||
* 查询短信签名申请状态
|
||||
*
|
||||
* @param signName 签名名称
|
||||
*/
|
||||
Map<String, Object> querySmsSign(String signName) throws Exception;
|
||||
|
||||
/**
|
||||
* 修改未审核通过的短信签名,并重新提交审核。
|
||||
*
|
||||
* @param smsSign 短信签名
|
||||
*/
|
||||
void modifySmsSign(SmsSign smsSign) throws Exception;
|
||||
|
||||
/**
|
||||
* 修改未审核通过的短信模板,并重新提交审核。
|
||||
*
|
||||
* @param smsTemplate 短信模板
|
||||
* @throws Exception
|
||||
*/
|
||||
void modifySmsTemplate(SmsTemplate smsTemplate) throws Exception;
|
||||
|
||||
/**
|
||||
* 查看短信模板
|
||||
*
|
||||
* @param templateCode 短信模板CODE
|
||||
* @throws Exception
|
||||
*/
|
||||
Map<String, Object> querySmsTemplate(String templateCode) throws Exception;
|
||||
|
||||
/**
|
||||
* 申请短信模板
|
||||
*
|
||||
* @param smsTemplate 短信模板
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
String addSmsTemplate(SmsTemplate smsTemplate) throws Exception;
|
||||
|
||||
/**
|
||||
* 删除短信模板
|
||||
*
|
||||
* @param templateCode 短信模板CODE
|
||||
* @throws Exception
|
||||
*/
|
||||
void deleteSmsTemplate(String templateCode) throws Exception;
|
||||
}
|
||||
57
framework/src/main/java/cn/lili/common/sms/SmsUtil.java
Normal file
57
framework/src/main/java/cn/lili/common/sms/SmsUtil.java
Normal file
@@ -0,0 +1,57 @@
|
||||
package cn.lili.common.sms;
|
||||
|
||||
import cn.lili.common.verification.enums.VerificationEnums;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 短信接口
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v4.0
|
||||
* @Description:
|
||||
* @since 2020/11/30 15:44
|
||||
*/
|
||||
public interface SmsUtil {
|
||||
|
||||
|
||||
/**
|
||||
* 验证码发送
|
||||
*
|
||||
* @param mobile 手机号
|
||||
* @param verificationEnums 验证码场景
|
||||
* @param uuid 用户标识uuid
|
||||
*/
|
||||
void sendSmsCode(String mobile, VerificationEnums verificationEnums, String uuid);
|
||||
|
||||
/**
|
||||
* 验证码验证
|
||||
*
|
||||
* @param mobile 手机号
|
||||
* @param verificationEnums 验证码场景
|
||||
* @param uuid 用户标识uuid
|
||||
* @param code 待验证code
|
||||
*/
|
||||
boolean verifyCode(String mobile, VerificationEnums verificationEnums, String uuid, String code);
|
||||
|
||||
/**
|
||||
* 短信发送
|
||||
*
|
||||
* @param mobile 接收手机号
|
||||
* @param param 参数
|
||||
* @param templateCode 模版code
|
||||
*/
|
||||
void sendSmsCode(String signName, String mobile, Map<String, String> param, String templateCode);
|
||||
|
||||
/**
|
||||
* 短信批量发送
|
||||
*
|
||||
* @param mobile 接收手机号
|
||||
* @param signName 签名
|
||||
* @param templateCode 模版code
|
||||
*/
|
||||
void sendBatchSms(String signName, List<String> mobile, String templateCode);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
package cn.lili.common.sms.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.lili.common.cache.Cache;
|
||||
import cn.lili.common.cache.CachePrefix;
|
||||
import cn.lili.common.exception.ServiceException;
|
||||
import cn.lili.common.security.context.UserContext;
|
||||
import cn.lili.common.sms.AliSmsUtil;
|
||||
import cn.lili.common.sms.SmsUtil;
|
||||
import cn.lili.common.utils.CommonUtil;
|
||||
import cn.lili.common.verification.enums.VerificationEnums;
|
||||
import cn.lili.modules.message.entity.dos.SmsSign;
|
||||
import cn.lili.modules.message.entity.dos.SmsTemplate;
|
||||
import cn.lili.modules.connect.util.Base64Utils;
|
||||
import cn.lili.modules.member.entity.dos.Member;
|
||||
import cn.lili.modules.member.service.MemberService;
|
||||
import cn.lili.modules.system.entity.dos.Setting;
|
||||
import cn.lili.modules.system.entity.dto.SmsSetting;
|
||||
import cn.lili.modules.system.entity.enums.SettingEnum;
|
||||
import cn.lili.modules.system.service.SettingService;
|
||||
import com.aliyun.dysmsapi20170525.models.*;
|
||||
import com.aliyun.teaopenapi.models.Config;
|
||||
import com.google.gson.Gson;
|
||||
import com.xkcoding.http.util.StringUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 短信网管阿里云实现
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v4.0
|
||||
* @Description:
|
||||
* @since 2020/11/30 15:44
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class SmsUtilAliImplService implements SmsUtil, AliSmsUtil {
|
||||
|
||||
private final Cache cache;
|
||||
|
||||
private final SettingService settingService;
|
||||
|
||||
private final MemberService memberService;
|
||||
|
||||
@Override
|
||||
public void sendSmsCode(String mobile, VerificationEnums verificationEnums, String uuid) {
|
||||
|
||||
String code = CommonUtil.getRandomNum();
|
||||
code = "111111";
|
||||
|
||||
switch (verificationEnums) {
|
||||
//如果某个模版需要自定义,则在此处进行调整
|
||||
case LOGIN:
|
||||
case REGISTER:
|
||||
case FIND_USER: {
|
||||
|
||||
//准备发送短信参数
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("code", code);
|
||||
cache.put(cacheKey(verificationEnums, mobile, uuid), code, 300L);
|
||||
//this.sendSmsCode("北京宏业汇成科技有限公司",mobile, params, verificationEnums.getSmsTemplate());
|
||||
break;
|
||||
}
|
||||
case UPDATE_PASSWORD: {
|
||||
Member member = memberService.getById(UserContext.getCurrentUser().getId());
|
||||
if (member == null || StringUtil.isEmpty(member.getMobile())) {
|
||||
return;
|
||||
}
|
||||
String memberMobile = member.getMobile();
|
||||
//准备发送短信参数
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("code", code);
|
||||
cache.put(cacheKey(verificationEnums, memberMobile, uuid), code, 300L);
|
||||
//this.sendSmsCode("北京宏业汇成科技有限公司",mobile, params, verificationEnums.getSmsTemplate());
|
||||
break;
|
||||
}
|
||||
//如果不是有效的验证码手段,则此处不进行短信操作
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verifyCode(String mobile, VerificationEnums verificationEnums, String uuid, String code) {
|
||||
Object result = cache.get(cacheKey(verificationEnums, mobile, uuid));
|
||||
if (code.equals(result)) {
|
||||
//校验之后,删除
|
||||
cache.remove(cacheKey(verificationEnums, mobile, uuid));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendSmsCode(String signName, String mobile, Map<String, String> param, String templateCode) {
|
||||
|
||||
com.aliyun.dysmsapi20170525.Client client = this.createClient();
|
||||
SendSmsRequest sendSmsRequest = new SendSmsRequest()
|
||||
.setSignName(signName)
|
||||
.setPhoneNumbers(mobile)
|
||||
.setTemplateCode(templateCode)
|
||||
.setTemplateParam(JSONUtil.toJsonStr(param));
|
||||
try {
|
||||
SendSmsResponse response = client.sendSms(sendSmsRequest);
|
||||
System.out.println(response.getBody().getCode());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendBatchSms(String signName, List<String> mobile, String templateCode) {
|
||||
|
||||
com.aliyun.dysmsapi20170525.Client client = this.createClient();
|
||||
|
||||
List<String> sign = mobile;
|
||||
|
||||
sign.replaceAll(e -> signName);
|
||||
|
||||
//手机号拆成多个小组进行发送
|
||||
List<List<String>> mobileList = new ArrayList<>();
|
||||
|
||||
//签名名称多个小组
|
||||
List<List<String>> signNameList = new ArrayList<>();
|
||||
|
||||
//循环分组
|
||||
for (int i = 0; i < (mobile.size() / 100 + (mobile.size() % 100 == 0 ? 0 : 1)); i++) {
|
||||
int endPoint = Math.min((100 + (i * 100)), mobile.size());
|
||||
mobileList.add(mobile.subList((i * 100), endPoint));
|
||||
signNameList.add(sign.subList((i * 100), endPoint));
|
||||
}
|
||||
|
||||
//发送短信
|
||||
for (int i = 0; i < mobileList.size(); i++) {
|
||||
SendBatchSmsRequest sendBatchSmsRequest = new SendBatchSmsRequest()
|
||||
.setPhoneNumberJson(JSONUtil.toJsonStr(mobileList.get(i)))
|
||||
.setSignNameJson(JSONUtil.toJsonStr(signNameList.get(i)))
|
||||
.setTemplateCode(templateCode);
|
||||
try {
|
||||
client.sendBatchSms(sendBatchSmsRequest);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addSmsSign(SmsSign smsSign) throws Exception {
|
||||
//设置参数添加短信签名
|
||||
com.aliyun.dysmsapi20170525.Client client = this.createClient();
|
||||
|
||||
AddSmsSignRequest.AddSmsSignRequestSignFileList signFileList0 = new AddSmsSignRequest.AddSmsSignRequestSignFileList()
|
||||
.setFileContents(Base64Utils.encode(smsSign.getBusinessLicense()))
|
||||
.setFileSuffix(smsSign.getBusinessLicense().substring(smsSign.getBusinessLicense().lastIndexOf(".") + 1));
|
||||
AddSmsSignRequest.AddSmsSignRequestSignFileList signFileList1 = new AddSmsSignRequest.AddSmsSignRequestSignFileList()
|
||||
.setFileContents(Base64Utils.encode(smsSign.getLicense()))
|
||||
.setFileSuffix(smsSign.getLicense().substring(smsSign.getBusinessLicense().lastIndexOf(".") + 1));
|
||||
AddSmsSignRequest addSmsSignRequest = new AddSmsSignRequest()
|
||||
.setSignName(smsSign.getSignName())
|
||||
.setSignSource(smsSign.getSignSource())
|
||||
.setRemark(smsSign.getRemark())
|
||||
.setSignFileList(java.util.Arrays.asList(
|
||||
signFileList0,
|
||||
signFileList1
|
||||
));
|
||||
AddSmsSignResponse response = client.addSmsSign(addSmsSignRequest);
|
||||
if (!response.getBody().getCode().equals("OK")) {
|
||||
throw new ServiceException(response.getBody().getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteSmsSign(String signName) throws Exception {
|
||||
com.aliyun.dysmsapi20170525.Client client = this.createClient();
|
||||
DeleteSmsSignRequest deleteSmsSignRequest = new DeleteSmsSignRequest()
|
||||
.setSignName(signName);
|
||||
|
||||
DeleteSmsSignResponse response = client.deleteSmsSign(deleteSmsSignRequest);
|
||||
if (!response.getBody().getCode().equals("OK")) {
|
||||
throw new ServiceException(response.getBody().getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> querySmsSign(String signName) throws Exception {
|
||||
//设置参数查看短信签名
|
||||
com.aliyun.dysmsapi20170525.Client client = this.createClient();
|
||||
QuerySmsSignRequest querySmsSignRequest = new QuerySmsSignRequest().setSignName(signName);
|
||||
|
||||
QuerySmsSignResponse response = client.querySmsSign(querySmsSignRequest);
|
||||
if (!response.getBody().getCode().equals("OK")) {
|
||||
throw new ServiceException(response.getBody().getMessage());
|
||||
}
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("SignStatus", response.getBody().getSignStatus());
|
||||
map.put("Reason", response.getBody().getReason());
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifySmsSign(SmsSign smsSign) throws Exception {
|
||||
//设置参数添加短信签名
|
||||
com.aliyun.dysmsapi20170525.Client client = this.createClient();
|
||||
|
||||
ModifySmsSignRequest.ModifySmsSignRequestSignFileList signFileList0 = new ModifySmsSignRequest.ModifySmsSignRequestSignFileList()
|
||||
.setFileContents(Base64Utils.encode(smsSign.getBusinessLicense()))
|
||||
.setFileSuffix(smsSign.getBusinessLicense().substring(smsSign.getBusinessLicense().lastIndexOf(".") + 1));
|
||||
ModifySmsSignRequest.ModifySmsSignRequestSignFileList signFileList1 = new ModifySmsSignRequest.ModifySmsSignRequestSignFileList()
|
||||
.setFileContents(Base64Utils.encode(smsSign.getLicense()))
|
||||
.setFileSuffix(smsSign.getLicense().substring(smsSign.getBusinessLicense().lastIndexOf(".") + 1));
|
||||
ModifySmsSignRequest modifySmsSign = new ModifySmsSignRequest()
|
||||
.setSignName(smsSign.getSignName())
|
||||
.setSignSource(smsSign.getSignSource())
|
||||
.setRemark(smsSign.getRemark())
|
||||
.setSignFileList(java.util.Arrays.asList(
|
||||
signFileList0,
|
||||
signFileList1
|
||||
));
|
||||
ModifySmsSignResponse response = client.modifySmsSign(modifySmsSign);
|
||||
if (!response.getBody().getCode().equals("OK")) {
|
||||
throw new ServiceException(response.getBody().getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifySmsTemplate(SmsTemplate smsTemplate) throws Exception {
|
||||
com.aliyun.dysmsapi20170525.Client client = this.createClient();
|
||||
ModifySmsTemplateRequest modifySmsTemplateRequest = new ModifySmsTemplateRequest()
|
||||
.setTemplateType(smsTemplate.getTemplateType())
|
||||
.setTemplateName(smsTemplate.getTemplateName())
|
||||
.setTemplateContent(smsTemplate.getTemplateContent())
|
||||
.setRemark(smsTemplate.getRemark())
|
||||
.setTemplateCode(smsTemplate.getTemplateCode());
|
||||
|
||||
ModifySmsTemplateResponse response = client.modifySmsTemplate(modifySmsTemplateRequest);
|
||||
if (!response.getBody().getCode().equals("OK")) {
|
||||
throw new ServiceException(response.getBody().getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> querySmsTemplate(String templateCode) throws Exception {
|
||||
com.aliyun.dysmsapi20170525.Client client = this.createClient();
|
||||
QuerySmsTemplateRequest querySmsTemplateRequest = new QuerySmsTemplateRequest()
|
||||
.setTemplateCode(templateCode);
|
||||
QuerySmsTemplateResponse response = client.querySmsTemplate(querySmsTemplateRequest);
|
||||
|
||||
if (!response.getBody().getCode().equals("OK")) {
|
||||
throw new ServiceException(response.getBody().getMessage());
|
||||
}
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("TemplateStatus", response.getBody().getTemplateStatus());
|
||||
map.put("Reason", response.getBody().getReason());
|
||||
map.put("TemplateCode", response.getBody().getTemplateCode());
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String addSmsTemplate(SmsTemplate smsTemplate) throws Exception {
|
||||
com.aliyun.dysmsapi20170525.Client client = this.createClient();
|
||||
AddSmsTemplateRequest addSmsTemplateRequest = new AddSmsTemplateRequest()
|
||||
.setTemplateType(1)
|
||||
.setTemplateName(smsTemplate.getTemplateName())
|
||||
.setTemplateContent(smsTemplate.getTemplateContent())
|
||||
.setRemark(smsTemplate.getRemark());
|
||||
|
||||
AddSmsTemplateResponse response = client.addSmsTemplate(addSmsTemplateRequest);
|
||||
if (!response.getBody().getCode().equals("OK")) {
|
||||
throw new ServiceException(response.getBody().getMessage());
|
||||
}
|
||||
return response.getBody().getTemplateCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteSmsTemplate(String templateCode) throws Exception {
|
||||
com.aliyun.dysmsapi20170525.Client client = this.createClient();
|
||||
DeleteSmsTemplateRequest deleteSmsTemplateRequest = new DeleteSmsTemplateRequest()
|
||||
.setTemplateCode(templateCode);
|
||||
|
||||
DeleteSmsTemplateResponse response = client.deleteSmsTemplate(deleteSmsTemplateRequest);
|
||||
if (!response.getBody().getCode().equals("OK")) {
|
||||
throw new ServiceException(response.getBody().getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 使用AK&SK初始化账号Client
|
||||
*
|
||||
* @return Client
|
||||
* @throws Exception
|
||||
*/
|
||||
public com.aliyun.dysmsapi20170525.Client createClient() {
|
||||
try {
|
||||
Setting setting = settingService.getById(SettingEnum.SMS_SETTING.name());
|
||||
if (StrUtil.isBlank(setting.getSettingValue())) {
|
||||
throw new ServiceException("您还未配置阿里云短信");
|
||||
}
|
||||
SmsSetting smsSetting = new Gson().fromJson(setting.getSettingValue(), SmsSetting.class);
|
||||
|
||||
Config config = new Config();
|
||||
// 您的AccessKey ID
|
||||
//config.accessKeyId = smsSetting.getAccessKeyId();
|
||||
config.accessKeyId = "LTAI4G4deX59EyjpEULaJdsU";
|
||||
// 您的AccessKey Secret
|
||||
//config.accessKeySecret = smsSetting.getAccessSecret();
|
||||
config.accessKeySecret = "BlRBpl7WBman6GYYwLKMiKqMTXFhWf";
|
||||
// 访问的域名
|
||||
config.endpoint = "dysmsapi.aliyuncs.com";
|
||||
return new com.aliyun.dysmsapi20170525.Client(config);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成缓存key
|
||||
*
|
||||
* @param verificationEnums 验证场景
|
||||
* @param mobile 手机号码
|
||||
* @param uuid 用户标识 uuid
|
||||
* @return
|
||||
*/
|
||||
static String cacheKey(VerificationEnums verificationEnums, String mobile, String uuid) {
|
||||
return CachePrefix.SMS_CODE.getPrefix() + verificationEnums.name() + mobile;
|
||||
}
|
||||
}
|
||||
36
framework/src/main/java/cn/lili/common/test/BaseTest.java
Normal file
36
framework/src/main/java/cn/lili/common/test/BaseTest.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package cn.lili.common.test;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.Rollback;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestContext;
|
||||
import org.springframework.test.context.TestExecutionListener;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* BaseTest
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v1.0
|
||||
* @since
|
||||
* 2020-06-13 12:17
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Rollback()
|
||||
@ContextConfiguration
|
||||
@Configuration
|
||||
@ComponentScan("cn.lili")
|
||||
public class BaseTest implements TestExecutionListener {
|
||||
@Override
|
||||
public void beforeTestClass(TestContext testContext) throws Exception {
|
||||
//设置环境变量 解决es冲突
|
||||
System.setProperty("es.set.netty.runtime.available.processors", "false");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package cn.lili.common.token;
|
||||
|
||||
/**
|
||||
* 权限枚举值
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v4.0
|
||||
* @Description:
|
||||
* @since 2020/11/25 09:21
|
||||
*/
|
||||
|
||||
public enum PermissionEnum {
|
||||
|
||||
/**
|
||||
* 超级权限,查看权限
|
||||
*/
|
||||
SUPER, QUERY
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package cn.lili.common.token;
|
||||
|
||||
import com.google.api.client.repackaged.org.apache.commons.codec.binary.Base64;
|
||||
import io.jsonwebtoken.io.Decoders;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
/**
|
||||
* SignWithUtil
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v1.0
|
||||
* 2020-11-18 17:30
|
||||
*/
|
||||
public class SecretKeyUtil {
|
||||
public static SecretKey generalKey() {
|
||||
byte[] encodedKey = Base64.decodeBase64("cuAihCz53DZRjZwbsGcZJ2Ai6At+T142uphtJMsk7iQ=");//自定义
|
||||
javax.crypto.SecretKey key = Keys.hmacShaKeyFor(encodedKey);
|
||||
return key;
|
||||
}
|
||||
|
||||
public static SecretKey generalKeyByDecoders() {
|
||||
return Keys.hmacShaKeyFor(Decoders.BASE64.decode("cuAihCz53DZRjZwbsGcZJ2Ai6At+T142uphtJMsk7iQ="));
|
||||
|
||||
}
|
||||
}
|
||||
24
framework/src/main/java/cn/lili/common/token/Token.java
Normal file
24
framework/src/main/java/cn/lili/common/token/Token.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package cn.lili.common.token;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* Token 实体类
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v1.0
|
||||
* 2020-11-13 10:02
|
||||
*/
|
||||
@Data
|
||||
public class Token {
|
||||
/**
|
||||
* 访问token
|
||||
*/
|
||||
private String accessToken;
|
||||
|
||||
/**
|
||||
* 刷新token
|
||||
*/
|
||||
private String refreshToken;
|
||||
|
||||
}
|
||||
138
framework/src/main/java/cn/lili/common/token/TokenUtil.java
Normal file
138
framework/src/main/java/cn/lili/common/token/TokenUtil.java
Normal file
@@ -0,0 +1,138 @@
|
||||
package cn.lili.common.token;
|
||||
|
||||
import cn.lili.common.cache.Cache;
|
||||
import cn.lili.common.cache.CachePrefix;
|
||||
import cn.lili.common.enums.ResultCode;
|
||||
import cn.lili.common.exception.ServiceException;
|
||||
import cn.lili.common.security.AuthUser;
|
||||
import cn.lili.common.security.enums.SecurityEnum;
|
||||
import cn.lili.common.security.enums.UserEnums;
|
||||
import cn.lili.config.properties.JWTTokenProperties;
|
||||
import com.google.gson.Gson;
|
||||
import io.jsonwebtoken.*;
|
||||
import io.jsonwebtoken.security.SignatureException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* TokenUtil
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v1.0
|
||||
* 2020-11-12 18:44
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class TokenUtil {
|
||||
|
||||
private final JWTTokenProperties tokenProperties;
|
||||
|
||||
private final Cache cache;
|
||||
|
||||
/**
|
||||
* 构建token
|
||||
*
|
||||
* @param username 主体
|
||||
* @param claim 私有声明
|
||||
* @param longTerm 长时间特殊token 如:移动端,微信小程序等
|
||||
* @return
|
||||
*/
|
||||
public Token createToken(String username, Object claim, boolean longTerm, UserEnums userEnums) {
|
||||
Token token = new Token();
|
||||
//访问token
|
||||
String accessToken = createToken(username, claim, tokenProperties.getTokenExpireTime());
|
||||
|
||||
cache.put(CachePrefix.ACCESS_TOKEN.getPrefix(userEnums) + accessToken, 1,
|
||||
tokenProperties.getTokenExpireTime(), TimeUnit.MINUTES);
|
||||
//刷新token生成策略:如果是长时间有效的token(用于app),则默认15天有效期刷新token。如果是普通用户登录,则刷新token为普通token2倍数
|
||||
Long expireTime = longTerm ? 15 * 24 * 60L : tokenProperties.getTokenExpireTime() * 2;
|
||||
String refreshToken = createToken(username, claim, expireTime);
|
||||
|
||||
cache.put(CachePrefix.REFRESH_TOKEN.getPrefix(userEnums) + refreshToken, 1, expireTime, TimeUnit.MINUTES);
|
||||
|
||||
token.setAccessToken(accessToken);
|
||||
token.setRefreshToken(refreshToken);
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新token
|
||||
*
|
||||
* @param oldRefreshToken 刷新token
|
||||
* @return token
|
||||
*/
|
||||
public Token refreshToken(String oldRefreshToken, UserEnums userEnums) {
|
||||
|
||||
Claims claims;
|
||||
try {
|
||||
claims = Jwts.parser()
|
||||
.setSigningKey(SecretKeyUtil.generalKeyByDecoders())
|
||||
.parseClaimsJws(oldRefreshToken).getBody();
|
||||
} catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | SignatureException | IllegalArgumentException e) {
|
||||
//token 过期 认证失败等
|
||||
throw new ServiceException(ResultCode.USER_AUTH_EXPIRED);
|
||||
}
|
||||
|
||||
//获取存储在claims中的用户信息
|
||||
String json = claims.get(SecurityEnum.USER_CONTEXT.getValue()).toString();
|
||||
AuthUser authUser = new Gson().fromJson(json, AuthUser.class);
|
||||
|
||||
|
||||
String username = authUser.getUsername();
|
||||
//获取是否长期有效的token
|
||||
boolean longTerm = authUser.getLongTerm();
|
||||
|
||||
|
||||
//如果缓存中有刷新token &&
|
||||
if (cache.hasKey(CachePrefix.REFRESH_TOKEN.getPrefix(userEnums) + oldRefreshToken)) {
|
||||
Token token = new Token();
|
||||
//访问token
|
||||
String accessToken = createToken(username, authUser, tokenProperties.getTokenExpireTime());
|
||||
cache.put(CachePrefix.ACCESS_TOKEN.getPrefix(userEnums) + accessToken, 1, tokenProperties.getTokenExpireTime(), TimeUnit.MINUTES);
|
||||
|
||||
//如果是信任登录设备,则刷新token长度继续延长
|
||||
Long expirationTime = tokenProperties.getTokenExpireTime() * 2;
|
||||
if (longTerm) {
|
||||
expirationTime = 60 * 24 * 15L;
|
||||
}
|
||||
|
||||
//刷新token生成策略:如果是长时间有效的token(用于app),则默认15天有效期刷新token。如果是普通用户登录,则刷新token为普通token2倍数
|
||||
String refreshToken = createToken(username, authUser, expirationTime);
|
||||
|
||||
cache.put(CachePrefix.REFRESH_TOKEN.getPrefix(userEnums) + refreshToken, 1, expirationTime, TimeUnit.MINUTES);
|
||||
token.setAccessToken(accessToken);
|
||||
token.setRefreshToken(refreshToken);
|
||||
cache.remove(CachePrefix.REFRESH_TOKEN.getPrefix(userEnums) + oldRefreshToken);
|
||||
return token;
|
||||
} else {
|
||||
throw new ServiceException(ResultCode.USER_AUTH_EXPIRED);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成token
|
||||
*
|
||||
* @param username 主体
|
||||
* @param claim 私有神明内容
|
||||
* @param expirationTime 过期时间(分钟)
|
||||
* @return
|
||||
*/
|
||||
private String createToken(String username, Object claim, Long expirationTime) {
|
||||
// JWT 生成
|
||||
return Jwts.builder()
|
||||
// jwt 私有声明
|
||||
.claim(SecurityEnum.USER_CONTEXT.getValue(), new Gson().toJson(claim))
|
||||
// JWT的主体
|
||||
.setSubject(username)
|
||||
// 失效时间 当前时间+过期分钟
|
||||
.setExpiration(new Date(System.currentTimeMillis() + expirationTime * 60 * 1000))
|
||||
// 签名算法和密钥
|
||||
.signWith(SecretKeyUtil.generalKey())
|
||||
.compact();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package cn.lili.common.token.base;
|
||||
|
||||
import cn.lili.common.security.enums.UserEnums;
|
||||
import cn.lili.common.token.Token;
|
||||
|
||||
/**
|
||||
* AbstractToken
|
||||
* 抽象token,定义生成token类
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v1.0
|
||||
* 2020-11-13 10:13
|
||||
*/
|
||||
public abstract class AbstractTokenGenerate {
|
||||
|
||||
/**
|
||||
* 生成token
|
||||
*
|
||||
* @param username 用户名
|
||||
* @param longTerm 是否长时间有效
|
||||
* @return
|
||||
*/
|
||||
public abstract Token createToken(String username, Boolean longTerm);
|
||||
|
||||
/**
|
||||
* 刷新token
|
||||
*
|
||||
* @param refreshToken 刷新token
|
||||
* @return token
|
||||
*/
|
||||
public abstract Token refreshToken(String refreshToken);
|
||||
|
||||
/**
|
||||
* 默认role
|
||||
*/
|
||||
public UserEnums role = UserEnums.MANAGER;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package cn.lili.common.token.base.generate;
|
||||
|
||||
import cn.lili.common.cache.Cache;
|
||||
import cn.lili.common.cache.CachePrefix;
|
||||
import cn.lili.common.security.AuthUser;
|
||||
import cn.lili.common.security.enums.UserEnums;
|
||||
import cn.lili.common.token.PermissionEnum;
|
||||
import cn.lili.common.token.Token;
|
||||
import cn.lili.common.token.TokenUtil;
|
||||
import cn.lili.common.token.base.AbstractTokenGenerate;
|
||||
import cn.lili.modules.permission.entity.dos.AdminUser;
|
||||
import cn.lili.modules.permission.entity.vo.UserMenuVO;
|
||||
import cn.lili.modules.permission.service.AdminUserService;
|
||||
import cn.lili.modules.permission.service.RoleMenuService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 管理员token生成
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v4.0
|
||||
* @Description:
|
||||
* @since 2020/11/16 10:51
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class ManagerTokenGenerate extends AbstractTokenGenerate {
|
||||
|
||||
private AdminUserService adminUserService;
|
||||
|
||||
private final TokenUtil tokenUtil;
|
||||
|
||||
private final RoleMenuService roleMenuService;
|
||||
|
||||
private final Cache cache;
|
||||
|
||||
@Autowired
|
||||
public void setAdminUserService(AdminUserService adminUserService) {
|
||||
this.adminUserService = adminUserService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Token createToken(String username, Boolean longTerm) {
|
||||
// 生成token
|
||||
AdminUser adminUser = adminUserService.findByUsername(username);
|
||||
AuthUser user = new AuthUser(adminUser.getUsername(), adminUser.getId(), UserEnums.MANAGER, adminUser.getNickName(), adminUser.getIsSuper());
|
||||
|
||||
|
||||
List<UserMenuVO> userMenuVOList = roleMenuService.findAllMenu(user.getId());
|
||||
//缓存权限列表
|
||||
cache.put(CachePrefix.PERMISSION_LIST.getPrefix(UserEnums.MANAGER) + user.getId(), this.permissionList(userMenuVOList));
|
||||
|
||||
return tokenUtil.createToken(username, user, longTerm, UserEnums.MANAGER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Token refreshToken(String refreshToken) {
|
||||
return tokenUtil.refreshToken(refreshToken, UserEnums.MANAGER);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户权限
|
||||
*
|
||||
* @param userMenuVOList
|
||||
* @return
|
||||
*/
|
||||
private Map<String, List<String>> permissionList(List<UserMenuVO> userMenuVOList) {
|
||||
Map<String, List<String>> permission = new HashMap<>();
|
||||
if (userMenuVOList == null || userMenuVOList.size() == 0) {
|
||||
return permission;
|
||||
}
|
||||
List<String> superPermissions = new ArrayList<>();
|
||||
List<String> queryPermissions = new ArrayList<>();
|
||||
initPermission(superPermissions, queryPermissions);
|
||||
|
||||
//循环权限菜单
|
||||
userMenuVOList.forEach(menu -> {
|
||||
//循环菜单,赋予用户权限
|
||||
if (menu.getPath() != null) {
|
||||
//获取路径集合
|
||||
String[] paths = menu.getPath().split("\\|");
|
||||
//for循环路径集合
|
||||
for (String path : paths) {
|
||||
//如果是超级权限 则计入超级权限
|
||||
if (menu.getIsSupper()) {
|
||||
//如果已有超级权限,则这里就不做权限的累加
|
||||
if (!superPermissions.contains(path)) {
|
||||
superPermissions.add(path);
|
||||
}
|
||||
}
|
||||
//否则计入浏览权限
|
||||
else {
|
||||
//如果已有超级权限,或者已有普通查看权限,则这里就不做权限的累加
|
||||
if (!superPermissions.contains(path) && !queryPermissions.contains(path)) {
|
||||
queryPermissions.add(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//去除无效的权限
|
||||
superPermissions.forEach(queryPermissions::remove);
|
||||
});
|
||||
permission.put(PermissionEnum.SUPER.name(), superPermissions);
|
||||
permission.put(PermissionEnum.QUERY.name(), queryPermissions);
|
||||
return permission;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始赋予的权限,查看权限包含首页流量统计权限,
|
||||
* 超级权限包含个人信息维护,密码修改权限
|
||||
*
|
||||
* @param superPermissions 超级权限
|
||||
* @param queryPermissions 查询权限
|
||||
*/
|
||||
void initPermission(List<String> superPermissions, List<String> queryPermissions) {
|
||||
//用户信息维护
|
||||
superPermissions.add("/manager/user/info");
|
||||
superPermissions.add("/manager/user/edit");
|
||||
superPermissions.add("/manager/user/editPassword*");
|
||||
//统计查看
|
||||
queryPermissions.add("/manager/statistics*");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package cn.lili.common.token.base.generate;
|
||||
|
||||
import cn.lili.common.security.AuthUser;
|
||||
import cn.lili.common.security.enums.UserEnums;
|
||||
import cn.lili.common.token.Token;
|
||||
import cn.lili.common.token.TokenUtil;
|
||||
import cn.lili.common.token.base.AbstractTokenGenerate;
|
||||
import cn.lili.config.context.ThreadContextHolder;
|
||||
import cn.lili.modules.base.entity.enums.ClientTypeEnum;
|
||||
import cn.lili.modules.member.entity.dos.Member;
|
||||
import cn.lili.modules.member.service.MemberService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 会员token生成
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v4.0
|
||||
* @Description:
|
||||
* @since 2020/11/16 10:50
|
||||
*/
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class MemberTokenGenerate extends AbstractTokenGenerate {
|
||||
|
||||
|
||||
private MemberService memberService;
|
||||
|
||||
private final TokenUtil tokenUtil;
|
||||
|
||||
@Override
|
||||
public Token createToken(String username, Boolean longTerm) {
|
||||
|
||||
Member member = memberService.findByUsername(username);
|
||||
|
||||
//获取客户端类型
|
||||
String clientType = ThreadContextHolder.getHttpRequest().getHeader("clientType");
|
||||
ClientTypeEnum clientTypeEnum;
|
||||
try {
|
||||
//如果客户端为空,则缺省值为PC,pc第三方登录时不会传递此参数
|
||||
if (clientType == null) {
|
||||
clientTypeEnum = ClientTypeEnum.PC;
|
||||
} else {
|
||||
clientTypeEnum = ClientTypeEnum.valueOf(clientType);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
clientTypeEnum = ClientTypeEnum.UNKNOWN;
|
||||
}
|
||||
//记录最后登录时间,客户端类型
|
||||
member.setLastLoginDate(new Date());
|
||||
member.setClientEnum(clientTypeEnum.name());
|
||||
memberService.updateById(member);
|
||||
|
||||
AuthUser authUser = new AuthUser(member.getUsername(), member.getId(),member.getNickName(), UserEnums.MEMBER);
|
||||
// 登陆成功生成token
|
||||
return tokenUtil.createToken(username, authUser, longTerm, UserEnums.MEMBER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Token refreshToken(String refreshToken) {
|
||||
return tokenUtil.refreshToken(refreshToken, UserEnums.MEMBER);
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
public void setMemberService(MemberService memberService) {
|
||||
this.memberService = memberService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package cn.lili.common.token.base.generate;
|
||||
|
||||
import cn.lili.common.exception.ServiceException;
|
||||
import cn.lili.common.security.AuthUser;
|
||||
import cn.lili.common.security.enums.UserEnums;
|
||||
import cn.lili.common.token.Token;
|
||||
import cn.lili.common.token.TokenUtil;
|
||||
import cn.lili.common.token.base.AbstractTokenGenerate;
|
||||
import cn.lili.common.enums.SwitchEnum;
|
||||
import cn.lili.modules.member.entity.dos.Member;
|
||||
import cn.lili.modules.member.service.MemberService;
|
||||
import cn.lili.modules.store.entity.dos.Store;
|
||||
import cn.lili.modules.store.service.StoreService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 商家token生成
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v4.0
|
||||
* @Description:
|
||||
* @since 2020/11/16 10:51
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class StoreTokenGenerate extends AbstractTokenGenerate {
|
||||
|
||||
private MemberService memberService;
|
||||
|
||||
private StoreService storeService;
|
||||
|
||||
private final TokenUtil tokenUtil;
|
||||
|
||||
@Autowired
|
||||
public void setMemberService(MemberService memberService) {
|
||||
this.memberService = memberService;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setStoreService(StoreService storeService) {
|
||||
this.storeService = storeService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Token createToken(String username, Boolean longTerm) {
|
||||
// 生成token
|
||||
Member member = memberService.findByUsername(username);
|
||||
if (member.getHaveStore().equals(SwitchEnum.CLOSE.name())) {
|
||||
throw new ServiceException("该会员未开通店铺");
|
||||
}
|
||||
AuthUser user = new AuthUser(member.getUsername(), member.getId(),member.getNickName(), UserEnums.STORE);
|
||||
LambdaQueryWrapper<Store> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(Store::getMemberId, member.getId());
|
||||
Store store = storeService.getOne(queryWrapper);
|
||||
user.setStoreId(store.getId());
|
||||
user.setStoreName(store.getStoreName());
|
||||
return tokenUtil.createToken(username, user, longTerm, UserEnums.STORE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Token refreshToken(String refreshToken) {
|
||||
return tokenUtil.refreshToken(refreshToken, UserEnums.STORE);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package cn.lili.common.trigger;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.lili.common.cache.Cache;
|
||||
import cn.lili.common.rocketmq.RocketmqSendCallbackBuilder;
|
||||
import cn.lili.common.trigger.delay.PromotionDelayQueue;
|
||||
import cn.lili.common.trigger.interfaces.TimeTrigger;
|
||||
import cn.lili.common.trigger.model.TimeTriggerMsg;
|
||||
import cn.lili.common.trigger.util.TimeTriggerUtil;
|
||||
import cn.lili.common.utils.DateUtil;
|
||||
import cn.lili.common.utils.StringUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.rocketmq.spring.core.RocketMQTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author paulG
|
||||
* @since 2020/11/5
|
||||
**/
|
||||
@Component
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class RocketmqTimerTrigger implements TimeTrigger {
|
||||
|
||||
private final RocketMQTemplate rocketMQTemplate;
|
||||
|
||||
private final Cache<Integer> cache;
|
||||
|
||||
private PromotionDelayQueue promotionDelayQueue;
|
||||
|
||||
@Autowired
|
||||
public void setPromotionDelayQueue(PromotionDelayQueue promotionDelayQueue) {
|
||||
this.promotionDelayQueue = promotionDelayQueue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(String executorName, Object param, Long triggerTime, String uniqueKey, String topic) {
|
||||
|
||||
|
||||
TimeTriggerMsg timeTriggerMsg = new TimeTriggerMsg(executorName, triggerTime, param, uniqueKey, topic);
|
||||
Message<TimeTriggerMsg> message = MessageBuilder.withPayload(timeTriggerMsg).build();
|
||||
|
||||
this.rocketMQTemplate.asyncSend(topic, message, RocketmqSendCallbackBuilder.commonCallback());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(TimeTriggerMsg timeTriggerMsg) {
|
||||
this.add(timeTriggerMsg.getTriggerExecutor(), timeTriggerMsg.getParam(), timeTriggerMsg.getTriggerTime(), timeTriggerMsg.getUniqueKey(), timeTriggerMsg.getTopic());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDelay(TimeTriggerMsg timeTriggerMsg, int delayTime) {
|
||||
String uniqueKey = timeTriggerMsg.getUniqueKey();
|
||||
if (StringUtils.isEmpty(uniqueKey)) {
|
||||
uniqueKey = StringUtils.getRandStr(10);
|
||||
}
|
||||
String generateKey = TimeTriggerUtil.generateKey(timeTriggerMsg.getTriggerExecutor(), timeTriggerMsg.getTriggerTime(), uniqueKey);
|
||||
this.cache.put(generateKey, 1);
|
||||
if (Boolean.TRUE.equals(promotionDelayQueue.addJobId(JSONUtil.toJsonStr(timeTriggerMsg), delayTime))) {
|
||||
log.info("add Redis key {} --------------------------", generateKey);
|
||||
log.info("定时执行在【" + DateUtil.toString(timeTriggerMsg.getTriggerTime(), "yyyy-MM-dd HH:mm:ss") + "】,消费【" + timeTriggerMsg.getParam().toString() + "】");
|
||||
} else {
|
||||
log.info("延时任务添加失败!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void edit(String executorName, Object param, Long oldTriggerTime, Long triggerTime, String uniqueKey, int delayTime, String topic) {
|
||||
this.delete(executorName, oldTriggerTime, uniqueKey, topic);
|
||||
this.addDelay(new TimeTriggerMsg(executorName, triggerTime, param, uniqueKey, topic), delayTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String executorName, Long triggerTime, String uniqueKey, String topic) {
|
||||
String generateKey = TimeTriggerUtil.generateKey(executorName, triggerTime, uniqueKey);
|
||||
log.info("delete redis key {} -----------------------", generateKey);
|
||||
this.cache.remove(generateKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package cn.lili.common.trigger.delay;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.lili.common.delayqueue.AbstractDelayQueueMachineFactory;
|
||||
import cn.lili.common.trigger.interfaces.TimeTrigger;
|
||||
import cn.lili.common.trigger.model.TimeTriggerMsg;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 促销延迟队列
|
||||
*
|
||||
* @author paulG
|
||||
* @version v4.1
|
||||
* @date 2020/11/17 7:19 下午
|
||||
* @description
|
||||
* @since 1
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class PromotionDelayQueue extends AbstractDelayQueueMachineFactory {
|
||||
|
||||
private final TimeTrigger timeTrigger;
|
||||
|
||||
@Override
|
||||
public void invoke(String jobId) {
|
||||
timeTrigger.add(JSONUtil.toBean(jobId, TimeTriggerMsg.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String setDelayQueueName() {
|
||||
return "promotion_delay";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package cn.lili.common.trigger.interfaces;
|
||||
|
||||
import cn.lili.common.trigger.model.TimeTriggerMsg;
|
||||
|
||||
/**
|
||||
* 延时执行接口
|
||||
*
|
||||
* @author Chopper
|
||||
*/
|
||||
public interface TimeTrigger {
|
||||
|
||||
/**
|
||||
* 添加延时任务
|
||||
*
|
||||
* @param executorName 执行器beanId
|
||||
* @param param 执行参数
|
||||
* @param triggerTime 执行时间 时间戳 秒为单位
|
||||
* @param uniqueKey 如果是一个 需要有 修改/取消 延时任务功能的延时任务,<br/>
|
||||
* 请填写此参数,作为后续删除,修改做为唯一凭证 <br/>
|
||||
* 建议参数为:COUPON_{ACTIVITY_ID} 例如 coupon_123<br/>
|
||||
* 业务内全局唯一
|
||||
* @param topic rocketmq topic
|
||||
*/
|
||||
void add(String executorName, Object param, Long triggerTime, String uniqueKey, String topic);
|
||||
|
||||
/**
|
||||
* 添加延时任务
|
||||
*
|
||||
* @param timeTriggerMsg 延时任务信息
|
||||
*/
|
||||
void add(TimeTriggerMsg timeTriggerMsg);
|
||||
|
||||
|
||||
/**
|
||||
* 添加延时任务
|
||||
*
|
||||
* @param timeTriggerMsg 延时任务信息
|
||||
* @param delayTime 延时时间(秒)
|
||||
*/
|
||||
void addDelay(TimeTriggerMsg timeTriggerMsg, int delayTime);
|
||||
|
||||
/**
|
||||
* 修改延时任务
|
||||
*
|
||||
* @param executorName 执行器beanId
|
||||
* @param param 执行参数
|
||||
* @param triggerTime 执行时间 时间戳 秒为单位
|
||||
* @param oldTriggerTime 旧的任务执行时间
|
||||
* @param uniqueKey 添加任务时的唯一凭证
|
||||
* @param delayTime 延时时间(秒)
|
||||
* @param topic rocketmq topic
|
||||
*/
|
||||
void edit(String executorName, Object param, Long oldTriggerTime, Long triggerTime, String uniqueKey, int delayTime, String topic);
|
||||
|
||||
/**
|
||||
* 删除延时任务
|
||||
*
|
||||
* @param executorName 执行器
|
||||
* @param triggerTime 执行时间
|
||||
* @param uniqueKey 添加任务时的唯一凭证
|
||||
* @param topic rocketmq topic
|
||||
*/
|
||||
void delete(String executorName, Long triggerTime, String uniqueKey, String topic);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package cn.lili.common.trigger.interfaces;
|
||||
|
||||
/**
|
||||
* 延时任务执行器接口
|
||||
* @author Chopper
|
||||
*
|
||||
*/
|
||||
public interface TimeTriggerExecutor {
|
||||
|
||||
|
||||
/**
|
||||
* 执行任务
|
||||
* @param object 任务参数
|
||||
*/
|
||||
void execute(Object object);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package cn.lili.common.trigger.model;
|
||||
|
||||
/**
|
||||
* @author paulG
|
||||
* @since 2020/8/20
|
||||
**/
|
||||
public abstract class TimeExecuteConstant {
|
||||
|
||||
/**
|
||||
* 促销延迟加载执行器
|
||||
*/
|
||||
public static final String PROMOTION_EXECUTOR = "promotionTimeTriggerExecutor";
|
||||
|
||||
/**
|
||||
* 拼团延迟加载执行器
|
||||
*/
|
||||
public static final String PINTUAN_EXECUTOR = "pintuanTimeTriggerExecutor";
|
||||
|
||||
/**
|
||||
* 拼团延迟加载执行器
|
||||
*/
|
||||
public static final String FULL_DISCOUNT_EXECUTOR = "fullDiscountTimeTriggerExecutor";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package cn.lili.common.trigger.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 延时任务消息
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v1.0
|
||||
* @since
|
||||
* 2019-02-12 下午5:46
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class TimeTriggerMsg implements Serializable {
|
||||
|
||||
|
||||
private static final long serialVersionUID = 8897917127201859535L;
|
||||
|
||||
/**
|
||||
* 执行器beanId
|
||||
*/
|
||||
private String triggerExecutor;
|
||||
|
||||
/**
|
||||
* 执行器 执行时间
|
||||
*/
|
||||
private Long triggerTime;
|
||||
|
||||
/**
|
||||
* 执行器参数
|
||||
*/
|
||||
private Object param;
|
||||
|
||||
/**
|
||||
* 唯一KEY
|
||||
*/
|
||||
private String uniqueKey;
|
||||
|
||||
/**
|
||||
* 信息队列主题
|
||||
*/
|
||||
private String topic;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package cn.lili.common.trigger.util;
|
||||
|
||||
/**
|
||||
* 延时任务mq实现内容,提供加密算法以及任务前缀参数
|
||||
*
|
||||
* @author Chopper
|
||||
*/
|
||||
public class TimeTriggerUtil {
|
||||
|
||||
/**
|
||||
* 前缀
|
||||
*/
|
||||
private static final String PREFIX = "{rocketmq_trigger}_";
|
||||
|
||||
/**
|
||||
* 生成延时任务标识key
|
||||
*
|
||||
* @param executorName 执行器beanId
|
||||
* @param triggerTime 执行时间
|
||||
* @param uniqueKey 自定义表示
|
||||
* @return 延时任务标识key
|
||||
*/
|
||||
public static String generateKey(String executorName, Long triggerTime, String uniqueKey) {
|
||||
return PREFIX + (executorName + triggerTime + uniqueKey).hashCode();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package cn.lili.common.utils;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.Base64.Decoder;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* base64转为multipartFile工具类
|
||||
*
|
||||
* @author Chopper
|
||||
*/
|
||||
public class Base64DecodeMultipartFile implements MultipartFile {
|
||||
|
||||
private final byte[] imgContent;
|
||||
private final String header;
|
||||
|
||||
public Base64DecodeMultipartFile(byte[] imgContent, String header) {
|
||||
this.imgContent = imgContent;
|
||||
this.header = header.split(";")[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return System.currentTimeMillis() + Math.random() + "." + header.split("/")[1];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOriginalFilename() {
|
||||
return System.currentTimeMillis() + (int) Math.random() * 10000 + "." + header.split("/")[1];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentType() {
|
||||
return header.split(":")[1];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return imgContent == null || imgContent.length == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSize() {
|
||||
return imgContent.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBytes() throws IOException {
|
||||
return imgContent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return new ByteArrayInputStream(imgContent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transferTo(File dest) throws IOException, IllegalStateException {
|
||||
new FileOutputStream(dest).write(imgContent);
|
||||
}
|
||||
|
||||
|
||||
public static MultipartFile base64Convert(String base64) {
|
||||
|
||||
String[] baseStrs = base64.split(",");
|
||||
Decoder decoder = Base64.getDecoder();
|
||||
byte[] b = decoder.decode(baseStrs[1]);
|
||||
|
||||
for (int i = 0; i < b.length; ++i) {
|
||||
if (b[i] < 0) {
|
||||
b[i] += 256;
|
||||
}
|
||||
}
|
||||
return new Base64DecodeMultipartFile(b, baseStrs[0]);
|
||||
}
|
||||
|
||||
|
||||
public static InputStream base64ToInputStream(String base64) {
|
||||
ByteArrayInputStream stream = null;
|
||||
try {
|
||||
byte[] bytes = Base64.getDecoder().decode(base64);
|
||||
stream = new ByteArrayInputStream(bytes);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
public static String inputStreamToStream(InputStream in) {
|
||||
byte[] data = null;
|
||||
// 读取图片字节数组
|
||||
try {
|
||||
ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
|
||||
byte[] buff = new byte[100];
|
||||
int rc = 0;
|
||||
while ((rc = in.read(buff, 0, 100)) > 0) {
|
||||
swapStream.write(buff, 0, rc);
|
||||
}
|
||||
data = swapStream.toByteArray();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return Base64.getEncoder().encodeToString(data);
|
||||
}
|
||||
}
|
||||
|
||||
59
framework/src/main/java/cn/lili/common/utils/BeanUtil.java
Normal file
59
framework/src/main/java/cn/lili/common/utils/BeanUtil.java
Normal file
@@ -0,0 +1,59 @@
|
||||
package cn.lili.common.utils;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* 对象属性复制
|
||||
* @author Chopper
|
||||
*/
|
||||
public class BeanUtil {
|
||||
|
||||
/**
|
||||
* 复制属性
|
||||
* @param objectFrom
|
||||
* @param objectTo
|
||||
*/
|
||||
public static void copyProperties(Object objectFrom,Object objectTo){
|
||||
BeanUtils.copyProperties(objectFrom, objectTo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取属性名数组
|
||||
*/
|
||||
public static String[] getFiledName(Object o) {
|
||||
Field[] fields = o.getClass().getDeclaredFields();
|
||||
Field[] superFields = o.getClass().getSuperclass().getDeclaredFields();
|
||||
String[] fieldNames = new String[fields.length + superFields.length];
|
||||
int index = 0;
|
||||
for (int i = 0; i < fields.length; i++) {
|
||||
fieldNames[index] = fields[i].getName();
|
||||
index++;
|
||||
}
|
||||
for (int i = 0; i < superFields.length; i++) {
|
||||
if (superFields[i].getName().equals("id")) {
|
||||
continue;
|
||||
}
|
||||
fieldNames[index] = superFields[i].getName();
|
||||
index++;
|
||||
}
|
||||
return fieldNames;
|
||||
}
|
||||
|
||||
/* 根据属性名获取属性值
|
||||
* */
|
||||
public static Object getFieldValueByName(String fieldName, Object o) {
|
||||
try {
|
||||
String firstLetter = fieldName.substring(0, 1).toUpperCase();
|
||||
String getter = "get" + firstLetter + fieldName.substring(1);
|
||||
Method method = o.getClass().getMethod(getter, new Class[]{});
|
||||
Object value = method.invoke(o, new Object[]{});
|
||||
return value;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package cn.lili.common.utils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* CheckMobileUtil
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v1.0
|
||||
* 2021-02-04 14:56
|
||||
*/
|
||||
public class CheckMobileUtil {
|
||||
|
||||
|
||||
// \b 是单词边界(连着的两个(字母字符 与 非字母字符) 之间的逻辑上的间隔),
|
||||
// 字符串在编译时会被转码一次,所以是 "\\b"
|
||||
// \B 是单词内部逻辑间隔(连着的两个字母字符之间的逻辑上的间隔)
|
||||
static String phoneReg = "\\b(ip(hone|od)|android|opera m(ob|in)i"
|
||||
+ "|windows (phone|ce)|blackberry"
|
||||
+ "|s(ymbian|eries60|amsung)|p(laybook|alm|rofile/midp"
|
||||
+ "|laystation portable)|nokia|fennec|htc[-_]"
|
||||
+ "|mobile|up.browser|[1-4][0-9]{2}x[1-4][0-9]{2})\\b";
|
||||
static String tableReg = "\\b(ipad|tablet|(Nexus 7)|up.browser"
|
||||
+ "|[1-4][0-9]{2}x[1-4][0-9]{2})\\b";
|
||||
|
||||
//移动设备正则匹配:手机端、平板
|
||||
static Pattern phonePat = Pattern.compile(phoneReg, Pattern.CASE_INSENSITIVE);
|
||||
static Pattern tablePat = Pattern.compile(tableReg, Pattern.CASE_INSENSITIVE);
|
||||
|
||||
/**
|
||||
* 检测是否是移动设备访问
|
||||
*
|
||||
* @param request 浏览器标识 获取方式:
|
||||
* @return true:移动设备接入,false:pc端接入
|
||||
* @Title: check
|
||||
*/
|
||||
public static boolean check(HttpServletRequest request) {
|
||||
String userAgent = request.getHeader("USER-AGENT").toLowerCase();
|
||||
if (null == userAgent) {
|
||||
userAgent = "";
|
||||
}
|
||||
// 匹配
|
||||
Matcher matcherPhone = phonePat.matcher(userAgent);
|
||||
Matcher matcherTable = tablePat.matcher(userAgent);
|
||||
if (matcherPhone.find() || matcherTable.find()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
51
framework/src/main/java/cn/lili/common/utils/CommonUtil.java
Normal file
51
framework/src/main/java/cn/lili/common/utils/CommonUtil.java
Normal file
@@ -0,0 +1,51 @@
|
||||
package cn.lili.common.utils;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 通用工具
|
||||
* @author Chopper
|
||||
*/
|
||||
public class CommonUtil {
|
||||
|
||||
/**
|
||||
* 以UUID重命名
|
||||
* @param fileName
|
||||
* @return
|
||||
*/
|
||||
public static String rename(String fileName) {
|
||||
String extName = fileName.substring(fileName.lastIndexOf("."));
|
||||
return UUID.randomUUID().toString().replace("-", "") + extName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 随机6位数生成
|
||||
*/
|
||||
public static String getRandomNum() {
|
||||
|
||||
Random random = new Random();
|
||||
int num = random.nextInt(999999);
|
||||
//不足六位前面补0
|
||||
String str = String.format("%06d", num);
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量递归删除时 判断target是否在ids中 避免重复删除
|
||||
* @param target
|
||||
* @param ids
|
||||
* @return
|
||||
*/
|
||||
public static Boolean judgeIds(String target, String[] ids){
|
||||
Boolean flag = false;
|
||||
for(String id : ids){
|
||||
if(id.equals(target)){
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
90
framework/src/main/java/cn/lili/common/utils/CookieUtil.java
Normal file
90
framework/src/main/java/cn/lili/common/utils/CookieUtil.java
Normal file
@@ -0,0 +1,90 @@
|
||||
package cn.lili.common.utils;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* CookieUtil
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v1.0
|
||||
* 2020-12-14 09:32
|
||||
*/
|
||||
public class CookieUtil {
|
||||
|
||||
/**
|
||||
* 新增cookie
|
||||
*
|
||||
* @param key key值
|
||||
* @param value 对应值
|
||||
* @param response 响应
|
||||
*/
|
||||
public static void addCookie(String key, String value, HttpServletResponse response) {
|
||||
try {
|
||||
Cookie c = new Cookie(key, value);
|
||||
c.setPath("/");
|
||||
response.addCookie(c);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增cookie
|
||||
*
|
||||
* @param key key值
|
||||
* @param value 对应值
|
||||
* @param maxAge cookie 有效时间
|
||||
* @param response 响应
|
||||
*/
|
||||
public static void addCookie(String key, String value, Integer maxAge, HttpServletResponse response) {
|
||||
try {
|
||||
Cookie c = new Cookie(key, value);
|
||||
c.setMaxAge(maxAge);
|
||||
c.setPath("/");
|
||||
response.addCookie(c);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除cookie
|
||||
*
|
||||
* @param key key值
|
||||
* @param response 响应
|
||||
*/
|
||||
public static void delCookie(String key, HttpServletResponse response) {
|
||||
try {
|
||||
Cookie c = new Cookie(key, "");
|
||||
c.setMaxAge(0);
|
||||
response.addCookie(c);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取cookie
|
||||
*
|
||||
* @param key key值
|
||||
* @param request 请求
|
||||
* @return cookie value
|
||||
*/
|
||||
public static String getCookie(String key, HttpServletRequest request) {
|
||||
try {
|
||||
if (request.getCookies() == null) {
|
||||
return null;
|
||||
}
|
||||
for (int i = 0; i < request.getCookies().length; i++) {
|
||||
if (request.getCookies()[i].getName().equals(key)) {
|
||||
return request.getCookies()[i].getValue();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
134
framework/src/main/java/cn/lili/common/utils/CurrencyUtil.java
Normal file
134
framework/src/main/java/cn/lili/common/utils/CurrencyUtil.java
Normal file
@@ -0,0 +1,134 @@
|
||||
package cn.lili.common.utils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 金额计算工具
|
||||
*/
|
||||
public final class CurrencyUtil {
|
||||
/**
|
||||
* 默认除法运算精度
|
||||
*/
|
||||
private static final int DEF_DIV_SCALE = 2;
|
||||
|
||||
/**
|
||||
* 这个类不能实例化
|
||||
*/
|
||||
private CurrencyUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的加法运算。
|
||||
*
|
||||
* @param v1 被加数
|
||||
* @param v2 加数
|
||||
* @return 两个参数的和
|
||||
*/
|
||||
public static Double add(double v1, double v2) {
|
||||
BigDecimal b1 = new BigDecimal(Double.toString(v1));
|
||||
BigDecimal b2 = new BigDecimal(Double.toString(v2));
|
||||
return b1.add(b2).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的减法运算。
|
||||
*
|
||||
* @param v1 被减数
|
||||
* @param v2 减数
|
||||
* @return 两个参数的差
|
||||
*/
|
||||
public static double sub(double v1, double v2) {
|
||||
BigDecimal b1 = new BigDecimal(Double.toString(v1));
|
||||
BigDecimal b2 = new BigDecimal(Double.toString(v2));
|
||||
return b1.subtract(b2).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的乘法运算。
|
||||
*
|
||||
* @param v1 被乘数
|
||||
* @param v2 乘数
|
||||
* @return 两个参数的积
|
||||
*/
|
||||
public static Double mul(double v1, double v2) {
|
||||
BigDecimal b1 = new BigDecimal(Double.toString(v1));
|
||||
BigDecimal b2 = new BigDecimal(Double.toString(v2));
|
||||
return b1.multiply(b2).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供(相对)精确的除法运算,当发生除不尽的情况时, 精确到小数点以后10位,以后的数字四舍五入。
|
||||
*
|
||||
* @param v1 被除数
|
||||
* @param v2 除数
|
||||
* @return 两个参数的商
|
||||
*/
|
||||
public static double div(double v1, double v2) {
|
||||
return div(v1, v2, DEF_DIV_SCALE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供(相对)精确的除法运算。 当发生除不尽的情况时,由scale参数指定精度,以后的数字四舍五入。
|
||||
*
|
||||
* @param v1 被除数
|
||||
* @param v2 除数
|
||||
* @param scale 表示表示需要精确到小数点以后几位。
|
||||
* @return 两个参数的商
|
||||
*/
|
||||
public static double div(double v1, double v2, int scale) {
|
||||
if (scale < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"The scale must be a positive integer or zero");
|
||||
}
|
||||
//如果被除数等于0,则返回0
|
||||
if (v2 == 0) {
|
||||
return 0;
|
||||
}
|
||||
BigDecimal b1 = new BigDecimal(Double.toString(v1));
|
||||
BigDecimal b2 = new BigDecimal(Double.toString(v2));
|
||||
return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的小数位四舍五入处理。
|
||||
*
|
||||
* @param v 需要四舍五入的数字
|
||||
* @param scale 小数点后保留几位
|
||||
* @return 四舍五入后的结果
|
||||
*/
|
||||
public static double round(double v, int scale) {
|
||||
if (scale < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"The scale must be a positive integer or zero");
|
||||
}
|
||||
BigDecimal b = new BigDecimal(Double.toString(v));
|
||||
BigDecimal one = new BigDecimal("1");
|
||||
return b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 金额转分
|
||||
*
|
||||
* @param money
|
||||
* @return
|
||||
*/
|
||||
public static Integer fen(Double money) {
|
||||
double price = mul(money, 100);
|
||||
return (int) price;
|
||||
}
|
||||
|
||||
/**
|
||||
* 金额转分
|
||||
*
|
||||
* @param money
|
||||
* @return
|
||||
*/
|
||||
public static double reversalFen(Double money) {
|
||||
double price = div(money, 100);
|
||||
return price;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(fen(23.4324));
|
||||
}
|
||||
}
|
||||
387
framework/src/main/java/cn/lili/common/utils/DateUtil.java
Normal file
387
framework/src/main/java/cn/lili/common/utils/DateUtil.java
Normal file
@@ -0,0 +1,387 @@
|
||||
package cn.lili.common.utils;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 日期相关的操作
|
||||
*
|
||||
* @author Chopper
|
||||
*/
|
||||
public class DateUtil {
|
||||
|
||||
public static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
public static final String STANDARD_DATE_FORMAT = "yyyy-MM-dd";
|
||||
|
||||
public static final String STANDARD_DATE_NO_UNDERLINE_FORMAT = "yyyyMMdd";
|
||||
|
||||
public static final String FULL_DATE = "yyyyMMddHHmmss";
|
||||
|
||||
|
||||
/**
|
||||
* 当天的开始时间
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static Date startOfTodDayTime() {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.set(Calendar.HOUR_OF_DAY, 0);
|
||||
calendar.set(Calendar.MINUTE, 0);
|
||||
calendar.set(Calendar.SECOND, 0);
|
||||
calendar.set(Calendar.MILLISECOND, 0);
|
||||
return calendar.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 当天的开始时间
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static Date startOfTodDayTime(Date date) {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.set(Calendar.HOUR_OF_DAY, 0);
|
||||
calendar.set(Calendar.MINUTE, 0);
|
||||
calendar.set(Calendar.SECOND, 0);
|
||||
calendar.set(Calendar.MILLISECOND, 0);
|
||||
return calendar.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 当天的开始时间
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static long startOfTodDay() {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.set(Calendar.HOUR_OF_DAY, 0);
|
||||
calendar.set(Calendar.MINUTE, 0);
|
||||
calendar.set(Calendar.SECOND, 0);
|
||||
calendar.set(Calendar.MILLISECOND, 0);
|
||||
Date date = calendar.getTime();
|
||||
return date.getTime() / 1000;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 当天的结束时间
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static Date endOfDate() {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.set(Calendar.HOUR_OF_DAY, 23);
|
||||
calendar.set(Calendar.MINUTE, 59);
|
||||
calendar.set(Calendar.SECOND, 59);
|
||||
calendar.set(Calendar.MILLISECOND, 999);
|
||||
return calendar.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 当天的结束时间
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static Date endOfDate(Date date) {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(date);
|
||||
calendar.set(Calendar.HOUR_OF_DAY, 23);
|
||||
calendar.set(Calendar.MINUTE, 59);
|
||||
calendar.set(Calendar.SECOND, 59);
|
||||
calendar.set(Calendar.MILLISECOND, 999);
|
||||
return calendar.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 某天的年月日
|
||||
*
|
||||
* @param dayUntilNow 距今多少天以前
|
||||
* @return 年月日map key为 year month day
|
||||
*/
|
||||
public static Map<String, Object> getYearMonthAndDay(int dayUntilNow) {
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>(3);
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.set(Calendar.HOUR_OF_DAY, 0);
|
||||
calendar.set(Calendar.MINUTE, 0);
|
||||
calendar.set(Calendar.SECOND, 0);
|
||||
calendar.set(Calendar.MILLISECOND, 0);
|
||||
calendar.add(Calendar.DATE, -dayUntilNow);
|
||||
map.put("year", calendar.get(Calendar.YEAR));
|
||||
map.put("month", calendar.get(Calendar.MONTH) + 1);
|
||||
map.put("day", calendar.get(Calendar.DAY_OF_MONTH));
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将一个字符串转换成日期格式
|
||||
*
|
||||
* @param date 字符串日期
|
||||
* @param pattern 日期格式
|
||||
* @return
|
||||
*/
|
||||
public static Date toDate(String date, String pattern) {
|
||||
if ("".equals("" + date)) {
|
||||
return null;
|
||||
}
|
||||
if (pattern == null) {
|
||||
pattern = STANDARD_DATE_FORMAT;
|
||||
}
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(pattern, Locale.ENGLISH);
|
||||
Date newDate = new Date();
|
||||
try {
|
||||
newDate = sdf.parse(date);
|
||||
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
return newDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上个月的开始结束时间
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static Long[] getLastMonth() {
|
||||
// 取得系统当前时间
|
||||
Calendar cal = Calendar.getInstance();
|
||||
int year = cal.get(Calendar.YEAR);
|
||||
int month = cal.get(Calendar.MONTH) + 1;
|
||||
|
||||
// 取得系统当前时间所在月第一天时间对象
|
||||
cal.set(Calendar.DAY_OF_MONTH, 1);
|
||||
|
||||
// 日期减一,取得上月最后一天时间对象
|
||||
cal.add(Calendar.DAY_OF_MONTH, -1);
|
||||
|
||||
// 输出上月最后一天日期
|
||||
int day = cal.get(Calendar.DAY_OF_MONTH);
|
||||
|
||||
String months = "";
|
||||
String days = "";
|
||||
|
||||
if (month > 1) {
|
||||
month--;
|
||||
} else {
|
||||
year--;
|
||||
month = 12;
|
||||
}
|
||||
if (!(String.valueOf(month).length() > 1)) {
|
||||
months = "0" + month;
|
||||
} else {
|
||||
months = String.valueOf(month);
|
||||
}
|
||||
if (!(String.valueOf(day).length() > 1)) {
|
||||
days = "0" + day;
|
||||
} else {
|
||||
days = String.valueOf(day);
|
||||
}
|
||||
String firstDay = "" + year + "-" + months + "-01";
|
||||
String lastDay = "" + year + "-" + months + "-" + days + " 23:59:59";
|
||||
|
||||
Long[] lastMonth = new Long[2];
|
||||
lastMonth[0] = DateUtil.getDateline(firstDay);
|
||||
lastMonth[1] = DateUtil.getDateline(lastDay, STANDARD_FORMAT);
|
||||
|
||||
return lastMonth;
|
||||
}
|
||||
|
||||
/**
|
||||
* 把日期转换成字符串型
|
||||
*
|
||||
* @param date 日期
|
||||
* @return
|
||||
*/
|
||||
public static String toString(Date date) {
|
||||
return toString(date,STANDARD_FORMAT);
|
||||
}
|
||||
/**
|
||||
* 把日期转换成字符串型
|
||||
*
|
||||
* @param date 日期
|
||||
* @param pattern 类型
|
||||
* @return
|
||||
*/
|
||||
public static String toString(Date date, String pattern) {
|
||||
if (date == null) {
|
||||
return "";
|
||||
}
|
||||
if (pattern == null) {
|
||||
pattern = STANDARD_DATE_FORMAT;
|
||||
}
|
||||
String dateString = "";
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
|
||||
try {
|
||||
dateString = sdf.format(date);
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
return dateString;
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间戳转换成时间类型
|
||||
*
|
||||
* @param time 时间戳
|
||||
* @param pattern 格式
|
||||
* @return
|
||||
*/
|
||||
public static String toString(Long time, String pattern) {
|
||||
if (time > 0) {
|
||||
if (time.toString().length() == 10) {
|
||||
time = time * 1000;
|
||||
}
|
||||
Date date = new Date(time);
|
||||
String str = DateUtil.toString(date, pattern);
|
||||
return str;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前时间是否在某个时间范围
|
||||
*
|
||||
* @param start 开始时间,以秒为单位的时间戳
|
||||
* @param end 结束时间,以秒为单位的时间戳
|
||||
* @return 是否在范围内
|
||||
*/
|
||||
public static boolean inRangeOf(long start, long end) {
|
||||
long now = getDateline();
|
||||
return start <= now && end >= now;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定日期的时间戳
|
||||
*
|
||||
* @param date 指定日期
|
||||
* @return 时间戳
|
||||
*/
|
||||
public static long getDateline(String date) {
|
||||
return toDate(date, STANDARD_DATE_FORMAT).getTime() / 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前时间的时间戳
|
||||
*
|
||||
* @return 时间戳
|
||||
*/
|
||||
public static long getDateline() {
|
||||
return System.currentTimeMillis() / 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前时间格式化字符串
|
||||
*
|
||||
* @return 时间戳
|
||||
*/
|
||||
public static String getCurrentDateStr(String format) {
|
||||
return toString(new Date(), format);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前时间格式化字符串
|
||||
*
|
||||
* @return 格式化的时间
|
||||
*/
|
||||
public static String getCurrentDateStr() {
|
||||
return toString(new Date(), FULL_DATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据日期格式及日期获取时间戳
|
||||
*
|
||||
* @param date 日期
|
||||
* @param pattern 日期格式
|
||||
* @return 时间戳
|
||||
*/
|
||||
public static long getDateline(String date, String pattern) {
|
||||
return toDate(date, pattern).getTime() / 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取几个月之前的日期时间戳
|
||||
*
|
||||
* @param beforeMonth 几个月之前
|
||||
* @return
|
||||
*/
|
||||
public static long getBeforeMonthDateline(int beforeMonth) {
|
||||
SimpleDateFormat format = new SimpleDateFormat(STANDARD_FORMAT);
|
||||
Calendar c = Calendar.getInstance();
|
||||
|
||||
//过去一月
|
||||
c.setTime(new Date());
|
||||
c.add(Calendar.MONTH, (0 - beforeMonth));
|
||||
Date m = c.getTime();
|
||||
String mon = format.format(m);
|
||||
return getDateline(mon, STANDARD_FORMAT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前天的结束时间
|
||||
*
|
||||
* @return 当前天的结束时间
|
||||
*/
|
||||
public static Date getCurrentDayEndTime() {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.set(Calendar.HOUR_OF_DAY, 0);
|
||||
cal.set(Calendar.MINUTE, 0);
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
cal.set(Calendar.DATE, cal.get(Calendar.DATE) + 1);
|
||||
cal.set(Calendar.SECOND, cal.get(Calendar.SECOND) - 1);
|
||||
return cal.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取延时时间(秒)
|
||||
*
|
||||
* @param startTime 开始时间
|
||||
* @return 延时时间(秒)
|
||||
*/
|
||||
public static Integer getDelayTime(Long startTime) {
|
||||
int time = Math.toIntExact((startTime - System.currentTimeMillis()) / 1000);
|
||||
// 如果时间为负数则改为一秒后执行
|
||||
if (time <= 0) {
|
||||
time = 1;
|
||||
}
|
||||
return time;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某年某月开始时间
|
||||
*
|
||||
* @param year 年
|
||||
* @param month 月
|
||||
* @return 开始时间
|
||||
*/
|
||||
public static Date getBeginTime(int year, int month) {
|
||||
YearMonth yearMonth = YearMonth.of(year, month);
|
||||
LocalDate localDate = yearMonth.atDay(1);
|
||||
LocalDateTime startOfDay = localDate.atStartOfDay();
|
||||
ZonedDateTime zonedDateTime = startOfDay.atZone(ZoneId.of("Asia/Shanghai"));
|
||||
|
||||
return Date.from(zonedDateTime.toInstant());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某年某月结束时间
|
||||
*
|
||||
* @param year 年
|
||||
* @param month 月
|
||||
* @return 结束时间
|
||||
*/
|
||||
public static Date getEndTime(int year, int month) {
|
||||
YearMonth yearMonth = YearMonth.of(year, month);
|
||||
LocalDate endOfMonth = yearMonth.atEndOfMonth();
|
||||
LocalDateTime localDateTime = endOfMonth.atTime(23, 59, 59, 999);
|
||||
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of("Asia/Shanghai"));
|
||||
return Date.from(zonedDateTime.toInstant());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package cn.lili.common.utils;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.proxy.HibernateProxy;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 代理对象实例化
|
||||
* @author Chopper
|
||||
*/
|
||||
public class HibernateProxyTypeAdapter extends TypeAdapter<HibernateProxy> {
|
||||
|
||||
public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
|
||||
return (HibernateProxy.class.isAssignableFrom(type.getRawType()) ? (TypeAdapter<T>) new HibernateProxyTypeAdapter(gson) : null);
|
||||
}
|
||||
};
|
||||
private final Gson context;
|
||||
|
||||
private HibernateProxyTypeAdapter(Gson context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HibernateProxy read(JsonReader in) throws IOException {
|
||||
throw new UnsupportedOperationException("Not supported");
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Override
|
||||
public void write(JsonWriter out, HibernateProxy value) throws IOException {
|
||||
if (value == null) {
|
||||
out.nullValue();
|
||||
return;
|
||||
}
|
||||
// Retrieve the original (not proxy) class
|
||||
Class<?> baseType = Hibernate.getClass(value);
|
||||
// Get the TypeAdapter of the original class, to delegate the serialization
|
||||
TypeAdapter delegate = context.getAdapter(TypeToken.get(baseType));
|
||||
// Get a filled instance of the original class
|
||||
Object unproxiedValue = ((HibernateProxy) value).getHibernateLazyInitializer()
|
||||
.getImplementation();
|
||||
// Serialize the value
|
||||
delegate.write(out, unproxiedValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
package cn.lili.common.utils;
|
||||
|
||||
import org.apache.http.*;
|
||||
import org.apache.http.client.HttpRequestRetryHandler;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.protocol.HttpClientContext;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.apache.http.config.Registry;
|
||||
import org.apache.http.config.RegistryBuilder;
|
||||
import org.apache.http.conn.routing.HttpRoute;
|
||||
import org.apache.http.conn.socket.ConnectionSocketFactory;
|
||||
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
|
||||
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
|
||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.URI;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class HttpClientUtils {
|
||||
|
||||
// org.apache.http.impl.client.CloseableHttpClient
|
||||
private static CloseableHttpClient httpClient = null;
|
||||
|
||||
// 这里就直接默认固定了,因为以下三个参数在新建的method中仍然可以重新配置并被覆盖.
|
||||
static final int connectionRequestTimeout = 30000;// ms毫秒,从池中获取链接超时时间
|
||||
static final int connectTimeout = 60000;// ms毫秒,建立链接超时时间
|
||||
static final int socketTimeout = 60000;// ms毫秒,读取超时时间
|
||||
|
||||
// 总配置,主要涉及是以下两个参数,如果要作调整没有用到properties会比较后麻烦,但鉴于一经粘贴,随处可用的特点,就不再做依赖性配置化处理了.
|
||||
// 而且这个参数同一家公司基本不会变动.
|
||||
static final int maxTotal = 500;// 最大总并发,很重要的参数
|
||||
static final int maxPerRoute = 100;// 每路并发,很重要的参数
|
||||
|
||||
// 正常情况这里应该配成MAP或LIST
|
||||
// 细化配置参数,用来对每路参数做精细化处理,可以管控各ip的流量,比如默认配置请求baidu:80端口最大100个并发链接,
|
||||
static final String detailHostName = "http://www.baidu.com";// 每个细化配置之ip(不重要,在特殊场景很有用)
|
||||
// 每个细化配置之port(不重要,在特殊场景很有用)
|
||||
static final int detailPort = 80;
|
||||
// 每个细化配置之最大并发数(不重要,在特殊场景很有用)
|
||||
static final int detailMaxPerRoute = 100;
|
||||
|
||||
private static CloseableHttpClient getHttpClient() {
|
||||
if (null == httpClient) {
|
||||
synchronized (HttpClientUtils.class) {
|
||||
if (null == httpClient) {
|
||||
httpClient = init();
|
||||
}
|
||||
}
|
||||
}
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* 链接池初始化 这里最重要的一点理解就是. 让CloseableHttpClient 一直活在池的世界里, 但是HttpPost却一直用完就消掉.
|
||||
* 这样可以让链接一直保持着.
|
||||
*/
|
||||
private static CloseableHttpClient init() {
|
||||
CloseableHttpClient newHotpoint;
|
||||
|
||||
// 设置连接池
|
||||
ConnectionSocketFactory plainsf = PlainConnectionSocketFactory.getSocketFactory();
|
||||
LayeredConnectionSocketFactory sslsf = SSLConnectionSocketFactory.getSocketFactory();
|
||||
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create().register("http", plainsf).register("https", sslsf).build();
|
||||
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
|
||||
// 将最大连接数增加
|
||||
cm.setMaxTotal(maxTotal);
|
||||
// 将每个路由基础的连接增加
|
||||
cm.setDefaultMaxPerRoute(maxPerRoute);
|
||||
|
||||
// 细化配置开始,其实这里用Map或List的for循环来配置每个链接,在特殊场景很有用.
|
||||
// 将每个路由基础的连接做特殊化配置,一般用不着
|
||||
HttpHost httpHost = new HttpHost(detailHostName, detailPort);
|
||||
// 将目标主机的最大连接数增加
|
||||
cm.setMaxPerRoute(new HttpRoute(httpHost), detailMaxPerRoute);
|
||||
// 细化配置结束
|
||||
|
||||
// 请求重试处理
|
||||
HttpRequestRetryHandler httpRequestRetryHandler = (exception, executionCount, context) -> {
|
||||
if (executionCount >= 2) {// 如果已经重试了2次,就放弃
|
||||
return false;
|
||||
}
|
||||
if (exception instanceof NoHttpResponseException) {// 如果服务器丢掉了连接,那么就重试
|
||||
return true;
|
||||
}
|
||||
if (exception instanceof SSLHandshakeException) {// 不要重试SSL握手异常
|
||||
return false;
|
||||
}
|
||||
if (exception instanceof InterruptedIOException) {// 超时
|
||||
return false;
|
||||
}
|
||||
if (exception instanceof UnknownHostException) {// 目标服务器不可达
|
||||
return false;
|
||||
}
|
||||
if (exception instanceof SSLException) {// SSL握手异常
|
||||
return false;
|
||||
}
|
||||
|
||||
HttpClientContext clientContext = HttpClientContext.adapt(context);
|
||||
HttpRequest request = clientContext.getRequest();
|
||||
// 如果请求是幂等的,就再次尝试
|
||||
return !(request instanceof HttpEntityEnclosingRequest);
|
||||
};
|
||||
|
||||
// 配置请求的超时设置
|
||||
RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(connectionRequestTimeout).setConnectTimeout(connectTimeout).setSocketTimeout(socketTimeout).build();
|
||||
newHotpoint = HttpClients.custom().setConnectionManager(cm).setDefaultRequestConfig(requestConfig).setRetryHandler(httpRequestRetryHandler).build();
|
||||
return newHotpoint;
|
||||
}
|
||||
|
||||
public static String doGet(String url, Map<String, String> param) {
|
||||
|
||||
// httpClient
|
||||
CloseableHttpClient httpClient = getHttpClient();
|
||||
|
||||
String resultString = "";
|
||||
CloseableHttpResponse response = null;
|
||||
try {
|
||||
// 创建uri
|
||||
URIBuilder builder = new URIBuilder(url);
|
||||
if (param != null) {
|
||||
for (String key : param.keySet()) {
|
||||
builder.addParameter(key, param.get(key));
|
||||
}
|
||||
}
|
||||
URI uri = builder.build();
|
||||
|
||||
// 创建http GET请求
|
||||
HttpGet httpGet = new HttpGet(uri);
|
||||
|
||||
// 执行请求
|
||||
response = httpClient.execute(httpGet);
|
||||
// 判断返回状态是否为200
|
||||
if (response.getStatusLine().getStatusCode() == 200) {
|
||||
resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
|
||||
} else {
|
||||
System.out.println(response.getStatusLine().getStatusCode());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (response != null) {
|
||||
response.close();
|
||||
}
|
||||
httpClient.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return resultString;
|
||||
}
|
||||
|
||||
public static String doPost(String url, Map<String, String> param) {
|
||||
// 创建HttpClient对象
|
||||
CloseableHttpClient httpClient = getHttpClient();
|
||||
CloseableHttpResponse response = null;
|
||||
String resultString = "";
|
||||
try {
|
||||
// 创建Http Post请求
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
// 创建参数列表
|
||||
if (param != null) {
|
||||
List<NameValuePair> paramList = new ArrayList<>();
|
||||
for (String key : param.keySet()) {
|
||||
paramList.add(new BasicNameValuePair(key, param.get(key)));
|
||||
}
|
||||
// 模拟表单
|
||||
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList, "utf-8");
|
||||
httpPost.setEntity(entity);
|
||||
}
|
||||
// 执行http请求
|
||||
response = httpClient.execute(httpPost);
|
||||
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
response.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return resultString;
|
||||
}
|
||||
|
||||
|
||||
public static String doPostJson(String url, String json) {
|
||||
// 创建HttpClient对象
|
||||
CloseableHttpClient httpClient = getHttpClient();
|
||||
CloseableHttpResponse response = null;
|
||||
String resultString = "";
|
||||
try {
|
||||
// 创建Http Post请求
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
// 创建请求内容
|
||||
StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
|
||||
httpPost.setEntity(entity);
|
||||
// 执行http请求
|
||||
response = httpClient.execute(httpPost);
|
||||
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
response.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return resultString;
|
||||
}
|
||||
}
|
||||
75
framework/src/main/java/cn/lili/common/utils/IpHelper.java
Normal file
75
framework/src/main/java/cn/lili/common/utils/IpHelper.java
Normal file
@@ -0,0 +1,75 @@
|
||||
package cn.lili.common.utils;
|
||||
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.lili.modules.connect.util.IpUtils;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
|
||||
/**
|
||||
* ip工具
|
||||
*
|
||||
* @author Chopper
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class IpHelper {
|
||||
|
||||
//qq lbs 地区查询key
|
||||
@Value("${lili.lbs.key}")
|
||||
private String key;
|
||||
//qq lbs 地区查询key
|
||||
@Value("${lili.lbs.sk}")
|
||||
private String sk;
|
||||
|
||||
private static final String api = "https://apis.map.qq.com";
|
||||
|
||||
|
||||
/**
|
||||
* 获取IP返回地理信息
|
||||
*
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
public String getIpCity(HttpServletRequest request) {
|
||||
|
||||
String url = "/ws/location/v1/ip?key=" + key + "&ip=" + IpUtils.getIpAddress(request);
|
||||
String sign = SecureUtil.md5(url + sk);
|
||||
url = api + url + "&sign=" + sign;
|
||||
String result = "未知";
|
||||
try {
|
||||
String json = HttpUtil.get(url, 3000);
|
||||
JsonObject jsonObject = JsonParser.parseString(json).getAsJsonObject();
|
||||
String status = jsonObject.get("status").getAsString();
|
||||
if ("0".equals(status)) {
|
||||
JsonObject address = jsonObject.get("result").getAsJsonObject().get("ad_info").getAsJsonObject();
|
||||
String nation = address.get("nation").getAsString();
|
||||
String province = address.get("province").getAsString();
|
||||
String city = address.get("city").getAsString();
|
||||
String district = address.get("district").getAsString();
|
||||
if (StrUtil.isNotBlank(nation) && StrUtil.isBlank(province)) {
|
||||
result = nation;
|
||||
} else {
|
||||
result = province;
|
||||
if (StrUtil.isNotBlank(city)) {
|
||||
result += " " + city;
|
||||
}
|
||||
if (StrUtil.isNotBlank(district)) {
|
||||
result += " " + district;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.info("获取IP地理信息失败");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
59
framework/src/main/java/cn/lili/common/utils/JasyptUtil.java
Normal file
59
framework/src/main/java/cn/lili/common/utils/JasyptUtil.java
Normal file
@@ -0,0 +1,59 @@
|
||||
package cn.lili.common.utils;
|
||||
|
||||
import org.jasypt.encryption.pbe.PooledPBEStringEncryptor;
|
||||
import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
|
||||
|
||||
|
||||
/**
|
||||
* 加密解密
|
||||
* @author Chopper
|
||||
*/
|
||||
public class JasyptUtil {
|
||||
|
||||
/**
|
||||
* Jasypt生成加密结果
|
||||
* @param password 配置文件中设定的加密密码 jasypt.encryptor.password
|
||||
* @param value 待加密值
|
||||
* @return
|
||||
*/
|
||||
public static String encyptPwd(String password,String value){
|
||||
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
|
||||
encryptor.setConfig(cryptor(password));
|
||||
String result = encryptor.encrypt(value);
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* 解密
|
||||
* @param password 配置文件中设定的加密密码 jasypt.encryptor.password
|
||||
* @param value 待解密密文
|
||||
* @return
|
||||
*/
|
||||
public static String decyptPwd(String password,String value){
|
||||
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
|
||||
encryptor.setConfig(cryptor(password));
|
||||
encryptor.decrypt(value);
|
||||
String result = encryptor.decrypt(value);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static SimpleStringPBEConfig cryptor(String password){
|
||||
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
|
||||
config.setPassword(password);
|
||||
config.setAlgorithm("PBEWITHHMACSHA512ANDAES_256");
|
||||
config.setKeyObtentionIterations("1000");
|
||||
config.setPoolSize(1);
|
||||
config.setProviderName("SunJCE");
|
||||
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
|
||||
config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
|
||||
config.setStringOutputType("base64");
|
||||
return config;
|
||||
}
|
||||
|
||||
public static void main(String[] args){
|
||||
|
||||
//加密 若修改了第一个参数加密password记得在配置文件同步修改
|
||||
System.out.println(encyptPwd("jasypt.encryptor.password","123456"));
|
||||
//解密
|
||||
System.out.println(decyptPwd("jasypt.encryptor.password","PYVnAYh+j5C3jkMV1d+myj6JzDaUk7pcfTWUaYsvQdEVkuvIVf7Y0mOU9XkffxT8"));
|
||||
}
|
||||
}
|
||||
57
framework/src/main/java/cn/lili/common/utils/ObjectUtil.java
Normal file
57
framework/src/main/java/cn/lili/common/utils/ObjectUtil.java
Normal file
@@ -0,0 +1,57 @@
|
||||
package cn.lili.common.utils;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.google.gson.Gson;
|
||||
import org.springframework.cglib.beans.BeanMap;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 对象转换工具
|
||||
* @author Chopper
|
||||
*/
|
||||
public class ObjectUtil {
|
||||
|
||||
public static String mapToString(Map<String, String[]> paramMap){
|
||||
|
||||
if (paramMap == null) {
|
||||
return "";
|
||||
}
|
||||
Map<String, Object> params = new HashMap<>(16);
|
||||
for (Map.Entry<String, String[]> param : paramMap.entrySet()) {
|
||||
|
||||
String key = param.getKey();
|
||||
String paramValue = (param.getValue() != null && param.getValue().length > 0 ? param.getValue()[0] : "");
|
||||
String obj = StrUtil.endWithIgnoreCase(param.getKey(), "password") ? "******" : paramValue;
|
||||
params.put(key,obj);
|
||||
}
|
||||
return new Gson().toJson(params);
|
||||
}
|
||||
|
||||
public static String mapToStringAll(Map<String, String[]> paramMap){
|
||||
|
||||
if (paramMap == null) {
|
||||
return "";
|
||||
}
|
||||
Map<String, Object> params = new HashMap<>(16);
|
||||
for (Map.Entry<String, String[]> param : paramMap.entrySet()) {
|
||||
|
||||
String key = param.getKey();
|
||||
String paramValue = (param.getValue() != null && param.getValue().length > 0 ? param.getValue()[0] : "");
|
||||
params.put(key, paramValue);
|
||||
}
|
||||
return new Gson().toJson(params);
|
||||
}
|
||||
|
||||
public static <T> Map<String, Object> beanToMap(T bean) {
|
||||
Map<String, Object> map = new HashMap<>(16);
|
||||
if (bean != null) {
|
||||
BeanMap beanMap = BeanMap.create(bean);
|
||||
for (Object key : beanMap.keySet()) {
|
||||
map.put(key+"", beanMap.get(key));
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package cn.lili.common.utils;
|
||||
|
||||
import cn.lili.common.enums.MessageCode;
|
||||
import cn.lili.common.enums.ResultCode;
|
||||
import cn.lili.common.exception.ServiceException;
|
||||
import cn.lili.common.security.AuthUser;
|
||||
import cn.lili.common.security.context.UserContext;
|
||||
|
||||
/**
|
||||
* 全局统一判定是否可操作某属性
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v1.0
|
||||
* 2020-08-20 18:07
|
||||
*/
|
||||
public class OperationalJudgment<t> {
|
||||
|
||||
/**
|
||||
* 需要判定的对象必须包含属性 memberId,storeId 代表判定的角色
|
||||
*
|
||||
* @param object 判定的对象
|
||||
* @param <t>
|
||||
* @return
|
||||
*/
|
||||
public static <t> t judgment(t object) {
|
||||
return judgment(object, "memberId", "storeId");
|
||||
}
|
||||
|
||||
/**
|
||||
* 需要判定的对象必须包含属性 memberId,storeId 代表判定的角色
|
||||
*
|
||||
* @param object
|
||||
* @param buyerIdField
|
||||
* @param storeIdField
|
||||
* @param <t>
|
||||
* @return 返回判定本身,防止多次查询对象
|
||||
*/
|
||||
public static <t> t judgment(t object, String buyerIdField, String storeIdField) {
|
||||
AuthUser tokenUser = UserContext.getCurrentUser();
|
||||
switch (tokenUser.getRole()) {
|
||||
case MANAGER:
|
||||
return object;
|
||||
case MEMBER:
|
||||
if (tokenUser.getId().equals(BeanUtil.getFieldValueByName(buyerIdField, object))) {
|
||||
return object;
|
||||
} else {
|
||||
throw new ServiceException(ResultCode.USER_AUTHORITY_ERROR);
|
||||
}
|
||||
case STORE:
|
||||
if (tokenUser.getStoreId().equals(BeanUtil.getFieldValueByName(storeIdField, object))) {
|
||||
return object;
|
||||
} else {
|
||||
throw new ServiceException(ResultCode.USER_AUTHORITY_ERROR);
|
||||
}
|
||||
}
|
||||
return object;
|
||||
}
|
||||
}
|
||||
151
framework/src/main/java/cn/lili/common/utils/PageUtil.java
Normal file
151
framework/src/main/java/cn/lili/common/utils/PageUtil.java
Normal file
@@ -0,0 +1,151 @@
|
||||
package cn.lili.common.utils;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.lili.common.vo.PageVO;
|
||||
import cn.lili.common.vo.SearchVO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.OrderItem;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 分页工具
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v4.0
|
||||
* @Description:
|
||||
* @since 2020/11/26 15:23
|
||||
*/
|
||||
public class PageUtil {
|
||||
|
||||
|
||||
/**
|
||||
* Mybatis-Plus分页封装
|
||||
*
|
||||
* @param page
|
||||
* @return
|
||||
*/
|
||||
public static <T> Page<T> initPage(PageVO page) {
|
||||
|
||||
Page<T> p;
|
||||
int pageNumber = page.getPageNumber();
|
||||
int pageSize = page.getPageSize();
|
||||
String sort = page.getSort();
|
||||
String order = page.getOrder();
|
||||
|
||||
if (pageNumber < 1) {
|
||||
pageNumber = 1;
|
||||
}
|
||||
if (pageSize < 1) {
|
||||
pageSize = 10;
|
||||
}
|
||||
if (pageSize > 100) {
|
||||
pageSize = 100;
|
||||
}
|
||||
if (StrUtil.isNotBlank(sort)) {
|
||||
Boolean isAsc = false;
|
||||
if (StrUtil.isBlank(order)) {
|
||||
isAsc = false;
|
||||
} else {
|
||||
if ("desc".equals(order.toLowerCase())) {
|
||||
isAsc = false;
|
||||
} else if ("asc".equals(order.toLowerCase())) {
|
||||
isAsc = true;
|
||||
}
|
||||
}
|
||||
p = new Page<>(pageNumber, pageSize);
|
||||
if (isAsc) {
|
||||
p.addOrder(OrderItem.asc(sort));
|
||||
} else {
|
||||
p.addOrder(OrderItem.desc(sort));
|
||||
}
|
||||
|
||||
} else {
|
||||
p = new Page<>(pageNumber, pageSize);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成条件搜索 全对象对比 equals
|
||||
* 如果需要like 需要另行处理
|
||||
*
|
||||
* @param object
|
||||
* @return
|
||||
*/
|
||||
public static <T> QueryWrapper<T> initWrapper(Object object) {
|
||||
return initWrapper(object, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成条件搜索 全对象对比
|
||||
*
|
||||
* @param object
|
||||
* @param searchVo
|
||||
* @return
|
||||
*/
|
||||
public static <T> QueryWrapper<T> initWrapper(Object object, SearchVO searchVo) {
|
||||
QueryWrapper<T> queryWrapper = new QueryWrapper<>();
|
||||
// 创建时间区间判定
|
||||
if (searchVo != null && StrUtil.isNotBlank(searchVo.getStartDate()) && StrUtil.isNotBlank(searchVo.getEndDate())) {
|
||||
Date start = cn.hutool.core.date.DateUtil.parse(searchVo.getStartDate());
|
||||
Date end = cn.hutool.core.date.DateUtil.parse(searchVo.getEndDate());
|
||||
queryWrapper.between("create_time", start, DateUtil.endOfDay(end));
|
||||
}
|
||||
if (object != null) {
|
||||
String[] fieldNames = BeanUtil.getFiledName(object);
|
||||
//遍历所有属性
|
||||
for (int j = 0; j < fieldNames.length; j++) {
|
||||
//获取属性的名字
|
||||
String key = fieldNames[j];
|
||||
//获取值
|
||||
Object value = BeanUtil.getFieldValueByName(key, object);
|
||||
//如果值不为空才做查询处理
|
||||
if (value != null && !"".equals(value)) {
|
||||
//字段数据库中,驼峰转下划线
|
||||
queryWrapper.eq(StringUtils.camel2Underline(key), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return queryWrapper;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* List 手动分页
|
||||
*
|
||||
* @param page
|
||||
* @param list
|
||||
* @return
|
||||
*/
|
||||
public static <T> List<T> listToPage(PageVO page, List<T> list) {
|
||||
|
||||
int pageNumber = page.getPageNumber() - 1;
|
||||
int pageSize = page.getPageSize();
|
||||
|
||||
if (pageNumber < 0) {
|
||||
pageNumber = 0;
|
||||
}
|
||||
if (pageSize < 1) {
|
||||
pageSize = 10;
|
||||
}
|
||||
if (pageSize > 100) {
|
||||
pageSize = 100;
|
||||
}
|
||||
|
||||
int fromIndex = pageNumber * pageSize;
|
||||
int toIndex = pageNumber * pageSize + pageSize;
|
||||
|
||||
if (fromIndex > list.size()) {
|
||||
return new ArrayList<>();
|
||||
} else if (toIndex >= list.size()) {
|
||||
return list.subList(fromIndex, list.size());
|
||||
} else {
|
||||
return list.subList(fromIndex, toIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
157
framework/src/main/java/cn/lili/common/utils/PasswordUtil.java
Normal file
157
framework/src/main/java/cn/lili/common/utils/PasswordUtil.java
Normal file
@@ -0,0 +1,157 @@
|
||||
package cn.lili.common.utils;
|
||||
|
||||
import java.security.Key;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import javax.crypto.spec.PBEParameterSpec;
|
||||
|
||||
public class PasswordUtil {
|
||||
|
||||
/**
|
||||
* JAVA6支持以下任意一种算法
|
||||
* PBEWITHMD5ANDDES
|
||||
* PBEWITHMD5ANDTRIPLEDES
|
||||
* PBEWITHSHAANDDESEDE
|
||||
* PBEWITHSHA1ANDRC2_40
|
||||
* PBKDF2WITHHMACSHA1
|
||||
* */
|
||||
|
||||
/**
|
||||
* 定义使用的算法为:PBEWITHMD5andDES算法
|
||||
*/
|
||||
public static final String ALGORITHM = "PBEWithMD5AndDES";
|
||||
|
||||
/**
|
||||
* 定义迭代次数为1000次
|
||||
*/
|
||||
private static final int ITERATIONCOUNT = 1000;
|
||||
|
||||
/**
|
||||
* 获取加密算法中使用的盐值,解密中使用的盐值必须与加密中使用的相同才能完成操作.
|
||||
* 盐长度必须为8字节
|
||||
*
|
||||
* @return byte[] 盐值
|
||||
*/
|
||||
public static byte[] getSalt() throws Exception {
|
||||
//实例化安全随机数
|
||||
SecureRandom random = new SecureRandom();
|
||||
//产出盐
|
||||
return random.generateSeed(8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据PBE密码生成一把密钥
|
||||
*
|
||||
* @param password 生成密钥时所使用的密码
|
||||
* @return Key PBE算法密钥
|
||||
*/
|
||||
private static Key getPBEKey(String password) throws Exception {
|
||||
// 实例化使用的算法
|
||||
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
|
||||
// 设置PBE密钥参数
|
||||
PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
|
||||
// 生成密钥
|
||||
SecretKey secretKey = keyFactory.generateSecret(keySpec);
|
||||
|
||||
return secretKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密明文字符串
|
||||
*
|
||||
* @param plaintext 待加密的明文字符串
|
||||
* @param password 生成密钥时所使用的密码
|
||||
* @param salt 盐值
|
||||
* @return 加密后的密文字符串
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String encrypt(String plaintext, String password, byte[] salt) throws Exception {
|
||||
|
||||
Key key = getPBEKey(password);
|
||||
|
||||
PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, ITERATIONCOUNT);
|
||||
|
||||
Cipher cipher = Cipher.getInstance(ALGORITHM);
|
||||
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec);
|
||||
|
||||
byte encipheredData[] = cipher.doFinal(plaintext.getBytes());
|
||||
|
||||
return bytesToHexString(encipheredData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密密文字符串
|
||||
*
|
||||
* @param ciphertext 待解密的密文字符串
|
||||
* @param password 生成密钥时所使用的密码(如需解密,该参数需要与加密时使用的一致)
|
||||
* @param salt 盐值(如需解密,该参数需要与加密时使用的一致)
|
||||
* @return 解密后的明文字符串
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String decrypt(String ciphertext, String password, byte[] salt) throws Exception {
|
||||
|
||||
Key key = getPBEKey(password);
|
||||
|
||||
PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, ITERATIONCOUNT);
|
||||
|
||||
Cipher cipher = Cipher.getInstance(ALGORITHM);
|
||||
|
||||
cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec);
|
||||
|
||||
byte[] passDec = cipher.doFinal(hexStringToBytes(ciphertext));
|
||||
|
||||
return new String(passDec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字节数组转换为十六进制字符串
|
||||
*
|
||||
* @param src 字节数组
|
||||
* @return
|
||||
*/
|
||||
public static String bytesToHexString(byte[] src) {
|
||||
StringBuilder stringBuilder = new StringBuilder("");
|
||||
if (src == null || src.length <= 0) {
|
||||
return null;
|
||||
}
|
||||
for (int i = 0; i < src.length; i++) {
|
||||
int v = src[i] & 0xFF;
|
||||
String hv = Integer.toHexString(v);
|
||||
if (hv.length() < 2) {
|
||||
stringBuilder.append(0);
|
||||
}
|
||||
stringBuilder.append(hv);
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将十六进制字符串转换为字节数组
|
||||
*
|
||||
* @param hexString 十六进制字符串
|
||||
* @return
|
||||
*/
|
||||
public static byte[] hexStringToBytes(String hexString) {
|
||||
if (hexString == null || hexString.equals("")) {
|
||||
return null;
|
||||
}
|
||||
hexString = hexString.toUpperCase();
|
||||
int length = hexString.length() / 2;
|
||||
char[] hexChars = hexString.toCharArray();
|
||||
byte[] d = new byte[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
int pos = i * 2;
|
||||
d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
private static byte charToByte(char c) {
|
||||
return (byte) "0123456789ABCDEF".indexOf(c);
|
||||
}
|
||||
}
|
||||
646
framework/src/main/java/cn/lili/common/utils/RedisUtil.java
Normal file
646
framework/src/main/java/cn/lili/common/utils/RedisUtil.java
Normal file
@@ -0,0 +1,646 @@
|
||||
package cn.lili.common.utils;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.DefaultTypedTuple;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Redis封装工具类
|
||||
*
|
||||
* @author paulG
|
||||
* @since 2020/11/7
|
||||
**/
|
||||
@Component
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class RedisUtil {
|
||||
|
||||
private final RedisTemplate redisTemplate;
|
||||
|
||||
//=============================common============================
|
||||
|
||||
/**
|
||||
* 指定缓存失效时间
|
||||
*
|
||||
* @param key 键
|
||||
* @param time 时间(秒)
|
||||
* @return
|
||||
*/
|
||||
public boolean expire(String key, long time) {
|
||||
try {
|
||||
if (time > 0) {
|
||||
redisTemplate.expire(key, time, TimeUnit.SECONDS);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key 获取过期时间
|
||||
*
|
||||
* @param key 键 不能为null
|
||||
* @return 时间(秒) 返回0代表为永久有效
|
||||
*/
|
||||
public long getExpire(String key) {
|
||||
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断key是否存在
|
||||
*
|
||||
* @param key 键
|
||||
* @return true 存在 false不存在
|
||||
*/
|
||||
public boolean hasKey(String key) {
|
||||
try {
|
||||
return redisTemplate.hasKey(key);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
*
|
||||
* @param key 可以传一个值 或多个
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void del(String... key) {
|
||||
if (key != null && key.length > 0) {
|
||||
if (key.length == 1) {
|
||||
redisTemplate.delete(key[0]);
|
||||
} else {
|
||||
redisTemplate.delete(CollectionUtils.arrayToList(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//============================String=============================
|
||||
|
||||
/**
|
||||
* 普通缓存获取
|
||||
*
|
||||
* @param key 键
|
||||
* @return 值
|
||||
*/
|
||||
public Object get(String key) {
|
||||
return key == null ? null : redisTemplate.opsForValue().get(key);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 普通缓存获取
|
||||
*
|
||||
* @param key 键
|
||||
* @return 值
|
||||
*/
|
||||
public <T> T get(String key, Class<T> clazz) {
|
||||
Object o = key == null ? null : redisTemplate.opsForValue().get(key);
|
||||
return (T) o;
|
||||
}
|
||||
|
||||
/**
|
||||
* 普通缓存放入
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return true成功 false失败
|
||||
*/
|
||||
public boolean set(String key, Object value) {
|
||||
try {
|
||||
redisTemplate.opsForValue().set(key, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 普通缓存放入并设置时间
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
|
||||
* @return true成功 false 失败
|
||||
*/
|
||||
public boolean set(String key, Object value, long time) {
|
||||
try {
|
||||
if (time > 0) {
|
||||
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
|
||||
} else {
|
||||
set(key, value);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递增
|
||||
*
|
||||
* @param key 键
|
||||
* @return
|
||||
*/
|
||||
public long incr(String key, long delta) {
|
||||
if (delta < 0) {
|
||||
throw new RuntimeException("递增因子必须大于0");
|
||||
}
|
||||
return redisTemplate.opsForValue().increment(key, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
* 递减
|
||||
*
|
||||
* @param key 键
|
||||
* @return
|
||||
*/
|
||||
public long decr(String key, long delta) {
|
||||
if (delta < 0) {
|
||||
throw new RuntimeException("递减因子必须大于0");
|
||||
}
|
||||
return redisTemplate.opsForValue().increment(key, -delta);
|
||||
}
|
||||
|
||||
//================================Map=================================
|
||||
|
||||
/**
|
||||
* HashGet
|
||||
*
|
||||
* @param key 键 不能为null
|
||||
* @param item 项 不能为null
|
||||
* @return 值
|
||||
*/
|
||||
public Object hget(String key, String item) {
|
||||
return redisTemplate.opsForHash().get(key, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取hashKey对应的所有键值
|
||||
*
|
||||
* @param key 键
|
||||
* @return 对应的多个键值
|
||||
*/
|
||||
public Map<Object, Object> hmget(String key) {
|
||||
return redisTemplate.opsForHash().entries(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* HashSet
|
||||
*
|
||||
* @param key 键
|
||||
* @param map 对应多个键值
|
||||
* @return true 成功 false 失败
|
||||
*/
|
||||
public boolean hmset(String key, Map<String, Object> map) {
|
||||
try {
|
||||
redisTemplate.opsForHash().putAll(key, map);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* HashSet 并设置时间
|
||||
*
|
||||
* @param key 键
|
||||
* @param map 对应多个键值
|
||||
* @param time 时间(秒)
|
||||
* @return true成功 false失败
|
||||
*/
|
||||
public boolean hmset(String key, Map<String, Object> map, long time) {
|
||||
try {
|
||||
redisTemplate.opsForHash().putAll(key, map);
|
||||
if (time > 0) {
|
||||
expire(key, time);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向一张hash表中放入数据,如果不存在将创建
|
||||
*
|
||||
* @param key 键
|
||||
* @param item 项
|
||||
* @param value 值
|
||||
* @return true 成功 false失败
|
||||
*/
|
||||
public boolean hset(String key, String item, Object value) {
|
||||
try {
|
||||
redisTemplate.opsForHash().put(key, item, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向一张hash表中放入数据,如果不存在将创建
|
||||
*
|
||||
* @param key 键
|
||||
* @param item 项
|
||||
* @param value 值
|
||||
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
|
||||
* @return true 成功 false失败
|
||||
*/
|
||||
public boolean hset(String key, String item, Object value, long time) {
|
||||
try {
|
||||
redisTemplate.opsForHash().put(key, item, value);
|
||||
if (time > 0) {
|
||||
expire(key, time);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除hash表中的值
|
||||
*
|
||||
* @param key 键 不能为null
|
||||
* @param item 项 可以使多个 不能为null
|
||||
*/
|
||||
public void hdel(String key, Object... item) {
|
||||
redisTemplate.opsForHash().delete(key, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断hash表中是否有该项的值
|
||||
*
|
||||
* @param key 键 不能为null
|
||||
* @param item 项 不能为null
|
||||
* @return true 存在 false不存在
|
||||
*/
|
||||
public boolean hHasKey(String key, String item) {
|
||||
return redisTemplate.opsForHash().hasKey(key, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
|
||||
*
|
||||
* @param key 键
|
||||
* @param item 项
|
||||
* @param by 要增加几(大于0)
|
||||
* @return
|
||||
*/
|
||||
public double hincr(String key, String item, double by) {
|
||||
return redisTemplate.opsForHash().increment(key, item, by);
|
||||
}
|
||||
|
||||
/**
|
||||
* hash递减
|
||||
*
|
||||
* @param key 键
|
||||
* @param item 项
|
||||
* @param by 要减少记(小于0)
|
||||
* @return
|
||||
*/
|
||||
public double hdecr(String key, String item, double by) {
|
||||
return redisTemplate.opsForHash().increment(key, item, -by);
|
||||
}
|
||||
|
||||
//============================set=============================
|
||||
|
||||
/**
|
||||
* 根据key获取Set中的所有值
|
||||
*
|
||||
* @param key 键
|
||||
* @return
|
||||
*/
|
||||
public Set<Object> sGet(String key) {
|
||||
try {
|
||||
return redisTemplate.opsForSet().members(key);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据value从一个set中查询,是否存在
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return true 存在 false不存在
|
||||
*/
|
||||
public boolean sHasKey(String key, Object value) {
|
||||
try {
|
||||
return redisTemplate.opsForSet().isMember(key, value);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将数据放入set缓存
|
||||
*
|
||||
* @param key 键
|
||||
* @param values 值 可以是多个
|
||||
* @return 成功个数
|
||||
*/
|
||||
public long sSet(String key, Object... values) {
|
||||
try {
|
||||
return redisTemplate.opsForSet().add(key, values);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将set数据放入缓存
|
||||
*
|
||||
* @param key 键
|
||||
* @param time 时间(秒)
|
||||
* @param values 值 可以是多个
|
||||
* @return 成功个数
|
||||
*/
|
||||
public long sSetAndTime(String key, long time, Object... values) {
|
||||
try {
|
||||
Long count = redisTemplate.opsForSet().add(key, values);
|
||||
if (time > 0) {
|
||||
expire(key, time);
|
||||
}
|
||||
return count;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取set缓存的长度
|
||||
*
|
||||
* @param key 键
|
||||
* @return
|
||||
*/
|
||||
public long sGetSetSize(String key) {
|
||||
try {
|
||||
return redisTemplate.opsForSet().size(key);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除值为value的
|
||||
*
|
||||
* @param key 键
|
||||
* @param values 值 可以是多个
|
||||
* @return 移除的个数
|
||||
*/
|
||||
public long setRemove(String key, Object... values) {
|
||||
try {
|
||||
Long count = redisTemplate.opsForSet().remove(key, values);
|
||||
return count;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
//===============================listByParentId=================================
|
||||
|
||||
/**
|
||||
* 获取list缓存的内容
|
||||
*
|
||||
* @param key 键
|
||||
* @param start 开始
|
||||
* @param end 结束 0 到 -1代表所有值
|
||||
* @return
|
||||
*/
|
||||
public List<Object> lGet(String key, long start, long end) {
|
||||
try {
|
||||
return redisTemplate.opsForList().range(key, start, end);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取list缓存的长度
|
||||
*
|
||||
* @param key 键
|
||||
* @return
|
||||
*/
|
||||
public long lGetListSize(String key) {
|
||||
try {
|
||||
return redisTemplate.opsForList().size(key);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过索引 获取list中的值
|
||||
*
|
||||
* @param key 键
|
||||
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
|
||||
* @return
|
||||
*/
|
||||
public Object lGetIndex(String key, long index) {
|
||||
try {
|
||||
return redisTemplate.opsForList().index(key, index);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将list放入缓存
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return
|
||||
*/
|
||||
public boolean lSet(String key, Object value) {
|
||||
try {
|
||||
redisTemplate.opsForList().rightPush(key, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将list放入缓存
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param time 时间(秒)
|
||||
* @return
|
||||
*/
|
||||
public boolean lSet(String key, Object value, long time) {
|
||||
try {
|
||||
redisTemplate.opsForList().rightPush(key, value);
|
||||
if (time > 0) {
|
||||
expire(key, time);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将list放入缓存
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return
|
||||
*/
|
||||
public boolean lSet(String key, List<Object> value) {
|
||||
try {
|
||||
redisTemplate.opsForList().rightPushAll(key, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将list放入缓存
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return
|
||||
*/
|
||||
public <T> boolean lPush(String key, List<T> value) {
|
||||
try {
|
||||
redisTemplate.opsForList().leftPushAll(key, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将list放入缓存
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param time 时间(秒)
|
||||
* @return
|
||||
*/
|
||||
public boolean lSet(String key, List<Object> value, long time) {
|
||||
try {
|
||||
redisTemplate.opsForList().rightPushAll(key, value);
|
||||
if (time > 0) {
|
||||
expire(key, time);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据索引修改list中的某条数据
|
||||
*
|
||||
* @param key 键
|
||||
* @param index 索引
|
||||
* @param value 值
|
||||
* @return
|
||||
*/
|
||||
public boolean lUpdateIndex(String key, long index, Object value) {
|
||||
try {
|
||||
redisTemplate.opsForList().set(key, index, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除N个值为value
|
||||
*
|
||||
* @param key 键
|
||||
* @param count 移除多少个
|
||||
* @param value 值
|
||||
* @return 移除的个数
|
||||
*/
|
||||
public long lRemove(String key, long count, Object value) {
|
||||
try {
|
||||
Long remove = redisTemplate.opsForList().remove(key, count, value);
|
||||
return remove;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
//===============================ZSet=================================
|
||||
|
||||
/**
|
||||
* 向Zset里添加成员
|
||||
*
|
||||
* @param key
|
||||
* @param score
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public boolean zadd(String key, long score, String value) {
|
||||
Boolean result = redisTemplate.opsForZSet().add(key, value, score);
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取 某key 下 某一分值区间的队列
|
||||
*
|
||||
* @param key
|
||||
* @param from
|
||||
* @param to
|
||||
* @return
|
||||
*/
|
||||
public Set<DefaultTypedTuple> zrangeByScoreWithScores(String key, int from, long to) {
|
||||
Set<DefaultTypedTuple> set = redisTemplate.opsForZSet().rangeByScoreWithScores(key, from, to);
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除 Zset队列值
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public Long zremove(String key, String... value) {
|
||||
return redisTemplate.opsForZSet().remove(key, value);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package cn.lili.common.utils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 用户名验证工具类
|
||||
* @author Chopper
|
||||
*/
|
||||
public class RegularUtil {
|
||||
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
private static final Pattern mobile = Pattern.compile("^1[3|4|5|8][0-9]\\d{8}$");
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
private static final Pattern email = Pattern.compile("^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*\\.[a-zA-Z0-9]{2,6}$");
|
||||
|
||||
public static boolean Mobile(String v){
|
||||
|
||||
Matcher m = mobile.matcher(v);
|
||||
if(m.matches()){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean Email(String v){
|
||||
|
||||
Matcher m = email.matcher(v);
|
||||
if(m.matches()){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
125
framework/src/main/java/cn/lili/common/utils/ResponseUtil.java
Normal file
125
framework/src/main/java/cn/lili/common/utils/ResponseUtil.java
Normal file
@@ -0,0 +1,125 @@
|
||||
package cn.lili.common.utils;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* response 输出响应工具
|
||||
*
|
||||
* @author Chopper
|
||||
*/
|
||||
@Slf4j
|
||||
public class ResponseUtil {
|
||||
|
||||
static final String ENCODING = "UTF-8";
|
||||
static final String CONTENT_TYPE = "application/json;charset=UTF-8";
|
||||
|
||||
/**
|
||||
* 输出前端内容以及状态指定
|
||||
*
|
||||
* @param response
|
||||
* @param status
|
||||
* @param content
|
||||
*/
|
||||
public static void output(HttpServletResponse response, Integer status, String content) {
|
||||
ServletOutputStream servletOutputStream = null;
|
||||
try {
|
||||
response.setCharacterEncoding(ENCODING);
|
||||
response.setContentType(CONTENT_TYPE);
|
||||
response.setStatus(status);
|
||||
servletOutputStream = response.getOutputStream();
|
||||
servletOutputStream.write(content.getBytes());
|
||||
} catch (Exception e) {
|
||||
log.error("response output error:", e);
|
||||
} finally {
|
||||
if (servletOutputStream != null) {
|
||||
try {
|
||||
servletOutputStream.flush();
|
||||
servletOutputStream.close();
|
||||
} catch (IOException e) {
|
||||
log.error("response output IO close error:", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* response 输出JSON
|
||||
*
|
||||
* @param response
|
||||
* @param status response 状态
|
||||
* @param resultMap
|
||||
*/
|
||||
public static void output(HttpServletResponse response, Integer status, Map<String, Object> resultMap) {
|
||||
response.setStatus(status);
|
||||
output(response, resultMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* response 输出JSON
|
||||
*
|
||||
* @param response
|
||||
* @param resultMap
|
||||
*/
|
||||
public static void output(HttpServletResponse response, Map<String, Object> resultMap) {
|
||||
ServletOutputStream servletOutputStream = null;
|
||||
try {
|
||||
response.setCharacterEncoding(ENCODING);
|
||||
response.setContentType(CONTENT_TYPE);
|
||||
servletOutputStream = response.getOutputStream();
|
||||
servletOutputStream.write(new Gson().toJson(resultMap).getBytes());
|
||||
} catch (Exception e) {
|
||||
log.error("response output error:", e);
|
||||
} finally {
|
||||
if (servletOutputStream != null) {
|
||||
try {
|
||||
servletOutputStream.flush();
|
||||
servletOutputStream.close();
|
||||
} catch (IOException e) {
|
||||
log.error("response output IO close error:", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造响应
|
||||
*
|
||||
* @param flag
|
||||
* @param code
|
||||
* @param msg
|
||||
* @return
|
||||
*/
|
||||
public static Map<String, Object> resultMap(boolean flag, Integer code, String msg) {
|
||||
return resultMap(flag, code, msg, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造响应
|
||||
*
|
||||
* @param flag
|
||||
* @param code
|
||||
* @param msg
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
public static Map<String, Object> resultMap(boolean flag, Integer code, String msg, Object data) {
|
||||
|
||||
Map<String, Object> resultMap = new HashMap<String, Object>(16);
|
||||
resultMap.put("success", flag);
|
||||
resultMap.put("message", msg);
|
||||
resultMap.put("code", code);
|
||||
resultMap.put("timestamp", System.currentTimeMillis());
|
||||
if (data != null) {
|
||||
resultMap.put("result", data);
|
||||
}
|
||||
return resultMap;
|
||||
}
|
||||
}
|
||||
114
framework/src/main/java/cn/lili/common/utils/ResultUtil.java
Normal file
114
framework/src/main/java/cn/lili/common/utils/ResultUtil.java
Normal file
@@ -0,0 +1,114 @@
|
||||
package cn.lili.common.utils;
|
||||
|
||||
|
||||
import cn.lili.common.enums.ResultCode;
|
||||
import cn.lili.common.vo.ResultMessage;
|
||||
|
||||
/**
|
||||
* @author lili
|
||||
*/
|
||||
public class ResultUtil<T> {
|
||||
|
||||
/**
|
||||
* 抽象类,存放结果
|
||||
*/
|
||||
private final ResultMessage<T> resultMessage;
|
||||
/**
|
||||
* 正常响应
|
||||
*/
|
||||
private static final Integer SUCCESS = 200;
|
||||
/**
|
||||
* 业务异常
|
||||
*/
|
||||
private static final Integer ERROR = 400;
|
||||
|
||||
|
||||
/**
|
||||
* 构造话方法,给响应结果默认值
|
||||
*/
|
||||
public ResultUtil() {
|
||||
resultMessage = new ResultMessage<>();
|
||||
resultMessage.setSuccess(true);
|
||||
resultMessage.setMessage("success");
|
||||
resultMessage.setCode(SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回数据
|
||||
*
|
||||
* @param t
|
||||
* @return
|
||||
*/
|
||||
public ResultMessage<T> setData(T t) {
|
||||
this.resultMessage.setResult(t);
|
||||
return this.resultMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务器异常 追加状态码
|
||||
*
|
||||
* @param resultCode 返回码
|
||||
*/
|
||||
public ResultMessage<T> setErrorMsg(ResultCode resultCode) {
|
||||
this.resultMessage.setSuccess(false);
|
||||
this.resultMessage.setMessage(resultCode.message());
|
||||
this.resultMessage.setCode(resultCode.code());
|
||||
return this.resultMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务器异常 追加状态码
|
||||
* @param code 状态码
|
||||
* @param msg 返回消息
|
||||
*/
|
||||
public ResultMessage<T> setErrorMsg(Integer code, String msg) {
|
||||
this.resultMessage.setSuccess(false);
|
||||
this.resultMessage.setMessage(msg);
|
||||
this.resultMessage.setCode(code);
|
||||
return this.resultMessage;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 返回成功消息
|
||||
*
|
||||
* @param resultCode 返回码
|
||||
* @return 返回成功消息
|
||||
*/
|
||||
public ResultMessage<T> setSuccessMsg(ResultCode resultCode) {
|
||||
this.resultMessage.setSuccess(true);
|
||||
this.resultMessage.setMessage(resultCode.message());
|
||||
this.resultMessage.setCode(resultCode.code());
|
||||
return this.resultMessage;
|
||||
|
||||
}
|
||||
|
||||
//抽象静态方法,返回结果集
|
||||
public static <T> ResultMessage<T> data(T t) {
|
||||
return new ResultUtil<T>().setData(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回成功
|
||||
* @param resultCode 返回状态码
|
||||
*/
|
||||
public static <T> ResultMessage<T> success(ResultCode resultCode) {
|
||||
return new ResultUtil<T>().setSuccessMsg(resultCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回失败
|
||||
* @param resultCode 返回状态码
|
||||
*/
|
||||
public static <T> ResultMessage<T> error(ResultCode resultCode) {
|
||||
return new ResultUtil<T>().setErrorMsg(resultCode);
|
||||
}
|
||||
/**
|
||||
* 返回失败
|
||||
* @param code 状态码
|
||||
* @param msg 返回消息
|
||||
*/
|
||||
public static <T> ResultMessage<T> error(Integer code, String msg) {
|
||||
return new ResultUtil<T>().setErrorMsg(code, msg);
|
||||
}
|
||||
}
|
||||
41
framework/src/main/java/cn/lili/common/utils/SnowFlake.java
Normal file
41
framework/src/main/java/cn/lili/common/utils/SnowFlake.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package cn.lili.common.utils;
|
||||
|
||||
import cn.hutool.core.lang.Snowflake;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 雪花分布式id获取
|
||||
*
|
||||
* @author Chopper
|
||||
*/
|
||||
public class SnowFlake {
|
||||
|
||||
/**
|
||||
* 机器id
|
||||
*/
|
||||
private static long workerId = 0L;
|
||||
/**
|
||||
* 机房id
|
||||
*/
|
||||
private static long datacenterId = 0L;
|
||||
|
||||
private static Snowflake snowflake = IdUtil.createSnowflake(workerId, datacenterId);
|
||||
|
||||
public static long getId() {
|
||||
return snowflake.nextId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成字符,带有前缀
|
||||
* @param prefix
|
||||
* @return
|
||||
*/
|
||||
public static String createStr(String prefix) {
|
||||
return prefix + DateUtil.toString(new Date(), "yyyyMMdd") + SnowFlake.getId();
|
||||
}
|
||||
public static String getIdStr() {
|
||||
return snowflake.nextId() + "";
|
||||
}
|
||||
}
|
||||
76
framework/src/main/java/cn/lili/common/utils/SpelUtil.java
Normal file
76
framework/src/main/java/cn/lili/common/utils/SpelUtil.java
Normal file
@@ -0,0 +1,76 @@
|
||||
package cn.lili.common.utils;
|
||||
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
|
||||
/**
|
||||
* SpelUtil
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v1.0
|
||||
* 2021-01-11 10:45
|
||||
*/
|
||||
public class SpelUtil {
|
||||
|
||||
|
||||
// spel表达式解析器
|
||||
private static SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
|
||||
|
||||
// 参数名发现器
|
||||
private static DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
|
||||
|
||||
/**
|
||||
* 转换 jspl参数
|
||||
*
|
||||
* @param joinPoint
|
||||
* @param spel
|
||||
* @return
|
||||
*/
|
||||
public static String compileParams(JoinPoint joinPoint, String spel) { // Spel表达式解析日志信息
|
||||
// 获得方法参数名数组
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
|
||||
String[] parameterNames = parameterNameDiscoverer.getParameterNames(signature.getMethod());
|
||||
if (parameterNames != null && parameterNames.length > 0) {
|
||||
EvaluationContext context = new StandardEvaluationContext();
|
||||
|
||||
//获取方法参数值
|
||||
Object[] args = joinPoint.getArgs();
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
context.setVariable(parameterNames[i], args[i]); // 替换spel里的变量值为实际值, 比如 #user --> user对象
|
||||
}
|
||||
return spelExpressionParser.parseExpression(spel).getValue(context).toString();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换 jspl参数
|
||||
*
|
||||
* @param joinPoint
|
||||
* @param spel
|
||||
* @return
|
||||
*/
|
||||
public static String compileParams(JoinPoint joinPoint, Object rvt, String spel) { // Spel表达式解析日志信息
|
||||
// 获得方法参数名数组
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
|
||||
String[] parameterNames = parameterNameDiscoverer.getParameterNames(signature.getMethod());
|
||||
if (parameterNames != null && parameterNames.length > 0) {
|
||||
EvaluationContext context = new StandardEvaluationContext();
|
||||
|
||||
//获取方法参数值
|
||||
Object[] args = joinPoint.getArgs();
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
context.setVariable(parameterNames[i], args[i]); // 替换spel里的变量值为实际值, 比如 #user --> user对象
|
||||
}
|
||||
context.setVariable("rvt", rvt);
|
||||
return spelExpressionParser.parseExpression(spel).getValue(context).toString();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package cn.lili.common.utils;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author Chopper
|
||||
*/
|
||||
@Component
|
||||
public class SpringContextUtil implements ApplicationContextAware {
|
||||
|
||||
/**
|
||||
* 上下文对象实例
|
||||
*/
|
||||
private static ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext appContext) throws BeansException {
|
||||
applicationContext = appContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取applicationContext
|
||||
* @return spring applicationContext
|
||||
*/
|
||||
public static ApplicationContext getApplicationContext() {
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过name获取 Bean.
|
||||
* @param name bean的名字
|
||||
* @return bean实例
|
||||
*/
|
||||
public static Object getBean(String name){
|
||||
return getApplicationContext().getBean(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过class获取Bean.
|
||||
* @param clazz bean的类型
|
||||
* @param <T> bean的类型
|
||||
* @return bean实例
|
||||
*/
|
||||
public static <T> T getBean(Class<T> clazz){
|
||||
return getApplicationContext().getBean(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过name,以及Clazz返回指定的Bean
|
||||
* @param name bean的名字
|
||||
* @param clazz bean的类型
|
||||
* @param <T> bean的类型
|
||||
* @return bean实例
|
||||
*/
|
||||
public static <T> T getBean(String name,Class<T> clazz){
|
||||
return getApplicationContext().getBean(name, clazz);
|
||||
}
|
||||
|
||||
}
|
||||
255
framework/src/main/java/cn/lili/common/utils/StringUtils.java
Normal file
255
framework/src/main/java/cn/lili/common/utils/StringUtils.java
Normal file
@@ -0,0 +1,255 @@
|
||||
package cn.lili.common.utils;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* 字串工具类
|
||||
*
|
||||
* @author pikachu
|
||||
*/
|
||||
public class StringUtils extends StrUtil {
|
||||
|
||||
/**
|
||||
* MD5加密方法
|
||||
*
|
||||
* @param str String
|
||||
* @return String
|
||||
*/
|
||||
public static String md5(String str) {
|
||||
MessageDigest messageDigest = null;
|
||||
try {
|
||||
messageDigest = MessageDigest.getInstance("MD5");
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
byte[] resultByte = messageDigest.digest(str.getBytes());
|
||||
StringBuffer result = new StringBuffer();
|
||||
for (int i = 0; i < resultByte.length; ++i) {
|
||||
int v = 0xFF & resultByte[i];
|
||||
if (v < 16) {
|
||||
result.append("0");
|
||||
}
|
||||
result.append(Integer.toHexString(v));
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将object转为数字
|
||||
*
|
||||
* @param obj 需要转object的对象
|
||||
* @param checked 如果为true格式不正确抛出异常
|
||||
* @return
|
||||
*/
|
||||
public static int toInt(Object obj, boolean checked) {
|
||||
int value = 0;
|
||||
if (obj == null) {
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
value = Integer.parseInt(obj.toString());
|
||||
} catch (Exception ex) {
|
||||
if (checked) {
|
||||
throw new RuntimeException("整型数字格式不正确");
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将一个字串转为long,如果无空,则返回默认值
|
||||
*
|
||||
* @param str 要转换的数字字串
|
||||
* @param defaultValue 默认值
|
||||
* @return
|
||||
*/
|
||||
public static Long toLong(String str, Long defaultValue) {
|
||||
Long value = defaultValue;
|
||||
if (str == null || "".equals(str)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
value = Long.parseLong(str);
|
||||
} catch (Exception ex) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将一个object转为double 如果object 为 null 则返回0;
|
||||
*
|
||||
* @param obj 需要转成Double的对象
|
||||
* @param checked 如果为true格式不正确抛出异常
|
||||
* @return
|
||||
*/
|
||||
public static Double toDouble(Object obj, boolean checked) {
|
||||
Double value = 0d;
|
||||
if (obj == null) {
|
||||
if (checked) {
|
||||
throw new RuntimeException("数字格式不正确");
|
||||
} else {
|
||||
return 0D;
|
||||
}
|
||||
}
|
||||
try {
|
||||
value = Double.valueOf(obj.toString());
|
||||
} catch (Exception ex) {
|
||||
if (checked) {
|
||||
throw new RuntimeException("数字格式不正确");
|
||||
} else {
|
||||
return 0D;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将一个字串转为Double,如果无空,则返回默认值
|
||||
*
|
||||
* @param str 要转换的数字字串
|
||||
* @param defaultValue 默认值
|
||||
* @return
|
||||
*/
|
||||
public static Double toDouble(String str, Double defaultValue) {
|
||||
Double value = defaultValue;
|
||||
if (str == null || "".equals(str)) {
|
||||
return 0d;
|
||||
}
|
||||
try {
|
||||
value = Double.valueOf(str);
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
value = defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取随机数
|
||||
*
|
||||
* @param n 随机次数
|
||||
* @return
|
||||
*/
|
||||
public static String getRandStr(int n) {
|
||||
Random random = new Random();
|
||||
String sRand = "";
|
||||
for (int i = 0; i < n; i++) {
|
||||
String rand = String.valueOf(random.nextInt(10));
|
||||
sRand += rand;
|
||||
}
|
||||
return sRand;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断一个数组是否为空,并且长度大于0
|
||||
*
|
||||
* @param list
|
||||
* @return true 不空/false 空
|
||||
*/
|
||||
public static boolean isNotEmpty(List list) {
|
||||
|
||||
return list != null && list.size() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 切个字符串,如果超出长度则切割
|
||||
*
|
||||
* @param var
|
||||
* @param size
|
||||
* @return
|
||||
*/
|
||||
public static String subStringLength(String var, Integer size) {
|
||||
if (var.length() > size) {
|
||||
return var.substring(0, (size - 4)).toString() + "...";
|
||||
}
|
||||
return var;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对象转map
|
||||
*
|
||||
* @param obj
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static Map<String, Object> objectToMap(Object obj) throws Exception {
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
|
||||
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
|
||||
for (PropertyDescriptor property : propertyDescriptors) {
|
||||
String key = property.getName();
|
||||
if (key.compareToIgnoreCase("class") == 0) {
|
||||
continue;
|
||||
}
|
||||
Method getter = property.getReadMethod();
|
||||
Object value = getter != null ? getter.invoke(obj) : null;
|
||||
map.put(key, value);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 驼峰法转下划线
|
||||
*/
|
||||
public static String camel2Underline(String str) {
|
||||
|
||||
if (StrUtil.isBlank(str)) {
|
||||
return "";
|
||||
}
|
||||
if (str.length() == 1) {
|
||||
return str.toLowerCase();
|
||||
}
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (int i = 1; i < str.length(); i++) {
|
||||
if (Character.isUpperCase(str.charAt(i))) {
|
||||
sb.append("_" + Character.toLowerCase(str.charAt(i)));
|
||||
} else {
|
||||
sb.append(str.charAt(i));
|
||||
}
|
||||
}
|
||||
return (str.charAt(0) + sb.toString()).toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果给定字符串{@code str}中不包含{@code appendStr},则在{@code str}后追加{@code appendStr};
|
||||
* 如果已包含{@code appendStr},则在{@code str}后追加{@code otherwise}
|
||||
*
|
||||
* @param str 给定的字符串
|
||||
* @param appendStr 需要追加的内容
|
||||
* @param otherwise 当{@code appendStr}不满足时追加到{@code str}后的内容
|
||||
* @return 追加后的字符串
|
||||
*/
|
||||
public static String appendIfNotContain(String str, String appendStr, String otherwise) {
|
||||
if (isEmpty(str) || isEmpty(appendStr)) {
|
||||
return str;
|
||||
}
|
||||
if (str.contains(appendStr)) {
|
||||
return str.concat(otherwise);
|
||||
}
|
||||
return str.concat(appendStr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package cn.lili.common.utils;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* @author Chopper
|
||||
*/
|
||||
public class ThreadPoolUtil {
|
||||
|
||||
/**
|
||||
* 核心线程数,会一直存活,即使没有任务,线程池也会维护线程的最少数量
|
||||
*/
|
||||
private static final int SIZE_CORE_POOL = 5;
|
||||
/**
|
||||
* 线程池维护线程的最大数量
|
||||
*/
|
||||
private static final int SIZE_MAX_POOL = 10;
|
||||
/**
|
||||
* 线程池维护线程所允许的空闲时间
|
||||
*/
|
||||
private static final long ALIVE_TIME = 2000;
|
||||
/**
|
||||
* 线程缓冲队列
|
||||
*/
|
||||
private static final BlockingQueue<Runnable> bqueue = new ArrayBlockingQueue<Runnable>(100);
|
||||
private static final ThreadPoolExecutor pool = new ThreadPoolExecutor(SIZE_CORE_POOL, SIZE_MAX_POOL, ALIVE_TIME, TimeUnit.MILLISECONDS, bqueue, new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
public static ThreadPoolExecutor threadPool;
|
||||
|
||||
static {
|
||||
pool.prestartAllCoreThreads();
|
||||
}
|
||||
|
||||
/**
|
||||
* 无返回值直接执行, 管他娘的
|
||||
*
|
||||
* @param runnable
|
||||
*/
|
||||
public static void execute(Runnable runnable) {
|
||||
getThreadPool().execute(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回值直接执行, 管他娘的
|
||||
*
|
||||
* @param callable
|
||||
*/
|
||||
public static <T> Future<T> submit(Callable<T> callable) {
|
||||
return getThreadPool().submit(callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* dcs获取线程池
|
||||
*
|
||||
* @return 线程池对象
|
||||
*/
|
||||
public static ThreadPoolExecutor getThreadPool() {
|
||||
if (threadPool != null) {
|
||||
return threadPool;
|
||||
} else {
|
||||
synchronized (ThreadPoolUtil.class) {
|
||||
if (threadPool == null) {
|
||||
threadPool = (ThreadPoolExecutor) Executors.newCachedThreadPool();
|
||||
return threadPool;
|
||||
}
|
||||
return threadPool;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ThreadPoolExecutor getPool() {
|
||||
return pool;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(pool.getPoolSize());
|
||||
}
|
||||
}
|
||||
102
framework/src/main/java/cn/lili/common/utils/UrlBuilder.java
Normal file
102
framework/src/main/java/cn/lili/common/utils/UrlBuilder.java
Normal file
@@ -0,0 +1,102 @@
|
||||
package cn.lili.common.utils;
|
||||
|
||||
import com.xkcoding.http.util.MapUtil;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 构造URL
|
||||
* </p>
|
||||
*
|
||||
* @author yangkai.shen (https://xkcoding.com)
|
||||
* @since 1.9.0
|
||||
*/
|
||||
@Setter
|
||||
public class UrlBuilder {
|
||||
|
||||
private final Map<String, String> params = new LinkedHashMap<>(7);
|
||||
private String baseUrl;
|
||||
|
||||
private UrlBuilder() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param baseUrl 基础路径
|
||||
* @return the new {@code UrlBuilder}
|
||||
*/
|
||||
public static UrlBuilder fromBaseUrl(String baseUrl) {
|
||||
UrlBuilder builder = new UrlBuilder();
|
||||
builder.setBaseUrl(baseUrl);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 只读的参数Map
|
||||
*
|
||||
* @return unmodifiable Map
|
||||
* @since 1.15.0
|
||||
*/
|
||||
public Map<String, Object> getReadOnlyParams() {
|
||||
return Collections.unmodifiableMap(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加参数
|
||||
*
|
||||
* @param key 参数名称
|
||||
* @param value 参数值
|
||||
* @return this UrlBuilder
|
||||
*/
|
||||
public UrlBuilder queryParam(String key, Object value) {
|
||||
if (StringUtils.isEmpty(key)) {
|
||||
throw new RuntimeException("参数名不能为空");
|
||||
}
|
||||
String valueAsString = (value != null ? value.toString() : null);
|
||||
this.params.put(key, valueAsString);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加参数
|
||||
*
|
||||
* @param value 参数值
|
||||
* @return this UrlBuilder
|
||||
*/
|
||||
public UrlBuilder pathAppend(String value) {
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
throw new RuntimeException("参数不能为空");
|
||||
}
|
||||
this.setBaseUrl(this.baseUrl += value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造url
|
||||
*
|
||||
* @return url
|
||||
*/
|
||||
public String build() {
|
||||
return this.build(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造url
|
||||
*
|
||||
* @param encode 转码
|
||||
* @return url
|
||||
*/
|
||||
public String build(boolean encode) {
|
||||
if (MapUtil.isEmpty(this.params)) {
|
||||
return this.baseUrl;
|
||||
}
|
||||
String baseUrl = StringUtils.appendIfNotContain(this.baseUrl, "?", "&");
|
||||
String paramString = MapUtil.parseMapToString(this.params, encode);
|
||||
return baseUrl + paramString;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package cn.lili.common.utils;
|
||||
|
||||
|
||||
import eu.bitwalker.useragentutils.DeviceType;
|
||||
import eu.bitwalker.useragentutils.UserAgent;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 客户端类型统计
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v1.0
|
||||
* 2021-02-26 10:53
|
||||
*/
|
||||
public class UserAgentUtils {
|
||||
|
||||
/**
|
||||
* 获取设备类型
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public static DeviceType getDeviceType(HttpServletRequest request) {
|
||||
return getUserAgent(request).getOperatingSystem().getDeviceType();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是PC
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public static boolean isComputer(HttpServletRequest request) {
|
||||
return DeviceType.COMPUTER.equals(getDeviceType(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是手机
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public static boolean isMobile(HttpServletRequest request) {
|
||||
return DeviceType.MOBILE.equals(getDeviceType(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是平板
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public static boolean isTablet(HttpServletRequest request) {
|
||||
return DeviceType.TABLET.equals(getDeviceType(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是手机和平板
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public static boolean isMobileOrTablet(HttpServletRequest request) {
|
||||
DeviceType deviceType = getDeviceType(request);
|
||||
return DeviceType.MOBILE.equals(deviceType) || DeviceType.TABLET.equals(deviceType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户代理对象
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public static UserAgent getUserAgent(HttpServletRequest request) {
|
||||
return UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package cn.lili.common.validation;
|
||||
|
||||
import cn.lili.common.validation.impl.MobileValidator;
|
||||
|
||||
import javax.validation.Constraint;
|
||||
import javax.validation.Payload;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* 手机号码校验注解
|
||||
*
|
||||
* @author Bulbasaur
|
||||
*/
|
||||
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
|
||||
@Retention(RUNTIME)
|
||||
@Documented
|
||||
@Constraint(validatedBy = {MobileValidator.class})
|
||||
public @interface Mobile {
|
||||
|
||||
String regexp() default "1[3|4|5|7|8]\\d{9}";
|
||||
|
||||
String message() default "手机号码格式不正确";
|
||||
|
||||
Class<?>[] groups() default {};
|
||||
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package cn.lili.common.validation.impl;
|
||||
|
||||
import cn.lili.common.validation.Mobile;
|
||||
|
||||
import javax.validation.ConstraintValidator;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
public class MobileValidator implements ConstraintValidator<Mobile, String> {
|
||||
|
||||
private static Pattern pattern = Pattern.compile("^0?(13[0-9]|14[0-9]|15[0-9]|16[0-9]|17[0-9]|18[0-9]|19[0-9])[0-9]{8}$");
|
||||
|
||||
@Override
|
||||
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
|
||||
Matcher m = pattern.matcher(value);
|
||||
return m.matches();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(Mobile constraintAnnotation) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package cn.lili.common.verification;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* ImageUtil
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v1.0
|
||||
* 2020-11-17 14:50
|
||||
*/
|
||||
public class ImageUtil {
|
||||
|
||||
/**
|
||||
* 添加水印
|
||||
*
|
||||
* @param oriImage
|
||||
* @param text
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void addWatermark(BufferedImage oriImage, String text) {
|
||||
Graphics2D graphics2D = oriImage.createGraphics();
|
||||
// 设置水印文字颜色
|
||||
graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
// 设置水印文字Font
|
||||
graphics2D.setColor(Color.black);
|
||||
// 设置水印文字透明度
|
||||
graphics2D.setFont(new Font("宋体", Font.BOLD, 30));
|
||||
// 第一参数->设置的内容,后面两个参数->文字在图片上的坐标位置(x,y)
|
||||
graphics2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.4f));
|
||||
graphics2D.drawString(text, 10, 40);
|
||||
graphics2D.dispose();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param oriImage 原图
|
||||
* @param templateImage 模板图
|
||||
* @param newImage 新抠出的小图
|
||||
* @param x 随机扣取坐标X
|
||||
* @param y 随机扣取坐标y
|
||||
*/
|
||||
public static void cutByTemplate(BufferedImage oriImage, BufferedImage templateImage, BufferedImage newImage,
|
||||
int x, int y) {
|
||||
// 临时数组遍历用于高斯模糊存周边像素值
|
||||
int[][] matrix = new int[3][3];
|
||||
int[] values = new int[9];
|
||||
|
||||
int xLength = templateImage.getWidth();
|
||||
int yLength = templateImage.getHeight();
|
||||
// 模板图像宽度
|
||||
for (int i = 0; i < xLength; i++) {
|
||||
// 模板图片高度
|
||||
for (int j = 0; j < yLength; j++) {
|
||||
// 如果模板图像当前像素点不是透明色 copy源文件信息到目标图片中
|
||||
int rgb = templateImage.getRGB(i, j);
|
||||
if (rgb < 0) {
|
||||
newImage.setRGB(i, j, oriImage.getRGB(x + i, y + j));
|
||||
|
||||
// 抠图区域高斯模糊
|
||||
readPixel(oriImage, x + i, y + j, values);
|
||||
fillMatrix(matrix, values);
|
||||
oriImage.setRGB(x + i, y + j, avgMatrix(matrix));
|
||||
}
|
||||
|
||||
// 防止数组越界判断
|
||||
if (i == (xLength - 1) || j == (yLength - 1)) {
|
||||
continue;
|
||||
}
|
||||
int rightRgb = templateImage.getRGB(i + 1, j);
|
||||
int downRgb = templateImage.getRGB(i, j + 1);
|
||||
// 描边处理,,取带像素和无像素的界点,判断该点是不是临界轮廓点,如果是设置该坐标像素是白色
|
||||
if ((rgb >= 0 && rightRgb < 0) || (rgb < 0 && rightRgb >= 0) || (rgb >= 0 && downRgb < 0)
|
||||
|| (rgb < 0 && downRgb >= 0)) {
|
||||
|
||||
newImage.setRGB(i, j, Color.GRAY.getRGB());
|
||||
|
||||
// oriImage.setRGB(x + i, y + j, Color.white.getRGB());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void readPixel(BufferedImage img, int x, int y, int[] pixels) {
|
||||
int xStart = x - 1;
|
||||
int yStart = y - 1;
|
||||
int current = 0;
|
||||
for (int i = xStart; i < 3 + xStart; i++)
|
||||
for (int j = yStart; j < 3 + yStart; j++) {
|
||||
int tx = i;
|
||||
if (tx < 0) {
|
||||
tx = -tx;
|
||||
|
||||
} else if (tx >= img.getWidth()) {
|
||||
tx = x;
|
||||
}
|
||||
int ty = j;
|
||||
if (ty < 0) {
|
||||
ty = -ty;
|
||||
} else if (ty >= img.getHeight()) {
|
||||
ty = y;
|
||||
}
|
||||
pixels[current++] = img.getRGB(tx, ty);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static void fillMatrix(int[][] matrix, int[] values) {
|
||||
int filled = 0;
|
||||
for (int[] x : matrix) {
|
||||
for (int j = 0; j < x.length; j++) {
|
||||
x[j] = values[filled++];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int avgMatrix(int[][] matrix) {
|
||||
int r = 0;
|
||||
int g = 0;
|
||||
int b = 0;
|
||||
for (int[] x : matrix) {
|
||||
for (int j = 0; j < x.length; j++) {
|
||||
if (j == 1) {
|
||||
continue;
|
||||
}
|
||||
Color c = new Color(x[j]);
|
||||
r += c.getRed();
|
||||
g += c.getGreen();
|
||||
b += c.getBlue();
|
||||
}
|
||||
}
|
||||
return new Color(r / 8, g / 8, b / 8).getRGB();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package cn.lili.common.verification;
|
||||
|
||||
import cn.lili.common.utils.Base64DecodeMultipartFile;
|
||||
import cn.lili.common.vo.SerializableStream;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Base64Utils;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* 验证码工具
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v4.0
|
||||
* @Description:
|
||||
* @since 2020/11/17 14:34
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class SliderImageUtil {
|
||||
|
||||
private static final int BOLD = 5;
|
||||
|
||||
private static final String IMG_FILE_TYPE = "jpg";
|
||||
|
||||
private static final String TEMP_IMG_FILE_TYPE = "png";
|
||||
|
||||
/**
|
||||
* 根据模板切图
|
||||
*
|
||||
* @param sliderFile
|
||||
* @param originalFile
|
||||
* @return
|
||||
* @throws Exception sliderFile, originalFile
|
||||
*/
|
||||
public static Map<String, Object> pictureTemplatesCut(SerializableStream sliderFile, SerializableStream originalFile) throws Exception {
|
||||
|
||||
Random random = new Random();
|
||||
Map<String, Object> pictureMap = new HashMap<>();
|
||||
// 拼图
|
||||
BufferedImage sliderImage = ImageIO.read(Base64DecodeMultipartFile.base64ToInputStream(sliderFile.getBase64()));
|
||||
int sliderWidth = sliderImage.getWidth();
|
||||
int sliderHeight = sliderImage.getHeight();
|
||||
|
||||
// 原图
|
||||
BufferedImage originalImage = ImageIO.read(Base64DecodeMultipartFile.base64ToInputStream(originalFile.getBase64()));
|
||||
int originalWidth = originalImage.getWidth();
|
||||
int originalHeight = originalImage.getHeight();
|
||||
|
||||
// 随机生成抠图坐标X,Y
|
||||
// X轴距离右端targetWidth Y轴距离底部targetHeight以上
|
||||
int randomX = random.nextInt(originalWidth - 3 * sliderWidth) + 2 * sliderWidth;
|
||||
int randomY = random.nextInt(originalHeight - sliderHeight);
|
||||
log.info("原图大小{} x {},随机生成的坐标 X,Y 为({},{})", originalWidth, originalHeight, randomX, randomY);
|
||||
|
||||
// 新建一个和模板一样大小的图像,TYPE_4BYTE_ABGR表示具有8位RGBA颜色分量的图像,正常取imageTemplate.getType()
|
||||
BufferedImage newImage = new BufferedImage(sliderWidth, sliderHeight, sliderImage.getType());
|
||||
// 得到画笔对象
|
||||
Graphics2D graphics = newImage.createGraphics();
|
||||
// 如果需要生成RGB格式,需要做如下配置,Transparency 设置透明
|
||||
newImage = graphics.getDeviceConfiguration().createCompatibleImage(sliderWidth, sliderHeight,
|
||||
Transparency.TRANSLUCENT);
|
||||
|
||||
// 新建的图像根据模板颜色赋值,源图生成遮罩
|
||||
ImageUtil.cutByTemplate(originalImage, sliderImage, newImage, randomX, randomY);
|
||||
|
||||
// 设置“抗锯齿”的属性
|
||||
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
graphics.setStroke(new BasicStroke(BOLD, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
|
||||
graphics.drawImage(newImage, 0, 0, null);
|
||||
graphics.dispose();
|
||||
|
||||
//添加水印
|
||||
ImageUtil.addWatermark(originalImage, "请滑动拼图");
|
||||
ByteArrayOutputStream newImageOs = new ByteArrayOutputStream();// 新建流。
|
||||
ImageIO.write(newImage, TEMP_IMG_FILE_TYPE, newImageOs);// 利用ImageIO类提供的write方法,将bi以png图片的数据模式写入流。
|
||||
byte[] newImagery = newImageOs.toByteArray();
|
||||
|
||||
ByteArrayOutputStream oriImagesOs = new ByteArrayOutputStream();// 新建流。
|
||||
ImageIO.write(originalImage, IMG_FILE_TYPE, oriImagesOs);// 利用ImageIO类提供的write方法,将bi以jpg图片的数据模式写入流。
|
||||
byte[] oriImageByte = oriImagesOs.toByteArray();
|
||||
|
||||
pictureMap.put("slidingImage", "data:image/png;base64," + Base64Utils.encodeToString(newImagery));
|
||||
pictureMap.put("backImage", "data:image/png;base64," + Base64Utils.encodeToString(oriImageByte));
|
||||
// x轴
|
||||
pictureMap.put("randomX", randomX);
|
||||
// y轴
|
||||
pictureMap.put("randomY", randomY);
|
||||
|
||||
pictureMap.put("originalHeight", originalHeight);
|
||||
pictureMap.put("originalWidth", originalWidth);
|
||||
pictureMap.put("sliderHeight", sliderHeight);
|
||||
pictureMap.put("sliderWidth", sliderWidth);
|
||||
return pictureMap;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package cn.lili.common.verification;
|
||||
|
||||
import cn.lili.common.cache.Cache;
|
||||
import cn.lili.common.cache.CachePrefix;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 验证码sdk
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v4.0
|
||||
* @Description:
|
||||
* @since 2020/11/17 15:43
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class VerificationSDK {
|
||||
|
||||
private final Cache cache;
|
||||
|
||||
/**
|
||||
* 生成一个token 用于获取校验中心的验证码逻辑
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean checked(String verificationKey, String uuid) {
|
||||
//生成校验KEY,在验证码服务做记录
|
||||
String key = CachePrefix.VERIFICATION_KEY.getPrefix() + verificationKey;
|
||||
cache.get(key);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package cn.lili.common.verification.aop;
|
||||
|
||||
import cn.lili.common.enums.ResultCode;
|
||||
import cn.lili.common.exception.ServiceException;
|
||||
import cn.lili.common.verification.aop.annotation.Verification;
|
||||
import cn.lili.common.verification.enums.VerificationEnums;
|
||||
import cn.lili.common.verification.service.VerificationService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* 验证码验证拦截
|
||||
*
|
||||
* @author Chopper
|
||||
*/
|
||||
@Aspect
|
||||
@Configuration
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class VerificationInterceptor {
|
||||
|
||||
|
||||
private final VerificationService verificationService;
|
||||
|
||||
@Before("@annotation(cn.lili.common.verification.aop.annotation.Verification)")
|
||||
public void interceptor(JoinPoint pjp) {
|
||||
MethodSignature signature = (MethodSignature) pjp.getSignature();
|
||||
Method method = signature.getMethod();
|
||||
Verification verificationAnnotation = method.getAnnotation(Verification.class);
|
||||
VerificationEnums verificationEnums = verificationAnnotation.type();
|
||||
String uuid = verificationAnnotation.uuid();
|
||||
boolean result = verificationService.check(uuid, verificationEnums);
|
||||
if (result) {
|
||||
return;
|
||||
}
|
||||
throw new ServiceException(ResultCode.VERIFICATION_ERROR);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package cn.lili.common.verification.aop.annotation;
|
||||
|
||||
|
||||
import cn.lili.common.verification.enums.VerificationEnums;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 限流注解
|
||||
*
|
||||
* @author Chopper
|
||||
* @since 2018-02-05
|
||||
*/
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface Verification {
|
||||
/**
|
||||
* uuid
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String uuid();
|
||||
|
||||
/**
|
||||
* 验证类型
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
VerificationEnums type();
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package cn.lili.common.verification.enums;
|
||||
|
||||
/**
|
||||
* VerificationEnums
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v1.0
|
||||
* 2020-12-22 19:11
|
||||
*/
|
||||
public enum VerificationEnums {
|
||||
|
||||
/**
|
||||
* 登录
|
||||
* 注册
|
||||
* 找回用户
|
||||
* 修改密码
|
||||
* 支付钱包密码
|
||||
*/
|
||||
LOGIN,
|
||||
REGISTER,
|
||||
FIND_USER,
|
||||
UPDATE_PASSWORD,
|
||||
WALLET_PASSWORD;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package cn.lili.common.verification.service;
|
||||
|
||||
import cn.lili.common.verification.enums.VerificationEnums;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 验证码模块
|
||||
*/
|
||||
public interface VerificationService {
|
||||
/**
|
||||
* 获取校验对象
|
||||
*
|
||||
* @param verificationEnums 校验枚举
|
||||
* @param uuid uuid
|
||||
* @return 校验对象
|
||||
*/
|
||||
Map<String, Object> createVerification(VerificationEnums verificationEnums, String uuid) throws IOException;
|
||||
|
||||
/**
|
||||
* 预校验
|
||||
*
|
||||
* @param xPos 位移距离
|
||||
* @param uuid 用户唯一表示
|
||||
* @param verificationEnums 校验枚举
|
||||
* @return
|
||||
*/
|
||||
boolean preCheck(Integer xPos, String uuid, VerificationEnums verificationEnums);
|
||||
|
||||
/**
|
||||
* @param uuid 用户唯一表示
|
||||
* @param verificationEnums 校验枚举
|
||||
* @return
|
||||
*/
|
||||
boolean check(String uuid, VerificationEnums verificationEnums);
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
package cn.lili.common.verification.service.impl;
|
||||
|
||||
import cn.lili.common.cache.Cache;
|
||||
import cn.lili.common.cache.CachePrefix;
|
||||
import cn.lili.common.exception.ServiceException;
|
||||
import cn.lili.common.utils.StringUtils;
|
||||
import cn.lili.common.verification.SliderImageUtil;
|
||||
import cn.lili.common.verification.enums.VerificationEnums;
|
||||
import cn.lili.common.verification.service.VerificationService;
|
||||
import cn.lili.common.vo.SerializableStream;
|
||||
import cn.lili.modules.base.entity.dos.VerificationSource;
|
||||
import cn.lili.modules.base.entity.vo.VerificationVO;
|
||||
import cn.lili.modules.base.service.VerificationSourceService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* 认证处理类
|
||||
*
|
||||
* @author Chopper
|
||||
* @version v1.0
|
||||
* 2020-11-17 14:59
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class VerificationServiceImpl implements VerificationService {
|
||||
|
||||
|
||||
private final VerificationSourceService verificationSourceService;
|
||||
|
||||
private final Cache cache;
|
||||
|
||||
/**
|
||||
* 创建校验
|
||||
*
|
||||
* @return 验证码参数
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> createVerification(VerificationEnums verificationEnums, String uuid) throws IOException {
|
||||
|
||||
if (uuid == null) {
|
||||
throw new ServiceException("非法请求,请重新刷新页面操作");
|
||||
}
|
||||
|
||||
//获取验证码配置
|
||||
VerificationVO verificationVO = verificationSourceService.getVerificationCache();
|
||||
|
||||
List<VerificationSource> verificationResources = verificationVO.getVerificationResources();
|
||||
List<VerificationSource> verificationSlider = verificationVO.getVerificationSlider();
|
||||
|
||||
|
||||
Random random = new Random();
|
||||
// 随机选择需要切的图下标
|
||||
int resourceNum = random.nextInt(verificationResources.size());
|
||||
// 随机选择剪切模版下标
|
||||
int sliderNum = random.nextInt(verificationSlider.size());
|
||||
|
||||
// 随机选择需要切的图片地址
|
||||
String originalResource = verificationResources.get(resourceNum).getResource();
|
||||
// 随机选择剪切模版图片地址
|
||||
String sliderResource = verificationSlider.get(sliderNum).getResource();
|
||||
|
||||
try {
|
||||
//获取缓存中的资源
|
||||
SerializableStream originalFile = getInputStream(originalResource);
|
||||
SerializableStream sliderFile = getInputStream(sliderResource);
|
||||
Map<String, Object> resultMap = SliderImageUtil.pictureTemplatesCut(sliderFile, originalFile);
|
||||
// 生成验证参数 120可以验证 无需手动清除,120秒有效时间自动清除
|
||||
cache.put(cacheKey(verificationEnums, uuid), resultMap.get("randomX"), 120L);
|
||||
resultMap.put("key", cacheKey(verificationEnums, uuid));
|
||||
// 移除横坐标移动距离
|
||||
resultMap.remove("randomX");
|
||||
return resultMap;
|
||||
} catch (ServiceException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据网络地址,获取源文件
|
||||
* 这里简单说一下,这里是将不可序列化的inputstream序列化对象,存入redis缓存
|
||||
* @param originalResource
|
||||
* @return
|
||||
*/
|
||||
private SerializableStream getInputStream(String originalResource) throws Exception {
|
||||
|
||||
Object object = cache.get(CachePrefix.VERIFICATION_IMAGE.getPrefix() + originalResource);
|
||||
if (object != null) {
|
||||
return (SerializableStream) object;
|
||||
}
|
||||
if (StringUtils.isNotEmpty(originalResource)) {
|
||||
URL url = new URL(originalResource);
|
||||
InputStream inputStream = url.openStream();
|
||||
SerializableStream serializableStream = new SerializableStream(inputStream);
|
||||
cache.put(CachePrefix.VERIFICATION_IMAGE.getPrefix() + originalResource, serializableStream);
|
||||
return serializableStream;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 预校验图片 用于前端回显
|
||||
*
|
||||
* @param xPos X轴移动距离
|
||||
* @param verificationEnums 验证key
|
||||
* @return 验证是否成功
|
||||
*/
|
||||
@Override
|
||||
public boolean preCheck(Integer xPos, String uuid, VerificationEnums verificationEnums) {
|
||||
Integer randomX = (Integer) cache.get(cacheKey(verificationEnums, uuid));
|
||||
if (randomX == null) {
|
||||
return false;
|
||||
}
|
||||
log.debug("{}{}", randomX, xPos);
|
||||
//验证结果
|
||||
boolean result = Math.abs(randomX - xPos) < 3;
|
||||
if (result) {
|
||||
//验证成功,则记录验证结果 验证有效时间,120秒
|
||||
cache.put(cacheResult(verificationEnums, uuid), true, 120L);
|
||||
return result;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证码校验
|
||||
*
|
||||
* @param uuid 用户标识
|
||||
* @param verificationEnums 验证key
|
||||
* @return 验证是否成功
|
||||
*/
|
||||
@Override
|
||||
public boolean check(String uuid, VerificationEnums verificationEnums) {
|
||||
Object object = cache.get(cacheResult(verificationEnums, uuid));
|
||||
if (object == null) {
|
||||
return false;
|
||||
} else {
|
||||
cache.remove(cacheResult(verificationEnums, uuid));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成缓存key 记录缓存需要验证的内容
|
||||
*
|
||||
* @param verificationEnums 验证码枚举
|
||||
* @param uuid 用户uuid
|
||||
* @return 缓存key
|
||||
*/
|
||||
public static String cacheKey(VerificationEnums verificationEnums, String uuid) {
|
||||
return CachePrefix.VERIFICATION_KEY.getPrefix() + verificationEnums.name() + uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成缓存key 记录缓存验证的结果
|
||||
*
|
||||
* @param verificationEnums 验证码枚举
|
||||
* @param uuid 用户uuid
|
||||
* @return 缓存key
|
||||
*/
|
||||
public static String cacheResult(VerificationEnums verificationEnums, String uuid) {
|
||||
return CachePrefix.VERIFICATION_RESULT.getPrefix() + verificationEnums.name() + uuid;
|
||||
}
|
||||
|
||||
}
|
||||
55
framework/src/main/java/cn/lili/common/vo/PageVO.java
Normal file
55
framework/src/main/java/cn/lili/common/vo/PageVO.java
Normal file
@@ -0,0 +1,55 @@
|
||||
package cn.lili.common.vo;
|
||||
|
||||
import cn.lili.common.utils.StringUtils;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 查询参数
|
||||
*
|
||||
* @author Chopper
|
||||
* @date 2020/11/26 14:43
|
||||
*/
|
||||
@Data
|
||||
public class PageVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty(value = "页号")
|
||||
private Integer pageNumber = 1;
|
||||
|
||||
@ApiModelProperty(value = "页面大小")
|
||||
private Integer pageSize = 10;
|
||||
|
||||
@ApiModelProperty(value = "排序字段")
|
||||
private String sort;
|
||||
|
||||
@ApiModelProperty(value = "排序方式 asc/desc")
|
||||
private String order;
|
||||
|
||||
@ApiModelProperty(value = "需要驼峰转换蛇形", notes = "一般不做处理,如果数据库中就是蛇形,则这块需要处理。")
|
||||
private Boolean notConvert;
|
||||
|
||||
public Integer getMongoPageNumber() {
|
||||
int i = pageNumber - 1;
|
||||
if (i < 0) {
|
||||
return pageNumber;
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
public String getSort() {
|
||||
if (!StringUtils.isEmpty(sort)) {
|
||||
if (notConvert == null || Boolean.FALSE.equals(notConvert)) {
|
||||
return StringUtils.camel2Underline(sort);
|
||||
} else {
|
||||
return sort;
|
||||
}
|
||||
}
|
||||
return sort;
|
||||
}
|
||||
|
||||
}
|
||||
43
framework/src/main/java/cn/lili/common/vo/ResultMessage.java
Normal file
43
framework/src/main/java/cn/lili/common/vo/ResultMessage.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package cn.lili.common.vo;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 前后端交互VO
|
||||
*
|
||||
* @author Chopper
|
||||
* @date 2020/11/26 14:40
|
||||
*/
|
||||
@Data
|
||||
public class ResultMessage<T> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 成功标志
|
||||
*/
|
||||
private boolean success;
|
||||
|
||||
/**
|
||||
* 消息
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 返回代码
|
||||
*/
|
||||
private Integer code;
|
||||
|
||||
/**
|
||||
* 时间戳
|
||||
*/
|
||||
private long timestamp = System.currentTimeMillis();
|
||||
|
||||
/**
|
||||
* 结果对象
|
||||
*/
|
||||
private T result;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user