diff --git a/docs/sql/01_schema.sql b/docs/sql/01_schema.sql new file mode 100644 index 0000000..36a902e --- /dev/null +++ b/docs/sql/01_schema.sql @@ -0,0 +1,53 @@ +-- ===================================================================== +-- 开放接口中心 网关库表(T0.9) +-- 库:open_gateway(projman provision 建;本脚本由 T1.7 生产导入,可重复执行) +-- 方言:MySQL +-- ===================================================================== + +DROP TABLE IF EXISTS open_api_route; +CREATE TABLE open_api_route ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + target_code VARCHAR(32) NOT NULL COMMENT '目标应用 ctx(usermsg/notice/...)', + path_pattern VARCHAR(256) NOT NULL COMMENT 'Ant 风格;匹配剥掉 target 首段后的路径,如 /oapi/message/**', + http_method VARCHAR(8) NOT NULL DEFAULT '*' COMMENT 'GET/POST/PUT/DELETE/*', + visibility VARCHAR(16) NOT NULL DEFAULT 'INTERNAL' COMMENT 'INTERNAL/EXTERNAL', + auth_mode VARCHAR(16) NOT NULL DEFAULT 'SIGNED' COMMENT 'SIGNED/ANON/OPTIONAL', + cache_ttl_seconds INT NOT NULL DEFAULT 0, + cache_scope VARCHAR(16) NOT NULL DEFAULT 'CALLER' COMMENT 'CALLER/GLOBAL', + rate_limit_qps INT NOT NULL DEFAULT 0 COMMENT '0=不限', + timeout_ms INT NOT NULL DEFAULT 30000, + request_body_limit_mb INT NOT NULL DEFAULT 10, + enabled TINYINT NOT NULL DEFAULT 1, + remark VARCHAR(256), + gmt_create DATETIME NOT NULL, + gmt_modified DATETIME NOT NULL, + UNIQUE KEY uk_route (target_code, path_pattern, http_method) +); + +DROP TABLE IF EXISTS open_api_log; +CREATE TABLE open_api_log ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + ts DATETIME(3) NOT NULL, + caller_app_key VARCHAR(64) COMMENT 'ANON 记 anonymous;401 且无 appKey 记 NULL', + target_code VARCHAR(32) NOT NULL, + method VARCHAR(8) NOT NULL, + path VARCHAR(256) NOT NULL COMMENT '剥掉 target 首段后的路径(与注册表 path_pattern 同一坐标系)', + status INT NOT NULL COMMENT '网关出口 HTTP 状态', + backend_code INT COMMENT '提供方业务 code(响应为 JSON 信封时尽力解析,否则 NULL)', + cost_ms INT NOT NULL, + KEY idx_ts (ts), + KEY idx_caller_ts (caller_app_key, ts), + KEY idx_target_ts (target_code, ts) +); + +DROP TABLE IF EXISTS open_api_stat_daily; +CREATE TABLE open_api_stat_daily ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + stat_date DATE NOT NULL, + target_code VARCHAR(32) NOT NULL, + caller_app_key VARCHAR(64) NOT NULL, + total INT NOT NULL DEFAULT 0, + err_total INT NOT NULL DEFAULT 0, + avg_cost_ms INT NOT NULL DEFAULT 0, + UNIQUE KEY uk_stat (stat_date, target_code, caller_app_key) +); diff --git a/src/main/java/com/yunhai/platform/gateway/entity/OpenApiLog.java b/src/main/java/com/yunhai/platform/gateway/entity/OpenApiLog.java new file mode 100644 index 0000000..1821987 --- /dev/null +++ b/src/main/java/com/yunhai/platform/gateway/entity/OpenApiLog.java @@ -0,0 +1,59 @@ +package com.yunhai.platform.gateway.entity; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Index; +import javax.persistence.Table; +import java.time.LocalDateTime; + +/** + * 网关访问台账(T0.9;观测设施,只插入不改写;索引 idx_ts / idx_caller_ts / idx_target_ts 与 DDL 对齐)。 + */ +@Entity +@Table(name = "open_api_log", indexes = { + @Index(name = "idx_ts", columnList = "ts"), + @Index(name = "idx_caller_ts", columnList = "caller_app_key, ts"), + @Index(name = "idx_target_ts", columnList = "target_code, ts") +}) +@Getter +@Setter +public class OpenApiLog { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "ts", nullable = false, columnDefinition = "DATETIME(3)") + private LocalDateTime ts; + + /** ANON 记 anonymous;401 且无 appKey 记 NULL */ + @Column(name = "caller_app_key", length = 64) + private String callerAppKey; + + @Column(name = "target_code", nullable = false, length = 32) + private String targetCode; + + @Column(name = "method", nullable = false, length = 8) + private String method; + + /** 剥掉 target 首段后的路径(与注册表 path_pattern 同一坐标系) */ + @Column(name = "path", nullable = false, length = 256) + private String path; + + /** 网关出口 HTTP 状态 */ + @Column(name = "status", nullable = false) + private int status; + + /** 提供方业务 code(响应为 JSON 信封时尽力解析,否则 NULL) */ + @Column(name = "backend_code") + private Integer backendCode; + + @Column(name = "cost_ms", nullable = false) + private int costMs; +} diff --git a/src/main/java/com/yunhai/platform/gateway/entity/OpenApiRoute.java b/src/main/java/com/yunhai/platform/gateway/entity/OpenApiRoute.java new file mode 100644 index 0000000..e5cfff6 --- /dev/null +++ b/src/main/java/com/yunhai/platform/gateway/entity/OpenApiRoute.java @@ -0,0 +1,96 @@ +package com.yunhai.platform.gateway.entity; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.PrePersist; +import javax.persistence.PreUpdate; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import java.time.LocalDateTime; + +/** + * 路由注册表(T0.9;DDL 见 docs/sql/01_schema.sql,字段与列逐一对齐)。 + *
+ * Java 字段默认值与 DDL DEFAULT 逐列一致——插入即生效,不依赖 DB 默认兜底
+ * (提供方注册初版由 02_init_routes.sql 直插,运行时仅读)。
+ */
+@Entity
+@Table(name = "open_api_route", uniqueConstraints = @UniqueConstraint(
+ name = "uk_route", columnNames = {"target_code", "path_pattern", "http_method"}))
+@Getter
+@Setter
+public class OpenApiRoute {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(name = "target_code", nullable = false, length = 32)
+ private String targetCode;
+
+ @Column(name = "path_pattern", nullable = false, length = 256)
+ private String pathPattern;
+
+ /** GET/POST/PUT/DELETE/* */
+ @Column(name = "http_method", nullable = false, length = 8)
+ private String httpMethod = "*";
+
+ /** INTERNAL/EXTERNAL */
+ @Column(name = "visibility", nullable = false, length = 16)
+ private String visibility = "INTERNAL";
+
+ /** SIGNED/ANON/OPTIONAL */
+ @Column(name = "auth_mode", nullable = false, length = 16)
+ private String authMode = "SIGNED";
+
+ @Column(name = "cache_ttl_seconds", nullable = false)
+ private int cacheTtlSeconds = 0;
+
+ /** CALLER/GLOBAL */
+ @Column(name = "cache_scope", nullable = false, length = 16)
+ private String cacheScope = "CALLER";
+
+ /** 0=不限 */
+ @Column(name = "rate_limit_qps", nullable = false)
+ private int rateLimitQps = 0;
+
+ @Column(name = "timeout_ms", nullable = false)
+ private int timeoutMs = 30000;
+
+ @Column(name = "request_body_limit_mb", nullable = false)
+ private int requestBodyLimitMb = 10;
+
+ @Column(name = "enabled", nullable = false)
+ private boolean enabled = true;
+
+ @Column(name = "remark", length = 256)
+ private String remark;
+
+ @Column(name = "gmt_create", nullable = false)
+ private LocalDateTime gmtCreate;
+
+ @Column(name = "gmt_modified", nullable = false)
+ private LocalDateTime gmtModified;
+
+ @PrePersist
+ void preCreate() {
+ LocalDateTime now = LocalDateTime.now();
+ if (gmtCreate == null) {
+ gmtCreate = now;
+ }
+ if (gmtModified == null) {
+ gmtModified = now;
+ }
+ }
+
+ @PreUpdate
+ void preUpdate() {
+ gmtModified = LocalDateTime.now();
+ }
+}
diff --git a/src/main/java/com/yunhai/platform/gateway/entity/OpenApiStatDaily.java b/src/main/java/com/yunhai/platform/gateway/entity/OpenApiStatDaily.java
new file mode 100644
index 0000000..357edee
--- /dev/null
+++ b/src/main/java/com/yunhai/platform/gateway/entity/OpenApiStatDaily.java
@@ -0,0 +1,46 @@
+package com.yunhai.platform.gateway.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import java.time.LocalDate;
+
+/**
+ * 日统计(T0.9;由台账刷盘线程按日 upsert,uk_stat = stat_date + target_code + caller_app_key)。
+ */
+@Entity
+@Table(name = "open_api_stat_daily", uniqueConstraints = @UniqueConstraint(
+ name = "uk_stat", columnNames = {"stat_date", "target_code", "caller_app_key"}))
+@Getter
+@Setter
+public class OpenApiStatDaily {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(name = "stat_date", nullable = false)
+ private LocalDate statDate;
+
+ @Column(name = "target_code", nullable = false, length = 32)
+ private String targetCode;
+
+ @Column(name = "caller_app_key", nullable = false, length = 64)
+ private String callerAppKey;
+
+ @Column(name = "total", nullable = false)
+ private int total = 0;
+
+ @Column(name = "err_total", nullable = false)
+ private int errTotal = 0;
+
+ @Column(name = "avg_cost_ms", nullable = false)
+ private int avgCostMs = 0;
+}
diff --git a/src/main/java/com/yunhai/platform/gateway/repository/OpenApiLogRepository.java b/src/main/java/com/yunhai/platform/gateway/repository/OpenApiLogRepository.java
new file mode 100644
index 0000000..bfde09e
--- /dev/null
+++ b/src/main/java/com/yunhai/platform/gateway/repository/OpenApiLogRepository.java
@@ -0,0 +1,7 @@
+package com.yunhai.platform.gateway.repository;
+
+import com.yunhai.platform.gateway.entity.OpenApiLog;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface OpenApiLogRepository extends JpaRepository