smmcat-economy 使用演示代码

所有插件都可以共同使用一个交易市场,这思路是来源于 G胖 的建立
稀有道具可以售卖、购买;卖出后为了买另一个游戏的道具。这些思路通过这个插件可以完成。

不同的插件,通过设置唯一的 by 值,交易市场可以做出每个业务事件触发进行隔离
通过插件作者自己控制商品的走向,交易市场只提供展示和实现交易业务的媒介

为此,随手做了一个引用该服务做出的 demo,可以运行或者研究一下:

插件名:koishi-plugin-smmcat-economydemo

import { Context, Schema, Session, h } from 'koishi'
import { } from 'koishi-plugin-smmcat-economy'
import { } from 'koishi-plugin-smmcat-localstorage'
import { } from 'koishi-plugin-monetary'
import { Economy_BuyEventData, Economy_EventData, Economy_RecycleData } from 'koishi-plugin-smmcat-economy/lib/type'
import fs from 'fs/promises'
import path from 'path'

export const name = 'smmcat-trydemo'

export interface Config { }
export const inject = ['economy', 'localstorage', 'database', "monetary"]
export const Config: Schema<Config> = Schema.object({})

export function apply(ctx: Context, config: Config) {

  /** 商品清单 */
  type FoodItem = {
    name: string,
    pic: string,
    type: string,
    price: number,
    info: string
  }

  /** 用户仓库 */
  type UserStash = {
    [key: string]: FoodItem & {
      total: number
    }
  }

  let foodList: FoodItem[] = []

  async function initStoreFoodData() {
    foodList = JSON.parse(await fs.readFile(path.join(__dirname, './data.json'), 'utf-8'))
  }



  const shop = {
    userDataList: {},
    /** 数据初始化 */
    async init() {
      const upath = path.join(ctx.localstorage.basePath, 'smmcat-shop')
      try {
        await fs.access(upath)
      } catch (error) {
        fs.mkdir(upath, { recursive: true })
      }

      const dirList = await fs.readdir(upath)
      const temp = {}
      const eventList = dirList.map((item) => {
        return new Promise(async (resolve) => {
          try {
            temp[item] = JSON.parse(await ctx.localstorage.getItem(`smmcat-shop/${item}`))
            resolve(true)
          } catch (error) {
            resolve(true)
          }
        })
      })
      await Promise.all(eventList)
      this.userDataList = temp
    },
    /** 上架商品 */
    async uploadProduct(session: Session<'id'>, productName: string, price: number, quantity = 1) {
      console.log(session.user.id);

      const userId = session.userId
      const userData: UserStash = this.initUserData(userId)
      if (!userData[productName]) {
        await session.send('你的仓库中没有该商品')
        return
      }
      const productItem = userData[productName]

      await session.send(`${productName}的市场价为:${productItem.price};\n你即将售卖的单价为:${price}`)
      await session.send(`是否上架?\n(20秒回复 "是" 则上架)`)
      await session.prompt(20000)
      if ((productItem.total - quantity) <= 0) {
        await session.send('数量不足,无法上架')
        return
      }
      productItem.total -= quantity
      const upValue = {
        pic: productItem.pic,
        name: productItem.name,
        total: quantity,
        price,
        uid: session.user.id,
        by: 'smmcat-shop',
        type: productItem.type,
        isSell: true
      }
      if (productItem.total == 0) {
        delete userData[productName]
      }
      await ctx.economy.sellMarketSomeItem(session, upValue)
      this.updataStore(userId)
      await session.send('提交完成')
    },
    /** 用户信息初始化 */
    initUserData(userId: string) {
      if (!this.userDataList[userId]) {
        this.userDataList[userId] = {}
        const rowData = foodList[Math.floor(Math.random() * foodList.length)]
        console.log(rowData);
        this.userDataList[userId][rowData.name] = { ...rowData, total: 20 }
        this.updataStore(userId)
      }
      return this.userDataList[userId]
    },
    /** 数据本地存储 */
    async updataStore(userId: string) {
      const dataList = this.userDataList[userId]
      await ctx.localstorage.setItem(`smmcat-shop/${userId}`, JSON.stringify(dataList))
    }
  }



  ctx.economy.on('buying', async (event: Economy_EventData<Economy_BuyEventData>) => {
    const buy_userId = event.session.userId
    const userStash: UserStash = shop.initUserData(buy_userId)

    await event.session.send(
      `${h.image(event.upValue.pic)}` +
      `商品名:${event.upValue.name}\n` +
      `下单数量:${event.upValue.quantity}\n` +
      `总价:${event.upValue.total}\n` +
      '请在20秒内决定购买.\n如若购买请发 "是"')

    const res = await event.session.prompt(20000)
    if (res === undefined) {
      await event.session.send('超时结束...')
      return
    }

    if (res.trim() === "是") {
      const productItem = foodList.find((item) => item.name === event.upValue.name)
      if (!productItem) {
        await event.session.send('找不到该商品信息,操作失败')
        return
      }

      try {
        console.log(event.session.id);
        console.log(event.upValue.uid);

        // 结算账单
        await ctx.monetary.cost(event.session.user.id, event.upValue.total)
        await ctx.monetary.gain(event.upValue.uid, event.upValue.total)

      } catch (error) {
        await event.session.send('操作失败,余额不足')
        return
      }

      const getData = { ...productItem, total: event.upValue.quantity }
      if (!userStash[event.upValue.name]) {
        userStash[event.upValue.name] = getData
      } else {
        userStash[event.upValue.name].total += event.upValue.quantity
      }

      event.upValue.isBuy = true
      shop.updataStore(buy_userId)
      await event.session.send('购买完成...')
    }
  }, 'smmcat-shop')

  ctx.command("查看余额").userFields(["id"]).action(async ({ session }) => {

    const uid = session.user.id;
    const [data] = await ctx.database.get("monetary", { uid });
    if (!data) {
      const num = 1000;
      ctx.monetary.gain(uid, num);
      await session.send(`首次玩家,赠您1000积分`);
      return;
    }
    await session.send(`您当前积分为:${data.value.toString()}`);
  })

  ctx
    .command('商品上架 <productName> <quantity:number>').userFields(["id"])
    .action(async ({ session }, productName, quantity = 1) => {
      const userStash = shop.initUserData(session.userId)
      console.log(userStash);

      if (!productName) {
        await session.send('请输入要上架市场的物品')
        return
      }

      if (!userStash[productName]) {
        await session.send('你的仓库不存在该物品')
        return
      }
      await session.send('您准备对商品定价多少?(20秒内回复)')
      const res = await session.prompt(20000)
      if (res === undefined) return
      if (isNaN(Number(res))) {
        await session.send('输入有误,结束提交')
        return
      }
      const price = Math.floor(Number(res))
      await shop.uploadProduct(session, productName, price, quantity)
    })

  ctx
    .command('查看库存')
    .action(async ({ session }) => {
      const userId = session.userId
      const userStash: UserStash = shop.initUserData(userId)
      const stashKeyList = Object.keys(userStash)

      if (!stashKeyList.length) {
        await session.send('你当前没有任何库存')
        return
      }

      const msg = stashKeyList.map((item) => {
        return `[${userStash[item].name}] 持有 ${userStash[item].total} (市场价:${userStash[item].price})`
      }).join('\n')

      await session.send(msg)
    })


  ctx.economy.on('recycle', async (event: Economy_EventData<Economy_RecycleData>) => {
    const userId = event.session.userId
    const userStash: UserStash = shop.initUserData(userId)
    event.upValue.forEach((item) => {
      // 操作每个数据退回到用户仓库
      const productItem = foodList.find((i) => i.name === item.name)
      if (!productItem) return

      const getData = { ...productItem, total: item.total }
      if (!userStash[item.name]) {
        userStash[item.name] = getData
      } else {
        userStash[item.name].total += item.total
      }
    })
    // 操作完成后,通知服务
    shop.updataStore(userId)
    event.isOver = true
  }, 'smmcat-shop')


  ctx.on('ready', () => {
    initStoreFoodData()
    shop.init()
  })

}


对应的数据 data.json 来源

[
    {
        "name": "猪脚饭",
        "pic": "https://smmcat.cn/run/fish/props/64/猪脚饭.png",
        "type": "食物",
        "price": 13,
        "info": "打工人的一碗猪脚饭"
    },
    {
        "name": "烧鸡",
        "pic": "https://smmcat.cn/run/fish/props/64/水果鸡肉.png",
        "type": "食物",
        "price": 15,
        "info": "真是一个不折不扣的烧鸡"
    },
    {
        "name": "维他柠檬茶",
        "pic": "https://smmcat.cn/run/fish/props/64/快乐水.png",
        "type": "饮料",
        "price": 6,
        "info": "维他柠檬茶,爽过吸大码"
    }
]

效果展示

上架操作



购买操作




收益统计

商店日志

服务自带指令

2 个赞