fix: 码全代码添加

This commit is contained in:
2024-10-11 15:56:51 +08:00
parent 9a853f40c2
commit 7776e95902
907 changed files with 81296 additions and 324 deletions

38
template/cvbp/cvbp-public/.gitignore vendored Normal file
View File

@@ -0,0 +1,38 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

View File

@@ -0,0 +1,38 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2023 codvision.com All Rights Reserved.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.codvision</groupId>
<artifactId>cvbp-public</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cvbp-auth-core</artifactId>
<packaging>jar</packaging>
<version>1.0.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.codvision</groupId>
<artifactId>cvbp-common-core</artifactId>
</dependency>
<dependency>
<groupId>com.codvision</groupId>
<artifactId>cvbp-common-feign</artifactId>
</dependency>
<dependency>
<groupId>com.codvision</groupId>
<artifactId>cvbp-cache-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
package com.codvision.authcore.annotation;
import java.lang.annotation.*;
/**
* 权限注解
*
* @author lingee
* @date 2023/7/28
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequiresPermissions {
String value() default "";
}

View File

@@ -0,0 +1,90 @@
package com.codvision.authcore.bean;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* <p>
* 组织信息
* </p>
*
* @author lingee
* @since 2023-11-10
*/
@Data
@ApiModel(value = "Org", description = "组织信息")
public class OrgBean implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("组织id")
private Integer oid;
@ApiModelProperty("名称")
private String name;
@ApiModelProperty("全称")
private String fullname;
@ApiModelProperty("类型")
private Integer type;
@ApiModelProperty("等级")
private Short level;
@ApiModelProperty("父组织编码")
private Integer poid;
private String geom;
@ApiModelProperty("排序")
private Integer orderby;
@ApiModelProperty("组织路径")
private String path;
@ApiModelProperty("边界数据")
private String wkt;
@ApiModelProperty("组织id路径")
private String oidPath;
private String refOid;
private String geoJson;
@ApiModelProperty("备注")
private String remark;
private String refCode;
@ApiModelProperty("行政区域id")
private Integer districtOid;
@ApiModelProperty("删除标记,1:已删除,0:正常")
private Short delFlag;
@ApiModelProperty("逻辑删除辅助字段")
private Integer delKey;
@ApiModelProperty("专有钉钉部门code")
private String zzdOrgCode;
@ApiModelProperty("创建时间")
private Date createTime;
@ApiModelProperty("更新时间")
private Date updateTime;
@ApiModelProperty("行政区划编码")
private String districtCode;
@ApiModelProperty("组织属性id")
private Integer orgPropertyId;
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
package com.codvision.authcore.bean;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* 分页数据
*
* @author lingee
* @date 2023/9/21
*/
@Data
public class PageData<T> implements Serializable {
private static final long serialVersionUID = -6577523148643960799L;
/**
* 总数
*/
private int total = 0;
/**
* 当前页
*/
private int page = 1;
/**
* 页大小
*/
private int size = 20;
/**
* 数据
*/
private List<T> list;
}

View File

@@ -0,0 +1,87 @@
package com.codvision.authcore.bean;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.util.Date;
/**
* <p>
* 系统菜单展示信息
* </p>
*
* @author lingee
* @since 2023-11-09
*/
@Getter
@Setter
@ApiModel(value = "SysMenuVO", description = "系统菜单展示信息")
public class SysMenuBean implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("ID")
private Integer id;
@ApiModelProperty("菜单名称")
private String name;
@ApiModelProperty("菜单父id")
private Integer pid;
@ApiModelProperty("菜单权限标识")
private String permission;
@ApiModelProperty("前端路由标识路径")
private String url;
@ApiModelProperty("类型")
private Short type;
@ApiModelProperty("排序值")
private Short sortOrder;
@ApiModelProperty("创建时间")
private Date createTime;
@ApiModelProperty("更新时间")
private Date updateTime;
@ApiModelProperty("删除标记,1:已删除,0:正常")
private Short delFlag;
@ApiModelProperty("逻辑删除辅助字段")
private Integer delKey;
@ApiModelProperty("菜单图标")
private String icon;
@ApiModelProperty("是否隐藏")
private Boolean isHidden;
@ApiModelProperty("组件地址")
private String component;
@ApiModelProperty("布局是否可见")
private Boolean layout;
@ApiModelProperty("跳转地址")
private String redirect;
@ApiModelProperty("目标地址")
private String target;
@ApiModelProperty("所属分类")
private String category;
@ApiModelProperty("是否默认")
private Boolean isDefault;
@ApiModelProperty("菜单路径")
private String pathName;
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
package com.codvision.authcore.bean;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* <p>
* 系统角色展示信息
* </p>
*
* @author lingee
* @since 2023-11-09
*/
@Getter
@Setter
@ApiModel(value = "SysRoleVO", description = "系统角色展示信息")
public class SysRoleBean implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("ID")
private Integer id;
@ApiModelProperty("角色名称")
private String roleName;
@ApiModelProperty("角色标识")
private String roleCode;
@ApiModelProperty("角色描述")
private String roleDesc;
@ApiModelProperty("数据权限类型")
private Short dsType;
@ApiModelProperty("数据权限作用范围")
private Short dsScope;
@ApiModelProperty("删除标记,1:已删除,0:正常")
private Short delFlag;
@ApiModelProperty("逻辑删除辅助字段")
private Short delKey;
@ApiModelProperty("创建时间")
private Date createTime;
@ApiModelProperty("更新时间")
private Date updateTime;
@ApiModelProperty("是否默认")
private Short isDefault;
@ApiModelProperty("父角色id")
private Integer pId;
@ApiModelProperty("角色等级")
private Short level;
@ApiModelProperty("角色的菜单")
private List<SysMenuBean> roleMenus;
@ApiModelProperty("角色的子角色")
private List<SysRoleBean> child = new ArrayList<>();
}

View File

