
简介Tduck填鸭是一款基于B/S架构的开源表单在线收集系统主要面向泛零售、电商、金融、调研等领域的企业以及需要搭建内部信息反馈平台的技术人员。资源提供完整源码与配置文件覆盖新建表单、表单设置、反馈统计三大核心模块可实现美观且可拖拽的自定义表单设计并通过多种渠道与方式收集有效信息提升客户服务效率。包内共244个文件以221个Java源文件为主辅以XML配置、YAML部署配置、HTML页面及SQL数据库脚本整体仅476KB结构清晰便于阅读与迁移。内容预览中可见验证码缓存、用户项目控制器等关键模块同时包含Dockerfile和Git忽略文件适合从零搭建或二次扩展表单系统的中高级开发人员参考。目前该资源已有251人学习浏览读者可以从中获得项目核心业务逻辑、数据表结构以及容器化部署思路有助于节省前期选型与基础搭建时间快速落地企业信息收集场景。1. 填鸭Tduck到底是什么一套能自托管的开源在线表单收集系统做技术这些年我经常遇到一个很尴尬的需求团队要做问卷、报名表、信息登记数据不想落到第三方平台手里又不想从零开发一套带设计器的表单系统。填鸭Tduck解决的就是这个场景——它是一个开源的在线收集系统自带表单生成器部署到自己的服务器之后就能像问卷星那样拖拽出表单、生成链接、回收数据并导出。标题里的“收集器”指的就是它的在线数据收集能力整个项目前后端分离后端是 Spring Boot MyBatis 的 Java 工程前端是 Vue适合中小团队私有化部署也适合Java开发者拿来做二次开发底座。2. 把收集器跑起来Tduck zip包导入与本地部署全流程拿到“填鸭Tduck-收集器开源表单在线收集系统表单生成器.zip”之后第一件事不是急着看代码而是先把工程跑起来。这类Java开源项目的部署套路大同小异准备环境、初始化数据库、改配置、启动后端、启动前端。下面按最小可行路径来走。2.1 先看清工程结构前后端分离的目录与模块解压zip包后建议先用tree -L 2看一眼目录布局。常见做法是根目录下分两个主模块一个管后端接口一个管前端页面再加上若干说明文件。拿到包之后我一般先找三个东西README或部署文档、SQL脚本、后端配置目录。unzip 填鸭Tduck-收集器开源表单在线收集系统表单生成器.zip -d tduck cd tduck tree -L 2解压参数里的-d tduck是指定解压目录避免文件散落在当前目录里。之所以要看目录结构是因为这类平台型项目一定有数据库初始化脚本和后端启动入口先确认它们的位置后面的步骤才不会抓瞎。注意别用带空格的原始zip文件名直接当目录名后续在配置文件里写路径时会多出很多转义麻烦。大多数Spring Boot前后端分离项目的结构是后端模块里有一个src/main/java的启动类和src/main/resources下的 yml 配置文件前端模块里有package.json和构建配置SQL脚本一般在根目录或独立的sql目录下。确认好这三个位置部署就算完成了三分之一。2.2 环境准备与数据库初始化MySQL、Redis、Node.jsTduck这种在线收集系统依赖两个基础中间件MySQL存表单定义和收集到的数据Redis做缓存和验证码之类的临时状态。前端构建还需要Node.js环境。版本上MySQL要5.7以上Redis任意稳定版都行Node.js建议用14或16太新的版本容易在后续npm install时踩坑。# 创建数据库注意字符集 mysql -uroot -p -e CREATE DATABASE tduck DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; # 导入初始化脚本 mysql -uroot -p tduck tduck.sql第一条命令创建名为tduck的数据库utf8mb4是必须的因为表单收集到的内容里很可能有 emoji 或生僻字用老的utf8字符集存不下。第二条命令把解压包里的SQL脚本导入这一步如果报错多半是MySQL版本和SQL脚本的字符集排序规则不兼容后面的避坑章节会专门说。Redis这边如果是本地测试确认服务在6379端口起来就行。用redis-cli ping能返回PONG就说明后端连接Redis不会有问题。生产环境则要给Redis设置密码并把密码写进后端配置。2.3 改配置启动后端application.yml里的数据库与Redis参数后端配置集中在application.yml里需要动的主要是数据源、Redis、以及文件存储三段。文件存储是容易被忽略的坑表单里的附件上传功能依赖它本地开发建议先配置成本地磁盘模式不然后面测试附件上传会直接失败。spring: datasource: url: jdbc:mysql://localhost:3306/tduck?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 你的数据库密码 redis: host: localhost port: 6379 # password: 你的redis密码本机无密码则注释掉 servlet: multipart: max-file-size: 20MB max-request-size: 50MB数据源的URL里characterEncodingutf8配合数据库的utf8mb4是常规写法serverTimezoneAsia/Shanghai用来避免MySQL驱动和系统时区不一致导致的时间字段报错。username和password按你本地环境填生产环境不建议用root账号跑业务库。Redis的password一项本地没设密码就整行注释掉否则启动时会一直报认证失败。multipart配置限制上传大小表单收集系统里附件动不动就超10MB默认值经常不够我习惯把单文件调到20MB。改完配置启动后端cd tduck-api mvn clean package -DskipTests java -jar target/*.jarmvn clean package是Maven打包命令-DskipTests跳过测试用例减少打包时间。第一次跑会下载大量依赖耗时取决于网络情况。启动成功后控制台会打印Spring Boot的启动横幅和端口号默认端口通常配置在application.yml的server.port里常见是8080如果被占用就改成你自己顺手的端口注意同时改前端代理指向。2.4 启动前端与验证npm run dev后的完整检查后端起来了前端也要跟上。前端工程是一个标准的Vue项目依赖安装和启动命令都是常规操作。cd tduck-front npm install npm run devnpm install如果卡得久问题多半出在依赖版本和Node版本的匹配上具体排查看第5章。启动成功后终端会提示访问地址通常是http://localhost:端口。这时先别急着注册账号手动做一轮冒烟检查页面能打开、登录页能加载、点“创建表单”能进入表单设计器、拖一个组件到画布不报错。这四步过了说明前后端通信是正常的部署链路基本闭环可以进入正式的建表单流程。3. 上手表单生成器从拖拽组件到发布收集链接Tduck的核心价值在于表单生成器。传统做法是前端写死表单结构后端存字段要改表单就得改代码Tduck的做法是把表单定义作为JSON数据保存运行时由设计器渲染。理解这一点后面所有自定义和二次开发都会顺很多。3.1 表单设计器怎么用组件、字段名与数据结构的对应关系进入设计器后左侧是组件面板中间是画布右侧是属性面板。组件类型覆盖了常见收集场景单行文本、多行文本、单选、多选、下拉选择、日期、数字、评分、附件上传等。使用方式就是拖拽然后点组件配置标题、是否必填、默认值、校验规则。每个组件都有一个字段名field这是整个表单系统的关键。用户填写的每一项最终都会以field: 输入值的形式打包成JSON存入数据库。比如设计一个报名表单拖一个“单行文本”并设置字段名为name拖一个“单选”字段名为city那么一条提交记录的数据结构就是{name:张三,city:北京}。理解这个数据结构非常重要。后续如果你要查数据库里的数据会发现表单定义和表单数据都按JSON存储字段可扩展性是这类免开发表单系统最大的优点。缺点是查询条件和统计要依赖数据库的JSON函数或者应用层处理不适合拿来做复杂联表分析。3.2 表单发布与收集填写限制、时间窗口和收集链接表单保存后点击发布或者上线系统会生成一个独立的收集链接和二维码。默认配置下任何人都能打开链接填写并提交。作为系统管理员你需要在这时候设置收集边界否则容易被无效数据刷爆。常见的设置项有四个。每个设备限填一次靠Cookie或设备指纹做限制适合课后作业收集这种场景。收集时间窗设定开始和结束时间到期自动停止适合活动报名。登录后才能填写适合内部系统需要追踪提交人身份的场景。数据总量限制达到指定条数自动关闭表单适合名额有限的抽奖或预约。我一般建议时间窗和数据量限制一起设置。很多团队只设了时间结果活动提前满员后面提交的人白填了一堆还要人工筛数据得不偿失。3.3 数据回收与导出后台查看、Excel导出与统计收集上来的数据在后台“表单数据”里逐条查看支持按提交时间筛选、查看单条详情。最常用的还是导出Excel。Tduck这类Java后端一般都集成了EasyExcel这类导出工具点击导出就会生成xlsx文件。导出时注意两件事。一是列顺序默认导出的列顺序不一定符合你的报表要求后面第6章会讲怎么自定义。二是异步导出如果表单数据量大导出请求容易超时稳妥的做法是后端生成文件后返回下载链接前端轮询或者通知用户。可以看看项目里有没有现成的导出任务表没有的话自己加也不复杂。统计功能上单选、多选类的题目会生成分布饼图或柱状图文本类题目只有列表展示不会有词云。如果需要更深入的交叉分析建议定期导出数据到专门的报表系统不要把在线收集系统当成BI工具用。4. 二次开发与业务集成把Tduck接进自己的系统部署跑通、表单能用这只是开始。实际项目中更多需求是“和我们的内部系统打通”发起的表单要能自动同步到工单系统收集到的数据要能回写业务库或者要在自己公司的站点里嵌入一个报名表单。这些都需要二次开发。4.1 通过后端API操作表单与数据Tduck后端把表单和数据都封装成了REST接口。想对接前先摸清Controller层的接口路径和请求参数常见做法是看tduck-api模块里的Controller类找到创建表单、提交数据、查询数据这三类核心接口。// 模拟向已有表单提交一条数据 HttpHeaders headers new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.set(Authorization, Bearer accessToken); MapString, Object body new HashMap(); body.put(key, 表单的唯一标识); body.put(data, {\name\:\张三\,\city\:\北京\}); HttpEntityMapString, Object request new HttpEntity(body, headers); ResponseEntityString response restTemplate.postForEntity(url, request, String.class);代码里key是创建表单后系统生成的项目标识相当于这张表单的IDdata是JSON格式的字符串key为字段名value为填写值必须和设计器里组件的field匹配。这个接口设计非常轻量好处是调用方不需要引入任何SDK坏处是一次提交的全部字段都压在data这个字符串里没有强校验提交前要做基础的数据清洗不能让脏数据直接进库。4.2 基于开源设计器扩展自定义组件表单设计器的组件库是开源前端代码你可以往里面加自己业务的专属组件。比如做一个“部门选择”组件内部下拉框拉取公司OA的组织架构。常见做法是把新组件做成一个独立的Vue组件文件然后仿照现有组件的注册方式在设计器组件面板里挂载。具体组件渲染和配置项各写一套后端存储层基本不用动因为无论组件长什么样提交的数据最终都是field: value的JSON键值对。这就是选择“表单定义按JSON存储”的架构优势——新增组件不破坏已有数据。扩展组件时有两条注意。新组件的field一定要设计成可配置别写死一个固定字符串。老表单如果已经保存过包含旧字段名的数据组件升级时要做好字段名的兼容映射否则历史数据会变成不可见。这属于典型的“看着容易、做起来翻车”的地方建议改动前先导出一份历史数据备份。4.3 团队协作与多用户权限边界怎么划Tduck自带注册登录体系默认每个用户各自管理自己的表单。如果团队要用常见做法是让成员各自注册账号然后通过“协作成员”或“项目分享”方式共享某一张表单的管理权。这样既保留了操作审计又不至于每个人都要知道数据库密码。再往前走一步如果公司需要组织架构、角色管理这类企业级能力开源版一般是不太够的需要自己在Spring Security的认证链上扩展。常规思路是改造用户表增加部门、角色字段然后在接口层做数据范围的拦截基础版Tduck的Controller代码维护起来不复杂加点切面逻辑就能实现数据权限隔离。改之前先评估一下团队是真的需要严格的数据权限还是只需要“能看、不能编辑”这种简单控制往往后者用一个只读账号加数据库视图就搞定了不用动代码。5. 填鸭Tduck部署避坑五个高频问题的排查记录部署这类开源系统绕不开各种环境问题。这章写几个反复出现的坑每条按“现象到原因再到解决”的顺序说清楚都是我实际排查过的方向。5.1 现象后端启动即崩报Unable to connect to Redis后端启动时控制台打印一堆异常核心错误是Unable to connect to Redis跟着的是Connection refused。原因大概率是Redis服务没起来或者application.yml里Redis的地址端口不对。本地开发最常见的就是Redis装好了但服务没启动。解决分两步。第一步在终端手动探测Redis是否响应redis-cli -h localhost -p 6379 ping如果返回PONG说明Redis活着问题在配置如果报连接失败先把Redis启动起来。第二步确认配置本机Redis没有密码时配置里的password行要么不存在、要么被注释掉很多项目默认模板里带了password:空值反而会让认证逻辑报错直接删掉或注释是最稳妥的。5.2 现象SQL脚本导入报错提示Unknown collation导入建表SQL时MySQL报Unknown collation: utf8mb4_0900_ai_ci。原因是SQL脚本的字符集排序规则是MySQL 8.0才支持的utf8mb4_0900_ai_ci而本地MySQL是5.7识别不了。这类“跨版本导入数据库脚本”的问题基本每个开源项目都会遇到。解决手段是把脚本里的排序规则全局替换为5.7支持的格式sed -i s/utf8mb4_0900_ai_ci/utf8mb4_general_ci/g tduck.sql执行完重新导入。顺便把库也建对字符集ALTER DATABASE tduck CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;以后拿到的任何开源项目SQL脚本先检查排序规则再导入能省掉一堆莫名其妙的报错。血泪经验一个字符集问题能让人怀疑是自己的姿势不对其实只是MySQL版本的锅。5.3 现象前端npm install卡死或编译报错npm install执行到一半卡住或者安装完成后npm run dev报Node Sass does not yet support your current environment之类的错误。这是本地Node版本和项目依赖的node-sass版本不匹配导致的。很多Java开源项目的前端Node版本锁定在老版本Node 18以上装node-sass大概率翻车。解决方式是用nvm切换Node版本别硬刚nvm install 14.21.3 nvm use 14.21.3 cd tduck-front rm -rf node_modules package-lock.json npm install npm run dev这里有个细节切换Node版本后要删掉node_modules和package-lock.json再重装否则锁文件里的依赖元数据还是针对旧版本的装完还是报错。选版本时看项目根目录的.nvmrc或者package.json里的engines字段没有的话直接试14或16命中率最高。5.4 现象表单里的图片和附件上传失败表单设计器里加了附件上传组件发布后测试填写选择图片上传直接报失败或者一直转圈。原因基本都在文件存储配置那一块。开源系统一般支持本地存储和对象存储两种模式如果配置里填了一个云存储的地址但没给密钥或者本地存储路径指向了一个无权写入的目录上传一定会失败。解决方式分两个场景。本地测试优先用本地存储模式在配置里把存储路径指到项目外的目录比如/data/tduck-upload确保目录存在且当前用户有写权限。如果生产环境要用对象存储检查密钥、桶名、Endpoint三项是否齐全缺失任何一项都会上传失败。排查上传问题最快的办法是打开浏览器开发者工具看上传请求的返回信息错误信息里通常写着存储配置相关的具体原因比盯着控制台日志猜效率高得多。5.5 现象登录后接口返回401页面数据加载不出来登录成功但进后台列表页接口全部返回401刷新页面也不行。原因是前端请求API时携带的登录态Token没被正确传递或者是前端开发服务器的代理配置指向了错误的后端端口。前后端分离项目里前端通过代理转发API请求如果代理端口和后端实际端口不一致请求根本没到后端当然无法完成认证。解决方法是检查前端构建配置里的代理设置Vue项目一般在vue.config.jsVite项目在vite.config.js// vite.config.js server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }确认target的端口和后端server.port完全一致。另外检查前端请求拦截器登录后在请求头里统一加的Authorization字段名要和后端JWT过滤器实际读的请求头名称一致这个也属于“看着对但其实对不上”的高频问题两边核对一下就能定位。6. 进阶技巧验证表单链路与自动化归档的三个日常动作部署只是开始真正提升效率的是围绕Tduck建立的三套日常动作。第一个动作是固化链路验证脚本别每次改完环境都靠手点页面。用curl可以直接模拟完整提交链路curl -X POST http://localhost:8080/d/form/api/save \ -H Content-Type: application/json \ -d {key:test-form-key,data:{\name\:\终端测试\,\city\:\上海\}}返回成功就说明端到端链路通了。这个脚本会让日常环境检查和二次开发验证变得非常快建议存成一个shell文件每次改完配置先跑它一遍。第二个动作是自定义导出列顺序。开源版默认导出的Excel列顺序沿用字段创建顺序但报表经常不需要某些内部字段。做法是找到导出工具类在生成表头的地方维护一个字段顺序列表只把想导出的字段map进去。这个改动只影响导出不影响收集数据风险很低但收益很直接。有次我们导数据给人事系统默认导出了三个内部标记字段还得手动删列改完顺序之后加班感直线下降。第三个动作是过期表单的自动归档。收集系统跑久了会产生很多已结束但数据还在的表单。借助Spring的Scheduled定时任务每天扫描一次结束时间超过90天的表单把数据备份成JSON或Excel导出后清除状态能避免数据库持续膨胀。备份文件按表单名加日期分目录存放归档时要记得把导出和删除放在一个事务里避免数据导出了但没删掉或者删掉了但导出失败那就没有后悔药了。这三个玩法共同点是改动量小、不动核心架构却能让团队长期用得更顺手。我的习惯是每接手一个开源系统先建好验证脚本再动第一个小需求确认链路没问题后再逐步加功能。这样既不影响现有使用也能沉淀出适合自己团队的运行规范希望帮到你。本文还有配套的精品资源点击获取