/** * 主人的 id(handle):Agent 的全名是「名字@主人 id」,如 小火子@yibo。 * - 新表 vk_owner_handles(handle 全局唯一);给已有的人类用户按邮箱 @ 前的部分回填,重名的加数字后缀。 * - vk_agents 增加 UNIQUE(owner_user_id, name):同一主人名下 Agent 不重名,全名因此全局唯一。 */ const HANDLES = 'vk_owner_handles' export async function up(db) { await db.schema.createTable(HANDLES, (t) => { t.string('user_id', 64).primary() t.string('handle', 32).notNullable() t.datetime('created_at', { precision: 3 }).notNullable() t.unique(['handle'], { indexName: 'uq_vk_owner_handles_handle' }) t.check("handle regexp '^[a-z0-9][a-z0-9_-]{1,31}$'", [], 'ck_vk_owner_handles_format') }) const users = await db('users').where((q) => q.whereNull('role').orWhereNot({ role: 'agent' })).orderBy('created_at', 'asc').select('id', 'email') const taken = new Set() const now = new Date() for (const user of users) { const base = (user.email.split('@')[0].toLowerCase().replace(/[^a-z0-9_-]/g, '-').replace(/^[^a-z0-9]+/, '') || 'user').slice(0, 24).padEnd(2, '0') let handle = base for (let n = 2; taken.has(handle); n += 1) handle = `${base}-${n}` taken.add(handle) await db(HANDLES).insert({ user_id: user.id, handle, created_at: now }) } const duplicate = await db('vk_agents').whereNotNull('owner_user_id').groupBy('owner_user_id', 'name').havingRaw('COUNT(*) > 1').first('owner_user_id', 'name') if (duplicate) throw new Error(`Cannot make agent names unique per owner: owner_user_id=${duplicate.owner_user_id}, name=${duplicate.name}`) await db.schema.alterTable('vk_agents', (t) => t.unique(['owner_user_id', 'name'], { indexName: 'uq_vk_agents_owner_name' })) } export async function down(db) { await db.schema.alterTable('vk_agents', (t) => t.dropUnique(['owner_user_id', 'name'], 'uq_vk_agents_owner_name')) await db.schema.dropTable(HANDLES) }