diff --git a/routes/kv-token.js b/routes/kv-token.js index bf3dd1c..d67c233 100644 --- a/routes/kv-token.js +++ b/routes/kv-token.js @@ -298,43 +298,25 @@ router.post( req.connection.socket?.remoteAddress || ""; - const results = []; - const errorList = []; - - // 批量处理所有键值对 - for (const [key, value] of Object.entries(data)) { - try { - const result = await kvStore.upsert(deviceId, key, value, creatorIp); - results.push({ - key: result.key, - created: result.createdAt.getTime() === result.updatedAt.getTime(), - }); - // 广播每个键的变更 - const uuid = res.locals.device?.uuid; - if (uuid) { - broadcastKeyChanged(uuid, { - key: result.key, - action: "upsert", - created: result.createdAt.getTime() === result.updatedAt.getTime(), - updatedAt: result.updatedAt, - batch: true, - }); - } - } catch (error) { - errorList.push({ - key, - error: error.message, - }); - } - } + // 使用优化的批量upsert方法 + const { results, errors: errorList } = await kvStore.batchUpsert(deviceId, data, creatorIp); return res.status(200).json({ - deviceId, - total: Object.keys(data).length, - successful: results.length, - failed: errorList.length, - results, - errors: errorList.length > 0 ? errorList : undefined, + code: 200, + message: "批量导入成功", + data: { + deviceId, + summary: { + total: Object.keys(data).length, + successful: results.length, + failed: errorList.length, + }, + results: results.map(r => ({ + key: r.key, + isNew: r.created, + })), + ...(errorList.length > 0 && { errors: errorList }), + }, }); }) ); diff --git a/utils/kvStore.js b/utils/kvStore.js index f521f36..a296383 100644 --- a/utils/kvStore.js +++ b/utils/kvStore.js @@ -102,6 +102,62 @@ class KVStore { }; } + /** + * 批量创建或更新键值对(优化性能) + * @param {number} deviceId - 设备ID + * @param {object} data - 键值对数据 {key1: value1, key2: value2, ...} + * @param {string} creatorIp - 创建者IP,可选 + * @returns {object} {results: Array, errors: Array} + */ + async batchUpsert(deviceId, data, creatorIp = "") { + const results = []; + const errors = []; + + // 使用事务处理所有操作 + await prisma.$transaction(async (tx) => { + for (const [key, value] of Object.entries(data)) { + try { + const item = await tx.kVStore.upsert({ + where: { + deviceId_key: { + deviceId: deviceId, + key: key, + }, + }, + update: { + value, + ...(creatorIp && {creatorIp}), + }, + create: { + deviceId: deviceId, + key: key, + value, + creatorIp, + }, + }); + + results.push({ + key: item.key, + created: item.createdAt.getTime() === item.updatedAt.getTime(), + createdAt: item.createdAt, + updatedAt: item.updatedAt, + }); + } catch (error) { + errors.push({ + key, + error: error.message, + }); + } + } + }); + + // 在事务完成后,一次性更新指标 + const totalKeys = await prisma.kVStore.count(); + keysTotal.set(totalKeys); + + return { results, errors }; + } + /** * 通过设备ID和键名删除 * @param {number} deviceId - 设备ID @@ -219,6 +275,37 @@ class KVStore { }); return count; } + + /** + * 获取指定设备的统计信息 + * @param {number} deviceId - 设备ID + * @returns {object} 统计信息 + */ + async getStats(deviceId) { + const [totalKeys, oldestKey, newestKey] = await Promise.all([ + prisma.kVStore.count({ + where: { deviceId }, + }), + prisma.kVStore.findFirst({ + where: { deviceId }, + orderBy: { createdAt: "asc" }, + select: { createdAt: true, key: true }, + }), + prisma.kVStore.findFirst({ + where: { deviceId }, + orderBy: { updatedAt: "desc" }, + select: { updatedAt: true, key: true }, + }), + ]); + + return { + totalKeys, + oldestKey: oldestKey?.key, + oldestCreatedAt: oldestKey?.createdAt, + newestKey: newestKey?.key, + newestUpdatedAt: newestKey?.updatedAt, + }; + } } export default new KVStore();