first commit

This commit is contained in:
easonzhu 2025-03-17 10:46:29 +08:00
commit 73feff4dbb
114 changed files with 7208 additions and 0 deletions

14
.gitignore vendored Normal file
View File

@ -0,0 +1,14 @@
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
*.log
.vscode

180
pom.xml Normal file
View File

@ -0,0 +1,180 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>DiagnoseApiServer</artifactId>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.7</version>
<relativePath/>
</parent>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.2.0.Final</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-sql</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-spring</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<!--https://doc.xiaominfo.com/knife4j/documentation/-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1.1-jre</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.3</version>
</dependency>
<dependency>
<groupId>org.ahocorasick</groupId>
<artifactId>ahocorasick</artifactId>
<version>0.6.3</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<!-- go to https://search.maven.org/search?q=tencentcloud-sdk-java and get the latest version. -->
<!-- 请到https://search.maven.org/search?q=tencentcloud-sdk-java查询所有版本最新版本如下 -->
<version>3.1.1142</version>
</dependency>
<!-- SLF4J API: 用于记录日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version> <!-- 使用适合你项目的版本 -->
</dependency>
<!-- Logback Classic: 提供 Logback 日志实现 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version> <!-- 使用适合你项目的版本 -->
</dependency>
<!-- Logback Core: Logback 的核心库 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.11</version> <!-- 使用适合你项目的版本 -->
</dependency>
<!-- Logback Slf4j binding (可以让 SLF4J 使用 Logback 作为日志实现) -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.36</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.syzb.startup.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>thin-jar</id>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.syzb.startup.Main</mainClass>
<layout>ZIP</layout>
<includes>
<include>
<groupId>nothing</groupId>
<artifactId>nothing</artifactId>
</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,51 @@
package com.common.aspect;
import com.common.annotation.Auth;
import com.common.constant.AccessRole;
import com.common.vo.BackendUserVO;
import com.rbac.service.AuthService;
import org.aspectj.lang.JoinPoint;
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.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
@Aspect
@Component
public class AuthAspect {
@Resource
AuthService authService;
@Pointcut("@annotation(com.common.annotation.Auth)")
private void pointcut() {
}
// 前置通知
@Before("pointcut()")
public void beforeCall(JoinPoint joinPoint) {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
BackendUserVO backendUser = (BackendUserVO) request.getAttribute("backendUser");
// 获取注解中的参数值
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
// 获取注解
Auth annotation = method.getAnnotation(Auth.class);
// 获取注解参数的值
AccessRole role = annotation.role();
// 验证帐号的合法性
authService.checkUserStatus(backendUser, role);
String callUrl = request.getRequestURI();
// 校验权限
//authService.checkUserPermission(backendUser, callUrl);
}
}

View File

@ -0,0 +1,45 @@
package com.diagnose.common.aspect;
import com.diagnose.common.util.RequestIdUtil;
import com.diagnose.common.util.logger.LoggerUtil;
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.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Date;
@Aspect
@Component
public class TaskAspect {
@Pointcut("@annotation(org.springframework.scheduling.annotation.Scheduled)")
private void scheduledPointcut() {
}
// 前置通知
@Before("scheduledPointcut()")
public void beforeCall(JoinPoint joinPoint) {
RequestIdUtil.setTime();
RequestIdUtil.setValue("TIME");
// 获取注解中的参数值
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
LoggerUtil.data.info(String.format("定时任务:%s:启动了", method.getName()));
}
// 最终通知
@AfterReturning(pointcut = "scheduledPointcut()")
public void afterReturningCall(JoinPoint joinPoint) {
long start = RequestIdUtil.getTime();
long time = new Date().getTime() - start;
// 获取注解中的参数值
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
LoggerUtil.data.info(String.format("定时任务:%s:耗时:%d", method.getName(), time));
}
}

View File

@ -0,0 +1,82 @@
package com.diagnose.common.aspect;
import com.alibaba.fastjson.JSONObject;
import com.diagnose.common.util.RequestIdUtil;
import com.diagnose.common.util.logger.LoggerUtil;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.http.HttpEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Aspect
@Component
public class WebLogAspect {
@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
private void requestPointcut() {
}
@Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
private void getPointcut() {
}
@Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)")
private void postPointcut() {
}
// 前置通知
@Before("requestPointcut() || getPointcut() || postPointcut()")
public void beforeCall(JoinPoint joinPoint) {
RequestIdUtil.setTime();
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
Object[] args = joinPoint.getArgs();
List<Object> arguments = new ArrayList<>();
for (Object arg : args) {
if (arg instanceof ServletRequest || arg instanceof ServletResponse || arg instanceof MultipartFile) {
//ServletRequest不能序列化从入参里排除否则报异常java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
//ServletResponse不能序列化 从入参里排除否则报异常java.lang.IllegalStateException: getOutputStream() has already been called for this response
continue;
}
arguments.add(arg);
}
if (!request.getRequestURI().contains("/swagger") && !request.getRequestURI().contains("/v3/api-docs")) {
LoggerUtil.data.info(String.format("%s:param:%s", request.getRequestURI(), JSONObject.toJSONString(arguments)));
}
}
// 最终通知
@AfterReturning(returning = "returnOb", pointcut = "requestPointcut() || getPointcut() || postPointcut())")
public void afterReturningCall(JoinPoint joinPoint, Object returnOb) {
long start = RequestIdUtil.getTime();
long time = new Date().getTime() - start;
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
if (!request.getRequestURI().contains("/swagger") && !request.getRequestURI().contains("/v3/api-docs") && !(returnOb instanceof HttpEntity)) {
LoggerUtil.data.info(String.format("%s:耗时:%d", request.getRequestURI(), time));
LoggerUtil.data.info(String.format("%s:result:%s", request.getRequestURI(), JSONObject.toJSONString(returnOb)));
}
}
// 异常通知
@AfterThrowing(value = "requestPointcut() || getPointcut() || postPointcut()", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, Exception ex) {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
if (!request.getRequestURI().contains("/swagger") && !request.getRequestURI().contains("/v3/api-docs")) {
LoggerUtil.data.info(String.format("%s:exception:%s", request.getRequestURI(), ExceptionUtils.getStackTrace(ex)));
}
}
}

View File

@ -0,0 +1,47 @@
package com.diagnose.common.config;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class JsonConfig {
static {
ParserConfig.getGlobalInstance().setSafeMode(true);
}
@Bean // 使用@Bean注入fastJsonHttpMessageConvert
public HttpMessageConverters fastJsonHttpMessageConverters() {
// 1.需要定义一个Convert转换消息的对象
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
// 2.添加fastjson的配置信息比如是否要格式化返回的json数据
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteMapNullValue, // 是否输出值为null的字段
SerializerFeature.WriteNullListAsEmpty, // 将Collection类型字段的字段空值输出为[]
// SerializerFeature.WriteNullNumberAsZero, // 将数值类型字段的空值输出为0
SerializerFeature.DisableCircularReferenceDetect, // 禁用循环引用
SerializerFeature.WriteDateUseDateFormat); //时间格式化
// 中文乱码解决方案
List<MediaType> mediaTypes = new ArrayList<>();
mediaTypes.add(new MediaType("application", "json", StandardCharsets.UTF_8));//设定json格式且编码为UTF-8
fastConverter.setSupportedMediaTypes(mediaTypes);
// 3.在convert中添加配置信息
fastConverter.setFastJsonConfig(fastJsonConfig);
return new HttpMessageConverters(fastConverter);
}
}

View File

@ -0,0 +1,22 @@
package com.diagnose.common.config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import java.util.concurrent.Executors;
@Configuration
@ConditionalOnProperty(name = "scheduledEnable", havingValue = "true")
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
// 开启定时任务多线程防止默认单线程阻塞问题超过核心线程数后还是会阻塞
taskRegistrar.setScheduler(Executors.newScheduledThreadPool(10));
}
}

View File

@ -0,0 +1,47 @@
package com.diagnose.common.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.annotations.ApiIgnore;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* 利用swagger3构建API
*
* @author yuanchao
*/
@Configuration
public class Swagger3Config {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.OAS_30)
.apiInfo(apiInfo())
.enable(true)
.select()
.apis(RequestHandlerSelectors.basePackage("com")) //扫描该包下的所有需要在Swagger中展示的API@ApiIgnore注解标注的除外
.paths(PathSelectors.any())
.build()
.ignoredParameterTypes(HttpSession.class, HttpServletRequest.class, HttpServletResponse.class)
.ignoredParameterTypes(ApiIgnore.class);
}
//创建API的基本信息这些信息会在Swagger UI中进行显示
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("数据应用API")
.description("数据应用API")
.version("1.0")
.build();
}
}

View File

@ -0,0 +1,32 @@
package com.diagnose.common.config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import java.util.Collections;
@Configuration
public class UpCorsConfiguration {
@Bean("upCorsFilter")
@ConditionalOnProperty(name = "cors.enable", havingValue = "true")
public CorsFilter corsFilter() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
//1,允许任何来源
corsConfiguration.setAllowedOriginPatterns(Collections.singletonList("*"));
//2,允许任何请求头
corsConfiguration.addAllowedHeader(CorsConfiguration.ALL);
//3,允许任何方法
corsConfiguration.addAllowedMethod(CorsConfiguration.ALL);
//4,允许凭证
corsConfiguration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfiguration);
return new CorsFilter(source);
}
}

View File

@ -0,0 +1,47 @@
package com.diagnose.common.config.cache;
import com.hazelcast.config.InMemoryFormat;
import java.util.HashMap;
import java.util.Map;
import static com.diagnose.common.config.cache.CacheKey.*;
public class CacheConfig {
public static final String DEFAULT_MAP_NAME = "default";
public static class LocalMapConfig {
public final int maxSize;
public final int liveSeconds;
public final InMemoryFormat inMemoryFormat;
LocalMapConfig(int maxSize, int liveSeconds) {
this.maxSize = maxSize;
this.liveSeconds = liveSeconds;
this.inMemoryFormat = InMemoryFormat.BINARY;
}
LocalMapConfig(int maxSize, int liveSeconds, InMemoryFormat inMemoryFormat) {
this.maxSize = maxSize;
this.liveSeconds = liveSeconds;
this.inMemoryFormat = inMemoryFormat;
}
}
public static Map<String, LocalMapConfig> getConfigMap() {
// 设置近地缓存实时同步,不采用批量提交策略
System.setProperty("hazelcast.map.invalidation.batch.enabled", "false");
// PER_NODEmax-size指定单个集群成员中map条目的最大数量这是max-size的默认策略如果使用这个配置需要注意max-size的值必须大于分区的数量默认为271
// PER_PARTITIONmax-size指定每个分区存储的map条目最大数这个策略建议不要在小规模的集群中使用因为小规模的集群单个节点包含了大量的分区在执行回收策略时会去按照分区的划分组个检查回收条件导致效率低下
// USED_HEAP_SIZE指在每个Hazelcast实例中max-size指定map所占用的内存堆的以megabytes计算兆字节最大值需要注意这个策略不能工作在in-memory-format=OBJECT因为当数据被设置为OBJECT时无法确定所占用的内存大小
// USED_HEAP_PERCENTAGE每个Hazelcast实例中max-size指定map占用内存堆的百分比例如JVM被设置有1000MB而这个值设置为max-size=10当map条目数占用的堆数据超过100MB时Hazelcast开始执行数据释放工作需要注意的是当使用这个策略时不能将in-memory-format设置为OBJECT理由同上
// FREE_HEAP_SIZEmax-size指定了单个JVM的堆最小空闲空间单位为megabytes
// FREE_HEAP_PERCENTAGEmax-size指定单个JVM的最小空闲空间的百分比例如JVM分配了1000MB的空间这个值设置为10当空闲堆只有100MB时会引发map的数据清除放行为
// 当map条数超过10000会引发map的数据清除行为
Map<String, LocalMapConfig> configMap = new HashMap<>();
configMap.put(DISTRIBUTED_LOCK, new LocalMapConfig(1000, 0));
return configMap;
}
}

View File

@ -0,0 +1,72 @@
package com.diagnose.common.config.cache;
import com.hazelcast.config.InMemoryFormat;
import java.util.HashMap;
import java.util.Map;
import static com.diagnose.common.config.cache.CacheKey.*;
public class CacheConfig {
public static final String DEFAULT_MAP_NAME = "default";
public static class LocalMapConfig {
public final int maxSize;
public final int liveSeconds;
public final InMemoryFormat inMemoryFormat;
LocalMapConfig(int maxSize, int liveSeconds) {
this.maxSize = maxSize;
this.liveSeconds = liveSeconds;
this.inMemoryFormat = InMemoryFormat.BINARY;
}
LocalMapConfig(int maxSize, int liveSeconds, InMemoryFormat inMemoryFormat) {
this.maxSize = maxSize;
this.liveSeconds = liveSeconds;
this.inMemoryFormat = inMemoryFormat;
}
}
public static Map<String, LocalMapConfig> getConfigMap() {
// 设置近地缓存实时同步,不采用批量提交策略
System.setProperty("hazelcast.map.invalidation.batch.enabled", "false");
// PER_NODEmax-size指定单个集群成员中map条目的最大数量这是max-size的默认策略如果使用这个配置需要注意max-size的值必须大于分区的数量默认为271
// PER_PARTITIONmax-size指定每个分区存储的map条目最大数这个策略建议不要在小规模的集群中使用因为小规模的集群单个节点包含了大量的分区在执行回收策略时会去按照分区的划分组个检查回收条件导致效率低下
// USED_HEAP_SIZE指在每个Hazelcast实例中max-size指定map所占用的内存堆的以megabytes计算兆字节最大值需要注意这个策略不能工作在in-memory-format=OBJECT因为当数据被设置为OBJECT时无法确定所占用的内存大小
// USED_HEAP_PERCENTAGE每个Hazelcast实例中max-size指定map占用内存堆的百分比例如JVM被设置有1000MB而这个值设置为max-size=10当map条目数占用的堆数据超过100MB时Hazelcast开始执行数据释放工作需要注意的是当使用这个策略时不能将in-memory-format设置为OBJECT理由同上
// FREE_HEAP_SIZEmax-size指定了单个JVM的堆最小空闲空间单位为megabytes
// FREE_HEAP_PERCENTAGEmax-size指定单个JVM的最小空闲空间的百分比例如JVM分配了1000MB的空间这个值设置为10当空闲堆只有100MB时会引发map的数据清除放行为
// 当map条数超过10000会引发map的数据清除行为
Map<String, LocalMapConfig> configMap = new HashMap<>();
configMap.put(DISTRIBUTED_LOCK, new LocalMapConfig(1000, 0));
configMap.put(DEPT, new LocalMapConfig(10000, 3600));
configMap.put(TAG, new LocalMapConfig(10000, 3600));
configMap.put(ADVISOR_INFO, new LocalMapConfig(10000, 300));
configMap.put(RECOMMEND, new LocalMapConfig(10000, 300));
configMap.put(USER, new LocalMapConfig(10000, 3600));
configMap.put(CAPTCHA, new LocalMapConfig(10000, 300));
configMap.put(RBAC, new LocalMapConfig(10000, 300));
configMap.put(URL_MAP, new LocalMapConfig(10000, 300, InMemoryFormat.OBJECT));
configMap.put(SCREEN, new LocalMapConfig(1000, 10, InMemoryFormat.OBJECT));
configMap.put(VIDEO_TX_ONLINE, new LocalMapConfig(1000, 20, InMemoryFormat.OBJECT));
configMap.put(VIDEO_LIVE_MESSAGE, new LocalMapConfig(10000, 86400));
configMap.put(VIDEO_LIVE, new LocalMapConfig(10000, 300));
configMap.put(VIDEO_LIVE_DELAY, new LocalMapConfig(10000, 10));
configMap.put(VIDEO_LIVE_COLUMN, new LocalMapConfig(10000, 300));
configMap.put(VIDEO_ACTIVITY, new LocalMapConfig(1000, 300));
configMap.put(VIDEO_LIVE_LIBRARY, new LocalMapConfig(1000, 300));
configMap.put(VIDEO_LIVE_USER_MAP, new LocalMapConfig(10000, 300));
configMap.put(CUSTOMER_MAP, new LocalMapConfig(10000, 3600));
configMap.put(QUESTION, new LocalMapConfig(1000, 30));
configMap.put(COURSE, new LocalMapConfig(10000, 300));
configMap.put(GROUP, new LocalMapConfig(10000, 300));
configMap.put(WX_USER, new LocalMapConfig(10000, 3600));
return configMap;
}
}

View File

@ -0,0 +1,25 @@
package com.diagnose.common.config.cache;
import java.io.Serializable;
public class CacheKey {
public static class OnlyKeyObj implements Serializable {
private static final long serialVersionUID = 1L;
@Override
public boolean equals(Object obj) {
return obj instanceof OnlyKeyObj;
}
}
// 缓存空对象,防止缓存穿透
public static final Object ONLY_KEY_OBJ = new OnlyKeyObj();
// 分布式锁
public static final String DISTRIBUTED_LOCK = "distributed_lock";
public static class LockKey {
}
}

View File

