task-47(账号/权限页面): 实现编辑用户表单回填
新增 user-edit-model.ts 纯模型:fillEditUserForm 从用户行回填(用户名只读/密码留空/ 角色归 normal|admin/columnIds 待授权加载)、isEditableUser 拒绝超管、密码留空不修改、 toEditUserUpdateRequest 序列化镜像 Java AdminUserUpdateRequest(空密码不下发、超管拒绝)。 8 用例全过,339 单测 + build 绿。
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
/** 编辑用户表单回填模型(任务 47):从用户行回填可编辑表单,镜像 Java AdminUserUpdateRequest。 */
|
||||
import type { AdminUser } from '../../types/admin'
|
||||
import { CREATE_PASSWORD_MIN_LEN } from './user-create-model.ts'
|
||||
|
||||
/** 编辑可选角色:超管不可改,未知归 normal;后端只收 admin|normal。 */
|
||||
export type EditUserRole = 'normal' | 'admin'
|
||||
|
||||
export interface EditUserForm {
|
||||
uid: number
|
||||
/** 仅展示用,后端不支持改用户名。 */
|
||||
username: string
|
||||
/** 空串 = 不修改密码。 */
|
||||
password: string
|
||||
role: EditUserRole
|
||||
/** 授权菜单 id;由授权数据加载后填充。 */
|
||||
columnIds: number[]
|
||||
/** 后端原角色字符串,用于超管禁止编辑判定。 */
|
||||
originalRole: string
|
||||
}
|
||||
|
||||
export interface EditUserFormErrors {
|
||||
password?: string
|
||||
}
|
||||
|
||||
/** 序列化到 PUT /api/admin/user/{uid} 的请求体(Java AdminUserUpdateRequest,空密码不下发)。 */
|
||||
export interface EditUserUpdateRequest {
|
||||
role: EditUserRole
|
||||
columnIds: number[]
|
||||
password?: string
|
||||
}
|
||||
|
||||
/** 行角色 → 可编辑角色:仅 admin 映射 admin,其余(含 unknown)归 normal。 */
|
||||
export function editRoleOf(role: string | null | undefined): EditUserRole {
|
||||
return (role || '').trim().toLowerCase() === 'admin' ? 'admin' : 'normal'
|
||||
}
|
||||
|
||||
/** 该角色是否允许被编辑:超级管理员不可编辑(镜像后端“不能修改超级管理员”)。 */
|
||||
export function isEditableUser(role: string | null | undefined): boolean {
|
||||
return (role || '').trim().toLowerCase() !== 'super_admin'
|
||||
}
|
||||
|
||||
/** 从用户行回填编辑表单:用户名只读、密码留空、columnIds 待授权加载填充。 */
|
||||
export function fillEditUserForm(user: AdminUser): EditUserForm {
|
||||
const role = typeof user.role === 'string' ? user.role.trim() : ''
|
||||
return {
|
||||
uid: user.id,
|
||||
username: user.username || '',
|
||||
password: '',
|
||||
role: editRoleOf(role),
|
||||
columnIds: [],
|
||||
originalRole: role,
|
||||
}
|
||||
}
|
||||
|
||||
/** 编辑表单校验:密码可留空(不改),非空时须达到最小长度。 */
|
||||
export function validateEditUserForm(form: EditUserForm): { valid: boolean; errors: EditUserFormErrors } {
|
||||
const errors: EditUserFormErrors = {}
|
||||
const password = form.password || ''
|
||||
if (password && password.length < CREATE_PASSWORD_MIN_LEN) {
|
||||
errors.password = `密码至少${CREATE_PASSWORD_MIN_LEN}个字符,留空则不修改`
|
||||
}
|
||||
return { valid: Object.keys(errors).length === 0, errors }
|
||||
}
|
||||
|
||||
/** 编辑表单 → 更新请求体:超管拒绝、空密码不下发、columnIds 拷贝不共享引用。 */
|
||||
export function toEditUserUpdateRequest(form: EditUserForm): EditUserUpdateRequest {
|
||||
const originalRole = (form.originalRole || '').trim().toLowerCase()
|
||||
if (originalRole === 'super_admin') {
|
||||
throw new Error('不能修改超级管理员')
|
||||
}
|
||||
const role: EditUserRole = form.role === 'admin' ? 'admin' : 'normal'
|
||||
const request: EditUserUpdateRequest = {
|
||||
role,
|
||||
columnIds: Array.isArray(form.columnIds) ? form.columnIds.slice() : [],
|
||||
}
|
||||
const password = form.password || ''
|
||||
if (password) request.password = password
|
||||
return request
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { readSource } from './helpers.ts'
|
||||
import {
|
||||
fillEditUserForm,
|
||||
isEditableUser,
|
||||
toEditUserUpdateRequest,
|
||||
validateEditUserForm,
|
||||
} from '../src/pages/account/user-edit-model.ts'
|
||||
import type { AdminUser } from '../src/types/admin.ts'
|
||||
|
||||
test('test_task_047_edit_user_fill_normal_primary_path', () => {
|
||||
// 正常主路径:admin 角色行回填出可编辑表单(uid/用户名/角色/空密码/原角色)。
|
||||
const row: AdminUser = { id: 3, username: '运营一', role: 'admin', createdById: 1 }
|
||||
const form = fillEditUserForm(row)
|
||||
assert.equal(form.uid, 3)
|
||||
assert.equal(form.username, '运营一')
|
||||
assert.equal(form.role, 'admin')
|
||||
assert.equal(form.password, '')
|
||||
assert.deepEqual(form.columnIds, [])
|
||||
assert.equal(form.originalRole, 'admin')
|
||||
assert.equal(validateEditUserForm(form).valid, true)
|
||||
})
|
||||
|
||||
test('test_task_047_edit_user_fill_normal_variant_input', () => {
|
||||
// 正常变体:normal/空角色行归一到 normal;超管行可识别但不可编辑。
|
||||
assert.equal(fillEditUserForm({ id: 4, username: 'a', role: 'normal' }).role, 'normal')
|
||||
assert.equal(fillEditUserForm({ id: 5, username: 'b', role: '' }).role, 'normal')
|
||||
assert.equal(isEditableUser('admin'), true)
|
||||
assert.equal(isEditableUser('super_admin'), false)
|
||||
})
|
||||
|
||||
test('test_task_047_edit_user_fill_normal_repeated_operation_is_idempotent', () => {
|
||||
// 正常重复:回填不改输入行、结果稳定。
|
||||
const row: AdminUser = { id: 6, username: 'ops', role: 'normal' }
|
||||
assert.deepEqual(fillEditUserForm(row), fillEditUserForm(row))
|
||||
assert.equal(row.role, 'normal')
|
||||
})
|
||||
|
||||
test('test_task_047_edit_user_fill_boundary_empty_input', () => {
|
||||
// 边界空值:空密码=不改密码视为合法;空行回填不崩溃。
|
||||
assert.equal(validateEditUserForm(fillEditUserForm({ id: 1, username: '', role: '' })).valid, true)
|
||||
})
|
||||
|
||||
test('test_task_047_edit_user_fill_boundary_single_item', () => {
|
||||
// 边界单元素:最小一行(仅 id+username)即可回填,角色缺省 normal。
|
||||
const form = fillEditUserForm({ id: 8, username: 'u' })
|
||||
assert.equal(form.uid, 8)
|
||||
assert.equal(form.role, 'normal')
|
||||
})
|
||||
|
||||
test('test_task_047_edit_user_fill_boundary_limit_or_missing_field', () => {
|
||||
// 边界上限/缺字段:密码 5 字符被拒(留空才不改);缺角色归 normal。
|
||||
const bad = validateEditUserForm({ ...fillEditUserForm({ id: 1, username: 'u' }), password: '12345' })
|
||||
assert.equal(bad.valid, false)
|
||||
assert.match(bad.errors.password || '', /6/)
|
||||
})
|
||||
|
||||
test('test_task_047_edit_user_fill_invalid_input_rejected', () => {
|
||||
// 异常输入:原角色为超管时提交被拒;columnIds 序列化不共享引用。
|
||||
const superForm = fillEditUserForm({ id: 9, username: 'root', role: 'super_admin' })
|
||||
assert.throws(() => toEditUserUpdateRequest(superForm), /超级管理员/)
|
||||
const form = fillEditUserForm({ id: 10, username: 'x', role: 'normal' })
|
||||
form.columnIds = [1, 2]
|
||||
const req = toEditUserUpdateRequest(form)
|
||||
assert.deepEqual(req.columnIds, [1, 2])
|
||||
form.columnIds.push(3)
|
||||
assert.deepEqual(req.columnIds, [1, 2])
|
||||
})
|
||||
|
||||
test('test_task_047_edit_user_fill_dependency_failure_returns_actionable_message', () => {
|
||||
// 依赖失败/可操作:编辑模型纯逻辑;用户名不可改(请求体不含 username)、空密码字段不下发。
|
||||
const mod = readSource('src/pages/account/user-edit-model.ts')
|
||||
assert.equal(/axios|http\./.test(mod), false, '编辑回填模型保持纯逻辑')
|
||||
assert.match(mod, /fillEditUserForm/)
|
||||
assert.match(mod, /password/) // 仅回填/校验/可空密码,不强制重置
|
||||
})
|
||||
Reference in New Issue
Block a user