# 自动定义表单 **Repository Path**: strivingboy/automatically-define-forms ## Basic Information - **Project Name**: 自动定义表单 - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-07-26 - **Last Updated**: 2026-07-26 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # datampl-ext > **外部数据管理表单的精简可借鉴实现** —— 用户通过可视化配置定义数据模板(字段名/类型/备注),前端页面化填报,后端按模板动态建表 + 四种 Excel 导入规则。 [![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.2.5-brightgreen)](https://spring.io/projects/spring-boot) [![Vue](https://img.shields.io/badge/Vue-3.4-brightgreen)](https://vuejs.org/) [![MySQL](https://img.shields.io/badge/MySQL-8.0-blue)](https://www.mysql.com/) [![License](https://img.shields.io/badge/license-MIT-blue)](LICENSE) ## ✨ 项目亮点 - **可视化数据模板**:用户在 Web 页定义字段集(`columnName`/`columnType`/`length`/`nullable`/`remark`),无需 DDL - **动态建表**:保存模板时自动 `CREATE TABLE`,列类型白名单(VARCHAR/INT/BIGINT/DECIMAL/DATE/DATETIME/TEXT) - **四种 Excel 导入规则可配**(互斥): - `NAME`(默认)—— 按字段名匹配,列顺序无关 - `POSITION` —— 严格按列位置,跳过表头 - `RANGE` —— 灵活行范围(`startRow` + `skipTailRows`),列名匹配 - `REMARK` —— 按字段备注精确匹配 - **下载模板按模式差异化**:NAME/POSITION/RANGE 输出 `columnName(备注)`;REMARK 输出纯备注 - **全局字段备注必填**:服务端 `validate()` 兜底,前端表单层双重校验 - **响应式 Envelope 统一**:`R { code, msg, data, success }`,前端 axios 拦截器统一解构 - **Schema 自动迁移**:启动时 `SchemaInitializer` 通过 `information_schema.COLUMNS` 检测并 `ALTER ADD COLUMN`(无需 Flyway/Liquibase) - **零外部 SDK 依赖**:所有内部类本地重写(无 opfa-core/digiop-base 残留),可移植性强 ## 🚀 快速启动 ### 前置环境 | 工具 | 版本 | |---|---| | JDK | 17+ | | Maven | 3.5+ | | Node.js | 18+ | | MySQL | 8.0+(或 H2 内存库 fallback) | ### 1. 克隆与配置 ```bash git clone datampl-ext cd datampl-ext ``` ### 2. 启动后端(默认走 MySQL) ```bash cd server mvn spring-boot:run ``` 服务启动于 `http://localhost:8080`,首次启动自动: - `CREATE DATABASE IF NOT EXISTS datampl_ext`(dev-mysql profile) - 执行 `db/schema.sql` 建元数据表 `data_data_filling_template` - 检查并 ALTER 添加新字段(`import_mode` / `start_row` / `skip_tail_rows`) ### 3. 启动前端 ```bash cd web npm install npm run dev ``` 前端运行于 `http://localhost:5173`。 ### 4. 浏览器访问 打开 `http://localhost:5173` 即可使用。默认跳转 `/template` 列表页。 ### 5. 切换到 H2(纯开发快速验证) 后端加 profile 参数: ```bash mvn spring-boot:run -Dspring-boot.run.profiles=dev-h2 ``` ⚠️ H2 内存库进程重启即清空,**仅用于纯开发**。 ## 📐 架构概览 ``` ┌──────────────────────────────────────────────────────────────┐ │ Browser (Vue 3 + Element Plus + Pinia + Vue Router) │ │ ┌────────────┬──────────────┬──────────────┐ │ │ │ Template │ Template │ DataFill │ │ │ │ ListView │ EditView │ View │ │ │ └────┬───────┴──────┬───────┴──────┬───────┘ │ │ │ │ │ │ │ └──────────────┼──────────────┘ │ │ │ axios (baseURL=/api) │ │ ▼ 响应拦截器 deepKeysToCamel │ └──────────────────────┼───────────────────────────────────────┘ │ ┌──────────────┼──────────────┐ │ ▼ │ │ Spring Boot 3.2.5 (8080) │ │ ┌──────────────────────┐ │ │ │ WebConfig (CORS) │ │ │ │ GlobalExceptionHandler│ │ │ └──────────────────────┘ │ │ ┌──────────────────────┐ │ │ │ Controller ────────►│ │ @RestController │ │ TemplateController │ │ POST /api/template/{list,detail,add,...} │ │ DataController │ │ POST /api/data/{query,insert,...} │ │ ExcelController │ │ POST /api/excel/import │ │ │ │ GET /api/excel/{export,template} │ └──────────┬───────────┘ │ │ ▼ │ │ ┌──────────────────────┐ │ │ │ Service (JdbcTemplate)│ │ │ │ TemplateService │ │ 模板 CRUD + 动态 DDL │ │ DataService │ │ 按列名查询行 │ │ ExcelService │ │ 四种导入模式 + POI 解析 │ └──────────┬───────────┘ │ └──────────────┼──────────────┘ ▼ JDBC + HikariCP ┌──────────────────────────────┐ │ MySQL 8 (datampl_ext) │ │ ┌────────────────────────┐ │ │ │ data_data_filling_ │ │ 元数据表(SchemaInitializer 维护) │ │ template │ │ │ ├────────────────────────┤ │ │ │ <动态业务表> │ │ 用户每保存一个模板 → 自动 CREATE │ │ e.g. test, biz_xxx │ │ │ └────────────────────────┘ │ └──────────────────────────────┘ ``` ## 🗂️ 目录结构 ``` datampl-ext/ ├── server/ # Spring Boot 服务(端口 8080) │ ├── pom.xml │ └── src/main/ │ ├── java/com/datampl/ext/ │ │ ├── DatamplExtApplication.java │ │ ├── beans/ # Template / ColumnInfo │ │ ├── common/ # R / PageResp / BizException / SnowflakeId / ... │ │ ├── config/ # WebConfig (CORS) / SchemaInitializer │ │ ├── controller/ # Template / Excel / Data │ │ ├── exception/ # GlobalExceptionHandler │ │ └── service/ # Template / Excel / Data │ └── resources/ │ ├── application.yml # 默认激活 dev-mysql │ ├── application-dev-h2.yml │ ├── application-dev-mysql.yml │ └── db/schema.sql │ ├── web/ # Vite 前端 │ ├── package.json / vite.config.ts / tsconfig.json │ └── src/ │ ├── main.ts / App.vue │ ├── router/index.ts # /template/* 路由 │ ├── layouts/AdminLayout.vue │ ├── api/ │ │ ├── http.ts # axios + 拦截器(snake→camel + R 解构) │ │ ├── template.ts # Template 接口 + list/detail/add/update/delete │ │ └── data.ts # data/* + excel/* + export/import URL │ └── views/ │ ├── TemplateListView.vue # 模板列表 │ ├── TemplateEditView.vue # 模板编辑(含导入模式 radio) │ └── DataFillView.vue # 数据填报页 │ ├── docs/subagent-reference/ # 项目记忆与文档(v1.3 编排体系) │ ├── project-map.md # 一级索引(≤5KB) │ ├── 领域记忆/ # 后端/前端/测试/数据库约定 │ ├── 任务/ # 历史任务卡 │ └── 证据/ # 测试证据 │ ├── data-import-extracted/ # 抽取的原始代码只读参考(不入库依赖) ├── temp_test_files/ # tester 临时测试文件(不入库) ├── .gitignore └── README.md ``` ## 🔌 API 文档 ### 统一响应 Envelope ```json { "code": 0, // 0 = 成功;非 0 = 业务异常 "msg": "success", // 提示文本 "data": { ... }, // 业务负载 "success": true // 同 code === 0 } ``` ### 模板管理(POST /api/template/*) | 路径 | 入参 | 出参 | |---|---|---| | `/list` | `{ pageNum, pageSize, keyword? }` | `{ list: TemplateRow[], total: number }` | | `/detail` | `{ tmplId }` | `{ template: TemplateRow, columns: ColumnInfo[] }` | | `/add` | `Template`(含 columns)| `{ success: boolean }` | | `/update` | `Template`(含 tmplId + columns)| `{ success: boolean }` | | `/delete` | `{ tmplId }` | `{ success: boolean }` | ### 数据填报(POST /api/data/*) | 路径 | 入参 | 出参 | |---|---|---| | `/query` | `{ tmplId, pageNum, pageSize }` | `{ list: Row[], total: number }` | | `/insert` | `{ tmplId, row: Record }` | `{ id: number }` | | `/update` | `{ tmplId, id, row }` | `{ success: boolean }` | | `/delete` | `{ tmplId, id }` | `{ success: boolean }` | ### Excel 导入导出 | 路径 | 方法 | 描述 | |---|---|---| | `/api/excel/import?tmplId=N` | POST multipart | 上传 xlsx/xls,按模板的 `importMode` 自动匹配 | | `/api/excel/export?tmplId=N` | GET | 下载当前模板的全部数据(带表头 + 数据)| | `/api/excel/template?tmplId=N` | GET | 下载空白模板(仅表头)按模式差异化 | ## 🎯 四种导入模式 ### 模式对照 | 模式 | 表头识别 | 列顺序 | 数据起始行 | 末尾跳过 | 下载模板表头 | |---|---|---|---|---|---| | **NAME**(默认) | 按 columnName 匹配(剥离 `(...)`) | 无所谓 | 固定第 2 行 | 无 | `columnName(备注)` | | **POSITION** | 忽略表头 | **严格** | 配置 `startRow` | 无 | `columnName(备注)` | | **RANGE** | 按 columnName 匹配 | 无所谓 | 配置 `startRow` | 配置 `skipTailRows` | `columnName(备注)` | | **REMARK** | 按 ColumnInfo.remark 精确匹配 | 无所谓 | 固定第 2 行 | 无 | **纯备注**(如"第一列")| ### 全局约束 - **字段备注必填**(无论哪种模式):前端 `submit()` 校验 + 后端 `validate()` 兜底 - **download 模板按模式差异化**(重要):导入时上传的 Excel 表头格式必须匹配下载格式 - **importMode 白名单**:`{NAME, POSITION, RANGE, REMARK}`,后端 `validate()` 拒绝其他值 ### 示例:REMARK 模式工作流 1. 用户编辑模板,字段定义 `columnName=test_id, remark=主键ID` 2. 切换 importMode = `REMARK` 3. 下载模板 → 表头单元格写 `"主键ID"`(**不**含 `test_id`) 4. 用户在 Excel 第 2 行填值,保存上传 5. 后端 `matchColumnsByRemark` 把表头 `"主键ID"` 与 `ColumnInfo.remark` 精确匹配 6. 数据按列索引正确写入 ## 🗄️ 数据模型 ### 元数据表 `data_data_filling_template` | 列 | 类型 | 说明 | |---|---|---| | `tmpl_id` | BIGINT AUTO_INCREMENT PK | 模板 ID | | `tmpl_name` | VARCHAR(200) UNIQUE | 模板名 | | `table_name` | VARCHAR(100) UNIQUE | 动态业务表名 | | `table_comment` | VARCHAR(500) | 表注释 | | `remark` | TEXT | 模板备注 | | `columns_json` | TEXT NOT NULL | 字段列表 JSON | | `import_mode` | VARCHAR(16) DEFAULT 'NAME' | 导入模式 | | `start_row` | INT DEFAULT 1 | 数据起始行(POSITION/RANGE) | | `skip_tail_rows` | INT DEFAULT 0 | 末尾跳过行数(RANGE) | | `create_time` / `update_time` | DATETIME | 自动维护 | ### 动态业务表 `` 由 `TemplateService.createBusinessTable` 生成: ```sql CREATE TABLE `` ( `id` BIGINT PRIMARY KEY AUTO_INCREMENT, `` [(length[,scale])] [NOT NULL], `` [(length[,scale])] [NOT NULL], ... ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; ``` 字段类型白名单:`VARCHAR`/`INT`/`BIGINT`/`DECIMAL`/`DATE`/`DATETIME`/`TEXT`。 ### SchemaInitializer 启动流程 1. **Phase A**(仅 dev-mysql):直连 `mysql://127.0.0.1:3306` 临时 DataSource → `CREATE DATABASE IF NOT EXISTS datampl_ext` 2. **Phase B**:通过主 DataSource 执行 `db/schema.sql`(`CREATE TABLE IF NOT EXISTS`) 3. **Phase C**(仅 dev-mysql):检查 `information_schema.COLUMNS`,对已存在表 `ALTER ADD COLUMN` 补齐新字段 ## 🛠️ 开发指南 ### 项目级规则 - 所有 subAgent 返回统一 SubAgent Return Envelope(详见 `.openCode/spec/SPEC.md`) - 公共 service / DDL / 接口路由 改动必须 `production_review` - 不修改 `data-import-extracted/` 目录(只读参考) - 不修改 `docs/subagent-reference/log/*.jsonl` - 默认不写单元测试(项目偏好),tester 仅做 MCP+curl 集成验证 ### 加新模板字段类型 1. 后端 `ColumnInfo` 加新枚举值 2. 后端 `ExcelService.columnDdl` 加 `case "NEW_TYPE":` 分支 3. 后端 `ExcelService.convert` 加转换逻辑 4. 后端 `SchemaInitializer` Phase C 加 ALTER(已存在表) 5. 后端 `TemplateService.validate` 加类型校验 6. 前端 `TemplateEditView` `TYPES` 数组加新值 ### 启动顺序(开发模式) ```bash # Terminal 1: 后端 cd server && mvn spring-boot:run # Terminal 2: 前端 cd web && npm run dev ``` Vite HMR 自动热更新前端;后端修改需 `mvn` 重启。 ## 🚢 部署指南 ### 后端打包 ```bash cd server mvn -DskipTests package java -jar target/datampl-ext-server-*.jar ``` 启动时通过 `--spring.profiles.active=dev-mysql`(或环境变量 `SPRING_PROFILES_ACTIVE`)指定 profile。 ### 前端打包 ```bash cd web npm run build ``` 产物在 `web/dist/`,可由任意静态服务器(Nginx/CDN)托管。需配置 `/api` 反代到后端 8080。 ### 反向代理示例(Nginx) ```nginx server { listen 80; server_name datampl.example.com; location / { root /opt/datampl/dist; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } ``` ## 🧪 测试 按项目偏好**不写 JUnit 单元测试**,但提供完整的集成测试证据: - `docs/subagent-reference/证据/` 目录存放 tester 验证报告 - IM01 任务(TASK-20260726-IM01)涵盖 AC1~AC14 的端到端验证 - 使用 MySQL MCP 直连 + curl 模拟真实请求 ## 📚 借鉴要点(给二次开发者) 如果要将本项目用作"数据管理表单"模板,可重点参考: | 模块 | 文件 | 借鉴点 | |---|---|---| | **动态 DDL + 安全** | `TemplateService.createBusinessTable / safeIdent` | 标识符白名单正则 + 反引号包裹 | | **Schema 自动迁移** | `SchemaInitializer.Phase C` | `information_schema.COLUMNS` 检测 + `ALTER ADD COLUMN` 幂等 | | **统一响应 Envelope** | `common/R.java` + `http.ts` 拦截器 | 后端 `R` + 前端 `code===0` 自动解构 | | **业务数据导出** | `ExcelService.exportExcel` | POI + 列宽自适应 + 类型化单元格 | | **下载导入模板** | `ExcelService.exportTemplate` | 按模式差异化表头 | | **复杂导入规则** | `ExcelService.importExcel` + `matchColumnsByName/ByRemark` | 多模式分支 + 表头哈希匹配 | | **Schema-First 表单** | `TemplateEditView.vue` | `el-table` 列内嵌 `el-input` + reactive