guilds.js 1.7 KB
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 }} input
   * @returns {Promise<{ ok: true, guild: Guild } | { ok: false, code: 'CONFLICT' }>}
   */
  async function create(_ctx, { name, slug, lead_user_id, require_lead_approval }) {
    const guild = { id: `g_${randomUUID()}`, name, slug, lead_user_id, require_lead_approval, 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<Array<{ id: string, name: string, slug: string }>>}
   */
  async function list(_ctx) {
    return db(TABLE).orderBy('name', 'asc').select('id', 'name', 'slug')
  }

  return { create, list }
}