一句话定位: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 Boot2.7.8(父 POM 统一管理)
语言Java1.8
ORMMyBatis-Plus3.5.3,主键 AUTO,下划线转驼峰
连接池HikariCP最大 15 连接,SELECT 1 FROM DUAL 探活
缓存EhCacheclasspath:ehcache.xml,非 Redis
鉴权JWT自定义请求头 token,有效期 604800 秒(7 天)
接口文档springdoc-openapi1.6.9,默认 关闭(enabled: false)
ExcelApache POI5.1.0,导入导出与打印
微信WxJava4.1.0(mp / miniapp / pay)
验证码anji-plus captcha1.2.8,默认关闭
云 SDK阿里云 OSS 3.12、七牛 7.11、又拍 4.2.2、AWS S3 1.12.400、腾讯云 3.1.977全是存储与短信
序列化fastjson 2.0.26 + Jackson两套并存
HTTPOkHttp 4.11WebHook 推送

8 个模块的分工,按代码量看重心很明显:

模块Java 文件职责
tduck-form123表单核心:表单、组件、逻辑、数据、统计、模板、主题
tduck-common50基类、常量、工具、通用配置
tduck-api45唯一启动模块,21 个 Controller 与全部配置文件
tduck-wx-mp31公众号对接、网页授权、JS-SDK、消息推送
tduck-account30账号、登录、令牌、第三方授权
tduck-webhook16推送配置、事件、发送与监听
tduck-storage135 种存储实现的统一抽象
tduck-ai8AI 对话客户端与建表接口

三个值得注意的设计取舍:

  • 配置只有一个入口。全仓库只有 tduck-api/src/main/resources/application.yml 和 application-prod.yml 两个主配置,spring.profiles.active 默认就是 prod。改配置只有一处,但也意味着换环境只能靠改文件,没有配置中心。
  • 表单设置用 JSON 列存。fm_user_form_setting.settings 是 MySQL json 类型,整个表单的提交设置、限次规则、通知开关都塞在一个字段里。改起来灵活,代价是没法用 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.yamlMySQL 8.0.36 + 平台镜像,端口 8999
docker/init-db/tduck-v5.sqlDocker 首次启动自动导入的脚本(是 v5,不是 v6)
readmeImages/README 截图
LICENSEMIT 全文

包内分层统一: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 -d

compose 里定义了两个服务:

项值
MySQLmysql: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-platformDocker 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,说明社区提问主要走文档站和商务渠道,遇到坑的公开检索记录少。

12. 参考链接

名称地址
代码仓库https://gitee.com/TDuckApp/tduck-platform
前端仓库https://gitee.com/TDuckApp/tduck-front
TReport 大屏https://gitee.com/TDuckApp/tduck-report-platform
官网https://www.tduckcloud.com
部署文档https://doc.tduckcloud.com
宝塔一键安装https://doc.tduckcloud.com/openSource/deploy/deployforbt.html
Docker 部署https://doc.tduckcloud.com/openSource/deploy/dockerDeploy.html
前后端分离部署https://doc.tduckcloud.com/openSource/deploy/fenli.html
产品能力对比https://www.tduckcloud.com/doc/x/nSJMvQh6
Issue 社区https://gitee.com/TDuckApp/tduck-platform/issues

标签: Java, tduck, 表单

添加新评论