@@ -0,0 +1,129 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
package com.codvision.authcore.bean;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* <p>
* 系统用户展示信息
* </p>
*
* @author lingee
* @since 2023-11-09
*/
@Data
@ApiModel(value = "SysUserBean", description = "系统用户展示信息")
public class SysUserBean implements Serializable {
private static final long serialVersionUID = 1L;
private String token;
@ApiModelProperty("ID")
private Long id;
@ApiModelProperty("用户登陆名")
private String loginName;
@ApiModelProperty("登陆密码")
private String password;
@ApiModelProperty("用户真实姓名")
private String name;
@ApiModelProperty("用户联系方式")
private String mobile;
@ApiModelProperty("拓展字段:昵称")
private String nickname;
@ApiModelProperty("拓展字段:头像")
private String avatarUrl;
@ApiModelProperty("拓展字段:邮箱")
private String email;
@ApiModelProperty("浙政钉ID")
private String zzdId;
@ApiModelProperty("微信服务号openID")
private String wxMpOpenid;
@ApiModelProperty("删除标记,1:已删除,0:正常")
private Short delFlag;
@ApiModelProperty("逻辑删除辅助字段")
private Short delKey;
@ApiModelProperty("备注")
private String remark;
@ApiModelProperty("创建时间")
private Date createTime;
@ApiModelProperty("更新时间")
private Date updateTime;
@ApiModelProperty("最近一次密码更新的时间")
private Date pwdLastUpdatedTime;
@ApiModelProperty("普通钉钉userid")
private String dingTalkUid;
@ApiModelProperty("微信unionId")
private String wxUnionid;
@ApiModelProperty("微信小程序openID")
private String wxMaOpenid;
@ApiModelProperty("三方同步来的用户唯一id")
private String syncId;
@ApiModelProperty("当前组织OID")
private Integer activeOid;
@ApiModelProperty("当前组织父级OID")
private Integer activePoid;
@ApiModelProperty("当前组织名称")
private String activeOrgName;
@ApiModelProperty("当前组织路径")
private String activeOrgPath;
@ApiModelProperty("当前角色ID")
private Integer activeRoleId;
@ApiModelProperty("当前角色名称")
private String activeRoleName;
@ApiModelProperty("当前角色编码")
private String activeRoleCode;
@ApiModelProperty("关联信息(组织与角色)")
private List<UserAssociatedInfoBean> associatedInfos;
@ApiModelProperty("是否已禁用")
private Boolean isForbidden;
@ApiModelProperty(value = "状态0未读1已读")
private Integer readStatus;
@ApiModelProperty(value = "通知消息是否撤回0否1是")
private Integer flagDel;
@ApiModelProperty("组织角色")
private UserOrgRoleBean activeOrgRole;
@ApiModelProperty("组织角色列表")
private List<UserOrgRoleBean> orgRoles;
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
package com.codvision.authcore.bean;
import lombok.Data;
import java.io.Serializable;
/**
* <p>
* 系统角色展示信息
* </p>
*
* @author lingee
* @since 2023-11-09
*/
@Data
public class UserAssociatedInfoBean implements Serializable {
private static final long serialVersionUID = -2558572689076752915L;
private Integer oid;
private String orgName;
private String orgPath;
private Integer orgLevel;
private Integer roleId;
private String roleName;
private Integer roleLevel;
private String roleCode;
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
package com.codvision.authcore.bean;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* <p>
* 组织用户关联信息展示信息
* </p>
*
* @author lingee
* @since 2023-11-10
*/
@Data
@ApiModel(value = "UserOrgRoleVO", description = "组织用户关联信息展示信息")
public class UserOrgRoleBean implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("用户UID")
private Integer uid;
@ApiModelProperty("组织OID")
private Integer oid;
@ApiModelProperty("角色ID")
private Integer roleId;
@ApiModelProperty("是否开启")
private Short flagEnabled;
@ApiModelProperty("排序字段")
private Integer orderby;
@ApiModelProperty("角色编码")
private String roleCode;
@ApiModelProperty("更新时间")
private Date updateTime;
@ApiModelProperty("组织信息")
private OrgBean org;
@ApiModelProperty("角色信息")
private SysRoleBean role;
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
package com.codvision.authcore.config;
import com.codvision.authcore.interceptor.AuthInterceptor;
import com.codvision.authcore.service.IAuthService;
import com.codvision.authcore.service.impl.AuthService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
/**
* 认证配置类
*
* @author lingee
* @date 2023/7/19
*/
@Configuration
@ConditionalOnProperty(value = "cvbp.auth.enabled", havingValue = "true", matchIfMissing = true)
public class AuthConfig implements WebMvcConfigurer {
@Resource
private AuthProperties authProperties;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor())
.excludePathPatterns("/v1/sysUser/login",
"/v1/sysUser/verifyCode",
"/v1/sysUser/loginByMobile",
"/v1/sysUser/loginSignature",
"/v1/manager/sysConfig/getListByKeys",
"/v1/manager/sysFile/preview/**",
"/v1/manager/sysFile/download/**",
"/v1/manager/website/addRecord",
"/customCaptcha/get",
"/captcha/get",
"/captcha/check",
"/backend/**",
"/**/error",
"/**/doc.*",
"/**/swagger-ui.*",
"/swagger-resources/**",
"/**/webjars/**",
"/favicon.ico",
"/**/v2/api-docs/**")
.addPathPatterns(authProperties.getIncludes());
}
@Bean
@ConditionalOnMissingBean(name = "authCenterConfig")
public IAuthService authService() {
return new AuthService();
}
@Bean
public AuthInterceptor authInterceptor() {
return new AuthInterceptor();
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
package com.codvision.authcore.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* 认证配置属性
*
* @author lingee
* @date 2023/7/25
*/
@Data
@Configuration
@ConfigurationProperties("cvbp.auth")
public class AuthProperties {
/**
* 未授权登录跳转地址
*/
public String authRedirectUrl = "";
/**
* 是否开启
*/
public Boolean enabled = true;
/**
* 登录地址
*/
private String host = "";
/**
* token名称
*/
private String tokenName = "Authorization";
/**
* cookie名称
*/
private String cookieName = "Authorization";
/**
* 排除地址
*/
private String excludes = "";
/**
* 拦截地址
*/
private String includes = "/**";
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
package com.codvision.authcore.constants;
/**
* 认证模块常量类
*
* @author lingee
* @date 2023/11/22
*/
public final class AuthCoreConstant {
/**
* 用户信息缓存
*/
public static final String LOGIN_USER_EXPIRE_PREFIX = "user:auth:";
/**
* 用户权限缓存
*/
public static final String LOGIN_USER_PERMISSION_EXPIRE_PREFIX = "user:permission:";
/**
* 页面嵌入标识
*/
public static final String IframeDownFlag = "iframeDown";
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
package com.codvision.authcore.interceptor;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.codvision.authcore.annotation.RequiresPermissions;
import com.codvision.authcore.bean.SysMenuBean;
import com.codvision.authcore.bean.SysUserBean;
import com.codvision.authcore.config.AuthProperties;
import com.codvision.authcore.constants.AuthCoreConstant;
import com.codvision.authcore.service.IAuthService;
import com.codvision.authcore.util.RequestUtil;
import com.codvision.commoncore.exception.AuthException;
import com.codvision.commoncore.exception.PermissionException;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Optional;
/**
* 认证拦截器
*
* @author lingee
* @date 2023/8/3
*/
public class AuthInterceptor implements HandlerInterceptor {
@Resource
private AuthProperties authProperties;
@Resource
private IAuthService authService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
final String path = request.getServletPath();
if (RequestUtil.isExcludedUri(path, authProperties.getExcludes())) {
return true;
}
final String token = authService.getAuthToken(request);
Optional.ofNullable(token).orElseThrow(() -> new AuthException(10002));
SysUserBean sysUser = authService.getCurrentUserInfo(token);
Optional.ofNullable(sysUser).orElseThrow(() -> new AuthException(10002));
if (handler.getClass().isAssignableFrom(HandlerMethod.class)) {
RequiresPermissions permissions = getRequiresPermissions((HandlerMethod) handler);
if (ObjectUtil.isEmpty(permissions)) {
return true;
}
String code = permissions.value();
if (StrUtil.isNotBlank(code)) {
Optional.ofNullable(sysUser.getActiveRoleId()).orElseThrow(() -> new PermissionException("当前用户无授权角色"));
List<SysMenuBean> sysMenus = authService.getMenuList(token);
if (CollectionUtils.isEmpty(sysMenus)) {
throw new PermissionException("当前用户无菜单权限");
}
if (sysMenus.stream().filter(x -> code.equals(x.getPermission())).count() == 0) {
throw new PermissionException("当前用户无访问权限");
}
}
}
return true;
}
/**
* 获取hander上的RequiresPermissions注解
*
* @param handler 处理对象
* @return 注解
*/
private RequiresPermissions getRequiresPermissions(HandlerMethod handler) {
RequiresPermissions requiresPermissions = handler.getMethodAnnotation(RequiresPermissions.class);
if (ObjectUtils.isEmpty(requiresPermissions)) {
requiresPermissions = AnnotatedElementUtils.getMergedAnnotation(handler.getBean().getClass(), RequiresPermissions.class);
}
return requiresPermissions;
}
}

View File

@@ -0,0 +1,11 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
/**
* 统一认证模块
*
* @author lingee
* @date 2023/7/17
*/
package com.codvision.authcore;

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
package com.codvision.authcore.remote;
import com.codvision.authcore.bean.PageData;
import com.codvision.authcore.bean.SysMenuBean;
import com.codvision.authcore.bean.SysUserBean;
import com.codvision.authcore.remote.fallback.RemoteAuthServiceFallback;
import com.codvision.commoncore.common.ResponseEntity;
import com.codvision.commonfeign.config.OpenFeignConfig;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;
/**
* 认证服务远程调用
*
* @author lingee
* @date 2023/7/18
*/
@FeignClient(name = "remote-auth-sevice", url = "${cvbp.auth.host}", fallback = RemoteAuthServiceFallback.class,
configuration = OpenFeignConfig.class)
public interface RemoteAuthService {
/**
* 获取当前登录用户信息
*
* @param token token
* @return 用户信息
*/
@PostMapping("/sysUser/getUserInfo")
ResponseEntity<SysUserBean> getUserInfo(@RequestHeader(value = "Authorization") String token);
/**
* 获取权限菜单列表
*
* @param page 当前页
* @param size 页面大小lø
* @return 菜单列表
*/
@PostMapping("/findAll")
ResponseEntity<PageData<SysMenuBean>> getUserMenus(@RequestHeader(value = "Authorization") String token,
@RequestParam(value = "page", required = false, defaultValue = "1") int page,
@RequestParam(value = "size", required = false, defaultValue = "1000") int size);
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
package com.codvision.authcore.remote.fallback;
import com.codvision.authcore.bean.PageData;
import com.codvision.authcore.bean.SysMenuBean;
import com.codvision.authcore.bean.SysUserBean;
import com.codvision.authcore.remote.RemoteAuthService;
import com.codvision.commoncore.common.ResponseEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 认证服务调用反馈
*
* @author lingee
* @date 2023/7/19
*/
@Service
@Slf4j
public class RemoteAuthServiceFallback implements RemoteAuthService {
private ResponseEntity failResponse() {
ResponseEntity response = new ResponseEntity();
response.error(990000).message("认证服务调用失败");
log.error(response.getMessage());
return response;
}
@Override
public ResponseEntity<SysUserBean> getUserInfo(String token) {
return failResponse();
}
@Override
public ResponseEntity<PageData<SysMenuBean>> getUserMenus(String token, int page, int size) {
return failResponse();
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
package com.codvision.authcore.service;
import com.codvision.authcore.bean.SysMenuBean;
import com.codvision.authcore.bean.SysUserBean;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
/**
* 认证接口
*
* @author lingee
* @date 2023/7/18
*/
public interface IAuthService {
/**
* 获取当前登录用户信息
*
* @param token token
* @return 用户
*/
SysUserBean getCurrentUserInfo(String token);
/**
* 获取上下文的token信息
*
* @param request 请求
* @return token
*/
String getAuthToken(HttpServletRequest request);
/**
* 获取菜单列表
*
* @param token token
* @return 菜单列表
*/
List<SysMenuBean> getMenuList(String token);
/**
* 根据用户名密码登录
*
* @param username 用户名
* @param password 密码
* @return 登录用户信息
*/
SysUserBean loginByUsernameAndPassword(String username, String password);
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
package com.codvision.authcore.service.impl;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.codvision.authcore.bean.PageData;
import com.codvision.authcore.bean.SysMenuBean;
import com.codvision.authcore.bean.SysUserBean;
import com.codvision.authcore.config.AuthProperties;
import com.codvision.authcore.remote.RemoteAuthService;
import com.codvision.authcore.service.IAuthService;
import com.codvision.authcore.util.RequestUtil;
import com.codvision.cachecore.utils.RedisUtil;
import com.codvision.commoncore.common.ResponseEntity;
import com.codvision.commoncore.utils.ResponseUtil;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import static com.codvision.authcore.constants.AuthCoreConstant.IframeDownFlag;
import static com.codvision.authcore.constants.AuthCoreConstant.LOGIN_USER_EXPIRE_PREFIX;
/**
* 认证服务
*
* @author lingee
* @date 2023/7/17
*/
public class AuthService implements IAuthService {
@Resource
private RemoteAuthService remoteAuthService;
@Resource
private AuthProperties authProperties;
@Override
public SysUserBean getCurrentUserInfo(String token) {
String key = LOGIN_USER_EXPIRE_PREFIX + token;
if (RedisUtil.hasKey(key)) {
SysUserBean sysUser = RedisUtil.get(key);
if (ObjectUtil.isNotEmpty(sysUser)) {
return sysUser;
}
}
ResponseEntity<SysUserBean> response = remoteAuthService.getUserInfo(token);
if (!ResponseUtil.isSuccess(response)) {
return null;
}
SysUserBean sysUser = response.getData();
RedisUtil.set(key, sysUser);
return sysUser;
}
@Override
public String getAuthToken(HttpServletRequest request) {
String tokenName = authProperties.getTokenName();
String cookieName = authProperties.getCookieName();
String token = request.getParameter(tokenName);
if (StrUtil.isNotBlank(token)) {
return token;
}
token = RequestUtil.getFromCookie(request, cookieName);
if (StrUtil.isNotBlank(token)) {
return token;
}
token = request.getHeader(tokenName);
if (StrUtil.isNotBlank(token)) {
return token;
}
final String iframeDown = request.getParameter(IframeDownFlag);
if (StrUtil.isNotBlank(iframeDown)) {
token = request.getParameter(tokenName);
}
return token;
}
@Override
public List<SysMenuBean> getMenuList(String token) {
ResponseEntity<PageData<SysMenuBean>> response = remoteAuthService.getUserMenus(token, 1, 10000);
if (!ResponseUtil.isSuccess(response)) {
return null;
}
PageData<SysMenuBean> data = response.getData();
return data.getList();
}
@Override
public SysUserBean loginByUsernameAndPassword(String username, String password) {
return null;
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
package com.codvision.authcore.util;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.codvision.authcore.bean.SysUserBean;
import com.codvision.authcore.service.IAuthService;
import com.codvision.commoncore.exception.AuthException;
import org.springframework.context.ApplicationContext;
import java.util.Optional;
/**
* 认证工具类
*
* @author lingee
* @date 2023/7/18
*/
public class AuthUtil {
/**
* 获取token
*
* @return token
*/
public static String getToken() {
ApplicationContext context = SpringUtil.getApplicationContext();
if (!context.containsBean("authService")) {
return null;
}
IAuthService authService = SpringUtil.getBean(IAuthService.class);
return authService.getAuthToken(RequestUtil.getRequest());
}
/**
* 获取当前登录用户信息
*
* @return 用户信息
*/
public static SysUserBean getCurrentUser() {
ApplicationContext context = SpringUtil.getApplicationContext();
if (!context.containsBean("authService")) {
return new SysUserBean();
}
IAuthService authService = SpringUtil.getBean(IAuthService.class);
String token = authService.getAuthToken(RequestUtil.getRequest());
Optional.ofNullable(token).orElseThrow(() -> new AuthException(10002));
SysUserBean sysUser = authService.getCurrentUserInfo(token);
Optional.ofNullable(sysUser).orElseThrow(() -> new AuthException(10002));
return sysUser;
}
/**
* 判断是否已登录
*
* @return
*/
public static boolean isLogin() {
ApplicationContext context = SpringUtil.getApplicationContext();
if (!context.containsBean("authService")) {
return false;
}
IAuthService authService = SpringUtil.getBean(IAuthService.class);
String token = authService.getAuthToken(RequestUtil.getRequest());
if (StrUtil.isBlank(token)) {
return false;
}
SysUserBean sysUser = authService.getCurrentUserInfo(token);
if (ObjectUtil.isEmpty(sysUser)) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,124 @@
package com.codvision.authcore.util;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
/**
* 请求工具类
*
* @author lingee
* @date 2023/7/18
*/
public class RequestUtil {
public static final String ClientIp = "client-ip";
public static String getIp() {
final HttpServletRequest httpServletRequest = getRequest();
if (httpServletRequest == null) {
return null;
}
return getIp(httpServletRequest);
}
public static String getFromHead(String name) {
if (StrUtil.isBlank(name)) {
return null;
}
final HttpServletRequest httpServletRequest = getRequest();
if (httpServletRequest == null) {
return null;
}
return httpServletRequest.getHeader(name);
}
public static String getFromCookie(HttpServletRequest request, String cookieName) {
Cookie cookie = ServletUtil.getCookie(request, cookieName);
if (cookie != null) {
return cookie.getValue();
}
return null;
}
private static String getIp(HttpServletRequest httpServletRequest) {
String ip = httpServletRequest.getHeader(ClientIp);
if (StrUtil.isBlank(ip)) {
ip = ServletUtil.getClientIP(httpServletRequest);
}
return ip;
}
public static String getRequestPath() {
final HttpServletRequest httpServletRequest = getRequest();
if (httpServletRequest == null) {
return null;
}
return httpServletRequest.getServletPath();
}
public static String getContextPath() {
final HttpServletRequest request = getRequest();
if (request == null) {
return "/";
}
return request.getContextPath();
}
public static HttpServletRequest getRequest() {
RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
if (attrs == null) {
return null;
}
final ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) attrs;
return servletRequestAttributes.getRequest();
}
private static String getStrFromRequest(final String key) {
final Object v = getObject(key);
if (v == null) {
return null;
}
return Objects.toString(v);
}
public static <T> T getObject(final String key) {
RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
if (attrs == null) {
return null;
}
Object obj = attrs.getAttribute(key, RequestAttributes.SCOPE_REQUEST);
if (obj == null) {
return null;
}
return (T) obj;
}
public static <T> void setObject(final HttpServletRequest request, final String key, final T obj) {
request.setAttribute(key, obj);
}
public static boolean isExcludedUri(String uri, String excludedUris) {
if (StringUtils.isBlank(uri) || StringUtils.isBlank(excludedUris)) {
return false;
}
PathMatcher matcher = new AntPathMatcher();
for (String ex : excludedUris.split(",")) {
uri = uri.trim();
ex = ex.trim();
if (matcher.match(ex.toLowerCase(), uri.toLowerCase())) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.codvision.authcore.config.AuthConfig,\
com.codvision.authcore.config.AuthProperties

View File

@@ -0,0 +1,38 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

View File

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2023 codvision.com All Rights Reserved.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.codvision</groupId>
<artifactId>cvbp-public</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cvbp-cache-core</artifactId>
<packaging>jar</packaging>
<version>1.0.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-crypto</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-extra</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
package com.codvision.cachecore.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* 缓存属性配置
*
* @author lingee
* @date 2023/7/26
*/
@Data
@Configuration
@ConfigurationProperties("cvbp.cache")
public class CacheProperties {
/**
* 缓存
*/
private Boolean enabled = false;
/**
* 缓存类型
*/
private String type = "redis";
/**
* 缓存超时时间(单位:秒)
*/
private Long timeout = 3600 * 24L;
/**
* redisson是否开启
*/
private Boolean redisson = false;
}

View File

@@ -0,0 +1,236 @@
package com.codvision.cachecore.config;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;
import org.redisson.config.SentinelServersConfig;
import org.redisson.config.SingleServerConfig;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import javax.annotation.Resource;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 自定义redis配置
*
* @author lingee
* @since 2023/7/26
*/
@Slf4j
@EnableCaching
@Configuration
@ConditionalOnExpression(value = "${cvbp.cache.enabled:true}")
@ConditionalOnProperty(value = "cvbp.cache.type", havingValue = "redis")
public class RedisConfig extends CachingConfigurerSupport {
private static final String REDIS_PREFIX = "redis://";
@Resource
private CacheProperties cacheProperties;
@Resource
private ObjectMapper objectMapper;
/**
* 缓存json序列化配置
*
* @return
*/
private Jackson2JsonRedisSerializer jackson2JsonRedisSerializer() {
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.configure(MapperFeature.USE_ANNOTATIONS, false);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
//此项必须配置否则会报java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to XXX
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.PROPERTY);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
return jackson2JsonRedisSerializer;
}
/**
* 当有多个管理器的时候,必须使用该注解在一个管理器上注释:表示该管理器为默认的管理器
*
* @param connectionFactory 链接工厂
* @return 缓存
*/
@Bean
@Primary
public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(cacheProperties.getTimeout()))
.disableCachingNullValues()
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer()));
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, cacheConfiguration);
return cacheManager;
}
@Bean(destroyMethod = "shutdown")
@ConditionalOnProperty(value = "cvbp.cache.redisson", havingValue = "true")
public RedissonClient redisson(RedisProperties redisProperties) {
Config config = new Config();
if (redisProperties.getSentinel() != null && !redisProperties.getSentinel().getNodes().isEmpty()) {
// 哨兵模式
SentinelServersConfig sentinelServersConfig = config.useSentinelServers();
sentinelServersConfig.setMasterName(redisProperties.getSentinel().getMaster());
List<String> sentinelAddress = new ArrayList<>();
for (String node : redisProperties.getCluster().getNodes()) {
sentinelAddress.add(REDIS_PREFIX + node);
}
sentinelServersConfig.setSentinelAddresses(sentinelAddress);
if (CharSequenceUtil.isNotEmpty(redisProperties.getSentinel().getPassword())) {
sentinelServersConfig.setSentinelPassword(redisProperties.getSentinel().getPassword());
}
} else if (redisProperties.getCluster() != null && !redisProperties.getCluster().getNodes().isEmpty()) {
// 集群模式
ClusterServersConfig clusterServersConfig = config.useClusterServers();
List<String> clusterNodes = new ArrayList<>();
for (String node : redisProperties.getCluster().getNodes()) {
clusterNodes.add(REDIS_PREFIX + node);
}
clusterServersConfig.setNodeAddresses(clusterNodes);
if (CharSequenceUtil.isNotEmpty(redisProperties.getPassword())) {
clusterServersConfig.setPassword(redisProperties.getPassword());
}
} else {
SingleServerConfig singleServerConfig = config.useSingleServer();
singleServerConfig.setAddress(REDIS_PREFIX + redisProperties.getHost() + ":" + redisProperties.getPort());
if (CharSequenceUtil.isNotEmpty(redisProperties.getPassword())) {
singleServerConfig.setPassword(redisProperties.getPassword());
}
singleServerConfig.setPingConnectionInterval(1000);
}
return Redisson.create(config);
}
@Primary
@Bean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
// 使用Jackson2JsonRedisSerialize 替换默认序列化(默认采用的是JDK序列化)
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// redisTemplate.setKeySerializer(jackson2JsonRedisSerializer);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
// redisTemplate.setHashKeySerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
//key的序列化采用StringRedisSerializer
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(jackson2JsonRedisSerializer);
return redisTemplate;
}
/**
* 自定义缓存key生成策略默认将使用该策略
*/
@Bean(name = "cacheKeyGenerator")
@Override
public KeyGenerator keyGenerator() {
return (target, method, params) -> {
Map<String, Object> container = new HashMap<>(3);
Class<?> targetClassClass = target.getClass();
//类地址
container.put("class", targetClassClass.toGenericString());
//方法名称
container.put("methodName", method.getName());
//包名称
container.put("package", targetClassClass.getPackage());
//参数列表
for (int i = 0; i < params.length; i++) {
container.put(String.valueOf(i), params[i]);
}
//转为JSON字符串
String jsonString;
try {
jsonString = objectMapper.writeValueAsString(container);
} catch (JsonProcessingException e) {
log.error(e.getMessage(), e);
throw new RuntimeException(e);
}
//做SHA256 Hash计算得到一个SHA256摘要作为Key
return DigestUtil.sha256Hex(jsonString);
};
}
@Bean
@Override
public CacheErrorHandler errorHandler() {
//异常处理当Redis发生异常时打印日志但是程序正常走
log.info("初始化 -> [{}]", "Redis CacheErrorHandler");
return new CacheErrorHandler() {
@Override
public void handleCacheGetError(RuntimeException e, Cache cache, Object key) {
log.error("Redis occur handleCacheGetErrorkey -> [{}]", key, e);
}
@Override
public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) {
log.error("Redis occur handleCachePutErrorkey -> [{}]value -> [{}]", key, value, e);
}
@Override
public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) {
log.error("Redis occur handleCacheEvictErrorkey -> [{}]", key, e);
}
@Override
public void handleCacheClearError(RuntimeException e, Cache cache) {
log.error("Redis occur handleCacheClearError", e);
}
};
}
}

View File

@@ -0,0 +1,11 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
/**
* 缓存模块
*
* @author lingee
* @date 2023/7/26
*/
package com.codvision.cachecore;

View File

@@ -0,0 +1,718 @@
package com.codvision.cachecore.utils;
import cn.hutool.core.convert.Convert;
import cn.hutool.extra.spring.SpringUtil;
import com.codvision.cachecore.config.CacheProperties;
import lombok.experimental.UtilityClass;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* 缓存工具类
*
* @author lingee
* @date 2023/7/27
*/
@UtilityClass
public class RedisUtil {
private static final Long SUCCESS = 1L;
/**
* 指定缓存失效时间
*
* @param key 键
* @param time 时间(秒)
*/
public boolean expire(String key, long time) {
RedisTemplate<Object, Object> redisTemplate = SpringUtil.getBean("redisTemplate");
Optional.ofNullable(redisTemplate)
.filter(template -> time > 0)
.ifPresent(template -> template.expire(key, time, TimeUnit.SECONDS));
return true;
}
/**
* 指定缓存失效时间
*
* @param key 键
* @param time 时间
* @param timeUnit 时间单位
*/
public boolean expire(String key, long time, TimeUnit timeUnit) {
RedisTemplate<Object, Object> redisTemplate = SpringUtil.getBean("redisTemplate");
Optional.ofNullable(redisTemplate)
.filter(template -> time > 0)
.ifPresent(template -> template.expire(key, time, timeUnit));
return true;
}
/**
* 根据 key 获取过期时间
*
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(Object key) {
RedisTemplate<Object, Object> redisTemplate = SpringUtil.getBean("redisTemplate");
return Optional.ofNullable(redisTemplate)
.map(template -> template.getExpire(key, TimeUnit.SECONDS))
.orElse(-1L);
}
/**
* 查找匹配key
*
* @param pattern key
* @return /
*/
public List<String> scan(String pattern) {
RedisTemplate<Object, Object> redisTemplate = SpringUtil.getBean("redisTemplate");
ScanOptions options = ScanOptions.scanOptions().match(pattern).build();
return Optional.ofNullable(redisTemplate).map(template -> {
RedisConnectionFactory factory = template.getConnectionFactory();
RedisConnection rc = Objects.requireNonNull(factory).getConnection();
Cursor<byte[]> cursor = rc.scan(options);
List<String> result = new ArrayList<>();
while (cursor.hasNext()) {
result.add(new String(cursor.next()));
}
RedisConnectionUtils.releaseConnection(rc, factory);
return result;
}).orElse(Collections.emptyList());
}
/**
* 分页查询 key
*
* @param patternKey key
* @param page 页码
* @param size 每页数目
* @return /
*/
public List<String> findKeysForPage(String patternKey, int page, int size) {
RedisTemplate redisTemplate = SpringUtil.getBean("redisTemplate");
ScanOptions options = ScanOptions.scanOptions().match(patternKey).build();
RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
RedisConnection rc = Objects.requireNonNull(factory).getConnection();
Cursor<byte[]> cursor = rc.scan(options);
List<String> result = new ArrayList<>(size);
int tmpIndex = 0;
int fromIndex = page * size;
int toIndex = page * size + size;
while (cursor.hasNext()) {
if (tmpIndex >= fromIndex && tmpIndex < toIndex) {
result.add(new String(cursor.next()));
tmpIndex++;
continue;
}
// 获取到满足条件的数据后,就可以退出了
if (tmpIndex >= toIndex) {
break;
}
tmpIndex++;
cursor.next();
}
RedisConnectionUtils.releaseConnection(rc, factory);
return result;
}
/**
* 判断key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
RedisTemplate<Object, Object> redisTemplate = SpringUtil.getBean("redisTemplate");
return Optional.ofNullable(redisTemplate).map(template -> template.hasKey(key)).orElse(false);
}
/**
* 删除缓存
*
* @param keys 可以传一个值 或多个
*/
public void del(String... keys) {
RedisTemplate<Object, Object> redisTemplate = SpringUtil.getBean("redisTemplate");
for (String key : keys) {
if (hasKey(key)) {
redisTemplate.delete(key);
}
}
}
/**
* 获取锁
*
* @param lockKey 锁key
* @param value value
* @param expireTime单位-秒
* @return boolean
*/
public boolean getLock(String lockKey, String value, int expireTime) {
RedisTemplate<Object, Object> redisTemplate = SpringUtil.getBean("redisTemplate");
return Optional.ofNullable(redisTemplate)
.map(template -> template.opsForValue().setIfAbsent(lockKey, value, expireTime, TimeUnit.SECONDS))
.orElse(false);
}
/**
* 释放锁
*
* @param lockKey 锁key
* @param value value
* @return boolean
*/
public boolean releaseLock(String lockKey, String value) {
RedisTemplate<Object, Object> redisTemplate = SpringUtil.getBean("redisTemplate");
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
return Optional.ofNullable(redisTemplate.execute(redisScript, Collections.singletonList(lockKey), value))
.map(Convert::toLong)
.filter(SUCCESS::equals)
.isPresent();
}
// ============================String=============================
/**
* 普通缓存获取
*
* @param key 键
* @return 值
*/
public <T> T get(String key) {
RedisTemplate<String, T> redisTemplate = SpringUtil.getBean("redisTemplate");
return redisTemplate.opsForValue().get(key);
}
/**
* 批量获取
*
* @param keys
* @return
*/
public <T> List<T> multiGet(List<String> keys) {
RedisTemplate<String, T> redisTemplate = SpringUtil.getBean("redisTemplate");
return redisTemplate.opsForValue().multiGet(keys);
}
/**
* 普通缓存放入
*
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
CacheProperties cacheProperties = SpringUtil.getBean(CacheProperties.class);
RedisTemplate<Object, Object> redisTemplate = SpringUtil.getBean("redisTemplate");
Optional.ofNullable(redisTemplate).map(template -> {
template.opsForValue().set(key, value, cacheProperties.getTimeout(), TimeUnit.SECONDS);
return true;
});
return true;
}
/**
* 普通缓存放入
*
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean increment(String key, long value) {
CacheProperties cacheProperties = SpringUtil.getBean(CacheProperties.class);
RedisTemplate<Object, Object> redisTemplate = SpringUtil.getBean("redisTemplate");
Optional.ofNullable(redisTemplate).map(template -> {
template.opsForValue().increment(key, value);
return true;
});
return true;
}
/**
* 普通缓存放入并设置时间
*
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
RedisTemplate<Object, Object> redisTemplate = SpringUtil.getBean("redisTemplate");
return Optional.ofNullable(redisTemplate).map(template -> {
if (time > 0) {
template.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
template.opsForValue().set(key, value);
}
return true;
}).orElse(false);
}
/**
* 普通缓存放入并设置时间
*
* @param key 键
* @param value 值
* @param time 时间
* @param timeUnit 类型
* @return true成功 false 失败
*/
public <T> boolean set(String key, T value, long time, TimeUnit timeUnit) {
RedisTemplate<String, T> redisTemplate = SpringUtil.getBean("redisTemplate");
Optional.ofNullable(redisTemplate).map(template -> {
if (time > 0) {
template.opsForValue().set(key, value, time, timeUnit);
} else {
template.opsForValue().set(key, value);
}
return true;
});
return true;
}
// ================================Map=================================
/**
* HashGet
*
* @param key 键 不能为null
* @param hashKey 项 不能为null
* @return 值
*/
public <HK, HV> HV hget(String key, HK hashKey) {
RedisTemplate<String, HV> redisTemplate = SpringUtil.getBean("redisTemplate");
return redisTemplate.<HK, HV>opsForHash().get(key, hashKey);
}
/**
* 获取hashKey对应的所有键值
*
* @param key 键
* @return 对应的多个键值
*/
public <HK, HV> Map<HK, HV> hmget(String key) {
RedisTemplate<String, HV> redisTemplate = SpringUtil.getBean("redisTemplate");
return redisTemplate.<HK, HV>opsForHash().entries(key);
}
/**
* HashSet
*
* @param key 键
* @param map 对应多个键值
* @return true 成功 false 失败
*/
public boolean hmset(String key, Map<?, ?> map) {
RedisTemplate<Object, Object> redisTemplate = SpringUtil.getBean("redisTemplate");
Optional.ofNullable(redisTemplate).map(template -> {
template.opsForHash().putAll(key, map);
return true;
});
return true;
}
/**
* HashSet 并设置时间
*
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<?, ?> map, long time) {
RedisTemplate<Object, Object> redisTemplate = SpringUtil.getBean("redisTemplate");
Optional.ofNullable(redisTemplate).map(template -> {
template.opsForHash().putAll(key, map);
if (time > 0) {
template.expire(key, time, TimeUnit.SECONDS);
}
return true;
});
return true;
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key, Object item, Object value) {
RedisTemplate<Object, Object> redisTemplate = SpringUtil.getBean("redisTemplate");
return Optional.ofNullable(redisTemplate).map(template -> {
template.opsForHash().put(key, item, value);
return true;
}).orElse(false);
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, Object item, Object value, long time) {
RedisTemplate<Object, Object> redisTemplate = SpringUtil.getBean("redisTemplate");
return Optional.ofNullable(redisTemplate).map(template -> {
template.opsForHash().put(key, item, value);
if (time > 0) {
template.expire(key, time, TimeUnit.SECONDS);
}
return true;
}).orElse(false);
}
/**
* 删除hash表中的值
*
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item) {
RedisTemplate redisTemplate = SpringUtil.getBean("redisTemplate");
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判断hash表中是否有该项的值
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
RedisTemplate redisTemplate = SpringUtil.getBean("redisTemplate");
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
*
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
* @return
*/
public double hincr(String key, String item, double by) {
RedisTemplate redisTemplate = SpringUtil.getBean("redisTemplate");
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash递减
*
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
* @return
*/
public double hdecr(String key, String item, double by) {
RedisTemplate redisTemplate = SpringUtil.getBean("redisTemplate");
return redisTemplate.opsForHash().increment(key, item, -by);
}
// ============================set=============================
/**
* 根据key获取Set中的所有值
*
* @param key 键
* @return
*/
public <T> Set<T> sGet(String key) {
RedisTemplate<String, T> redisTemplate = SpringUtil.getBean("redisTemplate");
return redisTemplate.opsForSet().members(key);
}
/**
* 根据value从一个set中查询,是否存在
*
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
RedisTemplate redisTemplate = SpringUtil.getBean("redisTemplate");
return redisTemplate.opsForSet().isMember(key, value);
}
/**
* 将数据放入set缓存
*
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
RedisTemplate redisTemplate = SpringUtil.getBean("redisTemplate");
return redisTemplate.opsForSet().add(key, values);
}
/**
* 将set数据放入缓存
*
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, Object... values) {
RedisTemplate redisTemplate = SpringUtil.getBean("redisTemplate");
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0) {
expire(key, time);
}
return count;
}
/**
* 获取set缓存的长度
*
* @param key 键
* @return
*/
public long sGetSetSize(String key) {
RedisTemplate redisTemplate = SpringUtil.getBean("redisTemplate");
return redisTemplate.opsForSet().size(key);
}
/**
* 移除值为value的
*
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object... values) {
RedisTemplate redisTemplate = SpringUtil.getBean("redisTemplate");
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
}
/**
* 获集合key1和集合key2的差集元素
*
* @param key 键
* @return
*/
public <T> Set<T> sDifference(String key, String otherKey) {
RedisTemplate<String, T> redisTemplate = SpringUtil.getBean("redisTemplate");
return redisTemplate.opsForSet().difference(key, otherKey);
}
// ===============================list=================================
/**
* 获取list缓存的内容
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
* @return
*/
public <T> List<T> lGet(String key, long start, long end) {
RedisTemplate<String, T> redisTemplate = SpringUtil.getBean("redisTemplate");
return redisTemplate.opsForList().range(key, start, end);
}
/**
* 获取list缓存的长度
*
* @param key 键
* @return
*/
public long lGetListSize(String key) {
RedisTemplate redisTemplate = SpringUtil.getBean("redisTemplate");
return redisTemplate.opsForList().size(key);
}
/**
* 通过索引 获取list中的值
*
* @param key 键
* @param index 索引 index>=0时 0 表头1 第二个元素依次类推index<0时-1表尾-2倒数第二个元素依次类推
* @return
*/
public Object lGetIndex(String key, long index) {
RedisTemplate redisTemplate = SpringUtil.getBean("redisTemplate");
return redisTemplate.opsForList().index(key, index);
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, Object value) {
RedisTemplate redisTemplate = SpringUtil.getBean("redisTemplate");
redisTemplate.opsForList().rightPush(key, value);
return true;
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, Object value, long time) {
RedisTemplate<Object, Object> redisTemplate = SpringUtil.getBean("redisTemplate");
redisTemplate.opsForList().rightPush(key, value);
if (time > 0) {
Optional.ofNullable(redisTemplate).ifPresent(template -> template.expire(key, time, TimeUnit.SECONDS));
}
return true;
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
RedisTemplate redisTemplate = SpringUtil.getBean("redisTemplate");
redisTemplate.opsForList().rightPushAll(key, value);
return true;
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
RedisTemplate redisTemplate = SpringUtil.getBean("redisTemplate");
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0) {
expire(key, time);
}
return true;
}
/**
* 根据索引修改list中的某条数据
*
* @param key 键
* @param index 索引
* @param value 值
* @return /
*/
public boolean lUpdateIndex(String key, long index, Object value) {
RedisTemplate redisTemplate = SpringUtil.getBean("redisTemplate");
redisTemplate.opsForList().set(key, index, value);
return true;
}
/**
* 移除N个值为value
*
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key, long count, Object value) {
RedisTemplate redisTemplate = SpringUtil.getBean("redisTemplate");
return redisTemplate.opsForList().remove(key, count, value);
}
/**
* 将zSet数据放入缓存
*
* @param key
* @param time
* @param tuples
* @return
*/
public long zSetAndTime(String key, long time, Set<ZSetOperations.TypedTuple<Object>> tuples) {
RedisTemplate redisTemplate = SpringUtil.getBean("redisTemplate");
Long count = redisTemplate.opsForZSet().add(key, tuples);
if (time > 0) {
expire(key, time);
}
return count;
}
/**
* Sorted set:有序集合获取
*
* @param key
* @param min
* @param max
* @return
*/
public Set<Object> zRangeByScore(String key, double min, double max) {
RedisTemplate redisTemplate = SpringUtil.getBean("redisTemplate");
ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
return zset.rangeByScore(key, min, max);
}
/**
* Sorted set:有序集合获取 正序
*
* @param key
* @param start
* @param end
* @return
*/
public Set<Object> zRange(String key, long start, long end) {
RedisTemplate redisTemplate = SpringUtil.getBean("redisTemplate");
ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
return zset.range(key, start, end);
}
/**
* Sorted set:有序集合获取 倒叙
*
* @param key
* @param start
* @param end
* @return
*/
public Set<Object> zReverseRange(String key, long start, long end) {
RedisTemplate redisTemplate = SpringUtil.getBean("redisTemplate");
ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
return zset.reverseRange(key, start, end);
}
/**
* 获取zSet缓存的长度
*
* @param key 键
* @return
*/
public long zGetSetSize(String key) {
RedisTemplate redisTemplate = SpringUtil.getBean("redisTemplate");
return redisTemplate.opsForZSet().size(key);
}
}

View File

@@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.codvision.cachecore.config.RedisConfig,\
com.codvision.cachecore.config.CacheProperties

View File

@@ -0,0 +1,38 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

View File

@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2023 codvision.com All Rights Reserved.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.codvision</groupId>
<artifactId>cvbp-public</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cvbp-common-core</artifactId>
<packaging>jar</packaging>
<version>1.0.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-crypto</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-extra</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-json</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,16 @@
package com.codvision.commoncore.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* @author codvision
*/
@Data
@AllArgsConstructor
public class CoordinatePoint {
Double lng;
Double lat;
}

View File

@@ -0,0 +1,189 @@
package com.codvision.commoncore.common;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ExpiryMap<K, V> implements Map<K, V> {
private boolean isRefresh = false;
private ScheduledExecutorService scheduledExecutorService;
private static final ConcurrentHashMap WORK_MAP = new ConcurrentHashMap();
private static final ConcurrentHashMap<Object, Long> EXPIRY_MAP = new ConcurrentHashMap<>();
public ExpiryMap() {
super();
}
public ExpiryMap(boolean isRefresh) {
this.isRefresh = isRefresh;
startClearKey();
}
public void closeMap() {
scheduledExecutorService.shutdown();
}
/**
* 定时清除失效key
*/
public void startClearKey() {
scheduledExecutorService = new ScheduledThreadPoolExecutor(1, new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build());
scheduledExecutorService.scheduleAtFixedRate(
new Runnable() {
@Override
public void run() {
System.out.println("===" + Thread.currentThread().getName() + "===");
removeInvalidKeys();
}
}
, 0, 3600, TimeUnit.SECONDS);
}
private static void removeInvalidKeys() {
EXPIRY_MAP.keySet().forEach(key -> {
if (EXPIRY_MAP.get(key) < System.currentTimeMillis()) {
EXPIRY_MAP.remove(key);
WORK_MAP.remove(key);
}
});
System.gc();
}
/**
* put方法需要设置key 的有效期!单位为:毫秒
*
* @param key
* @param value
* @param expiry key的有效期单位毫秒
* @return
*/
public V put(K key, V value, long expiry) {
if (!containsKey(key) || isRefresh) {//更新value只有需要刷新时间时才需要操作expiryMap
EXPIRY_MAP.put(key, System.currentTimeMillis() + expiry);
}
WORK_MAP.put(key, value);
return value;
}
@Override
public int size() {
return keySet().size();
}
@Override
public boolean isEmpty() {
return size() == 0;
}
@Override
public boolean containsKey(Object key) {
if (key != null && EXPIRY_MAP.containsKey(key)) {
boolean flag = EXPIRY_MAP.get(key) > System.currentTimeMillis();
return flag;
}
return false;
}
@Override
public boolean containsValue(Object value) {
Collection values = WORK_MAP.values();
if (values != null) {
return values.contains(value);
}
return false;
}
@Override
public V get(Object key) {
if (containsKey(key)) {
return (V) WORK_MAP.get(key);
}
return null;
}
@Deprecated
@Override
public V put(K key, V value) {
throw new RuntimeException("此方法已废弃请加上key失效时间");
}
@Override
public V remove(Object key) {
boolean containKey = containsKey(key);
EXPIRY_MAP.remove(key);
if (containKey) {
return (V) WORK_MAP.remove(key);
} else {
return null;
}
}
@Deprecated
@Override
public void putAll(Map<? extends K, ? extends V> m) {
throw new RuntimeException("此方法已废弃!");
}
@Override
public void clear() {
EXPIRY_MAP.clear();
WORK_MAP.clear();
}
@Override
public Set<K> keySet() {
removeInvalidKeys();
return WORK_MAP.keySet();
}
@Override
public Collection<V> values() {
removeInvalidKeys();
return WORK_MAP.values();
}
@Override
public Set<Entry<K, V>> entrySet() {
removeInvalidKeys();
return WORK_MAP.entrySet();
}
public static void main(String[] args) {
//ExpiryMap emap=new ExpiryMap();
ExpiryMap emap = new ExpiryMap(true);
emap.put("key1", "value1", 2 * 1000);
emap.put("key2", "value2", 5 * 1000);
emap.put("key3", "value3", 15 * 1000);
System.out.println(emap.size());
try {
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(emap.size());
System.out.println(emap.keySet());
System.out.println(emap.values());
System.out.println(emap.entrySet());
emap.put("key4", "value4", 8 * 1000);
System.out.println(emap.size());
try {
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(emap.size());
System.out.println(emap.keySet());
System.out.println(emap.values());
System.out.println(emap.entrySet());
emap.closeMap();
}
}

View File

@@ -0,0 +1,133 @@
package com.codvision.commoncore.common;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.HashMap;
/**
* @author codvision
*/
@ApiModel(description = "返回响应数据")
public class ResponseEntity<T> {
@ApiModelProperty(value = "编码")
private int code = 200;
@ApiModelProperty(value = "基本信息")
private String message = "成功";
@ApiModelProperty(value = "返回对象")
private T data;
private static final HashMap<Integer, String> ERROR_CODE = new HashMap<Integer, String>() {
{
put(100, "暂无数据");
put(200, "成功");
put(10000, "通用错误");
///用户类
put(10001, "用户名或密码错误");
put(10002, "登录状态已过期");
put(10003, "注册用户已存在");
put(10004, "账号已被锁定,请在一小时后重试");
///操作权限类
put(20001, "无操作权限");
///参数类
put(30001, "非法参数");
put(30002, "缺少必要参数");
put(30003, "文件内容为空");
put(30004, "不支持的文件格式");
put(30005, "获取参数失败");
////数据操作类
put(40001, "添加数据失败");
put(40002, "更新数据失败");
put(40003, "删除数据失败");
put(50001, "不存在的对象");
put(50002, "文件上传超过最大限制!");
put(50003, "文件上传失败!");
put(110000, "获取用户失败或用户不存在!");
put(100001, "钉钉获取用户信息失败,请稍后再试");
put(110002, "小程序授权获取手机号失败");
put(990000, "系统错误");
}
};
public ResponseEntity() {
}
public ResponseEntity(T data) {
this.data = data;
}
public int getCode() {
return code;
}
public ResponseEntity setCode(int code) {
this.code = code;
if (ERROR_CODE.containsKey(code)) {
setMessage(ERROR_CODE.get(code));
}
return this;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public ResponseEntity setData(T data) {
this.data = data;
return this;
}
public static <T> ResponseEntity<T> def(Class<T> clazz) {
return new ResponseEntity<>();
}
public ResponseEntity<T> ok() {
setCode(200);
return this;
}
public ResponseEntity<T> error(int code) {
setCode(code);
return this;
}
public ResponseEntity<T> error(int code, String message) {
setCode(code);
setMessage(message);
return this;
}
public ResponseEntity<T> message(String message) {
setMessage(message);
return this;
}
public ResponseEntity<T> data(T data) {
setData(data);
return this;
}
public ResponseEntity<T> back(int code, String message, T data) {
setCode(code);
setMessage(message);
setData(data);
return this;
}
}

View File

@@ -0,0 +1,43 @@
package com.codvision.commoncore.enums;
/**
* @author codvision
*/
public enum NetworkData {
/**
* 月的天数
*/
MONTH(30, ""),
/**
* 周的天数
*/
WEEK(7, ""),
/**
* NET_WORK_ID
*/
NET_WORK_ID(0, "0:0:0:0:0:0:0:1"),
/**
* 成功
*/
OK(200, "ok");
private final int code;
private final String desc;
NetworkData(int code, String desc) {
this.code = code;
this.desc = desc;
}
public int getCode() {
return code;
}
public String getDesc() {
return desc;
}
}

View File

@@ -0,0 +1,64 @@
package com.codvision.commoncore.enums;
/**
* where 条件操作符号
*
* @author lingee
* @date 2022/5/9
*/
public enum SqlOperator {
/**
* like
*/
LIKE("LIKE"),
/**
* =
*/
EQ("="),
/**
* 不等于
*/
NE("<>"),
/**
* 大于
*/
GT(">"),
/**
* 大于等于
*/
GE(">="),
/**
* 小于
*/
LT("<"),
/**
* 小于等于
*/
LE("<="),
/**
* 为空
*/
IS_NULL("IS NULL"),
/**
* 不为空
*/
IS_NOT_NULL("IS NOT NULL"),
/**
* 范围查找
*/
BETWEEN("BETWEEN"),
/**
* 多数据查找
*/
IN("IN");
private final String operator;
SqlOperator(String operator) {
this.operator = operator;
}
public String getOperator() {
return operator;
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
package com.codvision.commoncore.enums;
/**
* TrueOrFalse
*
* @author lingee
* @since 2024/4/29
*/
public enum TrueOrFalse {
FALSE("0", 0, false),
TRUE("1", 1, true);
private final String key;
private final int intVal;
private final boolean val;
TrueOrFalse(String key, int intVal, boolean val) {
this.key = key;
this.intVal = intVal;
this.val = val;
}
public String getKey() {
return key;
}
public int getIntVal() {
return intVal;
}
public boolean isVal() {
return val;
}
}

View File

@@ -0,0 +1,38 @@
package com.codvision.commoncore.exception;
import lombok.Getter;
import lombok.Setter;
/**
* 认证异常
*
* @author hxl
*/
@Getter
@Setter
public class AuthException extends RuntimeException {
private Integer code;
private String message;
public AuthException() {
super();
}
public AuthException(int code) {
this.code = code;
}
public AuthException(String message) {
super(message);
this.message = message;
}
public AuthException(int code, String message) {
super(message);
this.code = code;
this.message = message;
}
}

View File

@@ -0,0 +1,37 @@
package com.codvision.commoncore.exception;
import lombok.Getter;
import lombok.Setter;
/**
* 业务异常
*
* @author hxl
*/
@Getter
@Setter
public class BusinessException extends RuntimeException {
private Integer code;
private String message;
public BusinessException() {
super();
}
public BusinessException(String message) {
super(message);
this.message = message;
}
public BusinessException(int code) {
this.code = code;
}
public BusinessException(int code, String message) {
super(message);
this.code = code;
this.message = message;
}
}

View File

@@ -0,0 +1,15 @@
package com.codvision.commoncore.exception;
/**
* @Author huxb
* @description:
* @create 2023/2/7 16:16
*/
public class IdempotentException extends RuntimeException {
public IdempotentException() {
}
public IdempotentException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,16 @@
package com.codvision.commoncore.exception;
/**
* @Author huxb
* @description:
* @create 2022/11/22 10:28
*/
public class LimitException extends RuntimeException{
public LimitException() {
super();
}
public LimitException(String msg) {
super(msg);
}
}

View File

@@ -0,0 +1,38 @@
package com.codvision.commoncore.exception;
import lombok.Getter;
import lombok.Setter;
/**
* 权限异常
*
* @author hxl
*/
@Getter
@Setter
public class PermissionException extends RuntimeException {
private Integer code;
private String message;
public PermissionException() {
super();
}
public PermissionException(int code) {
this.code = code;
}
public PermissionException(String message) {
super(message);
this.message = message;
}
public PermissionException(int code, String message) {
super(message);
this.code = code;
this.message = message;
}
}

View File

@@ -0,0 +1,11 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
/**
* 公共模块
*
* @author lingee
* @date 2023/7/14
*/
package com.codvision.commoncore;

View File

@@ -0,0 +1,139 @@
package com.codvision.commoncore.utils;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.management.openmbean.InvalidKeyException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
/**
* @author zjm
* @version V1.0
* @Package com.codvision.xlwzl.util
* @date 2020/7/3 10:59
* @Copyright © 2017-2020 杭州码全科技有限公司
* @Description 使用ECB模式加密
*/
public class AES {
/**
* 偏移量字符串必须是16位 当模式是CBC的时候必须设置偏移量
*/
private static final String iv = "0123456789ABCDEF";
private static final String Algorithm = "AES";
/**
* 算法/模式/补码方式
*/
private static final String AlgorithmProvider = "AES/ECB/PKCS5Padding";
public static byte[] generatorKey() throws NoSuchAlgorithmException {
KeyGenerator keyGenerator = KeyGenerator.getInstance(Algorithm);
//默认128获得无政策权限后可为192或256
keyGenerator.init(256);
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
}
public static IvParameterSpec getIv() throws UnsupportedEncodingException {
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
System.out.println("偏移量:" + byteToHexString(ivParameterSpec.getIV()));
return ivParameterSpec;
}
public static byte[] encrypt(String src) throws NoSuchPaddingException, NoSuchAlgorithmException, java.security.InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException {
return encrypt(src, "codvision@2020YH".getBytes(StandardCharsets.UTF_8));
}
public static byte[] encrypt(String src, byte[] key) throws NoSuchPaddingException, NoSuchAlgorithmException, java.security.InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
SecretKey secretKey = new SecretKeySpec(key, Algorithm);
//IvParameterSpec ivParameterSpec = getIv();
Cipher cipher = Cipher.getInstance(AlgorithmProvider);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] cipherBytes = cipher.doFinal(src.getBytes(StandardCharsets.UTF_8));
return cipherBytes;
}
public static String decrypt(String src) throws Exception {
return decrypt(src, "codvision@2020YH".getBytes(StandardCharsets.UTF_8));
}
public static String decrypt(String src, byte[] key) throws Exception {
SecretKey secretKey = new SecretKeySpec(key, Algorithm);
//IvParameterSpec ivParameterSpec = getIv();
Cipher cipher = Cipher.getInstance(AlgorithmProvider);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] hexBytes = hexStringToBytes(src);
byte[] plainBytes = cipher.doFinal(hexBytes);
return new String(plainBytes, StandardCharsets.UTF_8);
}
/**
* 将byte转换为16进制字符串
*
* @param src
* @return
*/
public static String byteToHexString(byte[] src) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xff;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
sb.append("0");
}
sb.append(hv);
}
return sb.toString();
}
/**
* 将16进制字符串装换为byte数组
*
* @param hexString
* @return
*/
public static byte[] hexStringToBytes(String hexString) {
hexString = hexString.toUpperCase();
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] b = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
b[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return b;
}
private static byte charToByte(char c) {
return (byte) "0123456789ABCDEF".indexOf(c);
}
public static void main(String[] args) {
try {
// byte key[] = generatorKey();
// 密钥必须是16的倍数
String src = "6ea40c84b3fe7c6c3033e59bd59643a3";
System.out.println("原字符串:" + src);
String enc = byteToHexString(encrypt(src));
System.out.println("加密:" + enc);
System.out.println("解密:" + decrypt(enc));
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,76 @@
package com.codvision.commoncore.utils;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.CryptoException;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
/**
* @author hxl
*/
public class AesUtil {
/**
* Cipher负责完成加密或解密工作
*/
private Cipher cipher;
/**
* 密钥
*/
private SecretKey secretKey;
/**
* @param key 密钥 转为为字节后支持三种密钥长度128、192、256位
*/
public AesUtil(String key) {
this(new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), SymmetricAlgorithm.AES.getValue()));
}
public AesUtil(SecretKey secretKey) {
try {
this.cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
} catch (Exception e) {
throw new CryptoException(e);
}
this.secretKey = secretKey;
}
public byte[] encrypt(byte[] data) {
try {
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(new byte[cipher.getBlockSize()]));
return cipher.doFinal(data);
} catch (Exception e) {
throw new CryptoException(e);
}
}
public byte[] decrypt(byte[] data) {
try {
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(new byte[cipher.getBlockSize()]));
return cipher.doFinal(data);
} catch (Exception e) {
throw new CryptoException(e);
}
}
public String encryptHex(byte[] data) {
return HexUtil.encodeHexStr(encrypt(data));
}
public byte[] decryptHex(String data) {
return decrypt(HexUtil.decodeHex(data));
}
public String encryptBase64(byte[] data) {
return Base64.encode(encrypt(data));
}
public byte[] decryptBase64(String data) {
return decrypt(Base64.decode(data));
}
}

View File

@@ -0,0 +1,74 @@
package com.codvision.commoncore.utils;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.UUID;
/**
* @Author huxb
* @description: 随机生成appKeyappSecret
* @create 2021/12/21 16:44
*/
public class AppUtils {
//生成 app_secret 密钥
private final static String SERVER_NAME = "maquan_codvision";
private final static String[] chars = new String[]{"a", "b", "c", "d", "e", "f",
"g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s",
"t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I",
"J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
"W", "X", "Y", "Z"};
/**
* 短8位UUID思想其实借鉴微博短域名的生成方式但是其重复概率过高而且每次生成4个需要随即选取一个。
* 本算法利用62个可打印字符通过随机生成32位UUID由于UUID都为十六进制所以将UUID分成8组每4个为一组然后通过模62操作结果作为索引取出字符
* 这样重复率大大降低。
* 经测试,在生成一千万个数据也没有出现重复,完全满足大部分需求。
*/
public static String getAppId() {
StringBuffer shortBuffer = new StringBuffer();
String uuid = UUID.randomUUID().toString().replace("-", "");
for (int i = 0; i < 8; i++) {
String str = uuid.substring(i * 4, i * 4 + 4);
int x = Integer.parseInt(str, 16);
shortBuffer.append(chars[x % 0x3E]);
}
return shortBuffer.toString();
}
/**
* 通过appId和内置关键词生成APP Secret
*/
public static String getAppSecret(String appId) {
try {
String[] array = new String[]{appId, SERVER_NAME};
StringBuffer sb = new StringBuffer();
// 字符串排序
Arrays.sort(array);
for (int i = 0; i < array.length; i++) {
sb.append(array[i]);
}
String str = sb.toString();
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(str.getBytes());
byte[] digest = md.digest();
StringBuffer hexstr = new StringBuffer();
String shaHex = "";
for (int i = 0; i < digest.length; i++) {
shaHex = Integer.toHexString(digest[i] & 0xFF);
if (shaHex.length() < 2) {
hexstr.append(0);
}
hexstr.append(shaHex);
}
return hexstr.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException();
}
}
}

View File

@@ -0,0 +1,163 @@
package com.codvision.commoncore.utils;
import cn.hutool.core.util.ObjectUtil;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* 对象工具类
*
* @author lingee
* @since 2023-7-20
*/
public class BeanUtil extends cn.hutool.core.bean.BeanUtil {
/**
* 获取属性名数组
*
* @param o 获取字段的对象
* @return 返回各个字段
*/
public static Field[] getFields(Object o) {
Field[] fields = o.getClass().getDeclaredFields();
Field[] superFields = o.getClass().getSuperclass().getDeclaredFields();
Field[] allFields = new Field[fields.length + superFields.length];
int index = 0;
for (int i = 0; i < fields.length; i++) {
if ("serialVersionUID".equals(fields[i].getName())) {
continue;
}
Field field = fields[i];
if (ObjectUtil.isNotEmpty(field)) {
allFields[index] = field;
}
index++;
}
for (int i = 0; i < superFields.length; i++) {
if ("id".equals(superFields[i].getName())
|| "serialVersionUID".equals(superFields[i].getName())) {
continue;
}
Field field = superFields[i];
if (ObjectUtil.isNotEmpty(field)) {
allFields[index] = field;
}
index++;
}
return allFields;
}
/**
* 获取属性名数组
*
* @param o 获取字段的对象
* @return 返回各个字段
*/
public static String[] getFieldName(Object o) {
Field[] fields = o.getClass().getDeclaredFields();
Field[] superFields = o.getClass().getSuperclass().getDeclaredFields();
String[] fieldNames = new String[fields.length + superFields.length];
int index = 0;
for (int i = 0; i < fields.length; i++) {
fieldNames[index] = fields[i].getName();
index++;
}
for (int i = 0; i < superFields.length; i++) {
if ("id".equals(superFields[i].getName())) {
continue;
}
fieldNames[index] = superFields[i].getName();
index++;
}
return fieldNames;
}
/**
* 根据属性名获取属性值
*
* @param fieldName 属性名
* @param o 对象
* @return 属性值
*/
public static Object getFieldValueByName(String fieldName, Object o) {
try {
String firstLetter = fieldName.substring(0, 1).toUpperCase();
String getter = "get" + firstLetter + fieldName.substring(1);
Method method = o.getClass().getMethod(getter, new Class[]{});
Object value = method.invoke(o, new Object[]{});
return value;
} catch (Exception e) {
return null;
}
}
/**
* 将对象转换为key value
* A=a&B=b&C=c 格式
*
* @param object 对象
* @return 格式化结果
*/
public static String formatKeyValuePair(Object object) {
//准备接受的字符串
StringBuilder stringBuffer = new StringBuilder();
//获取对象字段
String[] fieldNames = BeanUtil.getFieldName(object);
//遍历所有属性
for (int j = 0; j < fieldNames.length; j++) {
//不是第一个并且不是最后一个,拼接&
if (j != 0) {
stringBuffer.append("&");
}
//获取属性的名字
String key = fieldNames[j];
//获取值
Object value = BeanUtil.getFieldValueByName(key, object);
assert value != null;
stringBuffer.append(key).append("=").append(value.toString());
}
return stringBuffer.toString();
}
/**
* key value键值对 转换为 对象
* A=a&B=b&C=c 格式 转换为对象
*
* @param str 对象字符串
* @param t 范型
* @param <T> 范型
* @return 格式化结果
*/
public static <T> T formatKeyValuePair(String str, T t) {
//填写对参数键值对
String[] params = str.split("&");
//获取对象字段
String[] fieldNames = BeanUtil.getFieldName(t);
try {
//循环每个参数
for (String param : params) {
String[] keyValues = param.split("=");
for (int i = 0; i < fieldNames.length; i++) {
if (fieldNames[i].equals(keyValues[0])) {
Field f = t.getClass().getDeclaredField(fieldNames[i]);
f.setAccessible(true);
//长度为2 才转换,否则不转
if (keyValues.length == 2) {
f.set(t, keyValues[1]);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return t;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
package com.codvision.commoncore.utils;
import org.apache.commons.lang3.StringUtils;
/**
* 强密码校验包含数字、大小写字母和特殊符号长度要求8-15位且不与用户名相同
* @author hxl
*/
@SuppressWarnings("all")
public class CheckPwdUtil {
// 数字
public static final String REG_NUMBER = ".*\\d+.*";
// 小写字母
public static final String REG_UPPERCASE = ".*[A-Z]+.*";
// 大写字母
public static final String REG_LOWERCASE = ".*[a-z]+.*";
// 特殊符号(~!@#$%^&*()_+|<>,.?/:;'[]{}\)
public static final String REG_SYMBOL = ".*[~!@#$%^&*()_+|<>,.?/:;'\\[\\]{}\"]+.*";
public static boolean checkPasswordRule(String username, String password) {
if (StringUtils.isBlank(password) || password.length() < 8 || password.length() > 15) {
return false;
}
int i = 0;
if (password.matches(REG_NUMBER)) { i++; }
if (password.matches(REG_LOWERCASE)) { i++; }
if (password.matches(REG_UPPERCASE)) { i++; }
if (password.matches(REG_SYMBOL)) { i++; }
boolean contains = password.equals(username);
if (i < 4 || contains) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,139 @@
package com.codvision.commoncore.utils;
import com.codvision.commoncore.bean.CoordinatePoint;
/**
* 坐标工具类
*
* @author codvision
*/
public class CoordinateUtil {
public static final String BAIDU_LBS_TYPE = "bd09ll";
public static double pi = 3.1415926535897932384626;
public static double a = 6378245.0;
public static double ee = 0.00669342162296594323;
/**
* 84 to 火星坐标系 (GCJ-02)
*
* @param lat
* @param lon
* @return
*/
public static CoordinatePoint wgs84ToGcj02(double lon, double lat) {
if (outOfChina(lon, lat)) {
return null;
}
double dLat = transformLat(lon - 105.0, lat - 35.0);
double dLon = transformLon(lon - 105.0, lat - 35.0);
double radLat = lat / 180.0 * pi;
double magic = Math.sin(radLat);
magic = 1 - ee * magic * magic;
double sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
double mgLat = lat + dLat;
double mgLon = lon + dLon;
return new CoordinatePoint(mgLon, mgLat);
}
/**
* * 火星坐标系 (GCJ-02) to 84 * * @param lon * @param lat * @return
*/
public static CoordinatePoint gcj02ToWgs84(double lon, double lat) {
CoordinatePoint gps = transform(lon, lat);
double lontitude = lon * 2 - gps.getLng();
double latitude = lat * 2 - gps.getLat();
return new CoordinatePoint(lontitude, latitude);
}
/**
* 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换算法 将 GCJ-02 坐标转换成 BD-09 坐标
*
* @param ggLat
* @param ggLat
*/
public static CoordinatePoint gcj02ToBd09(double ggLng, double ggLat) {
double x = ggLng, y = ggLat;
double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * pi);
double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * pi);
double bdLng = z * Math.cos(theta) + 0.0065;
double bdLat = z * Math.sin(theta) + 0.006;
return new CoordinatePoint(bdLng, bdLat);
}
/**
* * 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换算法 * * 将 BD-09 坐标转换成GCJ-02 坐标 * * @param
* bd_lat * @param bd_lon * @return
*/
public static CoordinatePoint bd09ToGcj02(double bdLng, double bdLat) {
double x = bdLng - 0.0065, y = bdLat - 0.006;
double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * pi);
double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * pi);
double ggLng = z * Math.cos(theta);
double ggLat = z * Math.sin(theta);
return new CoordinatePoint(ggLng, ggLat);
}
/**
* (BD-09)-->84
*
* @param bdLng
* @param bdLat
* @return
*/
public static CoordinatePoint bd09ToWgs84(double bdLng, double bdLat) {
CoordinatePoint gcj02 = bd09ToGcj02(bdLng, bdLat);
CoordinatePoint map84 = gcj02ToWgs84(gcj02.getLng(),
gcj02.getLat());
return map84;
}
public static boolean outOfChina(double lon, double lat) {
if (lon < 72.004 || lon > 137.8347) {
return true;
}
return lat < 0.8293 || lat > 55.8271;
}
public static CoordinatePoint transform(double lon, double lat) {
if (outOfChina(lon, lat)) {
return new CoordinatePoint(lat, lon);
}
double dLat = transformLat(lon - 105.0, lat - 35.0);
double dLon = transformLon(lon - 105.0, lat - 35.0);
double radLat = lat / 180.0 * pi;
double magic = Math.sin(radLat);
magic = 1 - ee * magic * magic;
double sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
double mgLat = lat + dLat;
double mgLon = lon + dLon;
return new CoordinatePoint(mgLon, mgLat);
}
public static double transformLat(double x, double y) {
double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y
+ 0.2 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0;
return ret;
}
public static double transformLon(double x, double y) {
double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1
* Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0
* pi)) * 2.0 / 3.0;
return ret;
}
}

View File

@@ -0,0 +1,564 @@
package com.codvision.commoncore.utils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* @author codvision
*/
public class DateUtil extends cn.hutool.core.date.DateUtil {
public static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static final String STANDARD_DATE_FORMAT = "yyyy-MM-dd";
public static final String STANDARD_DATE_NO_UNDERLINE_FORMAT = "yyyyMMdd";
public static final String FULL_DATE = "yyyyMMddHHmmss";
public static Date getSomeWeekAgoDate(Date date, int weeks) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.WEEK_OF_YEAR, -weeks);
Date someWeeksAgo = calendar.getTime();
return someWeeksAgo;
}
/**
* 获取N个月前日期
*
* @param date
* @param month
* @return
*/
public static Date getSomeMonthAgoDate(Date date, int month) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.MONTH, -month);
Date someMonthAgo = calendar.getTime();
return someMonthAgo;
}
/**
* 获取N年前日期
*
* @param date
* @return
*/
public static Date getSomeYearAgoDate(Date date, int year) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.YEAR, -year);
Date someYearAgo = calendar.getTime();
return someYearAgo;
}
public static String strToStr(String strDate) {
Date date = new Date();
try {
//先按照原格式转换为时间
date = new SimpleDateFormat("yyyyMMddHHmmss").parse(strDate);
} catch (ParseException e) {
e.printStackTrace();
}
//再将时间转换为对应格式字符串
String str = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
return str;
}
/**
* 根据系统时间返回一串无符号字符串
*/
public static String toString(Date date) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
return simpleDateFormat.format(date);
}
/**
* 根据系统时间返回一串无符号字符串
*/
public static String monthToString(Date date) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMM");
return simpleDateFormat.format(date);
}
/**
* 根据系统时间返回一串无符号字符串
*/
public static String dayToString(Date date) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
return simpleDateFormat.format(date);
}
/**
* 根据系统时间返回一串无符号字符串
*/
public static String toStringFor16(Date date, String value) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
String data = simpleDateFormat.format(date);
Random rome = new Random();
int last = rome.nextInt(90000000) + 10000000;
return value + data + last;
}
public static Date toDate(String date) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
return simpleDateFormat.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
/**
* 将一个字符串转换成日期格式
*
* @param date 字符串日期
* @param pattern 日期格式
* @return date
*/
public static Date toDate(String date, String pattern) {
if ("".equals("" + date)) {
return null;
}
if (pattern == null) {
pattern = STANDARD_DATE_FORMAT;
}
SimpleDateFormat sdf = new SimpleDateFormat(pattern, Locale.ENGLISH);
Date newDate = new Date();
try {
newDate = sdf.parse(date);
} catch (Exception ex) {
ex.printStackTrace();
}
return newDate;
}
public static String sdf(Date date) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return simpleDateFormat.format(date);
}
public static String getMonthBeginTime() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd 00:00:00");
Calendar c = Calendar.getInstance();
c.add(Calendar.MONTH, 0);
//设置为1号,当前日期既为本月第一天
c.set(Calendar.DAY_OF_MONTH, 1);
return sdf.format(c.getTime());
}
public static String getMonthEndTime() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd 23:59:59");
Calendar c = Calendar.getInstance();
c.add(Calendar.MONTH, 0);
c.set(Calendar.DAY_OF_MONTH, c.getActualMaximum(Calendar.DAY_OF_MONTH));
return sdf.format(c.getTime());
}
public static String getMonthBeginTimeOfLastYear() {
Calendar c = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd 00:00:00");
c.add(Calendar.MONTH, 0);
c.add(Calendar.YEAR, -1);
//设置为1号,当前日期既为本月第一天
c.set(Calendar.DAY_OF_MONTH, 1);
return sdf.format(c.getTime());
}
public static String getMonthEndTimeOfLastYear() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd 23:59:59");
Calendar c = Calendar.getInstance();
c.add(Calendar.MONTH, 0);
c.add(Calendar.YEAR, -1);
c.set(Calendar.DAY_OF_MONTH, c.getActualMaximum(Calendar.DAY_OF_MONTH));
return sdf.format(c.getTime());
}
public static String getToday() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(new Date());
}
/**
* 获得昨天日期:yyyy-MM-dd HH:mm:ss
*/
public static String getYesterdayBeginTime() {
Calendar c = Calendar.getInstance();
c.add(Calendar.DATE, -1);
SimpleDateFormat startSdf = new SimpleDateFormat("yyyy-MM-dd 00:00:00");
return startSdf.format(c.getTime());
}
/**
* 获得昨天日期:yyyy-MM-dd HH:mm:ss
*/
public static String getYesterdayEndTime() {
Calendar c = Calendar.getInstance();
c.add(Calendar.DATE, -1);
SimpleDateFormat startSdf = new SimpleDateFormat("yyyy-MM-dd 23:59:59");
return startSdf.format(c.getTime());
}
public static String getTodayBeginTime() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd 00:00:00");
return sdf.format(new Date());
}
public static String getTodayEndTime() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd 23:59:59");
return sdf.format(new Date());
}
public static String getNowDateTime() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(new Date());
}
/**
* 获得明天日期:yyyy-MM-dd HH:mm:ss
*/
public static String getTomorrowBeginTime() {
Calendar c = Calendar.getInstance();
c.add(Calendar.DATE, 1);
SimpleDateFormat startSdf = new SimpleDateFormat("yyyy-MM-dd 00:00:00");
return startSdf.format(c.getTime());
}
/**
* 获得明天日期:yyyy-MM-dd HH:mm:ss
*/
public static String getTomorrowEndTime() {
Calendar c = Calendar.getInstance();
c.add(Calendar.DATE, 1);
SimpleDateFormat startSdf = new SimpleDateFormat("yyyy-MM-dd 23:59:59");
return startSdf.format(c.getTime());
}
/**
* 获得明天日期:yyyy-MM-dd time
*/
public static String getTomorrowTime(String time) {
Calendar c = Calendar.getInstance();
c.add(Calendar.DATE, 1);
SimpleDateFormat startSdf = new SimpleDateFormat(String.format("yyyy-MM-dd %s", time));
return startSdf.format(c.getTime());
}
public static String getYearBeginTime() {
Calendar c = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-01-01 00:00:00");
return sdf.format(c.getTime());
}
public static String getYearEndTime() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-12-31 23:59:59");
Calendar c = Calendar.getInstance();
return sdf.format(c.getTime());
}
public static String getYearBeginTimeOfLastYear() {
Calendar c = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-01-01 00:00:00");
c.add(Calendar.YEAR, -1);
return sdf.format(c.getTime());
}
public static String getYearEndTimeOfLastYear() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-12-31 23:59:59");
Calendar c = Calendar.getInstance();
c.add(Calendar.YEAR, -1);
return sdf.format(c.getTime());
}
/**
* 默认当天
*
* @return
*/
public static Map<String, Integer> getWeekOfMonth() {
return DateUtil.getWeekOfMonth(DateUtil.getToday());
}
/**
* 当前时间所属周,一周占用两个月的情况下属于上个月
*
* @param time
* @return
*/
public static Map<String, Integer> getWeekOfMonth(String time) {
//Calendar周日为一周的第一天
//1.当月的1号为周日:
//日期是否为周日 是,周数-1 第一个周日属于上个月最后一周
//2.当月的1号为周一:
//日期是否为周日 是,周数-1
//3.当月的1号为周二--六:
//日期是否为周日 是,周数-2 否,周数-1 为0属于上个月最后一周
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
int resultYear;
int resultMonth;
int resultWeekOfMonth;
Map<String, Integer> resultMap = new HashMap<>(16);
try {
Date parse = sdf.parse(time);
Calendar calendar = Calendar.getInstance();
calendar.setTime(parse);
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
int weekOfMonth = calendar.get(Calendar.WEEK_OF_MONTH);
resultYear = year;
resultMonth = month + 1;
Calendar monthCalendar = Calendar.getInstance();
monthCalendar.set(Calendar.YEAR, year);
monthCalendar.set(Calendar.MONTH, month);
monthCalendar.set(Calendar.DAY_OF_MONTH, 1);
int monthDayOfWeek = monthCalendar.get(Calendar.DAY_OF_WEEK);
if (monthDayOfWeek == Calendar.SUNDAY) {
if (dayOfWeek == Calendar.SUNDAY) {
if (dayOfMonth == 1) {
if (month == 0) {
resultWeekOfMonth = getLastWeekOfMonth(year - 1, 11);
resultYear = year - 1;
resultMonth = 12;
} else {
resultWeekOfMonth = getLastWeekOfMonth(year, month - 1);
resultMonth = month;
}
} else {
resultWeekOfMonth = weekOfMonth - 1;
}
} else {
resultWeekOfMonth = weekOfMonth;
}
} else if (monthDayOfWeek == Calendar.MONDAY) {
if (dayOfWeek == Calendar.SUNDAY) {
resultWeekOfMonth = weekOfMonth - 1;
} else {
resultWeekOfMonth = weekOfMonth;
}
} else {
if (dayOfWeek == Calendar.SUNDAY) {
resultWeekOfMonth = weekOfMonth - 2;
} else {
resultWeekOfMonth = weekOfMonth - 1;
}
if (resultWeekOfMonth == 0) {
if (month == 0) {
resultWeekOfMonth = getLastWeekOfMonth(year - 1, 11);
resultYear = year - 1;
resultMonth = 12;
} else {
resultWeekOfMonth = getLastWeekOfMonth(year, month - 1);
resultMonth = month;
}
}
}
resultMap.put("year", resultYear);
resultMap.put("month", resultMonth);
resultMap.put("weekOfMonth", resultWeekOfMonth);
} catch (ParseException e) {
e.printStackTrace();
}
return resultMap;
}
/**
* 按月度获取当月最后一周是第几周
*
* @param year
* @param month
* @return
*/
private static int getLastWeekOfMonth(int year, int month) {
int result;
//当月1号
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month);
calendar.set(Calendar.DAY_OF_MONTH, 1);
//当月最后一天
Calendar lastDayOfMonth = Calendar.getInstance();
lastDayOfMonth.set(Calendar.YEAR, year);
lastDayOfMonth.set(Calendar.MONTH, month);
lastDayOfMonth.set(Calendar.DAY_OF_MONTH, 1);
lastDayOfMonth.roll(Calendar.DAY_OF_MONTH, -1);
//
int lastWeekOfMonth = lastDayOfMonth.get(Calendar.WEEK_OF_MONTH);
//
int lastDayOfWeek = lastDayOfMonth.get(Calendar.DAY_OF_WEEK);
//当月1号是周几
int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
//如果1号是周日或周一,那么这个月的所有除了周日的日期用Calendar获取的周数与需要的周数是一致的;
if (dayOfWeek == Calendar.SUNDAY || dayOfWeek == Calendar.MONDAY) {
//如果最后一天是周日需要的周数要-1
if (lastDayOfWeek == Calendar.SUNDAY) {
result = lastWeekOfMonth - 1;
} else {
result = lastWeekOfMonth;
}
} else {//1号是周二,三,四,五,六的情况下,这个月的所有除了周日的日期用Calendar获取的周数-1后与需要的周数是一致的;
//是周日的日期需要-2
if (lastDayOfWeek == Calendar.SUNDAY) {
result = lastWeekOfMonth - 2;
} else {
result = lastWeekOfMonth - 1;
}
}
return result;
}
public static String getWeekBeginTime() {
Date date = new Date();
Calendar cal = Calendar.getInstance();
cal.setTime(date);
int dayofweek = cal.get(Calendar.DAY_OF_WEEK);
if (dayofweek == 1) {
dayofweek += 7;
}
cal.add(Calendar.DATE, 2 - dayofweek);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd 00:00:00");
return sdf.format(cal.getTime());
}
/**
* 获取本周的结束时间
*
* @return
*/
public static String getWeekEndTime() {
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
int dayofweek = cal.get(Calendar.DAY_OF_WEEK);
if (dayofweek == 1) {
dayofweek += 7;
}
cal.add(Calendar.DATE, 2 - dayofweek);
cal.setTime(cal.getTime());
cal.add(Calendar.DAY_OF_WEEK, 6);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd 23:59:59");
return sdf.format(cal.getTime());
}
public static String getSomeHoursAgo(int hours) {
Date date = new Date(System.currentTimeMillis() - hours * 3600 * 1000L);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(date);
}
public static String getSomeHoursLater(int hours) {
Date date = new Date(System.currentTimeMillis() + hours * 3600 * 1000L);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(date);
}
public static String getSomeDaysAgo(int days) {
Date date = new Date(System.currentTimeMillis() - days * 86400 * 1000L);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(date);
}
public static String getSomeDaysLater(int days) {
Date date = new Date(System.currentTimeMillis() + days * 86400 * 1000L);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(date);
}
public static long getHourLeftMilliseconds() {
Date now = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:59:59");
try {
Date end = sdf.parse(sdf2.format(now));
return end.getTime() - now.getTime();
} catch (Exception ex) {
}
return 0;
}
public static long getDayLeftMilliseconds() {
Date now = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String endTime = getTodayEndTime();
try {
Date end = sdf.parse(endTime);
return end.getTime() - now.getTime();
} catch (Exception ex) {
}
return 0;
}
public static long getWeekLeftMilliseconds() {
Date now = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String endTime = getWeekEndTime();
try {
Date end = sdf.parse(endTime);
return end.getTime() - now.getTime();
} catch (Exception ex) {
}
return 0;
}
public static long getMonthLeftMilliseconds() {
Date now = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String endTime = getMonthEndTime();
try {
Date end = sdf.parse(endTime);
return end.getTime() - now.getTime();
} catch (Exception ex) {
}
return 0;
}
public static long getYearLeftMilliseconds() {
Date now = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String endTime = getYearEndTime();
try {
Date end = sdf.parse(endTime);
return end.getTime() - now.getTime();
} catch (Exception ex) {
}
return 0;
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
package com.codvision.commoncore.utils;
import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.AES;
import com.codvision.commoncore.exception.BusinessException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
/**
* 登陆密码解密
*
* @author hxl
*/
public class DecPwdUtil {
/**
* AES数据块长度为128位IV偏移量长度需要为16个字符ECB模式不用IV
* 密钥SECRET_KEY根据指定密钥位数分别为16、24、32个字符IV与密钥超过长度则截取不足则在末尾填充'\0'补足
*/
private static final String SECRET_KEY = "8237909036518327";
private static final String IV = "4a3f824766747c27";
public static String encryptAes(String password) {
AES aes = new AES(Mode.CBC, Padding.ZeroPadding, new SecretKeySpec(SECRET_KEY.getBytes(), "AES"), new IvParameterSpec(IV.getBytes()));
try {
return Base64.encode(aes.encrypt(password, StandardCharsets.UTF_8));
} catch (Exception e) {
throw new BusinessException(30001, "密码加密失败!");
}
}
public static String decryptAes(String password) {
AES aes = new AES(Mode.CBC, Padding.ZeroPadding, new SecretKeySpec(SECRET_KEY.getBytes(), "AES"), new IvParameterSpec(IV.getBytes()));
try {
byte[] decrypt = aes.decrypt(Base64.decode(password.getBytes(StandardCharsets.UTF_8)));
return new String(decrypt, StandardCharsets.UTF_8);
} catch (Exception e) {
throw new BusinessException(30001, "密码解密失败!");
}
}
public static void main(String[] args) {
System.out.println(encryptAes("Jcpt@2023"));
}
}

View File

@@ -0,0 +1,143 @@
package com.codvision.commoncore.utils;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;
/**
* 加解密工具类
*
* @author codvision
*/
public class Encryption {
private static final String chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
private static final int scale = 62;
/**
* 进行加密
*
* @return
*/
public String encoding(String password) {
String md5 = "";
try {
// 创建一个md5算法对象
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] messageByte = ("Codvision" + password).getBytes(StandardCharsets.UTF_8);
// 获得MD5字节数组,16*8=128位
byte[] md5Byte = md.digest(messageByte);
// 转换为16进制字符串
md5 = bytesToHex(md5Byte);
} catch (Exception e) {
e.printStackTrace();
}
return md5;
}
/**
* 二进制转十六进制
*
* @param bytes
* @return
*/
public static String bytesToHex(byte[] bytes) {
StringBuffer hexStr = new StringBuffer();
int num;
for (int i = 0; i < bytes.length; i++) {
num = bytes[i];
if (num < 0) {
num += 256;
}
if (num < 16) {
hexStr.append("0");
}
hexStr.append(Integer.toHexString(num));
}
return hexStr.toString().toUpperCase();
}
public static String getSign(String jsTicket, String nonceStr, long timeStamp, String url) throws Exception {
String planTex = "jsapi_ticket=" + jsTicket + "&noncestr=" + nonceStr + "&timestamp=" + timeStamp + "&url=" + url;
MessageDigest crypt = null;
try {
crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(planTex.getBytes(StandardCharsets.UTF_8));
return byteToHex(crypt.digest());
} catch (NoSuchAlgorithmException e) {
throw new Exception(e.getMessage());
}
}
/**
* 将bytes类型的数据转化为16进制类型
*
* @param digest
* @return
*/
private static String byteToHex(byte[] digest) {
Formatter formatter = new Formatter();
for (byte b : digest) {
formatter.format("%02x", Byte.valueOf(b));
}
String result = formatter.toString();
formatter.close();
return result;
}
/**
* 10进制转62进制
*
* @param num
* @return
*/
public static String encode(int num) {
java.util.Random random = new java.util.Random();
StringBuffer sbf = new StringBuffer();
for (int i = 0; i < 4; i++) {
int a = random.nextInt(10);
sbf.append(a);
}
StringBuilder sb = new StringBuilder();
int remainder = 0;
while (num > scale - 1) {
/**
* 对 scale 进行求余,然后将余数追加至 sb 中由于是从末位开始追加的因此最后需要反转reverse字符串
*/
remainder = Long.valueOf(num % scale).intValue();
sb.append(chars.charAt(remainder));
num = num / scale;
}
sb.append(chars.charAt(Long.valueOf(num).intValue()));
String value = sb.reverse().toString() + sbf;
return value;
}
public static int decode(String str) {
str = str.substring(0, str.length() - 4);
int num = 0;
int index = 0;
for (int i = 0; i < str.length(); i++) {
/**
* 查找字符的索引位置
*/
index = chars.indexOf(str.charAt(i));
/**
* 索引位置代表字符的数值
*/
num += (long) (index * (Math.pow(scale, str.length() - i - 1)));
}
return num;
}
}

