Files
netdisk-fast-download/.github/copilot-instructions.md
q 2fcf9cfab1 docs: 更新文档导航和解析器指南
- 添加演练场(Playground)文档导航区到主 README
- 新增 Python 解析器文档链接(开发指南、测试报告、LSP集成)
- 更新前端版本号至 0.1.9b19p
- 补充 Python 解析器 requests 库使用章节和官方文档链接
- 添加 JavaScript 和 Python 解析器的语言版本和官方文档
- 优化文档结构,分类为项目文档和外部资源
2026-01-11 22:35:45 +08:00

13 KiB
Raw Blame History

GitHub Copilot Instructions - NetDisk Fast Download

项目简介

网盘快速下载项目,支持多种网盘链接解析和下载加速的 Java Web 应用。

技术栈要求

核心技术

  • Java: JDK 17必须
  • 框架: Vert.x 4.5.23(异步响应式框架)
  • 构建: Maven 3.x
  • 日志: SLF4J 2.0.5 + Logback 1.5.19
  • 前端: Vue.js + Monaco Editor

重要依赖

  • Lombok 1.18.38 - 简化 Java 代码
  • Jackson 2.14.2 - JSON 处理
  • Commons Lang3 3.18.0 - 工具类
  • Reflections 0.10.2 - 反射工具

代码生成规范

Java 代码风格

1. 使用 Lombok 简化代码

// ✅ 推荐:使用 Lombok 注解
@Data
@Builder
@Slf4j
public class Example {
    private String name;
    private int value;
}

// ❌ 避免:手写 getter/setter
public class Example {
    private String name;
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}

2. 异步编程模式Vert.x

// ✅ 推荐:使用 Vert.x Future
public Future<String> fetchData() {
    return vertx.createHttpClient()
        .request(HttpMethod.GET, "http://example.com")
        .compose(HttpClientRequest::send)
        .compose(response -> response.body())
        .map(Buffer::toString);
}

// ❌ 避免:阻塞操作
public String fetchData() {
    // 不要在 Event Loop 中执行阻塞代码
    Thread.sleep(1000); // ❌
    return result;
}

3. 日志记录

// ✅ 推荐:使用 @Slf4j + 参数化日志
@Slf4j
public class Service {
    public void process(String id) {
        log.info("Processing item: {}", id);
        try {
            // ...
        } catch (Exception e) {
            log.error("Failed to process item: {}", id, e);
        }
    }
}

// ❌ 避免:字符串拼接
log.info("Processing item: " + id); // 性能差
System.out.println("Debug info"); // 不使用 System.out

4. 异常处理

// ✅ 推荐:完整的异常处理
public Future<Result> operation() {
    return service.execute()
        .recover(err -> {
            log.error("Operation failed", err);
            return Future.succeededFuture(Result.error(err.getMessage()));
        });
}

// ❌ 避免:空的 catch 块或吞掉异常
try {
    doSomething();
} catch (Exception e) {
    // ❌ 空 catch
}

包和类命名

  • 基础包名:cn.qaiu
  • 模块包结构:
    • cn.qaiu.core.* - 核心功能
    • cn.qaiu.parser.* - 解析器相关
    • cn.qaiu.db.* - 数据库相关
    • cn.qaiu.service.* - 业务服务
    • cn.qaiu.web.* - Web 相关

测试代码

// ✅ 推荐JUnit 4 测试
public class ServiceTest {
    
    @Before
    public void setUp() {
        // 初始化
    }
    
    @Test
    public void testMethod() {
        // Given
        String input = "test";
        
        // When
        String result = service.process(input);
        
        // Then
        assertEquals("expected", result);
    }
    
    @After
    public void tearDown() {
        // 清理
    }
}

特定模块指导

Core 模块 - Web 路由封装(必须使用,禁止重复造轮子)

核心思想:使用注解定义路由,框架自动处理请求和响应

1. 使用 @RouteHandler 和 @RouteMapping

// ✅ 推荐:使用注解定义路由
@RouteHandler(value = "/api/v1", order = 10)
@Slf4j
public class UserController {
    
    private final UserService userService = AsyncServiceUtil.getAsyncServiceInstance(UserService.class);
    
    // GET /api/v1/users
    @RouteMapping(value = "/users", method = RouteMethod.GET)
    public Future<JsonResult<List<User>>> getUsers() {
        return userService.findAll()
            .map(JsonResult::success)
            .otherwise(err -> JsonResult.error(err.getMessage()));
    }
    
    // GET /api/v1/user/:id (路径参数自动注入)
    @RouteMapping(value = "/user/:id", method = RouteMethod.GET)
    public Future<User> getUser(String id) {
        // 返回值自动序列化为 JSON
        return userService.findById(id);
    }
    
