Compare commits

...

57 Commits

Author SHA1 Message Date
litianxiang
0eb6426a99 买家端联调bug 2026-05-22 13:02:29 +08:00
litianxiang
743b2bbda9 买家端联调bug 2026-05-22 13:02:10 +08:00
litianxiang
9fc17d2940 test 2026-05-21 17:16:40 +08:00
litianxiang
41bb344e97 fix商品接口报错 2026-05-21 16:41:16 +08:00
litianxiang
cddd74db2a test 2026-05-21 15:55:14 +08:00
litianxiang
69dba26b73 test 2026-05-21 15:54:43 +08:00
litianxiang
206431ccb0 test 2026-05-21 15:49:03 +08:00
62f8219d30 更新 .gitea/workflows/master_gateway_build_manual.yaml 2026-05-21 15:11:52 +08:00
litianxiang
2daec6b123 买家端接口fegin适配 2026-05-18 14:54:18 +08:00
litianxiang
7e19ba4d06 登录鉴权按照Source判断id来自于何处 2026-05-13 09:40:31 +08:00
litianxiang
58951ff9b6 买家端需要的获取商家主页和模糊搜索接口 2026-05-11 16:40:45 +08:00
litianxiang
9124256f01 log配置 2026-05-07 13:39:45 +08:00
litianxiang
7f69eebedf Revert "选中状态fix"
This reverts commit e00e7f3e5e.
2026-05-07 11:39:12 +08:00
litianxiang
e00e7f3e5e 选中状态fix 2026-05-07 11:38:18 +08:00
litianxiang
a5c0695488 日志
订单表字段改名
视频返回新增字段
2026-05-06 16:58:47 +08:00
litianxiang
c8d1bc6985 新工作流 2026-05-06 15:03:52 +08:00
litianxiang
3b11905b55 ws配置 2026-05-06 13:42:07 +08:00
litianxiang
94379b9a16 cors 2026-05-06 10:39:55 +08:00
litianxiang
e642dbf041 cors 2026-05-06 09:50:24 +08:00
litianxiang
e7ef16b8ab cors 2026-05-06 09:50:11 +08:00
litianxiang
32bd7c7808 cors 2026-05-05 17:31:00 +08:00
litianxiang
a98ba4222c cors 2026-05-05 17:29:46 +08:00
litianxiang
472c349220 工作流测试 2026-05-05 17:24:30 +08:00
litianxiang
2ebad70036 工作流测试 2026-05-05 17:09:19 +08:00
litianxiang
5ed0a0a288 工作流测试 2026-05-05 17:00:53 +08:00
litianxiang
ffd45a4f43 cors配置 2026-05-05 16:54:16 +08:00
litianxiang
1b744635d6 cors配置 2026-05-05 16:53:30 +08:00
litianxiang
7422418dff 工作流缓存测试 2026-05-05 16:45:38 +08:00
litianxiang
9d3795f34f cors配置 2026-05-05 16:41:09 +08:00
litianxiang
8e2ba26fa7 cors配置 2026-05-05 16:33:52 +08:00
litianxiang
b697d86fef cors配置 2026-05-05 16:25:19 +08:00
litianxiang
89e6bda78d cors配置 2026-05-05 15:57:35 +08:00
litianxiang
edb0c01ec0 cors配置 2026-05-05 15:52:11 +08:00
litianxiang
af8fedfee4 /healthy接口新增 2026-05-05 12:37:26 +08:00
litianxiang
e3158828d6 /healthy接口新增 2026-05-05 12:21:05 +08:00
litianxiang
ad50e0122a /healthy接口新增 2026-05-05 11:54:49 +08:00
litianxiang
794c077aa1 服务端口号与宿主机统一,方便本地调试不需要修改bootstrap 2026-05-04 14:21:36 +08:00
litianxiang
5ae0ab58d8 服务端口号与宿主机统一,方便本地调试不需要修改bootstrap 2026-05-04 14:19:37 +08:00
litianxiang
a6e881eaf0 nacos注册配置 2026-05-04 10:27:21 +08:00
litianxiang
2b56b5f690 nacos注册测试 2026-05-04 10:23:02 +08:00
litianxiang
fc4f159f40 工作流 2026-04-28 16:40:56 +08:00
litianxiang
4e3ac6f973 bootstrap配置 2026-04-28 14:38:01 +08:00
litianxiang
6ddca902be 1 2026-04-28 13:22:13 +08:00
litianxiang
fdda674368 商品bug 2026-04-28 09:39:27 +08:00
litianxiang
7fac582123 host配置 2026-04-27 16:43:19 +08:00
litianxiang
adce180ac3 host配置 2026-04-27 16:29:55 +08:00
litianxiang
a29a4b2dc4 工作流配置 2026-04-27 16:10:02 +08:00
litianxiang
e992c7e47f 端口号错误 2026-04-27 15:09:53 +08:00
litianxiang
e2c30bd918 服务名修改 2026-04-27 14:30:30 +08:00
litianxiang
f060d1ab44 微服务改造 2026-04-27 13:54:04 +08:00
litianxiang
a539e48edf Merge remote-tracking branch 'origin/master' 2026-04-27 11:47:25 +08:00
litianxiang
77efb99f51 微服务改造 2026-04-27 11:47:16 +08:00
e1f5dcc855 添加 .gitea/workflows/master_gateway_build_manual.yaml 2026-04-24 14:48:27 +08:00
litianxiang
8d54d2e5c1 微服务改造 2026-04-23 10:52:49 +08:00
litianxiang
f7e87fe84c 微服务改造 2026-04-22 15:54:43 +08:00
litianxiang
5284bfa813 微服务改造 2026-04-22 11:16:47 +08:00
litianxiang
218bed1813 微服务改造 2026-04-22 11:15:35 +08:00
12 changed files with 598 additions and 107 deletions

