import { randomUUID } from 'node:crypto' const TABLE = 'vk_guilds' /** * @typedef {{ id: string, name: string, slug: string, lead_user_id: string, require_lead_approval: boolean, created_at: Date }} Guild */ /** * @param {{ db: import('knex').Knex }} deps */ export function createGuilds({ db }) { if (typeof db !== 'function') throw new Error('createGuilds: db is required') /** * 建公会并把负责人登记为成员。slug 已被占用返回 CONFLICT(由 UNIQUE 约束裁决)。 * @param {object} _ctx * @param {{ name: string, slug: string, lead_user_id: string, require_lead_approval: boolean, checkpoint_interval_sec?: number, piece_review_window_sec?: number, contest_review_window_sec?: number, transfer_ttl_sec?: number }} input 四个时间规则不传时用列默认值(6h / 24h / 72h / 24h) * @returns {Promise<{ ok: true, guild: Guild } | { ok: false, code: 'CONFLICT' }>} */ async function create(_ctx, { name, slug, lead_user_id, require_lead_approval, ...windows }) { const guild = { id: `g_${randomUUID()}`, name, slug, lead_user_id, require_lead_approval, ...windows, created_at: new Date() } return db.transaction(async (trx) => { // slug 冲突由 UNIQUE 裁决:被忽略的插入读不回本次 id。 await trx(TABLE).insert(guild).onConflict('slug').ignore() if ((await trx(TABLE).where({ id: guild.id }).first()) === undefined) return { ok: false, code: 'CONFLICT' } await trx('vk_guild_members').insert({ id: `gm_${randomUUID()}`, guild_id: guild.id, user_id: lead_user_id, role: 'lead', created_at: guild.created_at }) return { ok: true, guild } }) } /** * 全部公会(认领 Agent 时选择)。 * @param {object} _ctx * @returns {Promise>} */ async function list(_ctx) { return db(TABLE).orderBy('name', 'asc').select('id', 'name', 'slug') } /** * 运营视角的公会列表:负责人、公司 Agent 及其钱包、等级与周目标。 * @param {object} _ctx */ async function listForAdmin(_ctx) { return db(`${TABLE} as g`) .leftJoin('vk_agents as a', 'a.id', 'g.company_agent_id') .leftJoin('users as u', 'u.id', 'g.lead_user_id') .orderBy('g.name', 'asc') .select('g.id', 'g.name', 'g.slug', 'g.level', 'g.weekly_goal', 'g.require_lead_approval', 'u.email as lead_email', 'a.id as company_agent_id', 'a.name as company_agent_name', 'a.wallet_user_id as company_wallet_user_id') } /** * @param {object} _ctx * @param {{ guild_id: string, weekly_goal: number }} input 0 表示不设周目标 * @returns {Promise} */ async function setWeeklyGoal(_ctx, { guild_id, weekly_goal }) { return (await db(TABLE).where({ id: guild_id }).update({ weekly_goal, updated_at: new Date() })) === 1 } return { create, list, listForAdmin, setWeeklyGoal } }