    // POST /api/v1/user (查询参数自动注入)
    @RouteMapping(value = "/user", method = RouteMethod.POST)
    public Future<JsonResult<User>> createUser(HttpServerRequest request, String name, Integer age) {
        return userService.create(name, age)
            .map(JsonResult::success);
    }
    
    // 重定向示例
    @RouteMapping(value = "/redirect/:id", method = RouteMethod.GET)
    public void redirect(HttpServerResponse response, String id) {
        String targetUrl = "https://example.com/" + id;
        ResponseUtil.redirect(response, targetUrl);
    }
}

// ❌ 避免:手动创建 Router 和 Handler
Router router = Router.router(vertx);
router.get("/api/users").handler(ctx -> {
    // 不要这样写!使用注解方式
});

2. 自动参数注入规则

  • 路径参数/user/:idpublic Future<User> getUser(String id)
  • 查询参数?name=xxx&age=18public Future<User> create(String name, Integer age)
  • Vert.x 对象:自动注入 HttpServerRequest, HttpServerResponse, RoutingContext
  • 请求体POST/PUT 的 JSON 自动反序列化为方法参数对象

3. 响应处理

// 方式1返回 Future框架自动处理
public Future<User> getUser(String id) {
    return userService.findById(id);  // 自动序列化为 JSON
}

// 方式2返回 JsonResult 统一格式
public Future<JsonResult<User>> getUser(String id) {
    return userService.findById(id).map(JsonResult::success);
}

// 方式3手动控制响应仅在特殊情况使用
public void customResponse(HttpServerResponse response) {
    ResponseUtil.fireJsonObjectResponse(response, jsonObject);
}

4. WebSocket 路由

@RouteHandler("/ws")
public class WebSocketHandler {
    
    @SockRouteMapper("/chat")
    public void handleChat(SockJSSocket socket) {
        socket.handler(buffer -> {
            log.info("Received: {}", buffer.toString());
            socket.write(buffer);  // Echo
        });
    }
}

Core-Database 模块 - DDL 自动生成(必须使用,禁止重复造轮子)

核心思想:使用注解定义实体,自动生成建表 SQL

1. 定义实体类

// ✅ 推荐:使用注解定义实体
@Data
@Table(value = "t_user", keyFields = "id")  // 表名和主键
public class User {
    
    @Constraint(autoIncrement = true)
    private Long id;  // 主键自增
    
    @Constraint(notNull = true, uniqueKey = "uk_email")
    @Length(varcharSize = 100)
    private String email;  // 非空 + 唯一索引 + 长度100
    
    @Constraint(notNull = true)
    @Length(varcharSize = 50)
    private String name;
    
    @Constraint(defaultValue = "0")
    private Integer status;  // 默认值 0
    
    @Constraint(defaultValue = "NOW()", defaultValueIsFunction = true)
    private Date createdAt;  // 默认当前时间
    
    @TableGenIgnore  // 忽略此字段,不生成列
    private transient String tempField;
}

// 应用启动时自动建表
CreateTable.createTable(pool, JDBCType.MySQL);

// ❌ 避免:手写建表 SQL
String sql = "CREATE TABLE t_user (id BIGINT AUTO_INCREMENT PRIMARY KEY, ...)";
pool.query(sql).execute();  // 不要这样写!

2. 支持的注解

