diff --git a/docs/superpowers/specs/2026-07-30-oa-confirmation-kit-design.md b/docs/superpowers/specs/2026-07-30-oa-confirmation-kit-design.md new file mode 100644 index 00000000..07378173 --- /dev/null +++ b/docs/superpowers/specs/2026-07-30-oa-confirmation-kit-design.md @@ -0,0 +1,367 @@ +# OA 表单设计 · 转正套件(Confirmation Kit)设计文档 + +**日期**: 2026-07-30 +**状态**: 待实现 +**范围**: `web-admin` 用户管理主数据补齐;审批设计器「套件」Tab 转正套件;发起/详情运行时默认与联动;`backend` 审批通过后回写发起人 `sys_user` +**界面模式**: 对齐请假套件与 FormCreate 控件库;视觉 Fresh Mint +**前置**: +- `docs/superpowers/specs/2026-07-30-oa-leave-kit-design.md`(套件组装体模式) +- `docs/superpowers/specs/2026-07-30-oa-post-select-design.md`(职位组件) +- `docs/superpowers/specs/2026-07-27-oa-dept-select-design.md`(部门组件) +- `docs/superpowers/specs/2026-07-30-oa-owner-fields-design.md`(发起人默认值模式可参考) + +--- + +## 1. 背景与目标 + +在「2 表单设计」套件 Tab 增加 **转正套件**:一键插入转正申请常用字段;发起时自动带出申请人主数据;审批全部通过后将约定字段回写发起人用户主数据。 + +### 1.1 目标 + +- `sys_user` 补齐 **入职日期**、**试用期(月数)**;用户管理可编辑。 +- 套件 Tab 可拖入 **转正套件**(容器 + 基础/业务子字段)。 +- 发起时默认填入申请人:入职日期、试用期、姓名、邮箱、部门、手机号;**职位不默认**。 +- 转正日期可由「入职日期 + 试用期月数」自动建议,可改;手改后断开联动(同请假时长)。 +- 子字段可在设计器配置 **显示/隐藏**;隐藏字段仍可带默认值,通过后仍可回写。 +- 审批 **全部通过** 后回写发起人主数据(见 §2)。 + +### 1.2 非目标(首期) + +- 职位、岗位职级、转正日期回写用户表。 +- 转正通过后的合同、账号状态、人事档案等副作用。 +- 代他人发起并回写他人主数据。 +- 一体复合控件(单 type 包对象值)——否决,采用与请假相同的组装体方案。 + +--- + +## 2. 产品决策(已确认) + +| 项 | 决策 | +|----|------| +| 实现方案 | 方案 1:套件容器 + 子字段拼装(非一体值对象) | +| 容器类型 | `confirmationKit` | +| 回写时机 | 审批 **全部通过** 后再回写 | +| 回写对象 | 始终 **发起人** 的 `sys_user` | +| 回写字段 | 入职日期、试用期月数、姓名、邮箱、部门、手机号 | +| 不回写 | 职位、岗位职级、转正日期(仅审批单) | +| 试用期存储 | 整数月数(如 `3`),界面展示「3 个月」 | +| 转正日期 | 有入职日 + 试用期时自动建议,仍可改 | +| 转正日联动 | 未手改则随入职/试用期重算;手改后断开;可「恢复自动计算」 | +| 职位默认 | **不默认**,发起人自选 | +| 职位选择 | 保持现有职位组件 **多选** | +| 部门控件 | 现有「部门」组件,值 `{ id, name }` | +| 显示/隐藏 | 设计器按子字段配置;隐藏不展示、不前端必填 | +| 隐藏与回写 | 隐藏字段若有默认/提交值,通过后 **仍回写**(设计器弱提示) | +| 回写空值 | 某字段为空则 **跳过该列**,不清空主数据 | +| 回写失败 | 校验失败 **不回滚** 审批通过状态,记日志/告警 | + +--- + +## 3. 信息模型 + +### 3.1 心智模型 + +``` +┌─ 转正套件(confirmationKit / 容器)────────────────────┐ +│ 套件属性:子字段显示/隐藏 │ +│ │ +│ · datePicker → 入职日期 ← 默认申请人 │ +│ · inputNumber → 试用期(月) ← 默认申请人 │ +│ · datePicker → 转正日期 ← 自动建议,可改 │ +│ · post → 职位 ← 不默认,多选 │ +│ · input → 岗位职级 ← 空 │ +│ · input → 姓名 ← 默认申请人 │ +│ · input → 邮箱 ← 默认申请人 │ +│ · dept → 部门 ← 默认主部门 │ +│ · input → 手机号 ← 默认申请人 │ +└─────────────────────────────────────────────────────────┘ +``` + +- 选中 **套件容器**:配置显示字段等套件属性;运行时/发起页 **不** 额外渲染套件边框或标题。 +- 选中 **子字段**:标题、必填、校验等与普通组件相同。 + +### 3.2 子字段约定 + +| 语义 key | 建议 title | type | 必填默认 | 回写 | +|----------|------------|------|----------|------| +| `hireDate` | 入职日期 | `datePicker`(仅日期,`YYYY-MM-DD`) | 是 | ✓ → `hire_date` | +| `probationMonths` | 试用期(月) | `inputNumber`,整数 ≥0 | 是 | ✓ → `probation_months` | +| `confirmationDate` | 转正日期 | `datePicker`(仅日期) | 否 | ✗ | +| `post` | 职位 | `post`(现有,多选) | 否 | ✗ | +| `jobLevel` | 岗位职级 | `input` | 否 | ✗ | +| `name` | 姓名 | `input` | 是 | ✓ → `nick_name` | +| `email` | 邮箱 | `input` | 否 | ✓ → `email` | +| `dept` | 部门 | `dept`(现有,建议单选) | 是 | ✓ → `dept_id` ← `id` | +| `phone` | 手机号 | `input` | 否 | ✓ → `phone_no` | + +插入时子字段 `field` 生成唯一 ID(同表单可多套,自动去重后缀)。语义识别优先稳定标记(如 `props._fc_kit_field: 'hireDate'`),避免仅依赖 title。 + +### 3.3 Schema 结构(示意) + +```json +{ + "type": "confirmationKit", + "props": { + "visibleFields": { + "hireDate": true, + "probationMonths": true, + "confirmationDate": true, + "post": true, + "jobLevel": true, + "name": true, + "email": true, + "dept": true, + "phone": true + } + }, + "children": [ + { + "type": "datePicker", + "field": "hireDate_xxxx", + "title": "入职日期", + "props": { + "_fc_kit_field": "hireDate", + "type": "date", + "valueFormat": "YYYY-MM-DD" + }, + "$required": true + }, + { + "type": "inputNumber", + "field": "probationMonths_xxxx", + "title": "试用期(月)", + "props": { "_fc_kit_field": "probationMonths", "min": 0, "precision": 0 }, + "$required": true + }, + { + "type": "datePicker", + "field": "confirmationDate_xxxx", + "title": "转正日期", + "props": { + "_fc_kit_field": "confirmationDate", + "type": "date", + "valueFormat": "YYYY-MM-DD", + "confirmationDateManual": false + }, + "$required": false + }, + { + "type": "post", + "field": "post_xxxx", + "title": "职位", + "props": { "_fc_kit_field": "post", "multiple": true } + }, + { + "type": "input", + "field": "jobLevel_xxxx", + "title": "岗位职级", + "props": { "_fc_kit_field": "jobLevel" } + }, + { + "type": "input", + "field": "name_xxxx", + "title": "姓名", + "props": { "_fc_kit_field": "name" }, + "$required": true + }, + { + "type": "input", + "field": "email_xxxx", + "title": "邮箱", + "props": { "_fc_kit_field": "email" } + }, + { + "type": "dept", + "field": "dept_xxxx", + "title": "部门", + "props": { "_fc_kit_field": "dept", "multiple": false }, + "$required": true + }, + { + "type": "input", + "field": "phone_xxxx", + "title": "手机号", + "props": { "_fc_kit_field": "phone" } + } + ] +} +``` + +容器为布局类,不计可填字段数;`visibleFields` 与子字段 `hidden` 保持同步(实现时二选一为主、另一派生即可,文档以「隐藏不展示」为准)。 + +### 3.4 值格式 + +| 字段 | 格式 | +|------|------| +| 日期 | `YYYY-MM-DD` | +| 试用期 | 整数 | +| 部门 | `{ id, name }`(全路径名称优先) | +| 职位 | 与现有 `post` 一致(多选为数组) | +| 姓名/邮箱/手机号/职级 | 字符串 | + +--- + +## 4. 用户主数据 + +### 4.1 `sys_user` 增补 + +| 列 | 类型 | 说明 | +|----|------|------| +| `hire_date` | `DATE` NULL | 入职日期 | +| `probation_months` | `INT` NULL | 试用期月数(≥0) | + +同步范围: + +- SQLAlchemy `SysUser`、CRUD `_row_to_dict`、Create/Update Body +- 用户管理新增/编辑弹窗可编辑;查看详情展示 +- `userInfo` / `_build_user_info_payload` 带出 `hireDate`、`probationMonths` +- `docs/sql/` 增量脚本;字段一致性检查 + +既有已具备且用户管理可编辑:`nick_name`(姓名)、`email`、`dept_id`、`phone_no`。 + +### 4.2 列表展示 + +- 编辑弹窗:**必须**可维护入职日期、试用期。 +- 列表列:可选窄列或仅详情展示(实现时最小为弹窗 + 详情即可)。 + +--- + +## 5. 运行时 + +### 5.1 发起默认值 + +仅对 **空字段** 写入(不覆盖用户已填): + +| 语义 | 来源 | +|------|------| +| hireDate | `userInfo.hireDate` | +| probationMonths | `userInfo.probationMonths` | +| confirmationDate | 入职 + 试用期可算则写入建议值;否则空 | +| post | **不填** | +| jobLevel | 空 | +| name | 显示名 / `nickName` | +| email | `email` | +| dept | 主部门 `{ id, name }`(全路径优先) | +| phone | `phoneNo` | + +- **隐藏字段**:仍按上表写入默认到 `formData`(便于通过后回写)。 +- **详情 / 已发起单**:只展示已存 `form_data`,不按当前查看者覆盖。 +- 设计器画布:不依赖设计者登录身份作为业务默认。 + +### 5.2 转正日期联动 + +- 算法:`confirmationDate = hireDate` 按自然月加 `probationMonths`(实现时与前端/后端同一工具函数,可单测)。 +- `confirmationDateManual === false`:入职日或试用期变化时重算并写回转正日。 +- 用户手改转正日 → `confirmationDateManual = true`,断开联动。 +- 提供「恢复自动计算」:清手动标记并立即重算。 + +### 5.3 显示 / 隐藏 + +- 隐藏:发起/详情不渲染;前端不校验必填。 +- 默认值仍可写入 `formData`。 +- 通过后:若该语义在回写名单且值非空,照常回写。 +- 设计器弱提示:「隐藏且属于回写字段时,审批通过仍可能更新用户主数据。」 + +### 5.4 提交校验(发起) + +- 仅校验 **可见** 且必填的子字段。 +- 隐藏字段不拦必填。 +- 发布白名单纳入 `confirmationKit`;套件子节点缺失 → **警告可发**(对齐请假)。 + +--- + +## 6. 审批通过后回写 + +### 6.1 触发 + +实例状态变为 **全部通过** 时调用: + +`apply_confirmation_kit_user_sync(db, instance)` + +挂在现有「通过」后置点,避免散落多 API。 + +### 6.2 流程 + +``` +实例 → 全部通过 + → 扫描 form schema 中 confirmationKit + → 解析 children 语义标记 → 从 form_data 取值 + → 目标用户 = 发起人 user_id + → 有值则更新对应列;空则跳过 + → 格式校验失败:记日志,不回滚通过状态 +``` + +### 6.3 映射 + +| 语义 | `sys_user` 列 | +|------|----------------| +| hireDate | `hire_date` | +| probationMonths | `probation_months` | +| name | `nick_name` | +| email | `email` | +| dept | `dept_id`(取 `id`) | +| phone | `phone_no` | + +不写:`post`、`jobLevel`、`confirmationDate`。 + +### 6.4 幂等与安全 + +- 有值才更新,避免空值清空。 +- 租户隔离:仅更新本租户发起人。 +- 不信任客户端另传「回写目标用户」。 +- 手机号/邮箱校验对齐用户管理规则。 + +--- + +## 7. 设计器 + +### 7.1 套件 Tab + +- 卡片标题:**转正**(与请假并列)。 +- 拖入插入容器 + 9 子字段。 + +### 7.2 容器属性 + +- 「显示字段」九项勾选(默认全开),同步子字段 `hidden` / `visibleFields`。 +- 隐藏回写提示文案见 §5.3。 + +### 7.3 子字段属性 + +- 与普通同类型控件一致(含职位多选、部门单选)。 +- 转正日期可暴露「恢复自动计算」相关说明(运行时按钮也可挂在字段旁,实现时与请假时长 UX 对齐)。 + +--- + +## 8. 建议文件落点 + +| 区域 | 路径(示意) | +|------|----------------| +| 套件容器 | `web-admin/src/components/oa-approval/OaConfirmationKit.vue` | +| DragRule | `web-admin/src/utils/oa-confirmation-kit-rule.ts` | +| 注册 | 现有 kit 注册入口旁增加转正 | +| 默认值 + 联动 | `web-admin/src/utils/oa-confirmation-kit-runtime.ts` | +| 自然月加 | 前后端共享算法(前端 TS + 后端 Python,行为一致) | +| 用户管理 | `web-admin/src/views/system/user/index.vue` + API 类型 | +| 模型 / SQL | `SysUser` + `docs/sql/` 增量 | +| userInfo | `backend` `_build_user_info_payload` | +| 回写服务 | `backend/app/services/oa_confirmation_kit_sync.py` + 通过态钩子 | + +--- + +## 9. 验收标准 + +1. 用户管理可维护入职日期、试用期(月);`userInfo` 可带出。 +2. 套件 Tab 可拖「转正」;可隐藏部分字段。 +3. 发起:主数据六项自动填;职位为空;转正日可按规则建议;手改后断开,可恢复自动。 +4. 通过前主数据不变;通过后六项回写发起人;空字段不清空库。 +5. 隐藏字段的默认值在通过后仍可回写。 +6. 部门值为 `{id,name}`;职位为现有多选控件。 +7. 回写失败不撤销审批通过状态。 + +--- + +## 10. 开放实现细节(不阻塞产品) + +- 列表是否增加「入职日期」列:实现时最小范围可不加列。 +- 「恢复自动计算」是字段旁链接还是属性面板说明:对齐请假时长即可。 +- `visibleFields` 与子字段 `hidden` 以何者为 source of truth:实现选一并在 runtime 归一化。