tuqin
2 天以前 397218a689b159fa8018c249807326b195576f16
fix(hrm): 修复新增调岗申请报 500(原部门/原岗位未回填)

问题:人力资源 → 调岗申请 → 新增调岗申请,提交报 500「系统异常」,
日志为 java.sql.SQLException: Field 'current_dept_id' doesn't have a default value。

根因(前端后端都有问题):
1. 库里 hrm_transfer_application 的 current_dept_id / current_post_id 是 NOT NULL 且无默认值,
但前端表单只有 currentDeptName / currentPostName 两个只读展示框,
从来不会提交 currentDeptId / currentPostId,插入必然失败。
2. 前端 form.vue 传了 employeeId,但后端 HrmTransferApplicationSaveReqVO 没有这个字段,被直接忽略。
3. 前端 onOpenChange 里写的是 employees[0](注释自称「暂时取第一个员工作为示例」),
调岗对象根本不对,不是用户想调的人。

改动:
后端
- SaveReqVO 新增 employeeId(被调岗员工)。
- createTransferApplication 重排逻辑:先按 employeeId(或 userId)查员工档案,
自动补全 userId / currentDeptId / currentPostId(post_ids 存 JSON 数组,取第一个),
补全后再做「目标部门/岗位不能与原部门/岗位相同」的校验
(原来校验在补全之前,补全出来的原部门会绕过校验)。
- 补全不到时抛明确业务错误,不再直接漏出 SQL 异常:
TRANSFER_APPLICATION_CURRENT_DEPT_REQUIRED / _CURRENT_POST_REQUIRED。
- RespVO 新增 employeeId,详情接口按 userId 反查员工档案回填,供前端修改时回显。

前端
- data.ts 新增「被调岗员工」下拉(ApiSelect,走 /hrm/employee/simple-list),
查看/修改场景禁用。
- form.vue 去掉 employees[0] 的示例逻辑,改为监听 employeeId 变化,
选中员工后带出原部门/原岗位名称(ID 由后端按档案补全)。
顺带移除不再需要的 currentEmployeeId。

验证(蓝羽 mom-lyzy 实测):
- 只传 employeeId 新增 → 成功,落库 user_id=345、current_dept_id=125、current_post_id=48,
与员工档案完全一致
- 目标部门选成原部门 → 正确拦截「调岗失败,原因:目标部门与原部门相同」
- 详情接口返回 employeeId=111、userId=345、currentDeptName=财务部
已修改2个文件
61 ■■■■■ 文件已修改
src/views/hrm/transfer/data.ts 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/transfer/modules/form.vue 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/transfer/data.ts
@@ -6,6 +6,7 @@
import { getDictOptions } from '#/packages/effects/hooks/src';
import { getDeptList } from '#/api/system/dept';
import { getEmployeeSimpleList } from '#/api/hrm/employee';
import { getSimplePostList } from '#/api/system/post';
import { handleTree } from '#/packages/utils/src';
@@ -24,6 +25,26 @@
      },
    },
    {
      // 被调岗员工:原部门/原岗位由后端按员工档案自动带出,前端只需要选人
      fieldName: 'employeeId',
      label: '被调岗员工',
      component: 'ApiSelect',
      componentProps: {
        api: getEmployeeSimpleList,
        labelField: 'name',
        valueField: 'id',
        placeholder: '请选择要调岗的员工',
        showSearch: true,
        filterOption: (input: string, option: any) =>
          String(option?.label ?? '')
            .toLowerCase()
            .includes(input.toLowerCase()),
        // 查看/修改时不允许更换被调岗人
        disabled: formType !== 'create',
      },
      rules: 'selectRequired',
    },
    {
      fieldName: 'currentDeptId',
      component: 'Input',
      dependencies: {
src/views/hrm/transfer/modules/form.vue
@@ -17,7 +17,15 @@
const emit = defineEmits(['success']);
const formType = ref<FormType>('create');
const currentEmployeeId = ref<number>();
// 员工简单列表做一次缓存,供「选中员工后带出原部门」使用
let employeeCache: any[] = [];
async function loadEmployees() {
  if (employeeCache.length === 0) {
    employeeCache = await getEmployeeSimpleList();
  }
  return employeeCache;
}
const isDetail = computed(() => formType.value === 'detail');
const getTitle = computed(() => {
@@ -39,6 +47,22 @@
  layout: 'horizontal',
  schema: [],
  showDefaultActions: false,
  // 选中「被调岗员工」后带出原部门/原岗位(仅用于展示;
  // 真正的 currentDeptId / currentPostId 由后端按员工档案补全,避免非空约束报错)
  handleValuesChange: async (values: any, fieldsChanged: string[]) => {
    if (!fieldsChanged.includes('employeeId')) {
      return;
    }
    const employees = await loadEmployees();
    const employee = employees.find((item: any) => item.id === values.employeeId);
    if (!employee) {
      return;
    }
    await formApi.setValues({
      currentDeptName: employee.deptName || '',
      currentPostName: employee.postName || '',
    });
  },
});
const [Modal, modalApi] = useVbenModal({
@@ -53,7 +77,6 @@
    }
    modalApi.lock();
    const data = (await formApi.getValues()) as HrmTransferApi.Transfer;
    data.employeeId = currentEmployeeId.value;
    try {
      if (data.id) {
        await updateTransfer(data);
@@ -77,23 +100,12 @@
    formApi.setDisabled(data.formType === 'detail');
    modalApi.setState({ showConfirmButton: data.formType !== 'detail' });
    if (!data?.id) {
      // 新增时,自动获取当前登录员工的部门和岗位信息
      const employees = await getEmployeeSimpleList();
      // TODO: 这里应该根据登录用户获取对应的员工信息,暂时取第一个员工作为示例
      const currentEmployee = employees[0];
      if (currentEmployee) {
        currentEmployeeId.value = currentEmployee.id;
        await formApi.setValues({
          currentDeptName: currentEmployee.deptName,
          currentPostName: currentEmployee.postName,
        });
      }
      // 新增:被调岗员工由用户在表单里选择,选中后自动带出原部门/原岗位
      return;
    }
    modalApi.lock();
    try {
      const transfer = await getTransfer(data.id);
      currentEmployeeId.value = transfer.employeeId;
      await formApi.setValues(transfer);
      if (transfer.attachmentList?.length) {
        await formApi.setFieldValue('blobIds', transfer.attachmentList.map(item => ({