View File

@@ -0,0 +1,346 @@
package com.codvision.commoncore.utils;
import cn.hutool.core.collection.CollectionUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import javax.net.ssl.*;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.URI;
import java.net.UnknownHostException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
/**
* HttpClientUtils
*
* @author Bulbasaur
* @since 2021/7/9 1:40 上午
*/
@Slf4j
public class HttpClientUtils {
/**
* org.apache.http.impl.client.CloseableHttpClient
*/
private static CloseableHttpClient httpClient = null;
private static CloseableHttpClient httpsClient = null;
//这里就直接默认固定了,因为以下三个参数在新建的method中仍然可以重新配置并被覆盖.
/**
* ms毫秒,从池中获取链接超时时间
*/
static final int CONNECTION_REQUEST_TIMEOUT = 30000;
/**
* ms毫秒,建立链接超时时间
*/
static final int CONNECT_TIMEOUT = 60000;
/**
* ms毫秒,读取超时时间
*/
static final int SOCKET_TIMEOUT = 60000;
/**
* 总配置,主要涉及是以下两个参数,如果要作调整没有用到properties会比较后麻烦,但鉴于一经粘贴,随处可用的特点,就不再做依赖性配置化处理了.
* 而且这个参数同一家公司基本不会变动.
* 最大总并发,很重要的参数
*/
static final int MAX_TOTAL = 500;
/**
* 每路并发,很重要的参数
*/
static final int MAX_PER_ROUTE = 100;
/**
* 正常情况这里应该配成MAP或LIST
* 细化配置参数,用来对每路参数做精细化处理,可以管控各ip的流量,比如默认配置请求baidu:80端口最大100个并发链接,
* 每个细化配置之ip(不重要,在特殊场景很有用)
*/
static final String DETAIL_HOST_NAME = "http://www.baidu.com";
/**
* 每个细化配置之port(不重要,在特殊场景很有用)
*/
static final int DETAIL_PORT = 80;
/**
* 每个细化配置之最大并发数(不重要,在特殊场景很有用)
*/
static final int DETAIL_MAX_PER_ROUTE = 100;
private synchronized static CloseableHttpClient getHttpClient() {
if (null == httpClient) {
httpClient = initHttp();
}
return httpClient;
}
private synchronized static CloseableHttpClient getHttpsClient() {
if (null == httpsClient) {
httpsClient = initHttps();
}
return httpsClient;
}
//绕过证书
public static CloseableHttpClient initHttps() {
try {
SSLContext ctx = SSLContext.getInstance("TLS");
X509TrustManager tm = new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(X509Certificate[] arg0,
String arg1) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] arg0,
String arg1) throws CertificateException {
}
};
ctx.init(null, new TrustManager[]{tm}, null);
SSLConnectionSocketFactory ssf = new SSLConnectionSocketFactory(
ctx, NoopHostnameVerifier.INSTANCE);
httpsClient = HttpClients.custom()
.setSSLSocketFactory(ssf).build();
return httpsClient;
} catch (Exception e) {
return HttpClients.createDefault();
}
}
/**
* 链接池初始化 这里最重要的一点理解就是. 让CloseableHttpClient 一直活在池的世界里, 但是HttpPost却一直用完就消掉.
* 这样可以让链接一直保持着.
*/
private static CloseableHttpClient initHttp() {
CloseableHttpClient newHotpoint;
//设置连接池
ConnectionSocketFactory plainsf = PlainConnectionSocketFactory.getSocketFactory();
LayeredConnectionSocketFactory sslsf = SSLConnectionSocketFactory.getSocketFactory();
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create().register("http", plainsf).register("https", sslsf).build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
//将最大连接数增加
cm.setMaxTotal(MAX_TOTAL);
//将每个路由基础的连接增加
cm.setDefaultMaxPerRoute(MAX_PER_ROUTE);
//细化配置开始,其实这里用Map或List的for循环来配置每个链接,在特殊场景很有用.
//将每个路由基础的连接做特殊化配置,一般用不着
HttpHost httpHost = new HttpHost(DETAIL_HOST_NAME, DETAIL_PORT);
//将目标主机的最大连接数增加
cm.setMaxPerRoute(new HttpRoute(httpHost), DETAIL_MAX_PER_ROUTE);
//细化配置结束
//请求重试处理
HttpRequestRetryHandler httpRequestRetryHandler = (exception, executionCount, context) -> {
if (executionCount >= 2) {//如果已经重试了2次就放弃
return false;
}
if (exception instanceof NoHttpResponseException) {//如果服务器丢掉了连接,那么就重试
return true;
}
if (exception instanceof SSLHandshakeException) {//不要重试SSL握手异常
return false;
}
if (exception instanceof InterruptedIOException) {//超时
return false;
}
if (exception instanceof UnknownHostException) {//目标服务器不可达
return false;
}
if (exception instanceof SSLException) {//SSL握手异常
return false;
}
HttpClientContext clientContext = HttpClientContext.adapt(context);
HttpRequest request = clientContext.getRequest();
//如果请求是幂等的,就再次尝试
return !(request instanceof HttpEntityEnclosingRequest);
};
//配置请求的超时设置
RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT).setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();
newHotpoint = HttpClients.custom().setConnectionManager(cm).setDefaultRequestConfig(requestConfig).setRetryHandler(httpRequestRetryHandler).build();
return newHotpoint;
}
public static String doGet(String url, Map<String, Object> param) {
//httpClient
CloseableHttpClient httpClient;
if (url.startsWith("https://")) {
httpClient = getHttpsClient();
} else {
httpClient = getHttpClient();
}
String resultString = "";
CloseableHttpResponse response = null;
try {
//创建uri
URIBuilder builder = new URIBuilder(url);
if (param != null) {
for (String key : param.keySet()) {
builder.addParameter(key, (String) param.get(key));
}
}
URI uri = builder.build();
//创建http GET请求
HttpGet httpGet = new HttpGet(uri);
log.info(httpGet.toString());
//执行请求
response = httpClient.execute(httpGet);
//判断返回状态是否为200
if (response.getStatusLine().getStatusCode() == 200) {
resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
log.info(resultString);
} else {
log.error(url + "请求失败:" + response);
}
} catch (Exception e) {
log.error(url + "get请求错误", e);
throw new RuntimeException(e.getMessage());
} finally {
try {
if (response != null) {
response.close();
}
} catch (IOException e) {
log.error("Get错误", e);
throw new RuntimeException(e.getMessage());
}
}
return resultString;
}
public static String doPost(String url, Map<String, String> headers, String body) {
//httpClient
CloseableHttpClient httpClient;
if (url.startsWith("https://")) {
httpClient = getHttpsClient();
} else {
httpClient = getHttpClient();
}
String resultString = "";
CloseableHttpResponse response = null;
try {
//创建http GET请求
HttpPost httpPost = new HttpPost(url);
log.info(httpPost.toString());
httpPost.addHeader("Content-Type", "application/json;charset=utf-8");
if (CollectionUtil.isNotEmpty(headers)) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
httpPost.addHeader(entry.getKey(), entry.getValue());
}
}
StringEntity stringEntity = new StringEntity(body, "UTF-8");
httpPost.setEntity(stringEntity);
//执行请求
response = httpClient.execute(httpPost);
//判断返回状态是否为200
if (response.getStatusLine().getStatusCode() == 200) {
resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
log.info(resultString);
} else {
log.error(url + "请求失败:" + response);
}
} catch (Exception e) {
log.error(url + "post请求错误", e);
throw new RuntimeException(e.getMessage());
} finally {
try {
if (response != null) {
response.close();
}
} catch (IOException e) {
log.error("Post错误", e);
throw new RuntimeException(e.getMessage());
}
}
return resultString;
}
public static String doPost(String url, Map<String, String> headers, Map<String, String> params) {
//httpClient
CloseableHttpClient httpClient;
if (url.startsWith("https://")) {
httpClient = getHttpsClient();
} else {
httpClient = getHttpClient();
}
String resultString = "";
CloseableHttpResponse response = null;
try {
//创建uri
URIBuilder builder = new URIBuilder(url);
if (params != null) {
for (String key : params.keySet()) {
builder.addParameter(key, (String) params.get(key));
}
}
URI uri = builder.build();
HttpPost httpPost = new HttpPost(uri);
log.info(httpPost.toString());
if (CollectionUtil.isNotEmpty(headers)) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
httpPost.addHeader(entry.getKey(), entry.getValue());
}
}
//执行请求
response = httpClient.execute(httpPost);
//判断返回状态是否为200
if (response.getStatusLine().getStatusCode() == 200) {
resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
log.info(resultString);
} else {
log.error(url + "请求失败:" + response);
}
} catch (Exception e) {
log.error(url + "post请求错误", e);
throw new RuntimeException(e.getMessage());
} finally {
try {
if (response != null) {
response.close();
}
} catch (IOException e) {
log.error("Post错误", e);
throw new RuntimeException(e.getMessage());
}
}
return resultString;
}
}