View File

@@ -0,0 +1,111 @@
name: 手动 AiDA gateway back-java 开发分支构建部署
on:
workflow_dispatch:
jobs:
build_and_deploy:
runs-on: java21
outputs:
build_status: ${{ job.status }}
build_url: ${{ gitea.server_url }}/${{ gitea.repository.owner.name }}/${{ gitea.repository.name }}/actions/runs/${{ gitea.run_id }}
permissions:
contents: read
packages: write
env:
REMOTE_DEPLOY_PATH: /workspace/workspace_aida/DevelopVersion/master-aida-gateway-back
steps:
- name: 0.记录开始时间
id: build_start_time
run: echo "current_time=$(TZ='Asia/Hong_Kong' date '+%Y-%m-%d %H:%M:%S %Z')" >> $GITHUB_OUTPUT
- name: 1.检出代码
uses: actions/checkout@v4
with:
ref: master
- name: 3.缓存 Maven 依赖
uses: actions/cache@v5
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: 4.构建项目
run: |
java -version
mvn -v
mvn clean package -DskipTests
- name: 5.生成Dockerfile
run: |
echo "===== 生成Dockerfile ====="
cat > Dockerfile << 'EOF'
FROM openjdk:21-ea-21-jdk-slim
VOLUME /tmp
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' > /etc/timezone
ADD ./target/*.jar /app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
EOF
echo "Dockerfile内容:"
cat Dockerfile
- name: 6.生成docker-compose.yml
run: |
echo "===== 生成docker-compose.yml ====="
cat > docker-compose.yml << 'EOF'
version: '3'
services:
master-aida-gateway:
container_name: master-aida-gateway
build: .
volumes:
# 数据挂载
- ./log:/log
- ./temp:/temp
- ./uploads:/temp/uploads
ports:
- '10094:10094'
restart: always
EOF
# 验证docker-compose.yml生成
echo "docker-compose.yml内容:"
cat docker-compose.yml
- name: 7.上传jar到远程服务器
uses: appleboy/scp-action@master
with:
host: ${{ secrets.SERVER_HOST }}
port: 22
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_KEY }}
source: "target/*.jar,Dockerfile,docker-compose.yml"
target: ${{ env.REMOTE_DEPLOY_PATH }}
preserve_host_directory_structure: false
- name: 8. 重启 Docker 服务
uses: appleboy/ssh-action@master # 👈 专门执行命令的 action
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_KEY }}
key_base64: true
script: |
echo "========= 进入部署目录 ========="
cd ${{ env.REMOTE_DEPLOY_PATH }}
ls -l
echo "========= 停止旧服务 ========="
docker compose down
echo "========= 启动新服务 ========="
docker compose up -d --build
echo "========= 查看运行状态 ========="
docker compose ps

2
.gitignore vendored
View File

@@ -1 +1,3 @@
/.idea/
/target/
/log/

37
pom.xml
View File

@@ -62,17 +62,11 @@
<scope>runtime</scope>
</dependency>
<!-- Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- Hutool (aligned with aida_seller 5.8.26) -->
<!-- Hutool -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.26</version>
<version>5.8.23</version>
</dependency>
<!-- Redis (for token blacklist) -->
@@ -99,6 +93,31 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Spring Boot Logging显式引入确保 logback 正确初始化) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<!-- Knife4j Gateway Aggregation -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-gateway-spring-boot-starter</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-ui</artifactId>
<version>4.4.0</version>
</dependency>
<!-- Spring Cloud LoadBalancer -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
@@ -126,6 +145,8 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!-- 强制工作目录为模块根目录,确保 ./log 指向项目目录而非 Maven 安装目录 -->
<workingDirectory>${project.basedir}</workingDirectory>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>

View File

@@ -0,0 +1,79 @@
package com.aida.gateway.common.response;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @ClassName Response
* @Description success代表响应成功 fail代表主动响应失败 error代表系统异常
* @Author dwjian
* @Date 2019/9/8 21:48
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Response<T> implements Serializable {
private int errCode;
private String errMsg;
private T data;
public static <T> Response<T> success() {
return success(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getMsg(), null);
}
public static <T> Response<T> success(T data) {
return success(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getMsg(), data);
}
public static <T> Response<T> success(int code, T data) {
return success(code, ResultEnum.SUCCESS.getMsg(), data);
}
public static <T> Response<T> success(int code, String msg, T data) {
return getResponse(code, msg, data);
}
public static <T> Response<T> fail(String msg) {
return fail(ResultEnum.FAIL.getCode(), msg);
}
public static <T> Response<T> fail(T data) {
return fail(ResultEnum.FAIL.getCode(), ResultEnum.FAIL.getMsg(), data);
}
public static <T> Response<T> fail(ResultEnum resultEnum) {
return fail(resultEnum.getCode(), resultEnum.getMsg(), null);
}
public static <T> Response<T> fail(int code, String msg) {
return fail(code, msg, null);
}
public static <T> Response<T> fail(int code, String msg, T data) {
return getResponse(code, msg, data);
}
public static <T> Response<T> error(String msg) {
return error(ResultEnum.ERROR.getCode(), msg);
}
public static <T> Response<T> error(T data) {
return error(ResultEnum.ERROR.getCode(), ResultEnum.ERROR.getMsg(), data);
}
public static <T> Response<T> error(int code, String msg) {
return error(code, msg, null);
}
public static <T> Response<T> error(int code, String msg, T data) {
return getResponse(code, msg, data);
}
private static <T> Response<T> getResponse(int code, String msg, T data) {
return new Response<>(code, msg, data);
}
}

View File

@@ -0,0 +1,58 @@
package com.aida.gateway.common.response;
/**
* @ClassName ResultEnum
* @Description 响应结果枚举
* @Author dwjian
* @Date 2019/9/8 21:58
*/
public enum ResultEnum {
SUCCESS(true, 0, "SUCCESS"),
FAIL(false, -1, "FAIL"),
ERROR(false, -1, "system error!"),
PARAMETER_ERROR(false, -2, "parameter error!"),
NO_LOGIN(false, -100, "User not logged in"),
NO_PERMISSION(false, -200, "No access"),
ACCOUNT_LOCK(false, -300, "Account frozen"),
PROMPT(false, 1, "Prompt"),
WARNING(false, 2, "Warning"),
;
private int code;
private String msg;
private boolean isOK;
ResultEnum(boolean isOK, int code, String msg) {
this.isOK = isOK;
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public boolean isOK() {
return isOK;
}
public void setOK(boolean OK) {
isOK = OK;
}
}

View File

@@ -19,5 +19,13 @@ public class GatewayAuthProperties {
private List<String> ignorePaths;
/**
* 可选认证路径token 有则解析并写入下游请求头,无则放行。
* 与 ignorePaths 的区别ignorePaths 完全跳过认证逻辑;
* optionalAuthPaths 仍然尝试解析 token有 token 时正常写入 X-User-Id / X-User-Info
* 无 token 时才放行,确保已登录用户的信息能正确传递。
*/
private List<String> optionalAuthPaths;
private boolean blacklistEnabled = true;
}

View File

@@ -0,0 +1,24 @@
package com.aida.gateway.controller;
import com.aida.gateway.common.response.Response;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/gateway")
public class HealthController {
@GetMapping("/healthy")
@ResponseStatus(HttpStatus.OK)
public Response<Map<String, Integer>> checkStatus() {
Map<String, Integer> returnMap = new HashMap<>();
returnMap.put("code", 200);
return Response.success(returnMap);
}
}

View File

@@ -11,8 +11,10 @@ import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
@@ -25,50 +27,81 @@ import reactor.core.publisher.Mono;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import org.springframework.util.AntPathMatcher;
import org.springframework.beans.factory.annotation.Qualifier;
/**
* Gateway 全局鉴权过滤器。
* <p>
* 流程:
* 1. 白名单路径直接放行
* 2. 从请求头读取 JWT验证签名和有效期
* 3. 检查 Redis 黑名单logout 后 token 被拉黑)
* 4. 将用户 ID 和用户信息 JSON 写入下游请求头
* 5. 失败返回 401 JSON
* 1. 放过 OPTIONS 预检请求,由全局 CORS 配置处理
* 2. 白名单路径直接放行
* 3. 从请求头读取 JWT验证签名和有效期
* 4. 检查 Redis 黑名单logout 后 token 被拉黑)
* 5. 将用户 ID 和用户信息 JSON 写入下游请求头
* 6. 失败返回 401 JSON
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class GlobalAuthWebFilter implements WebFilter {
public class GlobalAuthWebFilter implements WebFilter, Ordered {
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE - 1;
}
private final GatewayAuthProperties authProperties;
@Qualifier("reactiveRedisTemplate")
private final ReactiveRedisTemplate<String, String> redisTemplate;
private final ObjectMapper objectMapper;
private final AntPathMatcher pathMatcher = new AntPathMatcher();
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
String path = exchange.getRequest().getURI().getPath();
// 1. 白名单直接放行
if ("OPTIONS".equalsIgnoreCase(exchange.getRequest().getMethod().name())) {
return chain.filter(exchange);
}
// 2. 白名单直接放行(完全跳过认证)
if (isIgnoredPath(path)) {
return chain.filter(exchange);
}
// 2. 提取 token
// 3. 可选认证路径token 有则解析,无则放行
if (isOptionalAuthPath(path)) {
String rawHeader = exchange.getRequest().getHeaders()
.getFirst(authProperties.getJwtTokenHeader());
if (StrUtil.isBlank(rawHeader)) {
// 无 token直接放行不写任何 header
return chain.filter(exchange);
}
// 有 token正常走解析流程复用下面的验证逻辑
return processTokenWithAuthCheck(exchange, chain, rawHeader);
}
// 4. 其他路径:必须有 token
String rawHeader = exchange.getRequest().getHeaders()
.getFirst(authProperties.getJwtTokenHeader());
if (StrUtil.isBlank(rawHeader)) {
return writeUnauthorized(exchange, AuthConstants.MSG_MISSING_TOKEN);
}
return processTokenWithAuthCheck(exchange, chain, rawHeader);
}
/**
* 统一的 token 解析与认证流程:解析 JWT → 黑名单检查 → 写入下游 header。
* 专供可选认证路径中有 token 的情况,以及普通路径的鉴权。
*/
private Mono<Void> processTokenWithAuthCheck(ServerWebExchange exchange, WebFilterChain chain, String rawHeader) {
String token = rawHeader;
if (rawHeader.startsWith(authProperties.getJwtTokenPrefix())) {
token = rawHeader.substring(authProperties.getJwtTokenPrefix().length());
}
// 3. JWT 签名验证
// JWT 签名验证
Claims claims;
try {
claims = parseToken(token);
@@ -77,7 +110,7 @@ public class GlobalAuthWebFilter implements WebFilter {
return writeUnauthorized(exchange, AuthConstants.MSG_TOKEN_EXPIRED);
}
// 4. 解析用户信息
// 解析用户信息
AuthPrincipalVo principal;
try {
principal = objectMapper.readValue(claims.getSubject(), AuthPrincipalVo.class);
@@ -90,15 +123,24 @@ public class GlobalAuthWebFilter implements WebFilter {
return writeUnauthorized(exchange, AuthConstants.MSG_INVALID_TOKEN);
}
// 5. 黑名单检查(仅当启用时)
// 黑名单检查
if (authProperties.isBlacklistEnabled()) {
String blacklistKey = AuthConstants.BLACKLIST_PREFIX + principal.getId();
Boolean isBlacklisted = redisTemplate.hasKey(blacklistKey).block();
if (Boolean.TRUE.equals(isBlacklisted)) {
return writeUnauthorized(exchange, AuthConstants.MSG_TOKEN_BLACKLISTED);
}
return redisTemplate.hasKey(blacklistKey).flatMap(isBlacklisted -> {
if (Boolean.TRUE.equals(isBlacklisted)) {
return writeUnauthorized(exchange, AuthConstants.MSG_TOKEN_BLACKLISTED);
}
return continueChain(exchange, chain, principal);
}).onErrorResume(e -> {
log.error("Redis check failed", e);
return continueChain(exchange, chain, principal);
});
}
return continueChain(exchange, chain, principal);
}
private Mono<Void> continueChain(ServerWebExchange exchange, WebFilterChain chain, AuthPrincipalVo principal) {
// 6. 写入下游请求头
String userInfoJson;
try {
@@ -121,25 +163,23 @@ public class GlobalAuthWebFilter implements WebFilter {
return false;
}
for (String pattern : authProperties.getIgnorePaths()) {
if (matches(pattern, requestUri)) {
if (pathMatcher.match(pattern, requestUri)) {
return true;
}
}
return false;
}
private boolean matches(String pattern, String uri) {
if (pattern.endsWith("/**")) {
String prefix = pattern.substring(0, pattern.length() - 3);
return uri.startsWith(prefix);
private boolean isOptionalAuthPath(String requestUri) {
if (authProperties.getOptionalAuthPaths() == null) {
return false;
}
if (pattern.endsWith("/*")) {
String prefix = pattern.substring(0, pattern.length() - 2);
if (!uri.startsWith(prefix)) return false;
String suffix = uri.substring(prefix.length());
return !suffix.contains("/");
for (String pattern : authProperties.getOptionalAuthPaths()) {
if (pathMatcher.match(pattern, requestUri)) {
return true;
}
}
return uri.contains(pattern);
return false;
}
private Claims parseToken(String token) {
@@ -163,6 +203,11 @@ public class GlobalAuthWebFilter implements WebFilter {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
String origin = exchange.getRequest().getHeaders().getFirst(HttpHeaders.ORIGIN);
if (origin != null) {
response.getHeaders().set(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, origin);
response.getHeaders().set(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
}
String body = String.format("{\"code\":401,\"message\":\"%s\"}", message);
DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(buffer));

View File

@@ -1,37 +0,0 @@
package com.aida.gateway.route;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 路由配置。
* <p>
* 注意:实际生产环境中建议将路由配置放在 Nacos 配置中心。
* StripPrefix=1 将 /seller 前缀剥离,例如:
* /seller/designer/check -> /designer/check (发到 aida-seller 的 /api/designer/check)
*/
@Configuration
public class RouteConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
// /internal/** 用于内部服务调用(如 logout 黑名单),不需要 stripPrefix
.route("aida-gateway-internal", r -> r
.path("/internal/**")
.uri("forward:/internal"))
// aida-seller 服务
.route("aida-seller", r -> r
.path("/seller/**")
.filters(f -> f.stripPrefix(1))
.uri("lb://aida-seller"))
// aida-back_001 服务
.route("aida-back", r -> r
.path("/api/**")
.filters(f -> f.stripPrefix(1))
.uri("lb://aida-back"))
.build();
}
}

View File

@@ -4,57 +4,158 @@
# ============================================================
server:
port: 5569
port: 10094
spring:
application:
name: aida-gateway
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowed-origin-patterns: "*"
allowed-methods:
- GET
- POST
- PUT
- DELETE
- OPTIONS
- PATCH
allowed-headers: "*"
allow-credentials: true
max-age: 3600
default-filters:
- DedupeResponseHeader=Access-Control-Allow-Origin Access-Control-Allow-Credentials
# ---------- 路由配置 ----------
routes:
# 多实例部署时推送会失效升级多实例要注意ws改造
- id: aida-back-websocket
uri: lb://aida-back
predicates:
- Path=/notification/**
- id: aida-back
uri: lb://aida-back
predicates:
- Path=/aida/**
filters:
- StripPrefix=1
- id: aida-seller
uri: lb://aida-seller
predicates:
- Path=/seller/**
filters:
- StripPrefix=1
- id: aida-python
uri: http://18.167.251.121:9994
predicates:
- Path=/python/**
- id: aida-buyer
uri: lb://aida-buyer
predicates:
- Path=/buyer/**
filters:
- StripPrefix=1
# ---------- Knife4j 网关聚合配置 ----------
knife4j:
gateway:
enabled: true
# 手动指定下游服务的文档聚合,可以灵活应对不同服务的 API 版本或路径前缀
strategy: manual
routes:
- name: aida主服务
url: /aida/v3/api-docs
service-name: aida-back
context-path: /aida
order: 1
- name: 商家端服务 (Seller)
url: /seller/v3/api-docs
service-name: aida-seller
context-path: /seller
order: 2
- name: 买家端服务 (Buyer)
url: /buyer/v3/api-docs
service-name: aida-buyer
context-path: /buyer
order: 3
# ---------- Gateway JWT 认证gateway 独有) ----------
gateway:
auth:
jwt-secret: ${BACK_JWT_SECRET:JWTSECRET}
jwt-secret: JWTSECRET
jwt-token-header: Authorization
jwt-token-prefix: Bearer-
blacklist-enabled: true
ignore-paths:
# Static resources & docs
- /favicon.ico
- /doc.html
- /swagger-ui.html
- /swagger-ui
- /swagger-ui/**
- /swagger-resources/**
- /v2/api-docs
- /v2/api-docs/**
- /v3/api-docs
- /v3/api-docs/**
- /webjars/**
- /api/account/login
- /api/account/preLogin
- /api/designer/check
- /**/v3/api-docs/**
# Actuator & internal
- /actuator/**
- /internal/**
- /api/account/sendEmail
- /api/account/noLoginRequired
- /api/account/resetPwd
- /api/account/designWorksRegister
- /api/account/questionnaire
- /api/account/schoolLogin
- /api/account/enterpriseLogin
- /api/account/organizationNameSearch
- /api/account/activateNewEmail
- /api/python/saveGeneratePicture
- /api/python/getLibraryByUserId
- /api/python/flush
- /api/account/healthy
- /api/third/party/**
- /api/element/initDefaultSysFile
- /api/ali-pay/trade/notify
- /api/paypal/ipn/back
- /api/alipay-hk/trade/notify
- /api/stripe/trade/notify
- /api/portfolio/**
- /api/global-award/**
- /api/llm/stream
# Account / Login (only /aida prefix)
- /aida/api/account/login
- /aida/api/account/preLogin
- /aida/api/account/sendEmail
- /aida/api/account/noLoginRequired
- /aida/api/account/resetPwd
- /aida/api/account/designWorksRegister
- /aida/api/account/questionnaire
- /aida/api/account/schoolLogin
- /aida/api/account/enterpriseLogin
- /aida/api/account/organizationNameSearch
- /aida/api/account/activateNewEmail
- /aida/api/account/healthy
- /gateway/healthy
# Designer
- /aida/api/designer/check
# Python (only /aida prefix)
- /aida/api/python/saveGeneratePicture
- /aida/api/python/getLibraryByUserId
- /aida/api/python/flush
# Third party (exact paths, only /aida prefix)
- /aida/api/third/party/addUser
- /aida/api/third/party/addTrialUser
- /aida/api/third/party/editUser
- /aida/api/third/party/addNoLoginRequiredNew
- /aida/api/third/party/deleteNoLoginRequiredNew
- /aida/api/third/party/updateNoLoginRequiredNew
- /aida/api/third/party/existNoLoginRequired
- /aida/api/third/party/getRedirectUrl
- /aida/api/third/party/auth/google_callback
- /aida/api/third/party/parseGoogleCredential
- /aida/api/third/party/receiveDesignResults
- /aida/api/third/party/parseWeChatCode
- /aida/api/third/party/receiveDesignParams
# Element
- /aida/api/element/initDefaultSysFile
# Portfolio (exact paths, only /aida prefix)
- /aida/api/portfolio/page
- /aida/api/portfolio/detail
- /aida/api/portfolio/commentPage
- /aida/api/portfolio/viewsIncrease
# Global Award
- /aida/api/global-award/**
# LLM stream
- /aida/api/llm/stream
# Payment notifications (only /aida prefix)
- /aida/api/ali-pay/trade/notify
- /aida/api/paypal/ipn/back
- /aida/api/alipay-hk/trade/notify
- /aida/api/stripe/trade/notify
# Notification
- /notification/**
logging:
level:
com.aida.gateway: debug
# buyer
- /buyer/account/**
- /buyer/designer/shop/**
- /buyer/designer/search

View File

@@ -4,17 +4,31 @@
# 示例docker run -e NACOS_NAMESPACE=prod ...
# ============================================================
nacos:
namespace: dev
host: 18.167.251.121:28848
username: nacos
password: Aidlab123123!
spring:
application:
name: aida-gateway
config:
import: optional:nacos:aida-public-${NACOS_NAMESPACE:dev}.yml
import: optional:nacos:aida-public-${nacos.namespace}.yml
cloud:
nacos:
discovery:
server-addr: ${NACOS_HOST:127.0.0.1:8848}
namespace: ${NACOS_NAMESPACE:dev}
server-addr: ${nacos.host}
namespace: ${nacos.namespace}
username: ${nacos.username}
password: ${nacos.password}
# ip: 18.167.251.121
port: 10094
# ip-type: ipv4
# prefer-ip-address: true
config:
server-addr: ${NACOS_HOST:127.0.0.1:8848}
namespace: ${NACOS_NAMESPACE:dev}
server-addr: ${nacos.host}
namespace: ${nacos.namespace}
file-extension: yaml
username: ${nacos.username}
password: ${nacos.password}

View File

@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<!-- 日志存放路径 -->
<property name="log.path" value="./log" />
<!-- 日志输出格式 -->
<property name="log.pattern.console" value="${CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" />
<property name="log.pattern.file" value="${FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%15t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" />
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern.console}</pattern>
</encoder>
</appender>
<!-- Info 日志文件 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/aida-gateway-info.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/aida-gateway-info.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern.file}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- Error 日志文件 -->
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/aida-gateway-error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/aida-gateway-error.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern.file}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 服务模块日志级别控制 -->
<logger name="com.aida.gateway" level="debug" />
<!-- Spring 日志级别控制 -->
<logger name="org.springframework" level="warn" />
<root level="info">
<appender-ref ref="console" />
</root>
<root level="info">
<appender-ref ref="file_info" />
<appender-ref ref="file_error" />
</root>
</configuration>