@ -0,0 +1,373 @@
package com.diagnose.common.config.cache;
import java.io.Serializable;
public class CacheKey {
public static class OnlyKeyObj implements Serializable {
private static final long serialVersionUID = 1L;
@Override
public boolean equals(Object obj) {
return obj instanceof OnlyKeyObj;
}
}
// 缓存空对象,防止缓存穿透
public static final Object ONLY_KEY_OBJ = new OnlyKeyObj();
// 分布式锁
public static final String DISTRIBUTED_LOCK = "distributed_lock";
public static class LockKey {
// 清除多余定时器日志
public static final String CLEAR_HISTORY_SCHEDULE_LOG = "clearHistoryScheduleLog";
// 结束直播中/暂停中的直播 分布式锁(字符串常量)
public static final String STOP_LIVING_VIDEO_LOCK = "stop_living_video_lock";
// 刷新视频直播状态 分布式锁(字符串常量)
public static final String UPDATE_VIDEO_LIVE_STATUS_LOCK = "update_video_live_status_lock";
// 从cache刷新视频直播播放量到DB 分布式锁(字符串常量)
public static final String SAVE_VIDEO_COUNT_TO_DB_LOCK = "save_video_count_to_db_lock";
// 购物车分布式锁
public static final String VIDEO_LIVE_HISTORY_LOCK = "video_live_history_lock";
public static final String SAVE_VIDEO_USER_DATA_TO_DB_LOCK = "save_video_user_data_to_db_lock";
public static final String SAVE_VIDEO_CUSTOMER_DATA_TO_DB_LOCK = "save_video_customer_data_to_db_lock";
public static final String LIVE_NOTIFY_LOCK = "live_notify_lock";
public static final String REFRESH_TRANSCODE_STATUS = "refresh_transcode_status";
public static final String SAVE_SHORT_VIDEO_WATCH_SECONDS = "save_short_video_watch_seconds";
public static final String COLLECT_LAST_WEEK_LOCK = "collect_last_week_lock";
public static final String COLLECT_LIVING_VIDEO_LOCK = "collect_living_video_lock";
public static final String COLLECT_RECENT_END_VIDEO_LOCK = "collect_recent_end_video_lock";
public static final String LOAD_USER_BLACK_LIST = "load_user_black_list";
public static final String SAVE_MESSAGE_READ = "save_message_read";
public static final String SAVE_GROUP_USER = "save_group_user";
public static final String COLLECT_GROUP_DATA = "collect_group_data";
public static final String SYNC_ORDER = "sync_order";
public static final String SYNC_MODULE_USER = "sync_module_user";
}
// 消息主题
public static class MessageTopicKey {
public static final String VIDEO_MSG = "video_msg";
public static final String VIDEO_NOTIFY = "video_notify";
public static final String ADMIN_USER = "admin_user";
public static final String VIDEO_REPORT = "video_report";
public static final String PC_ADVISOR = "pc_advisor";
public static final String PC_AUDIENCE = "pc_audience";
public static final String SESSION_VIDEO_MSG = "session_video_msg";
}
// 后台用户
public static final String USER = "user";
public static final class UserKey {
// userId -> userName
public static final String USER_MAP = "user_map";
// staffNo -> User
public static final String STAFF_MAP = "staff_map";
public static final String JWT_EXPIRE_MAP = "jwt_expire_map";
public static final String LOGOUT_JWT_MAP = "logout_jwt_map";
public static final String USER_DEPT_MAP = "user_dept_map";
public static final String USER_BLACK_LIST = "user_black_list";
public static final String USER_LOGIN_MAP = "user_login_map";
}
// 部门(营业部/分公司)
public static final String DEPT = "dept";
public static class DeptKey {
// deptId -> deptName
public static final String DEPT_MAP = "dept_map";
// advisorId -> dept
public static final String ADVISOR_DEPT_MAP = "advisor_dept_map";
// advisorId -> deptId
public static final String ADVISOR_DEPT_ID_MAP = "advisor_dept_id_map";
// advisorId -> dept
public static final String ADVISOR_RELATION_DEPT_MAP = "advisor_relation_dept_map";
// userId -> deptId
public static final String USER_DEPT_MAP = "user_dept_map";
}
// RBAC权限
public static final String RBAC = "rbac";
public static class RbacKey {
public static final String ROLE_PERMISSIONS_URL = "role_permissions_url|";
public static final String ALL_PERMISSIONS_URL = "all_permissions_url|";
}
// 管理后台图形验证码
public static final String CAPTCHA = "captcha";
// 标签
public static final String TAG = "tag";
public static class TagKey {
// tagId -> tagName
public static final String TAG_MAP = "tag_map";
}
// 投顾信息
public static final String ADVISOR_INFO = "advisor_info";
public static final class AdvisorInfoKey {
// advisorId -> AdvisorBasic
public static final String ADVISOR_MAP = "advisor_map";
// advisorId -> AdvisorBasicVO
public static final String ADVISOR_VO_MAP = "advisor_vo_map";
// userId -> AdvisorBasic
public static final String USER_ADVISOR_MAP = "user_advisor_map";
// SortedMap<AdvisorSortEntity, Integer> (followCount, advisorId) -> advisorId
public static final String APP_FOLLOW_COUNT_LIST = "app_follow_count_list";
// SortedMap<AdvisorSortEntity, Integer> (readCount, advisorId) -> advisorId
public static final String APP_READ_COUNT_LIST = "app_read_count_list";
// SortedMap<AdvisorSortEntity, Integer> (productCount, advisorId) -> advisorId
public static final String APP_PRODUCT_COUNT_LIST = "app_product_count_list";
// AdvisorInfoAppVO
public static final String APP_OBJ = "app_obj|";
// advisorId -> PN Counter<followCount>
public static final String APP_FOLLOW_COUNT = "app_follow_count|";
// userId -> List<AdvisorFollow>
public static final String USER_FOLLOW_ADVISOR = "user_follow_advisor|";
public static final String USER_ADVISOR_DEPT_MAP = "user_advisor_dept_map";
}
public static final String ADVERT = "advert";
public static class AdvertKey {
public static final String APP_ADVERT_LIST = "app_advert_list|";
}
public static final String RECOMMEND = "recommend";
public static class RecommendKey {
public static final String APP_RECOMMEND_LIST = "app_recommend_list|";
}
// 视频直播信息
public static final String VIDEO_LIVE = "video_live";
// 视频直播自定义缓存过期时间put方法设置的过期时间不生效所以这样处理
public static final String VIDEO_LIVE_DELAY = "video_live_delay";
public static class VideoLiveKey {
// videoId -> VideoLive
public static final String VIDEO_INFO = "video_info|";
// videoId -> VideoLive 10s缓存
public static final String VIDEO_INFO_DELAY = "video_info_delay|";
// SortedSet<VideoSortEntity> (startTime auditTime)
public static final String APP_LIST = "app_list";
public static final String APP_VIDEO_CART = "app_video_cart|";
public static final String APP_VIDEO_TAG = "app_video_tag|";
public static final String VIDEO_INFO_TAG = "video_info_tag|";
public static final String ADVISOR_NO_PLAY_LIST = "advisor_no_play_list";
// 与直播相关产品的权限
public static final String VIDEO_OTHER_AUTH = "video_other_auth|";
public static final String VIDEO_LIVE_RECENT_PUSH_CART = "video_live_recent_push_cart|";
public static final String ONLINE_COUNT = "online_count|";
public static final String APP_ADVISOR_LIST = "app_advisor_list|";
public static final String APP_DEPT_LIST = "app_dept_list|";
public static final String APP_ADVISOR_KEY_SET = "app_advisor_key_set|";
public static final String APP_DEPT_KEY_SET = "app_dept_key_set|";
}
// 视频直播专栏
public static final String VIDEO_LIVE_COLUMN = "video_live_column";
public static class VideoLiveColumnKey {
// List<columnId>
public static final String COLUMN_IDS = "column_ids";
// columnId -> VideoLiveColumn
public static final String COLUMN_INFO = "column_info|";
// columnId -> Sort<Integer> (videoId)
public static final String VIDEO_IDS = "video_ids|";
public static final String LATEST_LIVING_VIDEO = "latest_living_video|";
public static final String HIS_GUEST = "his_guest|";
public static final String APP_FOLLOW_COUNT = "app_follow_count|";
public static final String APP_PLAN_LIST = "app_plan_list";
public static final String VIDEO_COLUMN = "video_column";
}
// 视频直播资源
public static final String VIDEO_LIVE_LIBRARY = "video_live_library";
public static class VideoLiveLibraryKey {
// libraryId -> VideoLiveLibrary
public static final String LIBRARY_INFO = "library_info|";
// videoId -> List<libraryId>
public static final String LIBRARY_IDS = "library_ids|";
}
public static class VideoRecordKey {
// 购物车点击数
public static final String CART_READ_COUNT = "cart_read_count|";
// 浏览数
// videoId -> PV Counter<Integer>
public static final String READ_COUNT = "read_count|";
// 用户点赞
// videoId -> UV Counter<Integer>
public static final String FAVOR_USER_COUNT = "favor_user_count|";
// videoId -> Set<Integer> (userId)
public static final String USER_FAVOR_IDS = "user_favor_ids|";
// 视频分享
// videoId -> PV Counter<Integer>
public static final String SHARE_COUNT = "share_count|";
// 视频评论
// videoId -> PV Counter<Integer>
public static final String MESS_COUNT = "mess_count|";
// 用户预约
// userId -> Set<Integer> (videoId)
public static final String USER_SUBSCRIBE_IDS = "user_subscribe_ids|";
// videoId -> Set<String> (userId)
public static final String VIDEO_SUBSCRIBE_IDS = "user_subscribe_ids|";
public static final String USER_BROWSE_IDS = "user_browse_ids|";
public static final String USER_FOLLOW_COLUMN = "user_follow_column|";
public static final String TEMP_FAVOR_LIST = "temp_favor_list";
// 投顾关注
// advisorId -> Set<String> (userId)
public static final String ADVISOR_FOLLOW_IDS = "advisor_follow_ids|";
}
// 视频互动消息
public static final String VIDEO_LIVE_MESSAGE = "video_live_message";
public static class VideoLiveMessageKey {
// messageId -> VideoLiveMessage
public static final String MESSAGE_INFO = "message_info|";
// videoId -> List<Integer>(messageId)
public static final String MESSAGE_IDS = "message_ids|";
public static final String MESSAGE_IDS_ADVISOR = "message_ids_advisor|";
// 互动人数
// videoId -> UV Counter<Integer>
public static final String USER_COUNT = "read_user_count|";
public static final String MESSAGE_TOP_20 = "message_top_20|";
public static final String MESSAGE_ADVISOR_TOP_20 = "message_advisor_top_20|";
public static final String MESSAGE_COUNT = "message_count|";
}
// 评论
public static final String COMMENT = "comment";
public static class CommentKey {
public static final String APP_COMMENT_SORT_LIST = "app_comment_sort_list|";
public static final String APP_COMMENT_OBJ = "app_comment_obj|";
}
// 评论禁言
public static final String COMMENT_BLACK = "comment_black";
public static class CommentBlackKey {
public static final String ALL_BLACK_COMMENT = "all_black_comment";
public static final String ALL_BLACK_USER = "all_black_user";
}
public static final String VIDEO_ACTIVITY = "video_activity";
public static class VideoActivityKey {
public static final String VIDEO_ACTIVITY_LIST = "video_activity_list";
public static final String VIDEO_ACTIVITY_OBJ = "video_activity_obj|";
}
public static final String VIDEO_LIVE_HIS_DATE = "video_live_his_date";
public static final String QUESTION = "question";
public static class QuestionKey {
public static final String QUESTION_DETAILS = "question_details|";
}
public static final class OnlineLineKey {
public static final String USER_VIDEO_TOTAL_ONLINE = "user_video_total_online|";
}
public static final String URL_MAP = "url_map";
public static final class URL_KEY {
public static final String URL_KEY = "url_key|";
public static final String URL_KEY_VIDEO_ID = "url_key_video_id|";
public static final String URL_KEY_SHORT_VIDEO_ID = "url_key_short_video_id|";
}
public static final String VIDEO_TX_ONLINE = "video_tx_online|";
public static final class TXKey {
public static final String VIDEO_ONLINE_TX = "video_online_tx|";
}
public static final String CUSTOMER_MAP = "customer_map";
public static final class CustomerKey {
public static final String CUSTOMER_DETAILS = "customer_details|";
public static final String VIDEO_CUSTOMER_SET = "video_customer_set";
public static final String VIDEO_CUSTOMER_SALE_SET = "video_customer_sale_set";
public static final String CUSTOMER_SALE = "customer_sale|";
}
public static final String WX_USER = "wx_user";
public static final class WxUserKey {
public static final String USER = "user|";
}
public static final String SCREEN = "screen";
public static final class ScreenKey {
public static final String SCREEN_VIDEO_INFO = "screen_video_info|";
}
public static final String VIDEO_LIVE_USER_MAP = "video_live_user_map";
public static final class VideoLiveUserKey {
public static final String LIVE_USER_OBJ = "live_user_obj|";
}
public static final String COURSE = "course";
public static final class CourseKey {
public static final String COURSE_INFO = "course_info|";
public static final String COURSE_CONTENT = "course_content|";
public static final String SERIAL_INFO = "serial_info|";
public static final String SERIAL_CONTENT = "serial_content|";
public static final String PAGE = "page|";
public static final String SHORT_VIDEO = "short_video|";
public static final String SHORT_VIDEO_FAVOR_USER_IDS = "favor_user_ids|";
public static final String MAIN_TAB = "main_tab|";
public static final String MAIN_COURSE_LIST = "main_course_list|";
public static final String MAIN_SHORT_VIDEO_LIST = "main_short_video_list|";
public static final String PC_COURSE_LIST = "pc_course_list|";
public static final String SALE_USER_WORK_WEIXIN_QRCODE_IMAGE = "sale_user_work_weixin_qrcode_image|";
public static final String SHORT_VIDEO_WATCH_LIST = "short_video_watch_list";
public static final String COURSE_PACKAGE = "course_package|";
public static final String COURSE_PACKAGE_CONTENT = "course_package_content|";
}
public static final String GROUP = "group";
// 消息主题
public static class GroupMessageTopicKey {
public static final String ADMIN_GROUP_TOPIC = "admin_group_topic";
public static final String ADMIN_PRIVATE_TOPIC = "admin_private_topic";
public static final String ADMIN_SESSION_TOPIC = "admin_session_topic";
public static final String APP_GROUP_TOPIC = "app_group_topic";
public static final String APP_PRIVATE_TOPIC = "app_private_topic";
public static final String APP_SESSION_TOPIC = "app_session_topic";
}
public static class GroupKey {
public static final String GROUP_INFO = "group_info|";
public static final String MAIN_GROUP_LIST = "group_main_list|";
public static final String GROUP_MESSAGE_LIST = "group_message_list|";
public static final String GROUP_MESSAGE_DETAIL = "group_message_detail|";
public static final String USER_TOTAL_ONLINE = "user_total_online|";
public static final String TEMP_READ_LIST = "temp_read_list";
public static final String GROUP_MESSAGE_DATE_ID_MAP = "group_message_date_id_map|";
public static final String ONLINE_COUNT = "online_count|";
}
public static final String GROUP_ONLINE_USER = "group_online_user";
public static final String VIDEO_ONLINE_USER = "video_online_user";
}

View File

@ -0,0 +1,91 @@
package com.diagnose.common.config.cache;
import com.hazelcast.config.*;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PreDestroy;
import java.util.*;
@Configuration
public class HazelcastConfiguration {
@Value("${hazelcast.members}")
private String members;
@Value("${hazelcast.serverPort}")
private Integer serverPort;
private Set<String> cacheNameSet = new HashSet<>();
@Bean
public HazelcastInstance hazelcastInstance() {
List<String> memberList = Arrays.asList(members.split(","));
Config config = new Config();
// hazelcast作为缓存服务端监听的端口
config.getNetworkConfig().setPort(serverPort);
// 如果目标缓存端口被占用禁止重试其他端口
config.getNetworkConfig().setPortAutoIncrement(false);
config.getNetworkConfig().getJoin().getAutoDetectionConfig().setEnabled(false);
config.getNetworkConfig().getJoin().getTcpIpConfig().setEnabled(true).setMembers(memberList);
config.getJetConfig().setEnabled(true);
String clusterName = "hazelcast-cluster";
String instanceName = clusterName + "." + "localIP";
config.setInstanceName(instanceName);
config.setClusterName(clusterName);
for (Map.Entry<String, CacheConfig.LocalMapConfig> entry : CacheConfig.getConfigMap().entrySet()) {
String cacheName = entry.getKey();
cacheNameSet.add(cacheName);
config.addMapConfig(new MapConfig()
.setName(cacheName)
.setEvictionConfig(new EvictionConfig()
.setEvictionPolicy(EvictionPolicy.LRU)
.setSize(entry.getValue().maxSize)
.setMaxSizePolicy(MaxSizePolicy.PER_NODE))
.setTimeToLiveSeconds(entry.getValue().liveSeconds)
// 近地缓存设置
.setNearCacheConfig(new NearCacheConfig()
.setInMemoryFormat(entry.getValue().inMemoryFormat)
.setCacheLocalEntries(true)
.setTimeToLiveSeconds(entry.getValue().liveSeconds / 2)
.setEvictionConfig(new EvictionConfig()
.setMaxSizePolicy(MaxSizePolicy.ENTRY_COUNT)
.setSize(entry.getValue().maxSize / 2))
// 预加载近地缓存,防止近地缓存穿透(暂时不启用)
// .setPreloaderConfig(new NearCachePreloaderConfig()
// .setEnabled(true))
)
);
}
// 默认map配置主要用于USER_VIDEO_TOTAL_ONLINE + videoId
config.addMapConfig(new MapConfig()
.setName(CacheConfig.DEFAULT_MAP_NAME)
.setNearCacheConfig(new NearCacheConfig()
.setInMemoryFormat(InMemoryFormat.OBJECT)
.setCacheLocalEntries(true)
.setTimeToLiveSeconds(3600 * 10)
.setMaxIdleSeconds(3600 * 2)
.setEvictionConfig(new EvictionConfig()
.setMaxSizePolicy(MaxSizePolicy.ENTRY_COUNT)
.setSize(100000))
));
HazelcastInstance instance = Hazelcast.newHazelcastInstance(config);
return instance;
}
public void cleanCache() {
for (String cacheName : cacheNameSet) {
hazelcastInstance().getMap(cacheName).clear();
}
}
@PreDestroy
public void destroy() {
hazelcastInstance().shutdown();
}
}

View File

@ -0,0 +1,21 @@
package com.diagnose.common.config.mybatis;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.extension.injector.methods.InsertBatchSomeColumn;
import java.util.List;
public class EasySqlInjector extends DefaultSqlInjector {
public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
methodList.add(new InsertBatchSomeColumn());
methodList.add(new SaveBatchSomeColumn(i -> i.getFieldFill() == FieldFill.INSERT_UPDATE));
methodList.add(new Save());
return methodList;
}
}

View File

@ -0,0 +1,26 @@
package com.diagnose.common.config.mybatis;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan("com.diagnose")
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
@Bean
public EasySqlInjector easySqlInjector() {
return new EasySqlInjector();
}
}

View File

@ -0,0 +1,26 @@
package com.diagnose.common.config.mybatis;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan("com.syzb")
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
@Bean
public EasySqlInjector easySqlInjector() {
return new EasySqlInjector();
}
}

View File

@ -0,0 +1,64 @@
package com.diagnose.common.config.mybatis;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import java.util.stream.Collectors;
public class Save extends AbstractMethod {
public static final String saveSql = "<script>\nINSERT INTO %s %s VALUES %s ON DUPLICATE KEY UPDATE %s\n</script>";
public static final String methodName = "save";
public Save() {
super(methodName);
}
public Save(String name) {
super(name);
}
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;
String columnScript = SqlScriptUtils.convertTrim(tableInfo.getAllInsertSqlColumnMaybeIf(null),
LEFT_BRACKET, RIGHT_BRACKET, null, COMMA);
String valuesScript = SqlScriptUtils.convertTrim(tableInfo.getAllInsertSqlPropertyMaybeIf(null),
LEFT_BRACKET, RIGHT_BRACKET, null, COMMA);
String updateScript = tableInfo.getFieldList().stream().map(TableFieldInfo::getColumn).map(column ->
String.format("<if test=\"%s != null\">%s = VALUES(%s),</if>", column, column, column)
).collect(Collectors.joining(NEWLINE));
updateScript = SqlScriptUtils.convertTrim(updateScript, EMPTY, EMPTY, null, COMMA);
String keyProperty = null;
String keyColumn = null;
// 表包含主键处理逻辑,如果不包含主键当普通字段处理
if (StrUtil.isNotBlank(tableInfo.getKeyProperty())) {
if (tableInfo.getIdType() == IdType.AUTO) {
/* 自增主键 */
keyGenerator = Jdbc3KeyGenerator.INSTANCE;
keyProperty = tableInfo.getKeyProperty();
keyColumn = tableInfo.getKeyColumn();
} else {
if (null != tableInfo.getKeySequence()) {
keyGenerator = TableInfoHelper.genKeyGenerator(methodName, tableInfo, builderAssistant);
keyProperty = tableInfo.getKeyProperty();
keyColumn = tableInfo.getKeyColumn();
}
}
}
String sql = String.format(saveSql, tableInfo.getTableName(), columnScript, valuesScript, updateScript);
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
return this.addInsertMappedStatement(mapperClass, modelClass, methodName, sqlSource, keyGenerator, keyProperty, keyColumn);
}
}

View File

