UNPKG

koishi-plugin-pointmintmarket

Version:

积分商城系统 - 为其他插件提供商品注册和购买功能

468 lines (460 loc) 16.7 kB
var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __export = (target, all) => { for (var name2 in all) __defProp(target, name2, { get: all[name2], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { Config: () => Config, MarketService: () => MarketService, apply: () => apply, description: () => description, inject: () => inject, logs: () => logs, name: () => name }); module.exports = __toCommonJS(src_exports); var import_koishi3 = require("koishi"); // src/database.ts var MARKET_ITEMS_TABLE = "market_items"; var market_database = class { static { __name(this, "market_database"); } ctx; logs; lock = []; _itemsCache = []; get Items() { return this._itemsCache; } async updateCache() { this._itemsCache = await this.ctx.database.select(MARKET_ITEMS_TABLE).execute(); } // 用于确认数据库是否完成初始化 _initialized = false; get initialized() { return this._initialized; } constructor(ctx) { this.ctx = ctx; this.logs = logs; this.ctx.model.extend(MARKET_ITEMS_TABLE, { id: "integer", name: "string", description: "string", price: "integer", status: "string", registered: "boolean", image: "string", tags: "json", stock: "integer", pluginName: "string" }, { primary: "id" }); this._initialized = true; } async getNewItemId() { await this.updateCache(); try { if (this._itemsCache.length === 0) return 1; return this._itemsCache[this._itemsCache.length - 1].id + 1; } catch (error) { this.logs.warn(`获取最新商品ID时出错: ${error.message}`); return 1; } } async getAllMarketItem() { return this.ctx.database.select(MARKET_ITEMS_TABLE).execute(); } async getMarketItemById(itemId) { await this.updateCache(); const item = this._itemsCache.find((item2) => item2.id == itemId); return item || null; } async addMarketItem(item) { if (this._itemsCache.find((Items) => Items.name === item.name && Items.pluginName === item.pluginName)) { logs.warn(`商品 ${item.name} 已存在`); return; } await this.ctx.database.create(MARKET_ITEMS_TABLE, item); this.updateCache(); return; } // 交换两个商品的位置,id保持原位置 async swapMarketItem(itemId1, itemId2) { const item1 = await this.getMarketItemById(itemId1); const item2 = await this.getMarketItemById(itemId2); if (!item1 || !item2) { logs.warn(`商品 ${item1} 或 ${item2} 不存在`); return; } item1.id = itemId2; item2.id = itemId1; await this.updateMarketItem([item1, item2]); this.updateCache(); return; } // 将商品信息更新到数据库中 async updateMarketItem(items) { for (const item of items) { const { id, ...updateData } = item; await this.ctx.database.set(MARKET_ITEMS_TABLE, item.id, updateData); } this.updateCache(); return; } async deleteMarketItem(itemId, pluginName) { if (pluginName) { await this.ctx.database.remove(MARKET_ITEMS_TABLE, { pluginName }); } else { await this.ctx.database.remove(MARKET_ITEMS_TABLE, { id: itemId }); } this.updateCache(); return; } // 减少库存,需要使用内存锁 async reduceStock(itemId) { if (this.lock.includes(itemId.toString())) { while (this.lock.includes(itemId.toString())) { await new Promise((resolve2) => setTimeout(resolve2, 10)); } } this.lock.push(itemId.toString()); const item = await this.getMarketItemById(itemId); if (!item) { logs.warn(`商品 ${itemId} 不存在`); this.lock = this.lock.filter((id) => id !== itemId.toString()); return false; } if (item.stock === void 0 || item.stock === null || item.stock == -1) { logs.warn(`商品 ${itemId} 没有库存或库存设为了无限`); this.lock = this.lock.filter((id) => id !== itemId.toString()); return false; } if (item.stock == 0) { logs.warn(`商品 ${itemId} 库存不足`); this.lock = this.lock.filter((id) => id !== itemId.toString()); return false; } item.stock--; await this.updateMarketItem([item]); this.lock = this.lock.filter((id) => id !== itemId.toString()); return true; } }; // src/services.ts var import_koishi = require("koishi"); var import_koishi_plugin_pointmint = require("koishi-plugin-pointmint"); var MarketService = class extends import_koishi.Service { static { __name(this, "MarketService"); } static inject = ["points"]; static [import_koishi.Service.provide] = "market"; get Items() { return this.db.Items; } cfg; db; registerLock = false; constructor(ctx, cfg) { super(ctx, "market"); this.cfg = cfg; this.db = new market_database(ctx); } // 商品id和回调函数的映射 registeredCallbacks = /* @__PURE__ */ new Map(); // 注册商品 async registerItem(pluginName, options) { while (!this.db.initialized) { logs.info("等待数据库初始化"); await new Promise((resolve2) => setTimeout(resolve2, 100)); } while (this.registerLock) { await new Promise((resolve2) => setTimeout(resolve2, 10)); } this.registerLock = true; const Items = await this.db.getAllMarketItem(); const existingItem = Items.find((item) => item.name == options.name && item.pluginName == pluginName); if (existingItem) { existingItem.image = options.image || null; existingItem.registered = true; await this.db.updateMarketItem([existingItem]); this.registeredCallbacks.set(existingItem.id, options.onPurchase); logs.info(`插件 ${pluginName} 已更新商品 ${existingItem.name}`); } else { const newItemId = await this.db.getNewItemId(); const newItem = { id: newItemId, name: options.name, description: options.description, tags: options.tags, price: options.price || 0, stock: 0, status: "unavailable", registered: true, image: options.image, pluginName }; this.db.addMarketItem(newItem); this.registeredCallbacks.set(newItemId, options.onPurchase); logs.info(`插件 ${pluginName} 已注册商品 ${newItem.name}`); } this.registerLock = false; } // 购买商品 async purchaseItem(userId, itemId, session) { const item = await this.db.getMarketItemById(itemId); if (!item) { return { success: false, message: "商品不存在" }; } if (item.stock == 0) { return { success: false, message: "商品已售罄" }; } const transactionId = this.ctx.points.generateTransactionId(); const callback = this.registeredCallbacks.get(itemId); if (!callback) { await this.ctx.points.rollback(userId, transactionId, name); return { success: false, message: "商品回调函数未注册", item }; } const result = await this.ctx.points.reduce(userId, transactionId, item.price, name); if (result.code < 200 || result.code >= 300) { return { success: false, message: result.msg, item }; } const callbackResult = await callback(session); if (typeof callbackResult === "boolean") { if (!callbackResult) { await this.ctx.points.rollback(userId, transactionId, name); return { success: false, message: "购买失败", item }; } } else if (typeof callbackResult === "string") { await this.ctx.points.rollback(userId, transactionId, name); return { success: false, message: callbackResult, item }; } else { if (callbackResult.code !== 0) { await this.ctx.points.rollback(userId, transactionId, name); return { success: false, message: callbackResult.msg, item }; } } if (item.stock !== void 0 || item.stock !== null) { item.stock--; } return { success: true, message: "购买成功", transactionId, item }; } // 取消注册商品 async unregisterItem(itemId) { this.registeredCallbacks.delete(itemId); let item = await this.db.getMarketItemById(itemId); if (!item) { return; } this.db.updateMarketItem([item]); } // 取消注册某插件的所有商品 async unregisterItems(pluginName) { if (this.registerLock) { while (this.registerLock) { await new Promise((resolve2) => setTimeout(resolve2, 100)); } } this.registerLock = true; const Items = await this.db.getAllMarketItem(); const items = Items.filter((item) => item.pluginName === pluginName); if (items.length === 0) { this.registerLock = false; return; } for (const item of items) { this.registeredCallbacks.delete(item.id); item.registered = false; } this.db.updateMarketItem(items); logs.info(`插件 ${pluginName} 已取消注册所有商品`); this.registerLock = false; } // 删除某商品 async deleteItemById(itemId) { if (this.registerLock) { while (this.registerLock) { await new Promise((resolve2) => setTimeout(resolve2, 100)); } } this.registerLock = true; this.registeredCallbacks.delete(itemId); await this.db.deleteMarketItem(itemId); this.registerLock = false; } }; // src/command.ts async function registerCommands(ctx, config) { switch (config.commandType) { case "easy": { const shop = ctx.command("商城").alias("shop"); shop.action(async ({ session }) => { switch (config.marketStyle) { case "text": const items = ctx.market.Items; let page = 1; const totalPages = Math.ceil(items.length / config.marketStyle_maxItemsPerPage); const startIndex = (page - 1) * config.marketStyle_maxItemsPerPage; const endIndex = startIndex + config.marketStyle_maxItemsPerPage; const pageItems = items.slice(startIndex, endIndex); let itemsString = ""; for (const item of pageItems) { itemsString += `ID:${item.id} ${item.name} - ${item.price}积分 ${item.description} `; } let marketString = config.marketStyleTextStyle; marketString = marketString.replace("{items}", itemsString); marketString = marketString.replace("{page}", page.toString()); marketString = marketString.replace("{totalPages}", totalPages.toString()); await session.send(marketString); } }); const exchange = ctx.command("兑换 <id:number>").alias("exchange <id:string>"); exchange.action(async ({ session }, id) => { if (!session.userId) return "无法识别您的id"; const currentPoints = await ctx.points.get(session.userId); if (currentPoints < 0) return "您还没有积分账户,快去获得一些吧~"; if (id == void 0) { await session.send("请输入商品id"); const id2 = await session.prompt(6e4); } const result = await ctx.market.purchaseItem(session.userId, id, session); if (!result.success) { session.send(result.message); } }); } } } __name(registerCommands, "registerCommands"); // src/console/index.ts var import_path = require("path"); var consoleData = class { static { __name(this, "consoleData"); } ctx; db; constructor(ctx) { this.ctx = ctx; this.db = new market_database(ctx); this.init(); } init() { this.ctx.console.addListener("get-Items", () => this.db.getAllMarketItem()); this.ctx.console.addListener("save-Items", async (data) => { const items = data.map((item) => { return { id: item.id, name: item.name, description: item.description, price: item.price, stock: item.stock, tags: item.tags, status: item.status }; }); this.db.updateMarketItem(items); }); this.ctx.console.addListener("swap-Items", (data) => { try { this.db.swapMarketItem(data.id1, data.id2); return "success"; } catch (error) { return "failed"; } }); this.ctx.console.addListener("delete-Item", (id) => { this.ctx.market.deleteItemById(id); }); this.ctx.console.addEntry({ dev: (0, import_path.resolve)(__dirname, "../../client/index.ts"), prod: (0, import_path.resolve)(__dirname, "../dist") }); } }; // src/config.ts var import_koishi2 = require("koishi"); var Config = import_koishi2.Schema.intersect([ import_koishi2.Schema.object({ commandType: import_koishi2.Schema.union([ import_koishi2.Schema.const("easy").description("简单模式,使用`积分商城`和`兑换`命令"), import_koishi2.Schema.const("interactive").disabled().description("交互模式,使用指令`积分商城`进入后通过交互选择并兑换商品") ]).default("easy").description("选择使用哪种命令模式") }).description("基本设置"), import_koishi2.Schema.object({ purchaseOrder: import_koishi2.Schema.union([ import_koishi2.Schema.const("points-first").description("先扣除积分,再兑换商品"), import_koishi2.Schema.const("item-first").description("先兑换商品,再扣除积分") ]).description("购买时先操作积分还是先操作积分") }), import_koishi2.Schema.object({ marketStyleDefaultError: import_koishi2.Schema.string().default("未知错误").description("未知错误时的默认提示,使用`{code}`表示错误码,`{msg}`表示错误信息") }).description("基本样式设置"), import_koishi2.Schema.object({ marketStyle_maxItemsPerPage: import_koishi2.Schema.number().default(3).min(1).description("单页最大商品数量"), marketStyle: import_koishi2.Schema.union([ import_koishi2.Schema.const("text").description("使用纯文本输出商品信息"), import_koishi2.Schema.const("plugin").disabled().experimental().description("使用其他插件输出商品信息") ]).default("text").description("选择输出积分商城的商品信息的方式") }).description("积分商城个性化设置"), import_koishi2.Schema.union([ import_koishi2.Schema.object({ marketStyle: import_koishi2.Schema.const("text").required() }), import_koishi2.Schema.object({ marketStyle: import_koishi2.Schema.const("text"), marketStyleTextStyle: import_koishi2.Schema.string().role("textarea", { rows: [4, 3] }).default("【积分商城】\n {items} \n 当前为{page}页,共{totalPages}页 \n up上一页,down下一页,回复其他退出").description("选择输出积分商城的商品信息的文本样式,使用`{items}`表示商品信息,`{page}`表示当前页数,`{totalPages}`表示总页数") }), import_koishi2.Schema.object({ marketStyle: import_koishi2.Schema.const("plugin"), marketStylePlugin: import_koishi2.Schema.union([]).description("选择输出积分商城的商品信息的插件"), marketStylePlugin_marketName: import_koishi2.Schema.string().description("商城的名称") }) ]), import_koishi2.Schema.object({ debug: import_koishi2.Schema.boolean().default(false).description("是否启用调试日志") }).description("调试设置") ]); // src/index.ts var name = "pointmintmarket"; var description = "积分商城系统 - 为其他插件提供商品注册和购买功能"; var inject = { required: ["points", "database", "console"] }; var logs = new import_koishi3.Logger(name); function apply(ctx, config) { const db = new market_database(ctx); const cd = new consoleData(ctx); registerCommands(ctx, config); ctx.plugin(MarketService); } __name(apply, "apply"); // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { Config, MarketService, apply, description, inject, logs, name });