每个团队手里都攒着一堆「就差一个表格页面」的需求订单要能改状态、库存要能按条件查、请假单要能走一遍审批。后台表格、查询页、审批流程这三类东西需求方觉得是小活研发排期却一排就是一两周。常规做法是 Spring Boot 加 Vue 自研一套 CRUD可每来一个新表就重写一遍列表、筛选、分页和按钮逻辑写完还得处理登录、权限、部署。代码量不大但重复度极高改一个字段要动三层。本文分享一套把 PostgreSQL 直接变成业务系统的落地路径ToolJet 做界面与流程编排 PostgreSQL 做数据与约束 Caddy 做远程访问收口全程 Docker 起服务可直接落地。一、重复劳动的现场三类需求吃掉八成排期先把这些「小需求」拆开看它们的共同点非常明确表格页列表、筛选、分页、行内编辑本质是SELECT加一个UPDATE按钮。查询页几个输入框加一个条件组合本质是一条参数化 SQL。流程页提交、审核、驳回、通知本质是状态机加一次消息推送。共同点全部围绕已有的表结构逻辑不超过一屏代码却要套上完整的前后端工程。查询 SQL → 状态流转 → 通知推送这条链路在几乎每个内部系统里都会原样出现一遍。**核心结论**这些需求缺的不是代码能力而是一个不用写代码的运行时。二、选型依据为什么落点是 ToolJet 而不是自研把几个常见方案按「内部工具」这个场景比一遍维度ToolJetAppsmith自研 CRUD开源与部署CE 版可自托管CE 版可自托管完全自主PostgreSQL 支持一等公民自带查询器支持需装插件需自己写 DAO内置工作流Workflow 独立模块较弱靠 JS 编排自己实现状态机权限粒度组 页面 查询级组 页面级自己设计新增一张表的成本十分钟级半小时级一到两天自托管优先ToolJet CE 全部组件跑在自己机器上业务表不出内网。SQL 即逻辑查询直接写原生 SQL复杂 join 不用迁就 ORM。Workflow 独立审核流程不塞在页面 JS 里单独可视化编排。**核心结论**内部工具的胜负手是「改一个字段的成本」ToolJet 把这个成本压到了配置层。三、起服务Docker Compose 一次拉起 ToolJet 与元数据库ToolJet 自身的用户、应用、查询定义也要存库所以起两个 PostgreSQL一个存元数据一个放业务数据。下面这段 compose 用 CE 版镜像起 ToolJet 与元数据库业务库可以复用现有实例services:tooljet:image:tooljet/tooljet-ce:latestdepends_on:[postgres]environment:-NODE_ENVproduction-TOOLJET_HOSThttps://tooljet.internal-DB_HOSTpostgres-DB_USERtooljet-DB_PASSchange_me-LOCKDOWN_DEFAULT_WORKSPACEtrueports:[8080:8080]postgres:image:postgres:16-alpineenvironment:POSTGRES_USER:tooljetPOSTGRES_PASSWORD:change_mevolumes:[pgdata:/var/lib/postgresql/data]volumes:pgdata:强约束锁库账号 → 只读业务账号 → 页面级白名单三个开关在起服务时就要定下来。四、接数据源把 PostgreSQL 挂进 ToolJet 并做好账号隔离业务库绝不能用超级用户接进来先在库里准备好专用账号-- 只给 ToolJet 查询用的只读账号CREATEROLE tj_reader LOGIN PASSWORDstrong_pwd;GRANTCONNECTONDATABASEbizTOtj_reader;GRANTUSAGEONSCHEMApublicTOtj_reader;GRANTSELECTONALLTABLESINSCHEMApublicTOtj_reader;ALTERDEFAULTPRIVILEGESINSCHEMApublicGRANTSELECTONTABLESTOtj_reader;账号分级查询页绑只读账号写操作单独绑一个只有INSERT/UPDATE权限的账号。SSL 强制数据源配置里勾选 Require SSL跨机器访问不走明文。Schema 白名单只暴露业务 schema元数据库不出现在数据源列表里。连接池留意ToolJet 默认每个查询新建连接高并发页面要给 PostgreSQL 留足max_connections。**核心结论**权限收在数据库层最稳页面层再怎么配也拦不住一条手滑的DELETE。五、后台表格从建表到 CRUD 页面的字段设计建表时就把页面要用的东西设计进去能省掉大量后续补丁字段类型设计意图statusvarchar CHECK状态只允许白名单值页面下拉框直接读约束created_attimestamptz 默认 now()列表默认按它倒序分页稳定updated_byvarchar行内编辑时由页面自动填入当前账号versionint 默认 0乐观锁防止两个人同时改一行页面侧的操作建议列表绑定查询拖一个 Table 组件数据源指向上一步建好的SELECT查询。行内编辑走 UPDATE编辑保存动作绑定UPDATE ... WHERE id {{table.selectedRow.id}} AND version {{...}}。行数为 0 也要能看加一个空状态提示避免需求方以为系统挂了。**核心结论**把状态白名单、时间戳和乐观锁放进 DDL页面就只剩拖拽配置。六、查询页参数化查询与前端联动查询页的核心是一条绑定页面变量的参数化 SQLToolJet 用:参数名或{{组件值}}占位-- 绑定三个页面控件状态下拉、起止日期SELECTo.id,o.amount,o.status,c.nameAScustomer,ROUND(EXTRACT(EPOCHFROM(o.paid_at-o.created_at))/3600,1)AShours_to_payFROMorders oJOINcustomers cONc.ido.customer_idWHEREo.status:statusANDo.created_atBETWEEN:fromAND:toORDERBYo.created_atDESCLIMIT100;先跑再绑查询编辑器里先填常量验证 SQL 正确再把常量换成绑定表达式。分页放数据库别在前端切片LIMIT/OFFSET或游标交给 PostgreSQL。导出复用查询导出按钮直接复用同一个查询避免维护两份 SQL。**核心结论**一条参数化 SQL 加三个控件绑定就等于一个查询页。七、流程页用 Workflow 把审核和通知串起来ToolJet 的 Workflow 独立于页面存在适合放状态机。链路长这样表单提交 → 创建待审记录 → 审核人收到通知 → 通过则改状态并写日志 / 驳回则回到申请人Workflow 节点之间用 JavaScript 节点做格式转换例如把金额从分转成元再推送给审核人// Workflow 的 Code 节点整理待推送的审批摘要constrowsqueries.fetchPending.list;constitemsrows.map(r({id:r.id,applicant:r.applicant,amount:(r.amount/100).toFixed(2)}));consttotalitems.reduce((s,r)sNumber(r.amount),0);return{items,summary:共${items.length}单合计${total.toFixed(2)}元};审核动作落库通过/驳回按钮只触发 Workflow页面不直接写表避免状态被绕过。日志单独一张表谁在什么时间改了什么状态全部追加写入audit_log。失败要重试通知节点失败不应阻断状态变更配置失败分支只记日志。**核心结论**状态变更全部走 Workflow页面只负责收集参数流程才不会被旁路。八、权限体系组、页面、查询三层管控权限配错是内部工具最常见的事故源按三层来收口用户组按角色建组申请员、审核员、管理员人员只进组不单独授权。页面可见性审核页只对审核员组可见别用「藏按钮」代替隐藏页面。查询可执行性高危查询如批量删除单独建查询并绑定管理员组页面拿不到就调不动。检查顺序建议是组 → 页面 → 查询逐层往下验反着查很难定位是哪一层漏了。**核心结论**页面隐藏只是体验查询不可执行才是安全。九、远程访问反向代理与 HTTPS 收口ToolJet 直接暴露公网等于把数据库查询能力公开前面必须加一层代理# Caddy 配置自动签证书并转发到本机 ToolJettooljet.example.com{reverse_proxy127.0.0.1:8080 header_up X-Forwarded-Proto{scheme}encodegzip}只开 4438080仅监听127.0.0.1防火墙不放行。TOOLJET_HOST必须改成公网域名否则邮件里的链接、OAuth 回调都指错地址。加一层 IP 白名单远程访问场景下把办公网出口 IP 写进代理规则。先内网验证再上公网证书、回调、WebSocket 三项都在内网走通一遍。**核心结论**远程访问的正确姿势是「代理收口 域名固定 白名单」而不是把 8080 直接映射出去。十、上线前检查清单踩坑与排错对照表把上线前最容易翻车的几项列成清单逐条勾现象常见原因处理动作页面打开但查询全部超时业务库连接数被元数据库占满分库并调大max_connections保存页面报权限错误业务账号被误配成只读却执行了写查询拆分读写两个数据源远程访问白屏TOOLJET_HOST仍是http://localhost改为公网域名并重启容器审核后状态没变审核按钮直接写表绕过了 Workflow把写操作改为触发 Workflow表格数据串行两人同时编辑同一行启用version乐观锁备份别只备份业务库ToolJet 元数据库丢了所有页面和流程定义一起丢。升级先在测试实例演练CE 版跨大版本升级要跑迁移脚本直接滚生产容易卡住。**核心结论**上线前把「连接、权限、域名、流程、并发」五项各验一次能挡掉九成故障。结语n后台表格、查询和流程这三类需求几乎每个团队都会反复遇到。把它们从自研 CRUD 里解放出来用 ToolJet 承担界面与编排、PostgreSQL 承担数据与约束、Caddy 承担远程访问收口新增一个业务模块就从「排期一周」变成「配置半小时」。这套方案的价值不在省代码而在于把权限、审计和流程闭环沉淀成平台能力业务变化时只改配置不动工程。把重复的 CRUD 交给运行时把精力留给真正有业务含义的规则。
阅读完成 · 觉得有帮助?