一句话定位:TDuck(填鸭表单)是一个 Java 写的表单/问卷与数据采集后端,8 个 Maven 模块打包成一个 8999 端口的 Spring Boot 服务,前端在独立仓库 tduck-front。它用 MIT 协议开源了社区版后端,同时卖 Pro 与 TDuckX 商业源码版——所以「开源」和「能商用」是两件事,得分开看。| 项 | 值 |
|---|---|
| 仓库地址 | https://gitee.com/TDuckApp/tduck-platform |
| 项目定位 | 企业级表单与数据采集平台(表单设计、数据收集、统计报表) |
| 运营主体 | 填鸭表单(tduckcloud.com) |
| 开源协议 | MIT,Copyright (c) 2019 填鸭表单 |
| 后端技术栈 | Spring Boot 2.7.8 / Java 8 / MyBatis-Plus 3.5.3 / HikariCP / EhCache |
| 数据库 | MySQL 8.0(建库脚本按 8.0.31 导出),表前缀 ac_、fm_、wx_、webhook_ |
| 文件存储 | 阿里云 OSS、七牛、又拍、本地、通用 S3(枚举 0–4) |
| 前端 | 独立仓库 tduck-front(不在本仓库内) |
| 服务端口 | 8999(单服务,无微服务拆分) |
| 代码规模 | 316 个 Java 文件、21 个 Controller、19 张表 |
| 仓库状态 | 8 个模块、8 个分支、10 个标签;最后提交 af70fd6(2026-10-30) |
| 社区热度 | Gitee 10.5K Star / 1.6K Fork / 388 Watch / 6 Open Issues / 2 PR / 500 提交(2026-11-10 查询) |
| 文档日期 | 2026-11-10 |
1. 项目是什么
它是一套「表单系统」的后端,不是商城、不是低代码平台。
你用它做的是:拖拽设计一份表单 → 发布成链接或二维码 → 收集填写数据 → 导出、统计、推送给别的系统。仓库里全是这套流程的服务端代码:表单结构、组件配置、提交数据、统计、权限、文件上传、WebHook、公众号对接。
结构上是模块化单体:8 个 Maven 模块共用一个父 POM,但只有 tduck-api 打出来是可启动的 Spring Boot 应用,其余 7 个是被它依赖的库。没有注册中心、没有网关、没有服务间调用,部署就一个进程加一个 MySQL。
先认清三件事:
- 前端不在这个仓库。README 指向独立仓库
tduck-front,后端只提供接口。想看到界面,两个仓库都要拉。 - 开源的是社区版后端。README 的版本对比表把产品分成社区版、Pro、TDuckX 三档,社区版「后端开源、需自行扩展」,企业版是「完整商业源码交付」。仓库里的代码属于前者。
- 它是商业产品的地基,不是成品交付包。默认账号、默认密钥、示例邮箱都还留在配置和 SQL 里,说明这份代码的定位是「给你自己搭」,不是「装完即用」。
2. 核心特性
下表按 README 的功能清单整理,属于官方宣传口径,仓库本身无法证明每一项都在社区版可用:
| 能力 | 说明 |
|---|---|
| 拖拽设计 | 拖拽式生成表单问卷,声称 27+ 自定义组件 |
| 组件类型 | 单行/多行文本、日期、下拉、单选、文件上传、排序、级联、轮播、一键定位、手机号验证、矩阵量表、子表单 |
| 批量建表 | 通过文本批量导入表单组件 |
| 显隐逻辑 | 按条件控制题目显示与隐藏,对应 fm_user_form_logic |
| 数据管理 | 数据新增、编辑、导出、打印、预览、附件打包下载 |
| 外观配置 | 头图、背景图、背景色、按钮文字 |
| 报表统计 | 按题目实时统计,柱状图/折线图/饼图,可导出 PNG |
| 提交后行为 | 自定义文案、自动跳转网址 |
| 填写控制 | 按微信、账号、IP、设备、答题次数限次;可设允许填写时间段;记录微信个人信息 |
| 消息通知 | 邮件、微信公众号模板消息推送 |
| 系统集成 | 全量数据同步 API;WebHook 订阅新增、修改、删除事件 |
| 模板中心 | 表单存为模板,从模板创建新表单 |
| 用户管理 | 新增、修改、删除用户 |
| 多存储 | 阿里云、七牛、又拍、本地、通用 S3 |
| 第三方通道 | 邮件、短信(阿里云、腾讯云、中昱维信)、公众号参数配置 |
| 回收中心 | 删除的表单可恢复 |
| AI 能力 | AI 建表与公式生成,AiProperties 默认接口地址指向 DeepSeek |
| 可视化大屏 | 与 TReport 大屏项目配合展示数据 |
对技术评估者来说,真正能核对的是这几项:组件与逻辑确实是数据驱动的(表单结构存表、设置存 JSON 列),存储层确实抽象了 5 种后端,WebHook 确实是独立模块。至于「27+ 组件」具体是哪 27 个,得看前端仓库 tduck-front 的组件清单。
3. 技术架构
一个父 POM + 8 个模块 + 1 个启动服务。 版本组合偏保守:
| 层 | 技术 | 版本 / 说明 |
|---|---|---|
| 框架 | Spring Boot | 2.7.8(父 POM 统一管理) |
| 语言 | Java | 1.8 |
| ORM | MyBatis-Plus | 3.5.3,主键 AUTO,下划线转驼峰 |
| 连接池 | HikariCP | 最大 15 连接,SELECT 1 FROM DUAL 探活 |
| 缓存 | EhCache | classpath:ehcache.xml,非 Redis |
| 鉴权 | JWT | 自定义请求头 token,有效期 604800 秒(7 天) |
| 接口文档 | springdoc-openapi | 1.6.9,默认 关闭(enabled: false) |
| Excel | Apache POI | 5.1.0,导入导出与打印 |
| 微信 | WxJava | 4.1.0(mp / miniapp / pay) |
| 验证码 | anji-plus captcha | 1.2.8,默认关闭 |
| 云 SDK | 阿里云 OSS 3.12、七牛 7.11、又拍 4.2.2、AWS S3 1.12.400、腾讯云 3.1.977 | 全是存储与短信 |
| 序列化 | fastjson 2.0.26 + Jackson | 两套并存 |
| HTTP | OkHttp 4.11 | WebHook 推送 |
8 个模块的分工,按代码量看重心很明显:
| 模块 | Java 文件 | 职责 |
|---|---|---|
tduck-form | 123 | 表单核心:表单、组件、逻辑、数据、统计、模板、主题 |
tduck-common | 50 | 基类、常量、工具、通用配置 |
tduck-api | 45 | 唯一启动模块,21 个 Controller 与全部配置文件 |
tduck-wx-mp | 31 | 公众号对接、网页授权、JS-SDK、消息推送 |
tduck-account | 30 | 账号、登录、令牌、第三方授权 |
tduck-webhook | 16 | 推送配置、事件、发送与监听 |
tduck-storage | 13 | 5 种存储实现的统一抽象 |
tduck-ai | 8 | AI 对话客户端与建表接口 |
三个值得注意的设计取舍:
- 配置只有一个入口。全仓库只有
tduck-api/src/main/resources/application.yml和application-prod.yml两个主配置,spring.profiles.active默认就是prod。改配置只有一处,但也意味着换环境只能靠改文件,没有配置中心。 - 表单设置用 JSON 列存。
fm_user_form_setting.settings是 MySQLjson类型,整个表单的提交设置、限次规则、通知开关都塞在一个字段里。改起来灵活,代价是没法用 SQL 直接筛选某个设置项。 - 登录强制改密是自己加的。
AccountTableUpgradeRunner在启动时检查ac_user.need_change_password列是否存在,不存在就ALTER TABLE补上,并把admin@tduckcloud.com标记为待改密。用运行时代码做表结构升级,省了迁移工具,但要求数据库账号有 DDL 权限。
4. 目录结构
| 路径 | 用途 |
|---|---|
pom.xml | 父 POM:8 个模块、Boot 2.7.8、Java 8、依赖版本集中管理 |
tduck-api/ | 启动模块,端口 8999;Dockerfile 也在这里 |
tduck-form/ | 表单业务核心,代码量最大的模块 |
tduck-account/ | 账号与登录,含表结构自检 Runner |
tduck-common/ | 公共基类、异常、工具、常量 |
tduck-storage/ | 存储抽象与 5 种实现(cloud/ 目录) |
tduck-wx-mp/ | 公众号与小程序对接 |
tduck-webhook/ | 推送配置、事件、发送器、监听器 |
tduck-ai/ | AI 客户端、配置、建表请求 |
tduck-api/src/main/resources/application.yml | 主配置:端口、JWT、上传上限、swagger 开关 |
tduck-api/src/main/resources/application-prod.yml | 数据源、邮件、公众号配置 |
doc/tduck-v3.sql ~ doc/tduck-v6.sql | 四份建库脚本,v6 最新,19 张表 |
docker/docker-compose.yaml | MySQL 8.0.36 + 平台镜像,端口 8999 |
docker/init-db/tduck-v5.sql | Docker 首次启动自动导入的脚本(是 v5,不是 v6) |
readmeImages/ | README 截图 |
LICENSE | MIT 全文 |
包内分层统一:controller、service、entity、mapper、vo、param、util、config、exception。Controller 全部集中在 tduck-api 的 web/controller 下,业务模块不暴露 HTTP 入口。
5. 快速上手
路线一:Docker(最快,也最容易踩版本坑)
cd /tmp
git clone --depth 1 https://gitee.com/TDuckApp/tduck-platform.git
cd tduck-platform/docker
docker compose up -dcompose 里定义了两个服务:
| 项 | 值 |
|---|---|
| MySQL | mysql:8.0.36,库名 tduck,root 密码 tduck@mysql,映射 3306:3306 |
| 平台 | 镜像 tduckcloud/tduck-platform(未固定 tag),映射 8999:8999 |
| 初始化 | ./init-db 挂到 /docker-entrypoint-initdb.d,即导入 tduck-v5.sql |
| 上传目录 | 命名卷挂到容器内 BOOT-INF/lib/upload |
| 数据源 | 通过环境变量覆盖 spring.datasource.url 等 |
起来后访问 http://服务器IP:8999,用 README 给的默认账号 admin@tduckcloud.com / 123456 登录,登录后第一件事是改密码。
路线二:源码构建
前置:JDK 8、Maven、MySQL 8.0。先建库导入,再打包启动。
mysql -uroot -p -e 'CREATE DATABASE tduck DEFAULT CHARSET utf8mb4'
mysql -uroot -p tduck -e 'source /tmp/tduck-platform/doc/tduck-v6.sql'
cd /tmp/tduck-platform
mvn clean package -DskipTests
java -jar tduck-api/target/tduck-api.jar然后改 application-prod.yml 里的数据源账号密码。该文件默认是 localhost:3306/tduck、root/root,只适合本地。上传上限在 application.yml 的 spring.servlet.multipart,默认单文件 1000MB。
路线三:前端
git clone --depth 1 https://gitee.com/TDuckApp/tduck-front.git
cd tduck-front
npm install启动脚本以该仓库 package.json 为准;还要把接口地址改成后端 8999 端口,否则页面能打开但所有请求 404。
验证
curl -I http://127.0.0.1:8999/
docker compose logs -f tduck-platform接口文档默认关着(springdoc.swagger-ui.enabled: false),想调试得自己改成 true。官方文档站还提供了宝塔一键安装脚本,README 把它列为首选方式。
6. 功能模块清单
19 张表基本对应 19 块能力。下表用途按表名与关键字段整理:
| 表 | 用途 |
|---|---|
ac_user | 账号主体,含邮箱、密码哈希、登录渠道与改密标记 |
ac_user_authorize | 第三方登录授权绑定 |
ac_user_token | 登录令牌 |
fm_user_form | 表单主表:名称、类型、状态、所属用户 |
fm_user_form_item | 表单组件(题目)定义,一行一个组件 |
fm_user_form_logic | 组件显隐/跳转逻辑 |
fm_user_form_setting | 表单设置,settings 为 JSON 列 |
fm_user_form_data | 提交数据 |
fm_user_form_auth | 表单协作与访问授权 |
fm_user_form_theme | 表单与主题的绑定 |
fm_form_theme / fm_form_theme_category | 主题库与分类 |
fm_form_template / fm_form_template_category | 模板中心与分类 |
fm_user_form_view_count | 表单浏览计数 |
wx_mp_user | 公众号用户与 openid 映射 |
webhook_config | 推送目标配置(地址、事件开关) |
webhook_event | 推送事件记录与重试状态 |
sys_env_config | 系统级配置(存储、短信、邮件等) |
对应到界面上的模块:表单设计、逻辑设置、表单发布、数据管理、数据统计、模板中心、回收中心、用户管理、AI 配置、系统设置。这些在前端仓库里,本仓库只提供被调用的接口。
7. 版本与授权
| 项 | 实际情况 |
|---|---|
| 许可证 | MIT(LICENSE 全文,版权人「填鸭表单」,年份 2019) |
| 代码版本号 | 父 POM 0.0.1-SNAPSHOT,从未改成正式版本 |
| README 徽章 | Version-6.0 |
| Gitee 发行版 | 最新显示「TduckX 3.6 更新」,与 6.0 不是同一套编号 |
| 建库脚本 | 仓库内 v3、v4、v5、v6 四份,最新是 v6 |
| 分支/标签 | 8 个分支、10 个标签 |
| 最后提交 | 2026-10-30,修的是「受密码保护的表单提交时未校验密码」的绕过漏洞 |
版本口径是混乱的:POM 停在 0.0.1-SNAPSHOT,README 说 6.0,发行版说 TduckX 3.6,SQL 又有 v3 到 v6 四条线。实际含义大概是:数据库结构迭代到 v6,社区版对外叫 6.0,商业线叫 TDuckX。升级时不能只看版本号,得对着 SQL 和提交记录判断。
MIT 意味着什么:可以商用、可以闭源修改、可以售卖,义务只有一条——保留版权声明与许可声明。它不像 AGPL 那样对 SaaS 有传染性。但要注意两点:
- MIT 只覆盖这个仓库。前端
tduck-front、TReport 大屏是独立仓库,授权要各自确认。 - 商业版是另一份交付物(完整源码 + 组织权限 + SSO + 信创适配),不受这份 MIT 约束,也不因为「社区版开源」而变成免费。
8. 商业模式说明
典型的「开源社区版引流 + 商业源码版收费 + 项目交付」三层结构:
| 环节 | 做法 |
|---|---|
| 社区版 | 后端 MIT 开源,私有部署,功能基础,需自行扩展 |
| 商业版 | Pro / TDuckX 源码交付,卖点在组织权限、SSO、信创适配、项目化交付 |
| 交付服务 | 企业版升级与技术支持,按项目定制 |
| 获客入口 | Gitee 10.5K Star、官网体验站、文档站、B 站视频、宝塔一键安装 |
| 生态绑定 | 数据可通过 WebHook 同步到自家 TReport 大屏 |
README 里那张「传统 SaaS / 社区版 / 企业源码版」对比表,是厂商视角的宣传材料:它把社区版定位成「技术团队自主搭建、软件成本低但开发运维成本自担」,把企业版定位成「源码授权 + 技术服务」。这张表能帮你理解产品分层,但不能当功能核实依据。
对评估者有用的判断是:社区版的功能边界由前端和文档决定,不由这份后端代码决定。后端接口可能都留着,界面上却不一定开放;反过来也一样。真要确认某个功能在社区版能不能用,只能装上跑一遍。
9. 生态与周边
| 项目 / 资源 | 作用 | 地址 |
|---|---|---|
| 后端(本文主角) | 表单系统服务端 | gitee.com/TDuckApp/tduck-platform |
| 前端 | Vue 管理端与填写页 | gitee.com/TDuckApp/tduck-front |
| TReport 大屏 | 数据可视化展示,靠 WebHook 同步 | gitee.com/TDuckApp/tduck-report-platform |
| 官网 | 产品介绍、在线体验 | tduckcloud.com |
| 文档站 | 部署、功能说明、常见问题 | doc.tduckcloud.com |
| 商业版 | Pro / TDuckX 源码 | x.tduckcloud.com |
| GitHub 镜像 | 同源代码 | github.com/tduckcloud/tduck-platform |
| 容器镜像 | tduckcloud/tduck-platform | Docker Hub |
| 小程序插件 | 文档中提供 uniapp 接入说明 | doc.tduckcloud.com |
| 视频 | B 站频道,含数据同步教程 | space.bilibili.com/409825300 |
生态上比较实用的一点:它是少数在宝塔面板里有一键部署文档的国产表单系统,对不熟悉 Java 运维的人门槛低。比较缺的一点:没有 Helm Chart、没有 K8s 部署说明、没有公开的 API 手册(Swagger 默认关闭,要自己打开)。
10. 选型建议
适合你,如果:
- 你需要一个能私有部署的表单/问卷系统,数据必须落在自己服务器上,不接受问卷星这类 SaaS;
- 团队是 Java 栈,能看懂 Spring Boot 代码并愿意二次开发;
- 你需要表单数据和内部系统打通(WebHook + 同步 API 是现成能力);
- 你能接受 Java 8 + Boot 2.7.8 这套偏旧的运行时,或者有能力自己升级。
不适合你,如果:
- 你要的是「装完就用、不用改代码」的产品——默认配置里全是示例值,必须逐项替换;
- 你要求长期跟随安全更新——Boot 2.7 分支已停止开源维护,后续安全补丁得自己打;
- 你的核心诉求是复杂审批流、BPM、工单——这是表单采集工具,不是流程引擎;
- 你只想要前端页面,不想维护 Java 服务——那不如直接用 SaaS。
和同类怎么比:开源表单领域常见的对照是 SurveyKing、DWSurvey,以及各家的 SaaS 版本。TDuck 的差异点在于组件数量与外观配置做得比较细(主题、头图、背景、矩阵量表、子表单),以及商业版路径清晰(能买到源码交付)。相对劣势是运行时版本旧、部署配置里的默认值多、前端与后端分仓导致版本对齐要靠文档。
11. 风险与坑
- 默认口令是公开的:README 直接写
admin@tduckcloud.com/123456,SQL 里也有这条种子账号。好在AccountTableUpgradeRunner会给它打上待改密标记,但口令本身在文档里公开,暴露到公网前必须改。 - JWT 密钥是写死的示例值:
application.yml里platform.jwt.secret是一串固定 32 位十六进制,有效期 7 天。不换等于任何人都能伪造登录态。 - 配置里留着示例邮箱与授权码:
application-prod.yml的spring.mail是test@163.com加一串授权码,公众号还有默认token与aesKey。这些都是示例值,但如果直接拿去用会踩坑;如果这串授权码曾属于真实账号,建议当作已泄露处理并轮换。 - Docker 初始化用的是 v5 SQL,文档目录最新是 v6:两者表名一致,但 v6 的
ac_user多了need_change_password列。新代码启动时会自动补列,所以能跑通;但这意味着第一次启动需要 DDL 权限,只给了增删改查权限的库账号会报错,日志里能看到「检查或自动升级表结构失败」。 - 靠启动时改表代替迁移工具:没有 Flyway/Liquibase,结构变更写在启动 Runner 里,只处理了
ac_user一个字段。后续版本升级要自己对照 SQL 差异手工执行。 - Java 8 + Spring Boot 2.7.8 已是存量组合:Boot 2.7 的开源支持在 2023 年底结束,之后只有商业支持。长期运行要自己评估 CVE 与升级路径。
- POM 里
jwt.version定义了两次:一处 0.9.1,一处 0.7.0,后者生效。jjwt 0.7.0 是很老的版本,二次开发时如果按文档引入 0.9.x 的 API 会对不上。 - 表字符集是
utf8mb3:建库脚本里大量表用utf8mb3,存 emoji 或部分生僻字会截断或报错。业务上涉及用户昵称、备注时要注意。 - 镜像没固定 tag:compose 里
image: tduckcloud/tduck-platform没写版本,docker compose pull拿到的可能是更新的镜像,与本地 v5 库结构对不上。生产建议自己 build 并固定 tag。 - MySQL 端口直接映射到宿主机 3306:compose 默认这样写,且 root 允许任意主机登录(
MYSQL_ROOT_HOST=%)。服务器上已有 MySQL 会端口冲突,暴露公网更危险。 - 上传上限 1000MB:
spring.servlet.multipart单文件 1G,配合默认本地存储很容易被撑爆磁盘,需要自己加限流与配额。 - 密码保护表单曾有绕过漏洞:最近一次提交(2026-10-30)才修掉「提交时未校验密码」的问题。用最新代码,别从旧镜像或旧分支部署。
- 前端不在本仓库:接口改了前端不一定跟着改,两边没有版本对齐机制,升级要一起动。
- Star 数不等于维护强度:10.5K Star 但只有 6 个开放 Issue、2 个 PR,说明社区提问主要走文档站和商务渠道,遇到坑的公开检索记录少。