View File

@@ -0,0 +1,339 @@
package com.codvision.commoncore.utils;
import org.apache.commons.lang3.StringUtils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 身份证号码的格式610821-20061222-612-X 由18位数字组成前6位为地址码第7至14位为出生日期码第15至17位为顺序码
* 第18位为校验码。检验码分别是0-10共11个数字当检验码为“10”时为了保证公民身份证号码18位所以用“X”表示。虽然校验码为“X”不能更换但若需全用数字表示只需将18位公民身份号码转换成15位居民身份证号码去掉第7至8位和最后1位3个数码。
* 当今的身份证号码有15位和18位之分。1985年我国实行居民身份证制度当时签发的身份证号码是15位的1999年签发的身份证由于年份的扩展由两位变为四位和末尾加了效验码就成了18位。
* 1前1、2位数字表示所在省份的代码
* 2第3、4位数字表示所在城市的代码
* 3第5、6位数字表示所在区县的代码
* 4第7~14位数字表示出生年、月、日
* 5第15、16位数字表示所在地的派出所的代码
* 6第17位数字表示性别奇数表示男性偶数表示女性
* 7第18位数字是校检码根据一定算法生成
*
* @author codvision
*/
public class IdCardUtil {
/**
* 身份证有效
*/
public static final String VALIDITY = "该身份证有效!";
/**
* 位数不足
*/
public static final String LACKDIGITS = "身份证号码长度应该为15位或18位。请检查是否有多余的空格";
/**
* 最后一位应为数字
*/
public static final String LASTOFNUMBER = "身份证15位号码都应为数字 ; 18位号码除最后一位外都应为数字。";
/**
* 出生日期无效
*/
public static final String INVALIDBIRTH = "身份证出生日期无效。";
/**
* 生日不在有效范围
*/
public static final String INVALIDSCOPE = "身份证生日不在有效范围。";
/**
* 月份无效
*/
public static final String INVALIDMONTH = "身份证月份无效";
/**
* 日期无效
*/
public static final String INVALIDDAY = "身份证日期无效";
/**
* 身份证地区编码错误
*/
public static final String CODINGERROR = "身份证地区编码错误。";
/**
* 身份证校验码无效
*/
public static final String INVALIDCALIBRATION = "身份证校验码无效,不是合法的身份证号码";
private static final Pattern pattern = Pattern.compile("[0-9]*");
private static final Pattern idCardPattern = Pattern.compile("^((\\d{2}(([02468][048])|([13579][26]))[\\-\\/\\s]?((((0?[13578])|(1[02]))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])))))|(\\d{2}(([02468][1235679])|([13579][01345789]))[\\-\\/\\s]?((((0?[13578])|(1[02]))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\\-\\/\\s]?((0?[1-9])|(1[0-9])|(2[0-8]))))))?$");
public static Integer getCardSex(String cardCode) {
int sex = 0;
if (StringUtils.isNotEmpty(cardCode)) {
if (cardCode.length() == 18) {
if (Integer.parseInt(cardCode.substring(16).substring(0, 1)) % 2 == 0) {
sex = 2;
} else {
sex = 1;
}
} else if (cardCode.length() == 15) {
if (Integer.parseInt(cardCode.substring(14, 15)) % 2 == 0) {
sex = 2;
} else {
sex = 1;
}
}
}
return sex;
}
/**
* 检验身份证号码是否符合规范
*
* @param idStr 身份证号码
* @return 错误信息或成功信息
*/
public static String cardCodeVerifySimple(String idStr) {
//记录错误信息
String tipInfo = VALIDITY;
String ai = "";
idStr = idStr.trim();
// 判断号码的长度 15位或18位
if (idStr.length() != 15 && idStr.length() != 18) {
tipInfo = LACKDIGITS;
return tipInfo;
}
// 18位身份证前17位位数字如果是15位的身份证则所有号码都为数字
if (idStr.length() == 18) {
ai = idStr.substring(0, 17);
} else if (idStr.length() == 15) {
ai = idStr.substring(0, 6) + "19" + idStr.substring(6, 15);
}
if (isNumeric(ai) == false) {
tipInfo = LASTOFNUMBER;
return tipInfo;
}
// 判断出生年月是否有效
//年份
String strYear = ai.substring(6, 10);
// 月份
String strMonth = ai.substring(10, 12);
// 日期
String strDay = ai.substring(12, 14);
if (isDate(strYear + "-" + strMonth + "-" + strDay) == false) {
tipInfo = INVALIDBIRTH;
return tipInfo;
}
GregorianCalendar gc = new GregorianCalendar();
SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd");
try {
if ((gc.get(Calendar.YEAR) - Integer.parseInt(strYear)) > 150
|| (gc.getTime().getTime() - s.parse(strYear + "-" + strMonth + "-" + strDay).getTime()) < 0) {
tipInfo = INVALIDSCOPE;
return tipInfo;
}
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
}
if (Integer.parseInt(strMonth) > 12 || Integer.parseInt(strMonth) == 0) {
tipInfo = INVALIDMONTH;
return tipInfo;
}
if (Integer.parseInt(strDay) > 31 || Integer.parseInt(strDay) == 0) {
tipInfo = INVALIDDAY;
return tipInfo;
}
// 判断地区码是否有效
Hashtable<String, String> areacode = getAreaCode();
// 如果身份证前两位的地区码不在Hashtable则地区码有误
if (areacode.get(ai.substring(0, 2)) == null) {
tipInfo = CODINGERROR;
return tipInfo;
}
if (isVarifyCode(ai, idStr) == false) {
tipInfo = INVALIDCALIBRATION;
return tipInfo;
}
return tipInfo;
}
/**
* 根据身份证的号码算出当前身份证持有者的性别和年龄 18位身份证
*
* @return
* @throws Exception
*/
public static Map<String, Object> getCarInfo18W(String cardCode)
throws Exception {
Map<String, Object> map = new HashMap<String, Object>(16);
// 得到年份
String year = cardCode.substring(6).substring(0, 4);
// 得到月份
String yue = cardCode.substring(10).substring(0, 2);
String sex;
// 判断性别
if (Integer.parseInt(cardCode.substring(16).substring(0, 1)) % 2 == 0) {
sex = "";
} else {
sex = "";
}
// 得到当前的系统时间
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
// 当前年份
String fyear = format.format(date).substring(0, 4);
// 月份
String fyue = format.format(date).substring(5, 7);
int age = 0;
// 当前月份大于用户出身的月份表示已过生
if (Integer.parseInt(yue) <= Integer.parseInt(fyue)) {
age = Integer.parseInt(fyear) - Integer.parseInt(year) + 1;
// 当前用户还没过生
} else {
age = Integer.parseInt(fyear) - Integer.parseInt(year);
}
map.put("sex", sex);
map.put("age", age);
return map;
}
/**
* 15位身份证的验证
*
* @param
* @throws Exception
*/
public static Map<String, Object> getCarInfo15W(String card)
throws Exception {
Map<String, Object> map = new HashMap<String, Object>(16);
// 年份
String uyear = "19" + card.substring(6, 8);
// 月份
String uyue = card.substring(8, 10);
// 用户的性别
String usex = card.substring(14, 15);
String sex;
if (Integer.parseInt(usex) % 2 == 0) {
sex = "";
} else {
sex = "";
}
// 得到当前的系统时间
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
// 当前年份
String fyear = format.format(date).substring(0, 4);
// 月份
String fyue = format.format(date).substring(5, 7);
int age = 0;
// 当前月份大于用户出身的月份表示已过生
if (Integer.parseInt(uyue) <= Integer.parseInt(fyue)) {
age = Integer.parseInt(fyear) - Integer.parseInt(uyear) + 1;
// 当前用户还没过生
} else {
age = Integer.parseInt(fyear) - Integer.parseInt(uyear);
}
map.put("sex", sex);
map.put("age", age);
return map;
}
/**
* 判断第18位校验码是否正确 第18位校验码的计算方式
* 1. 对前17位数字本体码加权求和 公式为S = Sum(Ai * Wi), i =
* 0, ... , 16 其中Ai表示第i个位置上的身份证号码数字值Wi表示第i位置上的加权因子其各位对应的值依次为 7 9 10 5 8 4
* 2 1 6 3 7 9 10 5 8 4 2
* 2. 用11对计算结果取模 Y = mod(S, 11)
* 3. 根据模的值得到对应的校验码
* 对应关系为: Y值 0 1 2 3 4 5 6 7 8 9 10 校验码: 1 0 X 9 8 7 6 5 4 3 2
*/
private static boolean isVarifyCode(String ai, String idStr) {
String[] varifyCode = {"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"};
String[] wi = {"7", "9", "10", "5", "8", "4", "2", "1", "6", "3", "7", "9", "10", "5", "8", "4", "2"};
int sum = 0;
for (int i = 0; i < 17; i++) {
sum = sum + Integer.parseInt(String.valueOf(ai.charAt(i))) * Integer.parseInt(wi[i]);
}
int modValue = sum % 11;
String strVerifyCode = varifyCode[modValue];
ai = ai + strVerifyCode;
if (idStr.length() == 18) {
return ai.equals(idStr) != false;
}
return true;
}
/**
* 将所有地址编码保存在一个Hashtable中
*
* @return Hashtable 对象
*/
private static Hashtable<String, String> getAreaCode() {
Hashtable<String, String> hashtable = new Hashtable<String, String>();
hashtable.put("11", "北京");
hashtable.put("12", "天津");
hashtable.put("13", "河北");
hashtable.put("14", "山西");
hashtable.put("15", "内蒙古");
hashtable.put("21", "辽宁");
hashtable.put("22", "吉林");
hashtable.put("23", "黑龙江");
hashtable.put("31", "上海");
hashtable.put("32", "江苏");
hashtable.put("33", "浙江");
hashtable.put("34", "安徽");
hashtable.put("35", "福建");
hashtable.put("36", "江西");
hashtable.put("37", "山东");
hashtable.put("41", "河南");
hashtable.put("42", "湖北");
hashtable.put("43", "湖南");
hashtable.put("44", "广东");
hashtable.put("45", "广西");
hashtable.put("46", "海南");
hashtable.put("50", "重庆");
hashtable.put("51", "四川");
hashtable.put("52", "贵州");
hashtable.put("53", "云南");
hashtable.put("54", "西藏");
hashtable.put("61", "陕西");
hashtable.put("62", "甘肃");
hashtable.put("63", "青海");
hashtable.put("64", "宁夏");
hashtable.put("65", "新疆");
hashtable.put("71", "台湾");
hashtable.put("81", "香港");
hashtable.put("82", "澳门");
hashtable.put("91", "国外");
return hashtable;
}
/**
* 判断字符串是否为数字,0-9重复0次或者多次
*
* @param strnum
* @return true, 符合; false, 不符合。
*/
private static boolean isNumeric(String strnum) {
Matcher isNum = pattern.matcher(strnum);
return isNum.matches();
}
/**
* 功能判断字符串出生日期是否符合正则表达式包括年月日闰年、平年和每月31天、30天和闰月的28天或者29天
*
* @param strDate
* @return true, 符合; false, 不符合。
*/
public static boolean isDate(String strDate) {
Matcher m = idCardPattern.matcher(strDate);
return m.matches();
}
}