@ -0,0 +1,99 @@
package com.diagnose.common.config.mybatis;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class SaveBatchSomeColumn extends AbstractMethod {
private static final String methodName = "saveBatchSomeColumn";
/**
* 字段筛选条件
*/
private Predicate<TableFieldInfo> predicate;
public Predicate<TableFieldInfo> getPredicate() {
return predicate;
}
public void setPredicate(Predicate<TableFieldInfo> predicate) {
this.predicate = predicate;
}
/**
* 默认方法名
*/
public SaveBatchSomeColumn() {
super(methodName);
}
/**
* 默认方法名
*
* @param predicate 字段筛选条件
*/
public SaveBatchSomeColumn(Predicate<TableFieldInfo> predicate) {
super(methodName);
this.predicate = predicate;
}
/**
* @param name 方法名
* @param predicate 字段筛选条件
* @since 3.5.0
*/
public SaveBatchSomeColumn(String name, Predicate<TableFieldInfo> predicate) {
super(name);
this.predicate = predicate;
}
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;
List<TableFieldInfo> fieldList = tableInfo.getFieldList();
String insertSqlColumn = tableInfo.getKeyInsertSqlColumn(true, false) +
this.filterTableFieldInfo(fieldList, predicate, TableFieldInfo::getInsertSqlColumn, EMPTY);
String columnScript = LEFT_BRACKET + (insertSqlColumn.isEmpty() ? "" : insertSqlColumn.substring(0, insertSqlColumn.length() - 1)) + RIGHT_BRACKET;
String insertSqlProperty = tableInfo.getKeyInsertSqlProperty(true, ENTITY_DOT, false) +
this.filterTableFieldInfo(fieldList, predicate, i -> i.getInsertSqlProperty(ENTITY_DOT), EMPTY);
insertSqlProperty = LEFT_BRACKET + (insertSqlProperty.isEmpty() ? "" : insertSqlProperty.substring(0, insertSqlProperty.length() - 1)) + RIGHT_BRACKET;
String valuesScript = SqlScriptUtils.convertForeach(insertSqlProperty, "list", null, ENTITY, COMMA);
String updateScript = fieldList.stream().filter(predicate == null ? x -> true : predicate).map(TableFieldInfo::getColumn).map(column ->
String.format("%s = VALUES(%s),", column, column)).collect(Collectors.joining(NEWLINE));
updateScript = SqlScriptUtils.convertTrim(updateScript, EMPTY, EMPTY, null, COMMA);
String keyProperty = null;
String keyColumn = null;
// 表包含主键处理逻辑,如果不包含主键当普通字段处理
if (tableInfo.havePK()) {
if (tableInfo.getIdType() == IdType.AUTO) {
/* 自增主键 */
keyGenerator = Jdbc3KeyGenerator.INSTANCE;
keyProperty = tableInfo.getKeyProperty();
keyColumn = tableInfo.getKeyColumn();
} else {
if (null != tableInfo.getKeySequence()) {
keyGenerator = TableInfoHelper.genKeyGenerator(methodName, tableInfo, builderAssistant);
keyProperty = tableInfo.getKeyProperty();
keyColumn = tableInfo.getKeyColumn();
}
}
}
String sql = String.format(Save.saveSql, tableInfo.getTableName(), columnScript, valuesScript, updateScript);
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
return this.addInsertMappedStatement(mapperClass, modelClass, methodName, sqlSource, keyGenerator, keyProperty, keyColumn);
}
}

View File

@ -0,0 +1,25 @@
package com.diagnose.common.constant;
public enum ScheduleLogResult {
RUNNING(1, "执行中"),
SUCCESS(2, "成功"),
FAILURE(3, "失败");
public final Integer value;
public final String name;
ScheduleLogResult(Integer value, String name) {
this.value = value;
this.name = name;
}
public Integer getValue() {
return value;
}
public String getName() {
return name;
}
}

View File

