# runx **Repository Path**: cnoldtree/runx ## Basic Information - **Project Name**: runx - **Description**: java脚手架 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-13 - **Last Updated**: 2026-08-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Java 商业级脚手架 基于 **Spring Boot 3 + PostgreSQL + MyBatis-Plus + Sa-Token** 的 Maven 多模块脚手架,开箱即用企业级通用能力:统一响应、全局异常、参数校验、RBAC 权限、Redis 缓存与分布式锁、操作/登录日志、字段脱敏、XSS 清洗、接口限流、代码生成器。 ## 技术栈 | 分类 | 选型 | |------|------| | 核心框架 | Spring Boot 3.3.x(Java 17) | | 持久层 | MyBatis-Plus 3.5.x + Druid + PostgreSQL | | 缓存 | Redis(Spring Data Redis + Redisson) | | 认证授权 | Sa-Token 1.39.x(注解式 RBAC) | | 工具 | Hutool 5.8.x | | 接口文档 | SpringDoc OpenAPI 2.6.x | | 构建 | Maven 多模块聚合 | ## 模块结构 ``` java-scaffold ├── scaffold-common 通用层:统一响应 Result、全局异常、参数校验、工具类、常量 ├── scaffold-data 数据层:Druid+PG、MyBatis-Plus 分页/自动填充、Redis、分布式锁、限流 ├── scaffold-security 安全层:Sa-Token 集成 + RBAC 实体/服务 ├── scaffold-log 审计层:操作日志、登录日志、字段脱敏、XSS 清洗 ├── scaffold-generator 代码生成器(表结构 → CRUD 代码) └── scaffold-web 启动模块:配置、示例接口、聚合所有能力 ``` 模块依赖:`web → {security, log, generator} → data → common`。 ## 开发文档 - 脚手架开发指南(目录结构 / 配置约定 / 脚本命令 / 模板文件 / 依赖版本管理):[docs/development/scaffold-guideline.md](docs/development/scaffold-guideline.md) - 开发环境搭建:[docs/development-setup.md](docs/development-setup.md) - 架构决策记录(ADR):[docs/adr/](docs/adr/) ## 快速开始 ### 1. 前置条件 - JDK 17 - Maven 3.6+ - PostgreSQL 12+ - Redis 6+ ### 2. 建库(表结构由 Flyway 自动迁移,无需手动执行 SQL) ```bash createdb scaffold ``` > 表结构由 Flyway 接管:`scaffold-data/src/main/resources/db/migration/V1.0__init_schema.sql` 在应用首次启动时自动建表/迁移。 ### 3. 修改配置 编辑 `scaffold-web/src/main/resources/application-dev.yml`,修改数据库与 Redis 连接信息。 ### 4. 启动 推荐使用仓库脚本(离线环境自动适配): ```bash bash scripts/run-backend.sh # 启动后端(默认 8080,验证码关闭;PORT=9090 自定义端口) bash scripts/dev-frontend.sh # 前端开发服务器(5173,/api 代理到后端 8080) ``` > 前端为 pnpm Monorepo(`frontend/`,含 `@runx/types / hooks / ui / api` 共享包)。结构与命令详见 [frontend/README.md](frontend/README.md)。 或传统方式: ```bash mvn clean package -DskipTests java -jar scaffold-web/target/java-scaffold.jar ``` 或直接在 IDE 运行 `com.scaffold.WebApplication`。 ### 5. 验证 - 接口文档:http://localhost:8080/api/swagger-ui.html - Druid 监控:http://localhost:8080/api/druid/ (dev 环境,账号 admin/admin123) - 默认管理员账号:`admin / admin123`(首次启动自动初始化) 登录示例: ```bash curl -X POST http://localhost:8080/api/auth/login \ -H 'Content-Type: application/json' \ -d '{"username":"admin","password":"admin123"}' ``` 响应中的 `token` 放在后续请求的 `Authorization` 请求头。 ## 内置功能 > **API 在线文档(OpenAPI/Swagger)**:后端启动后访问 > `http://localhost:8080/api/swagger-ui.html` 即可查看并在线调试全部接口 > (95+ 个已标注 `@Operation` 的端点,含 Try it out 交互)。接口契约 JSON 位于 > `/api/v3/api-docs`。 - **统一响应**:`Result` / `PageResult`,约定 `code=200` 为成功 - **全局异常**:`BusinessException`、`MethodArgumentNotValidException` 等统一转换 - **RBAC 权限**:用户/角色/权限/菜单 + 注解 `@SaCheckLogin` `@SaCheckPermission`(见 `SysUserController`) - **操作日志**:`@Log` 注解 + AOP 异步入库(见 `LogAspect`) - **登录日志**:登录成功后自动记录 - **字段脱敏**:`@Sensitive(type = SensitiveType.PHONE)` 标注字段序列化时自动脱敏 - **XSS 清洗**:`XssFilter` 对请求参数与 Header 做输入清洗,JSON 请求体原样透传(避免破坏报文结构) - **分布式锁**:`DistributedLock.tryLock(...)` 封装 Redisson - **接口限流**:`@RateLimit(key = "#dto.username", rate = 5, interval = 1)` - **代码生成器**:见下文 ## 代码生成器 `scaffold-generator` 基于 MyBatis-Plus AutoGenerator + 自研 Velocity 渲染,根据表结构一键生成**前后端完整 CRUD 代码**: - **Entity**:表驱动生成真实字段(含 `deleted` 逻辑删除列) - **Mapper / Service / ServiceImpl**:MyBatis-Plus 标准结构 - **Controller**:分页 / 详情 / 新增 / 修改 / 删除,已封装 `Result` / `PageResult` 统一响应、`@SaCheckLogin` 鉴权(权限注解按需自行补充)、`@Tag` / `@Operation` OpenAPI 注解、构造器注入;**删除走 MyBatis-Plus 全局逻辑删除(`removeById` 自动置 `deleted=1`)**,与脚手架 `deleted` 字段约定一致,无需手写 SQL - **前端(Vue3 + Element Plus)**:API 封装(`{Entity}Api.js`)+ 列表/搜索/分页/新增编辑表单页(`.vue`)。字段由表结构驱动,**控件推断规则**:列名命中 `status/state/type/kind/category/level/gender/enable/_flag` → 字典下拉(`el-select`,生成占位选项,约定 0=禁用/1=启用);`is_` 前缀或 `boolean` → 开关;`timestamp/date/time` → 日期;`remark/description/note/content/memo/address` 或 `text` 类型 → 多行;数值类型 → 数字框;其余 → 文本。**按列 `NOT NULL` 自动生成 `el-form` 校验规则**,并支持字段级约束:`varchar(n)` → `max` 长度规则;列名命中 `email/mail` → 邮箱格式、`mobile/phone` → 手机号正则、`url/homepage/website/link` → URL 格式;提交时 `formRef.validate()` 拦截非法输入。字典字段生成占位选项并在注释中给出 `dictApi` 真实接口对接示例;布尔字段在表格中以「是/否」标签展示、字典字段以 `dictLabel` 格式化;框架字段(id、create_time、update_time、deleted)自动排除。表含真实外键约束(`FOREIGN KEY`)的列,自动识别为**关联下拉**:前端渲染 `el-select` 并在 `onMounted` 中从关联表接口(`/api/{module}/{rel-table}` 的 `page`)加载选项(value=关联表 id,label=名称字段,自动探测 `name/title/label` 命名字段),实现跨表关联选择,无需手写。 ### 运行方式 ```bash # 1. 先将依赖模块安装到本地仓库(首次或模块有变更时) mvn -q install -DskipTests # 2. 运行生成器:参数为「表名(可逗号分隔,或 all=全库业务表) 模块名」 mvn -f scaffold-generator/pom.xml -am exec:java \ -Dexec.mainClass=com.scaffold.generator.CodeGenerator \ -Dexec.args="sys_user,demo_table demo" # 3. 全库一键生成(自动读取库中全部业务表) java -cp com.scaffold.generator.CodeGenerator all demo # 4. 直落真实业务模块(GEN_LIVE=web 时,后端输出到 web 模块源码、前端输出到 examples/frontend) GEN_DB_URL=jdbc:postgresql://127.0.0.1:5432/scaffold GEN_DB_USER=postgres GEN_DB_PWD=postgres GEN_LIVE=web \ java -cp com.scaffold.generator.CodeGenerator category shop ``` 生成结果默认输出到仓库根的 `generated/`(不进入主编译 classpath,避免污染源码): - `generated/java/com/scaffold/web/modules/{module}/...`(后端六件套) - `generated/resources/mapper/...`(Mapper XML) - `generated/web/{module}/{Entity}.vue` 与 `{Entity}Api.js`(前端页面与接口封装) 将需要的文件复制到对应业务模块即可使用。前端需项目内存在 `src/utils/request.js`(基于 axios 封装,成功时返回 `Result` 对象),与后端 `Result` 约定一致。 ### 环境变量 | 变量 | 说明 | 默认值 | |------|------|--------| | `GEN_DB_URL` | 数据库连接 | `jdbc:postgresql://localhost:5432/scaffold` | | `GEN_DB_USER` | 数据库用户名 | `postgres` | | `GEN_DB_PWD` | 数据库密码 | `postgres` | | `GEN_OUTPUT_DIR` | Java 代码输出目录 | `generated/java` | | `GEN_XML_DIR` | Mapper XML 输出目录 | `generated/resources/mapper` | | `GEN_WEB_DIR` | 前端代码输出目录 | `generated/web` | | `GEN_LIVE` | 设为 `web` 时**直落真实业务模块**:后端写入 `scaffold-web/.../modules/{module}`,前端写入 `examples/frontend/{module}`(跳过 `generated/`) | 空(输出到 `generated/`) | > 控制器模板为自研 Velocity 渲染(与前端同机制),位于 `scaffold-generator/src/main/resources/templates/scaffold-controller.java.vm`,可按团队规范修改后重新运行。生成器**不使用** AutoGenerator 内置 controller 模板(同名冲突会退化为物理删除),删除统一依赖 MyBatis-Plus 全局 `logic-delete` 配置通过 `removeById` 逻辑删除。 ## 工程自举(scaffold-bootstrap) `scaffold-bootstrap` 提供「一行命令从零拉起全新多模块 Spring Boot 工程」的自举能力:复用 `scaffold-generator` 的 Velocity 渲染范式,把标准多模块骨架(common / data / security / log / web)渲染到指定目录,**完全自包含、不依赖 runx 内部模块**,开箱即 `mvn clean package` 可编译。 ### 运行方式 ```bash # 先将依赖模块安装到本地仓库(首次或模块有变更时) mvn -q install -DskipTests # 运行初始化器:参数为 groupId / artifactId / rootPackage / outputDir mvn -pl scaffold-bootstrap exec:java \ -Dexec.args="--groupId=com.acme --artifactId=acme --rootPackage=com.acme --outputDir=/tmp/acme" ``` ### 参数说明 | 参数 | 说明 | 默认值 | |------|------|--------| | `--groupId` | 工程 groupId | `com.example` | | `--artifactId` | 工程 artifactId(同时作为子目录名) | `demo-project` | | `--rootPackage` | 源码根包名 | 同 groupId | | `--outputDir` | 输出目录(默认在仓库之外,绝不污染 runx 源码树) | `./generated-bootstrap` | | `--capabilities` | 逗号分隔的能力清单(预留) | 空 | ### 生成工程结构 ``` {outputDir}/{artifactId}/ ├── pom.xml # parent=spring-boot-starter-parent:3.3.5 ├── .gitignore / Dockerfile ├── common/ (Result / PageResult / BusinessException / GlobalExceptionHandler / OperatorContext / ScaffoldAutoConfiguration) ├── data/ (BaseEntity / MyBatisPlusConfig / RedisConfig / AutoFillHandler) ├── security/ (SaTokenConfig) ├── log/ (Xss / SecurityHeaders / RateLimit / HttpsRedirect 四过滤器 + SensitiveDataConverter + ScaffoldLogAutoConfiguration) └── web/ (WebApplication + DemoController(ping→Result) + application{,-dev,-prod}.yml) ``` - 模块依赖序:`web → {security, log} → data → common`,与 runx 一致。 - 三方版本与 runx v0.60.0 对齐(mybatis-plus / sa-token / hutool 5.8.46 / druid / springdoc / redisson / postgresql / poi / flyway),固化到生成工程父 POM。 - 生成工程父 POM **复刻** `maven-compiler-plugin` 的 `annotationProcessorPaths(lombok)` + `fork=true`,避免 Lombok getter/setter 静默缺失导致编译失败。 ### 自动装配统一内核(Starter 约定) 每个能力模块以 Spring Boot 3 原生 SPI 注册: - `src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports` 注册 `@AutoConfiguration` 类; - 统一开关 `@ConditionalOnProperty(name = "scaffold..enabled", havingValue = "true", matchIfMissing = true)`(默认开启、可显式关闭); - 例外:`scaffold.security.https-redirect.enabled` 默认关闭(`matchIfMissing=false`)。 runx 自身以 `scaffold-log` 的安全过滤器族做了示范改造(`ScaffoldLogAutoConfiguration`),验证全链路。 ### 能力开关清单(生成工程 application-dev.yml) | 配置项 | 默认值 | 说明 | |--------|--------|------| | `scaffold.security.xss.enabled` | true | XSS 清洗 | | `scaffold.security.headers.enabled` | true | 安全响应头 | | `scaffold.rate-limit.enabled` | true | 细粒度限流 | | `scaffold.security.https-redirect.enabled` | false | HTTPS 强制跳转 | | `scaffold.log.sensitive.enabled` | true | 日志脱敏 | > 本地 Maven 损坏时,可进入生成工程目录在联网机器 / CI 执行 `mvn clean package` 复核编译。 ## 真实业务模块范例(demo) 仓库内置多个**由代码生成器产出、并已端到端实跑验证**的业务范例,证明「表 → 后端 → 前端」全链路可落地。`GEN_LIVE=web` 默认模块名为 `demo`,运行生成器(单表 / 多表 / `all` 全库)即落入 `scaffold-web/.../modules/demo/` 与 `examples/frontend/demo/`: - **product**(手工落库验证):`V1.0__init_schema.sql(Flyway 迁移)` 覆盖 必填+长度 / 数字 / 字典 `el-select` / 多行文本 / 开关 / URL 校验 各类场景,并带列注释供前端生成标签;后端在 `scaffold-web/.../modules/demo/`(Controller 路径 `/api/demo/product`) - **category**(生成器 `GEN_LIVE=web` 直落验证):`V1.0__init_schema.sql(Flyway 迁移)` 中的 `category` 表 + `category_status` 字典种子,由代码生成器直接落入 `scaffold-web/.../modules/demo/` 与 `examples/frontend/demo/`,演示「一条命令拉起完整业务模块」 - **sales_order**(生成器 `GEN_LIVE=web` 直落验证):`V1.0__init_schema.sql(Flyway 迁移)` 中的 `sales_order` 表,含 `product_id` / `category_id` 外键(关联 `product` / `category`)与 `order_status` 字典,演示**外键关联下拉**能力——生成的 `SalesOrder.vue` 中商品 / 分类列为关联 `el-select`,订单状态为字典下拉 - **content**(生成器 `GEN_LIVE=web` 直落验证):`V1.0__init_schema.sql(Flyway 迁移)` 中的 `article_category` + `article` 表,落入 `scaffold-web/.../modules/demo/` 与 `examples/frontend/demo/`,**一张 `article` 表组合演示生成器全部前端能力**——关联外键下拉(`article_category_id` → 关联 `el-select`,选项来自 `/api/demo/article-category`)、字典 `el-select`(`status` → `article_status` 字典)、多行文本(`content`)、数字(`view_count` / `sort`)、开关(`is_top`)、URL 校验(`cover_url`),且下拉统一用 Vue `v-for` 绑定运行时 `Options`(关联选项 / 字典选项由 `onMounted` 拉取,杜绝空 `el-select`) - **tag**(生成器 `GEN_LIVE=web` 直落验证):`V1.0__init_schema.sql(Flyway 迁移)` 中的 `tag` 字典型表,落入 `scaffold-web/.../modules/demo/` 与 `examples/frontend/demo/`,演示 v0.8 字典能力自举——后端额外 `/types` 端点 + 参数化 `/page` 过滤 - **前端范例**:`examples/frontend/demo/` 下 `Product.vue` / `Category.vue` / `SalesOrder.vue` / `Article.vue` / `ArticleCategory.vue` / `Tag.vue`(及各自 `Api.js`),共享字典工具 `examples/frontend/dictApi.js`(复制到你的 Vue 工程 `src/views` / `src/api` 并接入 `utils/request.js` 即可使用) 各范例均随 `scaffold-web` 一同编译(每次 `mvn package` 即验证其可编译),并已验证:Sa-Token 登录拿 token 后,分页 / 新增 / 详情 / 修改 / 逻辑删除 全部正常,全局逻辑删除(`deleted` 字段)生效。 > 想生成自己的模块:参照 `product` / `category` 表设计业务表 → 运行生成器(单表 / 多表 / `all` 全库)→ 默认输出到 `generated/`,或设 `GEN_LIVE=web` 直接落入业务模块源码树(见上方「环境变量」)。 ## 字典(真实字典对接) 生成器对 `select`(字典)型字段,默认生成**占位选项兜底 + 运行时从后端字典接口拉取**的前端代码,不再写死: - **后端**:内置 `system/dict` 字典模块(`scaffold-web/.../modules/system/dict`),提供 `GET /api/system/dict/options/{type}`,返回 `Result>`;字典数据存于 `sys_dict` 表(`V1.0__init_schema.sql(Flyway 迁移)`,已含 `product_status` / `product_category_id` 种子)。 - **约定**:字典类型编码 = `表名_列名`(如 `product_status`),与生成器前端 `dictApi.getOptions('<表名_列名>')` 调用一一对应。 - **前端**:生成的页面在 `onMounted` 中按类型拉取选项覆盖占位;`dictLabel(options, val)` 用 `String()` 统一对齐数字 / 字符串值,避免 `int` / `varchar` 字典错位。共享工具 `examples/frontend/dictApi.js` 需在真实项目中放置于 `src/api/dict.js`。 - **校验顺序**:若后端未配置该类型字典,前端保留占位选项,页面仍可正常使用。 ### 字典管理后台(可运维) 字典数据默认仅靠 `V1.0__init_schema.sql(Flyway 迁移)` 种子维护,但脚手架同时内置了字典的**增删改查后台**,让字典真正成为可配置项: - **后端接口**(`scaffold-web/.../modules/system/dict/DictController.java`,均需登录): - `GET /api/system/dict/page?current=&size=&dictType=&dictLabel=&status` 分页查询(管理后台用,已过滤逻辑删除) - `GET /api/system/dict/types` 列出全部字典类型编码(去重,供类型筛选) - `POST /api/system/dict` 新增;`PUT /api/system/dict/{id}` 修改;`DELETE /api/system/dict/{id}` 删除(**逻辑删除**,避免业务引用悬空) - **前端范例**:`examples/frontend/system/Dict.vue` + `dictApi.js` 中的 `pageDict / listDictTypes / createDict / updateDict / removeDict`,复制到你的 Vue 工程 `src/views/system` / `src/api` 即可作为字典管理页使用。 - **闭环验证**:在管理后台新增一条 `product_status=预售`,商品页(`Product.vue`)的「状态」下拉会立即出现该选项;禁用 / 删除后,下拉同步不再展示。 ## 单元测试 代码生成器配套 JUnit 5 单测,位于 `scaffold-generator/src/test/java/com/scaffold/generator/`: - `CodeGeneratorTest`:命名转换(`toCamelCase` / `toPascalCase`)、控件类型推断(`resolveFormType`)、校验规则构造(`buildRules`,覆盖必填 / 长度 / 邮箱 / 手机号 / URL)、字典选项(`buildDictOptions`),以及**不依赖数据库**的前端模板渲染产物结构断言(用临时目录渲染真实 `.vm` 模板,校验 `formRef.validate()`、字典下拉、控件映射等) - `GeneratorTemplateTest`:模板契约守护,锁死「控制器仅用 `@SaCheckLogin` 不臆测 `@SaCheckPermission`」「Vue 模板含提交校验与字典格式化」等关键不变量,防止模板被误改回归 > 测试刻意与数据库解耦(`renderFrontendForTable` 仅依赖已解析的字段列表),因此 **CI 无 PostgreSQL 环境亦可运行**。 本地运行: ```bash mvn -pl scaffold-generator -am test ``` ## 容器化一键部署(Docker) 提供 `Dockerfile` + `docker-compose.yml`,一键拉起 PostgreSQL、Redis 与应用,自动建表并通过环境变量注入凭据。 ### 前置条件 - Docker 24+ 与 Docker Compose v2 ### 启动 ```bash docker compose up -d --build ``` - 应用根路径:`http://localhost:8080/api` - 默认管理员:`admin / admin123`(首次启动自动初始化) - 健康探针:`http://localhost:8080/api/actuator/health` ### 默认凭据(仅示例,生产务必修改) | 组件 | 用户名 | 密码 | |------|--------|------| | PostgreSQL | scaffold | scaffold123 | | Redis | — | redis123 | ### 自定义 - 修改 `docker-compose.yml` 中的 `DB_PASSWORD` / `REDIS_PASSWORD` 与环境变量、端口映射 - 表结构由 Flyway 在应用首次启动时自动迁移(`scaffold-data/src/main/resources/db/migration/V1.0__init_schema.sql`;`docker-compose.yml` 已不再挂载 initdb 脚本) - 数据持久化到命名卷 `pg-data` / `redis-data`,删除卷即重置 ### 单独构建镜像 ```bash docker build -t java-scaffold:latest . docker run -p 8080:8080 \ -e DB_URL=jdbc:postgresql://host.docker.internal:5432/scaffold \ -e DB_USERNAME=scaffold -e DB_PASSWORD=scaffold123 \ -e REDIS_HOST=host.docker.internal -e REDIS_PASSWORD=redis123 \ java-scaffold:latest ``` ## 生产建议 - 数据库连接与 Redis 密码已通过 `${ENV:default}` 占位符外移:dev 保留默认值便于本地零配置启动,生产通过环境变量(如 `DB_PASSWORD`、`REDIS_PASSWORD`、`DRUID_PWD`)注入真实凭据,勿提交明文 - 生产使用 `--spring.profiles.active=prod`,已默认关闭 Swagger 与 Druid 监控页 - 已内置 `/actuator/health` 健康探针(默认仅暴露 health 端点),供 docker healthcheck 与 K8s liveness/readiness 探针使用 - `CorsConfig` 的 `allowedOriginPatterns` 收紧为具体域名 - 限流/锁的 Redis Key 前缀可按业务调整 - 建议接入日志采集(ELK/Loki)与监控(Prometheus + Grafana) ## 目录与编码约定 - 包根:`com.scaffold` - 实体继承 `BaseEntity`(统一 id / 时间 / 逻辑删除) - 业务异常统一抛 `BusinessException`,由 `GlobalExceptionHandler` 转换 - Controller 入参用 `@Valid` + Jakarta 校验注解 ## CI 持续集成 提供 `.github/workflows/ci.yml`:在 push / PR 到 `main` / `master` / `develop` 时,使用 **JDK 17(Temurin)** + Maven 依赖缓存执行 `mvn -B clean verify`(编译打包 + 运行单元测试),并上传 `scaffold-web/target/java-scaffold.jar` 构建产物(保留 7 天)。生成器单测不依赖数据库,可在 CI 环境直接运行。 ## 版本历程 | 版本 | 关键能力 | |------|----------| | v0.1.0 | 多模块骨架(common / data / security / log / generator / web)、统一响应、全局异常、参数校验 | | v0.2.0 | 容器化(Dockerfile + docker-compose,一键 PG/Redis/应用) | | v0.3.0 | 代码生成器(前后端 CRUD 模板) | | v0.4.0 | 前端模板增强:字典下拉、必填校验、智能控件推断 | | v0.4.1 | 前端模板再增强:字段级校验(长度 / 邮箱 / 手机号 / URL)、真实 `validate()` 提交 | | v0.5.0 | 真实业务模块范例:`product` 端到端验证(修复生成器臆测 `@SaCheckPermission`) | | v0.6.0 | 生成器单元测试(JUnit 5,与 DB 解耦,CI 实跑) | | v0.7.0 | 前端字典下拉对接真实后端字典接口(`/api/system/dict/options/{type}`) | | v0.8.0 | 字典管理后台 CRUD(可运维,逻辑删除,类型去重) | | v0.9.0 | 生成器支持多表 / `all` 全库批量生成 + `GEN_LIVE=web` 直落真实模块;控制器改为自研 Velocity 渲染并依赖 MP 全局逻辑删除(`removeById`)|范例新增 `category` | | v0.10.0 | 生成器识别**外键列**渲染关联下拉(`product_id` → 商品下拉,跨表关联);新增 `sales_order` 范例端到端验证;**修复**生成前端模板 `PageResult` 分页字段误用(`records` → `list`,否则列表恒空)|单测 21→22 | | v0.11.0 | 存量业务表重生关联下拉:为 `product`.`category_id` 补**真实外键** → `category`(类型对齐 BIGINT,约束生效);重新生成 shop 模块使 Product 前端从字典下拉升级为 **relation 关联下拉**(调用 `category` 分页接口拉取选项,跨表关联);一并修复 shop 全模块前端 `PageResult` 分页字段误用(`records` → `list`);清理已废弃的 `product_category_id` 孤儿字典|单测 22 全绿、端到端验证关联外键写入与列表返回 | | v0.12.0 | 生成器识别**字典型表**(同时含 `dict_type`/`dict_label`/`dict_value`)自举 v0.8 字典能力:后端额外生成 `/types` 端点(去重类型编码,逻辑删除自动过滤)+ 参数化 `/page` 过滤;前端字典维护页模板顶部类型下拉 + 固定字段表格 + `listTypes` API;新增 `tag` 字典型范例表端到端验证(`/page`、`/types`、增删、status 过滤全绿)。附注:`tag` 表与 `sys_dict` 对齐用 `BIGINT` + 全局 `ASSIGN_ID` 雪花 id(非 `BIGSERIAL`),避免显式种子 id 导致序列不同步引发插入主键冲突;构建侧锁定 `maven-compiler-plugin` 显式 `annotationProcessorPaths` + `fork=true`(3.13+ 已移除隐式处理器路径)|单测 22→24 | | v0.13.0 | 新增 `content` 业务模块范例(`article_category` + `article`,生成器 `GEN_LIVE=web` 直落验证):一张 `article` 表**组合演示生成器全部前端能力**——关联外键下拉(`article_category_id` → 关联 `el-select`,选项来自 `/api/content/article-category`)、字典 `el-select`(`status` → `article_status` 字典)、多行文本(`content`)、数字(`view_count`/`sort`)、开关(`is_top`)、URL 校验(`cover_url`)。修复内容:生成器前端模板 `el-select` 选项渲染误用 Velocity `#foreach` 遍历构建期未定义的 `Options` 变量(导致下拉恒空),改为 Vue `v-for` 绑定运行时 `Options`(关联 / 字典选项由 `onMounted` 拉取)|单测 24→25(新增 select 渲染契约测试)|端到端验证 article 全链路(关联 / 字典 / 文本 / 数字 / 开关 / URL 全部正确持久化) | | v0.14.0 | 生成器后端 `/page` **真正支持动态查询过滤**,让前端搜索栏不再是摆设:标准控制器模板按 `queryable` 字段(input/number/select/relation,不含 textarea/datetime/switch)自动接收 `@RequestParam` 查询参数并拼接 `QueryWrapper`——文本字段 `LIKE`、数值 / 外键字段 `EQ` 且做 `Long` 转换(避免 PG 类型不匹配)。前端搜索栏早已发出 `query` 对象,此前后端忽略。新增 `FieldInfo` 元数据 `getRawCol()/isString()/isNumeric()` 供模板取用。修复两个生成期陷阱:① 方法参数列表尾逗号非法(用首字段标记法去掉尾逗号);② `$f.name.isBlank()` 会被 Velocity 误求值为字符串「title」的 `isBlank()`(→`!false`),须改用 `${f.name}.isBlank()` 渲染为字面变量调用。|单测 25→27(新增控制器 /page 过滤契约测试)|端到端验证 LIKE/状态EQ/分类外键EQ/组合过滤均精准命中 | | v0.15.0 | 生成器后端 `/page` 查询条件**全类型覆盖**(打通搜索栏「最后一公里」):放宽 `queryable` 判定,使 `datetime` / `switch` 也纳入查询参数——`input`→`LIKE`、`select`/`relation`→`EQ`(`Long` 转换)、`switch`→`EQ Boolean`、`number`→`min/max` 区间(`ge`/`le`)、`datetime`→`start/end` 区间(`LocalDateTime.parse` + 结束取当日最末时刻保证整日命中)。前端搜索栏相应渲染区间双输入框 / 日期双选择器 / 是·否下拉。新增 `article.publish_time` 自定义 datetime 列演示区间过滤。修复两处生成期陷阱(同 v0.14 系列):① 签名段 `formType` 分支嵌套多一层 `#end` 漏闭合 → 模板 EOF 未闭合;② `$Cap.isBlank()` 同 `$f.name.isBlank()` 误求值为字符串(→`minfalse`),改用 `${Cap}.isBlank()`。|单测 27→28(新增 number/datetime 区间 + switch 布尔契约 + 前端区间/开关控件渲染测试)|端到端验证 标题LIKE / 状态EQ / 分类FK EQ / 浏览量区间 / 发布时间区间(起/止/组合) / 置顶开关 / 组合过滤 全部精准命中。**模块收敛**:`GEN_LIVE=web` 默认模块名 `demo`;删除历史遗留的 `shop`/`content` 重复模块与 `system` 内 `tag` 副本(均与 `demo` 冲突 `ConflictingBeanDefinition`),业务范例统一收敛到 `demo` + `system(dict)`,仓库结构清爽一致 | | v0.16.0 | 生成器 **GEN_LIVE=web 模式下生成后自动编译校验**(R4「生成即生产可用」闭环最后一环):直落真实模块后自动 `mvn -o -pl scaffold-web -am compile`,编译失败则打印本次生成/覆盖的文件清单并 `System.exit(1)`(回滚交由版本管理,避免误删用户文件);`render()` 统一收集写入路径到 `generatedFiles` 清单,`runCommand()` 封装外部命令执行。单测 28→29(新增命令执行判定:成功返回 0、不存在命令返回非 0)。端到端验证:全库直落 `all` 后自动编译校验通过,article /page、tag /types 无回归 | | v0.17.0 | 生成器产出补齐 **R5 详情页 + Excel 导入导出**(Hutool 驱动,零业务代码手写):① 前端列表新增「详情」抽屉(`el-drawer` + `el-descriptions` 遍历字段展示)、导入(`el-upload`)/导出按钮;② 后端控制器新增 `POST /import`(Hutool `ExcelReader` 按中文表头别名映射、清空主键交 MP `ASSIGN_ID` 重生成、`saveBatch`)与 `GET /export`(`ExcelWriter` 仅写业务字段别名、`setOnlyAlias` 自动排除审计列);③ 导入导出**排除 datetime 字段**(避免 Hutool 解析 `LocalDateTime` 异常,日期列本就适合界面填写);④ web 模块补 `poi-ooxml:5.2.5` 依赖(Hutool Excel 运行时必需,编译期不报错)。单测 29→31(新增控制器导入/导出端点、前端抽屉+按钮断言)。端到端验证:导出合法 OOXML(8 列含表头)、导入 `code=200` 且 5 行数据新增成功、详情接口正常 | | v0.18.0 | 生成器产出补齐 **R3 权限落地一体化**(生成即带菜单 + 权限码,零运维手写):① 标准 / 字典控制器模板为 7/6 个端点分别加 `@SaCheckPermission("{table}:list/detail/add/edit/delete/import/export")` 注解(原仅 `@SaCheckLogin`,权限靠开发者补,违反「生成即生产可用」);② 新增 `scaffold-perm.java.vm` 每表生成一个 `ApplicationRunner` 幂等种子——写入 `sys_permission`(status=1)+ `sys_menu`(菜单名/路径/组件)+ 绑定 `SUPER_ADMIN`(自注册规避首次启动顺序导致 admin 拿不到新权限);③ `CodeGenerator` 在 `GEN_LIVE=web` 循环内 `renderControllerForTable` 后调用 `renderPermForTable`,并把 `table` 注入 Velocity 上下文(修复 `${table}` 未注入渲染成 `:list`)。SUPER_ADMIN 因 `PermissionServiceImpl` 返回全量 `status=1` 权限码而自动放行。单测 31→33(新增控制器权限注解契约 + 种子写入断言;翻转旧契约「不应臆测权限」为「必须生成标准权限约定」)。端到端验证:admin 登录 `permissions` 已含 `article:*` 等生成码、`/demo/article/page` 带 token 返 200、无 token 返 401 未登录拦截、`sys_permission`/`sys_menu`/`sys_role_permission` 全量落地 | | v0.19.0 | **R1 安全收口**(两处真实缺口,非伪改造):① **JSON 请求体 XSS 清洗**——`XssRequestWrapper` 此前对 JSON 体「原样透传」(注释明言取舍),本次改为解析 JSON 后**仅对字符串叶节点**用 `HtmlUtil.escape` 转义,数字 / 布尔 / null 与嵌套对象 / 数组结构原样保留(`Jackson ObjectMapper` 解析,避免整体转义破坏反序列化;解析失败原样透传不丢请求)。前端 Vue `{{ }}` 插值自动转义 → 渲染为字面文本,彻底堵住 JSON 载荷 XSS 入口。② **CORS 凭据误配收紧**——原 `CorsConfig` 用 `allowedOriginPatterns("*")` + `allowCredentials(true)`,是 CSRF / 凭据跨域泄露的典型误配;改为:源外部化为 `scaffold.cors.allowed-origins` 配置项,**通配源强制 `allowCredentials=false`**,仅显式列出具体域名时才允许凭证(`allow-credentials` 可配)。scaffold-log 补 `junit-jupiter` 测试依赖,新增 `XssRequestWrapperTest`(5 例:字符串叶转义 / 数字布尔嵌套保留 / 大整数精度 / 非法 JSON 透传 / 事件处理器标签清洗)。端到端验证:POST 含 `