Loading... ## 前言 因为之前没注意服务器安全,搭建aut的服务器被侵入了,导致aut的数据丢失,并且刚好碰到小组作业,没时间,就没去重建aut。这段时间放假有空,刚好可以用格式化的机器搭建个bncr玩玩,不试不知道,bncr的确比aut设计得更规范,对开发者极友好(开发文档需要吐槽一下,太简陋了)。这里简单记录一下我的第一个Bncr插件(应该叫模块,这里为了符合标题) ## 需要解决的问题 很久以前,在使用盖亚的订单系统时,当遇到高并发(多人同时进行会话)的情况下,因为订单数据存在框架内嵌式 KV 数据库(无论是aut还是bncr都采用这种数据库)中,如果同时对订单数据进行修改,可能会出现数据丢失的情况。因为类似这种对订单数据进行修改的操作,都是通过先从数据库get,后set来更新数据,假如多个会话同时get同一份数据,然后set,那么当全部会话结束时,这个订单数据只会新增了最后会话set中新增的数据,前面会话set的数据就丢失了。这种是非常严重的业务事故(现在aut的盖亚系统还存在这个情况,我已经弃更了,有事也别找我),这里刚好可以针对这种情况对数据库进行扩展来练练手,尝试解决这个问题。 ## 插件本体 在`plugins/yourfolder/mod/数据库扩展.js`中写入下面的代码,写入后需要重启bncr,模块才会生效 ```javascript /** * 作者 * @author Sirhexs * 插件名称 * @name 数据库扩展 * 团队 * @team Sirhexs * 版本 * @version 1.0.0 * 说明 * @description 可通过这个单例模式模块对数据库频繁读写数据进行同步读写限制,采用类似悲观锁的原理,防止数据在高并发下的读写错误。 * 是否管理员专用 * @admin false * 优先级 * @priority 10 * 分类 * @classification ["数据库扩展"] * 是否公开 * @public true * 是否禁用 * @disable false * 是否服务模块 * @service true */ class DataAccess { // 构造函数,这里需要使用单例模式 constructor() { if (DataAccess.instance) { return DataAccess.instance; } // 定义写锁 this.isNotLock = true // 设置单例实例 DataAccess.instance = this; // 返回单例实例 return this; } // 在数组开头插入数据 async unshift(tableName, key, data) { return await this.operate(tableName, key, async (dbData) => { dbData.unshift(data) }) } // 更新对象中的数据 async update(tableName, key, propertyPath, operation) { return await this.operate(tableName, key, async (dbData) => { // 获取路径数组 const properties = propertyPath.split('.'); // 初始化寻址对象 let current = dbData; // 遍历寻找 for (let i = 0; i < properties.length - 1; i++) { if (current[properties[i]] === undefined) { throw new Error(`数据库[${tableName}]中[${key}]不存在${propertyPath}的${properties[i]}。`); } current = current[properties[i]]; } // 检查路径中最后一个属性 const finalProperty = properties[properties.length - 1]; if (current.hasOwnProperty(finalProperty)) { // 调用传进来的函数进行修改 current[finalProperty] = operation(current[finalProperty]); } else { throw new Error(`数据库[${tableName}]中[${key}]不存在${propertyPath}`); } }) } async operate(tableName, key, fun) { if (this.isNotLock) { // 上锁 this.isNotLock = false //创建一个系统数据库实例 const db = new BncrDB(tableName); // 获取数据 let dbData = await db.get(key, []) // 更改数据 try { await fun(dbData) } catch (err) { // 解锁 this.isNotLock = true console.error(err.message) return false } // 设置数据 const bool = await db.set(key, dbData) // 解锁 this.isNotLock = true // 返回保存结果 return bool } else { return await this.operate(tableName, key, fun) } } } // 实例化 const instance = new DataAccess(); // CommonJS模块化导出 module.exports = instance; ``` ## 使用 在`plugins/yourfolder/数据库扩展测试.js`中写入下面的代码,这里不需要重启bncr,plugins二级目录下的.js 文件会热载入 ```javascript /** * @author Sirhexs * @name 数据库扩展测试 * @team Sirhexs * @version 1.0.0 * @description 数据库扩展模块测试插件 * @rule ^测试$ * @admin false * @priority 100 * @classification ["测试"] * @public false * @disable false */ // 导入 数据库扩展 模块 const ExtensionDB = require('./mod/数据库扩展.js'); module.exports = async (sender) => { const db = new BncrDB('PluginTest'); // unshift测试 console.log("unshift测试运行前,unshift测试数据:",await db.get("unshift测试数据",[])) await ExtensionDB.unshift("PluginTest", "unshift测试数据", { data: "在数组中新增一条数据" }) console.log("unshift测试运行后,unshift测试数据:",await db.get("unshift测试数据")) // update测试 const updateTestData = await db.get("update测试数据") // 如果是第一次运行,先写入测试数据 if (!updateTestData) { await db.set('update测试数据', { name: 'John', address: { city: 'New York', postalCode: { code: 10001, suffix: 1234 } } }); } console.log("update测试运行前,update测试数据:", await db.get("update测试数据")) await ExtensionDB.update("PluginTest", "update测试数据", 'address.postalCode.code', (data) => { return data + 100 }) console.log("update测试运行后,update测试数据:", await db.get("update测试数据")) } ``` ## 测试 给机器人发送 测试 指令即可  运行测试很简单,也能得出我们想要的结果,而上面提到的问题,在代码中其实很难复现出来,所以这里就没有进行相关的测试。 最后修改:2024 年 07 月 16 日 © 允许规范转载 赞 如果觉得我的文章对你有用,请随意赞赏
2 条评论
内容的丰富性和深度让人仿佛置身于知识的海洋,受益匪浅。
网络流行语融入自然,贴近年轻读者。