@ -0,0 +1,208 @@
package com.diagnose.common.entity;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.diagnose.common.constant.ScheduleLogResult;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* <p>
*
* </p>
*
* @author helloSyzb
* @since 2022-11-09
*/
public class ScheduleLog implements Serializable {
/**
* 同步记录ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 服务名
*/
@TableField("server_name")
private String serverName;
/**
* 定时任务名称
*/
@TableField("schedule_name")
private String scheduleName;
/**
* 执行时间
*/
private LocalDate date;
/**
* 实际开始时间
*/
@TableField("start_time")
private LocalDateTime startTime;
/**
* 实际结束时间
*/
@TableField("end_time")
private LocalDateTime endTime;
/**
* 执行结果:1:执行中 2:成功 3:失败
*/
private Integer result;
/**
* 附加信息json
*/
private String ext;
/**
* 异常信息
*/
private String error;
/**
* 执行机器IP
*/
private String ip;
public static ScheduleLog start(String scheduleName, String ip) {
String server = System.getProperty("server.server");
ScheduleLog log = new ScheduleLog();
log.setServerName(server);
log.setScheduleName(scheduleName);
log.setDate(LocalDate.now());
log.setStartTime(LocalDateTime.now());
log.setResult(ScheduleLogResult.RUNNING.value);
log.setIp(ip);
return log;
}
public static ScheduleLog success(Integer id, String ext) {
ScheduleLog log = new ScheduleLog();
log.setId(id);
log.setEndTime(LocalDateTime.now());
log.setResult(ScheduleLogResult.SUCCESS.value);
if (StrUtil.isNotEmpty(ext)) {
log.setExt(ext);
}
return log;
}
public static ScheduleLog error(Integer id, String error) {
ScheduleLog log = new ScheduleLog();
log.setId(id);
log.setEndTime(LocalDateTime.now());
log.setResult(ScheduleLogResult.FAILURE.value);
if (StrUtil.isNotEmpty(error)) {
log.setError(error);
}
return log;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getServerName() {
return serverName;
}
public void setServerName(String serverName) {
this.serverName = serverName;
}
public String getScheduleName() {
return scheduleName;
}
public void setScheduleName(String scheduleName) {
this.scheduleName = scheduleName;
}
public LocalDate getDate() {
return date;
}
public void setDate(LocalDate date) {
this.date = date;
}
public LocalDateTime getStartTime() {
return startTime;
}
public void setStartTime(LocalDateTime startTime) {
this.startTime = startTime;
}
public LocalDateTime getEndTime() {
return endTime;
}
public void setEndTime(LocalDateTime endTime) {
this.endTime = endTime;
}
public Integer getResult() {
return result;
}
public void setResult(Integer result) {
this.result = result;
}
public String getExt() {
return ext;
}
public void setExt(String ext) {
this.ext = ext;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
@Override
public String toString() {
return "ScheduleLog{" +
"id=" + id +
", serverName=" + serverName +
", scheduleName=" + scheduleName +
", date=" + date +
", startTime=" + startTime +
", endTime=" + endTime +
", result=" + result +
", ext=" + ext +
", error=" + error +
", ip=" + ip +
"}";
}
}

View File

@ -0,0 +1,84 @@
package com.diagnose.common.generator;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.Scanner;
// 演示例子执行 main 方法控制台输入模块表名回车自动生成对应项目目录中
public class CodeGenerator {
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
String help = "请输入" + tip + "";
System.out.println(help);
if (scanner.hasNext()) {
String ipt = scanner.nextLine();
if (StrUtil.isNotBlank(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "");
}
public static void main(String[] args) {
DataSourceConfig dsc = new DataSourceConfig
.Builder("jdbc:mysql://47.96.178.171:3306/db_upsync?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8",
"eason",
"mysql2025easonzhu")
// .Builder("jdbc:mysql://172.16.9.44:3306/db_crm_dyqh?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false",
// "taf",
// "taf2015")
.build();
// 代码生成器
AutoGenerator mpg = new AutoGenerator(dsc);
String moduleName = scanner("模块名");
String projectPath = System.getProperty("user.dir") + "";
// 全局配置
GlobalConfig globalConfig = new GlobalConfig
.Builder()
.outputDir(projectPath + "/src/main/java")
.author("helloSyzb")
.openDir(false)
.fileOverride()
.build();
// 包配置
PackageConfig packageConfig = new PackageConfig
.Builder()
.parent("com.diagnose")
.moduleName(moduleName)
.build();
// 配置模板
TemplateConfig templateConfig = new TemplateConfig
.Builder()
.mapperXml(null)
.service(null, null)
.controller(null)
.build();
// 策略配置
StrategyConfig strategyConfig = new StrategyConfig
.Builder()
.addInclude(scanner("表名,多个英文逗号分割").split(","))
.entityBuilder()
.naming(NamingStrategy.underline_to_camel)
.controllerBuilder()
.enableRestStyle()
.build();
mpg.global(globalConfig);
mpg.packageInfo(packageConfig);
mpg.template(templateConfig);
mpg.strategy(strategyConfig);
mpg.execute(new FreemarkerTemplateEngine());
}
}

View File

@ -0,0 +1,84 @@
package com.diagnose.common.generator;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.Scanner;
// 演示例子执行 main 方法控制台输入模块表名回车自动生成对应项目目录中
public class CodeGenerator {
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
String help = "请输入" + tip + "";
System.out.println(help);
if (scanner.hasNext()) {
String ipt = scanner.nextLine();
if (StrUtil.isNotBlank(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "");
}
public static void main(String[] args) {
DataSourceConfig dsc = new DataSourceConfig
.Builder("jdbc:mysql://47.96.178.171:3306/db_upsync?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8",
"eason",
"mysql2025easonzhu")
// .Builder("jdbc:mysql://172.16.9.44:3306/db_crm_dyqh?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false",
// "taf",
// "taf2015")
.build();
// 代码生成器
AutoGenerator mpg = new AutoGenerator(dsc);
String moduleName = scanner("模块名");
String projectPath = System.getProperty("user.dir") + "";
// 全局配置
GlobalConfig globalConfig = new GlobalConfig
.Builder()
.outputDir(projectPath + "/src/main/java")
.author("helloSyzb")
.openDir(false)
.fileOverride()
.build();
// 包配置
PackageConfig packageConfig = new PackageConfig
.Builder()
.parent("com.syzb")
.moduleName(moduleName)
.build();
// 配置模板
TemplateConfig templateConfig = new TemplateConfig
.Builder()
.mapperXml(null)
.service(null, null)
.controller(null)
.build();
// 策略配置
StrategyConfig strategyConfig = new StrategyConfig
.Builder()
.addInclude(scanner("表名,多个英文逗号分割").split(","))
.entityBuilder()
.naming(NamingStrategy.underline_to_camel)
.controllerBuilder()
.enableRestStyle()
.build();
mpg.global(globalConfig);
mpg.packageInfo(packageConfig);
mpg.template(templateConfig);
mpg.strategy(strategyConfig);
mpg.execute(new FreemarkerTemplateEngine());
}
}

View File

@ -0,0 +1,72 @@
package com.diagnose.common.handler;
import com.diagnose.common.result.ResponseStatus;
public class BizException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* 错误码
*/
protected Integer errorCode = -1;
/**
* 错误信息
*/
protected String errorMsg;
public BizException() {
super();
}
public BizException(ResponseStatus responseStatus) {
super(responseStatus.code.toString() + "," + responseStatus.message);
this.errorCode = responseStatus.code;
this.errorMsg = responseStatus.message;
}
public BizException(ResponseStatus responseStatus, String extendMsg) {
super(responseStatus.code.toString() + "," + responseStatus.message + "," + extendMsg);
this.errorCode = responseStatus.code;
this.errorMsg = extendMsg;
}
public BizException(ResponseStatus responseStatus, Throwable cause) {
super(responseStatus.code.toString() + "," + responseStatus.message, cause);
this.errorCode = responseStatus.code;
this.errorMsg = responseStatus.message;
}
public BizException(String errorMsg) {
super(errorMsg);
this.errorMsg = errorMsg;
}
public BizException(Integer errorCode, String errorMsg) {
super(errorCode.toString() + "," + errorMsg);
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
public BizException(Integer errorCode, String errorMsg, Throwable cause) {
super(errorCode.toString() + "," + errorMsg, cause);
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
public Integer getErrorCode() {
return errorCode;
}
public void setErrorCode(Integer errorCode) {
this.errorCode = errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
}

View File

@ -0,0 +1,72 @@
package com.diagnose.common.handler;
import com.syzb.common.result.ResponseStatus;
public class BizException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* 错误码
*/
protected Integer errorCode = -1;
/**
* 错误信息
*/
protected String errorMsg;
public BizException() {
super();
}
public BizException(ResponseStatus responseStatus) {
super(responseStatus.code.toString() + "," + responseStatus.message);
this.errorCode = responseStatus.code;
this.errorMsg = responseStatus.message;
}
public BizException(ResponseStatus responseStatus, String extendMsg) {
super(responseStatus.code.toString() + "," + responseStatus.message + "," + extendMsg);
this.errorCode = responseStatus.code;
this.errorMsg = extendMsg;
}
public BizException(ResponseStatus responseStatus, Throwable cause) {
super(responseStatus.code.toString() + "," + responseStatus.message, cause);
this.errorCode = responseStatus.code;
this.errorMsg = responseStatus.message;
}
public BizException(String errorMsg) {
super(errorMsg);
this.errorMsg = errorMsg;
}
public BizException(Integer errorCode, String errorMsg) {
super(errorCode.toString() + "," + errorMsg);
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
public BizException(Integer errorCode, String errorMsg, Throwable cause) {
super(errorCode.toString() + "," + errorMsg, cause);
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
public Integer getErrorCode() {
return errorCode;
}
public void setErrorCode(Integer errorCode) {
this.errorCode = errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
}

View File

@ -0,0 +1,97 @@
package com.diagnose.common.handler;
import com.google.common.collect.ImmutableSet;
import com.diagnose.common.result.CommonResult;
import com.diagnose.common.result.ResponseStatus;
import com.diagnose.common.util.logger.LoggerUtil;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import java.util.Set;
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理自定义的业务异常
*
* @param req
* @param e
* @return
*/
@ExceptionHandler(value = BizException.class)
@ResponseBody
public CommonResult<Void> bizExceptionHandler(HttpServletRequest req, BizException e) {
LoggerUtil.error("发生业务异常:" + req.getRequestURI() + ":" + ExceptionUtils.getStackTrace(e));
return CommonResult.error(e.getErrorCode(), e.getErrorMsg());
}
@ExceptionHandler({ConstraintViolationException.class})
@org.springframework.web.bind.annotation.ResponseStatus(HttpStatus.OK)
@ResponseBody
public CommonResult<Void> handleConstraintViolationException(HttpServletRequest req, ConstraintViolationException ex) {
LoggerUtil.error("GET参数异常:" + req.getRequestURI() + ":" + ExceptionUtils.getStackTrace(ex));
return CommonResult.error(ResponseStatus.PARM_ERROR.code, ex.getMessage());
}
// 入参错误返回200,统一定义返回信息
@ExceptionHandler({MethodArgumentNotValidException.class})
@org.springframework.web.bind.annotation.ResponseStatus(HttpStatus.OK)
@ResponseBody
public CommonResult<Void> handleMethodArgumentNotValidException(HttpServletRequest req, MethodArgumentNotValidException ex) {
StringBuilder sb = new StringBuilder();
for (FieldError err : ex.getBindingResult().getFieldErrors()) {
sb.append("字段:")
.append(err.getField())
.append(",传入值:")
.append(err.getRejectedValue())
.append(",")
.append(err.getDefaultMessage())
.append("; ");
}
LoggerUtil.error("POST参数异常:" + req.getRequestURI() + ":" + ExceptionUtils.getStackTrace(ex));
return CommonResult.error(ResponseStatus.PARM_ERROR.code, sb.toString());
}
@ExceptionHandler({MissingServletRequestParameterException.class})
@org.springframework.web.bind.annotation.ResponseStatus(HttpStatus.OK)
@ResponseBody
public CommonResult<Void> handleMissingServletRequestParameterException(HttpServletRequest req, MissingServletRequestParameterException ex) {
StringBuilder sb = new StringBuilder("字段:")
.append(ex.getParameterName())
.append(",类型:")
.append(ex.getParameterType())
.append(":")
.append(ex.getMessage())
.append(".");
LoggerUtil.error("入参缺失:" + req.getRequestURI() + ":" + ExceptionUtils.getStackTrace(ex));
return CommonResult.error(ResponseStatus.PARM_ERROR.code, sb.toString());
}
@ExceptionHandler({HttpMessageNotReadableException.class})
@org.springframework.web.bind.annotation.ResponseStatus(HttpStatus.OK)
@ResponseBody
public CommonResult<Void> handleHttpMessageNotReadableException(HttpServletRequest req, HttpMessageNotReadableException ex) {
StringBuilder sb = new StringBuilder("入参格式或类型错误:")
.append(ex.getMessage());
LoggerUtil.error("入参格式或类型错误:" + req.getRequestURI() + ":" + ExceptionUtils.getStackTrace(ex));
return CommonResult.error(ResponseStatus.PARM_ERROR.code, sb.toString());
}
@ExceptionHandler(value = Exception.class)
@ResponseBody
public CommonResult<Void> doHandlerException(HttpServletRequest req, Exception e) {
LoggerUtil.error("全局异常捕获:" + req.getRequestURI() + ":" + ExceptionUtils.getStackTrace(e));
return CommonResult.error(ResponseStatus.SYS_BUSY);
}
}

View File

@ -0,0 +1,16 @@
package com.diagnose.common.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.diagnose.common.entity.ScheduleLog;
/**
* <p>
* Mapper 接口
* </p>
*
* @author helloSyzb
* @since 2021-11-23
*/
public interface ScheduleLogMapper extends BaseMapper<ScheduleLog> {
}

View File

@ -0,0 +1,82 @@
package com.syzb.common.query;
import io.swagger.annotations.ApiModelProperty;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
public class AppUserInfoQuery {
@ApiModelProperty("登录方式 1:用户名密码 2:token")
@NotNull
@Min(1)
@Max(2)
private Integer loginType;
@ApiModelProperty("用户名")
private String userName;
@ApiModelProperty("密码")
private String password;
@ApiModelProperty("token")
private String token;
@ApiModelProperty("refreshToken")
private String refreshToken;
@ApiModelProperty("客户类型 1:H5 2:Web")
@NotNull
@Min(1)
@Max(2)
private Integer clientType;
public Integer getLoginType() {
return loginType;
}
public void setLoginType(Integer loginType) {
this.loginType = loginType;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
public Integer getClientType() {
return clientType;
}
public void setClientType(Integer clientType) {
this.clientType = clientType;
}
}

View File

@ -0,0 +1,35 @@
package com.syzb.common.query;
import io.swagger.annotations.ApiModelProperty;
public class BaseProductQuery implements IProduct {
@ApiModelProperty("产品id")
private Integer productId;
@ApiModelProperty("产品类型1观点包 2单篇观点 3视频 5交易圈 6图文直播间 7组合 8锦囊")
private Integer productType;
public BaseProductQuery(Integer productId, Integer productType) {
this.productId = productId;
this.productType = productType;
}
@Override
public Integer getProductId() {
return productId;
}
public void setProductId(Integer productId) {
this.productId = productId;
}
@Override
public Integer getProductType() {
return productType;
}
public void setProductType(Integer productType) {
this.productType = productType;
}
}

View File

@ -0,0 +1,18 @@
package com.diagnose.common.query;
import io.swagger.annotations.ApiModelProperty;
public class KeywordPageQuery extends PageQuery {
@ApiModelProperty("关键词")
private String keyword;
public String getKeyword() {
return keyword;
}
public void setKeyword(String keyword) {
this.keyword = keyword;
}
}

View File

@ -0,0 +1,61 @@
package com.syzb.common.query;
import io.swagger.annotations.ApiModelProperty;
public class ListAdvertQuery extends PageQuery {
@ApiModelProperty("产品类型0投顾 1观点包 2单篇观点 3视频 5交易圈 6图文直播间 7组合 8锦囊 10H5")
private Integer productType;
@ApiModelProperty("产品ID")
private Integer productId;
@ApiModelProperty("名称 仅H5")
private String name;
@ApiModelProperty("位置 1:投顾首页 2:App首页 3:小程序首页")
private Integer position;
@ApiModelProperty("创建者")
private Integer createUserId;
public Integer getProductType() {
return productType;
}
public void setProductType(Integer productType) {
this.productType = productType;
}
public Integer getProductId() {
return productId;
}
public void setProductId(Integer productId) {
this.productId = productId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getPosition() {
return position;
}
public void setPosition(Integer position) {
this.position = position;
}
public Integer getCreateUserId() {
return createUserId;
}
public void setCreateUserId(Integer createUserId) {
this.createUserId = createUserId;
}
}

View File

@ -0,0 +1,62 @@
package com.diagnose.common.query;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.time.LocalDate;
public class ListScheduleLogQuery {
private String serverName;
private String scheduleName;
@NotEmpty
@NotNull
private LocalDate date;
private Integer result;
@NotEmpty
@NotNull
private String token;
public String getServerName() {
return serverName;
}
public void setServerName(String serverName) {
this.serverName = serverName;
}
public String getScheduleName() {
return scheduleName;
}
public void setScheduleName(String scheduleName) {
this.scheduleName = scheduleName;
}
public LocalDate getDate() {
return date;
}
public void setDate(LocalDate date) {
this.date = date;
}
public Integer getResult() {
return result;
}
public void setResult(Integer result) {
this.result = result;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}

View File

@ -0,0 +1,29 @@
package com.diagnose.common.query;
import io.swagger.annotations.ApiModelProperty;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
public class OnlyIdPageQuery extends PageQuery {
@ApiModelProperty("ID")
@NotNull
@Min(1)
private Integer id;
public OnlyIdPageQuery() {
}
public OnlyIdPageQuery(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}

View File

@ -0,0 +1,29 @@
package com.diagnose.common.query;
import io.swagger.annotations.ApiModelProperty;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
public class OnlyIdQuery {
@ApiModelProperty("ID")
@NotNull
@Min(1)
private Integer id;
public OnlyIdQuery() {
}
public OnlyIdQuery(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}

View File

@ -0,0 +1,40 @@
package com.diagnose.common.query;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.ApiModelProperty;
import javax.validation.constraints.Min;
public class PageQuery {
@ApiModelProperty("当前页数")
@Min(1)
private Integer current = 1;
@ApiModelProperty("每页记录数")
@Min(1)
private Integer size = 10;
// public Page toPage() {
// return new Page(this.getCurrent(), this.getSize());
// }
public <T> Page<T> toPage() {
return new Page<>(this.getCurrent(), this.getSize());
}
public Integer getCurrent() {
return current;
}
public void setCurrent(Integer current) {
this.current = current;
}
public Integer getSize() {
return size;
}
public void setSize(Integer size) {
this.size = size;
}
}

View File

@ -0,0 +1,114 @@
package com.diagnose.common.query;
import com.syzb.common.constant.AdvertPosition;
import com.syzb.common.entity.Advert;
import com.syzb.common.validation.EnumValidator;
import io.swagger.annotations.ApiModelProperty;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
public class SaveAdvertQuery implements IProduct {
@ApiModelProperty("位置 1:投顾首页 2:App首页 3:小程序首页")
@NotNull
@EnumValidator(AdvertPosition.class)
private Integer position;
@ApiModelProperty("权重")
@NotNull
@Min(1)
@Max(100)
private Integer weight;
@ApiModelProperty("产品类型0投顾 1观点包 2单篇观点 3视频 5交易圈 6图文直播间 7组合 8锦囊 10H5")
@Min(0)
private Integer productType;
@ApiModelProperty("产品ID")
@Min(1)
private Integer productId;
@ApiModelProperty("名称 仅H5类型")
private String name;
@ApiModelProperty("URL 仅H5类型")
private String url;
@ApiModelProperty("图片URL")
@NotBlank
private String imgUrl;
public Advert toPO(Integer createUserId) {
Advert advert = new Advert();
advert.setPosition(this.position);
advert.setWeight(this.weight);
advert.setProductType(this.productType);
advert.setProductId(this.productId);
advert.setName(this.name);
advert.setUrl(this.url);
advert.setImgUrl(this.imgUrl);
advert.setCreateUserId(createUserId);
advert.setCreateTime(LocalDateTime.now());
return advert;
}
public Integer getPosition() {
return position;
}
public void setPosition(Integer position) {
this.position = position;
}
public Integer getWeight() {
return weight;
}
public void setWeight(Integer weight) {
this.weight = weight;
}
public Integer getProductType() {
return productType;
}
public void setProductType(Integer productType) {
this.productType = productType;
}
public Integer getProductId() {
return productId;
}
public void setProductId(Integer productId) {
this.productId = productId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getImgUrl() {
return imgUrl;
}
public void setImgUrl(String imgUrl) {
this.imgUrl = imgUrl;
}
}

View File

@ -0,0 +1,50 @@
package com.syzb.common.query;
import io.swagger.annotations.ApiModelProperty;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
public class SetCommentTopQuery {
@ApiModelProperty("评论id")
@NotNull
private Integer id;
@ApiModelProperty("置顶: 1设置置顶 2取消置顶")
@NotNull
@Min(1)
@Max(2)
private Integer flag;
@ApiModelProperty("置顶权重")
@NotNull
@Min(0)
@Max(1000)
private Integer weight;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getFlag() {
return flag;
}
public void setFlag(Integer flag) {
this.flag = flag;
}
public Integer getWeight() {
return weight;
}
public void setWeight(Integer weight) {
this.weight = weight;
}
}

View File

@ -0,0 +1,41 @@
package com.diagnose.common.result;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
public class AppPager<T> implements Serializable {
private static final long serialVersionUID = 1L;
private List<T> list;
private boolean hasNext;
public static <T> AppPager<T> emptyPager() {
return (AppPager<T>) EMPTY_PAGER;
}
public static final AppPager EMPTY_PAGER = new AppPager<>(Collections.emptyList(), false);
public AppPager(List<T> list, boolean hasNext) {
this.list = list;
this.hasNext = hasNext;
}
public List<T> getList() {
return list;
}
public void setList(List<T> list) {
this.list = list;
}
public boolean isHasNext() {
return hasNext;
}
public void setHasNext(boolean hasNext) {
this.hasNext = hasNext;
}
}

View File

@ -0,0 +1,87 @@
package com.diagnose.common.result;
import com.diagnose.common.util.RequestIdUtil;
import io.swagger.annotations.ApiModelProperty;
public class CommonResult<T> {
@ApiModelProperty(value = "状态码", name = "code")
private Integer code;
@ApiModelProperty(value = "消息", name = "message")
private String message;
@ApiModelProperty(value = "数据体", name = "data")
private T data;
@ApiModelProperty(value = "请求id", name = "requestId")
private String requestId;
public CommonResult(Integer code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
this.requestId = RequestIdUtil.getValue();
}
public static CommonResult<Void> error(Integer code, String message) {
return new CommonResult(code, message, null);
}
public static CommonResult<Void> error(ResponseStatus responseStatus) {
return new CommonResult(responseStatus.code, responseStatus.message, null);
}
public static CommonResult<Void> success() {
return CommonResult.success(null);
}
public static <T> CommonResult<T> success(T data) {
return new CommonResult(ResponseStatus.OK.code, ResponseStatus.OK.message, data);
}
public boolean isSuccess() {
return ResponseStatus.OK.code.equals(this.code);
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public String getRequestId() {
return requestId;
}
public void setRequestId(String requestId) {
this.requestId = requestId;
}
@Override
public String toString() {
return "CommonResult{" +
"code=" + code +
", message='" + message + '\'' +
", data=" + data +
'}';
}
}

View File

@ -0,0 +1,49 @@
package com.diagnose.common.result;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.util.Collections;
import java.util.List;
public class Pager<T> {
private List<T> list;
private long total;
public static <T> Pager<T> emptyPager() {
return (Pager<T>) EMPTY_PAGER;
}
public static final Pager EMPTY_PAGER = new Pager<>(Collections.emptyList(), 0);
public Pager(List<T> list, long total) {
this.list = list;
this.total = total;
}
public Pager(Page<T> page) {
this.list = page.getRecords();
this.total = page.getTotal();
}
public static Pager fromPage(Page page) {
return new Pager(page);
}
public List<T> getList() {
return list;
}
public void setList(List<T> list) {
this.list = list;
}
public long getTotal() {
return total;
}
public void setTotal(long total) {
this.total = total;
}
}

View File

@ -0,0 +1,59 @@
package com.diagnose.common.result;
/**
* 0: 成功
* 10001999: 认证相关错误码
* 20002999: 会话相关错误码
* 30003999: 外部系统相关错误码
* 40004999: 参数相关错误码
* 50005999: 系统内部相关错误码
* 60006999业务相关错误码
*/
public enum ResponseStatus {
/*********************成功*************************/
OK(0, "OK"),
/*********************认证**************************/
AUTH_FAIL(1000, "认证失败"),
AUTH_FAIL_CAPTCHA(1001, "认证码错误"),
PERMISSION_ERROR(1007, "权限不足"),
DATA_PERMISSION_ERROR(1008, "数据权限不足"),
PASSWORD_ERROR(1011, "密码错误"),
/*********************SESSIION********************/
SESSION_EXPIRY(2000, "未登录,不允许操作"),
SESSION_EXCEED(2001, "登录超时,请重新登录"),
SESSION_PASSWORD_RESET(2002, "密码被重置,请重新登录"),
PHONE_NOT_LOGIN(2003, "手机号未登录,不允许操作"),
SESSION_USER_LOGOUT(2007, "账户已退出,请重新登陆"),
/*********************外部系统***********************/
OUTSYS_ERROR(3000, "外部系统错误"),
/*********************入参系统***********************/
PARM_ERROR(4000, "入参错误"),
/*********************内部系统***********************/
SYS_BUSY(5000, "系统忙,请稍后重试"),
/*********************业务***********************/
/**
* 自定义响应信息
*/
STATUS_ERROR(6006, "操作状态不对,不允许操作"),
;
// 成员变量
public final Integer code;
public final String message;
ResponseStatus(int code, String message) {
this.code = code;
this.message = message;
}
//覆盖方法
@Override
public String toString() {
return this.code + "_" + this.message;
}
}

View File

@ -0,0 +1,317 @@
package com.diagnose.common.service;
import com.alibaba.fastjson.JSONObject;
import com.diagnose.common.entity.ScheduleLog;
import com.hazelcast.collection.ISet;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.crdt.pncounter.PNCounter;
import com.hazelcast.instance.impl.HazelcastInstanceProxy;
import com.hazelcast.map.IMap;
import com.diagnose.common.config.cache.CacheKey;
import com.diagnose.common.handler.BizException;
import com.diagnose.common.util.logger.LoggerUtil;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.stereotype.Component;
import javax.annotation.Nullable;
import javax.annotation.Resource;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import static com.diagnose.common.config.cache.CacheKey.ONLY_KEY_OBJ;
@Component
public class CacheService {
@Resource
private ScheduleLogService scheduleLogService;
@Resource
private HazelcastInstance hazelcastInstance;
private static final int UNLOCK_SLEEP_TIME = 5000;
private static final Set<String> loadedSet = new HashSet<>();
/**
* 从缓存加载数据,未找到则执行load回调
*
* @param mapName cacheMap的名字
* @param key cacheKey
* @param load 加载数据回调
* @param <T> 数据类型
* @return
*/
public <T> T get(String mapName, String key, Callable<T> load) {
Map<String, Object> cacheMap = hazelcastInstance.getMap(mapName);
return this.get(cacheMap, key, load);
}
/**
* 从缓存加载数据,未找到则执行load回调
*
* @param cacheMap cacheMap
* @param key cacheKey
* @param load 加载数据回调
* @param <T> 数据类型
* @return
*/
public <T> T get(Map<String, Object> cacheMap, String key, Callable<T> load) {
Object obj = cacheMap.get(key);
if (obj == null) {
T result;
try {
result = load.call();
} catch (BizException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
if (result == null) {
// 防止缓存穿透
cacheMap.put(key, ONLY_KEY_OBJ);
return null;
}
cacheMap.put(key, result);
return result;
} else if (ONLY_KEY_OBJ.equals(obj)) {
return null;
}
return (T) obj;
}
public <T> ISet<T> getSet(String key, Callable<Set<T>> load) {
ISet<T> iSet = hazelcastInstance.getSet(key);
if (loadedSet.contains(key) || !iSet.isEmpty()) {
loadedSet.add(key);
return iSet;
}
simpleLock("task-getSet-" + key,
0, TimeUnit.SECONDS,
10, TimeUnit.SECONDS,
() -> {
try {
Set<T> set = load.call();
iSet.addAll(set);
loadedSet.add(key);
} catch (BizException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
});
return iSet;
}
public <K, V> IMap<K, V> getMap(String key, Callable<Map<K, V>> load) {
IMap<K, V> iMap = hazelcastInstance.getMap(key);
if (loadedSet.contains(key) || !iMap.isEmpty()) {
loadedSet.add(key);
return iMap;
}
simpleLock("task-getMap-" + key,
0, TimeUnit.SECONDS,
10, TimeUnit.SECONDS,
() -> {
try {
Map<K, V> map = load.call();
iMap.clear();
iMap.putAll(map);
loadedSet.add(key);
} catch (BizException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
});
return iMap;
}
public Integer getLong(String key, Callable<Integer> load, int delta) {
PNCounter counter = hazelcastInstance.getPNCounter(key);
long v = counter.get();
if (loadedSet.contains(key) || v != 0) {
loadedSet.add(key);
if (delta == 0) {
return (int) v;
}
return (int) counter.addAndGet(delta);
}
simpleLock("task-getLong-" + key,
0, TimeUnit.SECONDS,
10, TimeUnit.SECONDS,
() -> {
try {
int value = load.call();
counter.reset();
counter.addAndGet(value + delta);
loadedSet.add(key);
} catch (BizException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
});
return (int) counter.get();
}
/**
* 如果key不存在则执行load回调并返回执行结果如果存在返回默认值(一般用于防止缓存穿透)
*
* @param cacheMap cacheMap
* @param key cacheKey
* @param load 回调方法
* @param defaultValue 默认值
* @param <T> 数据类型
* @return
*/
public <T> T runNx(Map<String, Object> cacheMap, String key, Callable<T> load, T defaultValue) {
Object obj = cacheMap.get(key);
if (obj == null) {
T result;
try {
result = load.call();
} catch (Exception e) {
throw new RuntimeException(e);
}
// 防止缓存穿透
cacheMap.put(key, ONLY_KEY_OBJ);
return result;
}
return defaultValue;
}
/**
* 多线程锁
*
* @param taskName 任务名字
* @param time 加锁时间值
* @param timeunit 加锁时间类型
* @param leaseTime 自动释放时间值
* @param leaseTimeunit 自动释放时间类型
* @param fn 加锁成功后执行的方法
* @param <T> 类型
*/
public <T> void lock(String taskName,
long time, @Nullable TimeUnit timeunit,
long leaseTime, @Nullable TimeUnit leaseTimeunit,
Callable<T> fn) {
try {
IMap<String, Integer> map = hazelcastInstance.getMap(CacheKey.DISTRIBUTED_LOCK);
boolean lock = map.tryLock(taskName, time, timeunit, leaseTime, leaseTimeunit);
if (lock) {
String host = ((HazelcastInstanceProxy) hazelcastInstance).getOriginal().getLocalEndpoint().getAddress().getHost();
Integer logId = null;
long runTime = 0;
try {
logId = scheduleLogService.save(ScheduleLog.start(taskName, host));
long startTime = System.currentTimeMillis();
LoggerUtil.data.info(taskName + "-开始");
T result = fn.call();
runTime = System.currentTimeMillis() - startTime;
String resultJSON = JSONObject.toJSONString(result);
LoggerUtil.data.info(taskName + "-结束:" + runTime, resultJSON);
scheduleLogService.save(ScheduleLog.success(logId, result == null ? null : resultJSON));
} catch (Exception e) {
LoggerUtil.error.error(taskName + "-异常:" + ExceptionUtils.getStackTrace(e));
scheduleLogService.save(ScheduleLog.error(logId, ExceptionUtils.getStackTrace(e)));
} finally {
if (runTime >= UNLOCK_SLEEP_TIME) {
map.forceUnlock(taskName);
} else if (lock) {
TimeUnit.MILLISECONDS.sleep(UNLOCK_SLEEP_TIME - runTime);
if (map.isLocked(taskName)) {
map.unlock(taskName);
}
}
}
}
} catch (Exception e) {
LoggerUtil.error.error(taskName + "-异常:" + ExceptionUtils.getStackTrace(e));
}
}
/**
* 多线程锁
*
* @param taskName 任务名字
* @param time 加锁时间值
* @param timeunit 加锁时间类型
* @param leaseTime 自动释放时间值
* @param leaseTimeunit 自动释放时间类型
* @param fn 加锁成功后执行的方法
*/
public void lock(String taskName,
long time, @Nullable TimeUnit timeunit,
long leaseTime, @Nullable TimeUnit leaseTimeunit,
Runnable fn) {
try {
IMap<String, Integer> map = hazelcastInstance.getMap(CacheKey.DISTRIBUTED_LOCK);
long runTime = 0;
boolean lock = map.tryLock(taskName, time, timeunit, leaseTime, leaseTimeunit);
if (lock) {
String host = ((HazelcastInstanceProxy) hazelcastInstance).getOriginal().getLocalEndpoint().getAddress().getHost();
Integer logId = null;
try {
logId = scheduleLogService.save(ScheduleLog.start(taskName, host));
long startTime = System.currentTimeMillis();
LoggerUtil.data.info(taskName + "-开始");
fn.run();
runTime = System.currentTimeMillis() - startTime;
LoggerUtil.data.info(taskName + "-结束:" + runTime);
scheduleLogService.save(ScheduleLog.success(logId, null));
} catch (Exception e) {
LoggerUtil.error.error(taskName + "-异常:" + ExceptionUtils.getStackTrace(e));
scheduleLogService.save(ScheduleLog.error(logId, ExceptionUtils.getStackTrace(e)));
} finally {
if (runTime >= UNLOCK_SLEEP_TIME) {
map.forceUnlock(taskName);
} else if (lock) {
TimeUnit.MILLISECONDS.sleep(UNLOCK_SLEEP_TIME - runTime);
if (map.isLocked(taskName)) {
map.unlock(taskName);
}
}
}
}
} catch (Exception e) {
LoggerUtil.error.error(taskName + "-异常:" + ExceptionUtils.getStackTrace(e));
}
}
public void simpleLock(String key, long time, @Nullable TimeUnit timeunit, long leaseTime, @Nullable TimeUnit leaseTimeunit, Runnable fn) {
IMap<String, Integer> map = hazelcastInstance.getMap(CacheKey.DISTRIBUTED_LOCK);
boolean lock = false;
try {
lock = map.tryLock(key, time, timeunit, leaseTime, leaseTimeunit);
if (lock) {
fn.run();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
if (lock && map.isLocked(key)) {
map.unlock(key);
}
}
}
public Integer getLong(String cacheKey, int delta) {
PNCounter counter = hazelcastInstance.getPNCounter(cacheKey);
return (int) counter.addAndGet(delta);
}
public void clearCache(String mapName, List<String> cacheKeys) {
Map<String, Object> cacheMap = hazelcastInstance.getMap(mapName);
cacheKeys.forEach(cacheMap::remove);
}
public void clearCache(String mapName, String cacheKey) {
Map<String, Object> cacheMap = hazelcastInstance.getMap(mapName);
cacheMap.remove(cacheKey);
}
}

View File

@ -0,0 +1,285 @@
package com.diagnose.common.service;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Table;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.map.IMap;
import com.syzb.common.constant.CommentBlackScope;
import com.syzb.common.constant.CommentBlackStatus;
import com.syzb.common.constant.CommentBlackType;
import com.syzb.common.entity.CommentBlack;
import com.syzb.common.handler.BizException;
import com.syzb.common.mapper.CommentBlackMapper;
import com.syzb.common.query.AddCommentBlackQuery;
import com.syzb.common.query.BaseProductQuery;
import com.syzb.common.query.CommentBlackQuery;
import com.syzb.common.query.RemoveCommentBlackQuery;
import com.syzb.common.result.Pager;
import com.syzb.common.result.ResponseStatus;
import com.syzb.common.util.logger.LoggerUtil;
import com.syzb.common.vo.BackendUserVO;
import com.syzb.common.vo.CommentBlackVO;
import com.syzb.common.vo.MergeProductInfoVO;
import com.syzb.rbac.entity.Dept;
import com.syzb.rbac.entity.UserDept;
import com.syzb.rbac.service.DeptService;
import com.syzb.rbac.service.UserService;
import com.syzb.rbac.service.WxUserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
import static com.syzb.common.config.cache.CacheKey.COMMENT_BLACK;
import static com.syzb.common.config.cache.CacheKey.CommentBlackKey.ALL_BLACK_COMMENT;
import static com.syzb.common.config.cache.CacheKey.CommentBlackKey.ALL_BLACK_USER;
@Service
public class CommentBlackService {
@Resource
private CommentBlackMapper commentBlackMapper;
@Resource
private UserService userService;
@Resource
private MergeProductService mergeProductService;
@Resource
private HazelcastInstance hazelcastInstance;
@Resource
private CacheService cacheService;
@Resource
private DeptService deptService;
@Resource
private WxUserService wxUserService;
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Transactional(rollbackFor = Exception.class)
public Integer addCommentBlack(BackendUserVO backendUserVO, AddCommentBlackQuery query) {
String userPhone = query.getUserPhone();
LocalDateTime now = LocalDateTime.now();
//禁言开始时间-1s
now = now.minusSeconds(1L);
QueryWrapper<CommentBlack> wrapper = Wrappers.query();
wrapper.eq("phone", userPhone)
.in("status", Arrays.asList(CommentBlackStatus.EFFECT.value, CommentBlackStatus.EXPIRED.value))
.lt("start_time", now)
.ge("end_time", now);
long validSize = commentBlackMapper.selectCount(wrapper);
if (validSize > 0) {
throw new BizException(ResponseStatus.REPETITIVE_ERROR);
}
CommentBlack commentBlack = query.toPO();
commentBlack.setStartTime(now);
commentBlack.setStatus(CommentBlackStatus.EFFECT.value);
commentBlack.setOperatorId(backendUserVO.getUserId());
commentBlack.setUpdateTime(now);
commentBlack.setAdvisorId(backendUserVO.getAdvisorId());
if (CommentBlackType.DAY.value.equals(commentBlack.getType())) {
commentBlack.setEndTime(now.plusDays(1));
} else if (CommentBlackType.MONTH.value.equals(commentBlack.getType())) {
commentBlack.setEndTime(now.plusMonths(1));
} else {
commentBlack.setEndTime(now.plusYears(100));
}
int count = commentBlackMapper.insert(commentBlack);
if (count < 1) {
throw new BizException(ResponseStatus.DB_SAVE_ERROR);
}
this.clearCache(ALL_BLACK_USER, ALL_BLACK_COMMENT);
return commentBlack.getId();
}
@Transactional(rollbackFor = Exception.class)
public void removeCommentBlack(BackendUserVO backendUserVO, RemoveCommentBlackQuery query) {
LocalDateTime now = LocalDateTime.now();
QueryWrapper<CommentBlack> wrapper = Wrappers.query();
wrapper.eq("phone", query.getUserPhone())
.eq("product_id", query.getProductId())
.eq("product_type", query.getProductType())
.in("status", Arrays.asList(CommentBlackStatus.EFFECT.value, CommentBlackStatus.EXPIRED.value))
.lt("start_time", now)
.ge("end_time", now);
List<CommentBlack> commentBlackList = commentBlackMapper.selectList(wrapper);
if (CollectionUtils.isEmpty(commentBlackList)) {
throw new BizException(ResponseStatus.REMOVE_BLACK_USER_ERROR);
}
for (CommentBlack commentBlack : commentBlackList) {
CommentBlack updateObj = new CommentBlack();
updateObj.setId(commentBlack.getId());
updateObj.setStatus(CommentBlackStatus.REMOVED.value);
updateObj.setUpdateTime(now);
updateObj.setEndTime(now);
int count = commentBlackMapper.updateById(updateObj);
if (count < 1) {
throw new BizException(ResponseStatus.DB_SAVE_ERROR);
}
}
this.clearCache(ALL_BLACK_USER, ALL_BLACK_COMMENT);
}
public Pager<CommentBlackVO> queryCommentBlackList(BackendUserVO backendUserVO, CommentBlackQuery query) {
LocalDateTime startTime = null;
LocalDateTime endTime = null;
LocalDateTime startOpTime = null;
LocalDateTime endOpTime = null;
if (StrUtil.isNotEmpty(query.getStartTime())) {
startTime = LocalDateTime.parse(query.getStartTime(), formatter);
}
if (StrUtil.isNotEmpty(query.getEndTime())) {
endTime = LocalDateTime.parse(query.getEndTime(), formatter);
}
if (StrUtil.isNotEmpty(query.getStartOpTime())) {
startOpTime = LocalDateTime.parse(query.getStartOpTime(), formatter);
}
if (StrUtil.isNotEmpty(query.getEndOpTime())) {
endOpTime = LocalDateTime.parse(query.getEndOpTime(), formatter);
}
QueryWrapper<CommentBlack> wrapper = Wrappers.query();
wrapper.like(StrUtil.isNotEmpty(query.getUserName()), "user_name", query.getUserName())
.eq(StrUtil.isNotEmpty(query.getPhone()), "phone", query.getPhone())
.eq(query.getStatus() != null, "status", query.getStatus())
.eq(query.getType() != null, "type", query.getType())
.eq(query.getProductType() != null, "product_type", query.getProductType())
.eq(query.getProductType() != null && query.getProductId() != null, "product_id", query.getProductId())
.like(StrUtil.isNotBlank(query.getContent()), "content", query.getContent())
.like(StrUtil.isNotEmpty(query.getReason()), "reason", query.getReason())
.eq(query.getOperatorId() != null, "operator_id", query.getOperatorId())
.ge(startTime != null, "start_time", startTime)
.lt(endTime != null, "start_time", endTime)
.ge(startOpTime != null, "end_time", startOpTime)
.lt(endOpTime != null, "end_time", endOpTime);
wrapper.orderByDesc("start_time");
Page<CommentBlack> page = commentBlackMapper.selectPage(query.toPage(), wrapper);
List<CommentBlack> list = page.getRecords();
if (CollectionUtils.isEmpty(list)) {
return Pager.emptyPager();
}
List<BaseProductQuery> baseProductQueryList = list.stream().map(commentBlack -> {
if (commentBlack.getProductId() != null && commentBlack.getProductType() != null) {
return new BaseProductQuery(commentBlack.getProductId(), commentBlack.getProductType());
}
return null;
}).filter(Objects::nonNull).collect(Collectors.toList());
Table<Integer, Integer, MergeProductInfoVO> productTable = mergeProductService.queryMergeProductInfo(baseProductQueryList);
Map<Integer, UserDept> userMap = userService.getUserMap();
Map<String, Dept> deptMap = deptService.getDeptMap();
List<CommentBlackVO> voList = list.stream().map(CommentBlackVO::new).collect(Collectors.toList());
for (CommentBlackVO commentBlackVO : voList) {
// 查询产品信息
if (commentBlackVO.getProductId() != null && commentBlackVO.getProductType() != null) {
MergeProductInfoVO mergeProductInfoVO = productTable.get(commentBlackVO.getProductType(), commentBlackVO.getProductId());
if (mergeProductInfoVO != null) {
commentBlackVO.setProductName(mergeProductInfoVO.getProductName());
}
}
LocalDateTime now = LocalDateTime.now();
if (CommentBlackStatus.EFFECT.value.equals(commentBlackVO.getStatus()) && now.isAfter(commentBlackVO.getEndTime())) {
commentBlackVO.setStatus(CommentBlackStatus.EXPIRED.value);
}
UserDept user = userMap.get(commentBlackVO.getOperatorId());
if (user != null) {
commentBlackVO.setOperatorName(user.getName());
}
if (StrUtil.isNotEmpty(commentBlackVO.getUserOrgNo())) {
Dept dept = deptMap.get(commentBlackVO.getUserOrgNo());
if (dept != null) {
commentBlackVO.setUserOrgName(dept.getName());
}
}
commentBlackVO.setUserHeadPic(wxUserService.getHeadPic(commentBlackVO.getPhone()));
}
return new Pager<>(voList, page.getTotal());
}
/**
* 校验是否禁言
*/
public boolean checkIsBlack(String phone, Integer productId, Integer productType) {
// 判断是否禁言用户
Set<String> blackUsers = getAllBlackUser();
if (!blackUsers.contains(phone)) {
return false;
}
List<CommentBlack> blackComments = getAllBlackComment();
if (CollectionUtils.isEmpty(blackComments)) {
return false;
}
for (CommentBlack commentBlack : blackComments) {
if (commentBlack.getPhone().equals(phone)) {
if (CommentBlackScope.PRODUCT.value.equals(commentBlack.getScope())) {
if (commentBlack.getProductId().equals(productId) && commentBlack.getProductType().equals(productType)) {
return true;
}
} else if (CommentBlackScope.PRODUCT_TYPE.value.equals(commentBlack.getScope())) {
if (commentBlack.getProductType().equals(productType)) {
return true;
}
} else if (CommentBlackScope.GLOBAL.value.equals(commentBlack.getScope())) {
return true;
}
}
}
return false;
}
private Set<String> getAllBlackUser() {
return cacheService.get(COMMENT_BLACK, ALL_BLACK_USER, () ->
getAllBlackComment().stream().map(CommentBlack::getPhone).collect(Collectors.toSet()));
}
private List<CommentBlack> getAllBlackComment() {
LocalDateTime now = LocalDateTime.now();
return cacheService.get(COMMENT_BLACK, ALL_BLACK_COMMENT, () -> {
QueryWrapper<CommentBlack> wrapper = Wrappers.<CommentBlack>query()
.in("status", Arrays.asList(CommentBlackStatus.EFFECT.value, CommentBlackStatus.EXPIRED.value))
.lt("start_time", now)
.gt("end_time", now);
List<CommentBlack> commentBlackList = commentBlackMapper.selectList(wrapper);
LoggerUtil.info("db当前黑名单用户" + JSONObject.toJSONString(commentBlackList));
return commentBlackList;
});
}
public Set<String> getBlackUserIds(Integer productId, Integer productType) {
Set<String> blackUsers = new HashSet<>();
List<CommentBlack> blackComments = getAllBlackComment();
for (CommentBlack commentBlack : blackComments) {
if (CommentBlackScope.PRODUCT.value.equals(commentBlack.getScope())) {
if (commentBlack.getProductId().equals(productId) && commentBlack.getProductType().equals(productType)) {
blackUsers.add(commentBlack.getPhone());
}
} else if (CommentBlackScope.PRODUCT_TYPE.value.equals(commentBlack.getScope())) {
if (commentBlack.getProductType().equals(productType)) {
blackUsers.add(commentBlack.getPhone());
}
} else if (CommentBlackScope.GLOBAL.value.equals(commentBlack.getScope())) {
blackUsers.add(commentBlack.getPhone());
}
}
return blackUsers;
}
private void clearCache(String... cacheKeys) {
IMap<String, Object> cacheMap = hazelcastInstance.getMap(COMMENT_BLACK);
for (String key : cacheKeys) {
cacheMap.remove(key);
}
}
}

View File

@ -0,0 +1,112 @@
package com.diagnose.common.service;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import com.syzb.advisor.entity.AdvisorBasic;
import com.syzb.advisor.service.AdvisorInfoService;
import com.syzb.advisor.vo.AdvisorInfoAppVO;
import com.syzb.common.constant.ProductType;
import com.syzb.common.query.IProduct;
import com.syzb.common.util.CollectUtil;
import com.syzb.common.vo.MergeProductInfoVO;
import com.syzb.course.query.IdAndSaleUserQuery;
import com.syzb.course.service.ShortVideoService;
import com.syzb.course.vo.ShortVideoVO;
import com.syzb.video.service.app.AppVideoColumnService;
import com.syzb.video.service.app.AppVideoInfoService;
import com.syzb.video.vo.column.VideoColumnAppVO;
import com.syzb.video.vo.info.VideoInfoAppVO;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
public class MergeProductService {
@Resource
private AdvisorInfoService advisorInfoService;
@Resource
private AppVideoInfoService appVideoInfoService;
@Resource
private AppVideoColumnService appVideoColumnService;
@Resource
private ShortVideoService shortVideoService;
public Table<Integer, Integer, MergeProductInfoVO> queryMergeProductInfo(List<? extends IProduct> productQueryList) {
Table<Integer, Integer, MergeProductInfoVO> voTable = HashBasedTable.create();
Table<Integer, Integer, Object> objTable = this.queryMergeProductInfo(productQueryList, false);
objTable.cellSet().forEach(cell -> voTable.put(cell.getRowKey(), cell.getColumnKey(), (MergeProductInfoVO) cell.getValue()));
return voTable;
}
/**
* 查询产品信息
*
* @param productQueryList
* @return productTypeproductIdMergeProductInfoVO
*/
public Table<Integer, Integer, Object> queryMergeProductInfo(List<? extends IProduct> productQueryList, boolean returnDetail) {
Table<Integer, Integer, Object> table = HashBasedTable.create();
if (CollectionUtils.isEmpty(productQueryList)) {
return table;
}
// 去重
productQueryList = productQueryList.stream().filter(product -> product.getProductType() != null && product.getProductId() != null).filter(CollectUtil.distinctByKey(product -> product.getProductType() + ":" + product.getProductId())).collect(Collectors.toList());
Map<Integer, AdvisorBasic> advisorBasicMap = advisorInfoService.getAdvisorMap();
productQueryList.forEach(product -> {
// 查询产品信息,投顾信息团队信息
if (ProductType.ADVISOR_INFO.value.equals(product.getProductType())) {
AdvisorInfoAppVO advisorInfoVO = advisorInfoService.getForApp(product.getProductId(), null);
if (advisorInfoVO != null) {
Object value = returnDetail ? advisorInfoVO : new MergeProductInfoVO(product.getProductId(), advisorInfoVO.getShowName(), advisorInfoVO.getProfile(), advisorInfoVO.getStatus(), null, advisorInfoVO.getCreateTime(), advisorInfoVO.getPublishTime(), advisorBasicMap.get(product.getProductId()), ProductType.ADVISOR_INFO.value, null, null, null);
table.put(product.getProductType(), product.getProductId(), value);
}
} else if (ProductType.VIDEO_SINGLE.value.equals(product.getProductType())) {
VideoInfoAppVO videoInfoVO = appVideoInfoService.getVideoInfo(product.getProductId(), null);
if (videoInfoVO != null) {
Object value;
if (returnDetail) {
value = videoInfoVO;
} else {
MergeProductInfoVO vo = new MergeProductInfoVO(product.getProductId(), videoInfoVO.getTitle(), videoInfoVO.getViewPoint(),
videoInfoVO.getStatus(), videoInfoVO.getRiskLevel(), videoInfoVO.getCreateTime(), videoInfoVO.getAuditTime(), advisorBasicMap.get(videoInfoVO.getAdvisorId()),
ProductType.VIDEO_SINGLE.value, null, null, null
);
vo.setVideoPlayType(videoInfoVO.getPlayType());
value = vo;
}
table.put(product.getProductType(), product.getProductId(), value);
}
} else if (ProductType.VIDEO_COLUMN.value.equals(product.getProductType())) {
VideoColumnAppVO columnVO = appVideoColumnService.getColumnDetail(product.getProductId(), null);
if (columnVO != null) {
Object value;
if (returnDetail) {
value = columnVO;
} else {
value = new MergeProductInfoVO(product.getProductId(), columnVO.getName(), columnVO.getIntroduce(),
columnVO.getStatus(), null, null, null, null,
ProductType.VIDEO_COLUMN.value, null, null, null
);
}
table.put(product.getProductType(), product.getProductId(), value);
}
} else if (ProductType.SHORT_VIDEO.value.equals(product.getProductType())) {
ShortVideoVO shortVideoVO = shortVideoService.getForApp(new IdAndSaleUserQuery(product.getProductId()), null, false);
Object value = returnDetail ? shortVideoVO : new MergeProductInfoVO(product.getProductId(), shortVideoVO.getTitle(), shortVideoVO.getViewPoint(),
shortVideoVO.getStatus(), shortVideoVO.getRiskLevel(), shortVideoVO.getCreateTime(), shortVideoVO.getAuditTime(), advisorBasicMap.get(shortVideoVO.getAdvisorId()),
ProductType.SHORT_VIDEO.value, null, null, null);
table.put(product.getProductType(), product.getProductId(), value);
}
});
return table;
}
}

View File

@ -0,0 +1,70 @@
package com.diagnose.common.service;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.diagnose.common.entity.ScheduleLog;
import com.diagnose.common.handler.BizException;
import com.diagnose.common.mapper.ScheduleLogMapper;
import com.diagnose.common.query.ListScheduleLogQuery;
import com.diagnose.common.result.ResponseStatus;
import com.diagnose.common.util.CodecUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
@Service
public class ScheduleLogService {
private static final String salt = "UP_OEM_SCHEDULE_LOG";
@Resource
private ScheduleLogMapper scheduleLogMapper;
public List<ScheduleLog> list(ListScheduleLogQuery query) {
checkToken(query);
String serverName = query.getServerName();
String scheduleName = query.getScheduleName();
LocalDate date = query.getDate();
Integer result = query.getResult();
QueryWrapper<ScheduleLog> wrapper = Wrappers.query();
wrapper.eq(StrUtil.isNotEmpty(serverName), "server_name", serverName)
.eq(StrUtil.isNotEmpty(scheduleName), "schedule_name", scheduleName)
.eq(date != null, "date", date)
.eq(result != null && result != 0, "result", result);
return scheduleLogMapper.selectList(wrapper);
}
@Transactional
public Integer save(ScheduleLog log) {
if (log.getId() == null) {
scheduleLogMapper.insert(log);
} else {
scheduleLogMapper.updateById(log);
}
return log.getId();
}
@Transactional
public void clearHistory(int saveDays) {
LocalDate saveDate = LocalDate.now().minusDays(saveDays);
QueryWrapper<ScheduleLog> wrapper = Wrappers.query();
wrapper.lt("date", saveDate);
scheduleLogMapper.delete(wrapper);
}
private void checkToken(ListScheduleLogQuery query) {
String token = query.getToken();
LocalDate date = query.getDate();
String dateStr = date.format(DateTimeFormatter.BASIC_ISO_DATE);
String hash = CodecUtil.md5(dateStr + salt);
if (!hash.equals(token)) {
throw new BizException(ResponseStatus.AUTH_FAIL);
}
}
}

View File

@ -0,0 +1,58 @@
package com.diagnose.common.util;
import cn.hutool.core.util.StrUtil;
import com.google.common.base.Charsets;
import org.springframework.util.DigestUtils;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
public class CodecUtil {
public static String md5(String src) {
return new BigInteger(1, DigestUtils.md5Digest(src.getBytes(Charsets.UTF_8))).toString(16);
}
public static String md5(byte[] src) {
return new BigInteger(1, DigestUtils.md5Digest(src)).toString(16);
}
private static byte[] byteMerger(byte[] byte1, byte[] byte2) {
byte[] byte3 = new byte[byte1.length + byte2.length];
System.arraycopy(byte1, 0, byte3, 0, byte1.length);
System.arraycopy(byte2, 0, byte3, byte1.length, byte2.length);
return byte3;
}
public static String sha1(String key, String str) throws NoSuchAlgorithmException, InvalidKeyException {
Mac mac = Mac.getInstance("HmacSHA1");
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), mac.getAlgorithm());
mac.init(secretKeySpec);
byte[] hash = mac.doFinal(str.getBytes(StandardCharsets.UTF_8));
byte[] sigBuf = byteMerger(hash, str.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(sigBuf);
}
public static String mobileEncrypt(String mobile) {
if (StrUtil.isEmpty(mobile) || (mobile.length() != 11)) {
return "";
}
return mobile.replaceAll("(\\w{3})\\w*(\\w{4})", "$1****$2");
}
// 带盐值的MD5加密
public static String md5WithSalt(String key, String salt) {
return md5(key + salt);
}
public static void main(String[] args) {
String sign = md5WithSalt("video_demo", "1615860427");
System.out.println(sign);
}
}

View File

@ -0,0 +1,15 @@
package com.diagnose.common.util;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Predicate;
public class CollectUtil {
public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Map<Object, Boolean> seen = new HashMap<>();
return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
}

View File

@ -0,0 +1,48 @@
package com.diagnose.common.util;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class Debounce {
// 线程池用于调度任务
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
// 保存当前的调度任务
private ScheduledFuture<?> future;
// 防抖函数
public void debounce(Runnable task, long delayInMillis) {
// 如果之前有未执行的任务取消它
if (future != null && !future.isDone()) {
future.cancel(false);
}
// 调度新的任务
future = scheduler.schedule(task, delayInMillis, TimeUnit.MILLISECONDS);
}
// 用于关闭线程池
public void shutdown() {
scheduler.shutdown();
}
// public static void main(String[] args) throws InterruptedException {
// Debounce debounce = new Debounce();
//
// // 模拟多次快速调用
// for (int i = 1; i <= 5; i++) {
// int j = i;
// // 应该只答应最后一次任务的序号
// debounce.debounce(() -> System.out.println("Task executed!" + j), 500);
// Thread.sleep(100); // 每100毫秒触发一次
// }
//
// // 给出足够时间执行最后一次任务
// Thread.sleep(1100);
//
// debounce.shutdown();
// }
}

View File

@ -0,0 +1,28 @@
package com.diagnose.common.util;
import cn.hutool.core.util.StrUtil;
public class HideUtils {
public static String hidePhoneNo(String phoneNo) {
if (StrUtil.isBlank(phoneNo)) {
return phoneNo;
}
if (phoneNo.length() >= 7) {
//前三后四
return phoneNo.substring(0, 3) + "****" +
phoneNo.substring(phoneNo.length() - 4);
} else {
return phoneNo;
}
}
public static String maskLastName(String name) {
if (StrUtil.isBlank(name)) {
return null;
}
int length = name.length();
return name.substring(0, length - 1) + "*";
}
}

View File

@ -0,0 +1,39 @@
package com.diagnose.common.util;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class IPUtil {
/**
* 获取IP地址
*
* @param request
* @return
*/
public static String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
if ("0:0:0:0:0:0:0:1".equals(ip)) {
ip = "127.0.0.1";
}
if (ip.split(",").length > 1) {
ip = ip.split(",")[0];
}
return ip;
}
}

View File

@ -0,0 +1,41 @@
package com.diagnose.common.util;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import org.slf4j.MDC;
import java.util.Date;
public class RequestIdUtil {
public static void setValue(String str) {
String requestId = RandomUtil.randomString(6);
MDC.put("requestId", str + "-" + requestId);
}
public static void setValue() {
String requestId = RandomUtil.randomString(6);
MDC.put("requestId", requestId);
}
public static String getValue() {
String requestId = MDC.get("requestId");
return StrUtil.isNotEmpty(requestId) ? requestId : "";
}
public static void removeValue() {
MDC.remove("requestId");
}
public static void setTime() {
MDC.put("requestTime", String.valueOf(new Date().getTime()));
}
public static long getTime() {
return Long.parseLong(MDC.get("requestTime"));
}
public static void removeTime() {
MDC.remove("requestTime");
}
}

View File

@ -0,0 +1,41 @@
package com.syzb.common.util;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import org.slf4j.MDC;
import java.util.Date;
public class RequestIdUtil {
public static void setValue(String str) {
String requestId = RandomUtil.randomString(6);
MDC.put("requestId", str + "-" + requestId);
}
public static void setValue() {
String requestId = RandomUtil.randomString(6);
MDC.put("requestId", requestId);
}
public static String getValue() {
String requestId = MDC.get("requestId");
return StrUtil.isNotEmpty(requestId) ? requestId : "";
}
public static void removeValue() {
MDC.remove("requestId");
}
public static void setTime() {
MDC.put("requestTime", String.valueOf(new Date().getTime()));
}
public static long getTime() {
return Long.parseLong(MDC.get("requestTime"));
}
public static void removeTime() {
MDC.remove("requestTime");
}
}

View File

@ -0,0 +1,145 @@
package com.diagnose.common.util;
import cn.hutool.core.codec.Base64;
import com.diagnose.common.util.logger.LoggerUtil;
import org.apache.commons.lang3.exception.ExceptionUtils;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
public class RsaUtil {
/**
* RSA最大加密大小
*/
private final static int MAX_ENCRYPT_BLOCK = 117;
/**
* **
* RSA最大解密大小
*/
private final static int MAX_DECRYPT_BLOCK = 128;
/**
* 公钥加密
*
* @param pubKey 公钥字符串
* @param str 需要加密操作的字符串
* @return 结果再Base64加密后的字符串
* @throws Exception
*/
public static String pubKeyEncryption(String pubKey, String str) {
try {
// String 公钥
PublicKey rsaPublicKey = getPublicKey(pubKey);
//公钥加密
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] inputArray = str.getBytes();
int inputLength = inputArray.length;
// 分段加密
int offSet = 0;
byte[] resultBytes = {};
byte[] cache;
while (inputLength - offSet > 0) {
if (inputLength - offSet > MAX_ENCRYPT_BLOCK) {
cache = cipher.doFinal(inputArray, offSet, MAX_ENCRYPT_BLOCK);
offSet += MAX_ENCRYPT_BLOCK;
} else {
cache = cipher.doFinal(inputArray, offSet, inputLength - offSet);
offSet = inputLength;
}
resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + cache.length);
System.arraycopy(cache, 0, resultBytes, resultBytes.length - cache.length, cache.length);
}
return Base64.encode(resultBytes);
} catch (Exception exception) {
LoggerUtil.info("RsaUtil.pubKeyEncryption异常" + ExceptionUtils.getStackTrace(exception));
return null;
}
}
/**
* 用私钥 解密
*
* @param priKey 私钥字符串
* @param encryptionBase64Str RSA加密后又base64编码后的字符串
* @return 解密结果
* @throws Exception
*/
public static String priKeyDecryption(String priKey, String encryptionBase64Str) {
try {
// base64 解码
byte[] base64 = Base64.decode(encryptionBase64Str);
// String 私钥
PrivateKey rsaPrivateKey = getPrivateKey(priKey);
//私钥解密
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
// 返回UTF-8编码的解密信息
int inputLen = base64.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 对数据分段解密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
cache = cipher.doFinal(base64, offSet, MAX_DECRYPT_BLOCK);
} else {
cache = cipher.doFinal(base64, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_DECRYPT_BLOCK;
}
out.close();
return out.toString(StandardCharsets.UTF_8);
} catch (Exception exception) {
LoggerUtil.info("RsaUtil.priKeyDecryption异常" + ExceptionUtils.getStackTrace(exception));
return null;
}
}
/**
* String转公钥PublicKey
*
* @param key
* @return
* @throws Exception
*/
private static PublicKey getPublicKey(String key) throws Exception {
byte[] keyBytes = Base64.decode(key);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(keySpec);
}
/**
* String转私钥PrivateKey
*
* @param key
* @return
* @throws Exception
*/
private static PrivateKey getPrivateKey(String key) throws Exception {
byte[] keyBytes = Base64.decode(key);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(keySpec);
}
}

View File

@ -0,0 +1,18 @@
package com.diagnose.common.util;
import cn.hutool.core.codec.Base62;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.HashUtil;
import cn.hutool.core.util.RandomUtil;
public class ShortUrlGenerator {
public static String generateShortUrl(String url) {
int hash = HashUtil.bkdrHash(url);
byte[] hashBytes = Convert.intToBytes(hash);
byte[] randomBytes = RandomUtil.randomBytes(1);
return Base62.encode(ArrayUtil.addAll(hashBytes, randomBytes));
}
}

View File

@ -0,0 +1,28 @@
package com.diagnose.common.util;
import cn.hutool.core.util.StrUtil;
import org.jsoup.Jsoup;
import org.jsoup.safety.Whitelist;
public class TextUtil {
public static String removeHtmlTags(String text) {
text = Jsoup.parse(text).text();
return text.replaceAll("\\s*|\t|\r|\n|&nbsp;", "");
}
/**
* 处理富文本中的XSS攻击内容
*
* @param unsafeHtml 富文本内容
* @return 清洗后的文本内容
*/
public static String cleanUnsafeHtml(String unsafeHtml) {
if (StrUtil.isEmpty(unsafeHtml)) {
return unsafeHtml;
}
Whitelist whitelist = Whitelist.relaxed();
return Jsoup.clean(unsafeHtml, whitelist);
}
}

View File

@ -0,0 +1,142 @@
package com.diagnose.common.util;
import com.diagnose.common.util.logger.LoggerUtil;
import org.apache.commons.lang3.exception.ExceptionUtils;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Base64;
public class UpDes {
private static final byte[] DEFAULT_IV = {0x75, 0x70, 0x63, 0x68, 0x69, 0x6e, 0x61, 0x31};
private Key mKey;
public UpDes() {
}
public UpDes(String strKey) {
setKey(strKey); // 生成密匙
}
public Key getKey() {
return mKey;
}
/**
* 根据参数生成 KEY
*/
public void setKey(String strKey) {
try {
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
DESKeySpec keySpec = new DESKeySpec(strKey.getBytes());
keyFactory.generateSecret(keySpec);
mKey = keyFactory.generateSecret(keySpec);
} catch (Exception e) {
LoggerUtil.error("UpDes.setKey:" + ExceptionUtils.getStackTrace(e));
}
}
/**
* 加密 String 明文输入 ,String 密文输出
*/
public String encryptStr(String strMing) {
return encryptStr(strMing, DEFAULT_IV);
}
/**
* 加密 String 明文输入 ,String 密文输出
*/
public String encryptStr(String strMing, byte[] biv) {
byte[] byteMi;
byte[] byteMing;
String strMi = "";
try {
byteMing = strMing.getBytes();
byteMi = this.encryptByte(byteMing, biv);
strMi = Base64.getEncoder().encodeToString(byteMi);
} catch (Throwable t) {
LoggerUtil.error("UpDes.encryptStr:" + ExceptionUtils.getStackTrace(t));
}
return strMi;
}
/**
* 加密以 byte[] 明文输入 ,byte[] 密文输出
*
* @param byteS byteS
* @return byte
*/
private byte[] encryptByte(byte[] byteS, byte[] biv) {
byte[] byteFina = null;
Cipher cipher;
try {
IvParameterSpec iv = new IvParameterSpec(biv);
cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, mKey, iv);
byteFina = cipher.doFinal(byteS);
} catch (Exception e) {
LoggerUtil.error("UpDes.encryptByte:" + ExceptionUtils.getStackTrace(e));
}
return byteFina;
}
/**
* 解密 String 密文输入 ,String 明文输出
*
* @param strMi strMi
* @return String
*/
public String decryptStr(String strMi) {
return decryptStr(strMi, DEFAULT_IV);
}
/**
* 解密 String 密文输入 ,String 明文输出
*
* @param strMi strMi
* @param biv biv
* @return String
*/
public String decryptStr(String strMi, byte[] biv) {
byte[] byteMing;
byte[] byteMi;
String strMing = "";
try {
byteMi = Base64.getDecoder().decode(strMi);
byteMing = this.decryptByte(byteMi, biv);
strMing = new String(byteMing, StandardCharsets.UTF_8);
} catch (Exception e) {
LoggerUtil.error("UpDes.decryptStr:" + ExceptionUtils.getStackTrace(e));
}
return strMing;
}
/**
* 解密以 byte[] 密文输入 , byte[] 明文输出
*
* @param byteD byteD
* @param biv biv
* @return byte
*/
private byte[] decryptByte(byte[] byteD, byte[] biv) {
Cipher cipher;
byte[] byteFina = null;
try {
IvParameterSpec iv = new IvParameterSpec(biv);
cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, mKey, iv);
byteFina = cipher.doFinal(byteD);
} catch (Exception e) {
LoggerUtil.error("UpDes.decryptByte:" + ExceptionUtils.getStackTrace(e));
}
return byteFina;
}
}

View File

@ -0,0 +1,32 @@
package com.diagnose.common.util;
import cn.hutool.core.exceptions.ExceptionUtil;
import com.diagnose.common.util.logger.LoggerUtil;
import org.springframework.boot.web.context.WebServerApplicationContext;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.net.InetAddress;
import java.net.UnknownHostException;
@Component
public class WebServerInfo {
@Resource
private WebServerApplicationContext webServerApplicationContext;
public String getServerIp() {
// 获取本地的 IP 地址Spring Boot 默认会绑定到 0.0.0.0所有网络接口
try {
InetAddress inetAddress = InetAddress.getLocalHost();
return inetAddress.getHostAddress();
} catch (UnknownHostException e) {
LoggerUtil.error(ExceptionUtil.stacktraceToString(e));
}
return "0.0.0.0";
}
public int getServerPort() {
return webServerApplicationContext.getWebServer().getPort();
}
}

View File

@ -0,0 +1,46 @@
package com.diagnose.common.util.logger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.Objects;
import java.util.stream.Collectors;
public class LoggerAgent {
private final Logger logger;
private LoggerAgent(String name) {
logger = LoggerFactory.getLogger(name);
}
public static LoggerAgent build(String name) {
return new LoggerAgent(name);
}
public void info(String message) {
logger.info(message);
}
public void info(Object... message) {
logger.info(Arrays.stream(message).filter(Objects::nonNull).map(Object::toString).collect(Collectors.joining( )));
}
public void error(String message) {
logger.error(message);
}
public void error(Object... message) {
logger.error(Arrays.stream(message).filter(Objects::nonNull).map(Object::toString).collect(Collectors.joining( )));
}
public void warn(String message) {
logger.warn(message);
}
public void warn(Object... message) {
logger.warn(Arrays.stream(message).filter(Objects::nonNull).map(Object::toString).collect(Collectors.joining( )));
}
}

View File

@ -0,0 +1,33 @@
package com.diagnose.common.util.logger;
public class LoggerUtil {
public static final LoggerAgent video = LoggerAgent.build("video");
public static final LoggerAgent data = LoggerAgent.build("data");
public static final LoggerAgent error = LoggerAgent.build("error");
public static final LoggerAgent websocket = LoggerAgent.build("websocket");
public static final LoggerAgent auth = LoggerAgent.build("auth");
public static final LoggerAgent api = LoggerAgent.build("api");
public static void info(String message) {
data.info(message);
}
public static void info(Object... message) {
data.info(message);
}
public static void error(String message) {
error.error(message);
}
public static void error(Object... message) {
error.error(message);
}
}

View File

@ -0,0 +1,68 @@
package com.diagnose.common.validation;
import com.google.common.collect.Sets;
import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.util.Set;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Documented
@Constraint(validatedBy = EnumChecker.class)
@Target({METHOD, FIELD, ANNOTATION_TYPE, PARAMETER, CONSTRUCTOR})
@Retention(RUNTIME)
public @interface EnumValidator {
String message() default "不符合枚举值定义";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
Class<? extends Enum<?>> value();
}
class EnumChecker implements ConstraintValidator<EnumValidator, Integer> {
private Set<Integer> AVAILABLE_ENUM_VALUES;
public static Set<Integer> getValuesSet(Class<? extends Enum<?>> enumClass, Field valueField) throws IllegalAccessException {
Enum<?>[] enums = enumClass.getEnumConstants();
Set<Integer> valuesSet = Sets.newHashSetWithExpectedSize(enums.length);
for (Enum<?> e : enums) {
Object valueObj = valueField.get(e);
valuesSet.add((Integer) valueObj);
}
return valuesSet;
}
@Override
public void initialize(EnumValidator validator) {
try {
Class<? extends Enum<?>> enumClass = validator.value();
Field valueField = enumClass.getField("value");
AVAILABLE_ENUM_VALUES = getValuesSet(enumClass, valueField);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
if (value == null) {
return true;
} else {
return AVAILABLE_ENUM_VALUES.contains(value);
}
}
}

View File

@ -0,0 +1,56 @@
package com.diagnose.common.validation;
import com.google.common.collect.Sets;
import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.Set;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Documented
@Constraint(validatedBy = IntArrayChecker.class)
@Target({METHOD, FIELD, ANNOTATION_TYPE, PARAMETER, CONSTRUCTOR})
@Retention(RUNTIME)
public @interface IntArrayValidator {
String message() default "不符合取值范围";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
int[] value();
}
class IntArrayChecker implements ConstraintValidator<IntArrayValidator, Integer> {
private Set<Integer> AVAILABLE_VALUES;
@Override
public void initialize(IntArrayValidator validator) {
int[] values = validator.value();
Set<Integer> valuesSet = Sets.newHashSetWithExpectedSize(values.length);
for (int value : values) {
valuesSet.add(value);
}
AVAILABLE_VALUES = valuesSet;
}
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
if (value == null) {
return true;
} else {
return AVAILABLE_VALUES.contains(value);
}
}
}

View File

@ -0,0 +1,73 @@
package com.diagnose.common.vo;
import com.diagnose.common.entity.Advert;
import com.diagnose.common.query.IProduct;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
public class AdvertAppVO implements IProduct, Serializable {
@ApiModelProperty("产品类型0投顾 1观点包 2单篇观点 3视频 5交易圈 6图文直播间 7组合 8锦囊 10H5")
private Integer productType;
@ApiModelProperty("产品ID")
private Integer productId;
@ApiModelProperty("名称 仅H5类型")
private String name;
@ApiModelProperty("URL 仅H5类型")
private String url;
@ApiModelProperty("图片URL")
private String imgUrl;
public AdvertAppVO(Advert advert) {
this.productType = advert.getProductType();
this.productId = advert.getProductId();
this.name = advert.getName();
this.url = advert.getUrl();
this.imgUrl = advert.getImgUrl();
}
public Integer getProductType() {
return productType;
}
public void setProductType(Integer productType) {
this.productType = productType;
}
public Integer getProductId() {
return productId;
}
public void setProductId(Integer productId) {
this.productId = productId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getImgUrl() {
return imgUrl;
}
public void setImgUrl(String imgUrl) {
this.imgUrl = imgUrl;
}
}

View File

@ -0,0 +1,108 @@
package com.diagnose.common.vo;
import com.syzb.business.vo.BusinessUserVO;
import io.swagger.annotations.ApiModelProperty;
public class AppCUserInfoVO {
@ApiModelProperty("用户id")
private String userId;
@ApiModelProperty("用户名")
private String userName;
@ApiModelProperty("昵称")
private String nickName;
@ApiModelProperty("用户头像")
private String imgUrl;
@ApiModelProperty("客户类型 1:H5 2:Web")
private Integer clientType;
@ApiModelProperty("token")
private String token;
@ApiModelProperty("refreshToken")
private String refreshToken;
@ApiModelProperty("upToken")
private String upToken;
public AppCUserInfoVO() {
}
public AppCUserInfoVO(String token, String refreshToken, BusinessUserVO userVO, Integer clientType) {
this.userId = userVO.getUserId().toString();
this.userName = userVO.getUsername();
this.nickName = userVO.getNickName();
this.imgUrl = userVO.getHeadPicUrl();
this.clientType = clientType;
this.token = token;
this.refreshToken = refreshToken;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public String getImgUrl() {
return imgUrl;
}
public void setImgUrl(String imgUrl) {
this.imgUrl = imgUrl;
}
public Integer getClientType() {
return clientType;
}
public void setClientType(Integer clientType) {
this.clientType = clientType;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
public String getUpToken() {
return upToken;
}
public void setUpToken(String upToken) {
this.upToken = upToken;
}
}

View File

@ -0,0 +1,81 @@
package com.syzb.common.vo;
import io.swagger.annotations.ApiModelProperty;
public class AppUserInfoVO {
@ApiModelProperty("用户姓名")
private String userName;
@ApiModelProperty("客户风险等级,1保守型2谨慎型3稳健型4积极型5激进型-1最低风险类型")
private String corpRiskLevel;
@ApiModelProperty("客户风险测评日,YYYYMMDD")
private String corpBeginDate;
@ApiModelProperty("客户风险到期日,YYYYMMDD")
private String corpEndDate;
@ApiModelProperty("营业部编号")
private String branchNo;
@ApiModelProperty("机构标志0个人 1机构 2自营 3产品 4特法户 5产品机构端6机构机构端")
public String organFlag;
public String getUserName() {
return userName;
}
public AppUserInfoVO(String userName, String corpRiskLevel, String corpBeginDate, String corpEndDate, String branchNo, String organFlag) {
this.userName = userName;
this.corpRiskLevel = corpRiskLevel;
this.corpBeginDate = corpBeginDate;
this.corpEndDate = corpEndDate;
this.branchNo = branchNo;
this.organFlag = organFlag;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getCorpRiskLevel() {
return corpRiskLevel;
}
public void setCorpRiskLevel(String corpRiskLevel) {
this.corpRiskLevel = corpRiskLevel;
}
public String getCorpBeginDate() {
return corpBeginDate;
}
public void setCorpBeginDate(String corpBeginDate) {
this.corpBeginDate = corpBeginDate;
}
public String getCorpEndDate() {
return corpEndDate;
}
public void setCorpEndDate(String corpEndDate) {
this.corpEndDate = corpEndDate;
}
public String getBranchNo() {
return branchNo;
}
public void setBranchNo(String branchNo) {
this.branchNo = branchNo;
}
public String getOrganFlag() {
return organFlag;
}
public void setOrganFlag(String organFlag) {
this.organFlag = organFlag;
}
}

View File

@ -0,0 +1,68 @@
package com.diagnose.common.vo;
import com.syzb.advisor.vo.AdvisorInfoAdminVO;
import com.syzb.rbac.entity.UserLogin;
import com.syzb.rbac.vo.UserAdminVO;
import io.swagger.annotations.ApiModelProperty;
import java.util.List;
public class AuthVO {
@ApiModelProperty("用户信息")
private UserAdminVO user;
@ApiModelProperty("投顾信息")
private AdvisorInfoAdminVO advisorInfo;
@ApiModelProperty("角色权限名列表")
private List<String> roles;
@ApiModelProperty("token")
private String token;
public AuthVO(UserLogin user, String token) {
this.user = new UserAdminVO(user);
this.token = token;
}
public AuthVO(UserAdminVO user, AdvisorInfoAdminVO advisorInfo, List<String> roles, String token) {
this.user = user;
this.advisorInfo = advisorInfo;
this.roles = roles;
this.token = token;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public UserAdminVO getUser() {
return user;
}
public void setUser(UserAdminVO user) {
this.user = user;
}
public AdvisorInfoAdminVO getAdvisorInfo() {
return advisorInfo;
}
public void setAdvisorInfo(AdvisorInfoAdminVO advisorInfo) {
this.advisorInfo = advisorInfo;
}
public List<String> getRoles() {
return roles;
}
public void setRoles(List<String> roles) {
this.roles = roles;
}
}

View File

@ -0,0 +1,21 @@
package com.diagnose.common.vo;
import io.swagger.annotations.ApiModelProperty;
public class CountVO {
@ApiModelProperty("执行动作后的数量")
private Integer count;
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
public CountVO(Integer count) {
this.count = count;
}
}

View File

@ -0,0 +1,24 @@
package com.diagnose.common.vo;
public class IdCountVO {
private Integer id;
private Integer count;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
}

View File

@ -0,0 +1,34 @@
package com.diagnose.common.vo;
import java.io.Serializable;
public class IdNameVO implements Serializable {
private Integer id;
private String name;
public IdNameVO() {
}
public IdNameVO(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,21 @@
package com.diagnose.common.vo;
import io.swagger.annotations.ApiModelProperty;
public class InsertIdVO {
@ApiModelProperty("ID")
private Integer id;
public InsertIdVO(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}

View File

@ -0,0 +1,25 @@
package com.diagnose.common.vo;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
public class OnlyBoolVO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("ID")
private Boolean result;
public OnlyBoolVO(Boolean result) {
this.result = result;
}
public Boolean getResult() {
return result;
}
public void setResult(Boolean result) {
this.result = result;
}
}

View File

@ -0,0 +1,25 @@
package com.diagnose.common.vo;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
public class OnlyIdVO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("ID")
private Integer id;
public OnlyIdVO(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}

View File

@ -0,0 +1,153 @@
package com.syzb.common.vo;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
public class SafetyConfigVO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("密码有效天数")
private Integer passwordPeriod;
@ApiModelProperty("密码组成1大写字母 2小写字母 3数字 4特殊字母逗号隔开")
private String passwordComposition;
@ApiModelProperty("密码组成正则表达式")
private String regexPattern;
@ApiModelProperty("密码到期前多少天提醒用户")
private Integer expirationReminder;
@ApiModelProperty("密码最大长度")
private Integer passwordMax;
@ApiModelProperty("密码最小长度")
private Integer passwordMin;
@ApiModelProperty("密码不能与前多少次旧密码相同")
private Integer passwordSame;
@ApiModelProperty("登录多少分钟后未操作,强制离线")
private Integer maxLeftTime;
@ApiModelProperty("登录失败多少次会被锁定")
private Integer errorCount;
@ApiModelProperty("登录失败N次后锁定的时间分钟")
private Integer lockTime;
@ApiModelProperty("中台登录是否开启短信验证0未开启 1已开启")
private Integer checkMessage;
@ApiModelProperty("短信开启后,用户白名单")
private String messageWhite;
@ApiModelProperty("短信有效期多长时间,分钟")
private Integer messageValidTime;
public Integer getPasswordPeriod() {
return passwordPeriod;
}
public void setPasswordPeriod(Integer passwordPeriod) {
this.passwordPeriod = passwordPeriod;
}
public String getPasswordComposition() {
return passwordComposition;
}
public void setPasswordComposition(String passwordComposition) {
this.passwordComposition = passwordComposition;
}
public Integer getExpirationReminder() {
return expirationReminder;
}
public void setExpirationReminder(Integer expirationReminder) {
this.expirationReminder = expirationReminder;
}
public Integer getPasswordMax() {
return passwordMax;
}
public void setPasswordMax(Integer passwordMax) {
this.passwordMax = passwordMax;
}
public Integer getPasswordMin() {
return passwordMin;
}
public void setPasswordMin(Integer passwordMin) {
this.passwordMin = passwordMin;
}
public Integer getPasswordSame() {
return passwordSame;
}
public void setPasswordSame(Integer passwordSame) {
this.passwordSame = passwordSame;
}
public Integer getMaxLeftTime() {
return maxLeftTime;
}
public void setMaxLeftTime(Integer maxLeftTime) {
this.maxLeftTime = maxLeftTime;
}
public Integer getErrorCount() {
return errorCount;
}
public void setErrorCount(Integer errorCount) {
this.errorCount = errorCount;
}
public Integer getLockTime() {
return lockTime;
}
public void setLockTime(Integer lockTime) {
this.lockTime = lockTime;
}
public Integer getCheckMessage() {
return checkMessage;
}
public void setCheckMessage(Integer checkMessage) {
this.checkMessage = checkMessage;
}
public String getMessageWhite() {
return messageWhite;
}
public void setMessageWhite(String messageWhite) {
this.messageWhite = messageWhite;
}
public Integer getMessageValidTime() {
return messageValidTime;
}
public void setMessageValidTime(Integer messageValidTime) {
this.messageValidTime = messageValidTime;
}
public String getRegexPattern() {
return regexPattern;
}
public void setRegexPattern(String regexPattern) {
this.regexPattern = regexPattern;
}
}

View File

@ -0,0 +1,48 @@
package com.diagnose.common.vo;
import com.diagnose.common.entity.SensitiveWord;
import io.swagger.annotations.ApiModelProperty;
import java.time.LocalDateTime;
public class SensitiveWordVO {
@ApiModelProperty("ID")
private Integer id;
@ApiModelProperty("内容")
private String word;
@ApiModelProperty("添加时间")
private LocalDateTime createTime;
public SensitiveWordVO(SensitiveWord sensitive) {
setId(sensitive.getId());
setWord(sensitive.getWord());
setCreateTime(sensitive.getCreateTime());
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getWord() {
return word;
}
public void setWord(String word) {
this.word = word;
}
public LocalDateTime getCreateTime() {
return createTime;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
}

View File

@ -0,0 +1,19 @@
package com.diagnose.constant;
// 股票所属市场 0:深圳 1:上海 7:北京
public enum MktNum {
SZ(0, "深圳"),
SH(1, "上海"),
BJ(7, "北京"),
;
public final Integer value;
public final String name;
MktNum(Integer value, String name) {
this.value = value;
this.name = name;
}
}

View File

@ -0,0 +1,19 @@
package com.diagnose.constant;
// 证券市场 1:深圳证券交易所 2:上海证券交易所 190:北京证券交易所
public enum MktTypePar {
SZ(1, "深圳"),
SH(2, "上海"),
BJ(190, "北京"),
;
public final Integer value;
public final String name;
MktTypePar(Integer value, String name) {
this.value = value;
this.name = name;
}
}

View File

@ -0,0 +1,41 @@
package com.syzb.common.constant;
import java.util.Arrays;
public enum RiskLevel {
LOW_RISK(1, "低风险"),
LOW_MIDDLE_RISK(2, "中低风险"),
MIDDLE_RISK(3, "中风险"),
MIDDLE_HIGH_RISK(4, "中高风险"),
HIGH_RISK(5, "高风险"),
;
public final Integer value;
public final String name;
RiskLevel(Integer value, String name) {
this.value = value;
this.name = name;
}
public Integer getValue() {
return value;
}
public String getName() {
return name;
}
/**
* 获取风险等级描述
*
* @param riskLevel 风险等级code
* @return 风险等级描述
*/
public static String parseName(Integer riskLevel) {
return Arrays.stream(RiskLevel.values())
.filter(s -> s.value.equals(riskLevel)).findFirst()
.map(logicType -> logicType.name).orElse(null);
}
}

View File

@ -0,0 +1,19 @@
package com.diagnose.constant;
// 股票市场 1:深圳 2:上海 8:北京
public enum SecMarPar {
SZ(1, "深圳"),
SH(2, "上海"),
BJ(8, "北京"),
;
public final Integer value;
public final String name;
SecMarPar(Integer value, String name) {
this.value = value;
this.name = name;
}
}

View File

@ -0,0 +1,54 @@
package com.diagnose.controller;
import com.diagnose.common.result.CommonResult;
import com.diagnose.service.SearchService;
import com.diagnose.service.StockService;
import com.diagnose.vo.ComprehensiveEvaluationVO;
import com.diagnose.vo.FinancialValuationVO;
import com.diagnose.vo.StockVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.List;
@Api(tags = "投顾")
@RestController
public class DiagnoseController {
@Resource
private SearchService searchService;
@Resource
private StockService stockService;
@ApiOperation("综合搜索")
@GetMapping("/diagnose/search")
public CommonResult<List<StockVO>> search(@RequestParam("关键词") @Validated @NotBlank @ApiParam(required = true) String keyword) {
List<StockVO> list = searchService.search(keyword);
return CommonResult.success(list);
}
@ApiOperation("综合评分")
@GetMapping("/diagnose/comprehensiveEvaluation")
public CommonResult<ComprehensiveEvaluationVO> comprehensiveEvaluation(@RequestParam("证券统一编码") @Validated @NotNull @ApiParam(required = true) Long stkUniCode) {
ComprehensiveEvaluationVO vo = stockService.comprehensiveEvaluation(stkUniCode);
return CommonResult.success(vo);
}
@ApiOperation("财务估值")
@GetMapping("/diagnose/financialValuation")
public CommonResult<FinancialValuationVO> financialValuation(@RequestParam("证券统一编码") @Validated @NotNull @ApiParam(required = true) Long stkUniCode) {
FinancialValuationVO vo = stockService.financialValuation(stkUniCode);
return CommonResult.success(vo);
}
}

View File

@ -0,0 +1,92 @@
package com.syzb.common.entity;
import com.google.common.collect.ComparisonChain;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import java.time.LocalDateTime;
public class CommentSortEntity implements Serializable, Comparable<CommentSortEntity> {
private static final long serialVersionUID = 1L;
@ApiModelProperty("评论id")
private Integer id;
@ApiModelProperty("评论时间")
private LocalDateTime commentTime;
@ApiModelProperty("置顶权重")
private Integer weight;
@ApiModelProperty("用户id")
private String userId;
@ApiModelProperty("是否公开1是 2否")
private Integer isOpen;
public CommentSortEntity() {
}
public CommentSortEntity(Comment comment) {
this.id = comment.getId();
this.commentTime = comment.getCommentTime();
this.weight = comment.getWeight();
this.userId = comment.getUserId();
this.isOpen = comment.getIsOpen();
}
public CommentSortEntity(LocalDateTime commentTime, Integer weight) {
this.commentTime = commentTime;
this.weight = weight;
}
@Override
public int compareTo(CommentSortEntity o) {
// 倒序 -
return -ComparisonChain.start()
.compare(this.weight == null ? (Integer) 0 : this.weight, o.weight == null ? (Integer) 0 : o.weight)
.compare(this.commentTime, o.commentTime).result();
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public LocalDateTime getCommentTime() {
return commentTime;
}
public void setCommentTime(LocalDateTime commentTime) {
this.commentTime = commentTime;
}
public Integer getWeight() {
return weight;
}
public void setWeight(Integer weight) {
this.weight = weight;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public Integer getIsOpen() {
return isOpen;
}
public void setIsOpen(Integer isOpen) {
this.isOpen = isOpen;
}
}

View File

@ -0,0 +1,92 @@
package com.syzb.common.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <p>
* 风险等级配置
* </p>
*
* @author helloSyzb
* @since 2022-11-09
*/
public class RiskLevel implements Serializable {
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 1观点包 2单篇观点 3视频 5交易圈 6图文直播间 7组合 8锦囊 21三方产品-增值产品 22三方产品-课程 23三方产品-ETF专区 24三方产品-选股工具 25三方产品-小飞机理财
*/
@TableField("product_type")
private Integer productType;
/**
* 风险等级1低风险 2中低风险 3中风险 4中高风险 5高风险
*/
@TableField("risk_level")
private Integer riskLevel;
@TableField("update_user_id")
private Integer updateUserId;
@TableField("update_time")
private LocalDateTime updateTime;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getProductType() {
return productType;
}
public void setProductType(Integer productType) {
this.productType = productType;
}
public Integer getRiskLevel() {
return riskLevel;
}
public void setRiskLevel(Integer riskLevel) {
this.riskLevel = riskLevel;
}
public Integer getUpdateUserId() {
return updateUserId;
}
public void setUpdateUserId(Integer updateUserId) {
this.updateUserId = updateUserId;
}
public LocalDateTime getUpdateTime() {
return updateTime;
}
public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime;
}
@Override
public String toString() {
return "RiskLevel{" +
"id=" + id +
", productType=" + productType +
", riskLevel=" + riskLevel +
", updateUserId=" + updateUserId +
", updateTime=" + updateTime +
"}";
}
}

View File

@ -0,0 +1,208 @@
package com.diagnose.common.entity;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.syzb.common.constant.ScheduleLogResult;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* <p>
*
* </p>
*
* @author helloSyzb
* @since 2022-11-09
*/
public class ScheduleLog implements Serializable {
/**
* 同步记录ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 服务名
*/
@TableField("server_name")
private String serverName;
/**
* 定时任务名称
*/
@TableField("schedule_name")
private String scheduleName;
/**
* 执行时间
*/
private LocalDate date;
/**
* 实际开始时间
*/
@TableField("start_time")
private LocalDateTime startTime;
/**
* 实际结束时间
*/
@TableField("end_time")
private LocalDateTime endTime;
/**
* 执行结果:1:执行中 2:成功 3:失败
*/
private Integer result;
/**
* 附加信息json
*/
private String ext;
/**
* 异常信息
*/
private String error;
/**
* 执行机器IP
*/
private String ip;
public static ScheduleLog start(String scheduleName, String ip) {
String server = System.getProperty("server.server");
ScheduleLog log = new ScheduleLog();
log.setServerName(server);
log.setScheduleName(scheduleName);
log.setDate(LocalDate.now());
log.setStartTime(LocalDateTime.now());
log.setResult(ScheduleLogResult.RUNNING.value);
log.setIp(ip);
return log;
}
public static ScheduleLog success(Integer id, String ext) {
ScheduleLog log = new ScheduleLog();
log.setId(id);
log.setEndTime(LocalDateTime.now());
log.setResult(ScheduleLogResult.SUCCESS.value);
if (StrUtil.isNotEmpty(ext)) {
log.setExt(ext);
}
return log;
}
public static ScheduleLog error(Integer id, String error) {
ScheduleLog log = new ScheduleLog();
log.setId(id);
log.setEndTime(LocalDateTime.now());
log.setResult(ScheduleLogResult.FAILURE.value);
if (StrUtil.isNotEmpty(error)) {
log.setError(error);
}
return log;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getServerName() {
return serverName;
}
public void setServerName(String serverName) {
this.serverName = serverName;
}
public String getScheduleName() {
return scheduleName;
}
public void setScheduleName(String scheduleName) {
this.scheduleName = scheduleName;
}
public LocalDate getDate() {
return date;
}
public void setDate(LocalDate date) {
this.date = date;
}
public LocalDateTime getStartTime() {
return startTime;
}
public void setStartTime(LocalDateTime startTime) {
this.startTime = startTime;
}
public LocalDateTime getEndTime() {
return endTime;
}
public void setEndTime(LocalDateTime endTime) {
this.endTime = endTime;
}
public Integer getResult() {
return result;
}
public void setResult(Integer result) {
this.result = result;
}
public String getExt() {
return ext;
}
public void setExt(String ext) {
this.ext = ext;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
@Override
public String toString() {
return "ScheduleLog{" +
"id=" + id +
", serverName=" + serverName +
", scheduleName=" + scheduleName +
", date=" + date +
", startTime=" + startTime +
", endTime=" + endTime +
", result=" + result +
", ext=" + ext +
", error=" + error +
", ip=" + ip +
"}";
}
}

View File

@ -0,0 +1,70 @@
package com.syzb.common.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <p>
*
* </p>
*
* @author helloSyzb
* @since 2022-08-26
*/
public class SensitiveWord implements Serializable {
/**
* 主键
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 敏感词
*/
private String word;
/**
* 创建时间
*/
@TableField("create_time")
private LocalDateTime createTime;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getWord() {
return word;
}
public void setWord(String word) {
this.word = word;
}
public LocalDateTime getCreateTime() {
return createTime;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
@Override
public String toString() {
return "SensitiveWord{" +
"id=" + id +
", word=" + word +
", createTime=" + createTime +
"}";
}
}

View File

@ -0,0 +1,57 @@
package com.syzb.common.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
/**
* <p>
*
* </p>
*
* @author helloSyzb
* @since 2024-07-02
*/
public class ShortUrl implements Serializable {
/**
* 哈希后得短链
*/
@TableId("resize_url")
private String resizeUrl;
/**
* 原始链接
*/
private String url;
public ShortUrl(String resizeUrl, String url) {
this.resizeUrl = resizeUrl;
this.url = url;
}
public String getResizeUrl() {
return resizeUrl;
}
public void setResizeUrl(String resizeUrl) {
this.resizeUrl = resizeUrl;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
@Override
public String toString() {
return "ShortUrl{" +
"resizeUrl=" + resizeUrl +
", url=" + url +
"}";
}
}

View File

@ -0,0 +1,25 @@
package com.diagnose.common.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.syzb.common.entity.Comment;
import com.syzb.common.vo.IdCountVO;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* <p>
* 评论表 Mapper 接口
* </p>
*
* @author helloSyzb
* @since 2024-04-25
*/
public interface CommentMapper extends BaseMapper<Comment> {
@Select("select ${ew.sqlSelect} from comment ${ew.customSqlSegment}")
List<IdCountVO> selectCommentCount(@Param(Constants.WRAPPER) Wrapper<Comment> wrapper);
}

View File

@ -0,0 +1,34 @@
package com.diagnose.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.diagnose.vo.DiagnoseBackTestVO;
import com.diagnose.vo.DiagnoseRankVO;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.math.BigDecimal;
import java.util.List;
public interface DiagnoseMapper extends BaseMapper<DiagnoseRankVO> {
@Select("SELECT * FROM (" +
" SELECT *, ROW_NUMBER() OVER (PARTITION BY stk_uni_code ORDER BY trade_date DESC) AS rn\n" +
" FROM _fe_stk_diag_rank\n" +
" WHERE stk_uni_code = #{stkUniCode} AND isvalid = 1\n" +
") t WHERE t.rn = 1")
DiagnoseRankVO selectRank(@Param("stkUniCode") Long stkUniCode);
@Select("SELECT max(total_indu_rank) AS _total_indu_rank FROM _fe_stk_diag_rank WHERE plate_uni_code = #{plateUniCode}")
Integer selectTotalInduCount(@Param("plateUniCode") Long plateUniCode);
@Select("SELECT max(total_mkt_rank) AS _total_mkt_rank FROM _fe_stk_diag_rank")
Integer selectTotalMktCount();
@Select("SELECT * FROM (" +
" SELECT *, RANK() OVER (ORDER BY trade_date DESC) AS rk\n" +
" FROM _fe_stk_diag_backtest\n" +
" WHERE star = #{star} AND isvalid = 1\n" +
") t WHERE t.rk = 1")
List<DiagnoseBackTestVO> selectBackTest(@Param("star") BigDecimal star);
}

View File

@ -0,0 +1,35 @@
package com.diagnose.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.diagnose.vo.FinanceCashShortVO;
import com.diagnose.vo.FinanceIndexAnalysisVO;
import com.diagnose.vo.FinancialValuationVO;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface FinanceMapper extends BaseMapper<FinancialValuationVO> {
@Select("SELECT * FROM (" +
" SELECT fin_sum_mac, score, ROW_NUMBER() OVER (PARTITION BY stk_code, mkt_num ORDER BY trd_date DESC) AS rn\n" +
" FROM _fin_val_fac\n" +
" WHERE stk_code = #{stkCode} AND mkt_num = #{mktNum}\n" +
") t WHERE t.rn = 1")
FinancialValuationVO selectFinancialValuation(@Param("stkCode") String stkCode, @Param("mktNum") Integer mktNum);
@Select("SELECT * FROM (" +
" SELECT *, ROW_NUMBER() OVER (PARTITION BY stk_code, sec_mar_par ORDER BY end_date DESC) AS rn\n" +
" FROM _fin_idx_ana\n" +
" WHERE stk_code = #{stkCode} AND sec_mar_par = #{secMarPar}\n" +
") t WHERE t.rn <= 5")
List<FinanceIndexAnalysisVO> selectFinanceIndexAnalysis(@Param("stkCode") String stkCode, @Param("secMarPar") Integer secMarPar);
@Select("SELECT * FROM (" +
" SELECT *, ROW_NUMBER() OVER (PARTITION BY stk_code, sec_mar_par ORDER BY end_date DESC) AS rn\n" +
" FROM _fin_cash_short\n" +
" WHERE stk_code = #{stkCode} AND sec_mar_par = #{secMarPar}\n" +
") t WHERE t.rn <= 5")
List<FinanceCashShortVO> selectFinanceCashShort(@Param("stkCode") String stkCode, @Param("secMarPar") Integer secMarPar);
}

View File

@ -0,0 +1,19 @@
package com.diagnose.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.diagnose.vo.StockVO;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface StockMapper extends BaseMapper<StockVO> {
@Select("SELECT sc.sec_uni_code, sc.sec_code, sc.sec_short_name, sc.sec_spe_short_name, sc.mkt_type_par, pi.plate_uni_code, pi.plate_name, pi.plate_code\n" +
"FROM _pub_sec_code sc\n" +
"JOIN _pub_sec_plate sp\n" +
"ON sc.sec_uni_code = sp.sec_uni_code\n" +
"JOIN pub_plate_info pi\n" +
"ON sp.plate_uni_code = pi.plate_uni_code")
List<StockVO> selectAllStock();
}

View File

@ -0,0 +1,18 @@
package com.diagnose.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.diagnose.common.vo.CountVO;
import com.diagnose.vo.StockVO;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface UnionMapper extends BaseMapper<CountVO> {
@Select("SELECT sc.sec_uni_code, sc.sec_code, sc.sec_short_name, sc.sec_spe_short_name, sc.mkt_type_par, pi.plate_uni_code, pi.plate_name, pi.plate_code FROM _pub_sec_code sc\n" +
"JOIN _pub_sec_plate sp\n" +
"ON sc.SEC_UNI_CODE = sp.SEC_UNI_CODE\n" +
"JOIN pub_plate_info pi\n" +
"ON sp.PLATE_UNI_CODE = pi.PLATE_UNI_CODE")
List<StockVO> selectAllStock();
}

View File

@ -0,0 +1,36 @@
package com.diagnose.service;
import cn.hutool.core.lang.Validator;
import com.diagnose.vo.StockVO;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Service
public class SearchService {
private static final int limit = 10;
@Resource
private StockService stockService;
public List<StockVO> search(String keyword) {
String keywordTrim = keyword.trim();
List<StockVO> allStockList = stockService.selectALlStockCache();
Stream<StockVO> parallelStream = allStockList.parallelStream();
if (Validator.isNumber(keywordTrim)) {
parallelStream = parallelStream.filter(stock -> stock.getSecCode().contains(keywordTrim));
} else if (Validator.isWord(keywordTrim)) {
String keywordUpper = keywordTrim.toUpperCase();
parallelStream = parallelStream.filter(stock -> stock.getSecSpeShortName().contains(keywordUpper) );
} else {
parallelStream = parallelStream.filter(stock -> stock.getSecShortName().contains(keywordTrim));
}
return parallelStream.limit(limit).collect(Collectors.toList());
}
}

View File

@ -0,0 +1,38 @@
package com.diagnose.service;
import cn.hutool.core.lang.Validator;
import com.diagnose.vo.StockVO;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Service
public class SearchService {
private static final int limit = 10;
@Resource
private StockService stockService;
public List<StockVO> search(String keyword) {
String keywordTrim = keyword.trim();
List<StockVO> allStockList = stockService.selectALlStockCache();
Stream<StockVO> parallelStream = allStockList.parallelStream();
if (Validator.isNumber(keywordTrim)) {
parallelStream = parallelStream.filter(stock -> stock.getSecCode().contains(keywordTrim));
} else if (Validator.isWord(keywordTrim)) {
String keywordUpper = keywordTrim.toUpperCase();
parallelStream = parallelStream.filter(stock -> stock.getSecSpeShortName().contains(keywordUpper) );
} else {
parallelStream = parallelStream.filter(stock -> stock.getSecShortName().contains(keywordTrim));
}
return parallelStream.limit(limit).collect(Collectors.toList());
}
public List<StockVO> rank(@NotNull Long stkUniCode) {
}
}

View File

@ -0,0 +1,109 @@
package com.diagnose.service;
import com.diagnose.mapper.DiagnoseMapper;
import com.diagnose.mapper.FinanceMapper;
import com.diagnose.mapper.StockMapper;
import com.diagnose.vo.*;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
@Service
public class StockService {
private static final long cacheExpires = 60 * 60 * 1000;
private static long cacheTime = 0;
private List<StockVO> stockListCache = null;
private Map<Long, StockVO> stockMapCache = null; // <stkUniCode, StockVO>
@Resource
private StockMapper stockMapper;
@Resource
private FinanceMapper financeMapper;
@Resource
private DiagnoseMapper diagnoseMapper;
public List<StockVO> selectAllStock() {
return stockMapper.selectAllStock();
}
public synchronized List<StockVO> selectALlStockCache () {
long now = System.currentTimeMillis();
if (stockListCache == null || now - cacheTime > cacheExpires) {
stockListCache = selectAllStock();
cacheTime = now;
}
return stockListCache;
}
public StockVO getStock(Long stkUniCode) {
if (stockMapCache == null) {
stockMapCache = selectALlStockCache().stream().collect(Collectors.toMap(StockVO::getSecUniCode, Function.identity()));
}
return stockMapCache.get(stkUniCode);
}
public DiagnoseRankVO rank(StockVO stock) {
DiagnoseRankVO diagnoseRankVO = diagnoseMapper.selectRank(stock.getSecUniCode());
Integer totalInduCount = diagnoseMapper.selectTotalInduCount(stock.getPlateUniCode());
Integer totalMktCount = diagnoseMapper.selectTotalMktCount();
diagnoseRankVO.setTotalInduCount(totalInduCount);
diagnoseRankVO.setTotalMktCount(totalMktCount);
return diagnoseRankVO;
}
public ComprehensiveEvaluationVO comprehensiveEvaluation(Long stkUniCode) {
StockVO stock = getStock(stkUniCode);
if (stock == null) {
return null;
}
DiagnoseRankVO diagnoseRankVO = rank(stock);
BigDecimal star = diagnoseRankVO.getTotalStar();
if (star == null) {
return null;
}
List<DiagnoseBackTestVO> backTestList = diagnoseMapper.selectBackTest(star);
ComprehensiveEvaluationVO vo = new ComprehensiveEvaluationVO();
vo.setStock(stock);
vo.setRank(diagnoseRankVO);
vo.setBackTestList(backTestList);
return vo;
}
public FinancialValuationVO financialValuation(Long stkUniCode) {
StockVO stock = getStock(stkUniCode);
if (stock == null) {
return null;
}
String secCode = stock.getSecCode();
Integer mktNum = stock.getMktNum();
FinancialValuationVO vo = financeMapper.selectFinancialValuation(secCode, mktNum);
Integer secMarPar = stock.getSecMarPar();
List<FinanceIndexAnalysisVO> financeIndexAnalysisList = financeMapper.selectFinanceIndexAnalysis(secCode, secMarPar);
financeIndexAnalysisList.forEach(fiaVO -> {
if (fiaVO.getEndDate() != null && fiaVO.getEndDate().length() > 10) {
fiaVO.setEndDate(fiaVO.getEndDate().substring(0, 10));
}
});
vo.setFinanceIndexAnalysisList(financeIndexAnalysisList);
List<FinanceCashShortVO> financeCashShortList = financeMapper.selectFinanceCashShort(secCode, secMarPar);
financeCashShortList.forEach(fcsVO -> {
if (fcsVO.getEndDate() != null && fcsVO.getEndDate().length() > 10) {
fcsVO.setEndDate(fcsVO.getEndDate().substring(0, 10));
}
});
vo.setFinanceCashShortList(financeCashShortList);
return vo;
}
}

View File

@ -0,0 +1,25 @@
package com.diagnose.startup;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@ComponentScan("com.diagnose")
@SpringBootApplication
@EnableCaching
@EnableTransactionManagement
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
@EnableAsync
public class Main {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(Main.class);
app.setRegisterShutdownHook(true);
app.run(args);
}
}

View File

@ -0,0 +1,48 @@
package com.diagnose;
import org.ahocorasick.trie.Emit;
import org.ahocorasick.trie.Trie;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class test3 {
public static void main(String[] args) {
// 示例数据
List<String> list = new ArrayList<>();
list.add("hello world");
list.add("java programming");
list.add("ahocorasick algorithm");
list.add("substring matching");
list.add("performance optimization");
list.add("data structures");
list.add("algorithm design");
list.add("string search");
list.add("efficient matching");
list.add("aho-corasick implementation");
list.add("another example");
list.add("yet another example");
list.add("example");
list.add("abcexample");
list.add("abcexampledef");
// 目标子串
String input = "example";
Trie trie = Trie.builder()
.ignoreOverlaps()
.addKeyword(input) // 将目标子串作为模式插入
.build();
// 匹配字符串
Collection<Emit> emits = trie.parseText(input);
// 输出结果
System.out.println("匹配到的字符串:");
for (Emit emit : emits) {
System.out.println(emit.getKeyword());
}
}
}

View File

@ -0,0 +1,80 @@
package com.diagnose.util;
import java.util.*;
public class StringMatcher {
private final int GRAM_SIZE = 1;
private Map<String, Set<Integer>> indexMap = new HashMap<>();
private List<String> dataList;
public StringMatcher(List<String> dataList) {
this.dataList = dataList;
buildIndex();
}
private void buildIndex() {
// 为了平衡性能和内存可以考虑只索引n-gram
for (int i = 0; i < dataList.size(); i++) {
String s = dataList.get(i);
// 生成所有可能的子串或n-gram
for (int j = 0; j < s.length() - GRAM_SIZE + 1; j++) {
String gram = s.substring(j, j + GRAM_SIZE); // n-gram
indexMap.computeIfAbsent(gram, k -> new HashSet<>()).add(i);
}
}
}
public List<String> search(String query, int limit) {
// 如果查询字符串长度小于GRAM_SIZE使用简单遍历
if (query.length() < GRAM_SIZE) {
return simpleSearch(query, limit);
}
Set<Integer> candidates = null;
// 使用查询的n-gram找候选集
for (int i = 0; i <= query.length() - GRAM_SIZE; i++) {
String gram = query.substring(i, i + GRAM_SIZE);
Set<Integer> indices = indexMap.getOrDefault(gram, Collections.emptySet());
if (candidates == null) {
candidates = new HashSet<>(indices);
} else {
candidates.retainAll(indices);
}
if (candidates.isEmpty()) {
return Collections.emptyList();
}
}
// 验证候选集
List<String> result = new ArrayList<>();
for (Integer idx : candidates) {
if (dataList.get(idx).contains(query)) {
result.add(dataList.get(idx));
if (result.size() >= limit) {
break;
}
}
}
return result;
}
private List<String> simpleSearch(String query, int limit) {
// 简单遍历法
List<String> result = new ArrayList<>();
for (String s : dataList) {
if (s.contains(query)) {
result.add(s);
if (result.size() >= limit) {
break;
}
}
}
return result;
}
}

View File

@ -0,0 +1,34 @@
package com.diagnose.util;
import java.util.ArrayList;
import java.util.List;
public class TestSearch {
public static void main(String[] args) {
// 示例数据
List<String> list = new ArrayList<>();
list.add("hello world");
list.add("java programming");
list.add("ahocorasick algorithm");
list.add("substring matching");
list.add("performance optimization");
list.add("data structures");
list.add("algorithm design");
list.add("string search");
list.add("efficient matching");
list.add("aho-corasick implementation");
list.add("another example");
list.add("yet another example");
list.add("example");
list.add("abcexample");
list.add("abcexampledef");
StringMatcher matcher = new StringMatcher(list);
List<String> results = matcher.search("example", 10);
for (String result : results) {
System.out.println(result);
}
}
}

View File

@ -0,0 +1,43 @@
package com.diagnose.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.List;
@ApiModel("综合评分")
public class ComprehensiveEvaluationVO {
@ApiModelProperty("股票基本信息")
private StockVO stock;
@ApiModelProperty("评分排名")
private DiagnoseRankVO rank;
@ApiModelProperty("回测列表")
private List<DiagnoseBackTestVO> backTestList;
public DiagnoseRankVO getRank() {
return rank;
}
public void setRank(DiagnoseRankVO rank) {
this.rank = rank;
}
public StockVO getStock() {
return stock;
}
public void setStock(StockVO stock) {
this.stock = stock;
}
public List<DiagnoseBackTestVO> getBackTestList() {
return backTestList;
}
public void setBackTestList(List<DiagnoseBackTestVO> backTestList) {
this.backTestList = backTestList;
}
}

View File

@ -0,0 +1,44 @@
package com.diagnose.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
@ApiModel("回测分析")
public class DiagnoseBackTestVO implements Serializable {
@ApiModelProperty("持仓周期")
private String timeSpan;
@ApiModelProperty("平均超额收益率")
private String avgExcessRet;
@ApiModelProperty("胜率")
private String winRate;
public String getTimeSpan() {
return timeSpan;
}
public void setTimeSpan(String timeSpan) {
this.timeSpan = timeSpan;
}
public String getAvgExcessRet() {
return avgExcessRet;
}
public void setAvgExcessRet(String avgExcessRet) {
this.avgExcessRet = avgExcessRet;
}
public String getWinRate() {
return winRate;
}
public void setWinRate(String winRate) {
this.winRate = winRate;
}
}

View File

@ -0,0 +1,133 @@
package com.diagnose.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import java.math.BigDecimal;
@ApiModel("得分排名")
public class DiagnoseRankVO implements Serializable {
@ApiModelProperty("行业排名")
private Integer totalInduRank;
@ApiModelProperty("行业总数")
private Integer totalInduCount;
@ApiModelProperty("市场排名")
private Integer totalMktRank;
@ApiModelProperty("市场总数")
private Integer totalMktCount;
@ApiModelProperty("综合得分")
private BigDecimal totalScore;
@ApiModelProperty("综合星级")
private BigDecimal totalStar;
@ApiModelProperty("财务得分")
private BigDecimal finScore;
@ApiModelProperty("量化得分")
private BigDecimal volScore;
@ApiModelProperty("市场热度得分")
private BigDecimal mhotScore;
@ApiModelProperty("主力得分")
private BigDecimal mainScore;
@ApiModelProperty("题材得分")
private BigDecimal mqScore;
public Integer getTotalInduRank() {
return totalInduRank;
}
public void setTotalInduRank(Integer totalInduRank) {
this.totalInduRank = totalInduRank;
}
public Integer getTotalInduCount() {
return totalInduCount;
}
public void setTotalInduCount(Integer totalInduCount) {
this.totalInduCount = totalInduCount;
}
public Integer getTotalMktRank() {
return totalMktRank;
}
public void setTotalMktRank(Integer totalMktRank) {
this.totalMktRank = totalMktRank;
}
public Integer getTotalMktCount() {
return totalMktCount;
}
public void setTotalMktCount(Integer totalMktCount) {
this.totalMktCount = totalMktCount;
}
public BigDecimal getTotalScore() {
return totalScore;
}
public void setTotalScore(BigDecimal totalScore) {
this.totalScore = totalScore;
}
public BigDecimal getTotalStar() {
return totalStar;
}
public void setTotalStar(BigDecimal totalStar) {
this.totalStar = totalStar;
}
public BigDecimal getFinScore() {
return finScore;
}
public void setFinScore(BigDecimal finScore) {
this.finScore = finScore;
}
public BigDecimal getVolScore() {
return volScore;
}
public void setVolScore(BigDecimal volScore) {
this.volScore = volScore;
}
public BigDecimal getMhotScore() {
return mhotScore;
}
public void setMhotScore(BigDecimal mhotScore) {
this.mhotScore = mhotScore;
}
public BigDecimal getMainScore() {
return mainScore;
}
public void setMainScore(BigDecimal mainScore) {
this.mainScore = mainScore;
}
public BigDecimal getMqScore() {
return mqScore;
}
public void setMqScore(BigDecimal mqScore) {
this.mqScore = mqScore;
}
}

View File

@ -0,0 +1,51 @@
package com.diagnose.vo;
import io.swagger.annotations.ApiModelProperty;
public class FinanceCashShortVO {
@ApiModelProperty("截止日期")
private String endDate;
@ApiModelProperty("经营活动现金流量净额")
private String cs10000;
@ApiModelProperty("投资活动现金流量净额")
private String cs20000;
@ApiModelProperty("筹资活动现金流量净额")
private String cs30000;
public String getEndDate() {
return endDate;
}
public void setEndDate(String endDate) {
this.endDate = endDate;
}
public String getCs10000() {
return cs10000;
}
public void setCs10000(String cs10000) {
this.cs10000 = cs10000;
}
public String getCs20000() {
return cs20000;
}
public void setCs20000(String cs20000) {
this.cs20000 = cs20000;
}
public String getCs30000() {
return cs30000;
}
public void setCs30000(String cs30000) {
this.cs30000 = cs30000;
}
}

View File

@ -0,0 +1,110 @@
package com.diagnose.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.math.BigDecimal;
@ApiModel("财务指标分析")
public class FinanceIndexAnalysisVO {
@ApiModelProperty("截止日期")
private String endDate;
@ApiModelProperty("销售净利率")
private BigDecimal salNpr;
@ApiModelProperty("销售毛利率")
private BigDecimal salGir;
@ApiModelProperty("净资产收益率")
private BigDecimal roea;
@ApiModelProperty("净利润同比增长率_同比")
private BigDecimal npYoy;
@ApiModelProperty("营业收入 同比")
private BigDecimal orYoy;
@ApiModelProperty("总资产周转率")
private BigDecimal taRate;
@ApiModelProperty("应付账款周转率")
private BigDecimal apRate;
@ApiModelProperty("资产负债率")
private BigDecimal assDebt;
public String getEndDate() {
return endDate;
}
public void setEndDate(String endDate) {
this.endDate = endDate;
}
public BigDecimal getSalNpr() {
return salNpr;
}
public void setSalNpr(BigDecimal salNpr) {
this.salNpr = salNpr;
}
public BigDecimal getSalGir() {
return salGir;
}
public void setSalGir(BigDecimal salGir) {
this.salGir = salGir;
}
public BigDecimal getRoea() {
return roea;
}
public void setRoea(BigDecimal roea) {
this.roea = roea;
}
public BigDecimal getNpYoy() {
return npYoy;
}
public void setNpYoy(BigDecimal npYoy) {
this.npYoy = npYoy;
}
public BigDecimal getOrYoy() {
return orYoy;
}
public void setOrYoy(BigDecimal orYoy) {
this.orYoy = orYoy;
}
public BigDecimal getTaRate() {
return taRate;
}
public void setTaRate(BigDecimal taRate) {
this.taRate = taRate;
}
public BigDecimal getApRate() {
return apRate;
}
public void setApRate(BigDecimal apRate) {
this.apRate = apRate;
}
public BigDecimal getAssDebt() {
return assDebt;
}
public void setAssDebt(BigDecimal assDebt) {
this.assDebt = assDebt;
}
}

Some files were not shown because too many files have changed in this diff Show More