import { mutation, query } from "./_generated/server"; import { v } from "convex/values"; import { requireUserId } from "./_utils/auth"; import { nowIso } from "./_utils/time"; import type { MutationCtx, QueryCtx } from "./_generated/server"; type WorkspaceSummary = { id: string; name: string; type: "personal" | "team"; iconUrl: string | null; memberCount: number; isDefault: boolean; }; async function findWorkspaceById(ctx: QueryCtx | MutationCtx, workspaceId: string) { return await ctx.db .query("workspaces") .withIndex("by_workspace_id", (q) => q.eq("id", workspaceId)) .first(); } async function countMembers(ctx: QueryCtx | MutationCtx, workspaceId: string): Promise { const members = await ctx.db .query("workspace_members") .withIndex("by_workspace_id", (q) => q.eq("workspace_id", workspaceId)) .collect(); return members.length; } export const ensureDefaultWorkspace = mutation({ args: { fallbackName: v.optional(v.string()), workspaceIdIfCreate: v.string(), }, handler: async (ctx, args) => { const userId = await requireUserId(ctx); const memberships = await ctx.db .query("workspace_members") .withIndex("by_user_id", (q) => q.eq("user_id", userId)) .collect(); if (memberships.length === 0) { const workspaceName = (args.fallbackName ?? "").trim() ? `${args.fallbackName!.trim()} 的空间` : "我的空间"; const ts = nowIso(); await ctx.db.insert("workspaces", { id: args.workspaceIdIfCreate, name: workspaceName, type: "personal", icon_url: null, created_by: userId, created_at: ts, }); await ctx.db.insert("workspace_members", { workspace_id: args.workspaceIdIfCreate, user_id: userId, role: "owner", is_default: true, created_at: ts, }); const summary: WorkspaceSummary = { id: args.workspaceIdIfCreate, name: workspaceName, type: "personal", iconUrl: null, memberCount: 1, isDefault: true, }; return { workspaces: [summary], activeWorkspaceId: args.workspaceIdIfCreate, }; } // 有 membership 就认为已有 workspace;再兜底一次补齐 workspace 记录。 const workspaceIds = Array.from(new Set(memberships.map((m) => m.workspace_id))); const summaries: WorkspaceSummary[] = []; for (const wid of workspaceIds) { const ws = await findWorkspaceById(ctx, wid); if (!ws) continue; const memberCount = await countMembers(ctx, wid); const isDefault = memberships.some((m) => m.workspace_id === wid && m.is_default); summaries.push({ id: ws.id, name: ws.name, type: ws.type, iconUrl: ws.icon_url, memberCount: memberCount || 1, isDefault, }); } // 说明:保持与原 fetchWorkspaceSummaries 一致:默认 workspace 优先,否则取第一个。 const defaultWs = summaries.find((w) => w.isDefault); const activeWorkspaceId = defaultWs?.id ?? summaries[0]?.id ?? ""; return { workspaces: summaries, activeWorkspaceId }; }, }); export const fetchWorkspaceSummaries = query({ args: {}, handler: async (ctx, args) => { const userId = await requireUserId(ctx); // 说明:为了复用 ensureDefaultWorkspace 的返回结构,这里直接走同样的聚合逻辑。 const memberships = await ctx.db .query("workspace_members") .withIndex("by_user_id", (q) => q.eq("user_id", userId)) .collect(); const workspaceIds = Array.from(new Set(memberships.map((m) => m.workspace_id))); const summaries: WorkspaceSummary[] = []; for (const wid of workspaceIds) { const ws = await findWorkspaceById(ctx, wid); if (!ws) continue; const memberCount = await countMembers(ctx, wid); const isDefault = memberships.some((m) => m.workspace_id === wid && m.is_default); summaries.push({ id: ws.id, name: ws.name, type: ws.type, iconUrl: ws.icon_url, memberCount: memberCount || 1, isDefault, }); } const defaultWs = summaries.find((w) => w.isDefault); const activeWorkspaceId = defaultWs?.id ?? summaries[0]?.id ?? ""; return { workspaces: summaries, activeWorkspaceId }; }, }); export const switchDefaultWorkspace = mutation({ args: { workspaceId: v.string() }, handler: async (ctx, args) => { const userId = await requireUserId(ctx); const target = await ctx.db .query("workspace_members") .withIndex("by_workspace_user", (q) => q.eq("workspace_id", args.workspaceId).eq("user_id", userId), ) .first(); if (!target) { throw new Error("无权切换至该工作空间"); } const memberships = await ctx.db .query("workspace_members") .withIndex("by_user_id", (q) => q.eq("user_id", userId)) .collect(); // 说明:Convex 暂无批量 update,这里逐条 patch。 for (const m of memberships) { if (m.is_default) { await ctx.db.patch(m._id, { is_default: false }); } } await ctx.db.patch(target._id, { is_default: true }); return { ok: true }; }, });