/** * 英雄/预设存储层:开发版与线上设计版统一读写线上 MongoDB。 * - heroes 集合:每个英雄一条文档,uuid 唯一索引 * - templates 集合:预设库(_id='presets' 单文档) * - meta 集合:版本闸门(_id='version' 单文档,schemaVersion 由开发版推送 meta 时写入) * 技能/Buff 底座不进数据库,开发版走 TS 编译,线上版走 meta.json 快照。 */ import { MongoClient } from 'mongodb' const MONGO_URL = process.env.MONGO_URL || '' const DB_NAME = process.env.MONGO_DB || 'heroconfig' let client = null let db = null /** 惰性连接 MongoDB(单例复用) */ async function mongo() { if (db) return db if (!MONGO_URL) throw new Error('未配置 MONGO_URL 环境变量') client = new MongoClient(MONGO_URL) await client.connect() db = client.db(DB_NAME) // uuid 唯一索引:保证英雄按 uuid 幂等覆盖 await db.collection('heroes').createIndex({ uuid: 1 }, { unique: true }) return db } /** 读取全部英雄(按 uuid 升序,剥离 Mongo 内部 _id) */ export async function getHeroes() { const docs = await (await mongo()).collection('heroes').find().sort({ uuid: 1 }).toArray() return docs.map(({ _id, ...h }) => h) } /** 全量覆盖:upsert 传入英雄,并删除库中多余的 uuid */ export async function saveHeroes(heroes) { const col = (await mongo()).collection('heroes') for (const h of heroes) { await col.replaceOne({ uuid: h.uuid }, h, { upsert: true }) } await col.deleteMany({ uuid: { $nin: heroes.map((h) => h.uuid) } }) } /** 读取预设库(无文档时返回空池) */ export async function getTemplates() { const doc = await (await mongo()).collection('templates').findOne({ _id: 'presets' }) return { triggerPresets: Array.isArray(doc?.triggerPresets) ? doc.triggerPresets : [], fieldPresets: Array.isArray(doc?.fieldPresets) ? doc.fieldPresets : [], } } /** 全量覆盖预设库 */ export async function saveTemplates(triggerPresets, fieldPresets) { await (await mongo()) .collection('templates') .replaceOne({ _id: 'presets' }, { _id: 'presets', triggerPresets, fieldPresets }, { upsert: true }) } /** 读取库中数据结构版本(首次为 0) */ export async function getSchemaVersion() { const doc = await (await mongo()).collection('meta').findOne({ _id: 'version' }) return doc?.schemaVersion ?? 0 } /** * 推送数据结构版本:只允许升不允许降,避免旧部署误把版本刷低导致线上版解锁写入。 * @returns {Promise} 写入后的实际版本 */ export async function bumpSchemaVersion(version) { const col = (await mongo()).collection('meta') const cur = await getSchemaVersion() const next = Math.max(cur, version) await col.replaceOne({ _id: 'version' }, { _id: 'version', schemaVersion: next, updatedAt: new Date() }, { upsert: true }) return next }