暂存提交
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.baiyue.firstleg</groupId>
|
||||
<artifactId>baiyue-firstleg-platform</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>integration-service</artifactId>
|
||||
<name>integration-service</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.baiyue.shared</groupId>
|
||||
<artifactId>baiyue-common-web</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.baiyue.integration;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
|
||||
|
||||
@SpringBootApplication
|
||||
@ConfigurationPropertiesScan
|
||||
public class IntegrationServiceApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(IntegrationServiceApplication.class, args);
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package com.baiyue.integration.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@ConfigurationProperties(prefix = "baiyue.integration")
|
||||
public record IntegrationProperties(BaiduOcr baiduOcr, AmazonSpApi amazonSpApi) {
|
||||
public record BaiduOcr(boolean enabled, String apiKey, String secretKey) {
|
||||
public boolean configured() {
|
||||
return enabled && hasText(apiKey) && hasText(secretKey);
|
||||
}
|
||||
}
|
||||
|
||||
public record AmazonSpApi(boolean enabled,
|
||||
String clientId,
|
||||
String clientSecret,
|
||||
String refreshToken,
|
||||
String awsAccessKey,
|
||||
String awsSecretKey,
|
||||
String region) {
|
||||
public boolean configured() {
|
||||
return enabled
|
||||
&& hasText(clientId)
|
||||
&& hasText(clientSecret)
|
||||
&& hasText(refreshToken)
|
||||
&& hasText(awsAccessKey)
|
||||
&& hasText(awsSecretKey)
|
||||
&& hasText(region);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasText(String value) {
|
||||
return value != null && !value.isBlank();
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.baiyue.integration.controller;
|
||||
|
||||
import com.baiyue.common.core.api.ApiResponse;
|
||||
import com.baiyue.integration.dto.ProviderStatus;
|
||||
import com.baiyue.integration.security.InternalServiceTokenGuard;
|
||||
import com.baiyue.integration.service.ProviderStatusService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/internal/integrations")
|
||||
public class IntegrationProviderController {
|
||||
@Resource
|
||||
private ProviderStatusService providerStatusService;
|
||||
@Resource
|
||||
private InternalServiceTokenGuard internalServiceTokenGuard;
|
||||
|
||||
@GetMapping("/providers")
|
||||
public ApiResponse<List<ProviderStatus>> providers(
|
||||
@RequestHeader(value = "X-Internal-Service-Token", required = false) String token) {
|
||||
internalServiceTokenGuard.verify(token);
|
||||
return ApiResponse.success(providerStatusService.providers());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.baiyue.integration.dto;
|
||||
|
||||
public record ProviderStatus(String provider,
|
||||
boolean enabled,
|
||||
boolean configured,
|
||||
String status,
|
||||
String message) {
|
||||
public static ProviderStatus of(String provider, boolean enabled, boolean configured) {
|
||||
if (!enabled) {
|
||||
return new ProviderStatus(provider, false, false, "DISABLED", "第三方能力未启用");
|
||||
}
|
||||
if (!configured) {
|
||||
return new ProviderStatus(provider, true, false, "NOT_CONFIGURED", "第三方密钥配置不完整");
|
||||
}
|
||||
return new ProviderStatus(provider, true, true, "READY", "配置已就绪");
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.baiyue.integration.provider;
|
||||
|
||||
import com.baiyue.integration.config.IntegrationProperties;
|
||||
import com.baiyue.integration.dto.ProviderStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class AmazonLogisticsAdapter implements IntegrationProviderAdapter {
|
||||
private final IntegrationProperties properties;
|
||||
|
||||
public AmazonLogisticsAdapter(IntegrationProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProviderStatus status() {
|
||||
IntegrationProperties.AmazonSpApi config = properties.amazonSpApi();
|
||||
return ProviderStatus.of("AMAZON_SP_API", config.enabled(), config.configured());
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.baiyue.integration.provider;
|
||||
|
||||
import com.baiyue.integration.config.IntegrationProperties;
|
||||
import com.baiyue.integration.dto.ProviderStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class BaiduOcrAdapter implements IntegrationProviderAdapter {
|
||||
private final IntegrationProperties properties;
|
||||
|
||||
public BaiduOcrAdapter(IntegrationProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProviderStatus status() {
|
||||
IntegrationProperties.BaiduOcr config = properties.baiduOcr();
|
||||
return ProviderStatus.of("BAIDU_OCR", config.enabled(), config.configured());
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.baiyue.integration.provider;
|
||||
|
||||
import com.baiyue.integration.dto.ProviderStatus;
|
||||
|
||||
public interface IntegrationProviderAdapter {
|
||||
ProviderStatus status();
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.baiyue.integration.security;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
|
||||
@Component
|
||||
public class InternalServiceTokenGuard {
|
||||
private final byte[] expectedToken;
|
||||
|
||||
public InternalServiceTokenGuard(@Value("${baiyue.security.internal-service-token}") String token) {
|
||||
if (token == null || token.getBytes(StandardCharsets.UTF_8).length < 32) {
|
||||
throw new IllegalStateException("INTERNAL_SERVICE_TOKEN 至少需要 32 字节");
|
||||
}
|
||||
this.expectedToken = token.getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public void verify(String actualToken) {
|
||||
byte[] actual = actualToken == null ? new byte[0] : actualToken.getBytes(StandardCharsets.UTF_8);
|
||||
if (!MessageDigest.isEqual(expectedToken, actual)) {
|
||||
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "内部服务令牌无效");
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.baiyue.integration.service;
|
||||
|
||||
import com.baiyue.integration.dto.ProviderStatus;
|
||||
import com.baiyue.integration.provider.IntegrationProviderAdapter;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class ProviderStatusService {
|
||||
private final List<IntegrationProviderAdapter> adapters;
|
||||
|
||||
public ProviderStatusService(List<IntegrationProviderAdapter> adapters) {
|
||||
this.adapters = adapters;
|
||||
}
|
||||
|
||||
public List<ProviderStatus> providers() {
|
||||
return adapters.stream().map(IntegrationProviderAdapter::status).toList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
server:
|
||||
port: ${INTEGRATION_PORT:9210}
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: integration-service
|
||||
autoconfigure:
|
||||
exclude:
|
||||
- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
|
||||
- com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration
|
||||
config:
|
||||
import:
|
||||
- optional:nacos:baiyue-common.yml?group=BAIYUE&refreshEnabled=true
|
||||
- optional:nacos:${spring.application.name}.yml?group=BAIYUE&refreshEnabled=true
|
||||
cloud:
|
||||
nacos:
|
||||
server-addr: ${NACOS_SERVER_ADDR:127.0.0.1:8848}
|
||||
username: ${NACOS_USERNAME:nacos}
|
||||
password: ${NACOS_PASSWORD:nacos}
|
||||
discovery:
|
||||
enabled: ${NACOS_DISCOVERY_ENABLED:true}
|
||||
register-enabled: ${NACOS_REGISTER_ENABLED:true}
|
||||
group: ${NACOS_GROUP:BAIYUE}
|
||||
namespace: ${NACOS_NAMESPACE:public}
|
||||
config:
|
||||
namespace: ${NACOS_NAMESPACE:public}
|
||||
|
||||
baiyue:
|
||||
security:
|
||||
internal-service-token: ${INTERNAL_SERVICE_TOKEN:change-me-to-an-internal-token-at-least-32-bytes-long}
|
||||
integration:
|
||||
baidu-ocr:
|
||||
enabled: ${BAIDU_OCR_ENABLED:false}
|
||||
api-key: ${BAIDU_OCR_API_KEY:}
|
||||
secret-key: ${BAIDU_OCR_SECRET_KEY:}
|
||||
amazon-sp-api:
|
||||
enabled: ${AMAZON_SP_API_ENABLED:false}
|
||||
client-id: ${AMAZON_SP_API_CLIENT_ID:}
|
||||
client-secret: ${AMAZON_SP_API_CLIENT_SECRET:}
|
||||
refresh-token: ${AMAZON_SP_API_REFRESH_TOKEN:}
|
||||
aws-access-key: ${AMAZON_SP_API_AWS_ACCESS_KEY:}
|
||||
aws-secret-key: ${AMAZON_SP_API_AWS_SECRET_KEY:}
|
||||
region: ${AMAZON_SP_API_REGION:}
|
||||
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: health,info,prometheus
|
||||
endpoint:
|
||||
health:
|
||||
probes:
|
||||
enabled: true
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.baiyue.integration.config;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class IntegrationPropertiesTest {
|
||||
@Test
|
||||
void providerIsConfiguredOnlyWhenEnabledAndAllSecretsExist() {
|
||||
var baidu = new IntegrationProperties.BaiduOcr(true, "key", "secret");
|
||||
var amazon = new IntegrationProperties.AmazonSpApi(
|
||||
true, "client", "secret", "refresh", "access", "aws-secret", "us-east-1");
|
||||
|
||||
assertThat(baidu.configured()).isTrue();
|
||||
assertThat(amazon.configured()).isTrue();
|
||||
assertThat(new IntegrationProperties.BaiduOcr(false, "key", "secret").configured()).isFalse();
|
||||
assertThat(new IntegrationProperties.AmazonSpApi(
|
||||
true, "client", "secret", "", "access", "aws-secret", "us-east-1").configured()).isFalse();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user