@Table - 表定义

  • value - 表名(默认类名转下划线)
  • keyFields - 主键字段名(默认 "id"

@Constraint - 字段约束

  • notNull = true - 非空约束
  • uniqueKey = "uk_name" - 唯一索引(相同名称的字段组成联合唯一索引)
  • defaultValue = "value" - 默认值
  • defaultValueIsFunction = true - 默认值是函数(如 NOW()
  • autoIncrement = true - 自增(仅用于主键)

@Length - 字段长度

  • varcharSize = 255 - VARCHAR 长度(默认 255
  • decimalSize = {10, 2} - DECIMAL 精度(默认 {22, 2}

@Column - 自定义列名

  • name = "column_name" - 指定数据库列名

@TableGenIgnore - 忽略字段(不生成列)

3. 自动创建数据库

// ✅ 推荐:自动创建数据库
JsonObject dbConfig = new JsonObject()
    .put("jdbcUrl", "jdbc:mysql://localhost:3306/mydb")
    .put("username", "root")
    .put("password", "password");

CreateDatabase.createDatabase(dbConfig);

// ❌ 避免:手动连接和执行 CREATE DATABASE

4. 支持的数据库类型

  • JDBCType.MySQL - MySQL
  • JDBCType.PostgreSQL - PostgreSQL
  • JDBCType.H2DB - H2 数据库

Parser 模块

  • 支持自定义解析器Java/Python/JavaScript
  • Python 使用 GraalPy 执行
  • 需要考虑安全性和沙箱隔离
  • WebSocket 支持外部 Python 环境连接
// Parser 接口实现示例
public class CustomParser implements IParser {
    @Override
    public Future<ParseResult> parse(String url, Map<String, String> params) {
        return Future.future(promise -> {
            // 异步解析逻辑
            promise.complete(result);
        });
    }
}

Maven 配置注意事项

测试执行

# 默认打包跳过测试
mvn clean package

# 执行测试
mvn test -Dmaven.test.skip=false
mvn clean package -Dmaven.test.skip=false

模块化构建

# 构建特定模块
mvn clean package -pl parser -am

重要约定

1. 异步优先

  • 所有 I/O 操作必须异步
  • 使用 Vert.x Future/Promise API
  • 避免阻塞 Event Loop

2. 资源管理

// ✅ 推荐:使用 try-with-resources
try (InputStream is = new FileInputStream(file)) {
    // 使用资源
}

// 或者确保在 finally 中关闭
HttpClient client = vertx.createHttpClient();
// 使用后必须关闭
client.close();

3. 配置外部化

  • 配置文件优先使用 JSON 格式
  • 敏感信息不要硬编码
  • 支持环境变量覆盖

4. 错误处理

  • 使用 Future 的 recover/otherwise
  • 记录详细的错误日志
  • 向用户返回友好的错误信息

性能考虑

  1. 使用连接池: 数据库连接、HTTP 客户端
  2. 缓存策略: 解析结果、静态资源
  3. 批量操作: 避免 N+1 查询问题
  4. 异步非阻塞: 充分利用 Vert.x 优势

安全要求

Parser 模块安全

  • 执行自定义代码必须沙箱隔离
  • 限制资源访问(文件、网络)
  • 设置执行超时
  • 验证输入参数
// ✅ 推荐:带安全检查的执行
public Future<Result> executeUserCode(String code) {
    // 验证代码
    if (!SecurityValidator.isValid(code)) {
        return Future.failedFuture("Invalid code");
    }
    
    // 在沙箱中执行
    return sandboxExecutor.execute(code, TIMEOUT);
}

输入验证

// ✅ 推荐:验证所有外部输入
public Future<Result> parse(String url) {
    if (StringUtils.isBlank(url) || !UrlValidator.isValid(url)) {
        return Future.failedFuture("Invalid URL");
    }
    // 继续处理
}

文档和注释

JavaDoc 注释

/**
 * 解析网盘链接获取下载信息
 * 
 * @param url 网盘分享链接
 * @param params 额外参数(如密码)
 * @return Future<ParseResult> 解析结果
 */
public Future<ParseResult> parse(String url, Map<String, String> params) {
    // 实现
}

复杂逻辑注释

// 处理特殊情况:某些网盘需要二次验证
// 参考文档docs/parser-flow.md
if (needsSecondaryVerification) {
    // 实现二次验证逻辑
}

常见模式

链式异步调用

return fetchMetadata(url)
    .compose(meta -> validateMetadata(meta))
    .compose(meta -> fetchDownloadUrl(meta))
    .compose(downloadUrl -> generateResult(downloadUrl))
    .recover(this::handleError);

事件处理

vertx.eventBus().<JsonObject>consumer("parser.request", msg -> {
    JsonObject body = msg.body();
    parse(body.getString("url"))
        .onSuccess(result -> msg.reply(JsonObject.mapFrom(result)))
        .onFailure(err -> msg.fail(500, err.getMessage()));
});

不应该做的事

  1. 在 Event Loop 线程中执行阻塞操作
  2. 使用 System.out.println() 而不是日志框架
  3. 硬编码配置值(端口、路径、密钥等)
  4. 忽略异常或使用空 catch 块
  5. 返回 null应该使用 Optional 或 Future.failedFuture()
  6. 在生产代码中使用 e.printStackTrace()
  7. 直接操作 Thread 而不使用 Vert.x 的 executeBlocking
  8. 提交包含 logs/ 目录的代码

代码审查清单

生成代码时请确保:

  • 使用 Lombok 注解简化代码
  • 异步操作使用 Vert.x Future
  • 添加了 @Slf4j 和适当的日志
  • 异常处理完整
  • 输入参数已验证
  • 资源正确释放
  • 添加了必要的 JavaDoc
  • 遵循项目包命名规范
  • 没有阻塞操作在 Event Loop 中
  • 测试用例覆盖主要场景

参考资源

  • Vert.x 文档: https://vertx.io/docs/
  • 项目 Parser 文档: parser/doc/
  • 前端文档: web-front/doc/
  • 安全测试指南: parser/doc/SECURITY_TESTING_GUIDE.md