import { pinyin } from 'pinyin-pro'; export interface Department { id: string; name: string; parentId?: string; order: number; createdAt: string; } export interface Member { id: string; name: string; username?: string; departmentId: string; roleId: string; phone: string; email: string; password: string; createdAt: string; isSystem?: boolean; } export interface RoleItem { id: string; name: string; description?: string; createdAt: string; isSystem?: boolean; permissions: string[]; } export interface PasswordRule { prefix: string; length: number; includeUppercase: boolean; includeLowercase: boolean; includeNumbers: boolean; includeSpecial: boolean; } export const DEFAULT_PASSWORD_RULE: PasswordRule = { prefix: 'Ftb', length: 8, includeUppercase: true, includeLowercase: true, includeNumbers: true, includeSpecial: false, }; export function generatePassword(rule: PasswordRule): string { let chars = ''; if (rule.includeUppercase) chars += 'ABCDEFGHJKLMNPQRSTUVWXYZ'; if (rule.includeLowercase) chars += 'abcdefghjkmnpqrstuvwxyz'; if (rule.includeNumbers) chars += '23456789'; if (rule.includeSpecial) chars += '!@#$%&*'; if (!chars) chars = 'abcdefghjkmnpqrstuvwxyz23456789'; const randomLength = Math.max(rule.length - rule.prefix.length, 4); let result = rule.prefix; for (let i = 0; i < randomLength; i++) { result += chars[Math.floor(Math.random() * chars.length)]; } return result; } export function maskPhone(phone: string): string { if (!phone || phone.length < 7) return phone; return phone.slice(0, 3) + '****' + phone.slice(7); } export function normalizeUsername(username: string | undefined | null): string { return (username ?? '').trim().replace(/\s+/g, '').toLowerCase(); } function nameToUsernameBase(name: string): string { const raw = name.trim(); if (!raw) return 'USER'; const letters = pinyin(raw, { toneType: 'none', type: 'array' }) .join('') .replace(/[^a-zA-Z0-9]/g, '') .toLowerCase(); return letters || 'USER'; } function duplicateSuffix(attempt: number): string { return attempt === 1 ? '1' : `${'0'.repeat(attempt - 1)}1`; } function uniqueUsernameFromBase(base: string, existingUsernames: Iterable): string { const normalizedBase = normalizeUsername(base) || 'USER'; const used = new Set(Array.from(existingUsernames, normalizeUsername).filter(Boolean)); if (!used.has(normalizedBase)) return normalizedBase; for (let attempt = 1; attempt < 1000; attempt += 1) { const candidate = `${normalizedBase}${duplicateSuffix(attempt)}`; if (!used.has(candidate)) return candidate; } return `${normalizedBase}${Date.now()}`; } export function generateUsernameFromName( name: string, existingUsernames: Iterable, currentUsername?: string, ): string { const normalizedCurrent = normalizeUsername(currentUsername); if (normalizedCurrent) return normalizedCurrent; return uniqueUsernameFromBase(nameToUsernameBase(name), existingUsernames); } export function ensureMemberUsernames(members: Member[]): { members: Member[]; changed: boolean } { const used = new Set(); let changed = false; const normalizedMembers = members.map((member) => { const requested = normalizeUsername(member.username); const username = requested ? uniqueUsernameFromBase(requested, used) : generateUsernameFromName(member.name, used); used.add(username); if (member.username === username) return member; changed = true; return { ...member, username }; }); return { members: normalizedMembers, changed }; }