Commit 5c84d461 by lijinqi

Merge branch 'api' into develop

# Conflicts:
#	computility-module-apihub/src/main/java/com/luhu/computility/module/apihub/dal/mysql/apicalllog/ApiCallLogMapper.java
#	computility-module-apihub/src/main/java/com/luhu/computility/module/apihub/dal/mysql/apiorder/ApiOrderMapper.java
#	computility-module-apihub/src/main/java/com/luhu/computility/module/apihub/service/apicalllog/ApiCallLogServiceImpl.java
#	computility-module-apihub/src/main/java/com/luhu/computility/module/apihub/service/apiorder/ApiOrderServiceImpl.java
parent 768875ff
...@@ -43,18 +43,6 @@ ...@@ -43,18 +43,6 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- <dependency>-->
<!-- <groupId>com.luhu</groupId>-->
<!-- <artifactId>computility-module-external</artifactId>-->
<!-- <version>${revision}</version>-->
<!-- </dependency>-->
<dependency>
<groupId>com.luhu</groupId>
<artifactId>computility-module-apihub-api</artifactId>
<version>${revision}</version>
</dependency>
</dependencies> </dependencies>
</project> </project>
package com.luhu.computility.framework.signature.core.aop; package com.luhu.computility.framework.signature.core.aop;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil; import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.fastjson.JSON;
import com.luhu.computility.framework.common.exception.ServiceException; import com.luhu.computility.framework.common.exception.ServiceException;
import com.luhu.computility.framework.common.pojo.CommonResult; import com.luhu.computility.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.luhu.computility.framework.common.util.servlet.ServletUtils; import com.luhu.computility.framework.common.util.servlet.ServletUtils;
import com.luhu.computility.framework.signature.core.annotation.ApiSignature; import com.luhu.computility.framework.signature.core.annotation.ApiSignature;
import com.luhu.computility.framework.signature.core.redis.ApiSignatureRedisDAO; import com.luhu.computility.framework.signature.core.redis.ApiSignatureRedisDAO;
import com.luhu.computility.module.apihub.api.apicalllog.ApiCallLogApi;
import com.luhu.computility.module.apihub.api.apicalllog.dto.ApiCallLogSaveReqDTO;
import com.luhu.computility.module.apihub.api.apiendpoint.ApiEndpointApi;
import com.luhu.computility.module.apihub.api.apiendpoint.vo.ApiEndpointRespDTO;
import com.luhu.computility.module.apihub.api.appcredential.AppCredentialApi;
import com.luhu.computility.module.apihub.api.appcredential.dto.AppCredentialRespDTO;
import com.luhu.computility.module.apihub.api.userapiusage.UserApiUsageApi;
import com.luhu.computility.module.apihub.api.userapiusage.dto.JoinUserApiUsageDTO;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.SortedMap; import java.util.SortedMap;
import java.util.TreeMap; import java.util.TreeMap;
import static com.luhu.computility.module.apihub.enums.ErrorCodeConstants.API_ENDPOINT_EXPIRED; import static com.luhu.computility.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
import static com.luhu.computility.module.apihub.enums.ErrorCodeConstants.API_ENDPOINT_NOT_AVAILABLE;
import static com.luhu.computility.module.apihub.enums.ErrorCodeConstants.API_ENDPOINT_NOT_EXISTS;
import static com.luhu.computility.module.apihub.enums.ErrorCodeConstants.INVALID_APPID;
import static com.luhu.computility.module.apihub.enums.ErrorCodeConstants.TIMESTAMP_EXCEPTION;
/** /**
* 拦截声明了 {@link ApiSignature} 注解的方法,实现签名 * 拦截声明了 {@link ApiSignature} 注解的方法,实现签名
...@@ -54,152 +37,51 @@ public class ApiSignatureAspect { ...@@ -54,152 +37,51 @@ public class ApiSignatureAspect {
private final ApiSignatureRedisDAO signatureRedisDAO; private final ApiSignatureRedisDAO signatureRedisDAO;
@Resource @Before("@annotation(signature)")
private AppCredentialApi appCredentialApi; public void beforePointCut(JoinPoint joinPoint, ApiSignature signature) {
// 1. 验证通过,直接结束
@Resource if (verifySignature(signature, Objects.requireNonNull(ServletUtils.getRequest()))) {
private ApiEndpointApi apiEndpointApi; return;
@Resource
private ApiCallLogApi apiCallLogApi;
@Resource
private UserApiUsageApi userApiUsageApi;
public ApiSignatureAspect(ApiSignatureRedisDAO signatureRedisDAO) {
this.signatureRedisDAO = signatureRedisDAO;
} }
// 2. 验证不通过,抛出异常
@Around("@annotation(signature)") log.error("[beforePointCut][方法{} 参数({}) 签名失败]", joinPoint.getSignature().toString(),
public Object aroundPointCut(ProceedingJoinPoint joinPoint, ApiSignature signature) throws Throwable { joinPoint.getArgs());
HttpServletRequest request = Objects.requireNonNull(ServletUtils.getRequest());
// 1.校验 Header
/* if (!verifyHeaders(signature, request)) {
throw new ServiceException(HEAD_EXCEPTION);
}*/
//查询appId对应的Secret
String appId = request.getHeader(signature.appId());
AppCredentialRespDTO appCredentialRespDTO = appCredentialApi.getAppSecretByAppid(appId);
if (!ObjectUtil.isEmpty(appCredentialRespDTO)) {
Assert.notNull(appCredentialRespDTO.getAppId(), "[appId({})] 找不到对应的 appSecret", appId);
} else {
throw new ServiceException(INVALID_APPID);
}
// 2. 签名校验
/*if (!verifySignature(signature, request, appCredentialDO)) {
log.error("[aroundPointCut][方法 {} 参数({}) 签名失败]", joinPoint.getSignature(), joinPoint.getArgs());
throw new ServiceException(BAD_REQUEST.getCode(), throw new ServiceException(BAD_REQUEST.getCode(),
StrUtil.blankToDefault(signature.message(), BAD_REQUEST.getMsg())); StrUtil.blankToDefault(signature.message(), BAD_REQUEST.getMsg()));
}*/
// 2. 应用-接口绑定校验
String path = request.getRequestURI();
String method = request.getMethod();
//2.1是否有该接口
ApiEndpointRespDTO apiEndpointRespDTO = apiEndpointApi.getApiEndpointByPathAndMethod(path, method);
if (Objects.isNull(apiEndpointRespDTO)) {
throw new ServiceException(API_ENDPOINT_NOT_EXISTS);
}
//2.2该接口是否订阅过
List<JoinUserApiUsageDTO> joinUserApiUsageResults = userApiUsageApi.selectJoinUserApiUsageResult(appCredentialRespDTO.getUserId(), apiEndpointRespDTO.getId());
if (CollectionUtil.isEmpty(joinUserApiUsageResults)){
throw new ServiceException(API_ENDPOINT_NOT_AVAILABLE);
}
//3. 用户套餐校验(只校验,不扣减)如果有一个套餐符合条件,则继续往下走
JoinUserApiUsageDTO joinUserApiUsageResult = null;
LocalDateTime now = LocalDateTime.now();
for (JoinUserApiUsageDTO j : joinUserApiUsageResults) {
// 未过期且有剩余额度
if (j.getExpireTime().isAfter(now) && j.getUsedTimes() < j.getPackageTimes()) {
if (joinUserApiUsageResult == null || j.getExpireTime().isBefore(joinUserApiUsageResult.getExpireTime())) {
joinUserApiUsageResult = j;
}
}
}
if (Objects.isNull(joinUserApiUsageResult)) {
throw new ServiceException(API_ENDPOINT_EXPIRED);
}
// 4. 执行目标方法
Object result = joinPoint.proceed();
// 5. 只有当接口执行成功才扣减额度
ApiCallLogSaveReqDTO apiCallLogSaveReqVO = new ApiCallLogSaveReqDTO();
if (result instanceof CommonResult) {
if (((CommonResult<?>) result).isSuccess()){
consumeUsage(joinUserApiUsageResults, apiEndpointRespDTO.getConsumptionPoints());
apiCallLogSaveReqVO.setResponseStatus(((CommonResult<?>) result).getCode().toString());
}
String requestBody = ServletUtils.isJsonRequest(request) ? ServletUtils.getBody(request) : null;
apiCallLogApi.createApiCallLog(
apiCallLogSaveReqVO
.setUserId(appCredentialRespDTO.getUserId())
.setCallTime(LocalDateTime.now())
.setMethod(method)
.setPath(path)
.setApiEndpointName(apiEndpointRespDTO.getName())
.setRequestParams(requestBody)
.setResponseParams(JSON.toJSONString(result))
);
}
return result;
}
/** 扣减一次额度(优先扣最近过期的套餐,用 Redis 保证并发安全) */
private void consumeUsage(List<JoinUserApiUsageDTO> joinUserApiUsageDTOS, Integer consumptionPoints) {
JoinUserApiUsageDTO target = null;
LocalDateTime now = LocalDateTime.now();
// 找到过期时间最早且可用的套餐
for (JoinUserApiUsageDTO j : joinUserApiUsageDTOS) {
if (!ObjectUtil.isEmpty(j.getExpireTime()) && j.getExpireTime().isAfter(now)
&& j.getUsedTimes() < j.getPackageTimes()) {
if (ObjectUtil.isEmpty(target) || j.getExpireTime().isBefore(target.getExpireTime())) {
target = j;
}
} }
}
// Redis key:USAGE_KEY + apiEndpointId + userId
String key = ApiSignatureRedisDAO.USAGE_KEY + target.getApiEndPointId() + target.getUserId();
// 自增使用次数
Integer used = signatureRedisDAO.incrementUsageWithExpire(key, consumptionPoints.longValue());
if (!ObjectUtil.isEmpty(used) && used <= target.getPackageTimes()) {
// 异步回写 DB public boolean verifySignature(ApiSignature signature, HttpServletRequest request) {
userApiUsageApi.asyncUpdateUsage(target.getId(), used); // 1.1 校验 Header
} if (!verifyHeaders(signature, request)) {
return false;
} }
public boolean verifySignature(ApiSignature signature, HttpServletRequest request, AppCredentialRespDTO appCredentialDO) {
// 1.2 校验 appId 是否能获取到对应的 appSecret // 1.2 校验 appId 是否能获取到对应的 appSecret
String appId = request.getHeader(signature.appId()); String appId = request.getHeader(signature.appId());
String appSecret = signatureRedisDAO.getAppSecret(appId);
Assert.notNull(appSecret, "[appId({})] 找不到对应的 appSecret", appId);
// 2. 校验签名【重要!】 // 2. 校验签名【重要!】
String clientSignature = request.getHeader(signature.sign()); // 客户端签名 String clientSignature = request.getHeader(signature.sign()); // 客户端签名
String serverSignatureString = buildSignatureString(signature, request, appCredentialDO.getAppSecret()); // 服务端签名字符串 String serverSignatureString = buildSignatureString(signature, request, appSecret); // 服务端签名字符串
String serverSignature = DigestUtil.sha256Hex(serverSignatureString); // 服务端签名 String serverSignature = DigestUtil.sha256Hex(serverSignatureString); // 服务端签名
log.error(DigestUtil.sha256Hex(serverSignatureString));
if (ObjUtil.notEqual(clientSignature, serverSignature)) { if (ObjUtil.notEqual(clientSignature, serverSignature)) {
return false; return false;
} }
// 3. 将 nonce 记入缓存,防止重复使用(重点二:此处需要将 ttl 设定为允许 timestamp 时间差的值 x 2 ) // 3. 将 nonce 记入缓存,防止重复使用(重点二:此处需要将 ttl 设定为允许 timestamp 时间差的值 x 2 )
String nonce = request.getHeader(signature.nonce()); String nonce = request.getHeader(signature.nonce());
signatureRedisDAO.setNonce(appId, nonce, signature.timeout() * 2, signature.timeUnit()); if (BooleanUtil.isFalse(signatureRedisDAO.setNonce(appId, nonce, signature.timeout() * 2, signature.timeUnit()))) {
String timestamp = request.getHeader(signature.timestamp());
log.info("[verifySignature][appId({}) timestamp({}) nonce({}) sign({}) 存在重复请求]", appId, timestamp, nonce, clientSignature);
throw new ServiceException(GlobalErrorCodeConstants.REPEATED_REQUESTS.getCode(), "存在重复请求");
}
return true; return true;
} }
/** /**
* 校验请求头加签参数 * 校验请求头加签参数
* * <p>
* 1. appId 是否为空 * 1. appId 是否为空
* 2. timestamp 是否为空,请求是否已经超时,默认 10 分钟 * 2. timestamp 是否为空,请求是否已经超时,默认 10 分钟
* 3. nonce 是否为空,随机数是否 10 位以上,是否在规定时间内已经访问过了 * 3. nonce 是否为空,随机数是否 10 位以上,是否在规定时间内已经访问过了
...@@ -231,21 +113,18 @@ public class ApiSignatureAspect { ...@@ -231,21 +113,18 @@ public class ApiSignatureAspect {
// 2. 检查 timestamp 是否超出允许的范围 (重点一:此处需要取绝对值) // 2. 检查 timestamp 是否超出允许的范围 (重点一:此处需要取绝对值)
long expireTime = signature.timeUnit().toMillis(signature.timeout()); long expireTime = signature.timeUnit().toMillis(signature.timeout());
long requestTimestamp = Long.parseLong(timestamp); long requestTimestamp = Long.parseLong(timestamp);
log.error("当前时间:"+System.currentTimeMillis()+"");;
long timestampDisparity = Math.abs(System.currentTimeMillis() - requestTimestamp); long timestampDisparity = Math.abs(System.currentTimeMillis() - requestTimestamp);
if (timestampDisparity > expireTime) { if (timestampDisparity > expireTime) {
throw new ServiceException(TIMESTAMP_EXCEPTION); return false;
} }
// 3. 检查 nonce 是否存在,有且仅能使用一次 // 3. 检查 nonce 是否存在,有且仅能使用一次
return signatureRedisDAO.getNonce(appId, nonce) == null; return signatureRedisDAO.getNonce(appId, nonce) == null;
} }
/** /**
* 构建签名字符串 * 构建签名字符串
* * <p>
* 格式为 = 请求参数 + 请求体 + 请求头 + 密钥 * 格式为 = 请求参数 + 请求体 + 请求头 + 密钥
* *
* @param signature signature * @param signature signature
...@@ -254,11 +133,13 @@ public class ApiSignatureAspect { ...@@ -254,11 +133,13 @@ public class ApiSignatureAspect {
* @return 签名字符串 * @return 签名字符串
*/ */
private String buildSignatureString(ApiSignature signature, HttpServletRequest request, String appSecret) { private String buildSignatureString(ApiSignature signature, HttpServletRequest request, String appSecret) {
SortedMap<String, String> parameterMap = getRequestParameterMap(request); // 请求头
SortedMap<String, String> headerMap = getRequestHeaderMap(signature, request); // 请求参数 SortedMap<String, String> headerMap = getRequestHeaderMap(signature, request); // 请求参数
log.error(MapUtil.join(headerMap, "&", "=") String requestBody = StrUtil.nullToDefault(ServletUtils.getBody(request), ""); // 请求体
+ "&appSecret=" + appSecret); return MapUtil.join(parameterMap, "&", "=")
return MapUtil.join(headerMap, "&", "=") + requestBody
+ "&appSecret=" + appSecret; + MapUtil.join(headerMap, "&", "=")
+ appSecret;
} }
/** /**
......
package com.luhu.computility.framework.signature.core.redis; package com.luhu.computility.framework.signature.core.redis;
import cn.hutool.core.util.BooleanUtil;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate;
...@@ -26,11 +25,6 @@ public class ApiSignatureRedisDAO { ...@@ -26,11 +25,6 @@ public class ApiSignatureRedisDAO {
private static final String SIGNATURE_NONCE = "api_signature_nonce:%s:%s"; private static final String SIGNATURE_NONCE = "api_signature_nonce:%s:%s";
/** /**
* 用户套餐使用次数 Key 前缀
*/
public static final String USAGE_KEY = "api_usage:";
/**
* 签名密钥 * 签名密钥
* <p> * <p>
* HASH 结构 * HASH 结构
...@@ -60,54 +54,4 @@ public class ApiSignatureRedisDAO { ...@@ -60,54 +54,4 @@ public class ApiSignatureRedisDAO {
return (String) stringRedisTemplate.opsForHash().get(SIGNATURE_APPID, appId); return (String) stringRedisTemplate.opsForHash().get(SIGNATURE_APPID, appId);
} }
// ========== 用户套餐使用次数 ==========
/**
* 原子自增用户套餐使用次数
*
* @param key Redis key(USAGE_KEY + apiEndpointId + userId)
* @return 当前使用次数
*/
public Long incrementUsage(String key) {
return stringRedisTemplate.opsForValue().increment(key, 1);
}
/**
* 获取用户套餐已使用次数
*
* @param key Redis key
* @return 使用次数
*/
public Long getUsage(String key) {
String value = stringRedisTemplate.opsForValue().get(key);
if (value == null) {
return 0L;
}
try {
return Long.parseLong(value);
} catch (NumberFormatException e) {
return 0L;
}
}
/**
* 重置用户套餐使用次数(可选,用于测试或套餐刷新)
*/
public void resetUsage(String key) {
stringRedisTemplate.opsForValue().set(key, "0");
}
/**
* 增加用户套餐使用次数,并设置过期时间
*
* @param key Redis key
* @return 当前使用次数
*/
public Integer incrementUsageWithExpire(String key, Long consumptionPoints) {
Integer used = stringRedisTemplate.opsForValue().increment(key, consumptionPoints).intValue();
return used;
}
} }
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
<artifactId>computility-module-apihub-api</artifactId> <artifactId>computility-module-apihub-api</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>computility-module-apihub-api</name>
<url>http://maven.apache.org</url> <url>http://maven.apache.org</url>
<properties> <properties>
...@@ -23,11 +22,36 @@ ...@@ -23,11 +22,36 @@
<artifactId>computility-common</artifactId> <artifactId>computility-common</artifactId>
</dependency> </dependency>
<!-- Web 相关 -->
<dependency>
<groupId>com.luhu</groupId>
<artifactId>computility-spring-boot-starter-web</artifactId>
<scope>provided</scope> <!-- 设置为 provided,只有限流、幂等使用到 -->
</dependency>
<!-- 参数校验 --> <!-- 参数校验 -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId> <artifactId>spring-boot-starter-validation</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
</dependency>
<!-- DB 相关 -->
<dependency>
<groupId>com.luhu</groupId>
<artifactId>computility-spring-boot-starter-redis</artifactId>
</dependency>
<!-- 服务保障相关 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
<optional>true</optional>
</dependency>
</dependencies> </dependencies>
</project> </project>
package com.luhu.computility.module.apihub.signature.config;
import com.luhu.computility.framework.redis.config.ComputilityRedisAutoConfiguration;
import com.luhu.computility.module.apihub.signature.core.aop.ApiHubApiSignatureAspect;
import com.luhu.computility.module.apihub.signature.core.redis.ApiHubApiSignatureRedisDAO;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.StringRedisTemplate;
/**
* HTTP API 签名的自动配置类
*
* @author Zhougang
*/
@AutoConfiguration(after = ComputilityRedisAutoConfiguration.class)
public class ComputilityApiHubApiSignatureAutoConfiguration {
@Bean
public ApiHubApiSignatureAspect signatureAspect(ApiHubApiSignatureRedisDAO signatureRedisDAO) {
return new ApiHubApiSignatureAspect(signatureRedisDAO);
}
@Bean
public ApiHubApiSignatureRedisDAO signatureRedisDAO(StringRedisTemplate stringRedisTemplate) {
return new ApiHubApiSignatureRedisDAO(stringRedisTemplate);
}
}
package com.luhu.computility.module.apihub.signature.core.annotation;
import com.luhu.computility.framework.common.exception.enums.GlobalErrorCodeConstants;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;
/**
* HTTP API 签名注解
*
* @author Zhougang
*/
@Inherited
@Documented
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiHubApiSignature {
/**
* 同一个请求多长时间内有效 默认 60 秒
*/
int timeout() default 60;
/**
* 时间单位,默认为 SECONDS 秒
*/
TimeUnit timeUnit() default TimeUnit.SECONDS;
// ========================== 签名参数 ==========================
/**
* 提示信息,签名失败的提示
*
* @see GlobalErrorCodeConstants#BAD_REQUEST
*/
String message() default "签名不正确"; // 为空时,使用 BAD_REQUEST 错误提示
/**
* 签名字段:appId 应用ID
*/
String appId() default "appId";
/**
* 签名字段:timestamp 时间戳
*/
String timestamp() default "timestamp";
/**
* 签名字段:nonce 随机数,10 位以上
*/
String nonce() default "nonce";
/**
* sign 客户端签名
*/
String sign() default "sign";
}
package com.luhu.computility.module.apihub.signature.core.aop;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.fastjson.JSON;
import com.luhu.computility.framework.common.exception.ServiceException;
import com.luhu.computility.framework.common.pojo.CommonResult;
import com.luhu.computility.framework.common.util.servlet.ServletUtils;
import com.luhu.computility.module.apihub.api.apicalllog.ApiCallLogApi;
import com.luhu.computility.module.apihub.api.apicalllog.dto.ApiCallLogSaveReqDTO;
import com.luhu.computility.module.apihub.api.apiendpoint.ApiEndpointApi;
import com.luhu.computility.module.apihub.api.apiendpoint.vo.ApiEndpointRespDTO;
import com.luhu.computility.module.apihub.api.appcredential.AppCredentialApi;
import com.luhu.computility.module.apihub.api.appcredential.dto.AppCredentialRespDTO;
import com.luhu.computility.module.apihub.api.userapiusage.UserApiUsageApi;
import com.luhu.computility.module.apihub.api.userapiusage.dto.JoinUserApiUsageDTO;
import com.luhu.computility.module.apihub.signature.core.annotation.ApiHubApiSignature;
import com.luhu.computility.module.apihub.signature.core.redis.ApiHubApiSignatureRedisDAO;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.SortedMap;
import java.util.TreeMap;
import static com.luhu.computility.module.apihub.enums.ErrorCodeConstants.API_ENDPOINT_EXPIRED;
import static com.luhu.computility.module.apihub.enums.ErrorCodeConstants.API_ENDPOINT_NOT_AVAILABLE;
import static com.luhu.computility.module.apihub.enums.ErrorCodeConstants.API_ENDPOINT_NOT_EXISTS;
import static com.luhu.computility.module.apihub.enums.ErrorCodeConstants.INVALID_APPID;
import static com.luhu.computility.module.apihub.enums.ErrorCodeConstants.TIMESTAMP_EXCEPTION;
/**
* 拦截声明了 {@link ApiHubApiSignature} 注解的方法,实现签名
*
* @author Zhougang
*/
@Aspect
@Slf4j
@AllArgsConstructor
public class ApiHubApiSignatureAspect {
private final ApiHubApiSignatureRedisDAO signatureRedisDAO;
@Resource
private AppCredentialApi appCredentialApi;
@Resource
private ApiEndpointApi apiEndpointApi;
@Resource
private ApiCallLogApi apiCallLogApi;
@Resource
private UserApiUsageApi userApiUsageApi;
public ApiHubApiSignatureAspect(ApiHubApiSignatureRedisDAO signatureRedisDAO) {
this.signatureRedisDAO = signatureRedisDAO;
}
@Around("@annotation(signature)")
public Object aroundPointCut(ProceedingJoinPoint joinPoint, ApiHubApiSignature signature) throws Throwable {
HttpServletRequest request = Objects.requireNonNull(ServletUtils.getRequest());
// 1.校验 Header
/* if (!verifyHeaders(signature, request)) {
throw new ServiceException(HEAD_EXCEPTION);
}*/
//查询appId对应的Secret
String appId = request.getHeader(signature.appId());
AppCredentialRespDTO appCredentialRespDTO = appCredentialApi.getAppSecretByAppid(appId);
if (!ObjectUtil.isEmpty(appCredentialRespDTO)) {
Assert.notNull(appCredentialRespDTO.getAppId(), "[appId({})] 找不到对应的 appSecret", appId);
} else {
throw new ServiceException(INVALID_APPID);
}
// 2. 签名校验
/*if (!verifySignature(signature, request, appCredentialDO)) {
log.error("[aroundPointCut][方法 {} 参数({}) 签名失败]", joinPoint.getSignature(), joinPoint.getArgs());
throw new ServiceException(BAD_REQUEST.getCode(),
StrUtil.blankToDefault(signature.message(), BAD_REQUEST.getMsg()));
}*/
// 2. 应用-接口绑定校验
String path = request.getRequestURI();
String method = request.getMethod();
//2.1是否有该接口
ApiEndpointRespDTO apiEndpointRespDTO = apiEndpointApi.getApiEndpointByPathAndMethod(path, method);
if (Objects.isNull(apiEndpointRespDTO)) {
throw new ServiceException(API_ENDPOINT_NOT_EXISTS);
}
//2.2该接口是否订阅过
List<JoinUserApiUsageDTO> joinUserApiUsageResults = userApiUsageApi.selectJoinUserApiUsageResult(appCredentialRespDTO.getUserId(), apiEndpointRespDTO.getId());
if (CollectionUtil.isEmpty(joinUserApiUsageResults)){
throw new ServiceException(API_ENDPOINT_NOT_AVAILABLE);
}
//3. 用户套餐校验(只校验,不扣减)如果有一个套餐符合条件,则继续往下走
JoinUserApiUsageDTO joinUserApiUsageResult = null;
LocalDateTime now = LocalDateTime.now();
for (JoinUserApiUsageDTO j : joinUserApiUsageResults) {
// 未过期且有剩余额度
if (j.getExpireTime().isAfter(now) && j.getUsedTimes() < j.getPackageTimes()) {
if (joinUserApiUsageResult == null || j.getExpireTime().isBefore(joinUserApiUsageResult.getExpireTime())) {
joinUserApiUsageResult = j;
}
}
}
if (Objects.isNull(joinUserApiUsageResult)) {
throw new ServiceException(API_ENDPOINT_EXPIRED);
}
// 4. 执行目标方法
Object result = joinPoint.proceed();
// 5. 只有当接口执行成功才扣减额度
ApiCallLogSaveReqDTO apiCallLogSaveReqVO = new ApiCallLogSaveReqDTO();
if (result instanceof CommonResult) {
if (((CommonResult<?>) result).isSuccess()){
consumeUsage(joinUserApiUsageResults, apiEndpointRespDTO.getConsumptionPoints());
apiCallLogSaveReqVO.setResponseStatus(((CommonResult<?>) result).getCode().toString());
}
String requestBody = ServletUtils.isJsonRequest(request) ? ServletUtils.getBody(request) : null;
apiCallLogApi.createApiCallLog(
apiCallLogSaveReqVO
.setUserId(appCredentialRespDTO.getUserId())
.setCallTime(LocalDateTime.now())
.setMethod(method)
.setPath(path)
.setApiEndpointName(apiEndpointRespDTO.getName())
.setRequestParams(requestBody)
.setResponseParams(JSON.toJSONString(result))
);
}
return result;
}
/** 扣减一次额度(优先扣最近过期的套餐,用 Redis 保证并发安全) */
private void consumeUsage(List<JoinUserApiUsageDTO> joinUserApiUsageDTOS, Integer consumptionPoints) {
JoinUserApiUsageDTO target = null;
LocalDateTime now = LocalDateTime.now();
// 找到过期时间最早且可用的套餐
for (JoinUserApiUsageDTO j : joinUserApiUsageDTOS) {
if (!ObjectUtil.isEmpty(j.getExpireTime()) && j.getExpireTime().isAfter(now)
&& j.getUsedTimes() < j.getPackageTimes()) {
if (ObjectUtil.isEmpty(target) || j.getExpireTime().isBefore(target.getExpireTime())) {
target = j;
}
}
}
// Redis key:USAGE_KEY + apiEndpointId + userId
String key = ApiHubApiSignatureRedisDAO.USAGE_KEY + target.getApiEndPointId() + target.getUserId();
// 自增使用次数
Integer used = signatureRedisDAO.incrementUsageWithExpire(key, consumptionPoints.longValue());
if (!ObjectUtil.isEmpty(used) && used <= target.getPackageTimes()) {
// 异步回写 DB
userApiUsageApi.asyncUpdateUsage(target.getId(), used);
}
}
public boolean verifySignature(ApiHubApiSignature signature, HttpServletRequest request, AppCredentialRespDTO appCredentialDO) {
// 1.2 校验 appId 是否能获取到对应的 appSecret
String appId = request.getHeader(signature.appId());
// 2. 校验签名【重要!】
String clientSignature = request.getHeader(signature.sign()); // 客户端签名
String serverSignatureString = buildSignatureString(signature, request, appCredentialDO.getAppSecret()); // 服务端签名字符串
String serverSignature = DigestUtil.sha256Hex(serverSignatureString); // 服务端签名
log.error(DigestUtil.sha256Hex(serverSignatureString));
if (ObjUtil.notEqual(clientSignature, serverSignature)) {
return false;
}
// 3. 将 nonce 记入缓存,防止重复使用(重点二:此处需要将 ttl 设定为允许 timestamp 时间差的值 x 2 )
String nonce = request.getHeader(signature.nonce());
signatureRedisDAO.setNonce(appId, nonce, signature.timeout() * 2, signature.timeUnit());
return true;
}
/**
* 校验请求头加签参数
*
* 1. appId 是否为空
* 2. timestamp 是否为空,请求是否已经超时,默认 10 分钟
* 3. nonce 是否为空,随机数是否 10 位以上,是否在规定时间内已经访问过了
* 4. sign 是否为空
*
* @param signature signature
* @param request request
* @return 是否校验 Header 通过
*/
private boolean verifyHeaders(ApiHubApiSignature signature, HttpServletRequest request) {
// 1. 非空校验
String appId = request.getHeader(signature.appId());
if (StrUtil.isBlank(appId)) {
return false;
}
String timestamp = request.getHeader(signature.timestamp());
if (StrUtil.isBlank(timestamp)) {
return false;
}
String nonce = request.getHeader(signature.nonce());
if (StrUtil.length(nonce) < 10) {
return false;
}
String sign = request.getHeader(signature.sign());
if (StrUtil.isBlank(sign)) {
return false;
}
// 2. 检查 timestamp 是否超出允许的范围 (重点一:此处需要取绝对值)
long expireTime = signature.timeUnit().toMillis(signature.timeout());
long requestTimestamp = Long.parseLong(timestamp);
log.error("当前时间:"+System.currentTimeMillis()+"");;
long timestampDisparity = Math.abs(System.currentTimeMillis() - requestTimestamp);
if (timestampDisparity > expireTime) {
throw new ServiceException(TIMESTAMP_EXCEPTION);
}
// 3. 检查 nonce 是否存在,有且仅能使用一次
return signatureRedisDAO.getNonce(appId, nonce) == null;
}
/**
* 构建签名字符串
*
* 格式为 = 请求参数 + 请求体 + 请求头 + 密钥
*
* @param signature signature
* @param request request
* @param appSecret appSecret
* @return 签名字符串
*/
private String buildSignatureString(ApiHubApiSignature signature, HttpServletRequest request, String appSecret) {
SortedMap<String, String> headerMap = getRequestHeaderMap(signature, request); // 请求参数
log.error(MapUtil.join(headerMap, "&", "=")
+ "&appSecret=" + appSecret);
return MapUtil.join(headerMap, "&", "=")
+ "&appSecret=" + appSecret;
}
/**
* 获取请求头加签参数 Map
*
* @param request 请求
* @param signature 签名注解
* @return signature params
*/
private static SortedMap<String, String> getRequestHeaderMap(ApiHubApiSignature signature, HttpServletRequest request) {
SortedMap<String, String> sortedMap = new TreeMap<>();
sortedMap.put(signature.appId(), request.getHeader(signature.appId()));
sortedMap.put(signature.timestamp(), request.getHeader(signature.timestamp()));
sortedMap.put(signature.nonce(), request.getHeader(signature.nonce()));
return sortedMap;
}
/**
* 获取请求参数 Map
*
* @param request 请求
* @return queryParams
*/
private static SortedMap<String, String> getRequestParameterMap(HttpServletRequest request) {
SortedMap<String, String> sortedMap = new TreeMap<>();
for (Map.Entry<String, String[]> entry : request.getParameterMap().entrySet()) {
sortedMap.put(entry.getKey(), entry.getValue()[0]);
}
return sortedMap;
}
}
\ No newline at end of file
package com.luhu.computility.module.apihub.signature.core.redis;
import lombok.AllArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.concurrent.TimeUnit;
/**
* HTTP API 签名 Redis DAO
*
* @author Zhougang
*/
@AllArgsConstructor
public class ApiHubApiSignatureRedisDAO {
private final StringRedisTemplate stringRedisTemplate;
/**
* 验签随机数
* <p>
* KEY 格式:signature_nonce:%s // 参数为 随机数
* VALUE 格式:String
* 过期时间:不固定
*/
private static final String SIGNATURE_NONCE = "api_signature_nonce:%s:%s";
/**
* 用户套餐使用次数 Key 前缀
*/
public static final String USAGE_KEY = "api_usage:";
/**
* 签名密钥
* <p>
* HASH 结构
* KEY 格式:%s // 参数为 appid
* VALUE 格式:String
* 过期时间:永不过期(预加载到 Redis)
*/
private static final String SIGNATURE_APPID = "api_signature_app";
// ========== 验签随机数 ==========
public String getNonce(String appId, String nonce) {
return stringRedisTemplate.opsForValue().get(formatNonceKey(appId, nonce));
}
public Boolean setNonce(String appId, String nonce, int time, TimeUnit timeUnit) {
return stringRedisTemplate.opsForValue().setIfAbsent(formatNonceKey(appId, nonce), "", time, timeUnit);
}
private static String formatNonceKey(String appId, String nonce) {
return String.format(SIGNATURE_NONCE, appId, nonce);
}
// ========== 签名密钥 ==========
public String getAppSecret(String appId) {
return (String) stringRedisTemplate.opsForHash().get(SIGNATURE_APPID, appId);
}
// ========== 用户套餐使用次数 ==========
/**
* 原子自增用户套餐使用次数
*
* @param key Redis key(USAGE_KEY + apiEndpointId + userId)
* @return 当前使用次数
*/
public Long incrementUsage(String key) {
return stringRedisTemplate.opsForValue().increment(key, 1);
}
/**
* 获取用户套餐已使用次数
*
* @param key Redis key
* @return 使用次数
*/
public Long getUsage(String key) {
String value = stringRedisTemplate.opsForValue().get(key);
if (value == null) {
return 0L;
}
try {
return Long.parseLong(value);
} catch (NumberFormatException e) {
return 0L;
}
}
/**
* 重置用户套餐使用次数(可选,用于测试或套餐刷新)
*/
public void resetUsage(String key) {
stringRedisTemplate.opsForValue().set(key, "0");
}
/**
* 增加用户套餐使用次数,并设置过期时间
*
* @param key Redis key
* @return 当前使用次数
*/
public Integer incrementUsageWithExpire(String key, Long consumptionPoints) {
Integer used = stringRedisTemplate.opsForValue().increment(key, consumptionPoints).intValue();
return used;
}
}
/**
* HTTP API 签名,校验安全性
*
* @see <a href="https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3>微信支付 —— 安全规范</a>
*/
package com.luhu.computility.module.apihub.signature;
\ No newline at end of file
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
<artifactId>computility-module-apihub-biz</artifactId> <artifactId>computility-module-apihub-biz</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>computility-module-apihub-biz</name>
<url>http://maven.apache.org</url> <url>http://maven.apache.org</url>
<properties> <properties>
......
...@@ -2,7 +2,6 @@ package com.luhu.computility.module.apihub.api; ...@@ -2,7 +2,6 @@ package com.luhu.computility.module.apihub.api;
import com.luhu.computility.module.apihub.api.apiendpoint.ApiEndpointApi; import com.luhu.computility.module.apihub.api.apiendpoint.ApiEndpointApi;
import com.luhu.computility.module.apihub.api.apiendpoint.vo.ApiEndpointRespDTO; import com.luhu.computility.module.apihub.api.apiendpoint.vo.ApiEndpointRespDTO;
import com.luhu.computility.module.apihub.dal.mysql.apicalllog.ApiCallLogMapper;
import com.luhu.computility.module.apihub.dal.mysql.apiendpoint.ApiEndpointMapper; import com.luhu.computility.module.apihub.dal.mysql.apiendpoint.ApiEndpointMapper;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
...@@ -16,7 +15,7 @@ import javax.annotation.Resource; ...@@ -16,7 +15,7 @@ import javax.annotation.Resource;
*/ */
@Service @Service
@Validated @Validated
public class ApiEndpointServiceImpl implements ApiEndpointApi { public class ApiEndpointApiServiceImpl implements ApiEndpointApi {
@Resource @Resource
private ApiEndpointMapper apiEndpointMapper; private ApiEndpointMapper apiEndpointMapper;
......
...@@ -2,7 +2,7 @@ package com.luhu.computility.module.apihub.controller.admin.apiendpoint; ...@@ -2,7 +2,7 @@ package com.luhu.computility.module.apihub.controller.admin.apiendpoint;
import com.luhu.computility.module.apihub.controller.admin.apiendpointapplicationrel.vo.ApiEndpointApplicationRelSaveReqVO; import com.luhu.computility.module.apihub.controller.admin.apiendpointapplicationrel.vo.ApiEndpointApplicationRelSaveReqVO;
import com.luhu.computility.module.apihub.service.apiendpointapplicationrel.ApiEndpointApplicationRelService; import com.luhu.computility.module.apihub.service.apiendpointapplicationrel.ApiEndpointApplicationRelService;
import com.luhu.computility.module.biz.controller.admin.industryapplication.vo.IndustryApplicationRespVO; import com.luhu.computility.module.biz.api.industryapplication.dto.IndustryApplicationRespDTO;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
...@@ -52,10 +52,10 @@ public class ApiEndpointController { ...@@ -52,10 +52,10 @@ public class ApiEndpointController {
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public CommonResult<Long> createApiEndpoint(@Valid @RequestBody ApiEndpointSaveReqVO createReqVO) { public CommonResult<Long> createApiEndpoint(@Valid @RequestBody ApiEndpointSaveReqVO createReqVO) {
Long apiEndpointId = apiEndpointService.createApiEndpoint(createReqVO); Long apiEndpointId = apiEndpointService.createApiEndpoint(createReqVO);
for (IndustryApplicationRespVO industryApplicationRespVO : createReqVO.getIndustryApplications()) { for (IndustryApplicationRespDTO industryApplicationRespDTO : createReqVO.getIndustryApplications()) {
apiEndpointApplicationRelService.createApiEndpointApplicationRel( apiEndpointApplicationRelService.createApiEndpointApplicationRel(
new ApiEndpointApplicationRelSaveReqVO() new ApiEndpointApplicationRelSaveReqVO()
.setIndustryApplicationId(industryApplicationRespVO.getId()) .setIndustryApplicationId(industryApplicationRespDTO.getId())
.setApiEndpointId(apiEndpointId)); .setApiEndpointId(apiEndpointId));
} }
return success(apiEndpointId); return success(apiEndpointId);
...@@ -69,10 +69,10 @@ public class ApiEndpointController { ...@@ -69,10 +69,10 @@ public class ApiEndpointController {
//删除以前的 //删除以前的
apiEndpointApplicationRelService.deleteApiEndpointApplicationRelByApiEndpointId(updateReqVO.getId()); apiEndpointApplicationRelService.deleteApiEndpointApplicationRelByApiEndpointId(updateReqVO.getId());
//插入新数据 //插入新数据
for (IndustryApplicationRespVO industryApplicationRespVO : updateReqVO.getIndustryApplications()) { for (IndustryApplicationRespDTO industryApplicationRespDTO : updateReqVO.getIndustryApplications()) {
apiEndpointApplicationRelService.createApiEndpointApplicationRel( apiEndpointApplicationRelService.createApiEndpointApplicationRel(
new ApiEndpointApplicationRelSaveReqVO() new ApiEndpointApplicationRelSaveReqVO()
.setIndustryApplicationId(industryApplicationRespVO.getId()) .setIndustryApplicationId(industryApplicationRespDTO.getId())
.setApiEndpointId(updateReqVO.getId())); .setApiEndpointId(updateReqVO.getId()));
} }
return success(true); return success(true);
...@@ -108,8 +108,8 @@ public class ApiEndpointController { ...@@ -108,8 +108,8 @@ public class ApiEndpointController {
public CommonResult<ApiEndpointRespVO> getApiEndpoint(@RequestParam("id") Long id) { public CommonResult<ApiEndpointRespVO> getApiEndpoint(@RequestParam("id") Long id) {
ApiEndpointDO apiEndpoint = apiEndpointService.getApiEndpoint(id); ApiEndpointDO apiEndpoint = apiEndpointService.getApiEndpoint(id);
ApiEndpointRespVO apiEndpointRespVO = BeanUtils.toBean(apiEndpoint, ApiEndpointRespVO.class); ApiEndpointRespVO apiEndpointRespVO = BeanUtils.toBean(apiEndpoint, ApiEndpointRespVO.class);
List<IndustryApplicationRespVO> industryApplicationRespVOS = apiEndpointApplicationRelService.getApplicationByApiEndpointId(id); List<IndustryApplicationRespDTO> industryApplicationRespDTOS = apiEndpointApplicationRelService.getApplicationByApiEndpointId(id);
apiEndpointRespVO.setIndustryApplications(industryApplicationRespVOS); apiEndpointRespVO.setIndustryApplications(industryApplicationRespDTOS);
return success(apiEndpointRespVO); return success(apiEndpointRespVO);
} }
...@@ -129,8 +129,8 @@ public class ApiEndpointController { ...@@ -129,8 +129,8 @@ public class ApiEndpointController {
List<ApiEndpointDO> apiEndpointDOS = apiEndpointService.getApiEndpointList(); List<ApiEndpointDO> apiEndpointDOS = apiEndpointService.getApiEndpointList();
List<ApiEndpointRespVO> apiEndpointRespVOS = BeanUtils.toBean(apiEndpointDOS, ApiEndpointRespVO.class); List<ApiEndpointRespVO> apiEndpointRespVOS = BeanUtils.toBean(apiEndpointDOS, ApiEndpointRespVO.class);
for (ApiEndpointRespVO apiEndpointRespVO : apiEndpointRespVOS) { for (ApiEndpointRespVO apiEndpointRespVO : apiEndpointRespVOS) {
List<IndustryApplicationRespVO> industryApplicationRespVOS = apiEndpointApplicationRelService.getApplicationByApiEndpointId(apiEndpointRespVO.getId()); List<IndustryApplicationRespDTO> industryApplicationRespDTOS = apiEndpointApplicationRelService.getApplicationByApiEndpointId(apiEndpointRespVO.getId());
apiEndpointRespVO.setIndustryApplications(industryApplicationRespVOS); apiEndpointRespVO.setIndustryApplications(industryApplicationRespDTOS);
} }
return success(apiEndpointRespVOS); return success(apiEndpointRespVOS);
} }
......
package com.luhu.computility.module.apihub.controller.admin.apiendpoint.vo; package com.luhu.computility.module.apihub.controller.admin.apiendpoint.vo;
import com.luhu.computility.module.biz.controller.admin.industryapplication.vo.IndustryApplicationRespVO; import com.luhu.computility.module.biz.api.industryapplication.dto.IndustryApplicationRespDTO;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*; import lombok.*;
import java.util.*; import java.util.*;
...@@ -63,6 +63,6 @@ public class ApiEndpointRespVO { ...@@ -63,6 +63,6 @@ public class ApiEndpointRespVO {
@Schema(description = "关联行业应用", example = "[{}]") @Schema(description = "关联行业应用", example = "[{}]")
@ExcelProperty("关联行业应用") @ExcelProperty("关联行业应用")
private List<IndustryApplicationRespVO> industryApplications; private List<IndustryApplicationRespDTO> industryApplications;
} }
\ No newline at end of file
package com.luhu.computility.module.apihub.controller.admin.apiendpoint.vo; package com.luhu.computility.module.apihub.controller.admin.apiendpoint.vo;
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.ExcelProperty;
import com.luhu.computility.module.biz.controller.admin.industryapplication.vo.IndustryApplicationRespVO; import com.luhu.computility.module.biz.api.industryapplication.dto.IndustryApplicationRespDTO;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*; import lombok.*;
import java.util.*; import java.util.*;
......
...@@ -20,7 +20,7 @@ import com.luhu.computility.module.apihub.service.apicategory.ApiCategoryService ...@@ -20,7 +20,7 @@ import com.luhu.computility.module.apihub.service.apicategory.ApiCategoryService
import com.luhu.computility.module.apihub.service.apiendpointapplicationrel.ApiEndpointApplicationRelService; import com.luhu.computility.module.apihub.service.apiendpointapplicationrel.ApiEndpointApplicationRelService;
import com.luhu.computility.module.apihub.service.apiendpointrel.ApiEndpointRelService; import com.luhu.computility.module.apihub.service.apiendpointrel.ApiEndpointRelService;
import com.luhu.computility.module.apihub.service.apipackage.ApiPackageService; import com.luhu.computility.module.apihub.service.apipackage.ApiPackageService;
import com.luhu.computility.module.biz.controller.admin.industryapplication.vo.IndustryApplicationRespVO; import com.luhu.computility.module.biz.api.industryapplication.dto.IndustryApplicationRespDTO;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
...@@ -81,8 +81,8 @@ public class AppApiController { ...@@ -81,8 +81,8 @@ public class AppApiController {
List<ApiEndpointRespVO> apiEndpointRespVOS = apiEndpointRelService.getApiEndpointByApiId(id); List<ApiEndpointRespVO> apiEndpointRespVOS = apiEndpointRelService.getApiEndpointByApiId(id);
for (ApiEndpointRespVO apiEndpointRespVO : apiEndpointRespVOS) { for (ApiEndpointRespVO apiEndpointRespVO : apiEndpointRespVOS) {
List<IndustryApplicationRespVO> industryApplicationRespVOS = apiEndpointApplicationRelService.getApplicationByApiEndpointId(apiEndpointRespVO.getId()); List<IndustryApplicationRespDTO> industryApplicationRespDTOS = apiEndpointApplicationRelService.getApplicationByApiEndpointId(apiEndpointRespVO.getId());
apiEndpointRespVO.setIndustryApplications(industryApplicationRespVOS); apiEndpointRespVO.setIndustryApplications(industryApplicationRespDTOS);
} }
apiRespVO.setApiEndPoints(apiEndpointRespVOS); apiRespVO.setApiEndPoints(apiEndpointRespVOS);
......
...@@ -11,10 +11,10 @@ import com.luhu.computility.module.apihub.dal.dataobject.apiendpoint.ApiEndpoint ...@@ -11,10 +11,10 @@ import com.luhu.computility.module.apihub.dal.dataobject.apiendpoint.ApiEndpoint
import com.luhu.computility.module.apihub.dal.dataobject.apiendpointapplicationrel.ApiEndpointApplicationRelDO; import com.luhu.computility.module.apihub.dal.dataobject.apiendpointapplicationrel.ApiEndpointApplicationRelDO;
import com.luhu.computility.module.apihub.dal.dataobject.apiendpointrel.ApiEndpointRelDO; import com.luhu.computility.module.apihub.dal.dataobject.apiendpointrel.ApiEndpointRelDO;
import com.luhu.computility.module.biz.api.industryapplication.dto.IndustryApplicationRespDTO; import com.luhu.computility.module.biz.api.industryapplication.dto.IndustryApplicationRespDTO;
import com.luhu.computility.module.biz.controller.admin.industryapplication.vo.IndustryApplicationRespVO;
import com.luhu.computility.module.biz.dal.dataobject.industryapplication.IndustryApplicationDO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import com.luhu.computility.module.apihub.controller.admin.apiendpointapplicationrel.vo.*; import com.luhu.computility.module.apihub.controller.admin.apiendpointapplicationrel.vo.*;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
/** /**
* 行业应用与接口关联 Mapper * 行业应用与接口关联 Mapper
...@@ -37,14 +37,16 @@ public interface ApiEndpointApplicationRelMapper extends BaseMapperX<ApiEndpoint ...@@ -37,14 +37,16 @@ public interface ApiEndpointApplicationRelMapper extends BaseMapperX<ApiEndpoint
.eqIfPresent(ApiEndpointApplicationRelDO::getApiEndpointId, apiEndpointId)); .eqIfPresent(ApiEndpointApplicationRelDO::getApiEndpointId, apiEndpointId));
} }
@Select("SELECT ia.id AS id, ia.title AS title " +
"FROM apihub_api_endpoint_application_rel rel " +
"LEFT JOIN biz_industry_application ia ON rel.industry_application_id = ia.id " +
"WHERE rel.api_endpoint_id = #{apiEndpointId} and rel.deleted = 0 and ia.deleted = 0")
List<IndustryApplicationRespDTO> selectIndustryApplicationByApiId(@Param("apiEndpointId") Long apiEndpointId);
default List<IndustryApplicationRespDTO> getApiEndpointByApiId(Long apiEndpointId) { default List<IndustryApplicationRespDTO> getApiEndpointByApiId(Long apiEndpointId) {
return selectJoinList(IndustryApplicationRespDTO.class, new MPJLambdaWrapperX<ApiEndpointApplicationRelDO>() return this.selectIndustryApplicationByApiId(apiEndpointId);
.selectAs(IndustryApplicationDO::getTitle, IndustryApplicationRespVO::getTitle)
.selectAs(ApiEndpointApplicationRelDO::getIndustryApplicationId, IndustryApplicationRespVO::getId)
.leftJoin(IndustryApplicationDO.class, IndustryApplicationDO::getId, ApiEndpointApplicationRelDO::getIndustryApplicationId)
.eqIfPresent(ApiEndpointApplicationRelDO::getApiEndpointId, apiEndpointId)
);
} }
} }
\ No newline at end of file
...@@ -8,7 +8,7 @@ import com.luhu.computility.module.apihub.controller.admin.apiendpointapplicatio ...@@ -8,7 +8,7 @@ import com.luhu.computility.module.apihub.controller.admin.apiendpointapplicatio
import com.luhu.computility.module.apihub.dal.dataobject.apiendpointapplicationrel.ApiEndpointApplicationRelDO; import com.luhu.computility.module.apihub.dal.dataobject.apiendpointapplicationrel.ApiEndpointApplicationRelDO;
import com.luhu.computility.framework.common.pojo.PageResult; import com.luhu.computility.framework.common.pojo.PageResult;
import com.luhu.computility.framework.common.pojo.PageParam; import com.luhu.computility.framework.common.pojo.PageParam;
import com.luhu.computility.module.biz.controller.admin.industryapplication.vo.IndustryApplicationRespVO; import com.luhu.computility.module.biz.api.industryapplication.dto.IndustryApplicationRespDTO;
/** /**
* 行业应用与接口关联 Service 接口 * 行业应用与接口关联 Service 接口
...@@ -53,7 +53,7 @@ public interface ApiEndpointApplicationRelService { ...@@ -53,7 +53,7 @@ public interface ApiEndpointApplicationRelService {
* @param apiEndpointId * @param apiEndpointId
* @return 行业应用 * @return 行业应用
*/ */
List<IndustryApplicationRespVO> getApplicationByApiEndpointId(Long apiEndpointId); List<IndustryApplicationRespDTO> getApplicationByApiEndpointId(Long apiEndpointId);
/** /**
......
package com.luhu.computility.module.apihub.service.apiendpointapplicationrel; package com.luhu.computility.module.apihub.service.apiendpointapplicationrel;
import com.luhu.computility.module.biz.controller.admin.industryapplication.vo.IndustryApplicationRespVO; import com.luhu.computility.module.biz.api.industryapplication.dto.IndustryApplicationRespDTO;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
...@@ -87,7 +87,7 @@ public class ApiEndpointApplicationRelServiceImpl implements ApiEndpointApplicat ...@@ -87,7 +87,7 @@ public class ApiEndpointApplicationRelServiceImpl implements ApiEndpointApplicat
} }
@Override @Override
public List<IndustryApplicationRespVO> getApplicationByApiEndpointId(Long apiEndpointId) { public List<IndustryApplicationRespDTO> getApplicationByApiEndpointId(Long apiEndpointId) {
return apiEndpointApplicationRelMapper.getApiEndpointByApiId(apiEndpointId); return apiEndpointApplicationRelMapper.getApiEndpointByApiId(apiEndpointId);
} }
......
...@@ -4,13 +4,11 @@ ...@@ -4,13 +4,11 @@
<parent> <parent>
<groupId>com.luhu</groupId> <groupId>com.luhu</groupId>
<artifactId>computility-module-biz</artifactId> <artifactId>computility-module-biz</artifactId>
<version>2.6.0-jdk8-SNAPSHOT</version> <version>${revision}</version>
</parent> </parent>
<artifactId>computility-module-biz-api</artifactId> <artifactId>computility-module-biz-api</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>computility-module-biz-api</name>
<url>http://maven.apache.org</url> <url>http://maven.apache.org</url>
<properties> <properties>
...@@ -24,5 +22,9 @@ ...@@ -24,5 +22,9 @@
<version>3.8.1</version> <version>3.8.1</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>com.luhu</groupId>
<artifactId>computility-common</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>
package com.luhu.computility.module.biz.api.industryapplication;
import com.luhu.computility.framework.apilog.core.annotation.ApiAccessLog;
import com.luhu.computility.framework.common.pojo.CommonResult;
import com.luhu.computility.framework.common.pojo.PageParam;
import com.luhu.computility.framework.common.pojo.PageResult;
import com.luhu.computility.framework.common.util.object.BeanUtils;
import com.luhu.computility.framework.excel.core.util.ExcelUtils;
import com.luhu.computility.module.biz.api.industryapplication.dto.IndustryApplicationPageReqVO;
import com.luhu.computility.module.biz.api.industryapplication.dto.IndustryApplicationRespDTO;
import com.luhu.computility.module.biz.api.industryapplication.dto.IndustryApplicationSaveReqVO;
import com.luhu.computility.module.biz.dal.dataobject.industryapplication.IndustryApplicationDO;
import com.luhu.computility.module.biz.service.industryapplication.IndustryApplicationService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.List;
import static com.luhu.computility.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static com.luhu.computility.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 行业应用")
@RestController
@RequestMapping("/biz/industry-application")
@Validated
public class IndustryApplicationController {
@Resource
private IndustryApplicationService industryApplicationService;
@PostMapping("/create")
@Operation(summary = "创建行业应用")
@PreAuthorize("@ss.hasPermission('biz:industry-application:create')")
public CommonResult<Long> createIndustryApplication(@Valid @RequestBody IndustryApplicationSaveReqVO createReqVO) {
return success(industryApplicationService.createIndustryApplication(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新行业应用")
@PreAuthorize("@ss.hasPermission('biz:industry-application:update')")
public CommonResult<Boolean> updateIndustryApplication(@Valid @RequestBody IndustryApplicationSaveReqVO updateReqVO) {
industryApplicationService.updateIndustryApplication(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除行业应用")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('biz:industry-application:delete')")
public CommonResult<Boolean> deleteIndustryApplication(@RequestParam("id") Long id) {
industryApplicationService.deleteIndustryApplication(id);
return success(true);
}
@DeleteMapping("/delete-list")
@Parameter(name = "ids", description = "编号", required = true)
@Operation(summary = "批量删除行业应用")
@PreAuthorize("@ss.hasPermission('biz:industry-application:delete')")
public CommonResult<Boolean> deleteIndustryApplicationList(@RequestParam("ids") List<Long> ids) {
industryApplicationService.deleteIndustryApplicationListByIds(ids);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得行业应用")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('biz:industry-application:query')")
public CommonResult<IndustryApplicationRespDTO> getIndustryApplication(@RequestParam("id") Long id) {
IndustryApplicationDO industryApplication = industryApplicationService.getIndustryApplication(id);
IndustryApplicationRespDTO respVO = BeanUtils.toBean(industryApplication, IndustryApplicationRespDTO.class);
return success(respVO);
}
@GetMapping("/page")
@Operation(summary = "获得行业应用分页")
@PreAuthorize("@ss.hasPermission('biz:industry-application:query')")
public CommonResult<PageResult<IndustryApplicationRespDTO>> getIndustryApplicationPage(@Valid IndustryApplicationPageReqVO pageReqVO) {
PageResult<IndustryApplicationDO> pageResult = industryApplicationService.getIndustryApplicationPage(pageReqVO);
PageResult<IndustryApplicationRespDTO> respResult = BeanUtils.toBean(pageResult, IndustryApplicationRespDTO.class);
return success(respResult);
}
@GetMapping("/list")
@Operation(summary = "获得行业应用分页")
@PreAuthorize("@ss.hasPermission('biz:industry-application:query')")
public CommonResult<List<IndustryApplicationRespDTO>> getIndustryApplicationList() {
List<IndustryApplicationDO> list = industryApplicationService.getAllIndustryApplication(); // 新增Service方法
List<IndustryApplicationRespDTO> result = BeanUtils.toBean(list, IndustryApplicationRespDTO.class);
return success(result);
}
@GetMapping("/export-excel")
@Operation(summary = "导出行业应用 Excel")
@PreAuthorize("@ss.hasPermission('biz:industry-application:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportIndustryApplicationExcel(@Valid IndustryApplicationPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<IndustryApplicationDO> list = industryApplicationService.getIndustryApplicationPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "行业应用.xls", "数据", IndustryApplicationRespDTO.class,
BeanUtils.toBean(list, IndustryApplicationRespDTO.class));
}
}
\ No newline at end of file
package com.luhu.computility.module.biz.api.industryapplication.dto;
import com.luhu.computility.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static com.luhu.computility.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 行业应用分页 Request VO")
@Data
public class IndustryApplicationPageReqVO extends PageParam {
@Schema(description = "标题")
private String title;
@Schema(description = "状态:0-已隐藏,1-已显示", example = "1")
private Integer showStatus;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
@Schema(description = "首页展示图片")
private String homeImage;
@Schema(description = "组件类型:0-全部", example = "1")
private Integer type;
@Schema(description = "排序值")
private Integer orderNum;
}
\ No newline at end of file
package com.luhu.computility.module.biz.api.industryapplication.dto; package com.luhu.computility.module.biz.api.industryapplication.dto;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@Schema(description = "管理后台 - 行业应用 Response VO")
@Data @Data
@ExcelIgnoreUnannotated
public class IndustryApplicationRespDTO { public class IndustryApplicationRespDTO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id; private Long id;
@Schema(description = "展示图地址")
@ExcelProperty("展示图地址")
private String image; private String image;
@Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("内容")
private String information; private String information;
@Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("标题")
private String title; private String title;
@Schema(description = "链接地址", example = "https://www.iocoder.cn")
@ExcelProperty("链接地址")
private String url; private String url;
@Schema(description = "描述内容", example = "随便")
@ExcelProperty("描述内容")
private String description; private String description;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime; private LocalDateTime createTime;
@Schema(description = "首页展示图片")
@ExcelProperty("首页展示图片")
private String homeImage; private String homeImage;
@Schema(description = "组件类型:0-全部", example = "1")
@ExcelProperty("组件类型:0-全部")
private Integer type; private Integer type;
@Schema(description = "排序值")
@ExcelProperty("排序值")
private Integer orderNum; private Integer orderNum;
@Schema(description = "状态:0-已隐藏,1-已显示", example = "1")
private Integer showStatus; private Integer showStatus;
} }
\ No newline at end of file
package com.luhu.computility.module.biz.api.industryapplication.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Schema(description = "管理后台 - 行业应用新增/修改 Request VO")
@Data
public class IndustryApplicationSaveReqVO {
@Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "21619")
private Long id;
@Schema(description = "展示图地址")
private String image;
@Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "内容不能为空")
private String information;
@Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "标题不能为空")
private String title;
@Schema(description = "链接地址", example = "https://www.iocoder.cn")
private String url;
@Schema(description = "描述内容", example = "随便")
private String description;
@Schema(description = "状态:0-已隐藏,1-已显示", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "状态:0-已隐藏,1-已显示不能为空")
private Integer showStatus;
@Schema(description = "备注", example = "你猜")
private String remark;
@Schema(description = "首页展示图片")
private String homeImage;
@Schema(description = "组件类型:0-全部", example = "1")
private Integer type;
@Schema(description = "排序值")
private Integer orderNum;
}
\ No newline at end of file
package com.luhu.computility.module.biz.enums;
import com.luhu.computility.framework.common.exception.ErrorCode;
/**
* AI 错误码枚举类
* <p>
* ai 系统,使用 1-040-000-000 段
*/
public interface ErrorCodeConstants {
// ========== AI 工作流 1-040-011-000 ==========
ErrorCode INDUSTRY_APPLICATION_NOT_EXISTS = new ErrorCode(1_040_011_000, "行业应用不存在");
ErrorCode BANNER_INFO_NOT_EXISTS = new ErrorCode(1_040_012_000, "首页banner不存在");
ErrorCode COMPUTILITY_INFORMATION_NOT_EXISTS = new ErrorCode(1_040_013_000, "算力资源不存在");
ErrorCode INFORMATION_NOT_EXISTS = new ErrorCode(1_040_014_000, "活动资讯管理不存在");
ErrorCode ORDER_NOT_EXISTS = new ErrorCode(1_040_015_000, "订单管理-需求单管理不存在");
ErrorCode PARTNER_NOT_EXISTS = new ErrorCode(1_040_016_000, "合作伙伴管理不存在");
ErrorCode SOLUTION_NOT_EXISTS = new ErrorCode(1_040_017_000, "解决方案不存在");
ErrorCode GET_SOLUTION_ERROR = new ErrorCode(1_040_017_001, "无法获取详情,请重试!");
}
package com.luhu.computility.module.biz.enums;
/**
* 客户企业审核状态
*/
public enum ShowStatus {
// 请根据实际情况改成对应的值和备注
NO(0, "隐藏"),
YES(1, "展示");
private int value;
private String remark;
private ShowStatus(int value, String remark) {
this.value = value;
this.remark = remark;
}
public int getValue() {
return value;
}
public String getRemark() {
return remark;
}
public static ShowStatus getByValue(int value) {
for (ShowStatus o : ShowStatus.values()) {
if (o.getValue() == value) {
return o;
}
}
return null;
}
}
...@@ -4,13 +4,12 @@ ...@@ -4,13 +4,12 @@
<parent> <parent>
<groupId>com.luhu</groupId> <groupId>com.luhu</groupId>
<artifactId>computility-module-biz</artifactId> <artifactId>computility-module-biz</artifactId>
<version>2.6.0-jdk8-SNAPSHOT</version> <version>${revision}</version>
</parent> </parent>
<artifactId>computility-module-biz-biz</artifactId> <artifactId>computility-module-biz-biz</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>computility-module-biz-biz</name>
<url>http://maven.apache.org</url> <url>http://maven.apache.org</url>
<properties> <properties>
...@@ -24,6 +23,10 @@ ...@@ -24,6 +23,10 @@
<version>3.8.1</version> <version>3.8.1</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>com.luhu</groupId>
<artifactId>computility-common</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.luhu</groupId> <groupId>com.luhu</groupId>
...@@ -33,6 +36,12 @@ ...@@ -33,6 +36,12 @@
<dependency> <dependency>
<groupId>com.luhu</groupId> <groupId>com.luhu</groupId>
<artifactId>computility-module-apihub-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.luhu</groupId>
<artifactId>computility-module-trade</artifactId> <artifactId>computility-module-trade</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
......
...@@ -20,7 +20,6 @@ import com.luhu.computility.module.trade.controller.admin.order.vo.TradeOrderPag ...@@ -20,7 +20,6 @@ import com.luhu.computility.module.trade.controller.admin.order.vo.TradeOrderPag
import com.luhu.computility.module.trade.dal.dataobject.order.TradeOrderDO; import com.luhu.computility.module.trade.dal.dataobject.order.TradeOrderDO;
import com.luhu.computility.module.trade.enums.order.TradeOrderStatusEnum; import com.luhu.computility.module.trade.enums.order.TradeOrderStatusEnum;
import com.luhu.computility.module.trade.service.order.TradeOrderQueryService; import com.luhu.computility.module.trade.service.order.TradeOrderQueryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
......
...@@ -75,5 +75,10 @@ ...@@ -75,5 +75,10 @@
<version>${revision}</version> <version>${revision}</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>com.luhu</groupId>
<artifactId>computility-module-apihub-api</artifactId>
<version>${revision}</version>
</dependency>
</dependencies> </dependencies>
</project> </project>
...@@ -8,6 +8,7 @@ import com.luhu.computility.framework.common.exception.enums.GlobalResponseCodeC ...@@ -8,6 +8,7 @@ import com.luhu.computility.framework.common.exception.enums.GlobalResponseCodeC
import com.luhu.computility.framework.common.pojo.CommonResult; import com.luhu.computility.framework.common.pojo.CommonResult;
import com.luhu.computility.framework.common.util.http.HttpUtils; import com.luhu.computility.framework.common.util.http.HttpUtils;
import com.luhu.computility.framework.signature.core.annotation.ApiSignature; import com.luhu.computility.framework.signature.core.annotation.ApiSignature;
import com.luhu.computility.module.apihub.signature.core.annotation.ApiHubApiSignature;
import com.luhu.computility.module.external.controller.openapi.dto.ImageRespDTO; import com.luhu.computility.module.external.controller.openapi.dto.ImageRespDTO;
import com.luhu.computility.module.external.controller.openapi.dto.PoetryImageReqDTO; import com.luhu.computility.module.external.controller.openapi.dto.PoetryImageReqDTO;
import com.luhu.computility.module.external.controller.openapi.dto.TextToImageReqDTO; import com.luhu.computility.module.external.controller.openapi.dto.TextToImageReqDTO;
...@@ -48,7 +49,7 @@ public class AigcNewApiController { ...@@ -48,7 +49,7 @@ public class AigcNewApiController {
@ApiAccessLog @ApiAccessLog
@PostMapping(value = "/text-to-image/season") @PostMapping(value = "/text-to-image/season")
@Operation(summary = "四季和景点id生成图", description = "接收简单生图参数,将生成图片保存在本地服务器,并返回生成图片的url") @Operation(summary = "四季和景点id生成图", description = "接收简单生图参数,将生成图片保存在本地服务器,并返回生成图片的url")
@ApiSignature @ApiHubApiSignature
public CommonResult<ImageRespDTO> textToImageV2(@RequestBody TextToImageReqDTO textToImageReqDTO){ public CommonResult<ImageRespDTO> textToImageV2(@RequestBody TextToImageReqDTO textToImageReqDTO){
try { try {
String requestBody = JSONUtil.toJsonStr(textToImageReqDTO); String requestBody = JSONUtil.toJsonStr(textToImageReqDTO);
...@@ -72,7 +73,7 @@ public class AigcNewApiController { ...@@ -72,7 +73,7 @@ public class AigcNewApiController {
@ApiAccessLog @ApiAccessLog
@PostMapping("/text-to-image/poetry") @PostMapping("/text-to-image/poetry")
@Operation(summary = "获取藏头诗图片", description = "接收关键词、景点id、省份id这些参数,返回藏头诗图片url") @Operation(summary = "获取藏头诗图片", description = "接收关键词、景点id、省份id这些参数,返回藏头诗图片url")
@ApiSignature @ApiHubApiSignature
public CommonResult<ImageRespDTO> textToImageByPoetry(@RequestBody PoetryImageReqDTO poetryImageReqDTO){ public CommonResult<ImageRespDTO> textToImageByPoetry(@RequestBody PoetryImageReqDTO poetryImageReqDTO){
try { try {
String requestBody = JSONUtil.toJsonStr(poetryImageReqDTO); String requestBody = JSONUtil.toJsonStr(poetryImageReqDTO);
......
...@@ -8,6 +8,7 @@ import com.luhu.computility.framework.common.exception.enums.GlobalResponseCodeC ...@@ -8,6 +8,7 @@ import com.luhu.computility.framework.common.exception.enums.GlobalResponseCodeC
import com.luhu.computility.framework.common.pojo.CommonResult; import com.luhu.computility.framework.common.pojo.CommonResult;
import com.luhu.computility.framework.common.util.http.HttpUtils; import com.luhu.computility.framework.common.util.http.HttpUtils;
import com.luhu.computility.framework.signature.core.annotation.ApiSignature; import com.luhu.computility.framework.signature.core.annotation.ApiSignature;
import com.luhu.computility.module.apihub.signature.core.annotation.ApiHubApiSignature;
import com.luhu.computility.module.external.controller.openapi.dto.ImageRespDTO; import com.luhu.computility.module.external.controller.openapi.dto.ImageRespDTO;
import com.luhu.computility.module.external.controller.openapi.dto.PoetryImageReqDTO; import com.luhu.computility.module.external.controller.openapi.dto.PoetryImageReqDTO;
import com.luhu.computility.module.external.controller.openapi.dto.TextToImageReqDTO; import com.luhu.computility.module.external.controller.openapi.dto.TextToImageReqDTO;
...@@ -49,7 +50,7 @@ public class AigcOldApiController { ...@@ -49,7 +50,7 @@ public class AigcOldApiController {
@ApiAccessLog @ApiAccessLog
@PostMapping(value = "/text-to-image/season") @PostMapping(value = "/text-to-image/season")
@Operation(summary = "四季和景点id生成图", description = "接收简单生图参数,将生成图片保存在本地服务器,并返回生成图片的url") @Operation(summary = "四季和景点id生成图", description = "接收简单生图参数,将生成图片保存在本地服务器,并返回生成图片的url")
//@ApiSignature @ApiHubApiSignature
public CommonResult<ImageRespDTO> textToImageBySeason(@RequestBody TextToImageReqDTO textToImageReqDTO){ public CommonResult<ImageRespDTO> textToImageBySeason(@RequestBody TextToImageReqDTO textToImageReqDTO){
try { try {
String requestBody = JSONUtil.toJsonStr(textToImageReqDTO); String requestBody = JSONUtil.toJsonStr(textToImageReqDTO);
...@@ -73,7 +74,7 @@ public class AigcOldApiController { ...@@ -73,7 +74,7 @@ public class AigcOldApiController {
@ApiAccessLog @ApiAccessLog
@PostMapping("/text-to-image/poetry") @PostMapping("/text-to-image/poetry")
@Operation(summary = "获取藏头诗图片", description = "接收关键词、景点id、省份id这些参数,返回藏头诗图片url") @Operation(summary = "获取藏头诗图片", description = "接收关键词、景点id、省份id这些参数,返回藏头诗图片url")
@ApiSignature @ApiHubApiSignature
public CommonResult<ImageRespDTO> textToImageByPoetry(@RequestBody PoetryImageReqDTO poetryImageReqDTO){ public CommonResult<ImageRespDTO> textToImageByPoetry(@RequestBody PoetryImageReqDTO poetryImageReqDTO){
try { try {
String requestBody = JSONUtil.toJsonStr(poetryImageReqDTO); String requestBody = JSONUtil.toJsonStr(poetryImageReqDTO);
......
...@@ -3,6 +3,7 @@ package com.luhu.computility.module.external.controller.openapi; ...@@ -3,6 +3,7 @@ package com.luhu.computility.module.external.controller.openapi;
import com.luhu.computility.framework.apilog.core.annotation.ApiAccessLog; import com.luhu.computility.framework.apilog.core.annotation.ApiAccessLog;
import com.luhu.computility.framework.common.pojo.CommonResult; import com.luhu.computility.framework.common.pojo.CommonResult;
import com.luhu.computility.framework.signature.core.annotation.ApiSignature; import com.luhu.computility.framework.signature.core.annotation.ApiSignature;
import com.luhu.computility.module.apihub.signature.core.annotation.ApiHubApiSignature;
import com.luhu.computility.module.external.controller.openapi.dto.AIQAReqDTO; import com.luhu.computility.module.external.controller.openapi.dto.AIQAReqDTO;
import com.luhu.computility.module.external.controller.openapi.dto.AIQARespDTO; import com.luhu.computility.module.external.controller.openapi.dto.AIQARespDTO;
import com.luhu.computility.module.external.controller.openapi.dto.CeateVideoStreamReqDTO; import com.luhu.computility.module.external.controller.openapi.dto.CeateVideoStreamReqDTO;
...@@ -51,7 +52,7 @@ public class OpenApiController { ...@@ -51,7 +52,7 @@ public class OpenApiController {
@ApiAccessLog @ApiAccessLog
@PostMapping(value = "/digital-human-conversation") @PostMapping(value = "/digital-human-conversation")
@Operation(summary = "数字人对话", description = "和数字人朱熹进行一问一答的对话") @Operation(summary = "数字人对话", description = "和数字人朱熹进行一问一答的对话")
@ApiSignature @ApiHubApiSignature
public CommonResult<ConversationRespDTO> digitalHumanConversation(@RequestBody ConversationReqDTO conversationReqDTO){ public CommonResult<ConversationRespDTO> digitalHumanConversation(@RequestBody ConversationReqDTO conversationReqDTO){
return openApiService.digitalHumanConversation(conversationReqDTO); return openApiService.digitalHumanConversation(conversationReqDTO);
} }
...@@ -64,7 +65,7 @@ public class OpenApiController { ...@@ -64,7 +65,7 @@ public class OpenApiController {
@Parameter(name = "url", description = "图片链接") @Parameter(name = "url", description = "图片链接")
}) })
@Operation(summary = "ai换脸-上传图片", description = "用户上传头像将视频中人物头像替换") @Operation(summary = "ai换脸-上传图片", description = "用户上传头像将视频中人物头像替换")
@ApiSignature @ApiHubApiSignature
public CommonResult<UploadImageRespDTO> uploadFaceSwapImage(@RequestPart(value = "image", required = false) MultipartFile image, public CommonResult<UploadImageRespDTO> uploadFaceSwapImage(@RequestPart(value = "image", required = false) MultipartFile image,
@RequestParam(value = "url", required = false) String url) { @RequestParam(value = "url", required = false) String url) {
return openApiService.uploadFaceSwapImage(image, url); return openApiService.uploadFaceSwapImage(image, url);
...@@ -74,7 +75,7 @@ public class OpenApiController { ...@@ -74,7 +75,7 @@ public class OpenApiController {
@ApiAccessLog @ApiAccessLog
@GetMapping("/create-video-stream") @GetMapping("/create-video-stream")
@Operation(summary = "ai换脸-生成换脸工作流", description = "只有先上传图片才能开始换脸工作流,用户根据promptId取最后生成的视频") @Operation(summary = "ai换脸-生成换脸工作流", description = "只有先上传图片才能开始换脸工作流,用户根据promptId取最后生成的视频")
@ApiSignature @ApiHubApiSignature
public CommonResult<CeateVideoStreamRespDTO> ceateVideoStream(@RequestBody CeateVideoStreamReqDTO ceateVideoStreamReqDTO){ public CommonResult<CeateVideoStreamRespDTO> ceateVideoStream(@RequestBody CeateVideoStreamReqDTO ceateVideoStreamReqDTO){
return openApiService.ceateVideoStream(ceateVideoStreamReqDTO); return openApiService.ceateVideoStream(ceateVideoStreamReqDTO);
} }
...@@ -83,7 +84,7 @@ public class OpenApiController { ...@@ -83,7 +84,7 @@ public class OpenApiController {
@ApiAccessLog @ApiAccessLog
@PostMapping(value = "/generate-face-swap-image", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @PostMapping(value = "/generate-face-swap-image", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation(summary = "ai换脸-图片换脸:上传图+图片生成", description = "ai换脸-图片换脸:上传图片+图片生成") @Operation(summary = "ai换脸-图片换脸:上传图+图片生成", description = "ai换脸-图片换脸:上传图片+图片生成")
@ApiSignature @ApiHubApiSignature
public CommonResult<GenerateFaceSwapRespDTO> faceImageGenerate(@RequestPart(value = "sourceImage", required = false) MultipartFile sourceImage, public CommonResult<GenerateFaceSwapRespDTO> faceImageGenerate(@RequestPart(value = "sourceImage", required = false) MultipartFile sourceImage,
@RequestParam(value = "sourceImageUrl", required = false) String sourceImageUrl, @RequestParam(value = "sourceImageUrl", required = false) String sourceImageUrl,
@RequestPart(value = "targetImage", required = false) MultipartFile targetImage, @RequestPart(value = "targetImage", required = false) MultipartFile targetImage,
...@@ -95,7 +96,7 @@ public class OpenApiController { ...@@ -95,7 +96,7 @@ public class OpenApiController {
@ApiAccessLog @ApiAccessLog
@GetMapping("/view-image") @GetMapping("/view-image")
@Operation(summary = "根据promptId取换脸后的图", description = "根据promptId取换脸后的图") @Operation(summary = "根据promptId取换脸后的图", description = "根据promptId取换脸后的图")
@ApiSignature @ApiHubApiSignature
public CommonResult<ViewSourceRespDTO> viewImage(@RequestBody ViewImageReqDTO viewImageReqDTO){ public CommonResult<ViewSourceRespDTO> viewImage(@RequestBody ViewImageReqDTO viewImageReqDTO){
return openApiService.viewImage(viewImageReqDTO); return openApiService.viewImage(viewImageReqDTO);
} }
...@@ -108,7 +109,7 @@ public class OpenApiController { ...@@ -108,7 +109,7 @@ public class OpenApiController {
@Parameter(name = "url", description = "图片链接") @Parameter(name = "url", description = "图片链接")
}) })
@Operation(summary = "ai换脸-视频换脸;ai换脸-上传图片+视频流生成", description = "ai换脸-上传图片+视频流生成") @Operation(summary = "ai换脸-视频换脸;ai换脸-上传图片+视频流生成", description = "ai换脸-上传图片+视频流生成")
@ApiSignature @ApiHubApiSignature
public CommonResult<GenerateFaceSwapRespDTO> faceVideoGenerate(@RequestPart(value = "image", required = false) MultipartFile image, public CommonResult<GenerateFaceSwapRespDTO> faceVideoGenerate(@RequestPart(value = "image", required = false) MultipartFile image,
@RequestParam(value = "url", required = false) String url) { @RequestParam(value = "url", required = false) String url) {
return openApiService.faceVideoGenerate(image, url); return openApiService.faceVideoGenerate(image, url);
...@@ -118,7 +119,7 @@ public class OpenApiController { ...@@ -118,7 +119,7 @@ public class OpenApiController {
@ApiAccessLog @ApiAccessLog
@GetMapping("/view-video") @GetMapping("/view-video")
@Operation(summary = "根据promptId取最后生成的视频", description = "根据promptId取最后生成的视频") @Operation(summary = "根据promptId取最后生成的视频", description = "根据promptId取最后生成的视频")
@ApiSignature @ApiHubApiSignature
public CommonResult<ViewSourceRespDTO> viewVideo(@RequestBody ViewVideoReqDTO viewVideoReqDTO){ public CommonResult<ViewSourceRespDTO> viewVideo(@RequestBody ViewVideoReqDTO viewVideoReqDTO){
return openApiService.viewVideo(viewVideoReqDTO); return openApiService.viewVideo(viewVideoReqDTO);
} }
...@@ -133,7 +134,7 @@ public class OpenApiController { ...@@ -133,7 +134,7 @@ public class OpenApiController {
@Parameter(name = "touristAreaId", description = "景点编码") @Parameter(name = "touristAreaId", description = "景点编码")
}) })
@Operation(summary = "图片拍照-相似图查找", description = "图片拍照-相似图查找") @Operation(summary = "图片拍照-相似图查找", description = "图片拍照-相似图查找")
@ApiSignature @ApiHubApiSignature
public CommonResult<List<MatchImageRespDTO>> matchImage(@RequestPart(value = "image", required = false) MultipartFile image public CommonResult<List<MatchImageRespDTO>> matchImage(@RequestPart(value = "image", required = false) MultipartFile image
, @RequestParam(value = "url", required = false) String url , @RequestParam(value = "url", required = false) String url
, @RequestParam(value = "limit", required = false) Integer limit , @RequestParam(value = "limit", required = false) Integer limit
...@@ -147,7 +148,7 @@ public class OpenApiController { ...@@ -147,7 +148,7 @@ public class OpenApiController {
@ApiAccessLog @ApiAccessLog
@PostMapping("/AIQA-chat") @PostMapping("/AIQA-chat")
@Operation(summary = "AI问答", description = "AI助手,关于行程和景区的疑问") @Operation(summary = "AI问答", description = "AI助手,关于行程和景区的疑问")
@ApiSignature @ApiHubApiSignature
public CommonResult<AIQARespDTO> AIQAChat(@RequestBody AIQAReqDTO aiqaReqDTO){ public CommonResult<AIQARespDTO> AIQAChat(@RequestBody AIQAReqDTO aiqaReqDTO){
return openApiService.AIQAChat(aiqaReqDTO); return openApiService.AIQAChat(aiqaReqDTO);
} }
......
...@@ -89,12 +89,12 @@ ...@@ -89,12 +89,12 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.luhu</groupId> <groupId>com.luhu</groupId>
<artifactId>computility-module-biz</artifactId> <artifactId>computility-module-biz-biz</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.luhu</groupId> <groupId>com.luhu</groupId>
<artifactId>computility-module-apihub</artifactId> <artifactId>computility-module-apihub-biz</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<dependency> <dependency>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment