💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。
- 推荐:kuan 的首页,持续学习,不断总结,共同进步,活到老学到老
- 导航
- 檀越剑指大厂系列:全面总结 java 核心技术点,如集合,jvm,并发编程 redis,kafka,Spring,微服务,Netty 等
- 常用开发工具系列:罗列常用的开发工具,如 IDEA,Mac,Alfred,electerm,Git,typora,apifox 等
- 数据库系列:详细总结了常用数据库 mysql 技术点,以及工作中遇到的 mysql 问题等
- 懒人运维系列:总结好用的命令,解放双手不香吗?能用一个命令完成绝不用两个操作
- 数据结构与算法系列:总结数据结构和算法,不同类型针对性训练,提升编程思维,剑指大厂
非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。💝💝💝 ✨✨ 欢迎订阅本专栏 ✨✨
博客目录
- 1.启动配置文件
- 2..gitignore 文件
- 3.logback.xml 日志文件
- 4.Swagger2Config
- 5.WebConfigurer
- 6.Result
- 7.JeecgBootException
- 8.JeecgBootExceptionHandler
- 9.MybatisPlusConfig
- 10.RedisLock
- 11.redis
- 12.RabbitMqConfig
1.启动配置文件
application.yml
spring:
profiles:
active: local
application-local.yml
server:
servlet:
context-path: /bfp-o2o
#Spring 相关配置
spring:
autoconfigure:
exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
datasource:
dynamic:
#设置默认的数据源或者数据源组,默认值即为master
primary: master
strict: false
datasource:
master:
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://10.239.183.66:3306/o2o?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false
# url: jdbc:mysql://10.201.1.222:3306/o2o?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false
username: root
password: cw68_sit@crv
o2o:
jd:
file:
remotePath: /home/ftpuser/jd/
remoteFileName: jd-back.csv
localPath: ./data/jd/
localFileName: jd-back.csv
mt:
file:
remotePath: /home/ftpuser/meituan/
remoteFileName: mt.csv
localPath: ./data/meituan/
localFileName: mt.csv
eleme:
file:
remotePath: /home/ftpuser/eleme/
remoteFileName: eleme.csv
localPath: ./data/eleme/
localFileName: eleme.csv
shop_ids_url: https://o2o-api.crv.com.cn/open_api/v1/dc-shop
suguo_url: https://wei-test.vipxiaoqu.com/wty-test
sftp:
name: ftpuser
passwd: ftpP#ssw0rd
ipAddr: 10.201.1.223
port: 22
#mybatis-plus配置
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
call-setters-on-nulls: true
auto-mapping-behavior: full
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath*:mapper/**/*Mapper.xml
global-config:
banner: false
# 逻辑删除配置
db-config:
id-type: AUTO
logic-delete-field: delFlag
logic-delete-value: 1
logic-not-delete-value: 0
table-underline: true
bootstrap.yml
spring:
application:
name: o2o-service
cloud:
nacos:
config:
file-extension: yaml
启动参数
-Dspring.cloud.nacos.discovery.serverAddr=10.239.165.190:80 -Dspring.cloud.nacos.discovery.namespace=bfp-sit -Dspring.cloud.nacos.config.serverAddr=10.239.165.190:80 -Dspring.cloud.nacos.config.namespace=bfp-sit
2…gitignore 文件
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
3.logback.xml 日志文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<!--定义日志文件的存储地址 -->
<property name="APPDIR" value="o2o-api"/>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>log/${APPDIR}.out</File>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 添加.gz 历史日志会启用压缩 大大缩小日志文件所占空间 -->
<fileNamePattern>log/daily/${APPDIR}.%d{yyyy-MM-dd}.gz</fileNamePattern>
<maxHistory>7</maxHistory><!-- 保留7天日志 -->
</rollingPolicy>
</appender>
<!--myibatis log configure -->
<logger name="com.apache.ibatis" level="TRACE"/>
<logger name="java.sql.Connection" level="DEBUG"/>
<logger name="java.sql.Statement" level="DEBUG"/>
<logger name="java.sql.PreparedStatement" level="DEBUG"/>
<logger name="com.wanjia.o2o" level="INFO"/>
<!-- 日志输出级别 -->
<root level="info">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
</configuration>
4.Swagger2Config
package org.jeecg.config;
import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.shiro.vo.DefContants;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import springfox.documentation.RequestHandler;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.Parameter;
import springfox.documentation.service.SecurityScheme;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @Author scott
*/
@Slf4j
@Configuration
@EnableSwagger2
@EnableSwaggerBootstrapUI
@ConditionalOnProperty(name = "swagger.enable", havingValue = "true")
public class Swagger2Config implements WebMvcConfigurer {
/**
* 显示swagger-ui.html文档展示页,还必须注入swagger资源:
*
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
Predicate<RequestHandler> selector1 = RequestHandlerSelectors.basePackage("org.jeecg.modules");
Predicate<RequestHandler> selector2 = RequestHandlerSelectors.basePackage("com.eastroc");
/**
* swagger2的配置文件,这里可以配置swagger2的一些基本的内容,比如扫描的包等等
*
* @return Docket
*/
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo()).groupName("预约系统")
.select()
//此包路径下的类,才生成接口文档
// .apis(RequestHandlerSelectors.basePackage("org.jeecg.modules"))
//加了ApiOperation注解的类,才生成接口文档
.apis(Predicates.or(selector1, selector2))
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build()
.securitySchemes(Collections.singletonList(securityScheme()))
.globalOperationParameters(setHeaderToken());
}
@Bean
public Docket createVrsRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo()).groupName("ARRANGE模块")
.select()
//此包路径下的类,才生成接口文档
.apis(RequestHandlerSelectors.basePackage("com.eastroc"))
//加了ApiOperation注解的类,才生成接口文档
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build()
.securitySchemes(Collections.singletonList(securityScheme()));
// .globalOperationParameters(setHeaderToken());
}
/***
* oauth2配置
* 需要增加swagger授权回调地址
* http://localhost:8888/webjars/springfox-swagger-ui/o2c.html
* @return
*/
@Bean
SecurityScheme securityScheme() {
return new ApiKey(DefContants.X_ACCESS_TOKEN, DefContants.X_ACCESS_TOKEN, "header");
}
/**
* JWT token
*
* @return
*/
private List<Parameter> setHeaderToken() {
ParameterBuilder tokenPar = new ParameterBuilder();
List<Parameter> pars = new ArrayList<>();
tokenPar.name(DefContants.X_ACCESS_TOKEN).description("token").modelRef(new ModelRef("string")).parameterType("header").required(false).build();
pars.add(tokenPar.build());
return pars;
}
/**
* api文档的详细信息函数,注意这里的注解引用的是哪个
*
* @return
*/
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
// //大标题
.title("Jeecg-Boot 后台服务API接口文档")
// 版本号
.version("1.0")
// .termsOfServiceUrl("NO terms of service")
// 描述
.description("后台API接口")
// 作者
.contact("JEECG团队")
.license("The Apache License, Version 2.0")
.licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
.build();
}
}
/**
* @author : qinyingjie
* @date : 2021/10/26
* @desc : http://localhost:8080/ant/doc.html#/home
*/
@Configuration
@EnableOpenApi //开启 Swagger3 ,可不写
@EnableKnife4j //开启 knife4j ,可不写
public class Knife4jConfig {
@Bean
public Docket createRestApi() {
// Swagger 2 使用的是:DocumentationType.SWAGGER_2
// Swagger 3 使用的是:DocumentationType.OAS_30
return new Docket(DocumentationType.OAS_30)
// 定义是否开启swagger,false为关闭,可以通过变量控制
.enable(true)
// 将api的元信息设置为包含在json ResourceListing响应中。
.apiInfo(new ApiInfoBuilder()
.title("ANT接口文档")
// 描述
.description("平台服务管理api")
.contact(new Contact("秦英杰", "深圳", "327782001@qq.com"))
.version("1.0.0")
.build())
// 分组名称
.groupName("1.0")
// 选择哪些接口作为swagger的doc发布
.select()
// 要扫描的API(Controller)基础包
.apis(RequestHandlerSelectors.basePackage("com.xiaofei"))
// .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build();
}
}
5.WebConfigurer
package com.eastroc.login;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.eastroc.common.enums.RoleEnum;
import com.eastroc.common.service.ISystemFixService;
import com.eastroc.common.utils.LoginUtils;
import com.eastroc.mine.entity.SystemFix;
import com.eastroc.mine.entity.UserWechat;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.exception.RoleException;
import org.jeecg.common.exception.SystemMaintenanceException;
import org.jeecg.common.exception.UserException;
import org.jeecg.common.util.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;
import java.util.Objects;
@Slf4j
@Component
public class XcxRoleInterceptor extends HandlerInterceptorAdapter {
@Autowired
private ISystemFixService systemFixService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
List<SystemFix> systemFixes = systemFixService.list();
if (CollectionUtils.isNotEmpty(systemFixes)) {
SystemFix systemFix = systemFixes.get(0);
//系统维护通知
long startInterval = DateUtil.between(systemFix.getFixStart(), new Date(), DateUnit.MS, false);
long endInterval = DateUtil.between(new Date(), systemFix.getFixEnd(), DateUnit.MS, false);
if (startInterval > 0 && endInterval > 0) {
String endDate = DateUtils.date2Str(systemFix.getFixEnd(), DateUtils.datetimeFormat.get());
throw new SystemMaintenanceException("系统维护中,维护时间至:" + endDate);
}
}
UserWechat user = LoginUtils.getCurrentUser(request);
if (Objects.isNull(user)) {
throw new UserException("登录信息没有" + request.getRequestURL());
}
if (Objects.isNull(user.getRoleId())) {
throw new UserException("登录角色没有" + request.getRequestURL() + "" + user.toString());
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
XcxRole role = method.getAnnotation(XcxRole.class);
if (Objects.isNull(role)) {
return true;
}
if (role.roles().length > 0) {
Integer currentRole = user.getRoleId();
for (RoleEnum annotationRole : role.roles()) {
if (currentRole.equals(annotationRole.getType())) {
return true;
}
}
}
throw new RoleException("用户越权访问接口");
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
6.Result
package org.jeecg.common.api.vo;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.jeecg.common.constant.CommonConstant;
import lombok.Data;
/**
* 接口返回数据格式
* @author scott
* @email jeecgos@163.com
* @date 2019年1月19日
*/
@Data
@ApiModel(value="接口返回对象", description="接口返回对象")
public class Result<T> implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 成功标志
*/
@ApiModelProperty(value = "成功标志")
private boolean success = true;
/**
* 返回处理消息
*/
@ApiModelProperty(value = "返回处理消息")
private String message = "操作成功!";
/**
* 返回代码
*/
@ApiModelProperty(value = "返回代码")
private Integer code = 0;
/**
* 返回数据对象 data
*/
@ApiModelProperty(value = "返回数据对象")
private T result;
/**
* 时间戳
*/
@ApiModelProperty(value = "时间戳")
private long timestamp = System.currentTimeMillis();
public Result() {
}
public Result<T> success(String message) {
this.message = message;
this.code = CommonConstant.SC_OK_200;
this.success = true;
return this;
}
public Result<T> good(T t) {
this.setResult(t);
this.code = CommonConstant.SC_OK_200;
this.success = true;
return this;
}
public Result<T> good() {
this.code = CommonConstant.SC_OK_200;
this.success = true;
this.setMessage("成功");
return this;
}
public Result<T> fail(String msg) {
this.setCode(CommonConstant.SC_INTERNAL_SERVER_ERROR_500);
this.setMessage(msg);
this.setSuccess(false);
return this;
}
public static Result<Object> ok() {
Result<Object> r = new Result<Object>();
r.setSuccess(true);
r.setCode(CommonConstant.SC_OK_200);
r.setMessage("成功");
return r;
}
public static Result<Object> ok(String msg) {
Result<Object> r = new Result<Object>();
r.setSuccess(true);
r.setCode(CommonConstant.SC_OK_200);
r.setMessage(msg);
return r;
}
public static Result<Object> ok(Object data) {
Result<Object> r = new Result<Object>();
r.setSuccess(true);
r.setCode(CommonConstant.SC_OK_200);
r.setResult(data);
return r;
}
public static Result<Object> error(String msg) {
return error(CommonConstant.SC_INTERNAL_SERVER_ERROR_500, msg);
}
public static Result<Object> error(int code, String msg) {
Result<Object> r = new Result<Object>();
r.setCode(code);
r.setMessage(msg);
r.setSuccess(false);
return r;
}
public Result<T> error500(String message) {
this.message = message;
this.code = CommonConstant.SC_INTERNAL_SERVER_ERROR_500;
this.success = false;
return this;
}
/**
* 无权限访问返回结果
*/
public static Result<Object> noauth(String msg) {
return error(CommonConstant.SC_JEECG_NO_AUTHZ, msg);
}
}
7.JeecgBootException
package org.jeecg.common.exception;
public class JeecgBootException extends RuntimeException {
private static final long serialVersionUID = 1L;
private Integer code;
public JeecgBootException(String message) {
super(message);
}
public JeecgBootException(Integer code, String message) {
super(message);
this.code = code;
}
public JeecgBootException(Throwable cause) {
super(cause);
}
public JeecgBootException(String message, Throwable cause) {
super(message, cause);
}
}
8.JeecgBootExceptionHandler
package org.jeecg.common.exception;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthorizedException;
import org.jeecg.common.api.vo.Result;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.connection.PoolException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.NoHandlerFoundException;
import java.util.Arrays;
/**
* 异常处理器
*
* @Author scott
* @Date 2019
*/
@Slf4j
@RestControllerAdvice
public class JeecgBootExceptionHandler {
@Value("${profileCurrentValue}")
private String profileCurrentValue;
/**
* 处理自定义异常
*/
@ExceptionHandler(JeecgBootException.class)
public Result<?> handleRRException(JeecgBootException e) {
log.error(e.getMessage(), e);
return Result.error(e.getMessage());
}
@ExceptionHandler(RoleException.class)
public Result<?> handleRoleException(RoleException e) {
log.error(e.getMessage(), e);
return Result.error("用户角色异常");
}
@ExceptionHandler(UserException.class)
public Result<?> handleUserException(UserException e) {
log.error(e.getMessage(), e);
return Result.error("用户登录异常");
}
@ExceptionHandler(SystemMaintenanceException.class)
public Result<?> handleSystemMaintenanceException(SystemMaintenanceException e) {
log.error(e.getMessage(), e);
return Result.error(e.getMessage());
}
@ExceptionHandler(NoHandlerFoundException.class)
public Result<?> handlerNoFoundException(Exception e) {
log.error(e.getMessage(), e);
return Result.error(404, "路径不存在,请检查路径是否正确");
}
@ExceptionHandler(DuplicateKeyException.class)
public Result<?> handleDuplicateKeyException(DuplicateKeyException e) {
log.error(e.getMessage(), e);
return Result.error("数据库中已存在该记录");
}
@ExceptionHandler({UnauthorizedException.class, AuthorizationException.class})
public Result<?> handleAuthorizationException(AuthorizationException e) {
log.error(e.getMessage(), e);
return Result.noauth("没有权限,请联系管理员授权");
}
@ExceptionHandler({Exception.class})
public Result<?> handleException(Exception e) {
log.error(e.getMessage(), e);
String env = "小程序";
if ("dev".equals(profileCurrentValue)) {
return Result.error("操作失败," + e.getMessage());
// env = "开发环境";
} else if ("test".equals(profileCurrentValue)) {
env += "测试环境";
} else if ("prod".equals(profileCurrentValue)) {
env += "生产环境";
}
HttpRequest.post("https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=8315d403-91d3-4bc2-bc72-91bd92a38a51")
.header(Header.CONTENT_TYPE, "application/json")
.body(" {\n" +
" \"msgtype\": \"text\",\n" +
" \"text\": {\n" +
" \"content\": \"" + env + "\n" + e.toString() + "\n" + Arrays.toString(e.getStackTrace()).substring(0, 500) + "\"\n" +
" }\n" +
" }")
.execute();
return Result.error("操作失败," + e.getMessage());
}
/**
* 自定义系统交互异常
*/
@ExceptionHandler(SystemException.class)
public Result<?> handleSystemException(SystemException e) {
log.error(e.getMessage(), e);
String env = "小程序";
if ("dev".equals(profileCurrentValue)) {
// return Result.error("操作失败," + e.getMessage());
env = "开发环境";
} else if ("test".equals(profileCurrentValue)) {
env += "测试环境";
} else if ("prod".equals(profileCurrentValue)) {
env += "生产环境";
}
env = env + "--系统交互异常";
HttpRequest.post("https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=8315d403-91d3-4bc2-bc72-91bd92a38a51")
.header(Header.CONTENT_TYPE, "application/json")
.body(" {\n" +
" \"msgtype\": \"text\",\n" +
" \"text\": {\n" +
" \"content\": \"" + env + "\n" + e.toString() + "\n" + e.getMessage() + "\"\n" +
" }\n" +
" }")
.execute();
return Result.error(e.getMessage());
}
/**
* @param e
* @return
* @Author 政辉
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public Result<?> HttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
StringBuffer sb = new StringBuffer();
sb.append("不支持");
sb.append(e.getMethod());
sb.append("请求方法,");
sb.append("支持以下");
String[] methods = e.getSupportedMethods();
if (methods != null) {
for (String str : methods) {
sb.append(str);
sb.append("、");
}
}
log.error(sb.toString(), e);
//return Result.error("没有权限,请联系管理员授权");
return Result.error(405, sb.toString());
}
/**
* spring默认上传大小100MB 超出大小捕获异常MaxUploadSizeExceededException
*/
@ExceptionHandler(MaxUploadSizeExceededException.class)
public Result<?> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {
log.error(e.getMessage(), e);
return Result.error("文件大小超出10MB限制, 请压缩或降低文件质量! ");
}
@ExceptionHandler(DataIntegrityViolationException.class)
public Result<?> handleDataIntegrityViolationException(DataIntegrityViolationException e) {
log.error(e.getMessage(), e);
return Result.error("字段太长,超出数据库字段的长度");
}
@ExceptionHandler(PoolException.class)
public Result<?> handlePoolException(PoolException e) {
log.error(e.getMessage(), e);
return Result.error("Redis 连接异常!");
}
}
package com.xiaofei.common.constant;
public class CommonConstant {
/**
* 成功
*/
public static final Integer SC_OK_200 = 200;
/**
* 服务器错误
*/
public static final Integer SC_INTERNAL_SERVER_ERROR_500 = 500;
/**
* 未认证
*/
public static final int SC_JEECG_NO_AUTHZ = 401;
}
9.MybatisPlusConfig
package org.jeecg.config;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
import com.baomidou.mybatisplus.extension.injector.LogicSqlInjector;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.jeecg.config.mybatis.MetaHandler;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.aop.interceptor.PerformanceMonitorInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
/**
* 单数据源配置(jeecg.datasource.open = false时生效)
*
* @Author zhoujf
*/
@Configuration
@MapperScan(value = {"org.jeecg.modules.**.mapper*", "com.eastroc.**.mapper*"})
public class MybatisPlusConfig {
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
// 设置sql的limit为无限制,默认是500
return new PaginationInterceptor().setLimit(-1);
}
/**
* 自动填充功能
*
* @return
*/
@Bean
public GlobalConfig globalConfig() {
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setMetaObjectHandler(new MetaHandler());
return globalConfig;
}
@Bean
@Profile({"dev", "test"}) // 在dev和test环境中开启 SQL执行效率插件【生产环境可以关闭】
public PerformanceMonitorInterceptor performanceMonitorInterceptor() {
return new PerformanceMonitorInterceptor();
}
@Bean
public ISqlInjector sqlInjector() {
return new LogicSqlInjector();
}
}
package com.xiaofei.common.config;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.function.Supplier;
public class MetaHandler implements MetaObjectHandler {
private static String CREATE_TIME = "createTime";
private static String UPDATE_TIME = "updateTime";
/**
* 插入使用
*/
@Override
public void insertFill(MetaObject metaObject) {
fillValue(metaObject, CREATE_TIME, () -> getDateValue(metaObject.getSetterType(CREATE_TIME)));
fillValue(metaObject, UPDATE_TIME, () -> getDateValue(metaObject.getSetterType(UPDATE_TIME)));
}
/**
* 更新使用
*/
@Override
public void updateFill(MetaObject metaObject) {
fillValue(metaObject, "et." + UPDATE_TIME, () -> getDateValue(metaObject.getSetterType("et." + UPDATE_TIME)));
}
private void fillValue(MetaObject metaObject, String fieldName, Supplier<Object> valueSupplier) {
if (!metaObject.hasGetter(fieldName)) {
return;
}
Object sidObj = metaObject.getValue(fieldName);
if (sidObj == null && metaObject.hasSetter(fieldName) && valueSupplier != null) {
setFieldValByName(fieldName, valueSupplier.get(), metaObject);
}
}
private Object getDateValue(Class<?> setterType) {
if (Date.class.equals(setterType)) {
return new Date();
} else if (LocalDateTime.class.equals(setterType)) {
return LocalDateTime.now();
} else if (Long.class.equals(setterType)) {
return System.currentTimeMillis();
}
return null;
}
}
10.RedisLock
package org.jeecg.common.aspect.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisLock {
/**
* 获取方法上的参数值生成key,spEl表达式:例如'#{data}'或者 '#{data.value}'或者 '#{data.value1}&#{data.value2}'
*/
String key() default "";
/**
* 自定义后缀,字符串,加在key的后面
*/
String suffix() default "";
/**
* key过期时间,毫秒,默认10秒
*/
String expireTime() default "10000";
/**
* 是否包含token值,需要请求头里包含token
*/
boolean includeToken() default false;
}
@RedisLock(key = "#data")
@AutoLog(value = "供应链新增订单到预约系统")
@ApiOperation(value = "供应链新增订单到预约系统", notes = "供应链新增订单到预约系统")
@PostMapping(value = "/expressOrder")
public Map<String, Object> expressOrder(@RequestBody String data) {}
package org.jeecg.modules.system.aspect;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.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.jeecg.common.aspect.annotation.RedisLock;
import org.jeecg.common.util.redis.JedisClient;
import org.jeecg.common.util.redis.RedisTool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.UUID;
@Slf4j
@Aspect
@Configuration
public class RedisLockAspect {
@Autowired
private JedisClient jedisClient;
@Around(value = "@annotation(org.jeecg.common.aspect.annotation.RedisLock)")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
Method method = methodSignature.getMethod();
RedisLock redisLock = method.getAnnotation(RedisLock.class);
Object[] arguments = pjp.getArgs();
//解析SpEL表达式
String key = String.valueOf(parseSpel(redisLock.key(), method, arguments));
//解析UserId表达式
String token = parseToken(redisLock.includeToken());
Object result;
String methodName = pjp.getSignature().getName();
//唯一标识
String requestId = UUID.randomUUID().toString();
String lockKey = "vrs_" + methodName + "_" + token + key;
//后缀
if (StringUtils.isNotBlank(redisLock.suffix())) {
lockKey = lockKey + "_" + redisLock.suffix();
}
try {
//添加分布式锁
boolean res = RedisTool.tryGetDistributedLock(jedisClient.getJedis(), lockKey, requestId, Integer.parseInt(redisLock.expireTime()));
if (res) {
//执行
result = pjp.proceed();
} else {
log.info("重复提交的请求: " + lockKey);
throw new Exception("重复提交的请求");
}
} catch (Exception e) {
log.error(e.getMessage(), e);
throw e;
} finally {
//移除分布式锁
RedisTool.releaseDistributedLock(jedisClient.getJedis(), lockKey, requestId);
log.info("释放分布式锁成功: " + lockKey);
}
return result;
}
private String parseToken(boolean includeToken) {
if (includeToken) {
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
if (sra != null && sra.getRequest() != null) {
HttpServletRequest request = sra.getRequest();
Object token = request.getHeader("token_manage");
return token == null ? "" : token.toString();
}
}
return "";
}
/**
* 解析SpEL表达式
* @param key SpEL表达式
* @param method 反射得到的方法
* @param args 反射得到的方法参数
* @return 解析后SpEL表达式对应的值
*/
private Object parseSpel(String key, Method method, Object[] args) {
if (StringUtils.isNotBlank(key)) {
// 创建解析器
ExpressionParser parser = new SpelExpressionParser();
// 通过Spring的LocalVariableTableParameterNameDiscoverer获取方法参数名列表
LocalVariableTableParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
// 构造上下文
EvaluationContext context = new StandardEvaluationContext();
if (args.length == parameterNames.length) {
for (int i = 0, len = args.length; i < len; i++) {
// 使用setVariable方法来注册自定义变量
context.setVariable(parameterNames[i], args[i]);
}
}
String[] keys = key.split("&");
StringBuilder sb = new StringBuilder();
if (keys.length > 0) {
for (String k : keys) {
sb.append(parser.parseExpression(k).getValue(context));
}
}
return sb.toString();
}
return "";
}
}
11.redis
package org.jeecg.common.util.redis;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import java.util.Collections;
@Component
public class RedisTool {
private static final String LOCK_SUCCESS = "OK";
private static final String SET_IF_NOT_EXIST = "NX"; // NX 代表只在键不存在时,才对键进行设置操作
private static final String SET_WITH_EXPIRE_TIME = "PX"; // PX 5000 设置键的过期时间为5000毫秒
/**
* 尝试获取分布式锁
* @param jedis Redis客户端
* @param lockKey 锁
* @param requestId 请求标识
* @param expireTime 超期时间
* @return 是否获取成功
*/
public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
/**
* 1、把key、value set到redis中,隐含覆盖,默认的ttl是-1(永不过期)
*
* 2、根据第三个参数,把key、value set到redis中
* nx : not exists, 只有key 不存在时才把key value set 到redis
* xx : is exists ,只有 key 存在是,才把key value set 到redis
*
* 3、4 和2 就相同,只是多加了个过期时间
* expx参数有两个值可选 :
* ex : seconds 秒
* px : milliseconds 毫秒
* 使用其他值,抛出 异常 : redis.clients.jedis.exceptions.JedisDataException : ERR syntax error
*/
try {
String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if (LOCK_SUCCESS.equals(result)) {
return true;
}
}finally {
if (jedis != null) {
jedis.close();
}
}
return false;
}
private static final Long RELEASE_SUCCESS = 1L;
/**
* 释放分布式锁
* @param jedis Redis客户端
* @param lockKey 锁
* @param requestId 请求标识
* @return 是否释放成功
*/
public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
try {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
if (RELEASE_SUCCESS.equals(result)) {
return true;
}
}finally {
if (jedis != null) {
jedis.close();
}
}
return false;
}
}
package org.jeecg.common.util.redis;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Data
@Configuration
@ConfigurationProperties(prefix = "spring.redis")
public class JedisConfig {
private String host;
private String password;
private String port;
private String timeout;
private String maxTotal;
private String maxIdle;
private String minIdle;
}
package org.jeecg.common.util.redis;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class JedisClient {
@Autowired
private JedisConfig jedisConfig;
//等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException;
private static int MAX_WAIT = 15 * 1000;
//超时时间
private static int TIMEOUT = 10 * 1000;
private JedisPool jedisPool = null;
public JedisPool getJedisPool() {
return jedisPool;
}
/**
* Jedis实例获取返回码
*
* @author jqlin
*/
public static class JedisStatus {
/**
* Jedis实例获取失败
*/
public static final long FAIL_LONG = -5L;
/**
* Jedis实例获取失败
*/
public static final int FAIL_INT = -5;
/**
* Jedis实例获取失败
*/
public static final String FAIL_STRING = "-5";
}
@PostConstruct
private void initialPool() {
//Redis服务器IP
String HOST = jedisConfig.getHost();
//Redis的端口号
int PORT = NumberUtils.toInt(jedisConfig.getPort(), 6379);
//访问密码
String AUTH = jedisConfig.getPassword();
try {
JedisPoolConfig config = new JedisPoolConfig();
//最大连接数,如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)。
config.setMaxTotal(NumberUtils.toInt(jedisConfig.getMaxTotal(), 400));
//最大空闲数,控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。
config.setMaxIdle(NumberUtils.toInt(jedisConfig.getMaxIdle(), 50));
//最小空闲数
config.setMinIdle(NumberUtils.toInt(jedisConfig.getMinIdle(), 10));
//是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
config.setTestOnBorrow(false);
//在return给pool时,是否提前进行validate操作
config.setTestOnReturn(false);
//在空闲时检查有效性,默认false
config.setTestWhileIdle(true);
//表示一个对象至少停留在idle状态的最短时间,然后才能被idle object evitor扫描并驱逐;
//这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义
config.setMinEvictableIdleTimeMillis(30000);
//表示idle object evitor两次扫描之间要sleep的毫秒数
config.setTimeBetweenEvictionRunsMillis(60000);
//表示idle object evitor每次扫描的最多的对象数
config.setNumTestsPerEvictionRun(1000);
//等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException;
config.setMaxWaitMillis(MAX_WAIT);
if (StringUtils.isNotBlank(AUTH)) {
jedisPool = new JedisPool(config, HOST, PORT, TIMEOUT, AUTH);
} else {
jedisPool = new JedisPool(config, HOST, PORT, TIMEOUT);
}
} catch (Exception e) {
if (jedisPool != null) {
jedisPool.close();
}
log.error("初始化Redis连接池失败", e);
}
}
/**
* 在多线程环境同步初始化
*/
private synchronized void poolInit() {
if (jedisPool == null) {
initialPool();
}
}
/**
* 同步获取Jedis实例
*
* @return Jedis
*/
public Jedis getJedis() {
if (jedisPool == null) {
poolInit();
}
Jedis jedis = null;
try {
if (jedisPool != null) {
jedis = jedisPool.getResource();
}
} catch (Exception e) {
log.error("同步获取Jedis实例失败" + e.getMessage(), e);
jedis.close();
}
return jedis;
}
/**
* 设置值
*
* @param key
* @param value
* @return -5:Jedis实例获取失败<br/>OK:操作成功<br/>null:操作失败
* @author jqlin
*/
public String set(String key, String value) {
try (
Jedis jedis = getJedis();
) {
if (jedis == null) {
return JedisStatus.FAIL_STRING;
}
return jedis.set(key, value);
} catch (Exception e) {
log.error("[redis error] -> 设置值失败");
throw new RuntimeException(e);
}
}
/**
* 设置值
*
* @param key
* @param value
* @param expire 过期时间,单位:秒
* @return -5:Jedis实例获取失败<br/>OK:操作成功<br/>null:操作失败
* @author jqlin
*/
public String set(String key, String value, int expire, TimeUnit timeUnit) {
try (
Jedis jedis = getJedis()
) {
if (jedis == null) {
return JedisStatus.FAIL_STRING;
}
if (TimeUnit.MILLISECONDS.equals(timeUnit)) {
expire = expire / 1000;
} else if (!TimeUnit.SECONDS.equals(timeUnit)) {
throw new RuntimeException("TimeUnit时间单位设置错误:请设置milliseconds或seconds");
}
return jedis.setex(key, expire, value);
} catch (Exception e) {
log.error("[redis error] -> 设置值失败");
throw new RuntimeException(e);
}
}
/**
* 获取值
*
* @param key
* @return
* @author jqlin
*/
public String get(String key) {
try (
Jedis jedis = getJedis()
) {
if (jedis == null) {
return JedisStatus.FAIL_STRING;
}
return jedis.get(key);
} catch (Exception e) {
log.error("[redis error] -> 设置值失败");
throw new RuntimeException(e);
}
}
/**
* 设置key的过期时间
*
* @param key
* @param -5:Jedis实例获取失败,1:成功,0:失败
* @return
* @author jqlin
*/
public long expire(String key, int seconds) {
try (
Jedis jedis = getJedis()
) {
if (jedis == null) {
return JedisStatus.FAIL_LONG;
}
return jedis.expire(key, seconds);
} catch (Exception e) {
log.error(String.format("设置key=%s的过期时间失败:" + e.getMessage(), key));
throw new RuntimeException(e);
}
}
/**
* 判断key是否存在
*
* @param key
* @return
* @author jqlin
*/
public boolean exists(String key) {
try (
Jedis jedis = getJedis();
) {
if (jedis == null) {
log.warn("Jedis实例获取为空");
return false;
}
return jedis.exists(key);
} catch (Exception e) {
log.error(String.format("判断key=%s是否存在失败:" + e.getMessage(), key));
throw new RuntimeException(e);
}
}
/**
* 删除key
*
* @param keys
* @return -5:Jedis实例获取失败,1:成功,0:失败
* @author jqlin
*/
public long del(String... keys) {
try (
Jedis jedis = getJedis();
) {
if (jedis == null) {
return JedisStatus.FAIL_LONG;
}
return jedis.del(keys);
} catch (Exception e) {
log.error(String.format("删除key=%s失败:" + e.getMessage(), keys));
throw new RuntimeException(e);
}
}
/**
* set if not exists,若key已存在,则setnx不做任何操作
*
* @param key
* @param value key已存在,1:key赋值成功
* @return
* @author jqlin
*/
public long setnx(String key, String value) {
try (
Jedis jedis = getJedis();
) {
if (jedis == null) {
return JedisStatus.FAIL_LONG;
}
return jedis.setnx(key, value);
} catch (Exception e) {
log.error("设置值失败:" + e.getMessage());
throw new RuntimeException(e);
}
}
/**
* set if not exists,若key已存在,则setnx不做任何操作
*
* @param key
* @param value key已存在,1:key赋值成功
* @param expire 过期时间,单位:秒
* @return
* @author jqlin
*/
public long setnx(String key, String value, int expire) {
try (
Jedis jedis = getJedis()
) {
if (jedis == null) {
return JedisStatus.FAIL_LONG;
}
Long result = jedis.setnx(key, value);
expire(key, expire);
return result;
} catch (Exception e) {
log.error("设置值失败:" + e.getMessage());
throw new RuntimeException(e);
}
}
/**
* 在列表key的头部插入元素
*
* @param key
* @param values -5:Jedis实例获取失败,>0:返回操作成功的条数,0:失败
* @return
* @author jqlin
*/
public long lpush(String key, String... values) {
try (
Jedis jedis = getJedis();
) {
if (jedis == null) {
return JedisStatus.FAIL_LONG;
}
return jedis.lpush(key, values);
} catch (Exception e) {
log.error("在列表key的头部插入元素失败:" + e.getMessage());
throw new RuntimeException(e);
}
}
/**
* 在列表key的尾部插入元素
*
* @param key
* @param values -5:Jedis实例获取失败,>0:返回操作成功的条数,0:失败
* @return
* @author jqlin
*/
public long rpush(String key, String... values) {
try (
Jedis jedis = getJedis()
) {
if (jedis == null) {
return JedisStatus.FAIL_LONG;
}
return jedis.rpush(key, values);
} catch (Exception e) {
log.error("在列表key的尾部插入元素失败:" + e.getMessage());
throw new RuntimeException(e);
}
}
/**
* 返回存储在key列表的特定元素
*
* @param key
* @param start 开始索引,索引从0开始,0表示第一个元素,1表示第二个元素
* @param end 结束索引,-1表示最后一个元素,-2表示倒数第二个元素
* @return redis client获取失败返回null
* @author jqlin
*/
public List<String> lrange(String key, long start, long end) {
try (
Jedis jedis = getJedis()
) {
if (jedis == null) {
return null;
}
return jedis.lrange(key, start, end);
} catch (Exception e) {
log.error("查询列表元素失败:" + e.getMessage());
throw new RuntimeException(e);
}
}
/**
* 获取List缓存对象
*
* @param key
* @param start
* @param end
* @return List<T> 返回类型
* @author jqlin
*/
public <T> List<T> range(String key, long start, long end, Class<T> clazz) {
List<String> dataList = lrange(key, start, end);
if (CollectionUtils.isEmpty(dataList)) {
return new ArrayList<>();
}
return JSON.parseArray(JSON.toJSONString(dataList), clazz);
}
/**
* 获取列表长度
*
* @param key -5:Jedis实例获取失败
* @return
* @author jqlin
*/
public long llen(String key) {
try (
Jedis jedis = getJedis()
) {
if (jedis == null) {
return JedisStatus.FAIL_LONG;
}
return jedis.llen(key);
} catch (Exception e) {
log.error("获取列表长度失败:" + e.getMessage());
throw new RuntimeException(e);
}
}
/**
* 移除等于value的元素<br/><br/>
* 当count>0时,从表头开始查找,移除count个;<br/>
* 当count=0时,从表头开始查找,移除所有等于value的;<br/>
* 当count<0时,从表尾开始查找,移除count个
*
* @param key
* @param count
* @param value
* @return -5:Jedis实例获取失败
* @author jqlin
*/
public long lrem(String key, long count, String value) {
try (
Jedis jedis = getJedis()
) {
if (jedis == null) {
return JedisStatus.FAIL_LONG;
}
return jedis.lrem(key, count, value);
} catch (Exception e) {
log.error("获取列表长度失败:" + e.getMessage());
throw new RuntimeException(e);
}
}
/**
* 对列表进行修剪
*
* @param key
* @param start
* @param end
* @return -5:Jedis实例获取失败,OK:命令执行成功
* @author jqlin
*/
public String ltrim(String key, long start, long end) {
try (
Jedis jedis = getJedis()
) {
if (jedis == null) {
return JedisStatus.FAIL_STRING;
}
return jedis.ltrim(key, start, end);
} catch (Exception e) {
log.error("获取列表长度失败:" + e.getMessage());
throw new RuntimeException(e);
}
}
/**
* 设置对象
*
* @param key
* @param obj
* @return
* @author jqlin
*/
public <T> String setObject(String key, T obj) {
try (
Jedis jedis = getJedis()
) {
if (jedis == null) {
return JedisStatus.FAIL_STRING;
}
byte[] data = JSON.toJSONBytes(obj);
return jedis.set(key.getBytes(), data);
} catch (Exception e) {
log.error("设置对象失败:" + e.getMessage(), e);
throw new RuntimeException(e);
}
}
/**
* 获取对象
*
* @param key
* @return
* @author jqlin
*/
public <T> T getObject(String key) {
try (
Jedis jedis = getJedis()
) {
if (jedis == null) {
return null;
}
T result = null;
byte[] data = jedis.get(key.getBytes());
if (data != null && data.length > 0) {
result = (T) JSON.parse(data);
}
return result;
} catch (Exception e) {
log.error("获取对象失败:" + e.getMessage());
throw new RuntimeException(e);
}
}
/**
* 缓存Map赋值
*
* @param key
* @param field
* @param value
* @return -5:Jedis实例获取失败
* @author jqlin
*/
public long hset(String key, String field, String value) {
try (
Jedis jedis = getJedis();
) {
if (jedis == null) {
return JedisStatus.FAIL_LONG;
}
return jedis.hset(key, field, value);
} catch (Exception e) {
log.error("缓存Map赋值失败:" + e.getMessage());
throw new RuntimeException(e);
}
}
/**
* 获取缓存的Map值
*
* @param key
* @return
*/
public String hget(String key, String field) {
try (
Jedis jedis = getJedis();
) {
if (jedis == null) {
return null;
}
return jedis.hget(key, field);
} catch (Exception e) {
log.error("获取缓存的Map值失败:" + e.getMessage());
throw new RuntimeException(e);
}
}
/**
* 缓存Map
*
* @param key
* @param map
* @return
*/
public String hmset(String key, Map<String, String> map) {
try (
Jedis jedis = getJedis();
) {
if (jedis == null) {
return JedisStatus.FAIL_STRING;
}
return jedis.hmset(key, map);
} catch (Exception e) {
log.error("缓存Map赋值失败:" + e.getMessage());
throw new RuntimeException(e);
}
}
/**
* 获取map所有的字段和值
*
* @param key
* @return
* @author jqlin
*/
public Map<String, String> hgetAll(String key) {
try (
Jedis jedis = getJedis()
) {
if (jedis == null) {
log.warn("Jedis实例获取为空");
return new HashMap<>();
}
return jedis.hgetAll(key);
} catch (Exception e) {
log.error("获取map所有的字段和值失败:" + e.getMessage());
throw new RuntimeException(e);
}
}
/**
* 查看哈希表 key 中,指定的field字段是否存在。
*
* @param key
* @param field
* @return
* @author jqlin
*/
public Boolean hexists(String key, String field) {
try (
Jedis jedis = getJedis()
) {
if (jedis == null) {
log.warn("Jedis实例获取为空");
return null;
}
return jedis.hexists(key, field);
} catch (Exception e) {
log.error("查看哈希表field字段是否存在失败:" + e.getMessage());
throw new RuntimeException(e);
}
}
/**
* 获取所有哈希表中的字段
*
* @param key
* @return
* @author jqlin
*/
public Set<String> hkeys(String key) {
try (
Jedis jedis = getJedis();
) {
if (jedis == null) {
log.warn("Jedis实例获取为空");
return new HashSet<>();
}
return jedis.hkeys(key);
} catch (Exception e) {
log.error("获取所有哈希表中的字段失败:" + e.getMessage());
throw new RuntimeException(e);
}
}
/**
* 获取所有哈希表中的值
*
* @param key
* @return
* @author jqlin
*/
public List<String> hvals(String key) {
try (
Jedis jedis = getJedis();
) {
if (jedis == null) {
log.warn("Jedis实例获取为空");
return new ArrayList<>();
}
return jedis.hvals(key);
} catch (Exception e) {
log.error("获取所有哈希表中的值失败:" + e.getMessage());
throw new RuntimeException(e);
}
}
/**
* 从哈希表 key 中删除指定的field
*
* @param key
* @param
* @return
* @author jqlin
*/
public long hdel(String key, String... fields) {
try (
Jedis jedis = getJedis();
) {
if (jedis == null) {
log.warn("Jedis实例获取为空");
return JedisStatus.FAIL_LONG;
}
return jedis.hdel(key, fields);
} catch (Exception e) {
log.error("map删除指定的field失败:" + e.getMessage());
throw new RuntimeException(e);
}
}
/**
* 获取正则匹配的key (慎用)
*
* @param pattern
* @return
*/
public Set<String> keys(String pattern) {
try (
Jedis jedis = getJedis()
) {
if (jedis == null) {
log.warn("Jedis实例获取为空");
return new HashSet<>();
}
return jedis.keys(pattern);
} catch (Exception e) {
log.error("操作keys失败:" + e.getMessage(), e);
throw new RuntimeException(e);
}
}
/**
* Redis Incrby 命令将 key 中储存的数字加上指定的增量值;
* 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCRBY 命令;
* 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
* 本操作的值限制在 64 位(bit)有符号数字表示之内。
*
* @return
*/
public Long incrBy(String key, long integer) {
try (
Jedis jedis = getJedis()
) {
if (jedis == null) {
log.warn("Jedis实例获取为空");
return 0L;
}
return jedis.incrBy(key, integer);
} catch (Exception e) {
log.error("操作 Incrby 失败:" + e.getMessage(), e);
throw new RuntimeException(e);
}
}
/**
* Redis Incr 命令将 key 中储存的数字值增一。
* 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。
* 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
* 本操作的值限制在 64 位(bit)有符号数字表示之内
*/
public Long incr(String key) {
try (
Jedis jedis = getJedis()
) {
if (jedis == null) {
log.warn("Jedis实例获取为空");
return 0L;
}
return jedis.incr(key);
} catch (Exception e) {
log.error("操作 Incr 失败:" + e.getMessage(), e);
throw new RuntimeException(e);
}
}
}
12.RabbitMqConfig
package org.jeecg.config;
import com.eastroc.common.consts.ArrangeConstant;
import lombok.RequiredArgsConstructor;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@RequiredArgsConstructor
public class RabbitMqConfig {
private final SimpleRabbitListenerContainerFactoryConfigurer factoryConfigurer;
private final CachingConnectionFactory connectionFactory;
@Bean(name = "syncshipmentOrderReportServiceQueue")
public Queue syncshipmentOrderReportServiceQueue() {
return QueueBuilder.durable(ArrangeConstant.Q_SYNC_SHIPMENT_ORDER_TO_REPORT_ONE).build();
}
@Bean
public Binding syncShipmentOrderReportServiceBinding(Queue syncshipmentOrderReportServiceQueue, DirectExchange shipmentOrderReportExchange) {
return BindingBuilder.bind(syncshipmentOrderReportServiceQueue).to(shipmentOrderReportExchange).with(ArrangeConstant.K_SHIPMENT_ORDER_REPORT_ONE);
}
@Bean(name = "shipmentOrderReportContainer")
public SimpleRabbitListenerContainerFactory shipmentOrderReportContainer() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factoryConfigurer.configure(factory, connectionFactory);
factory.setAcknowledgeMode(AcknowledgeMode.NONE);
factory.setPrefetchCount(10);
return factory;
}
@Bean
public DirectExchange shipmentOrderReportExchange() {
return (DirectExchange) ExchangeBuilder.directExchange(ArrangeConstant.E_EXCHANGE).durable(true).build();
}
}
package com.eastroc.common.consumer;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.eastroc.common.consts.ArrangeConstant;
import com.eastroc.order.entity.*;
import com.eastroc.order.entity.StoreBase;
import com.eastroc.common.mapper.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
@Slf4j
@Component
@RabbitListener(queues = ArrangeConstant.Q_SYNC_SHIPMENT_ORDER_TO_REPORT_ONE, containerFactory = "shipmentOrderReportContainer")
public class ShipmentOrderReportConsumer {
@Resource
private StoreBaseMapper storeBaseMapper;
@Resource
private ShipmentOrderMapper shipmentOrderMapper;
@Resource
private ShipmentOrderDetailMapper shipmentOrderDetailMapper;
@Resource
private ShipmentOrderReportMapper shipmentOrderReportMapper;
@Resource
private ShipmentOrderRelateDriverMapper shipmentOrderRelateDriverMapper;
@Resource
private AccessControlSystemCarInfoMapper accessControlSystemCarInfoMapper;
@Resource
private ShipmentOrderDetailGoodsInfoMapper shipmentOrderDetailGoodsInfoMapper;
@RabbitHandler
public void syncReport(String shipmentOrderId) {
log.info("syncReport shipmentOrderId={}", shipmentOrderId);
ShipmentOrder shipmentOrder = shipmentOrderMapper.selectOne(Wrappers.<ShipmentOrder>lambdaQuery().eq(ShipmentOrder::getId, shipmentOrderId));
List<ShipmentOrderDetail> shipmentOrderDetails = shipmentOrderDetailMapper.selectList(Wrappers.<ShipmentOrderDetail>lambdaQuery()
.eq(ShipmentOrderDetail::getShipmentOrderId, shipmentOrderId));
if (CollectionUtils.isNotEmpty(shipmentOrderDetails)) {
shipmentOrderDetails.forEach(item -> {
String shipmentDetailId = item.getId();
ShipmentOrderRelateDriver shipmentOrderRelateDriver = shipmentOrderRelateDriverMapper.selectOne(Wrappers.<ShipmentOrderRelateDriver>lambdaQuery()
.eq(ShipmentOrderRelateDriver::getShipmentOrderId, shipmentOrderId)
);
List<ShipmentOrderDetailGoodsInfo> shipmentOrderDetailGoodsInfos = shipmentOrderDetailGoodsInfoMapper.selectList(Wrappers.<ShipmentOrderDetailGoodsInfo>lambdaQuery()
.eq(ShipmentOrderDetailGoodsInfo::getShipmentOrderDetailId, shipmentDetailId)
);
if (CollectionUtils.isNotEmpty(shipmentOrderDetailGoodsInfos)) {
shipmentOrderDetailGoodsInfos.forEach(goodsInfo -> {
ShipmentOrderReport shipmentOrderReport = shipmentOrderReportMapper.selectOne(Wrappers.<ShipmentOrderReport>lambdaQuery()
.eq(ShipmentOrderReport::getTrainNumber, shipmentOrder.getTrainNumber())
.eq(ShipmentOrderReport::getOrderNumber, item.getOrderNumber())
.eq(ShipmentOrderReport::getGoodsType, goodsInfo.getGoodsType())
.eq(ShipmentOrderReport::getProductCode, goodsInfo.getProductCode()));
if (shipmentOrderReport == null) {
shipmentOrderReport = new ShipmentOrderReport();
this.buildShipmentOrderReport(shipmentOrder, item, shipmentOrderRelateDriver, goodsInfo, shipmentOrderReport);
shipmentOrderReportMapper.insert(shipmentOrderReport);
} else {
this.buildShipmentOrderReport(shipmentOrder, item, shipmentOrderRelateDriver, goodsInfo, shipmentOrderReport);
shipmentOrderReportMapper.updateById(shipmentOrderReport);
}
});
}
if (Objects.isNull(shipmentOrderRelateDriver)) {
shipmentOrderReportMapper.update(new ShipmentOrderReport(), Wrappers.<ShipmentOrderReport>lambdaUpdate()
.set(ShipmentOrderReport::getPeriodDay, null)
.set(ShipmentOrderReport::getDriverName, null)
.set(ShipmentOrderReport::getDriverPhone, null)
.set(ShipmentOrderReport::getCarNumber, null)
.eq(ShipmentOrderReport::getTrainNumber, shipmentOrder.getTrainNumber())
);
}
});
}
}
/**
* 构建报表信息
*
* @param shipmentOrder
* @param item
* @param shipmentOrderRelateDriver
* @param goodsInfo
* @param shipmentOrderReport
*/
private void buildShipmentOrderReport(ShipmentOrder shipmentOrder, ShipmentOrderDetail item, ShipmentOrderRelateDriver shipmentOrderRelateDriver
, ShipmentOrderDetailGoodsInfo goodsInfo, ShipmentOrderReport shipmentOrderReport) {
//构建基础信息
this.buildBaseInfo(shipmentOrder, shipmentOrderReport);
//构建详细信息
this.buildDetailInfo(item, shipmentOrderReport);
//构建约车信息
this.buildDriverInfo(shipmentOrderRelateDriver, shipmentOrderReport);
//构建产品信息
this.buildGoodsInfo(goodsInfo, shipmentOrderReport);
}
/**
* 构建产品信息
*
* @param goodsInfo
* @param shipmentOrderReport
*/
private void buildGoodsInfo(ShipmentOrderDetailGoodsInfo goodsInfo, ShipmentOrderReport shipmentOrderReport) {
shipmentOrderReport.setGoodsType(goodsInfo.getGoodsType());
shipmentOrderReport.setProductCode(goodsInfo.getProductCode());
shipmentOrderReport.setProductName(goodsInfo.getProductName());
shipmentOrderReport.setProductType(goodsInfo.getProductType());
shipmentOrderReport.setProductWeight(goodsInfo.getProductWeight());
shipmentOrderReport.setProductSize(goodsInfo.getProductSize());
shipmentOrderReport.setProductNumber(goodsInfo.getProductNumber());
shipmentOrderReport.setProductUnit(goodsInfo.getProductUnit());
shipmentOrderReport.setDelFlag(goodsInfo.getDelFlag());
shipmentOrderReport.setCreateTime(goodsInfo.getCreateTime());
shipmentOrderReport.setUpdateTime(goodsInfo.getUpdateTime());
shipmentOrderReport.setCreateBy(goodsInfo.getCreateBy());
shipmentOrderReport.setUpdateBy(goodsInfo.getUpdateBy());
}
/**
* 构建预约信息
*
* @param shipmentOrderRelateDriver
* @param shipmentOrderReport
*/
private void buildDriverInfo(ShipmentOrderRelateDriver shipmentOrderRelateDriver, ShipmentOrderReport shipmentOrderReport) {
if (Objects.nonNull(shipmentOrderRelateDriver)) {
shipmentOrderReport.setDriverPhone(shipmentOrderRelateDriver.getDriverPhone());
shipmentOrderReport.setDriverName(shipmentOrderRelateDriver.getDriverName());
shipmentOrderReport.setPeriodDay(shipmentOrderRelateDriver.getPeriodDay());
shipmentOrderReport.setCarNumber(shipmentOrderRelateDriver.getCarNumber());
}
}
/**
* 构建订单详情
*
* @param item
* @param shipmentOrderReport
*/
private void buildDetailInfo(ShipmentOrderDetail item, ShipmentOrderReport shipmentOrderReport) {
shipmentOrderReport.setAddress(item.getAddress());
shipmentOrderReport.setCardBoard(item.getCardBoard());
shipmentOrderReport.setOrderNumber(item.getOrderNumber());
shipmentOrderReport.setDetailId(item.getId());
shipmentOrderReport.setConsigneeName(item.getConsigneeName());
shipmentOrderReport.setConsigneePhone(item.getConsigneePhone());
shipmentOrderReport.setRealReceiveNumber(item.getRealReceiveNumber());
shipmentOrderReport.setDealerNumber(item.getDealerNumber());
shipmentOrderReport.setDealerName(item.getDealerName());
}
/**
* 构建车次基础信息
*
* @param shipmentOrder
* @param shipmentOrderReport
*/
private void buildBaseInfo(ShipmentOrder shipmentOrder, ShipmentOrderReport shipmentOrderReport) {
shipmentOrderReport.setTrainNumber(shipmentOrder.getTrainNumber());
shipmentOrderReport.setOrderStatus(shipmentOrder.getOrderStatus());
shipmentOrderReport.setOutTime(shipmentOrder.getOutTime());
shipmentOrderReport.setCompany(shipmentOrder.getCompany());
shipmentOrderReport.setOutType(shipmentOrder.getOutType());
shipmentOrderReport.setDeliveryMode(shipmentOrder.getDeliveryMode());
shipmentOrderReport.setDispatchDate(shipmentOrder.getCreateTime());
String storeBaseId = shipmentOrder.getStoreBaseId();
shipmentOrderReport.setStoreBaseId(storeBaseId);
shipmentOrderReport.setStoreBaseName(shipmentOrder.getStoreBaseName());
if (StringUtils.isNotBlank(storeBaseId)) {
StoreBase storeBase = storeBaseMapper.selectById(storeBaseId);
shipmentOrderReport.setStoreBaseCode(storeBase.getBaseCode());
}
shipmentOrderReport.setModes(shipmentOrder.getModes());
AccessControlSystemCarInfo accessControlSystemCarInfo = accessControlSystemCarInfoMapper.selectOne(Wrappers.<AccessControlSystemCarInfo>lambdaQuery()
.eq(AccessControlSystemCarInfo::getTrainNumber, shipmentOrder.getTrainNumber())
.orderByDesc(AccessControlSystemCarInfo::getCreateTime)
.last("limit 1")
);
if (accessControlSystemCarInfo != null) {
shipmentOrderReport.setInTime(accessControlSystemCarInfo.getInTime());
shipmentOrderReport.setSecondWeightTime(accessControlSystemCarInfo.getSecondWeightTime());
}
}
}
package com.eastroc.common.utils;
import com.eastroc.common.consts.ArrangeConstant;
import org.jeecg.common.util.ExecutorServiceFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import java.util.concurrent.CompletableFuture;
@Component
public class TransactionSynchronizationUtil {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* mq同步到历史订单报表
*
* @param shipmentOrderId
*/
public void syncReportTransaction(String shipmentOrderId) {
try {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
CompletableFuture.runAsync(() -> {
rabbitTemplate.convertAndSend(ArrangeConstant.E_EXCHANGE, ArrangeConstant.K_SHIPMENT_ORDER_REPORT_ONE, shipmentOrderId);
}, ExecutorServiceFactory.getInstance());
}
});
} catch (
Exception e) {
e.printStackTrace();
}
}
/**
* mq同步到历史订单报表
*
* @param shipmentOrderId
*/
public void syncReport(String shipmentOrderId) {
CompletableFuture.runAsync(() -> {
rabbitTemplate.convertAndSend(ArrangeConstant.E_EXCHANGE, ArrangeConstant.K_SHIPMENT_ORDER_REPORT_ONE, shipmentOrderId);
}, ExecutorServiceFactory.getInstance());
}
}
觉得有用的话点个赞 👍🏻
呗。
❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄
💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍
🔥🔥🔥Stay Hungry Stay Foolish 道阻且长,行则将至,让我们一起加油吧!🌙🌙🌙