feat(平台): 补齐服务端持久化和AI拆解契约
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
'use client';
|
||||
|
||||
import { create } from 'zustand';
|
||||
import { loadServerData } from '@/lib/server-data';
|
||||
import type { Member } from '@/lib/members';
|
||||
import { useMemberStore } from './useMemberStore';
|
||||
|
||||
interface AuthUser {
|
||||
id: string;
|
||||
@@ -14,53 +17,63 @@ interface AuthUser {
|
||||
interface AuthState {
|
||||
user: AuthUser | null;
|
||||
isAuthenticated: boolean;
|
||||
login: (phone: string, password: string, remember: boolean) => boolean;
|
||||
login: (phone: string, password: string, remember: boolean) => Promise<boolean>;
|
||||
logout: () => void;
|
||||
checkAuth: () => void;
|
||||
refreshUser: () => void;
|
||||
refreshUser: () => Promise<void>;
|
||||
}
|
||||
|
||||
interface StoredMembersData {
|
||||
members: Member[];
|
||||
}
|
||||
|
||||
const SESSION_KEY = 'ftb_auth_session';
|
||||
const PERSIST_KEY = 'ftb_auth_persist';
|
||||
|
||||
const FALLBACK_MEMBERS: Member[] = [
|
||||
{
|
||||
id: 'm-8',
|
||||
name: '\u8d85\u7ea7\u7ba1\u7406\u5458',
|
||||
departmentId: 'dept-1',
|
||||
roleId: 'role-admin',
|
||||
phone: '13200132008',
|
||||
email: 'chenshi@company.com',
|
||||
password: 'Ftb@2024',
|
||||
createdAt: '2024-01-01',
|
||||
},
|
||||
];
|
||||
|
||||
async function loadMembersForAuth(): Promise<Member[]> {
|
||||
try {
|
||||
const stored = await loadServerData<StoredMembersData>('members');
|
||||
if (stored?.members?.length) return stored.members;
|
||||
} catch {}
|
||||
|
||||
const inMemoryMembers = useMemberStore.getState().members;
|
||||
return inMemoryMembers.length > 0 ? inMemoryMembers : FALLBACK_MEMBERS;
|
||||
}
|
||||
|
||||
function toAuthUser(member: Member): AuthUser {
|
||||
return {
|
||||
id: member.id,
|
||||
name: member.name,
|
||||
roleId: member.roleId,
|
||||
departmentId: member.departmentId,
|
||||
phone: member.phone,
|
||||
email: member.email,
|
||||
};
|
||||
}
|
||||
|
||||
export const useAuthStore = create<AuthState>((set) => ({
|
||||
user: null,
|
||||
isAuthenticated: false,
|
||||
|
||||
login: (phone, password, remember) => {
|
||||
const membersRaw = localStorage.getItem('ftb_members_v1');
|
||||
let members: any[] = [];
|
||||
if (membersRaw) {
|
||||
const parsed = JSON.parse(membersRaw);
|
||||
members = parsed.members || [];
|
||||
}
|
||||
|
||||
// fallback: 如果 localStorage 没有数据或为空,使用默认 mock
|
||||
if (members.length === 0) {
|
||||
members = [
|
||||
{ id: 'm-1', name: '张三', departmentId: 'dept-1', roleId: 'role-pm', phone: '13800138001', email: 'zhangsan@company.com', password: 'Ftb@2024' },
|
||||
{ id: 'm-2', name: '李四', departmentId: 'dept-2-1', roleId: 'role-dev', phone: '13900139002', email: 'lisi@company.com', password: 'Ftb@2024' },
|
||||
{ id: 'm-3', name: '王五', departmentId: 'dept-2-2', roleId: 'role-dev', phone: '13700137003', email: 'wangwu@company.com', password: 'Ftb@2024' },
|
||||
{ id: 'm-4', name: '赵六', departmentId: 'dept-2-3', roleId: 'role-test', phone: '13600136004', email: 'zhaoliu@company.com', password: 'Ftb@2024' },
|
||||
{ id: 'm-5', name: '孙七', departmentId: 'dept-3', roleId: 'role-design', phone: '13500135005', email: 'sunqi@company.com', password: 'Ftb@2024' },
|
||||
{ id: 'm-6', name: '周八', departmentId: 'dept-4', roleId: 'role-pm', phone: '13400134006', email: 'zhouba@company.com', password: 'Ftb@2024' },
|
||||
{ id: 'm-7', name: '吴九', departmentId: 'dept-2-1', roleId: 'role-dev', phone: '13300133007', email: 'wujiu@company.com', password: 'Ftb@2024' },
|
||||
{ id: 'm-8', name: '陈十', departmentId: 'dept-1', roleId: 'role-admin', phone: '13200132008', email: 'chenshi@company.com', password: 'Ftb@2024' },
|
||||
];
|
||||
}
|
||||
|
||||
const member = members.find((m: any) => m.phone === phone && (m.password || 'Ftb@2024') === password);
|
||||
login: async (phone, password, remember) => {
|
||||
const members = await loadMembersForAuth();
|
||||
const member = members.find((m) => m.phone === phone && (m.password || 'Ftb@2024') === password);
|
||||
if (!member) return false;
|
||||
|
||||
const user: AuthUser = {
|
||||
id: member.id,
|
||||
name: member.name,
|
||||
roleId: member.roleId,
|
||||
departmentId: member.departmentId,
|
||||
phone: member.phone,
|
||||
email: member.email,
|
||||
};
|
||||
|
||||
const user = toAuthUser(member);
|
||||
set({ user, isAuthenticated: true });
|
||||
sessionStorage.setItem(SESSION_KEY, JSON.stringify(user));
|
||||
if (remember) {
|
||||
@@ -90,16 +103,16 @@ export const useAuthStore = create<AuthState>((set) => ({
|
||||
}
|
||||
},
|
||||
|
||||
refreshUser: () => {
|
||||
refreshUser: async () => {
|
||||
const cur = useAuthStore.getState().user;
|
||||
if (!cur) return;
|
||||
try {
|
||||
const raw = localStorage.getItem('ftb_members_v1');
|
||||
if (!raw) return;
|
||||
const parsed = JSON.parse(raw);
|
||||
const m = parsed.members?.find((x: any) => x.id === cur.id);
|
||||
if (!m) return;
|
||||
const next: AuthUser = { id: m.id, name: m.name, roleId: m.roleId, departmentId: m.departmentId, phone: m.phone, email: m.email };
|
||||
const memoryMember = useMemberStore.getState().members.find((x) => x.id === cur.id);
|
||||
const members = memoryMember ? [memoryMember] : await loadMembersForAuth();
|
||||
const member = members.find((x) => x.id === cur.id);
|
||||
if (!member) return;
|
||||
|
||||
const next = toAuthUser(member);
|
||||
useAuthStore.setState({ user: next });
|
||||
sessionStorage.setItem(SESSION_KEY, JSON.stringify(next));
|
||||
if (localStorage.getItem(PERSIST_KEY)) {
|
||||
|
||||
Reference in New Issue
Block a user