'use client'; import { create } from 'zustand'; import type { Department, Member, RoleItem, PasswordRule } from '@/lib/members'; import { DEFAULT_PASSWORD_RULE, ensureMemberUsernames, generatePassword, generateUsernameFromName } from '@/lib/members'; import { SYSTEM_ADMIN_MEMBER, SYSTEM_ADMIN_ROLE_ID, canDeleteSystemMember, ensureSystemAdminMember, sanitizeSystemAdminPatch, } from '@/lib/member-system'; import { DEFAULT_ROLE_PERMISSIONS, DEFAULT_ROLE_PRESETS } from '@/lib/permissions'; import { mergePresetRolePermissions } from '@/lib/role-permission-migration'; import { loadServerData, saveServerData, SERVER_DATA_CACHE_MS } from '@/lib/server-data'; const PRESET_DEPARTMENTS: Department[] = [ { id: 'dept-1', name: '产品部', order: 1, createdAt: '2024-01-01' }, { id: 'dept-2', name: '技术部', order: 2, createdAt: '2024-01-01' }, { id: 'dept-2-1', name: '前端组', parentId: 'dept-2', order: 1, createdAt: '2024-01-01' }, { id: 'dept-2-2', name: '后端组', parentId: 'dept-2', order: 2, createdAt: '2024-01-01' }, { id: 'dept-2-3', name: '测试组', parentId: 'dept-2', order: 3, createdAt: '2024-01-01' }, { id: 'dept-3', name: '设计部', order: 3, createdAt: '2024-01-01' }, { id: 'dept-4', name: '运营部', order: 4, createdAt: '2024-01-01' }, ]; const PRESET_ROLES: RoleItem[] = DEFAULT_ROLE_PRESETS; const MOCK_MEMBERS: Member[] = [ SYSTEM_ADMIN_MEMBER, ]; let lastMembersFetchAt = 0; interface MemberState { departments: Department[]; members: Member[]; roles: RoleItem[]; passwordRule: PasswordRule; loading: boolean; loaded: boolean; fetchMembers: (options?: { force?: boolean }) => Promise; updatePasswordRule: (rule: PasswordRule) => void; createDepartment: (data: Omit) => void; updateDepartment: (id: string, data: Partial) => void; deleteDepartment: (id: string) => void; createMember: (data: Omit) => void; updateMember: (id: string, data: Partial) => Promise; deleteMember: (id: string) => void; createRole: (data: Omit) => void; updateRole: (id: string, data: Partial) => void; deleteRole: (id: string) => void; } async function saveStored(state: { departments: Department[]; members: Member[]; roles: RoleItem[]; passwordRule: PasswordRule }) { await saveServerData('members', state); } async function loadStored(): Promise<{ departments: Department[]; members: Member[]; roles: RoleItem[]; passwordRule: PasswordRule } | null> { try { return await loadServerData<{ departments: Department[]; members: Member[]; roles: RoleItem[]; passwordRule: PasswordRule }>('members'); } catch {} return null; } export const useMemberStore = create((set, get) => ({ departments: PRESET_DEPARTMENTS, members: MOCK_MEMBERS, roles: PRESET_ROLES, passwordRule: DEFAULT_PASSWORD_RULE, loading: false, loaded: false, fetchMembers: async (options) => { if (!options?.force && get().loaded && Date.now() - lastMembersFetchAt < SERVER_DATA_CACHE_MS) return; const cached = await loadStored(); if (!options?.force && get().loaded && Date.now() - lastMembersFetchAt < SERVER_DATA_CACHE_MS) return; lastMembersFetchAt = Date.now(); if (cached) { // 迁移:旧 RoleItem 没 permissions 字段,补默认值 const migratedRoles = cached.roles.map((r) => { if (!('permissions' in r) || !Array.isArray((r as any).permissions)) { return { ...r, permissions: DEFAULT_ROLE_PERMISSIONS[r.id] ?? [] }; } return r; }); const permissionMerged = mergePresetRolePermissions(migratedRoles); const rolesWithNewDefaults = permissionMerged.roles; const adminRole = PRESET_ROLES.find((r) => r.id === SYSTEM_ADMIN_ROLE_ID)!; const roles = rolesWithNewDefaults.some((r) => r.id === SYSTEM_ADMIN_ROLE_ID) ? rolesWithNewDefaults.map((r) => r.id === SYSTEM_ADMIN_ROLE_ID ? { ...adminRole, ...r, isSystem: true, permissions: DEFAULT_ROLE_PERMISSIONS[SYSTEM_ADMIN_ROLE_ID] } : r) : [adminRole, ...rolesWithNewDefaults]; const rolesChanged = permissionMerged.changed || roles.length !== rolesWithNewDefaults.length || roles.some((role, index) => JSON.stringify(role) !== JSON.stringify(rolesWithNewDefaults[index])); const ensured = ensureSystemAdminMember(cached.members); const usernameEnsured = ensureMemberUsernames(ensured.members); const next = { departments: cached.departments, members: usernameEnsured.members, roles, passwordRule: cached.passwordRule || DEFAULT_PASSWORD_RULE, }; set({ ...next, loaded: true }); if (ensured.changed || usernameEnsured.changed || rolesChanged) void saveStored(next).catch(() => {}); } else { set({ loaded: true }); } }, updatePasswordRule: (rule) => { set({ passwordRule: rule }); void saveStored({ departments: get().departments, members: get().members, roles: get().roles, passwordRule: rule }).catch(() => {}); }, createDepartment: (data) => { const dept: Department = { ...data, id: `dept-${Date.now()}`, createdAt: new Date().toISOString().slice(0, 10) }; const departments = [...get().departments, dept]; set({ departments }); void saveStored({ departments, members: get().members, roles: get().roles, passwordRule: get().passwordRule }).catch(() => {}); }, updateDepartment: (id, data) => { const departments = get().departments.map((d) => d.id === id ? { ...d, ...data } : d); set({ departments }); void saveStored({ departments, members: get().members, roles: get().roles, passwordRule: get().passwordRule }).catch(() => {}); }, deleteDepartment: (id) => { const departments = get().departments.filter((d) => d.id !== id && d.parentId !== id); set({ departments }); void saveStored({ departments, members: get().members, roles: get().roles, passwordRule: get().passwordRule }).catch(() => {}); }, createMember: (data) => { const member: Member = { ...data, username: data.username || generateUsernameFromName(data.name, get().members.map((m) => m.username ?? '')), phone: data.phone ?? '', id: `m-${Date.now()}`, createdAt: new Date().toISOString().slice(0, 10), }; const members = [...get().members, member]; set({ members }); void saveStored({ departments: get().departments, members, roles: get().roles, passwordRule: get().passwordRule }).catch(() => {}); }, updateMember: async (id, data) => { const previousMembers = get().members; const safeData = sanitizeSystemAdminPatch(id, data); const updatedMembers = get().members.map((m) => m.id === id ? { ...m, ...safeData } : m); const ensured = ensureSystemAdminMember(updatedMembers); set({ members: ensured.members }); try { await saveStored({ departments: get().departments, members: ensured.members, roles: get().roles, passwordRule: get().passwordRule }); } catch (error) { set({ members: previousMembers }); throw error; } }, deleteMember: (id) => { if (!canDeleteSystemMember(id)) return; const members = get().members.filter((m) => m.id !== id); set({ members }); void saveStored({ departments: get().departments, members, roles: get().roles, passwordRule: get().passwordRule }).catch(() => {}); }, createRole: (data) => { const role: RoleItem = { ...data, id: `role-${Date.now()}`, createdAt: new Date().toISOString().slice(0, 10), permissions: data.permissions ?? [], }; const roles = [...get().roles, role]; set({ roles }); void saveStored({ departments: get().departments, members: get().members, roles, passwordRule: get().passwordRule }).catch(() => {}); }, updateRole: (id, data) => { const roles = get().roles.map((r) => { if (r.id !== id) return r; // 超管不允许改 permissions(防误操作锁死系统) if (r.id === 'role-admin' && data.permissions) { const { permissions, ...rest } = data; return { ...r, ...rest }; } return { ...r, ...data }; }); set({ roles }); void saveStored({ departments: get().departments, members: get().members, roles, passwordRule: get().passwordRule }).catch(() => {}); }, deleteRole: (id) => { if (id === SYSTEM_ADMIN_ROLE_ID) return; const roles = get().roles.filter((r) => r.id !== id); set({ roles }); void saveStored({ departments: get().departments, members: get().members, roles, passwordRule: get().passwordRule }).catch(() => {}); }, }));