4.6 KiB
Flutter Schema 驱动表单卡片
目标
Flutter 页面不为每种 OA 表单重复编写固定控件。服务端返回版本化的 JSON Schema 和 UI Schema,客户端从受控组件目录中自动生成移动表单卡片。
表单定义 API
├─ JSON Schema:字段、类型、必填、枚举和数据约束
└─ UI Schema:分组、顺序、标签、控件和移动端提示
↓
Flutter Schema 解析
↓
白名单控件渲染器
↓
Riverpod 草稿状态与本地校验
↓
用户确认卡片
↓
后端再次校验并执行写操作
已实现控件
text:单行文本textArea:多行文本select:受控枚举选择dateTime:日期和时间选择
未知字段或未知控件不能执行动态代码,只显示安全错误提示。后续控件必须通过客户端版本发布进入白名单,例如人员选择、部门选择、附件、金额、定位和明细表格。
AI 边界
AI 可以根据自然语言生成表单字段建议值,但不能:
- 生成或执行 Dart、JavaScript 等客户端代码。
- 绕过 Schema 枚举和校验规则。
- 绕过用户确认直接提交。
- 自行增加后端未定义的字段。
- 修改隐藏的租户、申请人、审批人或权限字段。
客户端校验用于交互体验,Kotlin 后端仍必须重新执行权限、数据、状态、幂等和业务规则校验。
当前演示
工作台点击“发起请假”后,请假类型、开始时间、结束时间和原因均由 Schema 渲染。点击“AI 自动填写”只更新草稿状态,点击“检查并确认”后才展示最终确认卡片。
客户端优先从受保护的 /api/v1/form-definitions/leave-request 加载最新定义,成功后写入本地缓存。网络或认证暂不可用时优先读取最近一次有效缓存;首次离线则使用随客户端发布并经过测试的安全内置定义。运行时可通过 --dart-define=AIOA_API_BASE_URL=... 和 --dart-define=AIOA_ACCESS_TOKEN=... 配置 API 与开发令牌。
未完成的表单值会在每次字段修改或应用 AI 建议后自动写入本机。再次进入页面时自动恢复并显示来源提示;损坏或不兼容的本地草稿会被隔离清除,避免阻断表单页面。用户可显式清除草稿,后续真实提交成功后也必须清除本地副本。
用户在确认卡片中确认后,客户端调用后端草稿创建 API。请求发出前会持久化请求体与 Idempotency-Key;断网或服务端暂时不可用时保留待重试记录,同一份表单再次提交会复用原幂等键,后端成功响应后才清除待重试记录和本地表单草稿。
附件内容不进入 PostgreSQL 或 Flowable 变量。后端为本人草稿创建 MinIO 短期预签名上传任务,客户端直传后通知后端完成;后端重新校验对象大小和 MIME 类型,再将元数据状态改为 READY。下载每次重新鉴权并生成短期 URL,上传、完成、下载和删除均写入审计记录。
请假提交成功后为部门主管生成站内待办通知;流程最终批准或驳回后为申请人生成结果通知。Flutter“待办”页包含通知列表和未读数量,点击未读通知后调用后端标记已读。推送仅作为后续提醒通道,权威通知内容与已读状态保存在 PostgreSQL。
主管在 Flutter 待办卡片中查看请假类型、时间与原因,并输入审批意见后批准或驳回。审批请求在本机持久化请求体和幂等键;弱网重试复用同一 Idempotency-Key,成功后从列表移除当前任务。
员工可从工作台进入“我的请假申请”,查看草稿和历史申请。草稿可以提交到 Flowable,待审批申请可以撤回;详情页展示权威状态和审批事件时间线。提交与撤回均持久化幂等键,弱网重试不会重复启动或取消流程。
自然语言草稿使用千问 qwen-plus:Flutter 只调用受保护的 Kotlin API,Kotlin 记录模型名、提示长度和澄清项数量后调用 Python AI 服务,Python 才持有供应商密钥。模型输出必须通过字段白名单、请假类型枚举、ISO-8601 时区、时间范围和文本长度校验;建议只填入本地草稿,仍需用户确认并由 Kotlin 业务接口重新校验。
Flutter 使用 Keycloak Authorization Code + PKCE 登录。Access Token、Refresh Token 和 ID Token 保存在系统安全存储中,访问令牌在过期前自动刷新;统一 HTTP 客户端只向 Kotlin API Origin 注入 Bearer Token,不会把令牌发送给 MinIO 预签名地址或千问。退出时调用 OIDC End Session 并清除本机凭据。