feat: 新增客服备用金台账与后台用户中文显示名
- 新增备用金账户、流水、支出、对账与权限 - 后台用户支持中文显示名并同步备用金姓名 - 审计、登录记录、任务及工单操作人优先展示中文名 - 补充备用金与人工支出方案文档
This commit is contained in:
@@ -28,6 +28,7 @@ export type AdminSession = {
|
||||
sessionId: string
|
||||
userId: number
|
||||
username: string
|
||||
displayName: string
|
||||
role: AdminRole
|
||||
expiresAt: string
|
||||
sessionVersion: number
|
||||
@@ -48,6 +49,7 @@ export async function ensureAdminUsersBootstrapped(): Promise<void> {
|
||||
const username = String(configuredUser?.username || '')
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
const displayName = normalizeDisplayName(configuredUser?.displayName, username) || username
|
||||
const password = String(configuredUser?.password || '').trim()
|
||||
const role = normalizeAdminRole(configuredUser?.role)
|
||||
|
||||
@@ -62,6 +64,7 @@ export async function ensureAdminUsersBootstrapped(): Promise<void> {
|
||||
const now = nowIso()
|
||||
await createAdminUser({
|
||||
username,
|
||||
displayName,
|
||||
passwordHash: hashAdminPassword(password),
|
||||
role,
|
||||
status: 'active',
|
||||
@@ -229,6 +232,7 @@ export async function verifyAdminSessionToken(token: unknown): Promise<AdminSess
|
||||
sessionId,
|
||||
userId: Number(user.id),
|
||||
username: String(user.username || ''),
|
||||
displayName: String(user.display_name || user.username || ''),
|
||||
role: normalizeAdminRole(user.role),
|
||||
expiresAt,
|
||||
sessionVersion: currentSessionVersion,
|
||||
@@ -251,6 +255,7 @@ export async function getAdminSessionSummary(token: unknown): Promise<JsonObject
|
||||
user: {
|
||||
userId: session.userId,
|
||||
username: session.username,
|
||||
displayName: session.displayName,
|
||||
role: session.role,
|
||||
permissions: session.permissions,
|
||||
},
|
||||
@@ -277,7 +282,7 @@ export async function getAdminUserList(query: JsonObject = {}): Promise<JsonObje
|
||||
const { items, total } = await listAdminUsers({
|
||||
page,
|
||||
pageSize,
|
||||
username: String(query.username || '').trim(),
|
||||
keyword: String(query.keyword || query.username || '').trim(),
|
||||
role: normalizeRoleQuery(query.role),
|
||||
status: normalizeStatusQuery(query.status),
|
||||
})
|
||||
@@ -290,6 +295,7 @@ export async function getAdminUserList(query: JsonObject = {}): Promise<JsonObje
|
||||
|
||||
export async function createManagedAdminUser(payload: JsonObject = {}): Promise<JsonObject> {
|
||||
const username = normalizeUsername(payload.username)
|
||||
const displayName = normalizeDisplayName(payload.displayName, username) || username
|
||||
const password = normalizePassword(payload.password)
|
||||
|
||||
if (!username) {
|
||||
@@ -319,6 +325,7 @@ export async function createManagedAdminUser(payload: JsonObject = {}): Promise<
|
||||
const now = nowIso()
|
||||
const created = await createAdminUser({
|
||||
username,
|
||||
displayName,
|
||||
passwordHash: hashAdminPassword(password),
|
||||
role: normalizeAdminRole(payload.role),
|
||||
status: normalizeAdminUserStatus(payload.status || 'active'),
|
||||
@@ -401,6 +408,28 @@ export async function updateManagedAdminUserStatus(
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateManagedAdminUserDisplayName(
|
||||
userId: number | string,
|
||||
payload: JsonObject = {},
|
||||
): Promise<JsonObject> {
|
||||
const user = await getRequiredAdminUser(userId)
|
||||
const displayName = normalizeDisplayName(payload.displayName, user.username) || user.username
|
||||
const updated = await updateAdminUser(user.id, {
|
||||
display_name: displayName,
|
||||
updated_at: nowIso(),
|
||||
})
|
||||
if (!updated) {
|
||||
throw createHttpError('后台用户更新失败', {
|
||||
statusCode: 500,
|
||||
errorCode: 'admin_user_update_failed',
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
user: mapAdminUser(updated),
|
||||
}
|
||||
}
|
||||
|
||||
export async function resetManagedAdminUserPassword(
|
||||
userId: number | string,
|
||||
payload: JsonObject = {},
|
||||
@@ -472,6 +501,7 @@ function createAdminSession(user: AdminUserRow): JsonObject {
|
||||
user: {
|
||||
userId: Number(user.id),
|
||||
username: String(user.username || ''),
|
||||
displayName: String(user.display_name || user.username || ''),
|
||||
role: normalizeAdminRole(user.role),
|
||||
},
|
||||
}
|
||||
@@ -559,6 +589,27 @@ function normalizeUsername(username: unknown): string {
|
||||
.toLowerCase()
|
||||
}
|
||||
|
||||
function normalizeDisplayName(displayName: unknown, fallback = ''): string {
|
||||
const normalized = String(displayName ?? '')
|
||||
.trim()
|
||||
.replace(/\s+/g, ' ')
|
||||
if (!normalized) {
|
||||
return fallback
|
||||
}
|
||||
const characters = Array.from(normalized)
|
||||
const hasControlCharacter = characters.some((character) => {
|
||||
const codePoint = character.codePointAt(0) || 0
|
||||
return codePoint <= 0x1f || codePoint === 0x7f
|
||||
})
|
||||
if (characters.length > 32 || hasControlCharacter) {
|
||||
throw createHttpError('姓名格式无效,最多 32 个字符且不能包含控制字符', {
|
||||
statusCode: 400,
|
||||
errorCode: 'admin_user_display_name_invalid',
|
||||
})
|
||||
}
|
||||
return normalized
|
||||
}
|
||||
|
||||
function normalizePassword(password: unknown): string {
|
||||
return String(password || '').trim()
|
||||
}
|
||||
@@ -634,6 +685,7 @@ function mapAdminUser(user: AdminUserRow): JsonObject {
|
||||
return {
|
||||
userId: Number(user.id),
|
||||
username: String(user.username || ''),
|
||||
displayName: String(user.display_name || user.username || ''),
|
||||
role: normalizeAdminRole(user.role),
|
||||
status: normalizeAdminUserStatus(user.status),
|
||||
createdAt: user.created_at,
|
||||
|
||||
Reference in New Issue
Block a user