跳到内容

表单平台

Form 模块提供问卷、调研、报名和反馈类业务的表单平台底座。

当前已完成模块骨架、应用装配、后台表单定义 / 版本 / 字段契约、发布渠道、公开预览、公开提交、答案快照、 文件题型、文本内容安全、条件显示、分步骤填写、统计报表、表单模板、复制创建、协作者权限快照、通知、审批、Webhook 端点和 Admin 管理入口。模块 Artifact 为 spring-open-module-form,包名为 com.springopen.module.form

API 前缀

  • 后台管理:/api/v1/admin/forms
  • 公开入口:/api/v1/forms/public
  • 开放 API:/api/v1/open/forms

能力发现

后台可通过 GET /api/v1/admin/forms/capabilities 查询当前装配的基础能力:

  • 模块启用状态。
  • 后台与公开 API 前缀。
  • 单表单最大字段数。
  • 单次提交 payload 默认最大字节数。
  • 公开入口开关。
  • 支持题型与生命周期状态。
  • 后台管理权限码清单,便于 Admin 侧按元数据启停操作入口。

表单定义管理

后台表单定义 API 已接入真实数据库契约,路径前缀为 /api/v1/admin/forms/definitions

方法路径说明
GET/api/v1/admin/forms/definitions分页查询表单定义、当前版本和字段摘要
GET/api/v1/admin/forms/definitions/{id}查询表单定义、版本列表和当前版本字段
GET/api/v1/admin/forms/definitions/{id}/versions/{versionId}查询指定版本字段
POST/api/v1/admin/forms/definitions创建表单定义和初始草稿版本
PUT/api/v1/admin/forms/definitions/{id}修改表单定义;字段变更只写入草稿版本
POST/api/v1/admin/forms/definitions/{id}/versions/draft基于当前版本创建新草稿
POST/api/v1/admin/forms/definitions/{id}/versions/{versionId}/publish发布草稿版本,旧发布版本转为历史
DELETE/api/v1/admin/forms/definitions/{id}逻辑删除定义、版本和字段

权限码:

  • form:view
  • form:create
  • form:update
  • form:publish
  • form:delete

后台菜单由迁移文件预置为顶级表单菜单,菜单 code 为 form,组件为 forms/index

字段契约:

  • 字段 Key 在同一版本内必须唯一。
  • 支持题型与 GET /capabilities 返回的 supportedQuestionTypes 保持一致。
  • 字段定义保存前会转换为 Schema starter 的 SchemaDefinition 并执行统一定义校验;提交值会转换为 SchemaValidationRequest 并执行统一值校验。Form 仍负责表单可见性、内容安全、文件引用快照、审批、Webhook 和付费提交等业务流程。
  • single_choice / multiple_choice 必须提供 optionsJson
  • file 题型可在 validationJson 中配置 maxFiles,用于限制单字段最多上传 / 提交的文件引用数量。
  • text / textarea 可在 validationJson 中配置 contentSafety=false 关闭提交期内容安全检测;默认会在装配 Content Safety Provider 时检测。
  • 所有题型都可在 validationJson 中配置 visibleWhenstepstepTitle,用于条件显示、逻辑跳转和分步骤填写。
  • 单表单字段数受 spring.open.form.max-fields-per-form 限制。

除旧题型外,Form 可复用 Schema starter 已定义的 emailurlphoneintegerdecimalbooleantimedate_timeimagejson 等字段类型。需要 Schema 校验的新字段类型依赖 spring-open-starter-schema 装配;若显式禁用 Schema starter,Form 只接受旧题型,避免出现定义可保存但提交不可校验的状态。

模板、复制创建与协作

后台表单详情页可将当前表单版本保存为模板,也可从模板复制创建新的私有草稿表单。模板保存字段结构、表单设置、来源表单 / 版本、 分类、官方标记和使用次数;复制时会按模板字段快照重建字段,不依赖来源表单后续变更。

