1. 年度升级的整体思路与主线
1.1 调问问卷系统的定位与用户场景
说起问卷系统,很多人第一反应是拿来做个满意度调查、收集一下报名信息,好像没什么技术含量。但真正在企业里跑过一轮完整采集流程的人都知道,一套能稳定支撑业务的问卷系统,远不是“表单加个提交按钮”那么简单。
调问从立项那天起,定位就很明确:做一套可私有化部署、可二次开发、数据完全自主可控的开源问卷系统。过去一年,围绕这个定位,团队一直在做收敛和打磨。所谓“迭代不止,赋能前行”,其实就是把社区里大量真实用户反馈回传的需求,一个个落地成可用的功能,让这套系统从“能跑”逐步变成“好用”。
目前系统主要跑在几类场景里:高校院系的课程评价和教学反馈、企业内部的人力调研和员工满意度、第三方服务商的客户回访、以及一些政务窗口的办事评价。这些场景有一个共同点——对数据敏感度要求高,且流程往往要和现有业务系统打通。这决定了调问在架构上必须保持开放,不能做成一个封闭的“问卷孤岛”。
1.2 过去一年收到的高频需求与改进方向
翻看过去一年的Issue和讨论区,高频需求其实非常集中。排在最前面的几类是这样的:第一,题型不够用,尤其是矩阵量表、排序题、文件上传这类相对复杂的组件;第二,逻辑跳转简陋,只能做简单的前后题跳转,没法按题组做条件编排;第三,导出格式不满足需求,不少用户直接说“我要SPSS能直接读的格式”;第四,部署太麻烦,很多小团队没有专职运维,希望一条命令能把整个系统跑起来;第五,和企微、钉钉、飞书这些IM工具的集成不够顺手。
这些需求汇总到一起,反映的是一个核心矛盾:系统过去偏“表单工具”,而用户真正需要的是“调研基础设施”。所以过去一年的升级,没有盲目堆功能,而是把资源集中投在三个方向上——编辑器体验、数据链路、集成生态。后面几章讲的都是围绕这三条主线的具体工作。
1.3 三条升级主线:编辑器、数据链路、集成生态
先简单交代一下三条主线对应的技术决策。编辑器这块,我们把前端的题型渲染层做了彻底重构,从原来一次性渲染整份问卷改成按需渲染,体感上最明显的变化是:一份上百题的问卷,拖拽和输入不再卡顿。数据链路这块,底层存储从单表大字段逐步拆成规范化结构,回收数据实时进统计服务,而不是等问卷关闭后才出报表。集成生态这块,我们把API全部升级到V2版本,补上了Webhook回调、OAuth2授权等基础能力。
这个顺序是有讲究的。编辑器体验上不去,用户连问卷都做不顺手,后面谈数据都是空中楼阁;数据链路不打通,回收量一大就卡死,再好的调研设计也白搭;集成生态不开放,系统就只能是个工具孤岛,进不了真实的业务流。所以如果你也在规划自己项目的年度技术路线,这个“体验先行、数据兜底、生态放开”的顺序可以直接抄作业。
2. 编辑器与题型体系:从能用走向好用
2.1 题型矩阵从12种扩展到28种
年度升级最直观的变化,是题型从原来的12种扩展到28种。基础的单选、多选、填空这些不用多说,重点补上的是这几类高价值题型:“矩阵量表题”支持多行多列的评分矩阵,做课程评价或者产品满意度调研时非常好用;“排序题”允许用户拖动选项调整优先级,适合做需求洞察;“文件上传题”支持单个文件最大50MB,后台可以限制扩展名和上传数量;“NPS推荐题”做了专门的分段样式,0到10分用色块区分,视觉上比普通单选清晰很多。
这里有一个经验想分享:题型扩展不能只在前端画一个组件就完事。一个题型要落地,背后起码涉及三道工序——编辑器里的配置面板、移动端和PC端的渲染适配、统计模块的聚合逻辑。比如“矩阵量表题”,编辑器里要配置行标签、列标签、是否允许N/A选项,渲染端要处理好横向滚动,统计端要输出每个交叉单元格的频数和均值。只要有一环漏了,这个题型上线后一定会被用户吐槽。
2.2 逻辑跳转与预置变量的设计思路
逻辑跳转是问卷系统里看起来简单、做起来最复杂的模块之一。以前的实现只能支持“如果第5题选了A,则跳到第8题”,这种单点跳转最头疼的问题是:问卷题目的顺序一调整,跳转关系就全部错乱。所以这次升级我们花了很大力气重构逻辑引擎,核心是把跳转条件从“题目ID”改成了“题目标识符”,题目可以任意拖拽排序,跳转逻辑自动跟随。
新的逻辑引擎支持三种控制方式:条件跳转、按组隐藏和结束问卷。条件跳转支持多条件组合,比如“第3题选B 且 第4题的填写值大于10”,满足条件后可以跳到指定题目、指定题组或者直接结束。按组隐藏则解决了一个常见需求:不同用户角色看到的题目范围不一样,比如内部员工和外部访客进入同一份问卷,题目可以直接按条件屏蔽。
我们还在编辑器里内置了“预置变量”,可以读取URL参数、当前时间、设备类型、用户ID等上下文信息。典型用法是投放渠道追踪:给不同渠道生成不同链接,链接里带source参数,问卷提交后自动记录来源,后续分析各渠道回收转化率时,就不再需要人工去数渠道数了。
2.3 编辑器底层重构:拖拽、快捷键与多端一致
今年上半年,编辑器做了一次伤筋动骨的重构,把题目的渲染方式从整份问卷一次性渲染,改成了按需渲染加虚拟滚动。改完以后,一份300题的问卷在低端笔记本上拖拽,基本能保持在流畅的帧率。这个优化没有引入任何重型框架,核心就是列表虚拟化加局部更新,源码里可以直接看到实现,想抄作业的同学建议重点看QuestionList.vue这个文件。
编辑器的操作效率也做了不少细节提升。新增了快捷键体系:Ctrl+D快速复制题目,Ctrl+Shift+↑/↓调整题目顺序,Ctrl+G把多道题快速编入一个题组。这些快捷键在制作长问卷时能明显减少鼠标点击次数,社区反馈下来,用户上手最快的反而是这些不起眼的细节功能。
这里必须提醒一句:编辑器重构期间最容易翻车的是草稿自动保存的时机。我们的策略是“操作防抖+定时双保险”,用户停止操作800毫秒后自动保存一次,同时每60秒强制保存一次。如果是大问卷,注意定期用“导出草稿JSON”功能做本地备份,这个功能在设计器右上角更多菜单里,导出的JSON可以直接用于导入恢复,关键时刻能救命。
3. 数据统计链路:从收数据到用数据
3.1 实时回收看板与基础统计
过去系统是“问卷关闭后统一出报表”,用户每次都得等。这次升级后,所有问卷在创建时就默认开启实时统计,每一条新回收数据进来,统计结果图表都会增量刷新。实测下来,在单份问卷日回收量1万条以下的场景里,回收数据到看板更新的延迟基本在3秒以内。
实时看板主要展示四类信息:回收总量与完成率、题目回答分布、地理分布(按IP归属地粗略统计)、设备来源。其中“完成率”这个指标我建议所有运营同学重点盯,它等于实际提交数除以进入问卷页面的访客数。很多调研做了大量推广,结果点击率不错,但问卷做到一半流失严重——这时候问题往往出在问卷长度或题目表述上,而不是推广渠道本身。
看板还支持按时间维度做对比视图,可以按小时、按天、按周查看回收曲线的变化趋势。比如某个活动渠道在晚上8点投放,你能在回收曲线里直接看到对应的波峰,渠道效果评估也就有据可依了。
3.2 交叉分析和筛选下钻
看板上的图表解决的是“看了不直观”的问题,但用户真正高频用到的是交叉分析。交叉分析是什么意思呢?简单说就是把两个题目放在一起看分布关系,比如“不同部门的员工对食堂的满意度差异”,行是部门、列是满意度等级,直接输出一个二维交叉表。实现上,我们优化了分组聚合SQL的生成逻辑,支持基于任意两道题目做组合,同时在后台做了缓存,重复跑同样的交叉分析不会反复查库。
筛选下钻也是这次迭代的重要能力。以前想做筛选,必须在创建问卷时就预设好“隐藏题”,灵活性极差。现在统计分析页提供了全局筛选器,可以按任意题的选项值、提交时间范围、渠道来源、设备类型等条件过滤数据,再一键下钻到明细列表。曾经有个做市场调研的社区用户反馈,他靠这个筛选器把5000条回收数据按城市和年龄段切分,挖出了三个完全不同的用户偏好模型,这类用法是我们设计之初没想到但非常欣慰的。
3.3 导出能力扩展:CSV/Excel/SPSS
导出功能是过去一年社区呼声最高的需求之一。新版支持三种主要格式:CSV带或不带UTF-8 BOM(Excel直接打开中文不乱码的关键)、.xlsx多Sheet导出、以及SPSS的.sav格式。这里重点说一下.sav格式的实现,它其实是基于开源库pyreadstat做的封装,问卷的选项标签会映射成SPSS的“值标签”,量表题自动识别为数值型变量。这样用户拿到数据后可以直接进SPSS做信度分析,不用再手动清洗变量。
导出设置的细节这次也做了补全:导出范围支持“全部数据”或“筛选后的数据”;题目范围支持“所有题目”或“指定题目”;匿名化选项可以把IP地址、自定义用户ID等敏感字段做脱敏后再导出。这些细节在等保和隐私合规要求严格的单位里很关键,建议相关同学认真看一下导出设置里的每一个开关。
3.4 隐私与合规细节:匿名回收、数据保留策略
数据合规不是一句空话,落到代码层面全是细节。新版做了一个重要的架构调整:匿名回收模式。开启后,系统不再记录提交者的IP地址、User-Agent、用户ID等元信息,前端也不会埋渠道来源的cookie,从技术根源上杜绝了个人信息采集。对某些敏感的内部调研来说,这个开关比事后“删除数据”要稳妥得多,因为数据压根没进过库。
数据保留策略这次也做成了可配置项。管理员可以在系统设置里设置自动清理周期,比如“回收数据保留180天,过期自动删除”。同时提供了“数据导出留痕”功能,每次导出操作都会记录操作人、导出时间、导出范围和数据量。这些功能单独看都是小功能,但组合起来就是一套完整的数据生命周期管理方案。
4. 接口与集成生态:让系统嵌入业务
4.1 OpenAPI V2 与 Webhook 重试机制
如果只是把问卷系统当独立工具用,API的优先级没那么高。但要让系统嵌入业务流,开放接口就是硬要求。年度升级把API整体升级到V2版本,所有接口统一前缀/api/v2,统一返回结构,统一错误码。认证方式支持Token和OAuth2 Client Credentials两种模式,对服务端到服务端的调用场景非常方便。
新增的Webhook订阅机制是比较实用的能力。管理员可以在后台配置一个回调URL,订阅response.created(新回收提交)、survey.completed(问卷关闭)、survey.published(问卷发布)等事件。系统会以POST方式推送JSON数据到指定URL,如果接收方没有返回2xx状态码,系统会按“30秒后重试、5分钟后重试、30分钟后重试”的节奏执行三次重试。这个重试机制我们投了不少精力做幂等处理,防止同一事件重复推送造成业务侧数据重复。
4.2 单点登录与组织权限
私有化部署的场景里,单点登录几乎是个“必须有”的能力。新版针对企业内部部署新增了CAS、OIDC、LDAP三种主流协议对接。具体来说,OIDC走标准授权码模式,支持任意符合OIDC规范的IdP;CAS则适用于高校和科研机构常见的老牌认证中心;LDAP适合以AD域控为账号体系的中大型企业。三种协议在后台配置页里填好地址和密钥就能启用,不需要改一行代码。
组织权限模型这次也做了升级,从原来“一个系统一套账号”的扁平结构,扩展成“组织—部门—成员—角色”四级模型。这样一套部署可以支持多个部门独立管理各自的问卷,问卷资源可以按部门隔离,也可以跨部门共享。权限控制的粒度细化到“谁能编辑问卷、谁能查看数据、谁能导出数据、谁能管理成员”四个维度,每个角色可以自由组合。权限设计这门功课,建议刚开始做私有化产品的团队认真参考,虽然前期建模麻烦,但后面处理客户需求时省下来的沟通成本远大于建模成本。
4.3 与企微、钉钉、飞书等IM的打通
IM集成是过去半年新增需求里增长最快的一个方向。调问现在支持企业微信、钉钉、飞书三个平台的消息推送和免登集成。具体玩法是这样的:以企业微信为例,管理员在后台填写企业ID、AgentId、Secret后,系统可以自动把问卷链接推送到指定成员或部门群,回收结果也可以实时推送给制作者;员工在企业微信内点击链接,可以直接免登进入问卷,系统自动识别成员身份,省去手动填工号的步骤。
实现上,三个平台的集成都是基于各家的开放API做的适配层。由于三家API风格差异较大,我们在源码里单独建了integration模块,每个平台一个子目录,通过统一接口对接上层业务。如果你要在自己的项目里接多家IM,建议也采用这种“统一接口+多实现”的方式,不要在每个业务点散落地写对接逻辑,不然后期维护会非常痛苦。
5. 部署与运维体验:降低门槛是第一优先级
5.1 轻量化架构演进:从单体到模块化
调问在架构上的演进一直比较克制,没有为了追热度去拆微服务。目前仍然是一个单体应用,但做了一次模块化改造,把问卷管理、回收存储、统计分析、系统管理四个核心域拆成了独立模块,模块之间通过内部接口通信。这样做的好处是:部署复杂度和单体一样低,但代码边界比原来清晰,维护成本下降明显。
在技术选型上,后端基于Java 17 + Spring Boot 3.x,前端是Vue 3 + Element Plus。存储这块,MySQL负责业务数据,Redis做缓存和会话管理,文件上传走本地磁盘或S3兼容对象存储。整套系统在2核4G的云服务器上就能跑得很稳,不算对象存储的话,运行时依赖只有MySQL和Redis两个外部组件,对运维同学非常友好。
5.2 Docker Compose 一键部署实践
部署体验是这次升级的重头戏。原来部署需要手动装JDK、配MySQL、跑初始化脚本,新人照着文档折腾一下午是常事。现在提供了完整的docker-compose.yml编排文件,包含应用容器、MySQL、Redis三个服务,内置健康检查和初始化逻辑。用户只要机器上装了Docker和Compose插件,按官方文档三步操作就能拉起一套完整环境。
wget -O docker-compose.yml https://example.com/install/docker-compose.yml docker compose up -d执行完这两条命令后,系统会自动初始化数据库、创建管理员账号,然后监听在8080端口。需要注意的一点是,docker-compose.yml里默认挂载了./data目录用于持久化,这个目录一定要提前做好定期备份。容器可以随时删了重建,但数据没了就真没了。
5.3 性能压测与资源占用记录
过去一年我们对系统做了多轮压测,分享一组数据供参考:在2核4G的云服务器上,MySQL和Redis同机部署的情况下,单机可以支撑约3000份问卷并行发布,问卷提交接口的QPS约500,P95响应时间在120ms左右。这个数据是在回收数据量每份问卷5万条以内测出来的,如果你有更高的量级预期,建议提前考虑升级配置或做读写分离。
内存占用方面,由于统计聚合结果做了Redis缓存,频繁访问看板的场景下内存会稳定在1GB左右。我们还专门做了慢查询日志优化,把统计分析页的核心SQL全部加了索引,并改造成分页查询。这里给所有用MySQL存问卷数据的团队一个建议:问卷明细表一定要按问卷ID建索引,否则数据量一上来,统计接口会把你数据库拖垮。
5.4 数据迁移与备份恢复
系统升级过程中,数据迁移是用户最焦虑的环节。这次我们做了一个数据迁移工具,支持从旧版本一键迁移到新版本,包括问卷定义、回收数据、用户账号、系统配置全量迁移。迁移工具会先做数据完整性校验,校验通过后才会执行正式迁移,并在迁移完成后输出一份报告,列出每张表的迁移条数和校验结果。
备份和恢复也做了体系化支持。后台提供“一键备份”功能,可以把数据库和上传文件打包下载;也支持配置定时备份,默认推荐每天凌晨执行一次,保留最近7天备份。在恢复操作上,系统只支持全量恢复,不支持部分恢复,所以恢复前务必确认备份包里的数据版本是否符合预期。
6. 开源社区协作与常见问题实录
6.1 开源社区的治理经验
一个开源项目能不能持续迭代,很多时候不取决于代码写得多好,而取决于社区治理。过去一年调问社区最值得说的,是形成了一套明确的Issue处理规范。所有新Issue进来,维护团队会在48小时内打上标签:bug、feature、question、duplicate、invalid。bug类问题要求附上复现步骤和环境信息,feature类问题要求描述使用场景和期望效果,信息不全的会直接打回补充。这套机制看起来费时间,实际上大大降低了维护者的沟通成本。
对于想参与贡献的开发者,我们在CONTRIBUTING.md里写清楚了完整的协作流程:Fork仓库、创建分支、提交PR、通过CI检查、等待Review。PR合并有两个硬性要求——所有测试必须通过,且新增代码必须包含相应单测。有些贡献者觉得单测麻烦,但项目能保持高迭代频率不出大事故,靠的就是这些测试兜底。
6.2 常见问题排查速查表
把过去一年社区反馈里最高频的问题整理成了一张速查表,遇到问题可以先对照排查。
| 问题现象 | 可能原因 | 解决办法 |
|---|---|---|
| 回收数据刷新不出来 | Redis缓存与MySQL数据不一致 | 后台“清理缓存”按钮,强制刷新统计缓存 |
| 导出Excel中文乱码 | CSV格式未选UTF-8 BOM | 导出时选择“CSV (UTF-8 BOM)”格式 |
| 跳转逻辑不生效 | 题目标识符被修改,条件指向旧标识符 | 编辑器里重新选择跳转目标题目 |
| Webhook收不到回调 | 回调地址未通过外网连通性检查 | 检查防火墙和回调地址可访问性 |
| 文件上传失败 | 上传目录权限不对 | 给/data/upload目录赋写权限 |
| 忘记管理员密码 | —— | 使用CLI命令重置:java -jar app.jar reset-password --email=admin@example.com |
另外有一个经常被忽略的坑:升级后浏览器缓存了旧版前端资源,导致页面白屏或样式错乱。遇到这种情况,让用户强制刷新(Ctrl+F5)即可,也可以在后端配置静态资源带版本号,从根源上规避。
6.3 为问卷系统贡献代码的注意事项
如果读到这里,你也想给调问贡献代码,有几点实际建议。第一,优先从good first issue标签的Issue入手,这些通常都是改动范围小、不涉及核心架构的问题,适合熟悉代码库;第二,做任何改动前,先在本地把docker compose up跑起来,确保环境可用,不建议直接改线上环境调试;第三,涉及数据库变更的PR,必须附带对应的迁移脚本,目录在src/main/resources/db/migration下,命名规则是V{版本号}__{描述}.sql。
拿到一个Issue之后,我建议你先别急着写代码,花半天时间把调用链读明白。比如你要做“评分题默认值”这个功能,至少要看明白这个题型从编辑器配置到数据存储再到统计展示的完整链路。有一次社区一位同学提交的PR,只改了编辑器的配置项,没动统计显示端,导致题型配置了默认值但统计结果里看不出任何区别,这种半截子功能Review时是过不了的。
7. 一年迭代下来的几点实在体会
写到这里,聊几句这一年踩坑换来的实在体会。
第一,开源项目的功能优先级,一定要让真实用户投票,而不是开发团队自己拍脑袋。调问这一年最受欢迎的几个功能——SPSS导出、Webhook、企微集成,全部来自社区Issue的高频反馈,没有一个是我们最初路线图里排在前面的事项。开发团队再了解用户,也难免有盲区,把渠道敞开,让需求自己冒出来,项目的路才会越走越宽。
第二,看似不起眼的细节,往往才是用户留存的关键。比如草稿自动保存、Excel导出的中文编码、手机端按钮的触控区域大小,这些功能放到宣传页里说不出口,但没有它们,用户在实际使用中就是会流失。今年我们对“导出文件编码”这个细节做了两次修复,每次修复后社区里的“感谢”回复都特别多。
第三,不要怕重构,但要给重构留出足够的测试覆盖。编辑器那次重构前后花了将近两个月,中间出现过不少回归问题,好在旧功能都有自动化测试兜住,没有造成严重事故。如果你正在纠结要不要对核心模块做重构,建议先问自己一个问题:当前模块的测试覆盖率能不能保证重构后行为一致?如果不能,先补测试再动手。