feat(平台): 补齐服务端持久化和AI拆解契约

This commit is contained in:
Script Generator
2026-06-25 15:21:32 +08:00
parent 5723356d08
commit 5adc7759ad
73 changed files with 5599 additions and 368 deletions

View File

@@ -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)) {