方法路径说明
GET/api/v1/admin/forms/templates分页查询表单模板,可按名称 / 编码、场景、分类、官方标记和状态筛选
GET/api/v1/admin/forms/templates/{id}查询模板字段和设置快照
POST/api/v1/admin/forms/templates将指定表单版本保存为模板
PUT/api/v1/admin/forms/templates/{id}修改模板信息;传入来源表单时刷新字段快照
POST/api/v1/admin/forms/templates/{id}/copy基于模板复制创建新的表单草稿
DELETE/api/v1/admin/forms/templates/{id}删除模板
GET/api/v1/admin/forms/definitions/{formId}/collaborators查询表单协作者权限快照
POST/api/v1/admin/forms/definitions/{formId}/collaborators新增或更新表单协作者
DELETE/api/v1/admin/forms/definitions/{formId}/collaborators/{collaboratorId}移除表单协作者

模板查询复用 form:view,模板创建 / 复制复用 form:create,模板修改和协作者维护复用 form:update, 模板删除复用 form:delete。协作者角色当前支持 adminviewerexporter,服务端会写入对应权限 JSON 快照。

发布渠道、公开预览与提交

后台可在表单详情中为已发布版本创建发布渠道。发布渠道只绑定已发布版本, 后续编辑草稿不会影响已生成渠道的预览快照。

方法路径说明
GET/api/v1/admin/forms/definitions/{formId}/publish-channels查询表单发布渠道
POST/api/v1/admin/forms/definitions/{formId}/publish-channels基于已发布版本创建发布渠道
PUT/api/v1/admin/forms/definitions/{formId}/publish-channels/{channelId}/status启用或暂停发布渠道
DELETE/api/v1/admin/forms/definitions/{formId}/publish-channels/{channelId}删除发布渠道
GET/api/v1/forms/public/{channelCode}公开预览发布渠道绑定的表单版本
POST/api/v1/forms/public/{channelCode}/submissions提交公开表单答案

渠道类型:

  • public_link:公开链接。
  • short_link:短链渠道快照,当前不直接依赖 Short Link 业务模块。
  • live_code:活码渠道快照,当前不直接依赖 Live Code 业务模块。

公开预览默认受 spring.open.form.public-access-enabled=false 保护。开启后, 只有 active 状态且处于有效时间窗口内的渠道才会返回表单结构。

公开提交要求请求体提供 answersJson,该值必须是 JSON 对象字符串。服务端会按发布版本字段重新校验:

  • 登录填写渠道必须存在当前登录用户。
  • 渠道 submissionLimit 已达到上限时拒绝提交。
  • answersJson 不能超过 spring.open.form.max-submission-payload-bytes
  • 必填字段必须存在且非空;数组和对象答案不能是空集合。
  • 未在发布版本中的字段会被拒绝;选择题、数字、日期、评分和文件题型会按字段类型重新校验。
  • 服务端会按发布版本字段的 visibleWhen 重新计算当前可见字段;隐藏字段不会参与必填、类型和内容安全校验,也不会写入 form_submission.answers_json 或答案快照。

提交成功后会写入 form_submissionform_submission_answer,返回提交编号、提交状态、字段数量和提交时间。后台可查询提交列表、 详情和字段答案快照,并可按筛选条件创建 CSV 异步导出任务。导出产物只包含提交元数据,不包含答案 JSON。

付费提交可在发布渠道上开启:paidEnabled=1paymentAmount>0 时,公开提交会先写入 pending_payment 状态并返回 billingRequired=truepaymentOrderNopaymentStatus=pendingpaymentAmountpaymentCurrency。支付订单由 Cashier scene=form-submission 承接,Cashier 支付成功回调后 Form 会幂等标记提交支付成功,并按原本内容安全结果进入 submittedunder_review,此时才增加提交计数、触发通知 / 审批 / Webhook 等后续副作用。未开启付费的渠道仍按原公开提交流程直接完成。

发布渠道付费字段:

  • paidEnabled:是否开启付费提交。
  • paymentAmount:单次提交金额,开启付费时必须大于 0。
  • paymentCurrency:币种,未填写时默认 CNY
  • cashierProvider:可选 Cashier 支付 Provider。
  • paymentSubject:可选支付标题,未填写时使用渠道名或表单名。

开放表单 API 与商业化配额

开放表单 API 复用 Open Platform 的 AK/SK、签名、防重放、scope、限流、调用日志和计量链路。Form 模块不直接依赖 Open Platform 业务实体,只消费 Runtime 提供的中立 OpenApiAccessGrant 和路由 scope 注册 contract。