View File

@@ -0,0 +1,82 @@
package com.codvision.commoncore.utils;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.CryptoException;
import cn.hutool.crypto.KeyUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.SignAlgorithm;
import java.security.*;
/**
* @author hxl
*/
public class LySign {
/**
* 算法
*/
protected String algorithm;
protected String algorithmAfterWith;
/**
* 公钥
*/
protected PublicKey publicKey;
/**
* 私钥
*/
protected PrivateKey privateKey;
/**
* 签名,用于签名和验证
*/
protected Signature signature;
public LySign(SignAlgorithm algorithmE, byte[] privateKey, byte[] publicKey) {
this.algorithm = algorithmE.getValue();
this.algorithmAfterWith = KeyUtil.getAlgorithmAfterWith(this.algorithm);
this.privateKey = SecureUtil.generatePrivateKey(this.algorithmAfterWith, privateKey);
this.publicKey = SecureUtil.generatePublicKey(this.algorithmAfterWith, publicKey);
try {
this.signature = Signature.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
throw new CryptoException(e);
}
}
public static LySign of(SignAlgorithm algorithm, String privateKey, String publicKey, boolean hexStr) {
byte[] privateBytes;
byte[] publicBytes;
if (hexStr) {
privateBytes = HexUtil.decodeHex(privateKey);
publicBytes = HexUtil.decodeHex(publicKey);
} else {
privateBytes = Base64.decode(privateKey);
publicBytes = Base64.decode(publicKey);
}
return new LySign(algorithm, privateBytes, publicBytes);
}
public String sign(String data) {
try {
signature.initSign(privateKey);
signature.update(data.getBytes());
byte[] sign = signature.sign();
return HexUtil.encodeHexStr(sign, false);
} catch (InvalidKeyException | SignatureException e) {
throw new CryptoException(e);
}
}
public boolean verify(String data, String sign) {
try {
signature.initVerify(publicKey);
signature.update(data.getBytes());
return signature.verify(HexUtil.decodeHex(sign));
} catch (InvalidKeyException | SignatureException e) {
throw new CryptoException(e);
}
}
}

