This commit is contained in:
六如
2024-09-10 21:01:57 +08:00
parent 5e8a5d7133
commit d4823ae473
32 changed files with 812 additions and 272 deletions

55
pom.xml
View File

@@ -20,7 +20,7 @@
<!-- <module>sop-common</module>--> <!-- <module>sop-common</module>-->
<!-- <module>sop-auth</module>--> <!-- <module>sop-auth</module>-->
<module>sop-example</module> <module>sop-example</module>
<module>sop-admin</module> <!-- <module>sop-admin</module>-->
<!-- <module>sop-gateway</module>--> <!-- <module>sop-gateway</module>-->
<module>sop-test</module> <module>sop-test</module>
<module>sop-sdk</module> <module>sop-sdk</module>
@@ -47,8 +47,6 @@
<!-- dubbo版本 --> <!-- dubbo版本 -->
<dubbo.version>3.2.10</dubbo.version> <dubbo.version>3.2.10</dubbo.version>
<logback.version>1.2.3</logback.version>
<junit.version>4.11</junit.version> <junit.version>4.11</junit.version>
<fastjson.version>1.2.83</fastjson.version> <fastjson.version>1.2.83</fastjson.version>
<commons-io.version>2.5</commons-io.version> <commons-io.version>2.5</commons-io.version>
@@ -60,11 +58,7 @@
<validation-api.version>2.0.1.Final</validation-api.version> <validation-api.version>2.0.1.Final</validation-api.version>
<hibernate-validator.version>6.0.13.Final</hibernate-validator.version> <hibernate-validator.version>6.0.13.Final</hibernate-validator.version>
<fastmybatis.version>3.0.10</fastmybatis.version> <fastmybatis.version>3.0.10</fastmybatis.version>
<mybatis-plus.version>3.5.3.1</mybatis-plus.version>
<guava.version>29.0-jre</guava.version> <guava.version>29.0-jre</guava.version>
<knife4j.version>3.0.2</knife4j.version>
<swagger.version>1.5.21</swagger.version>
<springfox.version>3.0.0</springfox.version>
<easyopen.version>1.16.9</easyopen.version> <easyopen.version>1.16.9</easyopen.version>
<asm.version>6.2</asm.version> <asm.version>6.2</asm.version>
<pagehelper.version>5.2.0</pagehelper.version> <pagehelper.version>5.2.0</pagehelper.version>
@@ -125,51 +119,12 @@
<version>${fastmybatis.version}</version> <version>${fastmybatis.version}</version>
</dependency> </dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${springfox.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-spring-web</artifactId>
<version>${springfox.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${springfox.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>${springfox.version}</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
<dependency>
<groupId>net.oschina.durcframework</groupId>
<artifactId>easyopen</artifactId>
<version>${easyopen.version}</version>
</dependency>
<dependency> <dependency>
<groupId>com.squareup.okhttp3</groupId> <groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId> <artifactId>okhttp</artifactId>
<version>3.14.7</version> <version>3.14.7</version>
</dependency> </dependency>
<dependency>
<groupId>net.oschina.durcframework</groupId>
<artifactId>easyopen-spring-boot-starter</artifactId>
<version>${easyopen.version}</version>
</dependency>
<dependency> <dependency>
<groupId>javax.validation</groupId> <groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId> <artifactId>validation-api</artifactId>
@@ -181,12 +136,6 @@
<version>${hibernate-validator.version}</version> <version>${hibernate-validator.version}</version>
</dependency> </dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<!-- commons --> <!-- commons -->
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
@@ -234,7 +183,7 @@
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
<version>1.18.4</version> <version>1.18.30</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>javax.servlet</groupId> <groupId>javax.servlet</groupId>

View File

@@ -15,4 +15,12 @@ public interface StoryService {
@Open("story.get") @Open("story.get")
StoryResponse getById(Integer id); StoryResponse getById(Integer id);
@Open("story.find")
default StoryResponse getById(Integer id, String name) {
StoryResponse storyResponse = new StoryResponse();
storyResponse.setId(id);
storyResponse.setName(name);
return storyResponse;
}
} }

View File

@@ -21,9 +21,12 @@ public class RegisterDTO implements Serializable {
private String methodName; private String methodName;
private String paramName; /**
* 字段信息
private String paramTypeName; *
* [{"name":"id": "type":"java.lang.Integer"}]
*/
private String paramInfo;
private Integer isPermission; private Integer isPermission;

View File

@@ -48,6 +48,7 @@
<groupId>io.gitee.durcframework</groupId> <groupId>io.gitee.durcframework</groupId>
<artifactId>fastmybatis-spring-boot-starter</artifactId> <artifactId>fastmybatis-spring-boot-starter</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>mysql-connector-java</artifactId>
@@ -80,13 +81,6 @@
<!-- provided --> <!-- provided -->
<!-- 仅在开发中使用 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>provided</scope>
</dependency>
<!-- 仅在开发中使用 --> <!-- 仅在开发中使用 -->
<dependency> <dependency>
<groupId>org.apache.dubbo</groupId> <groupId>org.apache.dubbo</groupId>

View File

@@ -21,11 +21,7 @@ public class ApiInfoDTO implements Serializable {
private String methodName; private String methodName;
private String paramName; private String paramInfo;
private String paramTypeName;
private Integer isIgnoreValidate;
private Integer isPermission; private Integer isPermission;

View File

@@ -67,17 +67,17 @@ public class ApiResponse {
/** /**
* 网关异常信息 * 网关异常信息
*/ */
private String msg; private String msg = "";
/** /**
* 业务异常码 * 业务异常码
*/ */
private String sub_code; private String sub_code = "";
/** /**
* 业务异常信息 * 业务异常信息
*/ */
private String sub_msg; private String sub_msg = "";
/** /**
* 返回对象 * 返回对象

View File

@@ -0,0 +1,9 @@
package com.gitee.sop.index.common;
import lombok.Data;
@Data
public class ParamInfoDTO {
private String name;
private String type;
}

View File

@@ -10,7 +10,7 @@ import lombok.Getter;
@Getter @Getter
public enum StatusEnum { public enum StatusEnum {
ENABLE(1), ENABLE(1),
DISABLE(0); DISABLE(2);
private final int value; private final int value;

View File

@@ -3,8 +3,10 @@ package com.gitee.sop.index.config;
import com.gitee.sop.index.message.ErrorFactory; import com.gitee.sop.index.message.ErrorFactory;
import com.gitee.sop.index.service.manager.impl.LocalApiCacheManagerImpl; import com.gitee.sop.index.service.manager.impl.LocalApiCacheManagerImpl;
import com.gitee.sop.index.service.manager.impl.LocalIsvManagerImpl; import com.gitee.sop.index.service.manager.impl.LocalIsvManagerImpl;
import com.gitee.sop.index.service.manager.impl.LocalSecretManagerImpl;
import com.gitee.sop.index.service.manager.impl.RedisApiCacheManagerImpl; import com.gitee.sop.index.service.manager.impl.RedisApiCacheManagerImpl;
import com.gitee.sop.index.service.manager.impl.RedisIsvManagerImpl; import com.gitee.sop.index.service.manager.impl.RedisIsvManagerImpl;
import com.gitee.sop.index.service.manager.impl.RedisSecretManager;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@@ -43,6 +45,18 @@ public class IndexConfig {
return new RedisIsvManagerImpl(); return new RedisIsvManagerImpl();
} }
@Bean
@ConditionalOnProperty(value = "manager.secret", havingValue = "local", matchIfMissing = true)
public LocalSecretManagerImpl localSecretManager() {
return new LocalSecretManagerImpl();
}
@Bean
@ConditionalOnProperty(value = "manager.secret", havingValue = "redis")
public RedisSecretManager redisSecretManager() {
return new RedisSecretManager();
}
@PostConstruct @PostConstruct
public void init() { public void init() {

View File

@@ -5,13 +5,12 @@ import com.gitee.sop.index.common.ApiRequestContext;
import com.gitee.sop.index.common.ApiResponse; import com.gitee.sop.index.common.ApiResponse;
import com.gitee.sop.index.service.RouteService; import com.gitee.sop.index.service.RouteService;
import com.gitee.sop.index.util.RequestUtil; import com.gitee.sop.index.util.RequestUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
/** /**
@@ -22,7 +21,7 @@ import javax.servlet.http.HttpServletRequest;
@RestController @RestController
public class IndexController { public class IndexController {
@Autowired @Resource
private RouteService routeService; private RouteService routeService;
@GetMapping("/") @GetMapping("/")

View File

@@ -48,14 +48,9 @@ public class ApiInfo {
private String methodName; private String methodName;
/** /**
* 参数名称 * 参数信息
*/ */
private String paramName; private String paramInfo;
/**
* 参数类型名称
*/
private String paramTypeName;
/** /**
* 接口是否需要授权访问 * 接口是否需要授权访问
@@ -70,7 +65,7 @@ public class ApiInfo {
/** /**
* 状态,1-启用,0-禁用 * 状态,1-启用,0-禁用
*/ */
private Integer apiStatus; private Integer status;
private LocalDateTime addTime; private LocalDateTime addTime;

View File

@@ -0,0 +1,45 @@
package com.gitee.sop.index.dao.entity;
import java.time.LocalDateTime;
import java.util.Date;
import com.gitee.fastmybatis.annotation.Pk;
import com.gitee.fastmybatis.annotation.PkStrategy;
import com.gitee.fastmybatis.annotation.Table;
import lombok.Data;
/**
* 表名isv_info
* 备注isv信息表
*
* @author tanghc
*/
@Table(name = "isv_info", pk = @Pk(name = "id", strategy = PkStrategy.INCREMENT))
@Data
public class IsvInfo {
private Long id;
/**
* appKey
*/
private String appId;
/**
* 1启用2禁用
*/
private Integer status;
/**
* 备注
*/
private String remark;
private LocalDateTime addTime;
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,57 @@
package com.gitee.sop.index.dao.entity;
import java.time.LocalDateTime;
import java.util.Date;
import com.gitee.fastmybatis.annotation.Pk;
import com.gitee.fastmybatis.annotation.PkStrategy;
import com.gitee.fastmybatis.annotation.Table;
import lombok.Data;
/**
* 表名isv_keys
* 备注ISV秘钥管理
*
* @author tanghc
*/
@Table(name = "isv_keys", pk = @Pk(name = "id", strategy = PkStrategy.INCREMENT))
@Data
public class IsvKeys {
private Long id;
private String appId;
/**
* 秘钥格式1PKCS8(JAVA适用)2PKCS1(非JAVA适用)
*/
private Integer keyFormat;
/**
* 开发者生成的公钥
*/
private String publicKeyIsv;
/**
* 开发者生成的私钥(交给开发者)
*/
private String privateKeyIsv;
/**
* 平台生成的公钥(交给开发者)
*/
private String publicKeyPlatform;
/**
* 平台生成的私钥
*/
private String privateKeyPlatform;
private LocalDateTime addTime;
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,15 @@
package com.gitee.sop.index.dao.mapper;
import com.gitee.fastmybatis.core.mapper.BaseMapper;
import com.gitee.sop.index.dao.entity.IsvInfo;
/**
* @author tanghc
*/
public interface IsvInfoMapper extends BaseMapper<IsvInfo> {
default IsvInfo getByAppId(String appId) {
return this.get(IsvInfo::getAppId, appId);
}
}

View File

@@ -0,0 +1,11 @@
package com.gitee.sop.index.dao.mapper;
import com.gitee.fastmybatis.core.mapper.BaseMapper;
import com.gitee.sop.index.dao.entity.IsvKeys;
/**
* @author tanghc
*/
public interface IsvKeysMapper extends BaseMapper<IsvKeys> {
}

View File

@@ -4,7 +4,7 @@ import com.gitee.sop.index.common.ApiInfoDTO;
import com.gitee.sop.index.dao.entity.ApiInfo; import com.gitee.sop.index.dao.entity.ApiInfo;
import com.gitee.sop.index.dao.mapper.ApiInfoMapper; import com.gitee.sop.index.dao.mapper.ApiInfoMapper;
import com.gitee.sop.index.service.manager.ApiCacheManager; import com.gitee.sop.index.service.manager.ApiCacheManager;
import org.springframework.beans.BeanUtils; import com.gitee.sop.index.util.CopyUtil;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
@@ -13,7 +13,7 @@ import javax.annotation.Resource;
* @author 六如 * @author 六如
*/ */
@Service @Service
public class ApiService { public class ApiInfoService {
@Resource @Resource
private ApiCacheManager apiCacheManager; private ApiCacheManager apiCacheManager;
@@ -23,12 +23,8 @@ public class ApiService {
public ApiInfoDTO getApi(String apiName, String apiVersion) { public ApiInfoDTO getApi(String apiName, String apiVersion) {
return apiCacheManager.getOrElse(apiName, apiVersion, () -> { return apiCacheManager.getOrElse(apiName, apiVersion, () -> {
ApiInfoDTO record = new ApiInfoDTO();
ApiInfo apiInfo = apiInfoMapper.getByNameVersion(apiName, apiVersion); ApiInfo apiInfo = apiInfoMapper.getByNameVersion(apiName, apiVersion);
if (apiInfo != null) { return CopyUtil.copyBean(apiInfo, ApiInfoDTO::new);
BeanUtils.copyProperties(apiInfo, record);
}
return record;
}); });
} }

View File

@@ -1,8 +1,10 @@
package com.gitee.sop.index.service; package com.gitee.sop.index.service;
import org.apache.dubbo.common.config.ReferenceCache;
import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.utils.SimpleReferenceCache;
import org.apache.dubbo.rpc.service.GenericService; import org.apache.dubbo.rpc.service.GenericService;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
@@ -16,10 +18,8 @@ import org.springframework.stereotype.Service;
@Service @Service
public class GenericServiceInvoker implements InitializingBean { public class GenericServiceInvoker implements InitializingBean {
private static final String TRUE = "true"; private static final String TRUE = "true";
private ApplicationConfig applicationConfig;
@Value("${nacos.host:localhost:8848}") private ApplicationConfig applicationConfig;
private String nacosHost;
@Value("${register.address:${register.type}://${register.host}") @Value("${register.address:${register.type}://${register.host}")
private String registerAddress; private String registerAddress;
@@ -32,13 +32,13 @@ public class GenericServiceInvoker implements InitializingBean {
@Override @Override
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {
RegistryConfig registryConfig = buildRegistryConfig(nacosHost); RegistryConfig registryConfig = buildRegistryConfig();
applicationConfig = new ApplicationConfig(); applicationConfig = new ApplicationConfig();
applicationConfig.setName(appName + "-generic"); applicationConfig.setName(appName + "-generic");
applicationConfig.setRegistry(registryConfig); applicationConfig.setRegistry(registryConfig);
} }
private RegistryConfig buildRegistryConfig(String nacosHost) { private RegistryConfig buildRegistryConfig() {
RegistryConfig config = new RegistryConfig(); RegistryConfig config = new RegistryConfig();
config.setAddress(registerAddress); config.setAddress(registerAddress);
return config; return config;
@@ -50,25 +50,9 @@ public class GenericServiceInvoker implements InitializingBean {
reference.setApplication(applicationConfig); reference.setApplication(applicationConfig);
reference.setInterface(interfaceName); reference.setInterface(interfaceName);
reference.setTimeout(timeout); reference.setTimeout(timeout);
try { ReferenceCache referenceCache = SimpleReferenceCache.getCache();
removeGenericSymbol(parameterTypes); GenericService genericService = referenceCache.get(reference);
GenericService genericService = reference.get();
return genericService.$invoke(method, parameterTypes, params); return genericService.$invoke(method, parameterTypes, params);
} finally {
reference.destroy();
}
} }
/**
* remove generic from parameterTypes
*/
private void removeGenericSymbol(String[] parameterTypes) {
for (int i = 0; i < parameterTypes.length; i++) {
int index = parameterTypes[i].indexOf('<');
if (index > -1) {
parameterTypes[i] = parameterTypes[i].substring(0, index);
}
}
}
} }

View File

@@ -1,91 +1,21 @@
package com.gitee.sop.index.service; package com.gitee.sop.index.service;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.gitee.sop.index.common.ApiInfoDTO;
import com.gitee.sop.index.common.ApiRequest;
import com.gitee.sop.index.common.ApiRequestContext; import com.gitee.sop.index.common.ApiRequestContext;
import com.gitee.sop.index.common.ApiResponse; import com.gitee.sop.index.common.ApiResponse;
import com.gitee.sop.index.exception.ExceptionExecutor;
import com.gitee.sop.index.service.validate.Validator;
import com.gitee.sop.index.util.ClassUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.utils.ClassUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
/** /**
* 接口路由 * 接口路由
* *
* @author 六如 * @author 六如
*/ */
@Service public interface RouteService {
@Slf4j
public class RouteService {
@Resource /**
private Validator validator; * 路由
*
@Resource * @param apiRequestContext 接口上下文
private GenericServiceInvoker genericServiceInvoker; * @return 返回结果
*/
@Resource ApiResponse route(ApiRequestContext apiRequestContext);
private ExceptionExecutor exceptionExecutor;
public ApiResponse route(ApiRequestContext apiRequestContext) {
ApiRequest apiRequest = apiRequestContext.getApiRequest();
log.info("收到客户端请求, ip={}, apiRequest={}", apiRequestContext.getIp(), apiRequest);
try {
// 接口校验
ApiInfoDTO apiInfoDTO = validator.validate(apiRequestContext);
return doRoute(apiRequestContext, apiInfoDTO);
} catch (Exception e) {
log.error("接口请求报错, , ip={}, apiRequest={}", apiRequestContext.getIp(), apiRequest, e);
return exceptionExecutor.executeException(apiRequestContext, e);
}
}
private ApiResponse doRoute(ApiRequestContext apiRequestContext, ApiInfoDTO apiInfo) {
ApiRequest apiRequest = apiRequestContext.getApiRequest();
Object result = genericServiceInvoker.invoke(
apiInfo.getInterfaceClassName(),
apiInfo.getMethodName(),
buildParamType(apiInfo),
buildInvokeParam(apiRequest, apiInfo)
);
return ApiResponse.success(result);
}
private String[] buildParamType(ApiInfoDTO apiInfo) {
String paramTypeName = apiInfo.getParamTypeName();
if (StringUtils.hasText(paramTypeName)) {
return new String[]{apiInfo.getParamTypeName()};
} else {
return new String[0];
}
}
private Object[] buildInvokeParam(ApiRequest apiRequest, ApiInfoDTO apiInfo) {
if (ObjectUtils.isEmpty(apiInfo.getParamTypeName())) {
return new Object[0];
}
String bizContent = apiRequest.getBiz_content();
JSONObject jsonObject = JSON.parseObject(bizContent);
if (ClassUtil.isPrimitive(apiInfo.getParamTypeName())) {
try {
Object value = jsonObject.getObject(apiInfo.getParamName(), ClassUtils.forName(apiInfo.getParamTypeName()));
return new Object[]{value};
} catch (ClassNotFoundException e) {
log.error("找不到参数class, paramTypeName={}, apiRequest={}, apiInfo={}",
apiInfo.getParamTypeName(), apiRequest, apiInfo, e);
throw new RuntimeException(e);
}
} else {
return new Object[]{jsonObject};
}
}
} }

View File

@@ -0,0 +1,108 @@
package com.gitee.sop.index.service;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.gitee.sop.index.common.ApiInfoDTO;
import com.gitee.sop.index.common.ApiRequest;
import com.gitee.sop.index.common.ApiRequestContext;
import com.gitee.sop.index.common.ApiResponse;
import com.gitee.sop.index.common.ParamInfoDTO;
import com.gitee.sop.index.exception.ExceptionExecutor;
import com.gitee.sop.index.service.validate.Validator;
import com.gitee.sop.index.util.ClassUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.utils.ClassUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 接口路由
*
* @author 六如
*/
@Service
@Slf4j
public class RouteServiceImpl implements RouteService {
private static final String CLASS = "class";
@Resource
protected Validator validator;
@Resource
protected GenericServiceInvoker genericServiceInvoker;
@Resource
protected ExceptionExecutor exceptionExecutor;
@Override
public ApiResponse route(ApiRequestContext apiRequestContext) {
ApiRequest apiRequest = apiRequestContext.getApiRequest();
log.info("收到客户端请求, ip={}, apiRequest={}", apiRequestContext.getIp(), apiRequest);
try {
// 接口校验
ApiInfoDTO apiInfoDTO = validator.validate(apiRequestContext);
// 微服务结果
Object result = doRoute(apiRequestContext, apiInfoDTO);
return ApiResponse.success(result);
} catch (Exception e) {
log.error("接口请求报错, , ip={}, apiRequest={}", apiRequestContext.getIp(), apiRequest, e);
return exceptionExecutor.executeException(apiRequestContext, e);
}
}
protected Object doRoute(ApiRequestContext apiRequestContext, ApiInfoDTO apiInfo) {
ApiRequest apiRequest = apiRequestContext.getApiRequest();
String paramInfo = apiInfo.getParamInfo();
List<ParamInfoDTO> paramInfoList = JSON.parseArray(paramInfo, ParamInfoDTO.class);
Object result = genericServiceInvoker.invoke(
apiInfo.getInterfaceClassName(),
apiInfo.getMethodName(),
buildParamType(paramInfoList),
buildInvokeParam(apiRequest, paramInfoList)
);
if (result instanceof Map) {
((Map<?, ?>) result).remove(CLASS);
}
return result;
}
protected String[] buildParamType(List<ParamInfoDTO> paramInfoList) {
if (ObjectUtils.isEmpty(paramInfoList)) {
return new String[0];
}
return paramInfoList.stream()
.map(ParamInfoDTO::getType)
.toArray(String[]::new);
}
protected Object[] buildInvokeParam(ApiRequest apiRequest, List<ParamInfoDTO> paramInfoList) {
if (ObjectUtils.isEmpty(paramInfoList)) {
return new Object[0];
}
String bizContent = apiRequest.getBiz_content();
JSONObject jsonObject = JSON.parseObject(bizContent);
List<Object> params = new ArrayList<>();
for (ParamInfoDTO paramInfoDTO : paramInfoList) {
if (ClassUtil.isPrimitive(paramInfoDTO.getType())) {
try {
Object value = jsonObject.getObject(paramInfoDTO.getName(), ClassUtils.forName(paramInfoDTO.getType()));
params.add(value);
} catch (ClassNotFoundException e) {
log.error("找不到参数class, paramInfoDTO={}, apiRequest={}", paramInfoDTO, apiRequest, e);
throw new RuntimeException("找不到class:" + paramInfoDTO.getType(), e);
}
} else {
Object value = jsonObject.getObject(paramInfoDTO.getName(), Object.class);
params.add(value);
}
}
return params.toArray(new Object[0]);
}
}

View File

@@ -7,14 +7,11 @@ import com.gitee.sop.index.common.StatusEnum;
import com.gitee.sop.index.dao.entity.ApiInfo; import com.gitee.sop.index.dao.entity.ApiInfo;
import com.gitee.sop.index.dao.mapper.ApiInfoMapper; import com.gitee.sop.index.dao.mapper.ApiInfoMapper;
import com.gitee.sop.index.service.manager.ApiCacheManager; import com.gitee.sop.index.service.manager.ApiCacheManager;
import com.gitee.sop.index.util.CopyUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Date;
/** /**
* @author 六如 * @author 六如
@@ -32,18 +29,14 @@ public class ApiRegisterServiceImpl implements ApiRegisterService {
@Override @Override
public void register(RegisterDTO registerDTO) { public void register(RegisterDTO registerDTO) {
log.info("收到接口注册, registerDTO={}", registerDTO); log.info("收到接口注册, registerDTO={}", registerDTO);
ApiInfoDTO apiInfoDTO = new ApiInfoDTO(); ApiInfoDTO apiInfoDTO = CopyUtil.copyBean(registerDTO, ApiInfoDTO::new);
BeanUtils.copyProperties(registerDTO, apiInfoDTO); apiInfoDTO.setStatus(StatusEnum.ENABLE.getValue());
ApiInfo apiInfo = apiInfoMapper.getByNameVersion(apiInfoDTO.getApiName(), apiInfoDTO.getApiVersion()); ApiInfo apiInfo = apiInfoMapper.getByNameVersion(apiInfoDTO.getApiName(), apiInfoDTO.getApiVersion());
LocalDateTime now = LocalDateTime.now();
if (apiInfo == null) { if (apiInfo == null) {
apiInfo = new ApiInfo(); apiInfo = new ApiInfo();
apiInfo.setApiStatus(StatusEnum.ENABLE.getValue());
apiInfo.setAddTime(now);
} }
apiInfo.setUpdateTime(now); CopyUtil.copyPropertiesIgnoreNull(apiInfoDTO, apiInfo);
BeanUtils.copyProperties(apiInfoDTO, apiInfo);
// 保存到数据库 // 保存到数据库
apiInfoMapper.saveOrUpdate(apiInfo); apiInfoMapper.saveOrUpdate(apiInfo);
// 保存到缓存 // 保存到缓存

View File

@@ -10,9 +10,6 @@ public class IsvDTO {
private String appId; private String appId;
private String publicKey;
private String privateKeyPlatform;
private Integer status; private Integer status;
} }

View File

@@ -1,31 +1,29 @@
package com.gitee.sop.index.service.manager.impl; package com.gitee.sop.index.service.manager.impl;
import com.gitee.sop.index.dao.entity.IsvInfo;
import com.gitee.sop.index.dao.mapper.IsvInfoMapper;
import com.gitee.sop.index.service.manager.IsvManager; import com.gitee.sop.index.service.manager.IsvManager;
import com.gitee.sop.index.service.manager.dto.IsvDTO; import com.gitee.sop.index.service.manager.dto.IsvDTO;
import com.gitee.sop.index.util.CopyUtil;
import javax.annotation.PostConstruct; import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
/** /**
* @author 六如 * @author 六如
*/ */
public class LocalIsvManagerImpl implements IsvManager { public class LocalIsvManagerImpl implements IsvManager {
private static final Map<String, IsvDTO> ISV_MAP = new HashMap<>(); @Resource
private IsvInfoMapper isvInfoMapper;
@Override @Override
public IsvDTO getIsv(String appId) { public IsvDTO getIsv(String appId) {
return ISV_MAP.get(appId); IsvInfo isvInfo = isvInfoMapper.getByAppId(appId);
return CopyUtil.copyBean(isvInfo, IsvDTO::new);
} }
@Override @Override
public void reload(String appId) { public void reload(String appId) {
} }
@PostConstruct
public void init() {
}
} }

View File

@@ -1,23 +1,23 @@
package com.gitee.sop.index.service.manager.impl; package com.gitee.sop.index.service.manager.impl;
import com.gitee.sop.index.dao.entity.IsvKeys;
import com.gitee.sop.index.dao.mapper.IsvKeysMapper;
import com.gitee.sop.index.service.manager.SecretManager; import com.gitee.sop.index.service.manager.SecretManager;
import java.util.HashMap; import javax.annotation.Resource;
import java.util.Map;
/** /**
* @author 六如 * @author 六如
*/ */
public class LocalSecretManagerImpl implements SecretManager { public class LocalSecretManagerImpl implements SecretManager {
static Map<String, String> PUB_KEY_MGR = new HashMap<>(); @Resource
static { private IsvKeysMapper isvKeysMapper;
PUB_KEY_MGR.put("2019032617262200001", "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlyb9aUBaljQP/vjmBFe1mF8HsWSvyfC2NTlpT/V9E+sBxTr8TSkbzJCeeeOEm4LCaVXL0Qz63MZoT24v7AIXTuMdj4jyiM/WJ4tjrWAgnmohNOegfntTto16C3l234vXz4ryWZMR/7W+MXy5B92wPGQEJ0LKFwNEoLspDEWZ7RdE53VH7w6y6sIZUfK+YkXWSwehfKPKlx+lDw3zRJ3/yvMF+U+BAdW/MfECe1GuBnCFKnlMRh3UKczWyXWkL6ItOpYHHJi/jx85op5BWDje2pY9QowzfN94+0DB3T7UvZeweu3zlP6diwAJDzLaFQX8ULfWhY+wfKxIRgs9NoiSAQIDAQAB");
}
@Override @Override
public String getIsvPublicKey(String appId) { public String getIsvPublicKey(String appId) {
return PUB_KEY_MGR.get(appId); return isvKeysMapper.query()
.eq(IsvKeys::getAppId, appId)
.getValue(IsvKeys::getPublicKeyIsv);
} }
} }

View File

@@ -0,0 +1,13 @@
package com.gitee.sop.index.service.manager.impl;
import com.gitee.sop.index.service.manager.SecretManager;
/**
* @author 六如
*/
public class RedisSecretManager implements SecretManager {
@Override
public String getIsvPublicKey(String appId) {
return "";
}
}

View File

@@ -4,25 +4,26 @@ import com.gitee.sop.index.common.ApiInfoDTO;
import com.gitee.sop.index.common.ApiRequest; import com.gitee.sop.index.common.ApiRequest;
import com.gitee.sop.index.common.ApiRequestContext; import com.gitee.sop.index.common.ApiRequestContext;
import com.gitee.sop.index.common.ParamNames; import com.gitee.sop.index.common.ParamNames;
import com.gitee.sop.index.common.StatusEnum;
import com.gitee.sop.index.config.ApiConfig; import com.gitee.sop.index.config.ApiConfig;
import com.gitee.sop.index.exception.ApiException; import com.gitee.sop.index.exception.ApiException;
import com.gitee.sop.index.message.ErrorEnum; import com.gitee.sop.index.message.ErrorEnum;
import com.gitee.sop.index.service.ApiService; import com.gitee.sop.index.service.ApiInfoService;
import com.gitee.sop.index.service.manager.IpBlacklistManager; import com.gitee.sop.index.service.manager.IpBlacklistManager;
import com.gitee.sop.index.service.manager.IsvApiPermissionManager; import com.gitee.sop.index.service.manager.IsvApiPermissionManager;
import com.gitee.sop.index.service.manager.IsvManager; import com.gitee.sop.index.service.manager.IsvManager;
import com.gitee.sop.index.service.manager.SecretManager;
import com.gitee.sop.index.service.manager.dto.IsvDTO; import com.gitee.sop.index.service.manager.dto.IsvDTO;
import com.gitee.sop.index.service.validate.alipay.AlipaySigner; import com.gitee.sop.index.service.validate.alipay.AlipaySigner;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.util.unit.DataSize; import org.springframework.util.unit.DataSize;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Arrays; import java.util.Arrays;
@@ -36,14 +37,12 @@ import java.util.Locale;
* @author tanghc * @author tanghc
*/ */
@Slf4j @Slf4j
@RequiredArgsConstructor
@Service @Service
public class ApiValidator implements Validator { public class ApiValidator implements Validator {
private static final int MILLISECOND_OF_ONE_SECOND = 1000; private static final int MILLISECOND_OF_ONE_SECOND = 1000;
private static final int STATUS_FORBIDDEN = 2;
private static List<String> FORMAT_LIST = Arrays.asList("json", "xml"); private static final List<String> FORMAT_LIST = Arrays.asList("json", "xml");
private final Signer signer = new AlipaySigner(); private final Signer signer = new AlipaySigner();
@@ -53,20 +52,24 @@ public class ApiValidator implements Validator {
@Value("${upload.max-file-size:${spring.servlet.multipart.max-file-size:10MB}}") @Value("${upload.max-file-size:${spring.servlet.multipart.max-file-size:10MB}}")
private String maxFileSize; private String maxFileSize;
private final ApiConfig apiConfig; @Resource
private ApiConfig apiConfig;
@Autowired @Resource
private ApiService apiService; private ApiInfoService apiInfoService;
@Autowired @Resource
private IpBlacklistManager ipBlacklistManager; private IpBlacklistManager ipBlacklistManager;
@Autowired @Resource
private IsvApiPermissionManager isvApiPermissionManager; private IsvApiPermissionManager isvApiPermissionManager;
@Autowired @Resource
private IsvManager isvManager; private IsvManager isvManager;
@Resource
private SecretManager secretManager;
@Override @Override
public ApiInfoDTO validate(ApiRequestContext apiRequestContext) { public ApiInfoDTO validate(ApiRequestContext apiRequestContext) {
@@ -84,7 +87,7 @@ public class ApiValidator implements Validator {
checkIP(apiRequestContext); checkIP(apiRequestContext);
ApiRequest apiRequest = apiRequestContext.getApiRequest(); ApiRequest apiRequest = apiRequestContext.getApiRequest();
ApiInfoDTO apiInfo = apiService.getApi(apiRequest.getMethod(), apiRequest.getVersion()); ApiInfoDTO apiInfo = apiInfoService.getApi(apiRequest.getMethod(), apiRequest.getVersion());
// 检查接口信息 // 检查接口信息
checkApiInfo(apiRequestContext, apiInfo); checkApiInfo(apiRequestContext, apiInfo);
@@ -189,7 +192,7 @@ public class ApiValidator implements Validator {
throw new ApiException(ErrorEnum.ISV_INVALID_APP_ID, apiRequestContext.getLocale()); throw new ApiException(ErrorEnum.ISV_INVALID_APP_ID, apiRequestContext.getLocale());
} }
// 禁止访问 // 禁止访问
if (isv.getStatus() == null || isv.getStatus() == STATUS_FORBIDDEN) { if (isv.getStatus() == null || isv.getStatus() == StatusEnum.DISABLE.getValue()) {
throw new ApiException(ErrorEnum.ISV_ACCESS_FORBIDDEN, apiRequestContext.getLocale()); throw new ApiException(ErrorEnum.ISV_ACCESS_FORBIDDEN, apiRequestContext.getLocale());
} }
return isv; return isv;
@@ -203,7 +206,7 @@ public class ApiValidator implements Validator {
apiRequest.takeNameVersion(), ParamNames.SIGN_NAME); apiRequest.takeNameVersion(), ParamNames.SIGN_NAME);
} }
// ISV上传的公钥 // ISV上传的公钥
String publicKey = isv.getPublicKey(); String publicKey = secretManager.getIsvPublicKey(isv.getAppId());
if (ObjectUtils.isEmpty(publicKey)) { if (ObjectUtils.isEmpty(publicKey)) {
throw new ApiException(ErrorEnum.ISV_MISSING_SIGNATURE_CONFIG, apiRequestContext.getLocale(), throw new ApiException(ErrorEnum.ISV_MISSING_SIGNATURE_CONFIG, apiRequestContext.getLocale(),
apiRequest.takeNameVersion()); apiRequest.takeNameVersion());

View File

@@ -0,0 +1,344 @@
package com.gitee.sop.index.util;
import com.alibaba.fastjson2.JSON;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* 属性拷贝工具类
*
* @author 六如
*/
public class CopyUtil extends BeanUtils {
/**
* 属性拷贝,第一个参数中的属性值拷贝到第二个参数中<br>
* 注意:当第一个参数中的属性有null值时,不会拷贝进去
*
* @param from 源对象
* @param to 目标对象
* @param ignoreProperties 忽略的字段
* @throws BeansException
*/
public static void copyPropertiesIgnoreNull(Object from, Object to, String... ignoreProperties)
throws BeansException {
Assert.notNull(from, "Source must not be null");
Assert.notNull(to, "Target must not be null");
Class<?> actualEditable = to.getClass();
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : Collections.emptyList());
for (PropertyDescriptor targetPd : targetPds) {
if (ignoreList.contains(targetPd.getName())) {
continue;
}
Method writeMethod = targetPd.getWriteMethod();
if (writeMethod != null) {
PropertyDescriptor sourcePd = getPropertyDescriptor(from.getClass(), targetPd.getName());
if (sourcePd != null) {
Method readMethod = sourcePd.getReadMethod();
if (readMethod != null &&
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
try {
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(from);
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
// 这里判断value是否为空 当然这里也能进行一些特殊要求的处理
// 例如绑定时格式转换等等
if (value != null) {
writeMethod.invoke(to, value);
}
} catch (Throwable ex) {
throw new FatalBeanException(
"Could not copy property '" + targetPd.getName() + "' from source to target", ex);
}
}
}
}
}
}
/**
* 拷贝指定的字段
*
* @param from 源对象
* @param to 目标对象
* @param includeFields 指定字段
*/
public static void copyPropertiesInclude(Object from, Object to, Set<String> includeFields) {
Objects.requireNonNull(includeFields, "includeFields can not null");
Assert.notNull(from, "Source must not be null");
Assert.notNull(to, "Target must not be null");
if (includeFields.isEmpty()) {
return;
}
Class<?> actualEditable = to.getClass();
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
for (PropertyDescriptor targetPd : targetPds) {
if (!includeFields.contains(targetPd.getName())) {
continue;
}
Method writeMethod = targetPd.getWriteMethod();
if (writeMethod != null) {
PropertyDescriptor sourcePd = getPropertyDescriptor(from.getClass(), targetPd.getName());
if (sourcePd != null) {
Method readMethod = sourcePd.getReadMethod();
if (readMethod != null &&
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
try {
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(from);
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
writeMethod.invoke(to, value);
} catch (Throwable ex) {
throw new FatalBeanException(
"Could not copy property '" + targetPd.getName() + "' from source to target", ex);
}
}
}
}
}
}
/**
* 拷贝属性
*
* @param from 被拷贝类
* @param to 目标类
*/
public static void copyProperties(Object from, Object to) {
BeanUtils.copyProperties(from, to);
}
/**
* 拷贝bean成为一个新类
*
* @param from 被拷贝类
* @param supplier 新的类获取回调
* @param <T> 新的类
* @return 返回新的类实例from为null时返回null
*/
public static <T> T copyBean(Object from, Supplier<T> supplier) {
if (from == null) {
return null;
}
T to = supplier.get();
BeanUtils.copyProperties(from, to);
return to;
}
/**
* 拷贝实例
*
* @param from 被拷贝类
* @param supplier 新的类获取回调
* @param after 对新的类最后续处理回调
* @param <T> 新的类
* @return 返回新的类
*/
public static <T> T copyBean(Object from, Supplier<T> supplier, Consumer<T> after) {
if (from == null) {
return null;
}
T to = supplier.get();
BeanUtils.copyProperties(from, to);
after.accept(to);
return to;
}
/**
* 拷贝List将list中的类转换成新的对象
*
* @param collection 被拷贝的集合
* @param toElement List新元素
* @param <T> 新元素类型
* @return 返回新的List
*/
public static <T> List<T> copyList(Collection<?> collection, Supplier<T> toElement) {
if (collection == null || collection.isEmpty()) {
return new ArrayList<>();
}
return collection.stream()
.map(source -> {
T target = toElement.get();
BeanUtils.copyProperties(source, target);
return target;
})
.collect(Collectors.toList());
}
public static <E, R> List<R> copyList(Collection<E> fromList, Function<E, R> function) {
if (fromList == null) {
return new ArrayList<>();
}
return fromList.stream()
.map(source -> {
R target = function.apply(source);
BeanUtils.copyProperties(source, target);
return target;
})
.collect(Collectors.toList());
}
/**
* 拷贝List并做后续处理
*
* @param fromList 被拷贝的list
* @param toElement 新元素
* @param after 对新元素做后续处理
* @param <T> 新类型
* @return 返回新的List
*/
public static <T> List<T> copyList(Collection<?> fromList, Supplier<T> toElement, Consumer<T> after) {
if (fromList == null) {
return new ArrayList<>();
}
return fromList.stream()
.map(source -> {
T target = toElement.get();
BeanUtils.copyProperties(source, target);
after.accept(target);
return target;
})
.collect(Collectors.toList());
}
/**
* 拷贝List并做后续处理
*
* @param fromList 被拷贝的list
* @param toElement 新元素
* @param after 对新元素做后续处理
* @param <T> 新类型
* @return 返回新的List
*/
public static <T, F> List<T> copyList(Collection<F> fromList, Supplier<T> toElement, CopyConsumer<F, T> after) {
if (fromList == null) {
return new ArrayList<>();
}
return fromList.stream()
.map(source -> {
T target = toElement.get();
BeanUtils.copyProperties(source, target);
after.apply(source, target);
return target;
})
.collect(Collectors.toList());
}
/**
* 深层次拷贝通过json转换的方式实现
*
* @param from 待转换的类
* @param toClass 目标类class
* @param <T> 目标类
* @return 返回目标类
*/
public static <T> T deepCopy(Object from, Class<T> toClass) {
String json = JSON.toJSONString(from);
return JSON.parseObject(json, toClass);
}
/**
* 深层次拷贝通过json转换的方式实现
*
* @param from 待转换的类
* @param toClass 目标类class
* @param <T> 目标类
* @return 返回目标类
*/
public static <T> List<T> deepCopyList(Object from, Class<T> toClass) {
String json = JSON.toJSONString(from);
return JSON.parseArray(json, toClass);
}
/**
* 拷贝map
*
* @param srcMap 原map
* @param valueGetter 值转换
* @param <K> Key类型
* @param <V> Value类型
* @return 返回新map
*/
public static <K, V> Map<K, V> copyMap(Map<K, ?> srcMap, Supplier<V> valueGetter) {
Map<K, V> ret = new LinkedHashMap<>(srcMap.size() * 2);
for (Map.Entry<K, ?> entry : srcMap.entrySet()) {
V value = copyBean(entry.getValue(), valueGetter);
ret.put(entry.getKey(), value);
}
return ret;
}
/**
* 拷贝map
*
* @param srcMap 原map
* @param function 值转换
* @param <K> Key类型
* @param <V> Value类型
* @return 返回新map
*/
public static <K, V, V0> Map<K, V> copyMap(Map<K, V0> srcMap, Function<V0, V> function) {
Map<K, V> ret = new LinkedHashMap<>(srcMap.size() * 2);
for (Map.Entry<K, V0> entry : srcMap.entrySet()) {
V value = function.apply(entry.getValue());
ret.put(entry.getKey(), value);
}
return ret;
}
/**
* 拷贝map,value是list
*
* @param srcMap 原map
* @param valueGetter 值转换
* @param <K> Key类型
* @param <V> Value类型
* @return 返回新map
*/
public static <K, V, V2> Map<K, List<V2>> copyMapList(Map<K, List<V>> srcMap, Function<List<V>, List<V2>> valueGetter) {
Map<K, List<V2>> ret = new LinkedHashMap<>(srcMap.size() * 2);
for (Map.Entry<K, List<V>> entry : srcMap.entrySet()) {
List<V2> value = valueGetter.apply(entry.getValue());
ret.put(entry.getKey(), value);
}
return ret;
}
public interface CopyConsumer<F, T> {
void apply(F from, T to);
}
}

View File

@@ -1,7 +1,6 @@
dubbo.registry.address=zookeeper://localhost:2181 dubbo.registry.address=zookeeper://localhost:2181
spring.datasource.driver-class-name=org.h2.Driver # mysql config
spring.datasource.url=jdbc:h2:mem:test mysql.host=127.0.0.1:3306
spring.sql.init.schema-locations=classpath:schema.sql mysql.username=root
spring.datasource.username=${mysql.username} mysql.password=root
spring.datasource.password=${mysql.password}

View File

@@ -12,17 +12,26 @@ dubbo.protocol.port=-1
# redis://localhost:6379 Cluster config:redis://10.20.153.10:6379?backup=10.20.153.11:6379,10.20.153.12:6379 # redis://localhost:6379 Cluster config:redis://10.20.153.10:6379?backup=10.20.153.11:6379,10.20.153.12:6379
# ------ # ------
dubbo.registry.address=nacos://localhost:8848 dubbo.registry.address=nacos://localhost:8848
####### dubbo config end #######
####### mysql config ####### ####### mysql config #######
mysql.host=127.0.0.1:3306 mysql.host=127.0.0.1:3306
mysql.username=root mysql.username=
mysql.password=root mysql.password=
mysql.db=sop mysql.db=sop
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://${mysql.host}/${mysql.db}?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai spring.datasource.url=jdbc:mysql://${mysql.host}/${mysql.db}?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
spring.datasource.username=${mysql.username} spring.datasource.username=${mysql.username}
spring.datasource.password=${mysql.password} spring.datasource.password=${mysql.password}
####### mysql config end #######
####### mybatis config #######
mybatis.fill.com.gitee.fastmybatis.core.support.LocalDateTimeFillInsert=add_time
mybatis.fill.com.gitee.fastmybatis.core.support.LocalDateTimeFillUpdate=update_time
# mybatis config file
mybatis.config-location=classpath:mybatis/mybatisConfig.xml
# print SQL
logging.level.com.gitee.sop.index.dao=error
logging.level.com.gitee.fastmybatis=debug
mybatis.print-sql=true

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!-- 全局映射器启用缓存 -->
<setting name="cacheEnabled" value="true" />
<!-- 查询时,关闭关联对象即时加载以提高性能 -->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 对于未知的SQL查询允许返回不同的结果集以达到通用的效果 -->
<setting name="multipleResultSetsEnabled" value="true" />
<!-- 允许使用列标签代替列名 -->
<setting name="useColumnLabel" value="true" />
<!-- 允许使用自定义的主键值(比如由程序生成的UUID 32位编码作为键值)数据表的PK生成策略将被覆盖 -->
<setting name="useGeneratedKeys" value="false" />
<!-- 对于批量更新操作缓存SQL以提高性能:BATCH -->
<setting name="defaultExecutorType" value="SIMPLE" />
<!-- 超时设置 -->
<setting name="defaultStatementTimeout" value="25000" />
</settings>
<plugins>
<plugin interceptor="com.gitee.fastmybatis.core.support.plugin.SqlFormatterPlugin">
</plugin>
</plugins>
</configuration>

View File

@@ -1,16 +0,0 @@
CREATE TABLE `api_info`
(
`id` INTEGER PRIMARY KEY AUTO_INCREMENT,
`application` varchar(64) NOT NULL DEFAULT '' COMMENT '应用名称',
`api_name` varchar(128) NOT NULL DEFAULT '' COMMENT '接口名称',
`api_version` varchar(16) NOT NULL DEFAULT '1.0' COMMENT '版本号',
`interface_class_name` varchar(128) NOT NULL DEFAULT '' COMMENT '接口class',
`method_name` varchar(128) NOT NULL DEFAULT '' COMMENT '方法名称',
`param_name` varchar(128) NOT NULL DEFAULT '' COMMENT '参数名称',
`param_type_name` varchar(128) NOT NULL DEFAULT '' COMMENT '参数类型名称',
is_permission tinyint(4) NOT NULL DEFAULT '0' COMMENT '接口是否需要授权访问',
is_need_Token tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否需要appAuthToken',
api_status tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态,1-启用,0-禁用',
`add_time` datetime NOT NULL DEFAULT null,
`update_time` datetime NOT NULL DEFAULT null
);

View File

@@ -1,8 +1,10 @@
package com.gitee.sop.support.config; package com.gitee.sop.support.config;
import com.alibaba.fastjson2.JSON;
import com.gitee.sop.index.api.ApiRegisterService; import com.gitee.sop.index.api.ApiRegisterService;
import com.gitee.sop.index.api.RegisterDTO; import com.gitee.sop.index.api.RegisterDTO;
import com.gitee.sop.support.annotation.Open; import com.gitee.sop.support.annotation.Open;
import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.annotation.DubboService;
@@ -15,10 +17,15 @@ import org.springframework.core.env.Environment;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
import java.io.Serializable;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Parameter; import java.lang.reflect.Parameter;
import java.util.Collections;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/** /**
@@ -42,7 +49,6 @@ public class SopAutoConfiguration implements InitializingBean {
String appName = environment.getProperty("spring.application.name"); String appName = environment.getProperty("spring.application.name");
Map<String, Object> beanMap = applicationContext.getBeansWithAnnotation(DubboService.class); Map<String, Object> beanMap = applicationContext.getBeansWithAnnotation(DubboService.class);
for (Object serviceObj : beanMap.values()) { for (Object serviceObj : beanMap.values()) {
Class<?> objClass = serviceObj.getClass();
Class<?> interfaceClass = findInterfaceClass(serviceObj); Class<?> interfaceClass = findInterfaceClass(serviceObj);
ReflectionUtils.doWithMethods(interfaceClass, method -> { ReflectionUtils.doWithMethods(interfaceClass, method -> {
regApi(appName, interfaceClass, method); regApi(appName, interfaceClass, method);
@@ -64,18 +70,14 @@ public class SopAutoConfiguration implements InitializingBean {
if (open == null) { if (open == null) {
return; return;
} }
Parameter[] parameters = method.getParameters(); List<ParamInfo> paramInfos = buildParamInfo(method);
Optional<Parameter> paramOpt = filterParameter(parameters);
String paramName = paramOpt.map(Parameter::getName).orElse("");
String paramTypeName = paramOpt.map(Parameter::getType).map(Class::getName).orElse("");
RegisterDTO registerDTO = new RegisterDTO(); RegisterDTO registerDTO = new RegisterDTO();
registerDTO.setApplication(appName); registerDTO.setApplication(appName);
registerDTO.setApiName(open.value()); registerDTO.setApiName(open.value());
registerDTO.setApiVersion(open.version()); registerDTO.setApiVersion(open.version());
registerDTO.setInterfaceClassName(interfaceClass.getName()); registerDTO.setInterfaceClassName(interfaceClass.getName());
registerDTO.setMethodName(method.getName()); registerDTO.setMethodName(method.getName());
registerDTO.setParamName(paramName); registerDTO.setParamInfo(JSON.toJSONString(paramInfos));
registerDTO.setParamTypeName(paramTypeName);
registerDTO.setIsPermission(parseBoolean(open.permission())); registerDTO.setIsPermission(parseBoolean(open.permission()));
registerDTO.setIsNeedToken(parseBoolean(open.needToken())); registerDTO.setIsNeedToken(parseBoolean(open.needToken()));
log.info("注册开放接口, apiInfo={}", registerDTO); log.info("注册开放接口, apiInfo={}", registerDTO);
@@ -86,6 +88,21 @@ public class SopAutoConfiguration implements InitializingBean {
} }
} }
private List<ParamInfo> buildParamInfo(Method method) {
Parameter[] parameters = method.getParameters();
if (parameters.length == 0) {
return Collections.emptyList();
}
return Stream.of(parameters)
.map(parameter -> {
ParamInfo paramInfo = new ParamInfo();
paramInfo.setName(parameter.getName());
paramInfo.setType(parameter.getType().getName());
return paramInfo;
})
.collect(Collectors.toList());
}
private Optional<Parameter> filterParameter(Parameter[] parameters) { private Optional<Parameter> filterParameter(Parameter[] parameters) {
if (ObjectUtils.isEmpty(parameters)) { if (ObjectUtils.isEmpty(parameters)) {
return Optional.empty(); return Optional.empty();
@@ -96,4 +113,13 @@ public class SopAutoConfiguration implements InitializingBean {
private int parseBoolean(boolean b) { private int parseBoolean(boolean b) {
return b ? 1 : 0; return b ? 1 : 0;
} }
@Data
private static class ParamInfo implements Serializable {
private static final long serialVersionUID = -404173450677698875L;
private String name;
private String type;
}
} }

View File

@@ -68,6 +68,41 @@ public class AlipayClientPostTest extends TestBase {
System.out.println(responseData); System.out.println(responseData);
} }
@Test
public void testFind() throws Exception {
// 公共请求参数
Map<String, String> params = new HashMap<String, String>();
params.put("app_id", appId);
params.put("method", "story.find");
params.put("format", "json");
params.put("charset", "utf-8");
params.put("sign_type", "RSA2");
params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
params.put("version", "1.0");
// 业务参数
Map<String, Object> bizContent = new HashMap<>();
bizContent.put("id", "122");
bizContent.put("name", "葫芦娃");
params.put("biz_content", JSON.toJSONString(bizContent));
String content = AlipaySignature.getSignContent(params);
String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8");
params.put("sign", sign);
System.out.println("----------- 请求信息 -----------");
System.out.println("请求参数:" + buildParamQuery(params));
System.out.println("商户秘钥:" + privateKey);
System.out.println("待签名内容:" + content);
System.out.println("签名(sign)" + sign);
System.out.println("URL参数" + buildUrlQuery(params));
System.out.println("----------- 返回结果 -----------");
String responseData = postJson(url, params);// 发送请求
System.out.println(responseData);
}
@Test @Test
public void testSave() throws Exception { public void testSave() throws Exception {