方法路径ScopeEntitlement item
GET/api/v1/open/forms/{channelCode}open:form:readopen-api-read
POST/api/v1/open/forms/{channelCode}/submissionsopen:form:submission:createopen-api-submit
GET/api/v1/open/forms/{channelCode}/submissions/{submissionNo}open:form:submission:readopen-api-submission-read
GET/api/v1/open/forms/{channelCode}/reportopen:form:report:readopen-api-report

开放 API 配额由 Entitlement 中性底座承接,Form 使用:

  • consumerCode=form
  • ownerType=open-platform-app
  • ownerId=<Open Platform App ID>
  • quotaScope=open-api
  • quotaPeriod=daily / monthly

当目标 Open Platform App 没有配置 Form 权益或对应 item / 配额策略时,Form 不做商业化拦截;一旦配置了启用中的日 / 月配额, 读、提交、提交详情和报表都会按对应 item 检查并消费配额,余额不足返回表单模块业务错误。

条件显示、逻辑跳转与分步骤

字段的 validationJson.visibleWhen 支持对象或数组。数组按 AND 语义计算,全部满足时字段可见。单个条件支持:

字段说明
fieldKey / field / key依赖的字段 Key
operator / opeqneinnot_incontainsnot_containsemptynot_empty
value / values / equals期望值,支持标量或数组

示例:

json
{
  "visibleWhen": {
    "fieldKey": "customerType",
    "operator": "eq",
    "value": "company"
  },
  "step": 2,
  "stepTitle": "企业资料"
}

App /form/public 和 PC /forms/<channelCode> 会按同一规则实时显示字段,并把可见字段按 step 分组显示上一步 / 下一步。 当某一步的字段因条件隐藏后,公开填写页会自动跳过该步骤,形成逻辑跳转体验。服务端仍以发布版本规则为准,前端隐藏或本地篡改不能绕过校验。

文件题型与内容安全

file 题型答案保存为资源引用快照,不在 Form 模块复制二进制文件或 Core File 文件状态。公开填写页上传文件时复用当前用户文件上传接口, 上传成功后提交形态如下:

json
{
  "resume": {
    "resourceId": 1001,
    "fileId": "FILE-1001",
    "name": "resume.pdf",
    "size": 128000,
    "contentType": "application/pdf",
    "extension": "pdf"
  }
}

validationJson.maxFiles 大于 1 时,同一字段提交上述对象数组;服务端只保留 resourceIdfileIdresourceCodeurlnameoriginalFilenamesizecontentTypeextensionpathdiskbucketvisibilityhash 等可审计快照字段,其他客户端附加字段会被丢弃。没有登录态的公开渠道仍可由业务自有入口提交已存在的 resourceCodeurl 引用;三端内置上传入口面向有当前用户文件权限的场景。

text / textarea 题型默认接入 Content Safety Provider。检测结果为 reject 时拒绝提交,检测结果为 review 时提交保存为 under_review,普通安全结果保存为 submitted。若当前应用未装配 Content Safety Manager,则检测步骤会跳过,不影响表单提交。

通知、审批与 Webhook

当公开提交进入 under_review 时,Form 会注册并使用审批流定义 form.submission.review;后台可对提交执行通过或拒绝,提交会记录 reviewedByreviewedAtreviewActionreviewRemark,状态转为 approvedrejected。若提交者是登录用户,审核结果会尽力发送站内通知;通知发送异常不会回滚审核结果。

Webhook 端点保存在 form_webhook_endpoint,投递快照保存在 form_webhook_delivery。每次提交创建、进入待审核、审核通过或审核拒绝时,Form 会为匹配事件的启用端点写入本地投递快照,并把同一 payload 入 Runtime Outbox 的 consumer=webhook,由 Runtime Webhook 投递器负责 HTTP 发送和系统级重试。后台“重试”会把本地投递重新排队并再次入 Runtime Outbox;若 Outbox 不可用或入队失败,本地投递会保留失败状态和错误摘要,便于运营排障。 端点可启用 HMAC-SHA256 签名;签名密钥只在创建 / 更新时写入,不会在接口响应中回显,后台只展示脱敏值。启用签名时服务端会要求端点存在密钥, 并把 webhook.signing-enabledwebhook.secret 和端点标识写入 Runtime Outbox headers,由 Runtime Webhook 投递器生成标准签名头。

