From b77930adfb627e1254a3785729c58b85a0854b3e Mon Sep 17 00:00:00 2001 From: QAIU <736226400@qq.com> Date: Thu, 8 May 2025 18:18:15 +0800 Subject: [PATCH] remove yarn.lock --- core-database/pom.xml | 6 + .../main/java/cn/qaiu/db/ddl/Constraint.java | 1 - .../main/java/cn/qaiu/db/ddl/CreateTable.java | 428 ++++++++++++------ .../java/cn/qaiu/db/pool/JDBCPoolInit.java | 8 +- .../main/java/cn/qaiu/db/pool/JDBCType.java | 27 +- .../cn/qaiu/vx/core/model/JsonResult.java | 36 +- .../cn/qaiu/parser/PanDomainTemplate.java | 3 +- .../main/java/cn/qaiu/parser/impl/LzTool.java | 4 +- parser/src/main/resources/logback.xml | 1 + .../src/main/java/cn/qaiu/lz/AppMain.java | 22 +- .../interceptorImpl/DefaultInterceptor.java | 42 +- .../common/interceptorImpl/RateLimiter.java | 77 ++++ .../lz/web/controller/ShoutController.java | 36 ++ .../cn/qaiu/lz/web/model/ShoutMessage.java | 40 ++ .../cn/qaiu/lz/web/service/ShoutService.java | 15 + .../lz/web/service/impl/ShoutServiceImpl.java | 90 ++++ web-service/src/main/resources/app-dev.yml | 21 +- .../src/main/resources/http-tools/pan-iz.http | 1 + .../src/main/resources/http-tools/temp.http | 122 +++++ .../src/main/resources/http-tools/test2.http | 18 + web-service/src/main/resources/logback.xml | 1 + .../java/cn/qaiu/db/ddl/CreateTableTest.java | 49 ++ 22 files changed, 829 insertions(+), 219 deletions(-) create mode 100644 web-service/src/main/java/cn/qaiu/lz/common/interceptorImpl/RateLimiter.java create mode 100644 web-service/src/main/java/cn/qaiu/lz/web/controller/ShoutController.java create mode 100644 web-service/src/main/java/cn/qaiu/lz/web/model/ShoutMessage.java create mode 100644 web-service/src/main/java/cn/qaiu/lz/web/service/ShoutService.java create mode 100644 web-service/src/main/java/cn/qaiu/lz/web/service/impl/ShoutServiceImpl.java create mode 100644 web-service/src/main/resources/http-tools/test2.http create mode 100644 web-service/src/test/java/cn/qaiu/db/ddl/CreateTableTest.java diff --git a/core-database/pom.xml b/core-database/pom.xml index 507a6a3..cb4c9b7 100644 --- a/core-database/pom.xml +++ b/core-database/pom.xml @@ -64,6 +64,12 @@ mysql-connector-j 9.2.0 + + + org.postgresql + postgresql + 42.7.3 + diff --git a/core-database/src/main/java/cn/qaiu/db/ddl/Constraint.java b/core-database/src/main/java/cn/qaiu/db/ddl/Constraint.java index cfcad14..1fcf4c6 100644 --- a/core-database/src/main/java/cn/qaiu/db/ddl/Constraint.java +++ b/core-database/src/main/java/cn/qaiu/db/ddl/Constraint.java @@ -21,7 +21,6 @@ public @interface Constraint { boolean notNull() default false; /** - * 唯一键约束 TODO 待实现 * @return 唯一键约束 */ String uniqueKey() default ""; diff --git a/core-database/src/main/java/cn/qaiu/db/ddl/CreateTable.java b/core-database/src/main/java/cn/qaiu/db/ddl/CreateTable.java index 3034ea8..2228980 100644 --- a/core-database/src/main/java/cn/qaiu/db/ddl/CreateTable.java +++ b/core-database/src/main/java/cn/qaiu/db/ddl/CreateTable.java @@ -8,7 +8,7 @@ import io.vertx.codegen.format.LowerCamelCase; import io.vertx.codegen.format.SnakeCase; import io.vertx.core.Future; import io.vertx.core.Promise; -import io.vertx.jdbcclient.JDBCPool; +import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.templates.annotations.Column; import io.vertx.sqlclient.templates.annotations.RowMapped; import org.apache.commons.lang3.StringUtils; @@ -24,164 +24,312 @@ import java.util.*; * @author QAIU */ public class CreateTable { - public static Map, String> javaProperty2SqlColumnMap = new HashMap<>(); + public static Map, String> javaProperty2SqlColumnMap = new HashMap<>() {{ + // Java类型到SQL类型的映射 + put(Integer.class, "INT"); + put(Short.class, "SMALLINT"); + put(Byte.class, "TINYINT"); + put(Long.class, "BIGINT"); + put(java.math.BigDecimal.class, "DECIMAL"); + put(Double.class, "DOUBLE"); + put(Float.class, "REAL"); + put(Boolean.class, "BOOLEAN"); + put(String.class, "VARCHAR"); + put(Date.class, "TIMESTAMP"); + put(java.time.LocalDateTime.class, "TIMESTAMP"); + put(java.sql.Timestamp.class, "TIMESTAMP"); + put(java.sql.Date.class, "DATE"); + put(java.sql.Time.class, "TIME"); + + // 基本数据类型 + put(int.class, "INT"); + put(short.class, "SMALLINT"); + put(byte.class, "TINYINT"); + put(long.class, "BIGINT"); + put(double.class, "DOUBLE"); + put(float.class, "REAL"); + put(boolean.class, "BOOLEAN"); + }}; private static final Logger LOGGER = LoggerFactory.getLogger(CreateTable.class); - - static { - javaProperty2SqlColumnMap.put(Integer.class, "INT"); - javaProperty2SqlColumnMap.put(Short.class, "SMALLINT"); - javaProperty2SqlColumnMap.put(Byte.class, "TINYINT"); - javaProperty2SqlColumnMap.put(Long.class, "BIGINT"); - javaProperty2SqlColumnMap.put(java.math.BigDecimal.class, "DECIMAL"); - javaProperty2SqlColumnMap.put(Double.class, "DOUBLE"); - javaProperty2SqlColumnMap.put(Float.class, "REAL"); - javaProperty2SqlColumnMap.put(Boolean.class, "BOOLEAN"); - javaProperty2SqlColumnMap.put(String.class, "VARCHAR"); - javaProperty2SqlColumnMap.put(java.util.Date.class, "TIMESTAMP"); - javaProperty2SqlColumnMap.put(java.time.LocalDateTime.class, "TIMESTAMP"); - javaProperty2SqlColumnMap.put(java.sql.Timestamp.class, "TIMESTAMP"); - javaProperty2SqlColumnMap.put(java.sql.Date.class, "DATE"); - javaProperty2SqlColumnMap.put(java.sql.Time.class, "TIME"); - - javaProperty2SqlColumnMap.put(int.class, "INT"); - javaProperty2SqlColumnMap.put(short.class, "SMALLINT"); - javaProperty2SqlColumnMap.put(byte.class, "TINYINT"); - javaProperty2SqlColumnMap.put(long.class, "BIGINT"); - javaProperty2SqlColumnMap.put(double.class, "DOUBLE"); - javaProperty2SqlColumnMap.put(float.class, "REAL"); - javaProperty2SqlColumnMap.put(boolean.class, "BOOLEAN"); - } + public static String UNIQUE_PREFIX = "idx_"; private static Case getCase(Class clz) { - switch (clz.getName()) { - case "io.vertx.codegen.format.CamelCase": - return CamelCase.INSTANCE; - case "io.vertx.codegen.format.SnakeCase": - return SnakeCase.INSTANCE; - case "io.vertx.codegen.format.LowerCamelCase": - return LowerCamelCase.INSTANCE; - default: - throw new UnsupportedOperationException(); + return switch (clz.getName()) { + case "io.vertx.codegen.format.CamelCase" -> CamelCase.INSTANCE; + case "io.vertx.codegen.format.SnakeCase" -> SnakeCase.INSTANCE; + case "io.vertx.codegen.format.LowerCamelCase" -> LowerCamelCase.INSTANCE; + default -> throw new UnsupportedOperationException(); + }; + } + + public static List getCreateTableSQL(Class clz, JDBCType type) { + // 获取表名和主键 + TableInfo tableInfo = extractTableInfo(clz, type); + + // 构建表的SQL语句 + List sqlList = new ArrayList<>(); + StringBuilder sb = new StringBuilder(50); + sb.append("CREATE TABLE IF NOT EXISTS ") + .append(tableInfo.quotationMarks).append(tableInfo.tableName).append(tableInfo.quotationMarks) + .append(" ( \r\n "); + + // 处理字段并生成列定义 + List indexSQLs = new ArrayList<>(); + processFields(clz, tableInfo, sb, indexSQLs); + + // 去掉最后一个逗号并添加表尾部信息 + String tableSQL = sb.substring(0, sb.lastIndexOf(",")) + tableInfo.endStr; + sqlList.add(tableSQL); + + // 添加索引SQL + sqlList.addAll(indexSQLs); + + return sqlList; + } + + + // 修改extractTableInfo方法,处理没有Table注解时默认使用id字段作为主键 + private static TableInfo extractTableInfo(Class clz, JDBCType type) { + String quotationMarks; + String endStr; + if (type == JDBCType.MySQL) { + quotationMarks = "`"; + endStr = ")ENGINE=InnoDB DEFAULT CHARSET=utf8;"; + } else { + quotationMarks = "\""; + endStr = ");"; + } + + String primaryKey = null; + String tableName = null; + Case caseFormat = SnakeCase.INSTANCE; + + // 判断类上是否有RowMapped注解 + if (clz.isAnnotationPresent(RowMapped.class)) { + RowMapped annotation = clz.getAnnotation(RowMapped.class); + caseFormat = getCase(annotation.formatter()); + } + + // 判断类上是否有Table注解 + if (clz.isAnnotationPresent(Table.class)) { + Table annotation = clz.getAnnotation(Table.class); + tableName = StringUtils.isNotEmpty(annotation.value()) + ? annotation.value() + : LowerCamelCase.INSTANCE.to(caseFormat, clz.getSimpleName()); + primaryKey = annotation.keyFields(); + } + + // 如果表名仍为null,使用类名转下划线命名作为表名 + if (StringUtils.isEmpty(tableName)) { + tableName = LowerCamelCase.INSTANCE.to(SnakeCase.INSTANCE, clz.getSimpleName()); + } + + // 如果主键为空,默认使用id字段作为主键 + if (StringUtils.isEmpty(primaryKey)) { + try { + clz.getDeclaredField("id"); + primaryKey = "id"; + } catch (NoSuchFieldException e) { + // 如果没有id字段,不设置主键 + primaryKey = null; + } + } + + return new TableInfo(tableName, quotationMarks, endStr, primaryKey, caseFormat, type); + } + + // 修改processFields方法,处理索引 + private static void processFields(Class clz, TableInfo tableInfo, StringBuilder sb, List indexSQLs) { + Field[] fields = clz.getDeclaredFields(); + for (Field field : fields) { + // 跳过无效字段 + if (isIgnoredField(field)) { + continue; + } + + // 获取字段名和SQL类型 + String column = LowerCamelCase.INSTANCE.to(tableInfo.caseFormat, field.getName()); + String sqlType = javaProperty2SqlColumnMap.get(field.getType()); + + // 处理字段注解 + column = processColumnAnnotation(field, column); + int[] decimalSize = {22, 2}; + int varcharSize = 255; + if (field.isAnnotationPresent(Length.class)) { + Length length = field.getAnnotation(Length.class); + decimalSize = length.decimalSize(); + varcharSize = length.varcharSize(); + } + + // 构建列定义 + sb.append(tableInfo.quotationMarks).append(column).append(tableInfo.quotationMarks) + .append(" ").append(sqlType); + appendTypeLength(sqlType, sb, decimalSize, varcharSize); + appendConstraints(field, sb, tableInfo); + appendPrimaryKey(tableInfo.primaryKey, column, sb); + + // 添加索引 + appendIndex(tableInfo, indexSQLs, field); + + sb.append(",\n "); } } - public static String getCreateTableSQL(Class clz, JDBCType type) { - String quotationMarks = type == JDBCType.H2DB ? "\"" : "`"; - String endStr = type == JDBCType.H2DB ? ");" : ")ENGINE=InnoDB DEFAULT CHARSET=utf8;"; - // 判断类上是否有次注解 - String primaryKey = null; // 主键 - String tableName = null; // 表名 - Case caseFormat = SnakeCase.INSTANCE; - if (clz.isAnnotationPresent(RowMapped.class)) { - RowMapped annotation = clz.getAnnotation(RowMapped.class); - Class formatter = annotation.formatter(); - caseFormat = getCase(formatter); - } + // 判断是否忽略字段 + private static boolean isIgnoredField(Field field) { + return field.getName().equals("serialVersionUID") + || StringUtils.isEmpty(javaProperty2SqlColumnMap.get(field.getType())) + || field.isAnnotationPresent(TableGenIgnore.class); + } - if (clz.isAnnotationPresent(Table.class)) { - // 获取类上的注解 - Table annotation = clz.getAnnotation(Table.class); - // 输出注解上的类名 - String tableNameAnnotation = annotation.value(); - if (StringUtils.isNotEmpty(tableNameAnnotation)) { - tableName = tableNameAnnotation; - } else { - tableName = LowerCamelCase.INSTANCE.to(caseFormat, clz.getSimpleName()); + // 处理Column注解 + private static String processColumnAnnotation(Field field, String column) { + if (field.isAnnotationPresent(Column.class)) { + Column columnAnnotation = field.getAnnotation(Column.class); + if (StringUtils.isNotBlank(columnAnnotation.name())) { + column = columnAnnotation.name(); } - primaryKey = annotation.keyFields(); } - Field[] fields = clz.getDeclaredFields(); - String column; - int[] decimalSize = {22, 2}; - int varcharSize = 255; - StringBuilder sb = new StringBuilder(50); - sb.append("CREATE TABLE IF NOT EXISTS ").append(quotationMarks).append(tableName).append(quotationMarks).append(" ( \r\n "); - boolean firstId = true; - for (Field f : fields) { - Class paramType = f.getType(); - String sqlType = javaProperty2SqlColumnMap.get(paramType); - if (f.getName().equals("serialVersionUID") || StringUtils.isEmpty(sqlType) || f.isAnnotationPresent(TableGenIgnore.class)) { - continue; + return column; + } + + // 添加类型长度 + private static void appendTypeLength(String sqlType, StringBuilder sb, int[] decimalSize, int varcharSize) { + if ("DECIMAL".equals(sqlType)) { + sb.append("(").append(decimalSize[0]).append(",").append(decimalSize[1]).append(")"); + } else if ("VARCHAR".equals(sqlType)) { + sb.append("(").append(varcharSize).append(")"); + } + } + + // 添加约束 + private static void appendConstraints(Field field, StringBuilder sb, TableInfo tableInfo) { + JDBCType type = tableInfo.dbType; + + if (field.isAnnotationPresent(Constraint.class)) { + Constraint constraint = field.getAnnotation(Constraint.class); + if (constraint.notNull()) { + sb.append(" NOT NULL"); } - column = LowerCamelCase.INSTANCE.to(caseFormat, f.getName()); - if (f.isAnnotationPresent(Column.class)) { - Column columnAnnotation = f.getAnnotation(Column.class); - //输出注解属性 - if (StringUtils.isNotBlank(columnAnnotation.name())) { - column = columnAnnotation.name(); - } + String apostrophe = constraint.defaultValueIsFunction() ? "" : "'"; + if (StringUtils.isNotEmpty(constraint.defaultValue())) { + sb.append(" DEFAULT ").append(apostrophe).append(constraint.defaultValue()).append(apostrophe); } - if (f.isAnnotationPresent(Length.class)) { - Length fieldAnnotation = f.getAnnotation(Length.class); - decimalSize = fieldAnnotation.decimalSize(); - varcharSize = fieldAnnotation.varcharSize(); - } - sb.append(quotationMarks).append(column).append(quotationMarks); - sb.append(" ").append(sqlType); - // 添加类型长度 - if (sqlType.equals("DECIMAL")) { - sb.append("(").append(decimalSize[0]).append(",").append(decimalSize[1]).append(")"); - } - if (sqlType.equals("VARCHAR")) { - sb.append("(").append(varcharSize).append(")"); - } - if (f.isAnnotationPresent(Constraint.class)) { - Constraint constraintAnnotation = f.getAnnotation(Constraint.class); - if (constraintAnnotation.notNull()) { - //非空约束 - sb.append(" NOT NULL"); - } - String apostrophe = constraintAnnotation.defaultValueIsFunction() ? "" : "'"; - if (StringUtils.isNotEmpty(constraintAnnotation.defaultValue())) { - //默认值约束 - sb.append(" DEFAULT ").append(apostrophe).append(constraintAnnotation.defaultValue()).append(apostrophe); - } - if (constraintAnnotation.autoIncrement() && paramType.equals(Integer.class) || paramType.equals(Long.class)) { - ////自增 + if (constraint.autoIncrement()) { + if (type == JDBCType.PostgreSQL) { + // 需要移除字段类型(最后一个单词) + if (field.getType().equals(Integer.class)) { + sb.delete(sb.lastIndexOf(" "), sb.length()); + sb.append(" SERIAL"); + } else if (field.getType().equals(Long.class)) { + sb.delete(sb.lastIndexOf(" "), sb.length()); + sb.append(" BIGSERIAL"); + } + } else if (field.getType().equals(Integer.class) || field.getType().equals(Long.class)) { sb.append(" AUTO_INCREMENT"); } } - if (StringUtils.isEmpty(primaryKey)) { - if (firstId) {//类型转换 - sb.append(" PRIMARY KEY"); - firstId = false; - } - } else { - if (primaryKey.equals(column.toLowerCase())) { - sb.append(" PRIMARY KEY"); + } + } + + // 添加主键 + private static void appendPrimaryKey(String primaryKey, String column, StringBuilder sb) { + if (StringUtils.isEmpty(primaryKey)) { + return; + } + if (primaryKey.equalsIgnoreCase(column)) { + sb.append(" PRIMARY KEY"); + } + } + + private static void appendIndex(TableInfo tableInfo, List indexSQLs, Field field) { + if (!field.isAnnotationPresent(Constraint.class)) { + return; + } + + Constraint constraint = field.getAnnotation(Constraint.class); + if (StringUtils.isEmpty(constraint.uniqueKey())) { + return; + } + + String indexName = UNIQUE_PREFIX + tableInfo.tableName + "_" + constraint.uniqueKey(); + String columnName = field.getName(); + + // 检查是否已有相同索引名称的索引 + Optional existingIndex = indexSQLs.stream() + .filter(sql -> sql.contains(tableInfo.quotationMarks + indexName + tableInfo.quotationMarks)) + .findFirst(); + + if (existingIndex.isPresent()) { + // 如果存在相同索引名称,追加字段到索引定义中 + String updatedIndex = existingIndex.get().replaceFirst( + "\\(([^)]+)\\)", // 匹配索引字段列表 + "($1, " + tableInfo.quotationMarks + columnName + tableInfo.quotationMarks + ")" + ); + indexSQLs.remove(existingIndex.get()); + indexSQLs.add(updatedIndex); + } else { + // 如果不存在相同索引名称,创建新的索引 + String indexSQL = String.format( + "CREATE UNIQUE INDEX %s %s%s%s ON %s%s%s (%s%s%s);", + tableInfo.dbType == JDBCType.MySQL ? "" : "IF NOT EXISTS", + tableInfo.quotationMarks, indexName, tableInfo.quotationMarks, + tableInfo.quotationMarks, tableInfo.tableName, tableInfo.quotationMarks, + tableInfo.quotationMarks, columnName, tableInfo.quotationMarks + ); + indexSQLs.add(indexSQL); + } + } + + // 表信息类 + private record TableInfo( + String tableName, // 表名 + String quotationMarks, // 引号或反引号 + String endStr, // 表尾部信息 + String primaryKey, // 主键字段 + Case caseFormat, // 命名格式 + JDBCType dbType // 数据库类型 + ) { + } + + public static Future createTable(Pool pool, JDBCType type) { + Promise promise = Promise.promise(); + Set> tableClasses = ReflectionUtil.getReflections().getTypesAnnotatedWith(Table.class); + + if (tableClasses.isEmpty()) { + LOGGER.warn("Table model class not found"); + promise.complete(); + return promise.future(); + } + + List> futures = new ArrayList<>(); + + for (Class clazz : tableClasses) { + List sqlList = getCreateTableSQL(clazz, type); + LOGGER.info("Class `{}` auto-generate table", clazz.getName()); + + for (String sql : sqlList) { + try { + pool.query(sql).execute().toCompletionStage().toCompletableFuture().join(); + futures.add(Future.succeededFuture()); + LOGGER.debug("Executed SQL:\n{}", sql); + } catch (Exception e) { + String message = e.getMessage(); + if (message != null && message.contains("Duplicate key name")) { + LOGGER.warn("Ignoring duplicate key error: {}", message); + futures.add(Future.succeededFuture()); + } else { + LOGGER.error("SQL Error: {}\nSQL: {}", message, sql); + futures.add(Future.failedFuture(e)); + throw new RuntimeException(e); // Stop execution for other exceptions + } } } - sb.append(",\n "); } - String sql = sb.toString(); - //去掉最后一个逗号 - int lastIndex = sql.lastIndexOf(","); - sql = sql.substring(0, lastIndex) + sql.substring(lastIndex + 1); - return sql.substring(0, sql.length() - 1) + endStr; - } - - public static Future createTable(JDBCPool pool, JDBCType type) { - Set> tableClassList = ReflectionUtil.getReflections().getTypesAnnotatedWith(Table.class); - if (tableClassList.isEmpty()) LOGGER.info("Table model class not fount"); - List> futures = new ArrayList<>(); - tableClassList.forEach(clazz -> { - String createTableSQL = getCreateTableSQL(clazz, type); - Future future = pool.query(createTableSQL).execute().compose(rs -> { - LOGGER.info("table auto generate:\n" + createTableSQL); - return Future.succeededFuture(); - }).onFailure(e -> { - LOGGER.error(e.getMessage() + " SQL: \n" + createTableSQL); - }); - futures.add(future); - }); - - Promise promise = Promise.promise(); - Future.all(futures).onSuccess(r -> { - LOGGER.info("create table success"); - promise.complete(); - }).onFailure(promise::fail); + Future.all(futures).onSuccess(r -> promise.complete()).onFailure(promise::fail); return promise.future(); } + } diff --git a/core-database/src/main/java/cn/qaiu/db/pool/JDBCPoolInit.java b/core-database/src/main/java/cn/qaiu/db/pool/JDBCPoolInit.java index 307ff5d..b22039f 100644 --- a/core-database/src/main/java/cn/qaiu/db/pool/JDBCPoolInit.java +++ b/core-database/src/main/java/cn/qaiu/db/pool/JDBCPoolInit.java @@ -11,9 +11,6 @@ import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - /** * 初始化JDBC *
Create date 2021/8/10 12:04 @@ -46,9 +43,6 @@ public class JDBCPoolInit { if (StringUtils.isBlank(builder.dbConfig.getString("provider_class"))) { builder.dbConfig.put("provider_class", providerClass); } - if (StringUtils.isBlank(builder.dbConfig.getString("driverClassName"))) { - builder.dbConfig.put("driverClassName", this.type.getDriverClassName()); - } } public static Builder builder() { @@ -97,6 +91,8 @@ public class JDBCPoolInit { return CreateTable.createTable(pool, type); } + + /** * 获取连接池 * diff --git a/core-database/src/main/java/cn/qaiu/db/pool/JDBCType.java b/core-database/src/main/java/cn/qaiu/db/pool/JDBCType.java index 5430b9b..789663d 100644 --- a/core-database/src/main/java/cn/qaiu/db/pool/JDBCType.java +++ b/core-database/src/main/java/cn/qaiu/db/pool/JDBCType.java @@ -8,38 +8,21 @@ import org.apache.commons.lang3.StringUtils; */ public enum JDBCType { // 添加驱动类型字段 - MySQL("com.mysql.cj.jdbc.Driver", "jdbc:mysql:"), - H2DB("org.h2.Driver", "jdbc:h2:"); - - private final String driverClassName; // 驱动类名 + MySQL("jdbc:mysql:"), + H2DB("jdbc:h2:"), + PostgreSQL("jdbc:postgresql:"); private final String urlPrefix; // JDBC URL 前缀 // 构造函数 - JDBCType(String driverClassName, String urlPrefix) { - this.driverClassName = driverClassName; + JDBCType(String urlPrefix) { this.urlPrefix = urlPrefix; } - // 获取驱动类名 - public String getDriverClassName() { - return driverClassName; - } - // 获取 JDBC URL 前缀 public String getUrlPrefix() { return urlPrefix; } - // 根据驱动类名获取 JDBC 类型 - public static JDBCType getJDBCType(String driverClassName) { - for (JDBCType jdbcType : values()) { - if (jdbcType.getDriverClassName().equalsIgnoreCase(driverClassName)) { - return jdbcType; - } - } - throw new RuntimeException("不支持的SQL驱动类型: " + driverClassName); - } - // 根据 JDBC URL 获取 JDBC 类型 public static JDBCType getJDBCTypeByURL(String jdbcURL) { for (JDBCType jdbcType : values()) { @@ -47,6 +30,6 @@ public enum JDBCType { return jdbcType; } } - throw new RuntimeException("不支持的SQL驱动类型: " + jdbcURL); + throw new RuntimeException("不支持的SQL类型: " + jdbcURL); } } diff --git a/core/src/main/java/cn/qaiu/vx/core/model/JsonResult.java b/core/src/main/java/cn/qaiu/vx/core/model/JsonResult.java index 2bf7b65..3d4cda7 100644 --- a/core/src/main/java/cn/qaiu/vx/core/model/JsonResult.java +++ b/core/src/main/java/cn/qaiu/vx/core/model/JsonResult.java @@ -8,7 +8,6 @@ import org.apache.commons.lang3.StringUtils; import java.io.Serial; import java.io.Serializable; -import java.util.List; /** * 响应实体 用于和前端交互 @@ -31,12 +30,10 @@ public class JsonResult implements Serializable { private int code = SUCCESS_CODE;//状态码 - private String msg = SUCCESS_MESSAGE;//消息 + private String msg = SUCCESS_MESSAGE; //消息 private boolean success = true; //是否成功 - private int count; - private T data; private long timestamp = System.currentTimeMillis(); //时间戳 @@ -55,20 +52,6 @@ public class JsonResult implements Serializable { this.success = success; } - public JsonResult(int code, String msg, boolean success, T data, int count) { - this(code, msg, success, data); - this.count = count; - } - - public int getCount() { - return count; - } - - public JsonResult setCount(int count) { - this.count = count; - return this; - } - public int getCode() { return code; } @@ -137,24 +120,9 @@ public class JsonResult implements Serializable { return new JsonResult<>(SUCCESS_CODE, msg, true, data); } - // 响应成功消息和数据实体 - public static JsonResult data(String msg, T data, int count) { - if (StringUtils.isEmpty(msg)) msg = SUCCESS_MESSAGE; - return new JsonResult<>(SUCCESS_CODE, msg, true, data, count); - } - // 响应数据实体 public static JsonResult data(T data) { - int count = 0; - if (data instanceof List) { - count = ((List) data).size(); - } - return new JsonResult<>(SUCCESS_CODE, SUCCESS_MESSAGE, true, data, count); - } - - // 响应数据实体 - public static JsonResult data(T data, int count) { - return new JsonResult<>(SUCCESS_CODE, SUCCESS_MESSAGE, true, data, count); + return new JsonResult<>(SUCCESS_CODE, SUCCESS_MESSAGE, true, data); } // 响应成功消息 diff --git a/parser/src/main/java/cn/qaiu/parser/PanDomainTemplate.java b/parser/src/main/java/cn/qaiu/parser/PanDomainTemplate.java index 2367ed6..f1db5fa 100644 --- a/parser/src/main/java/cn/qaiu/parser/PanDomainTemplate.java +++ b/parser/src/main/java/cn/qaiu/parser/PanDomainTemplate.java @@ -87,7 +87,8 @@ public enum PanDomainTemplate { "https://cowtransfer.com/s/{shareKey}", CowTool.class), CT("城通网盘", - compile("https://(?:[a-zA-Z\\d-]+\\.)?(ctfile|545c|u062|ghpym|474b)\\.com/f(ile)?/(?.+)"), + compile("https://(?:[a-zA-Z\\d-]+\\.)?(ctfile|545c|u062|ghpym|474b)\\.com/f(ile)?/" + + "(?[0-9a-zA-Z_-]+)(\\?p=(?\\w+))?"), "https://474b.com/file/{shareKey}", CtTool.class), // https://xxx.118pan.com/bxxx diff --git a/parser/src/main/java/cn/qaiu/parser/impl/LzTool.java b/parser/src/main/java/cn/qaiu/parser/impl/LzTool.java index f554773..890426d 100644 --- a/parser/src/main/java/cn/qaiu/parser/impl/LzTool.java +++ b/parser/src/main/java/cn/qaiu/parser/impl/LzTool.java @@ -183,9 +183,9 @@ public class LzTool extends PanBase { String jsText = getJsByPwd(pwd, html, "var urls =window.location.href"); ScriptObjectMirror scriptObjectMirror = JsExecUtils.executeDynamicJs(jsText, "file"); Map data = CastUtil.cast(scriptObjectMirror.get("data")); - System.out.println(data); MultiMap map = MultiMap.caseInsensitiveMultiMap(); data.forEach((k, v) -> map.set(k, v.toString())); + log.debug("解析参数: {}", map); MultiMap headers = getHeaders(sUrl); String url = SHARE_URL_PREFIX + "/filemoreajax.php?file=" + data.get("fid"); @@ -223,7 +223,7 @@ public class LzTool extends PanBase { .setSize(sizeNum) .setPanType(panType) .setParserUrl(getDomainName() + "/d/" + panType + "/" + id); - System.out.println(fileInfo); + log.debug("文件信息: {}", fileInfo); list.add(fileInfo); }); promise.complete(list); diff --git a/parser/src/main/resources/logback.xml b/parser/src/main/resources/logback.xml index dda2ef4..f7f57a9 100644 --- a/parser/src/main/resources/logback.xml +++ b/parser/src/main/resources/logback.xml @@ -54,6 +54,7 @@ + diff --git a/web-service/src/main/java/cn/qaiu/lz/AppMain.java b/web-service/src/main/java/cn/qaiu/lz/AppMain.java index d67ab4e..3f93e57 100644 --- a/web-service/src/main/java/cn/qaiu/lz/AppMain.java +++ b/web-service/src/main/java/cn/qaiu/lz/AppMain.java @@ -3,6 +3,7 @@ package cn.qaiu.lz; import cn.qaiu.WebClientVertxInit; import cn.qaiu.db.pool.JDBCPoolInit; import cn.qaiu.lz.common.cache.CacheConfigLoader; +import cn.qaiu.lz.common.interceptorImpl.RateLimiter; import cn.qaiu.vx.core.Deploy; import cn.qaiu.vx.core.util.ConfigConstant; import cn.qaiu.vx.core.util.VertxHolder; @@ -11,8 +12,9 @@ import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import io.vertx.core.json.jackson.DatabindCodec; import io.vertx.core.shareddata.LocalMap; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.commons.lang3.time.DateFormatUtils; + +import java.util.Date; import static cn.qaiu.vx.core.util.ConfigConstant.LOCAL; @@ -25,8 +27,6 @@ import static cn.qaiu.vx.core.util.ConfigConstant.LOCAL; */ public class AppMain { - private static final Logger LOGGER = LoggerFactory.getLogger(AppMain.class); - public static void main(String[] args) { Deploy.instance().start(args, AppMain::exec); } @@ -40,14 +40,22 @@ public class AppMain { private static void exec(JsonObject jsonObject) { WebClientVertxInit.init(VertxHolder.getVertxInstance()); DatabindCodec.mapper().registerModule(new JavaTimeModule()); + // 限流 + if (jsonObject.containsKey("rateLimit")) { + JsonObject rateLimit = jsonObject.getJsonObject("rateLimit"); + RateLimiter.init(rateLimit); + } // 数据库 if (jsonObject.getJsonObject(ConfigConstant.SERVER).getBoolean("enableDatabase")) { JDBCPoolInit.builder().config(jsonObject.getJsonObject("dataSource")) .build() .initPool().onSuccess(PreparedStatement -> { - LOGGER.info("数据库连接成功"); - String addr = jsonObject.getJsonObject(ConfigConstant.SERVER).getString("domainName"); - LOGGER.info("启动成功: \n本地服务地址: {}", addr); + VertxHolder.getVertxInstance().setTimer(1000, id -> { + System.out.println(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS")); + System.out.println("数据库连接成功"); + String addr = jsonObject.getJsonObject(ConfigConstant.SERVER).getString("domainName"); + System.out.println("启动成功: \n本地服务地址: " + addr); + }); }); } // 缓存 diff --git a/web-service/src/main/java/cn/qaiu/lz/common/interceptorImpl/DefaultInterceptor.java b/web-service/src/main/java/cn/qaiu/lz/common/interceptorImpl/DefaultInterceptor.java index 2d9a845..907df26 100644 --- a/web-service/src/main/java/cn/qaiu/lz/common/interceptorImpl/DefaultInterceptor.java +++ b/web-service/src/main/java/cn/qaiu/lz/common/interceptorImpl/DefaultInterceptor.java @@ -2,12 +2,15 @@ package cn.qaiu.lz.common.interceptorImpl; import cn.qaiu.vx.core.annotaions.HandleSortFilter; import cn.qaiu.vx.core.interceptor.BeforeInterceptor; +import cn.qaiu.vx.core.util.ConfigConstant; import cn.qaiu.vx.core.util.SharedDataUtil; import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; import lombok.extern.slf4j.Slf4j; import static cn.qaiu.vx.core.util.ConfigConstant.IGNORES_REG; +import static io.vertx.core.http.HttpHeaders.CONTENT_TYPE; /** * 前置拦截器实现 @@ -20,8 +23,43 @@ public class DefaultInterceptor implements BeforeInterceptor { @Override public void handle(RoutingContext ctx) { - // System.out.println("进入前置拦截器1->" + ctx.request().path()); - doNext(ctx); + // 读取配置 如果配置了限流 则进行限流 + if (!SharedDataUtil.getJsonConfig(ConfigConstant.GLOBAL_CONFIG).containsKey("rateLimit")) { + doNext(ctx); + return; + } + JsonObject rateLimit = SharedDataUtil.getJsonConfig(ConfigConstant.GLOBAL_CONFIG) + .getJsonObject("rateLimit"); + // # 限流配置 + //rateLimit: + // # 是否启用限流 + // enable: true + // # 限流的请求数 + // limit: 1000 + // # 限流的时间窗口(单位秒) + // timeWindow: 60 + if (rateLimit.getBoolean("enable")) { + // 获取当前请求的路径 + String path = ctx.request().path(); + // 正则匹配路径 + if (ignores.stream().anyMatch(ignore -> path.matches(ignore.toString()))) { + // 如果匹配到忽略的路径,则不进行限流 + doNext(ctx); + return; + } + RateLimiter.checkRateLimit(ctx.request()) + .onSuccess(v -> { + // 继续执行下一个拦截器 + doNext(ctx); + }) + .onFailure(t -> { + // 限流失败,返回错误响应 + log.warn("Rate limit exceeded for path: {}", path); + ctx.response().putHeader(CONTENT_TYPE, "text/html; charset=utf-8") + .setStatusCode(429) + .end(t.getMessage()); + }); + } } } diff --git a/web-service/src/main/java/cn/qaiu/lz/common/interceptorImpl/RateLimiter.java b/web-service/src/main/java/cn/qaiu/lz/common/interceptorImpl/RateLimiter.java new file mode 100644 index 0000000..e595714 --- /dev/null +++ b/web-service/src/main/java/cn/qaiu/lz/common/interceptorImpl/RateLimiter.java @@ -0,0 +1,77 @@ +package cn.qaiu.lz.common.interceptorImpl; + +import io.vertx.core.Future; +import io.vertx.core.Promise; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.json.JsonObject; +import lombok.extern.slf4j.Slf4j; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@Slf4j +public class RateLimiter { + + private static final Map ipRequestMap = new ConcurrentHashMap<>(); + private static int MAX_REQUESTS = 10; // 最大请求次数 + private static long TIME_WINDOW = 60 * 1000; // 时间窗口(毫秒) + + private static String PATH_REG; // 限流路径正则 + + public static void init(JsonObject rateLimitConfig) { + MAX_REQUESTS = rateLimitConfig.getInteger("limit", 10); + TIME_WINDOW = rateLimitConfig.getInteger("timeWindow", 60) * 1000L; // 转换为毫秒 + PATH_REG = rateLimitConfig.getString("pathReg", "/.*"); + log.info("RateLimiter initialized with max requests: {}, time window: {} ms, path regex: {}", + MAX_REQUESTS, TIME_WINDOW, PATH_REG); + } + + synchronized public static Future checkRateLimit(HttpServerRequest request) { + Promise promise = Promise.promise(); + if (!request.path().matches(PATH_REG)) { + // 如果请求路径不匹配正则,则不进行限流 + promise.complete(); + return promise.future(); + } + + String ip = request.remoteAddress().host(); + + ipRequestMap.compute(ip, (key, requestInfo) -> { + long currentTime = System.currentTimeMillis(); + if (requestInfo == null || currentTime - requestInfo.timestamp > TIME_WINDOW) { + // 初始化或重置计数器 + return new RequestInfo(1, currentTime); + } else { + // 增加计数器 + requestInfo.count++; + return requestInfo; + } + }); + + RequestInfo info = ipRequestMap.get(ip); + if (info.count > MAX_REQUESTS) { + // 超过限制 + // 计算剩余时间 + long remainingTime = TIME_WINDOW - (System.currentTimeMillis() - info.timestamp); + BigDecimal bigDecimal = BigDecimal.valueOf(remainingTime / 1000.0) + .setScale(2, RoundingMode.HALF_UP); + promise.fail("请求次数太多了,请" + bigDecimal + "秒后再试。"); + } else { + // 未超过限制,继续处理 + promise.complete(); + } + return promise.future(); + } + + private static class RequestInfo { + int count; + long timestamp; + + RequestInfo(int count, long time) { + this.count = count; + this.timestamp = time; + } + } +} diff --git a/web-service/src/main/java/cn/qaiu/lz/web/controller/ShoutController.java b/web-service/src/main/java/cn/qaiu/lz/web/controller/ShoutController.java new file mode 100644 index 0000000..389f5d7 --- /dev/null +++ b/web-service/src/main/java/cn/qaiu/lz/web/controller/ShoutController.java @@ -0,0 +1,36 @@ +package cn.qaiu.lz.web.controller; + +import cn.qaiu.lz.web.service.ShoutService; +import cn.qaiu.vx.core.annotaions.RouteHandler; +import cn.qaiu.vx.core.annotaions.RouteMapping; +import cn.qaiu.vx.core.enums.RouteMethod; +import cn.qaiu.vx.core.model.JsonResult; +import cn.qaiu.vx.core.util.AsyncServiceUtil; +import io.vertx.core.Future; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.RoutingContext; + +@RouteHandler("/v2/shout") +public class ShoutController { + + private final ShoutService shoutService = AsyncServiceUtil.getAsyncServiceInstance(ShoutService.class); + + @RouteMapping(value = "/submit", method = RouteMethod.POST) + public Future submitMessage(RoutingContext ctx) { + String content = ctx.body().asJsonObject().getString("content"); + if (content == null || content.trim().isEmpty()) { + return Future.failedFuture("内容不能为空"); + } + return shoutService.submitMessage(content, ctx.request().remoteAddress().host()).compose(code -> + Future.succeededFuture(JsonResult.data(code).toJsonObject())); + } + + @RouteMapping(value = "/retrieve", method = RouteMethod.GET) + public Future retrieveMessage(RoutingContext ctx) { + String code = ctx.request().getParam("code"); + if (code == null || code.length() != 6) { + return Future.failedFuture("提取码必须为6位数字"); + } + return shoutService.retrieveMessage(code); + } +} diff --git a/web-service/src/main/java/cn/qaiu/lz/web/model/ShoutMessage.java b/web-service/src/main/java/cn/qaiu/lz/web/model/ShoutMessage.java new file mode 100644 index 0000000..07ca134 --- /dev/null +++ b/web-service/src/main/java/cn/qaiu/lz/web/model/ShoutMessage.java @@ -0,0 +1,40 @@ +package cn.qaiu.lz.web.model; + +import cn.qaiu.db.ddl.Constraint; +import cn.qaiu.db.ddl.Length; +import cn.qaiu.db.ddl.Table; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; + +/** + * 隔空喊话消息 + */ +@Data +@Table("t_messages") +public class ShoutMessage { + + private static final long serialVersionUID = 1L; + + @Constraint(autoIncrement= true, notNull = true) + private Long id; + + @Length(varcharSize = 16) + @Constraint(notNull = true, uniqueKey = "uk_code") + private String code; // 6位提取码 + + @Length(varcharSize = 4096) + private String content; // 消息内容 + + @Length(varcharSize = 32) + private String ip; // 发送者IP + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date createTime = new Date(); // 创建时间 + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date expireTime; // 过期时间 + + private Boolean isUsed = false; // 是否已使用 +} diff --git a/web-service/src/main/java/cn/qaiu/lz/web/service/ShoutService.java b/web-service/src/main/java/cn/qaiu/lz/web/service/ShoutService.java new file mode 100644 index 0000000..c9e8470 --- /dev/null +++ b/web-service/src/main/java/cn/qaiu/lz/web/service/ShoutService.java @@ -0,0 +1,15 @@ +package cn.qaiu.lz.web.service; + +import cn.qaiu.vx.core.base.BaseAsyncService; +import io.vertx.codegen.annotations.ProxyGen; +import io.vertx.core.Future; +import io.vertx.core.json.JsonObject; + +@ProxyGen +public interface ShoutService extends BaseAsyncService { + // 提交消息并返回提取码 + Future submitMessage(String content, String host); + + // 通过提取码获取消息 + Future retrieveMessage(String code); +} diff --git a/web-service/src/main/java/cn/qaiu/lz/web/service/impl/ShoutServiceImpl.java b/web-service/src/main/java/cn/qaiu/lz/web/service/impl/ShoutServiceImpl.java new file mode 100644 index 0000000..d1ed531 --- /dev/null +++ b/web-service/src/main/java/cn/qaiu/lz/web/service/impl/ShoutServiceImpl.java @@ -0,0 +1,90 @@ +package cn.qaiu.lz.web.service.impl; + +import cn.qaiu.db.pool.JDBCPoolInit; +import cn.qaiu.lz.web.service.ShoutService; +import cn.qaiu.vx.core.annotaions.Service; +import cn.qaiu.vx.core.model.JsonResult; +import io.vertx.core.Future; +import io.vertx.core.Promise; +import io.vertx.core.json.JsonObject; +import io.vertx.jdbcclient.JDBCPool; +import io.vertx.sqlclient.Tuple; +import lombok.extern.slf4j.Slf4j; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Random; + +@Slf4j +@Service +@SuppressWarnings("SqlResolve") // 这里是为了避免检查SQL语句的警告 +public class ShoutServiceImpl implements ShoutService { + private static final int CODE_LENGTH = 6; + private static final int EXPIRE_HOURS = 24; + private final JDBCPool jdbcPool = JDBCPoolInit.instance().getPool(); + + @Override + public Future submitMessage(String content, String host) { + Promise promise = Promise.promise(); + String code = generateRandomCode(); + // 判断一下当前code是否存在消息 + LocalDateTime expireTime = LocalDateTime.now().plusHours(EXPIRE_HOURS); + + String sql = "INSERT INTO t_messages (code, content, expire_time, ip) VALUES (?, ?, ?, ?)"; + + jdbcPool.preparedQuery(sql) + .execute(Tuple.of(code, content, + java.sql.Timestamp.from(expireTime.atZone(ZoneId.systemDefault()).toInstant()), + host)) + .onSuccess(res -> { + log.info("Message submitted with code: {}", code); + promise.complete(code); + }) + .onFailure(err -> { + log.error("Failed to submit message", err); + promise.fail(err); + }); + + return promise.future(); + } + + @Override + public Future retrieveMessage(String code) { + Promise promise = Promise.promise(); + + String sql = "SELECT content FROM t_messages WHERE code = ? AND expire_time > NOW()"; + + jdbcPool.preparedQuery(sql) + .execute(Tuple.of(code)) + .onSuccess(rows -> { + if (rows.size() > 0) { + String content = rows.iterator().next().getString("content"); + // 标记为已使用 + markAsUsed(code); + promise.complete(JsonResult.data(content).toJsonObject()); + } else { + promise.fail("无效的提取码或消息已过期"); + } + }) + .onFailure(err -> { + log.error("Failed to retrieve message", err); + promise.fail(err); + }); + + return promise.future(); + } + + private void markAsUsed(String code) { + String sql = "UPDATE t_messages SET is_used = TRUE WHERE code = ?"; + jdbcPool.preparedQuery(sql).execute(Tuple.of(code)); + } + + private String generateRandomCode() { + Random random = new Random(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < CODE_LENGTH; i++) { + sb.append(random.nextInt(10)); + } + return sb.toString(); + } +} diff --git a/web-service/src/main/resources/app-dev.yml b/web-service/src/main/resources/app-dev.yml index ce2574b..abc1fbf 100644 --- a/web-service/src/main/resources/app-dev.yml +++ b/web-service/src/main/resources/app-dev.yml @@ -5,7 +5,8 @@ server: # 使用数据库 enableDatabase: true # 服务域名或者IP 生成二维码链接时需要 - domainName: http://127.0.0.1:6401 +# domainName: http://127.0.0.1:6401 + domainName: https://lz.qaiu.top # 反向代理服务器配置路径(不用加后缀) proxyConf: server-proxy @@ -25,18 +26,29 @@ custom: routeTimeOut: 15000 # 拦截器匹配规则 ignoresReg: - - /v2/statisticsInfo + # - /v2/statisticsInfo - .*/test.*$ # 参数注入的实体类包路径匹配正则 (防止同名类引发歧义) entityPackagesReg: - ^cn\.qaiu\.lz\.web\.model\..* +# 限流配置 +rateLimit: + # 是否启用限流 + enable: true + # 限流的请求数 + limit: 5 + # 限流的时间窗口(单位秒) + timeWindow: 10 + # 路径匹配规则 + pathReg: ^/v2/.* + # 数据源配置 dataSource: #jdbcUrl: jdbc:mysql://127.0.0.1:3306/nfddata?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=GMT%2B8&useSSL=false - jdbcUrl: jdbc:h2:file:./db/nfdData1;MODE=MySQL;DATABASE_TO_UPPER=FALSE + jdbcUrl: jdbc:h2:file:./db/nfdData;MODE=MySQL;DATABASE_TO_UPPER=FALSE username: root password: '123456' @@ -63,7 +75,8 @@ cache: mne: 30 mqq: 30 mkg: 30 - p115: 5 + p115: 30 + ct: 30 # httpClient静态代理服务器配置(外网代理) proxy: diff --git a/web-service/src/main/resources/http-tools/pan-iz.http b/web-service/src/main/resources/http-tools/pan-iz.http index 14a2dfe..8ffec17 100644 --- a/web-service/src/main/resources/http-tools/pan-iz.http +++ b/web-service/src/main/resources/http-tools/pan-iz.http @@ -25,3 +25,4 @@ https://api.ilanzou.com/unproved/file/redirect?uuid=0&devType=6×tamp=453240 https://www.ilanzou.com/s/zHkna1S ### fileId: 145042258 +https://api.ilanzou.com/unproved/recommend/list?devType=6&devModel=Chrome&uuid=AjiM-Wl782OuHyuvKiuaH&extra=2×tamp=311D0B438400DB5D98F1D2FF5A7A86F1&shareId=jQ9i3F0&type=0&offset=1&limit=60 diff --git a/web-service/src/main/resources/http-tools/temp.http b/web-service/src/main/resources/http-tools/temp.http index db9bb0c..3c96728 100644 --- a/web-service/src/main/resources/http-tools/temp.http +++ b/web-service/src/main/resources/http-tools/temp.http @@ -101,6 +101,128 @@ referer: https://www.vyuyun.com https://down2.bilnn.top/uploads/11283/306bdccd233ffd2c.jar?filename=C400003mAan70zUy5O+%282%29.m4a&mime=audio%2Fx-m4a&verify=1729911697-SLZtEHXB4TM*ze1j31WMyNA**p743DY*GN2sajUx5w*2mM** +### +# @no-cookie-jar +# curl 'https://www.vyuyun.com/apiv1/share/getShareDownUrl/QPyZsb/gDZBTm?password=&downcode=Iv7YsjhM_6kzHijjtHyTLfPqlr6xwR5kqVNfMMAdWBEBcW1iNNIK-hDvrwLyzF4a' +# -H 'Accept: */*' +# -H 'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8' +# -H 'Cache-Control: no-cache' +# -H 'Connection: keep-alive' +# -H 'Cookie: Hm_lvt_6e5421de7bb814e1dfc49bbc577c04d3=1734489414,1736133991,1736139184; HMACCOUNT=7C4EDB1414D4E56F; ginmin-session=MTczNjEzOTY5OHxEWDhFQVFMX2dBQUJFQUVRQUFBRV80QUFBQT09fE1kmd-TQNIU-AcLRbEhAMzpcoaTIqnDkU_z1Z2kW64A; Hm_lpvt_6e5421de7bb814e1dfc49bbc577c04d3=1736139719; __gads=ID=710230eb796d6baf:T=1736139187:RT=1736139729:S=ALNI_MauDIW3Tl0IYOZILi-6C0qMU328RA; __gpi=UID=00000fa7e525a50e:T=1736139187:RT=1736139729:S=ALNI_MYOvo89qg4zJq0s35CanMI3lx-zLw; __eoi=ID=df2245386092f112:T=1736139187:RT=1736139729:S=AA-AfjbzvOhiQPaERKQKlmEEC7Ds' +# -H 'DNT: 1' +# -H 'Pragma: no-cache' +# -H 'Referer: https://www.vyuyun.com/s/QPyZsb/file?password=' +# -H 'Sec-Fetch-Dest: empty' +# -H 'Sec-Fetch-Mode: cors' +# -H 'Sec-Fetch-Site: same-origin' +# -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36' +# -H 'sec-ch-ua: "Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"' +# -H 'sec-ch-ua-mobile: ?0' +# -H 'sec-ch-ua-platform: "Windows"' +# -H 'token;' +GET https://www.vyuyun.com/apiv1/share/getShareDownUrl/QPyZsb/gDZBTm?password=&downcode=Iv7YsjhM_6kzHijjtHyTLfPqlr6xwR5kqVNfMMAdWBEBcW1iNNIK-hDvrwLyzF4a +Accept: */* +Accept-Language: zh-CN,zh;q=0.9,en;q=0.8 +Cache-Control: no-cache +Connection: keep-alive +Cookie: Hm_lvt_6e5421de7bb814e1dfc49bbc577c04d3=1734489414,1736133991,1736139184; HMACCOUNT=7C4EDB1414D4E56F; ginmin-session=MTczNjEzOTY5OHxEWDhFQVFMX2dBQUJFQUVRQUFBRV80QUFBQT09fE1kmd-TQNIU-AcLRbEhAMzpcoaTIqnDkU_z1Z2kW64A; Hm_lpvt_6e5421de7bb814e1dfc49bbc577c04d3=1736139719; __gads=ID=710230eb796d6baf:T=1736139187:RT=1736139729:S=ALNI_MauDIW3Tl0IYOZILi-6C0qMU328RA; __gpi=UID=00000fa7e525a50e:T=1736139187:RT=1736139729:S=ALNI_MYOvo89qg4zJq0s35CanMI3lx-zLw; __eoi=ID=df2245386092f112:T=1736139187:RT=1736139729:S=AA-AfjbzvOhiQPaERKQKlmEEC7Ds +DNT: 1 +Pragma: no-cache +Referer: https://www.vyuyun.com/s/QPyZsb/file?password= +Sec-Fetch-Dest: empty +Sec-Fetch-Mode: cors +Sec-Fetch-Site: same-origin +User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 +sec-ch-ua: "Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24" +sec-ch-ua-mobile: ?0 +sec-ch-ua-platform: "Windows" +token: + +### + + + +### +# curl 'https://www.vyuyun.com/apiv1/share/file/QPyZsb?password=' +# -H 'Accept: application/json, text/plain, */*' +# -H 'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8' +# -H 'Cache-Control: no-cache' +# -H 'Connection: keep-alive' +# -H 'Cookie: Hm_lvt_6e5421de7bb814e1dfc49bbc577c04d3=1734489414,1736133991; HMACCOUNT=845216228CFDAEF3; ginmin-session=MTczNjEzNzIwNXxEWDhFQVFMX2dBQUJFQUVRQUFBaV80QUFBUVp6ZEhKcGJtY01CUUFEWVdsa0JuTjBjbWx1Wnd3SEFBVXhNVGt3T0E9PXygMqAo7e0FsZpAm5tggrVEAmipmptNAe4RF-StdbItOA==; __gads=ID=02846a0224427b16:T=1729911643:RT=1736137206:S=ALNI_MYN-82qd45RW0rYq1JewWVxCmoZAQ; __gpi=UID=00000f553e085dd5:T=1729911643:RT=1736137206:S=ALNI_Mb2dIMzZrO14lDlqCBOShH29OEfng; __eoi=ID=3e810b8ce65b0a46:T=1729911643:RT=1736137206:S=AA-AfjbN5kpzrPK6QBBlTEmMlxXx; Hm_lpvt_6e5421de7bb814e1dfc49bbc577c04d3=1736137222' +# -H 'DNT: 1' +# -H 'Pragma: no-cache' +# -H 'Referer: https://www.vyuyun.com/s/QPyZsb/file?password=' +# -H 'Sec-Fetch-Dest: empty' +# -H 'Sec-Fetch-Mode: cors' +# -H 'Sec-Fetch-Site: same-origin' +# -H 'Token;' +# -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36' +# -H 'X-Requested-With: XMLHttpRequest' +# -H 'sec-ch-ua: "Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"' +# -H 'sec-ch-ua-mobile: ?0' +# -H 'sec-ch-ua-platform: "Windows"' +GET https://www.vyuyun.com/apiv1/share/file/QPyZsb?password= +Accept: application/json, text/plain, */* +Accept-Language: zh-CN,zh;q=0.9,en;q=0.8 +Cache-Control: no-cache +Connection: keep-alive +Cookie: Hm_lvt_6e5421de7bb814e1dfc49bbc577c04d3=1734489414,1736133991; HMACCOUNT=845216228CFDAEF3; ginmin-session=MTczNjEzNzIwNXxEWDhFQVFMX2dBQUJFQUVRQUFBaV80QUFBUVp6ZEhKcGJtY01CUUFEWVdsa0JuTjBjbWx1Wnd3SEFBVXhNVGt3T0E9PXygMqAo7e0FsZpAm5tggrVEAmipmptNAe4RF-StdbItOA==; __gads=ID=02846a0224427b16:T=1729911643:RT=1736137206:S=ALNI_MYN-82qd45RW0rYq1JewWVxCmoZAQ; __gpi=UID=00000f553e085dd5:T=1729911643:RT=1736137206:S=ALNI_Mb2dIMzZrO14lDlqCBOShH29OEfng; __eoi=ID=3e810b8ce65b0a46:T=1729911643:RT=1736137206:S=AA-AfjbN5kpzrPK6QBBlTEmMlxXx; Hm_lpvt_6e5421de7bb814e1dfc49bbc577c04d3=1736137222 +DNT: 1 +Pragma: no-cache +Referer: https://www.vyuyun.com/s/QPyZsb/file?password= +Sec-Fetch-Dest: empty +Sec-Fetch-Mode: cors +Sec-Fetch-Site: same-origin +Token: +User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 +X-Requested-With: XMLHttpRequest +sec-ch-ua: "Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24" +sec-ch-ua-mobile: ?0 +sec-ch-ua-platform: "Windows" + +### +# curl 'https://www.vyuyun.com/apiv1/share/getShareDownUrl/QPyZsb/gDZBTm?password=&downcode=Iv7YsjhM_6kzHijjtHyTLT2CySejYHyXEtW_2oI9kqCnevPaRjMpzFDaq4dT4z3R' +# -H 'Accept: */*' +# -H 'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6' +# -H 'Cache-Control: no-cache' +# -H 'Connection: keep-alive' +# -H 'Cookie: noticeId=1; Hm_lvt_6e5421de7bb814e1dfc49bbc577c04d3=1736134006; HMACCOUNT=DD7A065B1B5C7BCF; ginmin-session=MTczNjEzNDExNXxEWDhFQVFMX2dBQUJFQUVRQUFELUFRal9nQUFEQm5OMGNtbHVad3dIQUFWMGIydGxiZ1p6ZEhKcGJtY01fNjhBXzZ4b1pVOVNMWGRwVVRWemVrTlFhRGxzTjNCRlNHOW5jMWRLU2pKVFluUlZOVWR2UlhkWGNEbElUMEZNYlV0RVMwRkJTeloxVlhaS1UycE5aMXBrTW1jMWJYVlRPVjlOWkVVMWNHMU1iMkZMTW1sR1NHcFhRbWhPWjAxb05FWmpWV2swVWpOU01GZE9aRk5UZUd0WVJFSmphMWw0WVZKVkxYRlhSalZ5UlhSSmRYaG9jbVJuTWpkbmRFWlBWRlpDV0ZkWGMzWjJaVFZtUjFwSWMwRnBlbXhhUVROVFowaEtZakJqTlc4cUJuTjBjbWx1Wnd3SUFBWnZjR1Z1YVdRR2MzUnlhVzVuREFJQUFBWnpkSEpwYm1jTUNnQUlZV1J0YVc1ZmFYTUdjM1J5YVc1bkRBTUFBVEE9fMMethsDCW-b_YQHRj_0KHUVB1AcmohDXI4L4en8_8Nd; Hm_lpvt_6e5421de7bb814e1dfc49bbc577c04d3=1736138831' +# -H 'DNT: 1' +# -H 'Pragma: no-cache' +# -H 'Referer: https://www.vyuyun.com/s/QPyZsb/file?password=' +# -H 'Sec-Fetch-Dest: empty' +# -H 'Sec-Fetch-Mode: cors' +# -H 'Sec-Fetch-Site: same-origin' +# -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0' +# -H 'sec-ch-ua: "Microsoft Edge";v="131", "Chromium";v="131", "Not_A Brand";v="24"' +# -H 'sec-ch-ua-mobile: ?0' +# -H 'sec-ch-ua-platform: "Windows"' +# -H 'token: heOR-wiQ5szCPh9l7pEHogsWJJ2SbtU5GoEwWp9HOALmKDKAAK6uUvJSjMgZd2g5muS9_MdE5pmLoaK2iFHjWBhNgMh4FcUi4R3R0WNdSSxkXDBckYxaRU-qWF5rEtIuxhrdg27gtFOTVBXWWsvve5fGZHsAizlZA3SgHJb0c5o*' +GET https://www.vyuyun.com/apiv1/share/getShareDownUrl/QPyZsb/gDZBTm?password=&downcode=Iv7YsjhM_6kzHijjtHyTLT2CySejYHyXEtW_2oI9kqCnevPaRjMpzFDaq4dT4z3R +Accept: */* +Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6 +Cache-Control: no-cache +Connection: keep-alive +Cookie: noticeId=1; Hm_lvt_6e5421de7bb814e1dfc49bbc577c04d3=1736134006; HMACCOUNT=DD7A065B1B5C7BCF; ginmin-session=MTczNjEzNDExNXxEWDhFQVFMX2dBQUJFQUVRQUFELUFRal9nQUFEQm5OMGNtbHVad3dIQUFWMGIydGxiZ1p6ZEhKcGJtY01fNjhBXzZ4b1pVOVNMWGRwVVRWemVrTlFhRGxzTjNCRlNHOW5jMWRLU2pKVFluUlZOVWR2UlhkWGNEbElUMEZNYlV0RVMwRkJTeloxVlhaS1UycE5aMXBrTW1jMWJYVlRPVjlOWkVVMWNHMU1iMkZMTW1sR1NHcFhRbWhPWjAxb05FWmpWV2swVWpOU01GZE9aRk5UZUd0WVJFSmphMWw0WVZKVkxYRlhSalZ5UlhSSmRYaG9jbVJuTWpkbmRFWlBWRlpDV0ZkWGMzWjJaVFZtUjFwSWMwRnBlbXhhUVROVFowaEtZakJqTlc4cUJuTjBjbWx1Wnd3SUFBWnZjR1Z1YVdRR2MzUnlhVzVuREFJQUFBWnpkSEpwYm1jTUNnQUlZV1J0YVc1ZmFYTUdjM1J5YVc1bkRBTUFBVEE9fMMethsDCW-b_YQHRj_0KHUVB1AcmohDXI4L4en8_8Nd; Hm_lpvt_6e5421de7bb814e1dfc49bbc577c04d3=1736138831 +DNT: 1 +Pragma: no-cache +Referer: https://www.vyuyun.com/s/QPyZsb/file?password= +Sec-Fetch-Dest: empty +Sec-Fetch-Mode: cors +Sec-Fetch-Site: same-origin +User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0 +sec-ch-ua: "Microsoft Edge";v="131", "Chromium";v="131", "Not_A Brand";v="24" +sec-ch-ua-mobile: ?0 +sec-ch-ua-platform: "Windows" +token: heOR-wiQ5szCPh9l7pEHogsWJJ2SbtU5GoEwWp9HOALmKDKAAK6uUvJSjMgZd2g5muS9_MdE5pmLoaK2iFHjWBhNgMh4FcUi4R3R0WNdSSxkXDBckYxaRU-qWF5rEtIuxhrdg27gtFOTVBXWWsvve5fGZHsAizlZA3SgHJb0c5o* + +### + + + + + + ### 118网盘 https://qaiu.118pan.com/b1228264 #密码:qaiu https://qaiu.118pan.com/b1228264 diff --git a/web-service/src/main/resources/http-tools/test2.http b/web-service/src/main/resources/http-tools/test2.http new file mode 100644 index 0000000..37aa7a4 --- /dev/null +++ b/web-service/src/main/resources/http-tools/test2.http @@ -0,0 +1,18 @@ +### +POST http://127.0.0.1:6400/v2/shout/submit +Content-Type: application/json + +{ + "content": "CREATE UNIQUE INDEX `idx_uk_code` ON `t_messages` (`code`);" +} + +### +GET http://127.0.0.1:6400/v2/shout/retrieve?code=878696 + +### +响应: +{ +"data": "这是一条秘密消息", +"code": 200, +"msg": "success" +} diff --git a/web-service/src/main/resources/logback.xml b/web-service/src/main/resources/logback.xml index 2e7a0e8..af6dda5 100644 --- a/web-service/src/main/resources/logback.xml +++ b/web-service/src/main/resources/logback.xml @@ -55,6 +55,7 @@ + diff --git a/web-service/src/test/java/cn/qaiu/db/ddl/CreateTableTest.java b/web-service/src/test/java/cn/qaiu/db/ddl/CreateTableTest.java new file mode 100644 index 0000000..2dffa09 --- /dev/null +++ b/web-service/src/test/java/cn/qaiu/db/ddl/CreateTableTest.java @@ -0,0 +1,49 @@ +package cn.qaiu.db.ddl; + +import cn.qaiu.db.pool.JDBCType; +import io.vertx.sqlclient.templates.annotations.Column; +import org.junit.Test; + +public class CreateTableTest { + + + + static class TestModel2 { + @Column(name = "id") + @Constraint(autoIncrement = true) + private Long id; + + @Column(name = "name") + @Constraint(notNull = true, uniqueKey = "ne_unique") + private String name; + + @Column(name = "age") + private Integer age; + + @Column(name = "email") + @Constraint(notNull = true, uniqueKey = "ne_unique") + private String email; + + @Column(name = "created_at") + private java.util.Date createdAt; + } + + @Test + public void getCreateTableSQL() { + // 测试 + String sql = String.join("\n", CreateTable.getCreateTableSQL(TestModel2.class, JDBCType.H2DB)); + System.out.println(sql); + } + @Test + public void getCreateTableSQL2() { + // 测试 + String sql = String.join("\n", CreateTable.getCreateTableSQL(TestModel2.class, JDBCType.MySQL)); + System.out.println(sql); + } + @Test + public void getCreateTableSQL3() { + // 测试 + String sql = String.join("\n", CreateTable.getCreateTableSQL(TestModel2.class, JDBCType.PostgreSQL)); + System.out.println(sql); + } +}