# 在线运行预览设计 ## 目标 为 EasyCode 生成出来的项目提供本机在线运行预览能力,让用户不需要手动下载、解压、配置和启动生成项目,也能快速确认真实运行效果。 ## 第一期范围 第一期运行在当前开发机器上。系统会为每个生成项目创建独立的预览工作目录,初始化专用 MySQL 数据库,把生成出来的 Spring Boot 后端和 Vue 前端作为子进程启动,并把本地预览地址返回给前端页面。 这一期主要用于开发阶段快速验证,不作为公开的多租户沙箱。 ## 用户流程 1. 用户完成项目预览生成。 2. 用户打开项目预览页。 3. 用户点击 `运行预览`。 4. 后端把生成源码 zip 导出到本地预览工作目录。 5. 后端创建专用预览数据库,并导入生成 SQL。 6. 后端在空闲本地端口启动生成出来的 Spring Boot 服务。 7. 后端在空闲本地端口启动生成出来的 Vue 前端。 8. 页面展示后端状态、前端状态、日志和外部预览链接。 9. 用户可以在页面上停止当前预览会话。 ## 架构 该能力在现有源码预览/下载服务旁边新增一个运行预览服务。 - 现有 `IFrontProjectPreviewService` 继续负责生成源码 zip。 - 新增 `IFrontProjectRunPreviewService` 负责运行预览会话。 - 第一期会话状态保存在内存中,并按 `userId + projectId` 隔离。 - 工作目录创建在 `preview-workspaces/project-{projectId}` 下。 - 生成项目会被解压到该工作目录。 - 预览数据库命名形如 `preview_{projectId}_{timestamp}`。 - 子进程通过环境变量注入端口和数据库配置,不直接改写生成文件。 ## 生成模板要求 生成后端模板必须从环境变量读取运行配置: ```yaml server: port: ${SERVER_PORT:8080} spring: datasource: url: ${DB_URL:jdbc:mysql://localhost:3306/vip?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8} username: ${DB_USERNAME:root} password: ${DB_PASSWORD:123456} ``` 生成 Vue 模板必须从环境变量读取前端端口和后端代理地址: ```js const port = process.env.PORT || 8081 const apiBaseUrl = process.env.VUE_APP_API_BASE_URL || 'http://localhost:8080' ``` ## 后端接口 ```text POST /front/project/{projectId}/run-preview GET /front/project/{projectId}/run-preview POST /front/project/{projectId}/run-preview/stop ``` 所有接口都使用当前前台用户 ID,并且必须调用项目服务校验项目归属。 ## 会话状态 状态枚举: - `NOT_STARTED` - `PREPARING` - `STARTING` - `RUNNING` - `FAILED` - `STOPPED` 返回字段: - `projectId` - `status` - `message` - `backendUrl` - `frontendUrl` - `backendPort` - `frontendPort` - `workspacePath` - `databaseName` - `logs` - `startedAt` - `updatedAt` ## 安全约束 第一期不能把生成项目直接运行在宿主 RuoYi 进程里。生成项目必须作为独立子进程启动,并且可以被单独停止。 解压工作目录时必须拒绝逃逸工作目录的 zip 条目。每次启动新会话前可以清理项目工作目录,但清理范围必须限制在配置的预览工作根目录内。 数据库初始化会创建新数据库并导入生成 SQL。第一期停止预览时暂不自动删除数据库;等预览生命周期稳定后,可以再补清理策略。 ## 后续阶段 第二期建议迁移到 Docker Compose: - 一个生成后端容器 - 一个生成前端容器 - 一个 MySQL 容器 - 反向代理路径,例如 `/preview/{projectId}/` - 基于 TTL 的自动清理