当前支持事件:

  • form.submission.created
  • form.submission.review-required
  • form.submission.approved
  • form.submission.rejected
  • form.webhook.test(测试投递)

后台提交查询

后台提交查询 API 路径前缀为 /api/v1/admin/forms/submissions

方法路径说明
GET/api/v1/admin/forms/submissions分页查询提交记录,可按表单、渠道、提交编号、状态、提交者、追踪标识和提交时间筛选
GET/api/v1/admin/forms/submissions/report按提交筛选条件生成统计报表快照,返回状态、渠道、每日趋势和字段答案分布
GET/api/v1/admin/forms/submissions/{id}查询提交主记录、答案 JSON 和字段答案快照
POST/api/v1/admin/forms/submissions/{id}/approve审核通过提交,写入审核动作、备注和审核人
POST/api/v1/admin/forms/submissions/{id}/reject审核拒绝提交,写入审核动作、备注和审核人
POST/api/v1/admin/forms/submissions/export按提交列表筛选条件创建 CSV 异步导出任务,产物只包含提交元数据,不导出答案 JSON

提交查询、报表和导出复用 form:view 权限,审核操作复用 form:update 权限。分页 / 详情接口返回提交编号、表单 / 版本 / 渠道、提交者、状态、 答案 JSON、字段数量、字段答案快照、客户端追踪标识和提交时间;导出接口返回 Core Async Task 异步任务,任务完成后可在后台异步任务中心下载私有 CSV 文件。 CSV 不包含答案 JSON;公开提交响应仍不会暴露答案 JSON。

报表接口即时生成 FR- 前缀的快照编号和生成时间,不额外持久化报表产物。后台页面复用提交列表筛选条件展示提交总数、审核中数量、 登录用户数、渠道数量、字段数量、状态占比、渠道占比、每日趋势、字段已答 / 空值、选项分布和数字 / 评分均值。

公开填写页:

  • App:/form/public?channelCode=<channelCode>
  • PC:/forms/<channelCode>

两端都会调用真实公开预览和提交 API,不使用本地伪造数据。

Webhook 管理

Webhook 管理 API 路径挂在表单定义下:

方法路径说明
GET/api/v1/admin/forms/definitions/{formId}/webhook-endpoints查询表单 Webhook 端点
POST/api/v1/admin/forms/definitions/{formId}/webhook-endpoints创建 Webhook 端点
PUT/api/v1/admin/forms/definitions/{formId}/webhook-endpoints/{endpointId}修改端点名称、目标地址、订阅事件、状态、最大尝试次数和签名配置
DELETE/api/v1/admin/forms/definitions/{formId}/webhook-endpoints/{endpointId}删除端点
POST/api/v1/admin/forms/definitions/{formId}/webhook-endpoints/{endpointId}/test创建测试投递并入 Runtime Outbox
GET/api/v1/admin/forms/webhook-deliveries分页查询投递快照,可按表单、端点、提交、事件、状态和 traceId 筛选
POST/api/v1/admin/forms/webhook-deliveries/{deliveryId}/retry将投递重新排队并再次入 Runtime Outbox

端点请求支持 signingEnabledsigningSecretclearSigningSecretsigningSecret 是 write-only 字段,响应只返回 signingSecretMasked;启用签名但没有新旧密钥时会拒绝保存,避免生成无效签名配置。端点状态支持 activedisabled;投递状态支持 pendingsucceededfailed。Admin 表单详情页已提供端点维护、签名密钥轮换 / 清空、测试投递、投递查询和人工重试入口。

配置

配置前缀:spring.open.form

配置项默认值说明
enabledtrue是否启用表单平台 API
max-fields-per-form200单个表单默认允许配置的最大字段数
max-submission-payload-bytes262144单次提交 payload 默认最大字节数
public-access-enabledfalse是否允许公开表单入口被直接访问

这些配置已由 FormServiceProvider 声明到 lifecycle 配置 schema,后台配置中心可识别表单启停、字段数、提交 payload 和公开入口开关;未接入配置中心的部署仍可通过 application.yml / 环境变量配置。

后续

当前运营视图已覆盖提交列表、报表快照、字段答案分布、异步导出、审核、模板、协作者和 Webhook 投递。后续只有在出现新的明确运营视角时,再按多表单看板、漏斗分析或跨渠道转化等独立功能簇扩展。

Released under the Apache License 2.0.