View File

@@ -0,0 +1,145 @@
package com.codvision.commoncore.utils;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* @author zjm
* @version V1.0
* @Package com.codvision.serverboot.util
* @date 2020/3/4 10:00
* @Copyright © 2017-2020 杭州码全科技有限公司
*/
public class Md5Utils {
/**
* md5加密
*
* @param message 内容
* @return 摘要
*/
public static String md5Encode(String message) {
String temp = "";
try {
MessageDigest md5Digest = MessageDigest.getInstance("MD5");
byte[] d = md5Digest.digest(message.getBytes());
temp = converByteToHexString(d);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return temp;
}
/**
* 转换byte到16进制
*
* @param b 要转换的byte
* @return 16进制对应的字符
*/
private static String converByteToHexString(byte[] b) {
String result = "";
for (int i = 1; i < b.length; i++) {
int temp = b[i] & 0xff;
String tempHex = Integer.toHexString(temp);
if (tempHex.length() < 2) {
result += "" + tempHex;
} else {
result += tempHex;
}
}
return result;
}
/**
* 获取摘要
*
* @param str 内容
* @return 摘要
*/
public static String getMd5(String str) {
try {
// 生成一个MD5加密计算摘要
MessageDigest md = MessageDigest.getInstance("MD5");
// 计算md5函数
md.update(str.getBytes());
String md5 = new BigInteger(1, md.digest()).toString(16);
return fillMd5(md5);
} catch (Exception e) {
throw new RuntimeException("MD5加密错误:" + e.getMessage(), e);
}
}
/**
* 填充摘要
*
* @param md5 摘要
* @return 填充后的摘要
*/
public static String fillMd5(String md5) {
return md5.length() == 32 ? md5 : fillMd5("0" + md5);
}
/**
* 使用指定哈希算法计算摘要信息
*
* @param content 内容
* @param algorithm 哈希算法
* @return 内容摘要
*/
public static String getMd5Digest(String content, String algorithm) {
try {
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
messageDigest.update(content.getBytes(StandardCharsets.UTF_8));
return bytesToHexString(messageDigest.digest());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
/**
* 将字节数组转换成16进制字符串
*
* @param bytes 即将转换的数据
* @return 16进制字符串
*/
private static String bytesToHexString(byte[] bytes) {
StringBuffer sb = new StringBuffer(bytes.length);
String temp = null;
for (int i = 0; i < bytes.length; i++) {
temp = Integer.toHexString(0xFF & bytes[i]);
if (temp.length() < 2) {
sb.append(0);
}
sb.append(temp);
}
return sb.toString();
}
public static String md532Encode(String s) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] bytes = md.digest(s.getBytes(StandardCharsets.UTF_8));
return toHex(bytes);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static String toHex(byte[] bytes) {
final char[] HEX_DIGITS = "0123456789abcdef".toCharArray();
StringBuilder ret = new StringBuilder(bytes.length * 2);
for (int i = 0; i < bytes.length; i++) {
ret.append(HEX_DIGITS[(bytes[i] >> 4) & 0x0f]);
ret.append(HEX_DIGITS[bytes[i] & 0x0f]);
}
return ret.toString();
}
}

View File

@@ -0,0 +1,54 @@
package com.codvision.commoncore.utils;
import com.codvision.commoncore.enums.NetworkData;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* 网络工具类
* Created by mrhe on 2018/2/17.
*/
public class NetworkUtils {
/**
* 获取请求主机IP地址,如果通过代理进来则透过防火墙获取真实IP地址;
*
* @param request
* @return
* @throws IOException
*/
public static String getIpAddress(HttpServletRequest request) throws IOException {
// 获取请求主机IP地址,如果通过代理进来则透过防火墙获取真实IP地址
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
} else if (ip.length() > 15) {
String[] ips = ip.split(",");
for (int index = 0; index < ips.length; index++) {
String strIp = ips[index];
if (!("unknown".equalsIgnoreCase(strIp))) {
ip = strIp;
break;
}
}
}
return ip.equals(NetworkData.NET_WORK_ID.getDesc()) ? "127.0.0.1" : ip;
}
}

View File

@@ -0,0 +1,46 @@
package com.codvision.commoncore.utils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
* @author zjm
* @version V1.0
* @Package com.codvision.serverboot.util
* @date 2020/3/4 9:48
* @Copyright © 2017-2020 杭州码全科技有限公司
*/
public class ParamUtil {
public static boolean isKeyExist(Map<String, Object> params, String key) {
return isKeyExist(params, key, true);
}
public static boolean isKeyExist(Map<String, Object> params, String key, boolean ignoreEmptyValue) {
if (ignoreEmptyValue) {
return params.containsKey(key) && null != params.get(key) && StringUtils.isNotEmpty(params.get(key).toString());
} else {
return params.containsKey(key) && null != params.get(key);
}
}
public static Map<String, String> objectToMap(Object obj) throws IllegalAccessException {
Map<String, String> map = new HashMap<String, String>(16);
Class<?> clazz = obj.getClass();
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
String fieldName = field.getName();
if (!(ObjectUtils.isEmpty(field.get(obj)))) {
Object value = field.get(obj);
map.put(fieldName, String.valueOf(value));
}
}
return map;
}
}

View File

@@ -0,0 +1,36 @@
package com.codvision.commoncore.utils;
import com.codvision.commoncore.common.ResponseEntity;
import com.codvision.commoncore.exception.BusinessException;
import lombok.NonNull;
/**
* @author hxl
*/
public final class Preconditions {
/**
* @param expression 布尔表达式
* @param code 自定义响应状态码
* @param errorMsg 检查失败时使用的异常消息
* @throws BusinessException if {@code expression} is false
*/
public static void checkArgument(boolean expression, int code, @NonNull Object errorMsg) {
if (!expression) {
throw new BusinessException(code, String.valueOf(errorMsg));
}
}
public static void checkArgument(boolean expression, @NonNull Object errorMsg) {
if (!expression) {
throw new BusinessException(String.valueOf(errorMsg));
}
}
public static void checkArgument(boolean expression, int code) {
if (!expression) {
throw new BusinessException(code, new ResponseEntity<>().setCode(code).getMessage());
}
}
}

View File

