Merge branch 'feature/REQ-2040' into 'master'
Feature/req 2040 See merge request universal/infrastructure/backend/nanopart!5
This commit is contained in:
commit
fd5932d02e
11
README.md
11
README.md
@ -1,5 +1,6 @@
|
||||
# 项目介绍
|
||||
一个通用的聚合服务:通用黑名单服务
|
||||
一个通用的聚合服务:
|
||||
* 通用黑白名单服务
|
||||
|
||||
# vm参数
|
||||
## dev环境
|
||||
@ -18,13 +19,19 @@
|
||||
-DCUSTOM_ENV=test
|
||||
-Dserver.port=8080
|
||||
|
||||
# 各模块结构
|
||||
# 黑白名单中各模块结构
|
||||
## all-login 登录白名单
|
||||
~~~json
|
||||
{
|
||||
"phone": "手机号"
|
||||
}
|
||||
~~~
|
||||
## ou_register_black_list 企业注册黑名单
|
||||
~~~json
|
||||
{
|
||||
"phone": "手机号"
|
||||
}
|
||||
~~~
|
||||
## attendance-pass 考勤黑名单
|
||||
~~~json
|
||||
{
|
||||
|
||||
@ -22,5 +22,9 @@
|
||||
<groupId>cn.axzo.framework</groupId>
|
||||
<artifactId>axzo-common-domain</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
package cn.axzo.nanopart.api;
|
||||
|
||||
import cn.axzo.framework.domain.web.result.ApiResult;
|
||||
import cn.axzo.nanopart.api.annotation.CheckSign;
|
||||
import cn.axzo.nanopart.api.constant.enums.ListTypeEnum;
|
||||
import cn.axzo.nanopart.api.request.BlackAndWhiteListExcelImportReq;
|
||||
import cn.axzo.nanopart.api.request.BlackAndWhiteListInternalSyncReq;
|
||||
import cn.axzo.nanopart.api.request.BlackAndWhiteListPlatformSyncReq;
|
||||
import cn.axzo.nanopart.api.request.BlackAndWhiteListReq;
|
||||
import cn.axzo.nanopart.api.response.BlackAndWhiteListExcelImportResp;
|
||||
import cn.axzo.nanopart.api.response.BlackAndWhiteListResp;
|
||||
@ -13,6 +16,7 @@ import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
@ -84,4 +88,35 @@ public interface BlackAndWhiteListApi {
|
||||
@PostMapping("api/black-white-list/import")
|
||||
ApiResult<BlackAndWhiteListExcelImportResp> importBlackAndWhiteListExcel(@RequestParam(value = "type") @NotNull(message = "名单类型不能为空") ListTypeEnum type,
|
||||
@RequestParam(value = "module") @NotBlank(message = "模块不能为空") String module, @RequestParam(value = "url", required = false) String url, @RequestPart(value = "file") MultipartFile file);
|
||||
|
||||
/**
|
||||
* 平台同步黑白名单
|
||||
* <p>
|
||||
* 每次最多同步1000条记录且整体上同步策略采用全量覆盖策略
|
||||
* 若总记录数超过1000条请自行分片多次调用
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 该接口需要验签,参与签名的字段为header中的:appKey,timestamp,
|
||||
* 签名字段为header中的sign
|
||||
* </p>
|
||||
*
|
||||
* @param req @{link BlackAndWhiteListPlatformSyncReq}
|
||||
* @return true-同步成功,false-同步失败
|
||||
*/
|
||||
@PostMapping("api/black-white-list/plat-sync")
|
||||
ApiResult<Boolean> platformSync(@RequestBody @Validated BlackAndWhiteListPlatformSyncReq req);
|
||||
|
||||
/**
|
||||
* TODO 该接口目前耗时较长待优化
|
||||
* 内部自同步黑白名单
|
||||
* <p>
|
||||
* 例如将其他模块的黑名单同步到登录模块黑名单
|
||||
* </p>
|
||||
*
|
||||
* @param req @{link BlackAndWhiteListInternalSyncReq}
|
||||
* @return 同步结果,成功返回同步记录总数
|
||||
*/
|
||||
@PostMapping("api/black-white-list/inner-sync")
|
||||
ApiResult<Long> internalSync(@RequestBody @Validated BlackAndWhiteListInternalSyncReq req);
|
||||
}
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
package cn.axzo.nanopart.api;
|
||||
|
||||
import cn.axzo.framework.domain.web.result.ApiResult;
|
||||
import cn.axzo.nanopart.api.request.SignatureGetReq;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 签名工具类
|
||||
*
|
||||
* @author chenwenjian
|
||||
* @version 1.0
|
||||
* @date 2024/1/2 16:53
|
||||
*/
|
||||
@FeignClient(name = "nanopart", url = "http://nanopart:8080")
|
||||
public interface SignatureUtilApi {
|
||||
|
||||
/**
|
||||
* 生成公私钥对
|
||||
*
|
||||
* @param algorithm 算法,例如:RSA
|
||||
* @return 公私钥对
|
||||
*/
|
||||
@GetMapping("api/signature/generateKeyPair")
|
||||
ApiResult<Map<String,String>> generateKeyPair(@RequestParam(value = "algorithm") String algorithm);
|
||||
|
||||
/**
|
||||
* 获取签名
|
||||
*
|
||||
* @param req 请求
|
||||
* @return 签名
|
||||
*/
|
||||
@PostMapping("api/signature/getSignature")
|
||||
ApiResult<String> getSignature(@RequestBody @Validated SignatureGetReq req);
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package cn.axzo.nanopart.api.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 签名验证注解
|
||||
*
|
||||
* @author chenwenjian
|
||||
* @version 1.0
|
||||
* @date 2024/1/2 10:37
|
||||
*/
|
||||
@Documented
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface CheckSign {
|
||||
/**
|
||||
* 签名字段,默认为sign
|
||||
*/
|
||||
String signField() default "sign";
|
||||
|
||||
/**
|
||||
* header中需要参与签名的字段,若为空,则除签名字段外全部参与签名
|
||||
*/
|
||||
String[] requireSignFields() default {};
|
||||
}
|
||||
@ -0,0 +1,164 @@
|
||||
package cn.axzo.nanopart.api.aspect;
|
||||
|
||||
import cn.axzo.framework.domain.web.BizException;
|
||||
import cn.axzo.framework.domain.web.code.RespCode;
|
||||
import cn.axzo.nanopart.api.annotation.CheckSign;
|
||||
import cn.axzo.nanopart.api.common.util.StringUtil;
|
||||
import cn.axzo.nanopart.api.config.SignManager;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author chenwenjian
|
||||
* @version 1.0
|
||||
* @date 2024/1/2 10:05
|
||||
*/
|
||||
@Slf4j
|
||||
@Aspect
|
||||
@Order(1)
|
||||
@Component
|
||||
public class CheckSignAspect {
|
||||
|
||||
@Resource
|
||||
private SignManager signManager;
|
||||
|
||||
@Pointcut(value = "@annotation(org.springframework.web.bind.annotation.RequestMapping)")
|
||||
public void requestMapping() {
|
||||
}
|
||||
|
||||
@Pointcut(value = "@annotation(org.springframework.web.bind.annotation.PostMapping)")
|
||||
public void postMapping() {
|
||||
log.info("post");
|
||||
}
|
||||
|
||||
@Pointcut(value = "@annotation(org.springframework.web.bind.annotation.GetMapping)")
|
||||
public void getMapping() {
|
||||
}
|
||||
|
||||
@Pointcut(value = "@annotation(org.springframework.web.bind.annotation.PutMapping)")
|
||||
public void putMapping() {
|
||||
}
|
||||
|
||||
@Pointcut(value = "@annotation(org.springframework.web.bind.annotation.DeleteMapping)")
|
||||
public void deleteMapping() {
|
||||
}
|
||||
|
||||
@Pointcut(value = "@annotation(org.springframework.web.bind.annotation.PatchMapping)")
|
||||
public void patchMapping() {
|
||||
}
|
||||
|
||||
@Pointcut("requestMapping() || postMapping() || getMapping() || putMapping() || deleteMapping()|| patchMapping()")
|
||||
public void mappingAnnotations() {
|
||||
}
|
||||
|
||||
// @Pointcut(value = "@annotation(cn.axzo.nanopart.api.annotation.CheckSign)")
|
||||
// public void checkSign() {
|
||||
// }
|
||||
|
||||
/**
|
||||
* 切入含有@CheckSign && @RestController 注解的类
|
||||
*/
|
||||
@Before(value = "@within(checkSign) && @within(restController)", argNames = "joinPoint,checkSign,restController")
|
||||
public void classHandler(JoinPoint joinPoint, CheckSign checkSign, RestController restController) {
|
||||
handle(joinPoint, checkSign);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切入含有@checkSign && @RequestMapping/@PostMapping/@GetMapping/@PutMapping/@DeleteMapping/@PatchMapping 之一注解的方法
|
||||
*/
|
||||
@Before(value = "@annotation(checkSign)")
|
||||
public void methodHandler(JoinPoint joinPoint, CheckSign checkSign) {
|
||||
handle(joinPoint, checkSign);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void handle(JoinPoint joinPoint, CheckSign checkSign) {
|
||||
// 验签
|
||||
verifySignature(checkSign);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验签
|
||||
*
|
||||
* @param checkSign 注解,包含签名字段名等配置
|
||||
*/
|
||||
private void verifySignature(CheckSign checkSign) {
|
||||
HttpServletRequest httpRequest = ((ServletRequestAttributes) Objects
|
||||
.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
|
||||
|
||||
// 获取调用方appKey,检查是否是受信任调用方
|
||||
String appKey = httpRequest.getHeader("appKey");
|
||||
if (StringUtils.isEmpty(appKey)) {
|
||||
throw new BizException(new RespCode("403", "验签失败"));
|
||||
}
|
||||
|
||||
// 从请求头中提取签名
|
||||
String sign = httpRequest.getHeader(checkSign.signField());
|
||||
if (StringUtils.isEmpty(sign)) {
|
||||
throw new BizException(new RespCode("403", "验签失败"));
|
||||
}
|
||||
|
||||
// 从Header中获取需要验签的参数字段,若字段值为空则该字段不计入签名计算
|
||||
Map<String, String> attendFields = new HashMap<>();
|
||||
// 无指定字段则所有字段参与计算签名
|
||||
if (ArrayUtil.isEmpty(checkSign.requireSignFields())) {
|
||||
Enumeration<String> headerNames = httpRequest.getHeaderNames();
|
||||
while (headerNames.hasMoreElements()) {
|
||||
String headerName = headerNames.nextElement();
|
||||
String value = httpRequest.getHeader(headerName);
|
||||
attendFields.put(headerName, value);
|
||||
}
|
||||
} else {
|
||||
// 有指定参与计算签名的字段,则只提取指定字段参与计算签名
|
||||
for (String field : checkSign.requireSignFields()) {
|
||||
String value = httpRequest.getHeader(field);
|
||||
attendFields.put(field, value);
|
||||
}
|
||||
}
|
||||
// 对所有参与签名计算的字段去除空值和去重后按照字段名的ASCII码从小到大排序(字典序)
|
||||
Map<String, Object> sortedAttendFields = attendFields.entrySet()
|
||||
.stream()
|
||||
.filter(entry -> !StringUtils.isEmpty(entry.getValue()))
|
||||
.distinct()
|
||||
.sorted(Map.Entry.comparingByKey())
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldValue, newValue) -> oldValue, LinkedHashMap<String, Object>::new));
|
||||
|
||||
// 拼接所有签名数据为字符串
|
||||
String data = StringUtil.extracted(sortedAttendFields);
|
||||
log.info("参与签名计算data:{}", data);
|
||||
|
||||
try {
|
||||
boolean verify = signManager.verify(appKey, data, sign);
|
||||
if (!verify) {
|
||||
throw new BizException(new RespCode("403", "验签失败"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new BizException(new RespCode("403", "验签失败"));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package cn.axzo.nanopart.api.common.util;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author chenwenjian
|
||||
* @version 1.0
|
||||
* @date 2024/1/4 10:53
|
||||
*/
|
||||
public class StringUtil {
|
||||
|
||||
/**
|
||||
* 将Map中的参数拼接成字符串
|
||||
*
|
||||
* @param params 键值对
|
||||
* @return 拼接后的字符串
|
||||
*/
|
||||
public static String extracted(Map<String, Object> params) {
|
||||
if (CollectionUtils.isEmpty(params)) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
StringBuilder data = new StringBuilder();
|
||||
for (Map.Entry<String, Object> entry : params.entrySet()) {
|
||||
if (data.length() > 0) {
|
||||
data.append("&");
|
||||
}
|
||||
data.append(entry.getKey()).append("=").append(entry.getValue().toString());
|
||||
}
|
||||
return data.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,9 +1,10 @@
|
||||
package cn.axzo.nanopart.api.config;
|
||||
|
||||
import cn.axzo.nanopart.api.constant.NanopartConstant;
|
||||
import cn.axzo.nanopart.api.constant.BlackAndWhiteListConstant;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
|
||||
@EnableFeignClients(NanopartConstant.BASIC_FEIGN_PACKAGE)
|
||||
@EnableFeignClients(BlackAndWhiteListConstant.BASIC_FEIGN_PACKAGE)
|
||||
public class NanopartApiAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
package cn.axzo.nanopart.api.config;
|
||||
|
||||
import cn.hutool.crypto.asymmetric.SignAlgorithm;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 签名配置
|
||||
*
|
||||
* @author chenwenjian
|
||||
* @version 1.0
|
||||
* @date 2024/1/2 14:33
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "api.sign")
|
||||
public class SignConfigProps {
|
||||
|
||||
/**
|
||||
* 是否启用签名
|
||||
*/
|
||||
private Boolean enable = true;
|
||||
|
||||
private Map<String, KeyPair> keyPairs;
|
||||
|
||||
@Data
|
||||
public static class KeyPair {
|
||||
/**
|
||||
* 签名算法
|
||||
*/
|
||||
private SignAlgorithm algorithm;
|
||||
|
||||
/**
|
||||
* 私钥
|
||||
*/
|
||||
private String privateKey;
|
||||
|
||||
|
||||
/**
|
||||
* 公钥
|
||||
*/
|
||||
private String publicKey;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
package cn.axzo.nanopart.api.config;
|
||||
|
||||
import cn.hutool.core.util.HexUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.asymmetric.Sign;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 签名管理器
|
||||
*
|
||||
* @author chenwenjian
|
||||
* @version 1.0
|
||||
* @date 2024/1/2 15:41
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class SignManager {
|
||||
|
||||
private final SignConfigProps signConfigProps;
|
||||
|
||||
/**
|
||||
* 生成签名
|
||||
* 生成的签名为十六进制字符串
|
||||
*
|
||||
* @param appKey 调用方
|
||||
* @param rawData 待签名数据,格式:所有需要参与签名计算的参数去重后按照字段名的ASCII码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串(不包含签名字段)
|
||||
* @return 签名
|
||||
*/
|
||||
public String sign(String appKey, String rawData) {
|
||||
Sign sign = getSignByAppKey(appKey);
|
||||
if (Objects.isNull(sign)) {
|
||||
return null;
|
||||
}
|
||||
return sign.signHex(rawData.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证签名
|
||||
*
|
||||
* @param appKey 调用方
|
||||
* @param rawData 待验证数据
|
||||
* @param rawSign 签名
|
||||
* @return true:验证成功 false:验证失败
|
||||
*/
|
||||
public boolean verify(String appKey, String rawData, String rawSign) {
|
||||
Sign sign = getSignByAppKey(appKey);
|
||||
if (Objects.isNull(sign)) {
|
||||
return false;
|
||||
}
|
||||
return sign.verify(rawData.getBytes(StandardCharsets.UTF_8), HexUtil.decodeHex(rawSign));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取签名
|
||||
*
|
||||
* @param appKey 调用方
|
||||
* @return 签名算法对象 {@link Sign}
|
||||
*/
|
||||
public Sign getSignByAppKey(String appKey) {
|
||||
if (CollectionUtils.isEmpty(signConfigProps.getKeyPairs())) {
|
||||
return null;
|
||||
}
|
||||
SignConfigProps.KeyPair keyPair = signConfigProps.getKeyPairs().get(appKey);
|
||||
if (Objects.isNull(keyPair)) {
|
||||
return null;
|
||||
}
|
||||
return SecureUtil.sign(keyPair.getAlgorithm(), keyPair.getPrivateKey(), keyPair.getPublicKey());
|
||||
}
|
||||
|
||||
}
|
||||
@ -7,7 +7,7 @@ package cn.axzo.nanopart.api.constant;
|
||||
* @modifiedBy:
|
||||
* @version: 1.0
|
||||
*/
|
||||
public class NanopartConstant {
|
||||
public class BlackAndWhiteListConstant {
|
||||
|
||||
public static final String BASIC_FEIGN_PACKAGE = "cn.axzo.nanopart.api";
|
||||
public static final String PHONE_REGEXP = "^1[3456789]\\d{9}$";
|
||||
@ -1,7 +1,7 @@
|
||||
package cn.axzo.nanopart.api.request;
|
||||
|
||||
|
||||
import cn.axzo.nanopart.api.constant.NanopartConstant;
|
||||
import cn.axzo.nanopart.api.constant.BlackAndWhiteListConstant;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
@ -21,6 +21,6 @@ public class BlackAndWhiteListInReq {
|
||||
@NotBlank(message = "模块名称不能为空格")
|
||||
private String module;
|
||||
|
||||
@Pattern(regexp = NanopartConstant.PHONE_REGEXP,message = "手机号格式错误")
|
||||
@Pattern(regexp = BlackAndWhiteListConstant.PHONE_REGEXP,message = "手机号格式错误")
|
||||
private String phone;
|
||||
}
|
||||
|
||||
@ -0,0 +1,53 @@
|
||||
package cn.axzo.nanopart.api.request;
|
||||
|
||||
import cn.axzo.nanopart.api.constant.enums.ListTypeEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 黑白名单内部自同步请求
|
||||
*
|
||||
* @author chenwenjian
|
||||
* @version 1.0
|
||||
* @date 2024/1/3 16:46
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BlackAndWhiteListInternalSyncReq {
|
||||
|
||||
@NotNull(message = "同步类型不能为空")
|
||||
private ListTypeEnum type;
|
||||
|
||||
/**
|
||||
* 同步源模块
|
||||
*/
|
||||
@NotBlank(message = "同步源模块不能为空")
|
||||
private String sourceModule;
|
||||
|
||||
/**
|
||||
* 同步源字段
|
||||
*/
|
||||
@NotEmpty(message = "同步源字段不能为空")
|
||||
private List<String> sourceFields;
|
||||
|
||||
/**
|
||||
* 同步目标模块
|
||||
*/
|
||||
@NotBlank(message = "同步目标模块不能为空")
|
||||
private String targetModule;
|
||||
|
||||
/**
|
||||
* 同步目标字段
|
||||
*/
|
||||
@NotEmpty(message = "同步目标字段不能为空")
|
||||
private List<String> targetFields;
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
package cn.axzo.nanopart.api.request;
|
||||
|
||||
import cn.axzo.nanopart.api.constant.enums.ListTypeEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 黑白名单平台同步请求
|
||||
*
|
||||
* @author chenwenjian
|
||||
* @version 1.0
|
||||
* @date 2024/1/2 18:17
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BlackAndWhiteListPlatformSyncReq {
|
||||
|
||||
/**
|
||||
* 名单类型,0为黑名单,1为白名单
|
||||
*/
|
||||
@NotNull(message = "名单类型错误")
|
||||
private ListTypeEnum type;
|
||||
|
||||
/**
|
||||
* 模块名,自主定义,例:cms-login
|
||||
*/
|
||||
@NotBlank(message = "模块名不能为空")
|
||||
private String module;
|
||||
|
||||
/**
|
||||
* 参数,例:[{"name":"username","value":"admin"},{"name":"password","value":"123456"}]
|
||||
* 单次同步数量最多1000条
|
||||
*/
|
||||
@NotEmpty(message = "参数不能为空")
|
||||
private List<Map<String, Object>> params;
|
||||
|
||||
/**
|
||||
* 分片号,从1开始,默认是1,若等于1,则先删除所有上次同步的名单,然后新增本次的名单,若大于1时,则直接新增到黑白名单中
|
||||
*/
|
||||
@Min(value = 1, message = "分片号必须大于1")
|
||||
@NotNull(message = "分片大小不能为空")
|
||||
private Long fragment = 1L;
|
||||
|
||||
/**
|
||||
* 是否同步至登录黑名单
|
||||
*/
|
||||
private Boolean isSyncLogin;
|
||||
|
||||
/**
|
||||
* 同步至登录黑名单时params中代表手机号的字段名,例:userPhone
|
||||
* <p>
|
||||
* 因为目前登录黑名单以手机号作为判断依据,所以当isSyncLogin为true时此字段必填
|
||||
* </p>
|
||||
*/
|
||||
private String syncLoginField;
|
||||
|
||||
/**
|
||||
* 是否同步至注册黑名单
|
||||
*/
|
||||
private Boolean isSyncRegister;
|
||||
|
||||
/**
|
||||
* 同步至注册黑名单时params中代表手机号的字段名,例:userPhone
|
||||
* <p>
|
||||
* 因为目前注册黑名单以手机号作为判断依据,所以当isSyncRegister为true时此字段必填
|
||||
* </p>
|
||||
*/
|
||||
private String syncRegisterField;
|
||||
}
|
||||
@ -1,7 +1,10 @@
|
||||
package cn.axzo.nanopart.api.request;
|
||||
|
||||
import cn.axzo.nanopart.api.constant.enums.ListTypeEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
@ -15,6 +18,9 @@ import java.util.Map;
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BlackAndWhiteListReq {
|
||||
|
||||
/**
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
package cn.axzo.nanopart.api.request;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 签名获取请求
|
||||
* @author chenwenjian
|
||||
* @version 1.0
|
||||
* @date 2024/1/4 10:15
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SignatureGetReq {
|
||||
|
||||
/**
|
||||
* 应用key,例如:nanopart
|
||||
*/
|
||||
@NotBlank(message = "appKey不能为空")
|
||||
private String appKey;
|
||||
|
||||
/**
|
||||
* 请求参数,需要参与计算签名的数据,例如:{"id":123456,"name":"张三"}
|
||||
*/
|
||||
@NotEmpty(message = "params不能为空")
|
||||
private Map<String,Object> params;
|
||||
}
|
||||
@ -33,5 +33,13 @@
|
||||
<artifactId>black-list-api</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-context</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.axzo.pokonyan</groupId>
|
||||
<artifactId>pokonyan</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
package cn.axzo.nanopart.server.constant;
|
||||
|
||||
/**
|
||||
* @author chenwenjian
|
||||
* @version 1.0
|
||||
* @date 2024/1/3 11:28
|
||||
*/
|
||||
public class BlackAndWhiteListConstant {
|
||||
|
||||
/**
|
||||
* 所有平台登录黑名单模块名
|
||||
*/
|
||||
public static final String ALL_LOGIN = "all-login";
|
||||
|
||||
/**
|
||||
* 企业注册黑名单模块名
|
||||
*/
|
||||
public static final String OU_REGISTER_BLACK_LIST = "ou_register_black_list";
|
||||
}
|
||||
@ -3,13 +3,17 @@ package cn.axzo.nanopart.server.controller;
|
||||
|
||||
import cn.axzo.framework.domain.web.result.ApiResult;
|
||||
import cn.axzo.nanopart.api.BlackAndWhiteListApi;
|
||||
import cn.axzo.nanopart.api.annotation.CheckSign;
|
||||
import cn.axzo.nanopart.api.constant.enums.ListTypeEnum;
|
||||
import cn.axzo.nanopart.api.request.BlackAndWhiteListExcelImportReq;
|
||||
import cn.axzo.nanopart.api.request.BlackAndWhiteListInternalSyncReq;
|
||||
import cn.axzo.nanopart.api.request.BlackAndWhiteListPlatformSyncReq;
|
||||
import cn.axzo.nanopart.api.request.BlackAndWhiteListReq;
|
||||
import cn.axzo.nanopart.api.response.BlackAndWhiteListExcelImportResp;
|
||||
import cn.axzo.nanopart.api.response.BlackAndWhiteListResp;
|
||||
import cn.axzo.nanopart.server.dao.entity.SaasBlackWhiteList;
|
||||
import cn.axzo.nanopart.server.service.BlackAndWhiteListService;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.alibaba.excel.context.AnalysisContext;
|
||||
import com.alibaba.excel.event.AnalysisEventListener;
|
||||
@ -44,12 +48,12 @@ public class BlackAndWhiteListController implements BlackAndWhiteListApi {
|
||||
|
||||
@Override
|
||||
public ApiResult<Long> create(BlackAndWhiteListReq req) {
|
||||
return blackAndWhiteListService.create(req);
|
||||
return ApiResult.ok(blackAndWhiteListService.create(req));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResult<Void> delete(BlackAndWhiteListReq req) {
|
||||
return blackAndWhiteListService.delete(req);
|
||||
return ApiResult.ok(blackAndWhiteListService.delete(req));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -59,12 +63,12 @@ public class BlackAndWhiteListController implements BlackAndWhiteListApi {
|
||||
|
||||
@Override
|
||||
public ApiResult<List<BlackAndWhiteListResp>> detail(BlackAndWhiteListReq req) {
|
||||
return blackAndWhiteListService.detail(req);
|
||||
return ApiResult.ok(blackAndWhiteListService.detail(req));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResult<Boolean> isInBlackOrWhiteList(BlackAndWhiteListReq req) {
|
||||
return blackAndWhiteListService.isInBlackOrWhiteList(req);
|
||||
return ApiResult.ok(blackAndWhiteListService.isInBlackOrWhiteList(req));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -73,4 +77,29 @@ public class BlackAndWhiteListController implements BlackAndWhiteListApi {
|
||||
return ApiResult.ok(blackAndWhiteListService.importBlackAndWhiteListExcel(type, module, url, file));
|
||||
}
|
||||
|
||||
/**
|
||||
* 平台同步黑白名单
|
||||
*
|
||||
* @param req @{link BlackAndWhiteListPlatformSyncReq}
|
||||
* @return
|
||||
*/
|
||||
@CheckSign(requireSignFields = {"appKey", "timestamp"})
|
||||
@Override
|
||||
public ApiResult<Boolean> platformSync(BlackAndWhiteListPlatformSyncReq req) {
|
||||
log.info("平台同步参数:req = {}", JSONUtil.toJsonStr(req));
|
||||
return ApiResult.ok(blackAndWhiteListService.platformSync(req));
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部自同步黑白名单
|
||||
*
|
||||
* @param req @{link BlackAndWhiteListInternalSyncReq}
|
||||
* @return 同步结果的记录id
|
||||
*/
|
||||
@CheckSign(requireSignFields = {"appKey", "timestamp"})
|
||||
@Override
|
||||
public ApiResult<Long> internalSync(BlackAndWhiteListInternalSyncReq req) {
|
||||
return ApiResult.ok(blackAndWhiteListService.internalSync(req));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,66 @@
|
||||
package cn.axzo.nanopart.server.controller;
|
||||
|
||||
import cn.axzo.framework.domain.ServiceException;
|
||||
import cn.axzo.framework.domain.web.result.ApiResult;
|
||||
import cn.axzo.nanopart.api.SignatureUtilApi;
|
||||
import cn.axzo.nanopart.api.config.SignManager;
|
||||
import cn.axzo.nanopart.api.request.SignatureGetReq;
|
||||
import cn.axzo.nanopart.api.common.util.StringUtil;
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author chenwenjian
|
||||
* @version 1.0
|
||||
* @date 2024/1/2 16:59
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class SignatureUtilController implements SignatureUtilApi {
|
||||
|
||||
private final SignManager signManager;
|
||||
|
||||
/**
|
||||
* 生成密钥对
|
||||
*
|
||||
* @param algorithm 算法
|
||||
* @return 密钥对
|
||||
*/
|
||||
@Override
|
||||
public ApiResult<Map<String, String>> generateKeyPair(String algorithm) {
|
||||
log.info("algorithm = {}", algorithm);
|
||||
Map<String, String> keyPair = new HashMap<>();
|
||||
try {
|
||||
KeyPair generateKeyPair = SecureUtil.generateKeyPair(algorithm);
|
||||
keyPair.put("publicKey", Base64.encode(generateKeyPair.getPublic().getEncoded()));
|
||||
keyPair.put("privateKey", Base64.encode(generateKeyPair.getPrivate().getEncoded()));
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException("密钥对生成失败");
|
||||
}
|
||||
log.info("keyPair = {}", JSONUtil.toJsonStr(keyPair));
|
||||
return ApiResult.ok(keyPair);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取签名
|
||||
*
|
||||
* @param req 请求
|
||||
* @return 签名
|
||||
*/
|
||||
@Override
|
||||
public ApiResult<String> getSignature(SignatureGetReq req) {
|
||||
String data = StringUtil.extracted(req.getParams());
|
||||
return ApiResult.ok(signManager.sign(req.getAppKey(), data));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -1,11 +1,15 @@
|
||||
package cn.axzo.nanopart.server.dao.entity;
|
||||
|
||||
|
||||
import cn.axzo.framework.data.mybatisplus.model.BaseEntity;
|
||||
import cn.axzo.pokonyan.config.mybatisplus.BaseEntity;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@ -16,7 +20,11 @@ import java.util.Map;
|
||||
* @modifiedBy:
|
||||
* @version: 1.0
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Data
|
||||
@Builder
|
||||
@TableName(value = "saas_black_white_list",autoResultMap = true)
|
||||
public class SaasBlackWhiteList extends BaseEntity<SaasBlackWhiteList> {
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ import cn.axzo.nanopart.server.dao.entity.SaasBlackWhiteList;
|
||||
import cn.axzo.nanopart.server.dao.mapper.BlackAndWhiteListMapper;
|
||||
import cn.azxo.framework.common.utils.StringUtils;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@ -35,9 +36,6 @@ public class BlackAndWhiteListRepository extends ServiceImpl<BlackAndWhiteListMa
|
||||
|
||||
public Long create(BlackAndWhiteListReq req) {
|
||||
SaasBlackWhiteList blackWhiteList = BeanMapper.copyBean(req, SaasBlackWhiteList.class, (req1, saasBlackWhiteList) -> saasBlackWhiteList.setType(req1.getType().getValue()));
|
||||
Date nowTime = new Date(System.currentTimeMillis());
|
||||
blackWhiteList.setCreateAt(nowTime);
|
||||
blackWhiteList.setUpdateAt(nowTime);
|
||||
blackAndWhiteListMapper.insert(blackWhiteList);
|
||||
return blackWhiteList.getId();
|
||||
}
|
||||
@ -50,7 +48,7 @@ public class BlackAndWhiteListRepository extends ServiceImpl<BlackAndWhiteListMa
|
||||
.eq(SaasBlackWhiteList::getIsDelete, 0)
|
||||
.set(SaasBlackWhiteList::getIsDelete, saasBlackWhiteList.getId())
|
||||
.update();
|
||||
log.info("deleted record:{}", JSONObject.valueToString(saasBlackWhiteList));
|
||||
log.info("deleted record:{}", JSONUtil.toJsonStr(saasBlackWhiteList));
|
||||
});
|
||||
}
|
||||
return null;
|
||||
@ -83,4 +81,50 @@ public class BlackAndWhiteListRepository extends ServiceImpl<BlackAndWhiteListMa
|
||||
.set(SaasBlackWhiteList::getIsDelete, 1)
|
||||
.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* 按模块删除名单
|
||||
*
|
||||
* @param type 名单类型
|
||||
* @param module 模块名
|
||||
* @return 删除结果,true:删除成功,false:删除失败
|
||||
*/
|
||||
public Boolean deleteByModule(Integer type, String module) {
|
||||
return lambdaUpdate()
|
||||
.eq(SaasBlackWhiteList::getType, type)
|
||||
.eq(SaasBlackWhiteList::getModule, module)
|
||||
.eq(SaasBlackWhiteList::getIsDelete, 0)
|
||||
.set(SaasBlackWhiteList::getIsDelete, 1)
|
||||
.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新名单参数
|
||||
*
|
||||
* @param id 名单id
|
||||
* @param param 名单参数
|
||||
* @return 更新结果,true:更新成功,false:更新失败
|
||||
*/
|
||||
public boolean updateParamById(Long id, Map<String, Object> param) {
|
||||
return lambdaUpdate()
|
||||
.eq(SaasBlackWhiteList::getId, id)
|
||||
.eq(SaasBlackWhiteList::getIsDelete, 0)
|
||||
.set(SaasBlackWhiteList::getParam, JSONUtil.toJsonStr(param))
|
||||
.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据模块名获取名单
|
||||
*
|
||||
* @param type 名单类型
|
||||
* @param module 模块名
|
||||
* @return 名单列表
|
||||
*/
|
||||
public List<SaasBlackWhiteList> getListByModule(Integer type, String module) {
|
||||
return lambdaQuery()
|
||||
.eq(SaasBlackWhiteList::getType, type)
|
||||
.eq(SaasBlackWhiteList::getModule, module)
|
||||
.eq(SaasBlackWhiteList::getIsDelete, 0)
|
||||
.list();
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,8 @@ package cn.axzo.nanopart.server.service;
|
||||
|
||||
import cn.axzo.framework.domain.web.result.ApiResult;
|
||||
import cn.axzo.nanopart.api.constant.enums.ListTypeEnum;
|
||||
import cn.axzo.nanopart.api.request.BlackAndWhiteListInternalSyncReq;
|
||||
import cn.axzo.nanopart.api.request.BlackAndWhiteListPlatformSyncReq;
|
||||
import cn.axzo.nanopart.api.request.BlackAndWhiteListReq;
|
||||
import cn.axzo.nanopart.api.response.BlackAndWhiteListExcelImportResp;
|
||||
import cn.axzo.nanopart.api.response.BlackAndWhiteListResp;
|
||||
@ -19,15 +21,19 @@ import java.util.List;
|
||||
*/
|
||||
public interface BlackAndWhiteListService {
|
||||
|
||||
ApiResult<Long> create(BlackAndWhiteListReq req);
|
||||
Long create(BlackAndWhiteListReq req);
|
||||
|
||||
ApiResult<Void> delete(BlackAndWhiteListReq req);
|
||||
Void delete(BlackAndWhiteListReq req);
|
||||
|
||||
ApiResult<List<BlackAndWhiteListResp>> detail(BlackAndWhiteListReq req);
|
||||
List<BlackAndWhiteListResp> detail(BlackAndWhiteListReq req);
|
||||
|
||||
ApiResult<Boolean> isInBlackOrWhiteList(BlackAndWhiteListReq req);
|
||||
Boolean isInBlackOrWhiteList(BlackAndWhiteListReq req);
|
||||
|
||||
BlackAndWhiteListExcelImportResp importBlackAndWhiteListExcel(ListTypeEnum type, String module, String url, MultipartFile file);
|
||||
|
||||
Boolean deleteByIds(List<Long> ids);
|
||||
|
||||
Boolean platformSync(BlackAndWhiteListPlatformSyncReq req);
|
||||
|
||||
Long internalSync(BlackAndWhiteListInternalSyncReq req);
|
||||
}
|
||||
|
||||
@ -2,20 +2,26 @@ package cn.axzo.nanopart.server.service.impl;
|
||||
|
||||
import cn.axzo.basics.common.BeanMapper;
|
||||
import cn.axzo.basics.common.exception.ServiceException;
|
||||
import cn.axzo.framework.domain.web.result.ApiResult;
|
||||
import cn.axzo.nanopart.api.constant.enums.ListTypeEnum;
|
||||
import cn.axzo.nanopart.api.request.BlackAndWhiteListInternalSyncReq;
|
||||
import cn.axzo.nanopart.api.request.BlackAndWhiteListPlatformSyncReq;
|
||||
import cn.axzo.nanopart.api.request.BlackAndWhiteListReq;
|
||||
import cn.axzo.nanopart.api.response.BlackAndWhiteListExcelImportResp;
|
||||
import cn.axzo.nanopart.api.response.BlackAndWhiteListResp;
|
||||
import cn.axzo.nanopart.server.constant.BlackAndWhiteListConstant;
|
||||
import cn.axzo.nanopart.server.dao.entity.SaasBlackWhiteList;
|
||||
import cn.axzo.nanopart.server.dao.repository.BlackAndWhiteListRepository;
|
||||
import cn.axzo.nanopart.server.service.BlackAndWhiteListService;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.alibaba.excel.context.AnalysisContext;
|
||||
import com.alibaba.excel.event.AnalysisEventListener;
|
||||
import com.google.common.collect.Lists;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
@ -24,11 +30,14 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@ -40,11 +49,18 @@ import java.util.stream.Collectors;
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RefreshScope
|
||||
@RequiredArgsConstructor
|
||||
public class BlackAndWhiteListServiceImpl implements BlackAndWhiteListService {
|
||||
|
||||
private final BlackAndWhiteListRepository blackAndWhiteListRepository;
|
||||
|
||||
/**
|
||||
* 黑白名单外部平台单次最大同步记录数
|
||||
*/
|
||||
@Value("${sync.fragmentSize:1000}")
|
||||
private Long fragmentSize;
|
||||
|
||||
/**
|
||||
* 新增一条黑白名单记录
|
||||
*
|
||||
@ -52,17 +68,17 @@ public class BlackAndWhiteListServiceImpl implements BlackAndWhiteListService {
|
||||
* @return 记录id
|
||||
*/
|
||||
@Override
|
||||
public ApiResult<Long> create(BlackAndWhiteListReq req) {
|
||||
public Long create(BlackAndWhiteListReq req) {
|
||||
// 黑白名单创建互斥,若存在于黑名单中则不允许存在于白名单中,反之亦然
|
||||
BlackAndWhiteListReq reverseReq = BeanMapper.copyBean(req, BlackAndWhiteListReq.class, (req1, req2) -> req2.setType((req1.getType().equals(ListTypeEnum.BLACK_LIST)) ? ListTypeEnum.WHITE_LIST : ListTypeEnum.BLACK_LIST));
|
||||
if (isInBlackOrWhiteList(reverseReq).getData()) {
|
||||
if (isInBlackOrWhiteList(reverseReq)) {
|
||||
throw new ServiceException("该记录已存在于" + reverseReq.getType().getDescription() + "中,不能创建对应" + req.getType().getDescription());
|
||||
}
|
||||
// 同类型名单内唯一性校验
|
||||
if (isInBlackOrWhiteList(req).getData()) {
|
||||
if (isInBlackOrWhiteList(req)) {
|
||||
throw new ServiceException("该记录已存在于" + req.getType().getDescription() + "中");
|
||||
}
|
||||
return ApiResult.ok(blackAndWhiteListRepository.create(req));
|
||||
return blackAndWhiteListRepository.create(req);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -72,8 +88,8 @@ public class BlackAndWhiteListServiceImpl implements BlackAndWhiteListService {
|
||||
* @return null
|
||||
*/
|
||||
@Override
|
||||
public ApiResult<Void> delete(BlackAndWhiteListReq req) {
|
||||
return ApiResult.ok(blackAndWhiteListRepository.delete(req));
|
||||
public Void delete(BlackAndWhiteListReq req) {
|
||||
return blackAndWhiteListRepository.delete(req);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,10 +99,9 @@ public class BlackAndWhiteListServiceImpl implements BlackAndWhiteListService {
|
||||
* @return 记录列表
|
||||
*/
|
||||
@Override
|
||||
public ApiResult<List<BlackAndWhiteListResp>> detail(BlackAndWhiteListReq req) {
|
||||
public List<BlackAndWhiteListResp> detail(BlackAndWhiteListReq req) {
|
||||
List<SaasBlackWhiteList> saasBlackWhiteLists = blackAndWhiteListRepository.detail(req);
|
||||
List<BlackAndWhiteListResp> blackAndWhiteListResps = BeanMapper.copyList(saasBlackWhiteLists, BlackAndWhiteListResp.class);
|
||||
return ApiResult.ok(blackAndWhiteListResps);
|
||||
return BeanMapper.copyList(saasBlackWhiteLists, BlackAndWhiteListResp.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -96,9 +111,9 @@ public class BlackAndWhiteListServiceImpl implements BlackAndWhiteListService {
|
||||
* @return 记录存在返回true,否则返回false
|
||||
*/
|
||||
@Override
|
||||
public ApiResult<Boolean> isInBlackOrWhiteList(BlackAndWhiteListReq req) {
|
||||
public Boolean isInBlackOrWhiteList(BlackAndWhiteListReq req) {
|
||||
List<SaasBlackWhiteList> blackWhiteLists = blackAndWhiteListRepository.detail(req);
|
||||
return ApiResult.ok(CollectionUtil.isNotEmpty(blackWhiteLists));
|
||||
return CollectionUtil.isNotEmpty(blackWhiteLists);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -144,6 +159,123 @@ public class BlackAndWhiteListServiceImpl implements BlackAndWhiteListService {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 平台同步黑白名单
|
||||
*
|
||||
* @param req {@link BlackAndWhiteListPlatformSyncReq}
|
||||
* @return true-同步成功,false-同步失败
|
||||
*/
|
||||
@Override
|
||||
public Boolean platformSync(BlackAndWhiteListPlatformSyncReq req) {
|
||||
if (Objects.isNull(req.getType())) {
|
||||
throw new ServiceException("请选择正确的名单类型");
|
||||
}
|
||||
if (req.getParams().size() > fragmentSize) {
|
||||
throw new ServiceException("请主动进行分片多次同步,单次同步数小于" + fragmentSize + "条数据");
|
||||
}
|
||||
// 第一个分片,需要先删除所有对应模块的名单,再新增
|
||||
if (req.getFragment() == 1 ){
|
||||
deleteByModule(req.getType(),req.getModule());
|
||||
}
|
||||
// 新增
|
||||
List<SaasBlackWhiteList> collect = req.getParams().stream().map(param -> SaasBlackWhiteList.builder()
|
||||
.type(req.getType().getValue())
|
||||
.module(req.getModule())
|
||||
.param(param)
|
||||
.build()).collect(Collectors.toList());
|
||||
boolean saved = blackAndWhiteListRepository.saveBatch(collect);
|
||||
// 同步至登录黑名单
|
||||
if (Objects.nonNull(req.getIsSyncLogin()) && req.getIsSyncLogin()){
|
||||
internalSync(BlackAndWhiteListInternalSyncReq.builder()
|
||||
.type(req.getType())
|
||||
.sourceModule(req.getModule())
|
||||
.sourceFields(Collections.singletonList(req.getSyncLoginField()))
|
||||
.targetModule(BlackAndWhiteListConstant.ALL_LOGIN).build());
|
||||
}
|
||||
// 同步至注册黑名单
|
||||
if (Objects.nonNull(req.getIsSyncRegister()) && req.getIsSyncRegister()){
|
||||
internalSync(BlackAndWhiteListInternalSyncReq.builder()
|
||||
.type(req.getType())
|
||||
.sourceModule(req.getModule())
|
||||
.sourceFields(Collections.singletonList(req.getSyncRegisterField()))
|
||||
.targetModule(BlackAndWhiteListConstant.OU_REGISTER_BLACK_LIST).build());
|
||||
}
|
||||
return saved;
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部自同步黑白名单
|
||||
*
|
||||
* @param req {@link BlackAndWhiteListInternalSyncReq}
|
||||
* @return 同步成功的记录总条数
|
||||
*/
|
||||
public Long internalSync(BlackAndWhiteListInternalSyncReq req) {
|
||||
AtomicReference<Long> count = new AtomicReference<>(0L);
|
||||
// 获取所有源记录
|
||||
List<SaasBlackWhiteList> sourceList = blackAndWhiteListRepository.getListByModule(req.getType().getValue(), req.getSourceModule());
|
||||
|
||||
sourceList.stream().map(s -> {
|
||||
// 抽取出需要同步的字段值
|
||||
HashMap<String, Object> map = new HashMap<>();
|
||||
req.getSourceFields().forEach(sf -> map.put(sf, s.getParam().get(sf)));
|
||||
return map;
|
||||
}).forEach(m -> {
|
||||
// 判断是否存在目标记录,不存在则新增一条
|
||||
if (!isInBlackOrWhiteList(BlackAndWhiteListReq.builder()
|
||||
.type(req.getType())
|
||||
.module(req.getTargetModule())
|
||||
.param(m)
|
||||
.build())) {
|
||||
create(BlackAndWhiteListReq.builder()
|
||||
.type(req.getType())
|
||||
.module(req.getTargetModule())
|
||||
.param(m)
|
||||
.build());
|
||||
count.getAndSet(count.get() + 1);
|
||||
}
|
||||
});
|
||||
return count.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定模块名的所有黑/白名单记录
|
||||
*
|
||||
* @param type 名单类型
|
||||
* @param module 模块名
|
||||
* @return 黑/白名单记录列表
|
||||
*/
|
||||
public List<BlackAndWhiteListResp> getListByModule(ListTypeEnum type, String module) {
|
||||
return BeanMapper.copyList(blackAndWhiteListRepository.getListByModule(type.getValue(), module), BlackAndWhiteListResp.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新指定id的黑白名单记录
|
||||
*
|
||||
* @param id 记录id
|
||||
* @param param 记录参数
|
||||
* @return true-更新成功,false-更新失败
|
||||
*/
|
||||
public boolean updateParamById(Long id, Map<String, Object> param) {
|
||||
return blackAndWhiteListRepository.updateParamById(id, param);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定模块名的所有黑/白名单记录
|
||||
*
|
||||
* @param type 名单类型
|
||||
* @param module 模块名
|
||||
* @return true-删除成功,false-删除失败
|
||||
*/
|
||||
public Boolean deleteByModule(ListTypeEnum type, String module) {
|
||||
if (Objects.isNull(type)) {
|
||||
throw new ServiceException("待删除名单类型不能为空");
|
||||
}
|
||||
if (!StringUtils.hasText(module)) {
|
||||
throw new ServiceException("待删除模块名不能为空");
|
||||
}
|
||||
return blackAndWhiteListRepository.deleteByModule(type.getValue(), module);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用EasyExcel读取Excel数据
|
||||
*
|
||||
|
||||
@ -4,12 +4,14 @@ import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
|
||||
@MapperScan(value = {"cn.axzo.**.mapper"})
|
||||
@SpringBootApplication
|
||||
@EnableFeignClients(basePackages = {
|
||||
"cn.axzo.nanopart.api"
|
||||
})
|
||||
@EnableAspectJAutoProxy()
|
||||
public class NanopartApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(NanopartApplication.class, args);
|
||||
|
||||
@ -4,7 +4,7 @@ spring:
|
||||
cloud:
|
||||
nacos:
|
||||
config:
|
||||
server-addr: ${NACOS_HOST:dev-nacos.axzo.cn}:${NACOS_PORT:80}
|
||||
server-addr: ${NACOS_HOST:https://dev-nacos.axzo.cn}:${NACOS_PORT:443}
|
||||
file-extension: yaml
|
||||
namespace: ${NACOS_NAMESPACE_ID:35eada10-9574-4db8-9fea-bc6a4960b6c7}
|
||||
prefix: ${spring.application.name}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user