@@ -0,0 +1,45 @@
package com.codvision.commoncore.utils;
import java.util.Random;
/**
* @author: fuyuaaaaa
* @description: 随机数 工具 类
* @program: smart-rear
* @creat: 2018-09-21 10:10
**/
public class RandomUtil {
/**
* 获取6位数验证码
*
* @return 验证码
*/
public static String getVerificationCode() {
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 6; i++) {
int a = random.nextInt(10);
sb.append(a);
}
return sb.toString();
}
/**
* 获取指定长度验证码
*
* @param length 长度
* @return 验证码
*/
public static String getRandomStr(int length) {
String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
Random random = new Random();
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < length; i++) {
int number = random.nextInt(26);
stringBuilder.append(str.charAt(number));
}
return stringBuilder.toString();
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
package com.codvision.commoncore.utils;
import com.codvision.commoncore.common.ResponseEntity;
import org.apache.commons.lang3.ObjectUtils;
/**
* 响应工具类
*
* @author lingee
* @date 2023/7/18
*/
public class ResponseUtil {
/**
* 判断请求是否成功
*
* @param response
* @return
*/
public static boolean isSuccess(ResponseEntity response) {
if (ObjectUtils.isNotEmpty(response)
&& response.getCode() == 200
&& ObjectUtils.isNotEmpty(response.getData())) {
return true;
}
return false;
}
public static boolean isSuccessNotData(ResponseEntity response) {
if (ObjectUtils.isNotEmpty(response)
&& response.getCode() == 200) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,49 @@
package com.codvision.commoncore.utils;
import java.util.List;
/**
* 树节点父类
* <p>
* 所有需要使用{@linkplain TreeUtils}工具类形成树形结构等操作的节点都需要实现该接口
*
* @author lingee
* @date 2024/04/24
*/
public interface TreeNode<T> {
/**
* 获取节点id
*
* @return 树节点id
*/
T id();
/**
* 获取该节点的父节点id
*
* @return 父节点id
*/
T parentId();
/**
* 是否是根节点
*
* @return true根节点
*/
boolean root();
/**
* 设置节点的子节点列表
*
* @param children 子节点
*/
void setChildren(List<? extends TreeNode<T>> children);
/**
* 获取所有子节点
*
* @return 子节点列表
*/
List<? extends TreeNode<T>> getChildren();
}

View File

@@ -0,0 +1,117 @@
package com.codvision.commoncore.utils;
import org.apache.commons.collections4.CollectionUtils;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
/**
* 树形结构工具类
*
* @author lingee
* @date 2024/04/24
*/
public class TreeUtils {
/**
* 根据所有树节点列表,生成含有所有树形结构的列表
*
* @param nodes 树形节点列表
* @param <T> 节点类型
* @return 树形结构列表
*/
public static <T extends TreeNode<?>> List<T> generateTrees(List<T> nodes) {
List<T> roots = new ArrayList<>();
for (Iterator<T> ite = nodes.iterator(); ite.hasNext(); ) {
T node = ite.next();
if (node.root()) {
roots.add(node);
// 从所有节点列表中删除该节点,以免后续重复遍历该节点
ite.remove();
}
}
if (CollectionUtils.isEmpty(roots)) {
roots.addAll(nodes);
}
roots.forEach(r -> {
setChildren(r, nodes);
});
return roots;
}
/**
* 从所有节点列表中查找并设置parent的所有子节点
*
* @param parent 父节点
* @param nodes 所有树节点列表
*/
public static <T extends TreeNode> void setChildren(T parent, List<T> nodes) {
List<T> children = new ArrayList<>();
Object parentId = parent.id();
for (Iterator<T> ite = nodes.iterator(); ite.hasNext(); ) {
T node = ite.next();
if (Objects.equals(node.parentId(), parentId)) {
children.add(node);
// 从所有节点列表中删除该节点,以免后续重复遍历该节点
ite.remove();
}
}
// 如果孩子为空,则直接返回,否则继续递归设置孩子的孩子
if (children.isEmpty()) {
return;
}
parent.setChildren(children);
children.forEach(m -> {
// 递归设置子节点
setChildren(m, nodes);
});
}
/**
* 移除父节点下的所有子节点
*
* @param parentId 父级id
* @param nodes 节点列表
* @param <T>
* @return
*/
public static <T extends TreeNode> List<Integer> removeChildren(Integer parentId, List<T> nodes) {
List<Integer> removeIds = new ArrayList<>();
for (Iterator<T> ite = nodes.iterator(); ite.hasNext(); ) {
T node = ite.next();
if (Objects.equals(node.parentId(), parentId)) {
removeIds.add((Integer) node.id());
ite.remove();
}
}
return removeIds;
}
/**
* 将parent的所有叶子节点填充至leafs列表中
*
* @param parent 父节点
* @param leafs 叶子节点列表
* @param <T> 实际节点类型
*/
public static <T extends TreeNode> void fillLeaf(T parent, List<T> leafs) {
List<T> children = parent.getChildren();
// 如果节点没有子节点则说明为叶子节点
if (CollectionUtils.isEmpty(children)) {
leafs.add(parent);
return;
}
// 递归调用子节点,查找叶子节点
for (T child : children) {
fillLeaf(child, leafs);
}
}
}

View File

@@ -0,0 +1,38 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2023 codvision.com All Rights Reserved.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.codvision</groupId>
<artifactId>cvbp-public</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cvbp-common-feign</artifactId>
<packaging>jar</packaging>
<version>1.0.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--feign 依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
package com.codvision.commonfeign.config;
import feign.Logger;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
/**
* openfeign配置
*
* @author lingee
* @date 2023/7/17
*/
@Configuration
@EnableFeignClients(basePackages = {"com.codvision"})
public class OpenFeignConfig {
/**
* 测试环境下,打印全部请求信息
* <p>
* 更多日志配置参见 feign.Logger.Level
*
* @return
*/
@Bean
@Profile("dev")
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}

View File

@@ -0,0 +1,11 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
/**
* openfeign远程调用
*
* @author lingee
* @date 2023/7/17
*/
package com.codvision.commonfeign;

View File

@@ -0,0 +1 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.codvision.commonfeign.config.OpenFeignConfig

View File

@@ -0,0 +1,38 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2023 codvision.com All Rights Reserved.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.codvision</groupId>
<artifactId>cvbp-public</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cvbp-db-core</artifactId>
<packaging>jar</packaging>
<version>1.0.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.codvision</groupId>
<artifactId>cvbp-common-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,15 @@
package com.codvision.db;
import com.codvision.db.constants.DbQueryProperty;
public interface DataSourceFactory {
/**
* 创建数据源实例
*
* @param property
* @return
*/
DbQuery createDbQuery(DbQueryProperty property);
}

View File

@@ -0,0 +1,83 @@
package com.codvision.db;
import com.codvision.db.core.DbColumn;
import com.codvision.db.core.DbTable;
import org.springframework.jdbc.core.RowMapper;
/**
* 表数据查询接口
*
* @author yuwei
* @since 2020-03-14
*/
public interface DbDialect {
RowMapper<DbTable> tableMapper();
RowMapper<DbColumn> columnMapper();
RowMapper<String> viewMapper();
/**
* 获取指定表的所有列
*
* @param dbName
* @param tableName
* @return
*/
String columns(String dbName, String tableName);
/**
* 获取数据库下的 所有表
*
* @param dbName
* @return
*/
String tables(String dbName);
/**
* 获取表结构
*
* @param dbName
* @param tableName
* @return
*/
String table(String dbName, String tableName);
/**
* 获取数据库下的 所有视图
*
* @param dbName
* @return
*/
String views(String dbName);
/**
* 构建 分页 sql
*
* @param sql
* @param offset
* @param count
* @return
*/
String buildPaginationSql(String sql, long offset, long count);
/**
* 包装 count sql
*
* @param sql
* @return
*/
String count(String sql);
/**
* oracl 读取long 类型会流关闭是oracle的bug需要特殊处理
*
* @return
*/
default RowMapper<DbColumn> columnLongMapper() {
return null;
}
;
}

View File

@@ -0,0 +1,148 @@
package com.codvision.db;
import com.codvision.db.core.DbColumn;
import com.codvision.db.core.DbTable;
import com.codvision.db.core.PageResult;
import java.sql.Connection;
import java.util.List;
import java.util.Map;
/**
* 表数据查询接口
*
* @author yuwei
* @since 2020-03-14
*/
public interface DbQuery {
/**
* 获取数据库连接
*/
Connection getConnection();
/**
* 检测连通性
*/
boolean valid();
/**
* 关闭数据源
*/
void close();
/**
* 获取指定表 具有的所有字段列表
*
* @param dbName
* @param tableName
* @return
*/
List<DbColumn> getTableColumns(String dbName, String tableName);
/**
* 获取指定数据库下 所有的表信息
*
* @param dbName
* @return
*/
List<DbTable> getTables(String dbName);
/**
* 获取指定数据库下 指定表信息
*
* @param dbName
* @param tableName
* @return
*/
DbTable getTable(String dbName, String tableName);
/**
* 获取数据库下的所有schemas
*
* @return
*/
List<String> getSchemas();
/**
* 获取数据库下的所有视图
*
* @param dbName 数据库名称
* @return
*/
List<String> getViews(String dbName);
/**
* 获取总数
*
* @param sql
* @return
*/
int count(String sql);
/**
* 获取总数带查询参数
*
* @param sql
* @return
*/
int count(String sql, Object[] args);
/**
* 获取总数带查询参数 NamedParameterJdbcTemplate
*
* @param sql
* @return
*/
int count(String sql, Map<String, Object> params);
/**
* 查询结果列表
*
* @param sql
* @return
*/
List<Map<String, Object>> queryList(String sql);
/**
* 查询结果列表带查询参数
*
* @param sql
* @param args
* @return
*/
List<Map<String, Object>> queryList(String sql, Object[] args);
/**
* 查询结果分页
*
* @param sql
* @param offset
* @param size
* @return
*/
PageResult<Map<String, Object>> queryByPage(String sql, long offset, long size);
/**
* 查询结果分页带查询参数
*
* @param sql
* @param args
* @param offset
* @param size
* @return
*/
PageResult<Map<String, Object>> queryByPage(String sql, Object[] args, long offset, long size);
/**
* 查询结果分页带查询参数 NamedParameterJdbcTemplate
*
* @param sql
* @param params
* @param offset
* @param size
* @return
*/
PageResult<Map<String, Object>> queryByPage(String sql, Map<String, Object> params, long offset, long size);
}

View File

@@ -0,0 +1,20 @@
package com.codvision.db;
import com.codvision.db.constants.DbType;
import com.codvision.db.dialect.DialectRegistry;
/**
* 方言工厂类
*
* @author yuwei
* @since 2020-03-14
*/
public class DialectFactory {
private static final DialectRegistry DIALECT_REGISTRY = new DialectRegistry();
public static DbDialect getDialect(DbType dbType) {
return DIALECT_REGISTRY.getDialect(dbType);
}
}

View File

@@ -0,0 +1,123 @@
package com.codvision.db.cache;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class DefaultSqlCache extends LinkedHashMap<String, DefaultSqlCache.ExpireNode<Object>> implements SqlCache {
private int capacity;
private long expire;
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public DefaultSqlCache(int capacity, long expire) {
super((int) Math.ceil(capacity / 0.75) + 1, 0.75f, true);
// 容量
this.capacity = capacity;
// 固定过期时间
this.expire = expire;
}
@Override
public void put(String key, Object value, long ttl) {
long expireTime = Long.MAX_VALUE;
if (ttl >= 0) {
expireTime = System.currentTimeMillis() + (ttl == 0 ? this.expire : ttl);
}
lock.writeLock().lock();
try {
// 封装成过期时间节点
put(key, new ExpireNode<>(expireTime, value));
} finally {
lock.writeLock().unlock();
}
}
@Override
public Object get(String key) {
lock.readLock().lock();
ExpireNode<Object> expireNode;
try {
expireNode = super.get(key);
} finally {
lock.readLock().unlock();
}
if (expireNode == null) {
return null;
}
// 惰性删除过期的
if (this.expire > -1L && expireNode.expire < System.currentTimeMillis()) {
try {
lock.writeLock().lock();
super.remove(key);
} finally {
lock.writeLock().unlock();
}
return null;
}
return expireNode.value;
}
@Override
public void delete(String key) {
try {
lock.writeLock().lock();
Iterator<Map.Entry<String, ExpireNode<Object>>> iterator = super.entrySet().iterator();
// 清除key的缓存
while (iterator.hasNext()) {
Map.Entry<String, ExpireNode<Object>> entry = iterator.next();
if (entry.getKey().equals(key)) {
iterator.remove();
}
}
} finally {
lock.writeLock().unlock();
}
}
@Override
protected boolean removeEldestEntry(Map.Entry<String, ExpireNode<Object>> eldest) {
if (this.expire > -1L && size() > capacity) {
clean();
}
// lru淘汰
return size() > this.capacity;
}
/**
* 清理已过期的数据
*/
private void clean() {
try {
lock.writeLock().lock();
Iterator<Map.Entry<String, ExpireNode<Object>>> iterator = super.entrySet().iterator();
long now = System.currentTimeMillis();
while (iterator.hasNext()) {
Map.Entry<String, ExpireNode<Object>> next = iterator.next();
// 判断是否过期
if (next.getValue().expire < now) {
iterator.remove();
}
}
} finally {
lock.writeLock().unlock();
}
}
/**
* 过期时间节点
*/
static class ExpireNode<V> {
long expire;
Object value;
public ExpireNode(long expire, Object value) {
this.expire = expire;
this.value = value;
}
}
}

View File

@@ -0,0 +1,41 @@
package com.codvision.db.cache;
import com.codvision.commoncore.utils.Md5Utils;
import java.util.Arrays;
/**
* SQL缓存接口
*/
public interface SqlCache {
/**
* 计算key
*/
default String buildSqlCacheKey(String sql, Object[] args) {
return Md5Utils.getMd5(sql + ":" + Arrays.toString(args));
}
/**
* 存入缓存
*
* @param key key
* @param value 值
*/
void put(String key, Object value, long ttl);
/**
* 获取缓存
*
* @param key key
* @return
*/
<T> T get(String key);
/**
* 删除缓存
*
* @param key key
*/
void delete(String key);
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
package com.codvision.db.config;
import com.codvision.db.versionctl.DbVersionCtlInitializer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 数据源模块配置
*
* @author lingee
* @date 2023/12/5
*/
@Slf4j
@Configuration
@ImportAutoConfiguration(DBVCTLProps.class)
public class DBConfig {
@Bean("dbVersionCtlInitializer")
public DbVersionCtlInitializer init() {
return new DbVersionCtlInitializer();
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
package com.codvision.db.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* 数据版本配置控制
*
* @author lingee
* @date 2023/12/5
*/
@Data
@Configuration
@ConfigurationProperties("cvbp.dbvctl")
public class DBVCTLProps {
/**
* 是否自动执行数据库版本控制,默认不执行
*/
private Boolean enabled = Boolean.FALSE;
/**
* 数据库非空但首次使用数据库版本管理时,指定生成版本基线的业务空间及其基线版本,多个业务空间时使用逗号连接。
* 例如:"raven_V1.0.0,sentry_V1.1.2"
*/
private String baselineBusinessSpaceAndVersions;
/**
* 是否重置数据库基线版本
*/
private Boolean baselineReset = Boolean.FALSE;
/**
* 数据库基线版本重置条件SQL只有[baselineReset]设置为true且该SQL查询结果非空才会进行数据库基线版本重置操作
* 通常建议使用时间戳字段[install_time]作为查询SQL的条件这样只会生效一次
* 以后升级版本时即使忘记将【baselineReset】属性清除或设置为false也不会导致数据库基线版本被误重置。
*/
private String baselineResetConditionSql = "";
}

View File

@@ -0,0 +1,38 @@
package com.codvision.db.constants;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.codvision.db.exception.DataQueryException;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.Serializable;
@Data
@AllArgsConstructor
public class DbQueryProperty implements Serializable {
private static final long serialVersionUID = 1L;
private DbType dbType;
private String host;
private String username;
private String password;
private Integer port;
private String dbName;
private String sid;
/**
* 参数合法性校验
*/
public void valid() {
if (ObjectUtil.isEmpty(dbType) || StrUtil.isEmpty(host) ||
StrUtil.isEmpty(username) || StrUtil.isEmpty(password) ||
ObjectUtil.isEmpty(port)) {
throw new DataQueryException("参数不完整");
}
if (DbType.OTHER.getDb().equals(dbType)) {
throw new DataQueryException("不支持的数据库类型");
}
}
}

View File

@@ -0,0 +1,90 @@
package com.codvision.db.constants;
/**
* 数据库类型
*
* @author yuwei
* @since 2020-03-14
*/
public enum DbType {
/**
* MYSQL
*/
MYSQL("1", "MySql数据库", "jdbc:mysql://${host}:${port}/${dbName}?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8"),
/**
* MARIADB
*/
MARIADB("2", "MariaDB数据库", "jdbc:mariadb://${host}:${port}/${dbName}"),
/**
* ORACLE
*/
ORACLE("3", "Oracle11g及以下数据库", "jdbc:oracle:thin:@${host}:${port}:${sid}"),
/**
* oracle12c new pagination
*/
ORACLE_12C("4", "Oracle12c+数据库", "jdbc:oracle:thin:@${host}:${port}:${sid}"),
/**
* POSTGRESQL
*/
POSTGRESQL("5", "PostgreSql数据库", "jdbc:postgresql://${host}:${port}/${dbName}"),
/**
* SQLSERVER2005
*/
MSSQL2008("6", "SQLServer2008及以下数据库", "jdbc:sqlserver://${host}:${port};DatabaseName=${dbName}"),
/**
* SQLSERVER
*/
MSSQL("7", "SQLServer2012+数据库", "jdbc:sqlserver://${host}:${port};DatabaseName=${dbName}"),
/**
* UNKONWN DB
*/
OTHER("8", "其他数据库", "");
/**
* 数据库名称
*/
private final String db;
/**
* 描述
*/
private final String desc;
/**
* url
*/
private final String url;
public String getDb() {
return this.db;
}
public String getDesc() {
return this.desc;
}
public String getUrl() {
return this.url;
}
DbType(String db, String desc, String url) {
this.db = db;
this.desc = desc;
this.url = url;
}
/**
* 获取数据库类型
*
* @param dbType 数据库类型字符串
*/
public static DbType getDbType(String dbType) {
for (DbType type : DbType.values()) {
if (type.name().equals(dbType)) {
return type;
}
}
return OTHER;
}
}

View File

@@ -0,0 +1,57 @@
package com.codvision.db.core;
import lombok.Data;
@Data
public class DbColumn {
/**
* 列名
*/
private String colName;
/**
* 数据类型
*/
private String dataType;
/**
* 数据长度
*/
private String dataLength;
/**
* 数据精度
*/
private String dataPrecision;
/**
* 数据小数位
*/
private String dataScale;
/**
* 是否主键
*/
private Boolean colKey;
/**
* 是否允许为空
*/
private Boolean nullable;
/**
* 列的序号
*/
private Integer colPosition;
/**
* 列默认值
*/
private String dataDefault;
/**
* 列注释
*/
private String colComment;
}

View File

@@ -0,0 +1,17 @@
package com.codvision.db.core;
import lombok.Data;
@Data
public class DbTable {
/**
* 表名
*/
private String tableName;
/**
* 表注释
*/
private String tableComment;
}

View File

@@ -0,0 +1,24 @@
package com.codvision.db.core;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.List;
@Data
@Accessors(chain = true)
public class PageResult<T> implements Serializable {
private static final long serialVersionUID = 1L;
private Integer pageNum;
private Integer pageSize;
private Integer total;
private List<T> data;
public PageResult(Integer total, List<T> data) {
this.total = total;
this.data = data;
}
}

View File

@@ -0,0 +1,62 @@
package com.codvision.db.datasource;
import com.codvision.db.DataSourceFactory;
import com.codvision.db.DbDialect;
import com.codvision.db.DbQuery;
import com.codvision.db.DialectFactory;
import com.codvision.db.constants.DbQueryProperty;
import com.codvision.db.constants.DbType;
import com.codvision.db.exception.DataQueryException;
import com.codvision.db.query.AbstractDbQueryFactory;
import com.codvision.db.query.CacheDbQueryFactoryBean;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.StringUtils;
import javax.sql.DataSource;
public abstract class AbstractDataSourceFactory implements DataSourceFactory {
@Override
public DbQuery createDbQuery(DbQueryProperty property) {
property.valid();
DataSource dataSource = createDataSource(property);
DbQuery dbQuery = createDbQuery(dataSource, property.getDbType());
return dbQuery;
}
public DbQuery createDbQuery(DataSource dataSource, DbType dbType) {
DbDialect dbDialect = DialectFactory.getDialect(dbType);
if (dbDialect == null) {
throw new DataQueryException("该数据库类型正在开发中");
}
AbstractDbQueryFactory dbQuery = new CacheDbQueryFactoryBean();
dbQuery.setDataSource(dataSource);
dbQuery.setJdbcTemplate(new JdbcTemplate(dataSource));
dbQuery.setDbDialect(dbDialect);
return dbQuery;
}
public DataSource createDataSource(DbQueryProperty property) {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(trainToJdbcUrl(property));
dataSource.setUsername(property.getUsername());
dataSource.setPassword(property.getPassword());
return dataSource;
}
protected String trainToJdbcUrl(DbQueryProperty property) {
String url = property.getDbType().getUrl();
if (StringUtils.isEmpty(url)) {
throw new DataQueryException("无效数据库类型!");
}
url = url.replace("${host}", property.getHost());
url = url.replace("${port}", String.valueOf(property.getPort()));
if (DbType.ORACLE.equals(property.getDbType()) || DbType.ORACLE_12C.equals(property.getDbType())) {
url = url.replace("${sid}", property.getSid());
} else {
url = url.replace("${dbName}", property.getDbName());
}
return url;
}
}

View File

@@ -0,0 +1,65 @@
package com.codvision.db.datasource;
import com.codvision.db.constants.DbQueryProperty;
import javax.sql.DataSource;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class CacheDataSourceFactoryBean extends AbstractDataSourceFactory {
/**
* 数据源缓存
*/
private static Map<String, DataSource> dataSourceMap = new ConcurrentHashMap<>();
@Override
public DataSource createDataSource(DbQueryProperty property) {
String key = property.getDbType() + ":" + property.getHost()
+ ":" + property.getPort() + ":" + property.getUsername()
+ ":" + property.getPassword() + ":" + property.getDbName()
+ ":" + property.getSid();
String s = compress(key);
DataSource dataSource = dataSourceMap.get(s);
if (null == dataSource) {
synchronized (CacheDataSourceFactoryBean.class) {
dataSource = super.createDataSource(property);
dataSourceMap.put(s, dataSource);
}
}
return dataSource;
}
// 压缩
public static String compress(String str) {
if (str == null || str.length() == 0) {
return str;
}
MessageDigest md = null;
try {
md = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
md.update(str.getBytes());
byte b[] = md.digest();
int i;
StringBuffer buf = new StringBuffer();
for (int offset = 0; offset < b.length; offset++) {
i = b[offset];
if (i < 0)
i += 256;
if (i < 16)
buf.append("0");
buf.append(Integer.toHexString(i));
}
// System.out.println("MD5(" + str + ",32小写) = " + buf.toString());
// System.out.println("MD5(" + str + ",32大写) = " + buf.toString().toUpperCase());
// System.out.println("MD5(" + str + ",16小写) = " + buf.toString().substring(8, 24));
// System.out.println("MD5(" + str + ",16大写) = " + buf.toString().substring(8, 24).toUpperCase());
return buf.toString().substring(8, 24).toUpperCase();
}
}

View File

@@ -0,0 +1,4 @@
package com.codvision.db.datasource;
public class DefaultDataSourceFactoryBean extends AbstractDataSourceFactory {
}

View File

@@ -0,0 +1,55 @@
package com.codvision.db.dialect;
import com.codvision.db.DbDialect;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
/**
* 方言抽象类
*
* @author yuwei
* @since 2020-03-14
*/
public abstract class AbstractDbDialect implements DbDialect {
@Override
public RowMapper<String> viewMapper() {
return (ResultSet rs, int rowNum) -> rs.getString("table_name");
}
@Override
public String columns(String dbName, String tableName) {
return "select column_name AS COLNAME, ordinal_position AS COLPOSITION, column_default AS DATADEFAULT, is_nullable AS NULLABLE, data_type AS DATATYPE, " +
"character_maximum_length AS DATALENGTH, numeric_precision AS DATAPRECISION, numeric_scale AS DATASCALE, column_key AS COLKEY, column_comment AS COLCOMMENT " +
"from information_schema.columns where table_schema = '" + dbName + "' and table_name = '" + tableName + "' order by ordinal_position ";
}
@Override
public String tables(String dbName) {
return "SELECT table_name AS TABLENAME, table_comment AS TABLECOMMENT FROM information_schema.tables where table_schema = '" + dbName + "' ";
}
public String table(String dbName, String tableName) {
return "SELECT table_name AS TABLENAME, table_comment AS TABLECOMMENT FROM information_schema.tables where table_schema = '" + dbName + "' and table_name = '" + tableName + "' ";
}
@Override
public String views(String dbName) {
return "SELECT table_name AS TABLENAME FROM information_schema.views where table_schema = '" + dbName + "' ";
}
@Override
public String buildPaginationSql(String originalSql, long offset, long count) {
// 获取 分页实际条数
StringBuilder sqlBuilder = new StringBuilder(originalSql);
sqlBuilder.append(" LIMIT ").append(offset).append(" , ").append(count);
return sqlBuilder.toString();
}
@Override
public String count(String sql) {
return "SELECT COUNT(*) FROM ( " + sql + " ) TEMP";
}
}

View File

@@ -0,0 +1,28 @@
package com.codvision.db.dialect;
import com.codvision.db.DbDialect;
import com.codvision.db.constants.DbType;
import java.util.EnumMap;
import java.util.Map;
public class DialectRegistry {
private final Map<DbType, DbDialect> dialect_enum_map = new EnumMap<>(DbType.class);
public DialectRegistry() {
dialect_enum_map.put(DbType.MARIADB, new MariaDBDialect());
dialect_enum_map.put(DbType.MYSQL, new MySqlDialect());
dialect_enum_map.put(DbType.ORACLE_12C, new Oracle12cDialect());
dialect_enum_map.put(DbType.ORACLE, new OracleDialect());
dialect_enum_map.put(DbType.POSTGRESQL, new PostgreDialect());
dialect_enum_map.put(DbType.MSSQL2008, new SQLServer2008Dialect());
dialect_enum_map.put(DbType.MSSQL, new SQLServerDialect());
dialect_enum_map.put(DbType.OTHER, new UnknownDialect());
}
public DbDialect getDialect(DbType dbType) {
return dialect_enum_map.get(dbType);
}
}

View File

@@ -0,0 +1,10 @@
package com.codvision.db.dialect;
/**
* MariaDB 数据库方言
*
* @author yuwei
* @since 2020-03-14
*/
public class MariaDBDialect extends MySqlDialect {
}

View File

@@ -0,0 +1,44 @@
package com.codvision.db.dialect;
import com.codvision.db.core.DbColumn;
import com.codvision.db.core.DbTable;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
/**
* MySql 数据库方言
*
* @author yuwei
* @since 2020-03-14
*/
public class MySqlDialect extends AbstractDbDialect {
@Override
public RowMapper<DbColumn> columnMapper() {
return (ResultSet rs, int rowNum) -> {
DbColumn entity = new DbColumn();
entity.setColName(rs.getString("COLNAME"));
entity.setDataType(rs.getString("DATATYPE"));
entity.setDataLength(rs.getString("DATALENGTH"));
entity.setDataPrecision(rs.getString("DATAPRECISION"));
entity.setDataScale(rs.getString("DATASCALE"));
entity.setColKey("PRI".equals(rs.getString("COLKEY")));
entity.setNullable("YES".equals(rs.getString("NULLABLE")));
entity.setColPosition(rs.getInt("COLPOSITION"));
entity.setDataDefault(rs.getString("DATADEFAULT"));
entity.setColComment(rs.getString("COLCOMMENT"));
return entity;
};
}
@Override
public RowMapper<DbTable> tableMapper() {
return (ResultSet rs, int rowNum) -> {
DbTable entity = new DbTable();
entity.setTableName(rs.getString("TABLENAME"));
entity.setTableComment(rs.getString("TABLECOMMENT"));
return entity;
};
}
}

View File

@@ -0,0 +1,17 @@
package com.codvision.db.dialect;
/**
* ORACLE Oracle12c+数据库方言
*
* @author yuwei
* @since 2020-03-14
*/
public class Oracle12cDialect extends OracleDialect {
@Override
public String buildPaginationSql(String originalSql, long offset, long count) {
StringBuilder sqlBuilder = new StringBuilder(originalSql);
sqlBuilder.append(" OFFSET ").append(offset).append(" ROWS FETCH NEXT ").append(count).append(" ROWS ONLY ");
return sqlBuilder.toString();
}
}

View File

@@ -0,0 +1,88 @@
package com.codvision.db.dialect;
import com.codvision.db.core.DbColumn;
import com.codvision.db.core.DbTable;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
/**
* Oracle Oracle11g及以下数据库方言
*
* @author yuwei
* @since 2020-03-14
*/
public class OracleDialect extends AbstractDbDialect {
@Override
public String columns(String dbName, String tableName) {
return "select columns.column_name AS colName, columns.data_type AS DATATYPE, columns.data_length AS DATALENGTH, columns.data_precision AS DATAPRECISION, " +
"columns.data_scale AS DATASCALE, columns.nullable AS NULLABLE, columns.column_id AS COLPOSITION, columns.data_default AS DATADEFAULT, comments.comments AS COLCOMMENT," +
"case when t.column_name is null then 0 else 1 end as COLKEY " +
"from sys.user_tab_columns columns LEFT JOIN sys.user_col_comments comments ON columns.table_name = comments.table_name AND columns.column_name = comments.column_name " +
"left join ( " +
"select col.column_name as column_name, con.table_name as table_name from user_constraints con, user_cons_columns col " +
"where con.constraint_name = col.constraint_name and con.constraint_type = 'P' " +
") t on t.table_name = columns.table_name and columns.column_name = t.column_name " +
"where columns.table_name = UPPER('" + tableName + "') order by columns.column_id ";
}
@Override
public String tables(String dbName) {
return "select tables.table_name AS TABLENAME, comments.comments AS TABLECOMMENT from sys.user_tables tables " +
"LEFT JOIN sys.user_tab_comments comments ON tables.table_name = comments.table_name ";
}
@Override
public String table(String dbName, String tableName) {
return "select tables.table_name AS TABLENAME, comments.comments AS TABLECOMMENT from sys.user_tables tables " +
"LEFT JOIN sys.user_tab_comments comments ON tables.table_name = comments.table_name where tables.table_name = '" + tableName + "' ";
}
@Override
public String buildPaginationSql(String originalSql, long offset, long count) {
StringBuilder sqlBuilder = new StringBuilder();
sqlBuilder.append("SELECT * FROM ( SELECT TMP.*, ROWNUM ROW_ID FROM ( ");
sqlBuilder.append(originalSql).append(" ) TMP WHERE ROWNUM <=").append((offset >= 1) ? (offset + count) : count);
sqlBuilder.append(") WHERE ROW_ID > ").append(offset);
return sqlBuilder.toString();
}
@Override
public RowMapper<DbColumn> columnLongMapper() {
return (ResultSet rs, int rowNum) -> {
DbColumn entity = new DbColumn();
entity.setDataDefault(rs.getString("DATADEFAULT"));
return entity;
};
}
@Override
public RowMapper<DbColumn> columnMapper() {
return (ResultSet rs, int rowNum) -> {
DbColumn entity = new DbColumn();
entity.setColName(rs.getString("COLNAME"));
entity.setDataType(rs.getString("DATATYPE"));
entity.setDataLength(rs.getString("DATALENGTH"));
entity.setDataPrecision(rs.getString("DATAPRECISION"));
entity.setDataScale(rs.getString("DATASCALE"));
entity.setColKey("1".equals(rs.getString("COLKEY")));
entity.setNullable("Y".equals(rs.getString("NULLABLE")));
//long类型单独处理
//entity.setDataDefault(rs.getString("DATADEFAULT"));
entity.setColPosition(rs.getInt("COLPOSITION"));
entity.setColComment(rs.getString("COLCOMMENT"));
return entity;
};
}
@Override
public RowMapper<DbTable> tableMapper() {
return (ResultSet rs, int rowNum) -> {
DbTable entity = new DbTable();
entity.setTableName(rs.getString("TABLENAME"));
entity.setTableComment(rs.getString("TABLECOMMENT"));
return entity;
};
}
}

View File

@@ -0,0 +1,78 @@
package com.codvision.db.dialect;
import com.codvision.db.core.DbColumn;
import com.codvision.db.core.DbTable;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
/**
* Postgre 数据库方言
*
* @author yuwei
* @since 2020-03-14
*/
public class PostgreDialect extends AbstractDbDialect {
@Override
public String columns(String dbName, String tableName) {
return "select col.column_name AS COLNAME, col.ordinal_position AS COLPOSITION, col.column_default AS DATADEFAULT, col.is_nullable AS NULLABLE, col.udt_name AS DATATYPE, " +
"col.character_maximum_length AS DATALENGTH, col.numeric_precision AS DATAPRECISION, col.numeric_scale AS DATASCALE, des.description AS COLCOMMENT, " +
"case when t.colname is null then 0 else 1 end as COLKEY " +
"from information_schema.columns col left join pg_description des on col.table_name::regclass = des.objoid and col.ordinal_position = des.objsubid " +
"left join ( " +
"select pg_attribute.attname as colname from pg_constraint inner join pg_class on pg_constraint.conrelid = pg_class.oid " +
"inner join pg_attribute on pg_attribute.attrelid = pg_class.oid and pg_attribute.attnum = any(pg_constraint.conkey) " +
"where pg_class.relname = '" + tableName + "' and pg_constraint.contype = 'p' " +
") t on t.colname = col.column_name " +
"where col.table_catalog = '" + dbName + "' and col.table_schema = 'public' and col.table_name = '" + tableName + "' order by col.ordinal_position ";
}
@Override
public String tables(String dbName) {
return "select relname AS TABLENAME, cast(obj_description(relfilenode, 'pg_class') as varchar) AS TABLECOMMENT from pg_class " +
"where relname in (select tablename from pg_tables where schemaname = 'public' and position('_2' in tablename) = 0) ";
}
@Override
public String table(String dbName, String tableName) {
return "select relname AS TABLENAME, cast(obj_description(relfilenode, 'pg_class') as varchar) AS TABLECOMMENT from pg_class " +
"where relname in (select tablename from pg_tables where schemaname = 'public' and tablename = '" + tableName + "'" +
" and position('_2' in tablename) = 0) ";
}
@Override
public String buildPaginationSql(String originalSql, long offset, long count) {
StringBuilder sqlBuilder = new StringBuilder(originalSql);
sqlBuilder.append(" LIMIT ").append(count).append(" offset ").append(offset);
return sqlBuilder.toString();
}
@Override
public RowMapper<DbColumn> columnMapper() {
return (ResultSet rs, int rowNum) -> {
DbColumn entity = new DbColumn();
entity.setColName(rs.getString("COLNAME"));
entity.setDataType(rs.getString("DATATYPE"));
entity.setDataLength(rs.getString("DATALENGTH"));
entity.setDataPrecision(rs.getString("DATAPRECISION"));
entity.setDataScale(rs.getString("DATASCALE"));
entity.setColKey("1".equals(rs.getString("COLKEY")) ? true : false);
entity.setNullable("YES".equals(rs.getString("NULLABLE")) ? true : false);
entity.setColPosition(rs.getInt("COLPOSITION"));
entity.setDataDefault(rs.getString("DATADEFAULT"));
entity.setColComment(rs.getString("COLCOMMENT"));
return entity;
};
}
@Override
public RowMapper<DbTable> tableMapper() {
return (ResultSet rs, int rowNum) -> {
DbTable entity = new DbTable();
entity.setTableName(rs.getString("TABLENAME"));
entity.setTableComment(rs.getString("TABLECOMMENT"));
return entity;
};
}
}

View File

@@ -0,0 +1,113 @@
package com.codvision.db.dialect;
import com.codvision.db.core.DbColumn;
import com.codvision.db.core.DbTable;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.util.StringUtils;
import java.sql.ResultSet;
/**
* SQLServer 2005 数据库方言
*
* @author yuwei
* @since 2020-03-14
*/
public class SQLServer2008Dialect extends AbstractDbDialect {
@Override
public String columns(String dbName, String tableName) {
return "select columns.name AS colName, columns.column_id AS COLPOSITION, columns.max_length AS DATALENGTH, columns.precision AS DATAPRECISION, columns.scale AS DATASCALE, " +
"columns.is_nullable AS NULLABLE, types.name AS DATATYPE, CAST(ep.value AS NVARCHAR(128)) AS COLCOMMENT, e.text AS DATADEFAULT, " +
"(select top 1 ind.is_primary_key from sys.index_columns ic left join sys.indexes ind on ic.object_id = ind.object_id and ic.index_id = ind.index_id and ind.name like 'PK_%' where ic.object_id=columns.object_id and ic.column_id=columns.column_id) AS COLKEY " +
"from sys.columns columns LEFT JOIN sys.types types ON columns.system_type_id = types.system_type_id " +
"LEFT JOIN syscomments e ON columns.default_object_id= e.id " +
"LEFT JOIN sys.extended_properties ep ON ep.major_id = columns.object_id AND ep.minor_id = columns.column_id AND ep.name = 'MS_Description' " +
"where columns.object_id = object_id('" + tableName + "') order by columns.column_id ";
}
@Override
public String tables(String dbName) {
return "select tables.name AS TABLENAME, CAST(ep.value AS NVARCHAR(128)) AS TABLECOMMENT " +
"from sys.tables tables LEFT JOIN sys.extended_properties ep ON ep.major_id = tables.object_id AND ep.minor_id = 0";
}
@Override
public String table(String dbName, String tableName) {
return "select tables.name AS TABLENAME, CAST(ep.value AS NVARCHAR(128)) AS TABLECOMMENT " +
"from sys.tables tables LEFT JOIN sys.extended_properties ep ON ep.major_id = tables.object_id AND ep.minor_id = 0" +
"where tables.name = '" + tableName + "'";
}
private static String getOrderByPart(String sql) {
String loweredString = sql.toLowerCase();
int orderByIndex = loweredString.indexOf("order by");
if (orderByIndex != -1) {
return sql.substring(orderByIndex);
} else {
return "";
}
}
@Override
public String buildPaginationSql(String originalSql, long offset, long count) {
StringBuilder pagingBuilder = new StringBuilder();
String orderby = getOrderByPart(originalSql);
String distinctStr = "";
String loweredString = originalSql.toLowerCase();
String sqlPartString = originalSql;
if (loweredString.trim().startsWith("select")) {
int index = 6;
if (loweredString.startsWith("select distinct")) {
distinctStr = "DISTINCT ";
index = 15;
}
sqlPartString = sqlPartString.substring(index);
}
pagingBuilder.append(sqlPartString);
// if no ORDER BY is specified use fake ORDER BY field to avoid errors
if (StringUtils.isEmpty(orderby)) {
orderby = "ORDER BY CURRENT_TIMESTAMP";
}
StringBuilder sql = new StringBuilder();
sql.append("WITH selectTemp AS (SELECT ").append(distinctStr).append("TOP 100 PERCENT ")
.append(" ROW_NUMBER() OVER (").append(orderby).append(") as __row_number__, ").append(pagingBuilder)
.append(") SELECT * FROM selectTemp WHERE __row_number__ BETWEEN ")
//FIX#299原因mysql中limit 10(offset,size) 是从第10开始不包含10,而这里用的BETWEEN是两边都包含所以改为offset+1
.append(offset + 1)
.append(" AND ")
.append(offset + count).append(" ORDER BY __row_number__");
return sql.toString();
}
@Override
public RowMapper<DbColumn> columnMapper() {
return (ResultSet rs, int rowNum) -> {
DbColumn entity = new DbColumn();
entity.setColName(rs.getString("COLNAME"));
entity.setDataType(rs.getString("DATATYPE"));
entity.setDataLength(rs.getString("DATALENGTH"));
entity.setDataPrecision(rs.getString("DATAPRECISION"));
entity.setDataScale(rs.getString("DATASCALE"));
entity.setColKey("1".equals(rs.getString("COLKEY")) ? true : false);
entity.setNullable("1".equals(rs.getString("NULLABLE")) ? true : false);
entity.setColPosition(rs.getInt("COLPOSITION"));
entity.setDataDefault(rs.getString("DATADEFAULT"));
entity.setColComment(rs.getString("COLCOMMENT"));
return entity;
};
}
@Override
public RowMapper<DbTable> tableMapper() {
return (ResultSet rs, int rowNum) -> {
DbTable entity = new DbTable();
entity.setTableName(rs.getString("TABLENAME"));
entity.setTableComment(rs.getString("TABLECOMMENT"));
return entity;
};
}
}

View File

@@ -0,0 +1,17 @@
package com.codvision.db.dialect;
/**
* SQLServer 数据库方言
*
* @author yuwei
* @since 2020-03-14
*/
public class SQLServerDialect extends SQLServer2008Dialect {
@Override
public String buildPaginationSql(String originalSql, long offset, long count) {
StringBuilder sqlBuilder = new StringBuilder(originalSql);
sqlBuilder.append(" OFFSET ").append(offset).append(" ROWS FETCH NEXT ").append(count).append(" ROWS ONLY ");
return sqlBuilder.toString();
}
}

View File

@@ -0,0 +1,50 @@
package com.codvision.db.dialect;
import com.codvision.db.core.DbColumn;
import com.codvision.db.core.DbTable;
import com.codvision.db.exception.DataQueryException;
import org.springframework.jdbc.core.RowMapper;
/**
* 未知 数据库方言
*
* @author yuwei
* @since 2020-03-14
*/
public class UnknownDialect extends AbstractDbDialect {
@Override
public String columns(String dbName, String tableName) {
throw new DataQueryException("不支持的数据库类型");
}
@Override
public String tables(String dbName) {
throw new DataQueryException("不支持的数据库类型");
}
@Override
public String table(String dbName, String tableName) {
throw new DataQueryException("不支持的数据库类型");
}
@Override
public String buildPaginationSql(String sql, long offset, long count) {
throw new DataQueryException("不支持的数据库类型");
}
@Override
public String count(String sql) {
throw new DataQueryException("不支持的数据库类型");
}
@Override
public RowMapper<DbColumn> columnMapper() {
throw new DataQueryException("不支持的数据库类型");
}
@Override
public RowMapper<DbTable> tableMapper() {
throw new DataQueryException("不支持的数据库类型");
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
package com.codvision.db.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 数据库类型
*
* @author lingee
* @date 2023/12/5
*/
@Getter
@AllArgsConstructor
public enum DbTypeEnum {
/**
* mysql数据库
*/
mysql("mysql"),
/**
* pgsql数据库
*/
postgresql("postgresql");
private final String type;
public static DbTypeEnum resolve(String type) {
for (DbTypeEnum value : values()) {
if (value.getType().equals(type)) {
return value;
}
}
throw new EnumConstantNotPresentException(DbTypeEnum.class, type);
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2023 codvision.com All Rights Reserved.
*/
package com.codvision.db.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 系统业务空间
*
* @author lingee
* @date 2023/12/12
*/
@AllArgsConstructor
@Getter
public enum SysBusinessSpace {
/**
* 基础能力中心
*/
SYS_BASE("base"),
/**
* 监控中心
*/
SYS_MONITOR("monitor"),
/**
* 认证中心
*/
SYS_AUTH("auth");
private final String code;
public static SysBusinessSpace resolve(String key) {
for (SysBusinessSpace value : values()) {
if (value.code.equals(key)) {
return value;
}
}
return null;
}
}

View File

@@ -0,0 +1,8 @@
package com.codvision.db.exception;
public class DataQueryException extends RuntimeException {
public DataQueryException(String message) {
super(message);
}
}

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