import { computed, toRefs, shallowReadonly } from 'vue' import { timerInterceptor } from '@/utils/timer' import { queryErmcpGoods, queryQuoteDay } from '@/services/api/goods' import { VueStore } from '../base' import { timerTask } from '@/utils/timer' import eventBus from '@/services/bus' import moment from 'moment' interface StoreState { loading: boolean; goodsList: Model.GoodsRsp[]; // 商品列表 quoteDayList: Model.QuoteDayRsp[]; // 盘面列表 } /** * 期货存储类 */ const store = new (class extends VueStore{ constructor() { const state: StoreState = { loading: false, goodsList: [], quoteDayList: [], } super(state) // 接收行情推送通知 eventBus.$on('QuotePushNotify', (res) => { const data = res as Proto.Quote[] data.forEach((item) => { const index = this.quotes.findIndex((e) => e.goodscode === item.goodscode) if (index > -1) { this.quotes[index] = item } else { this.quotes.push(item) } }) this.handleQuote() }) } /** 行情数据 */ private quotes: Proto.Quote[] = [] /** 期货行情列表 */ private quoteList = computed(() => { return this.state.goodsList.reduce((res, cur) => { const { goodscode, goodsname, goodsgroupid, marketid, decimalplace } = cur const { last = 0, bid = 0, ask = 0, bidvolume = 0, askvolume = 0, totalvolume = 0, lastvolume = 0, holdvolume = 0, holdincrement = 0, presettle = 0, totalturnover = 0, opened = 0, highest = 0, lowest = 0, lasttime = '', } = this.actions.getQuoteDayInfoByCode(goodscode).value ?? {} const change = last === 0 ? 0 : last - presettle // 涨跌额/涨跌: 最新价 - 昨结价 const item: Model.Futures = { marketid, goodsgroupid, goodscode, goodsname, decimalplace, last, lasttime, amplitude: change === 0 ? 0 : change / presettle, // 涨跌幅/幅度: (最新价 - 昨结价) / 昨结价 change, bid, ask, bidvolume, askvolume, totalvolume, lastvolume, holdvolume, holdincrement, presettle, totalturnover, opened, highest, lowest, } res.push(item) return res }, [] as Model.Futures[]) }) /** * 处理行情数据 */ private handleQuote = timerInterceptor.setThrottle(() => { const goodsList = this.state.goodsList const quoteList = this.state.quoteDayList this.quotes.forEach((item) => { const goods = goodsList.find((e) => e.goodscode.toUpperCase() === item.goodscode?.toUpperCase()) const quote = quoteList.find((e) => e.goodscode.toUpperCase() === item.goodscode?.toUpperCase()) const lasttime = (item.date && item.time) ? moment(item.date + item.time, 'YYYYMMDDHHmmss').format('YYYY-MM-DD HH:mm:ss') : '' // 处理报价小数位 const last = (() => { const decimal = goods?.decimalplace ?? 0 const num = Math.pow(10, decimal) if (item.last) { return item.last / num } return 0 })() if (quote) { Object.entries(item).forEach(([key, value]) => { // 只更新存在的属性 if (Reflect.has(quote, key)) { const prop = key as keyof Model.QuoteDayRsp; ((key: K) => quote[key] = value)(prop); } }) quote.last = last // 处理最高最低价 if (last) { if (last > quote.highest) { quote.highest = last; } if (last < quote.lowest) { quote.lowest = last; } } // 处理最新时间 if (lasttime) { quote.lasttime = lasttime; } } else { console.warn('行情推送的商品 ' + item.goodscode + ' 缺少盘面信息') quoteList.push({ ask: item.ask ?? 0, ask2: item.ask2 ?? 0, ask3: item.ask3 ?? 0, ask4: item.ask4 ?? 0, ask5: item.ask5 ?? 0, ask6: 0, ask7: 0, ask8: 0, ask9: 0, ask10: 0, askorderid: 0, askorderid2: 0, askorderid3: 0, askorderid4: 0, askorderid5: 0, askordervolume: 0, askordervolume2: 0, askordervolume3: 0, askordervolume4: 0, askordervolume5: 0, askordervolume6: 0, askordervolume7: 0, askordervolume8: 0, askordervolume9: 0, askordervolume10: 0, askqueueinfo: "", askvolume: item.askvolume ?? 0, askvolume2: item.askvolume2 ?? 0, askvolume3: item.askvolume3 ?? 0, askvolume4: item.askvolume4 ?? 0, askvolume5: item.askvolume5 ?? 0, askvolume6: 0, askvolume7: 0, askvolume8: 0, askvolume9: 0, askvolume10: 0, averageprice: 0, bid: item.bid ?? 0, bid2: item.bid2 ?? 0, bid3: item.bid3 ?? 0, bid4: item.bid4 ?? 0, bid5: item.bid5 ?? 0, bid6: 0, bid7: 0, bid8: 0, bid9: 0, bid10: 0, bidorderid: 0, bidorderid2: 0, bidorderid3: 0, bidorderid4: 0, bidorderid5: 0, bidordervolume: 0, bidordervolume2: 0, bidordervolume3: 0, bidordervolume4: 0, bidordervolume5: 0, bidordervolume6: 0, bidordervolume7: 0, bidordervolume8: 0, bidordervolume9: 0, bidordervolume10: 0, bidqueueinfo: "", bidvolume: item.bidvolume ?? 0, bidvolume2: item.bidvolume2 ?? 0, bidvolume3: item.bidvolume3 ?? 0, bidvolume4: item.bidvolume4 ?? 0, bidvolume5: item.bidvolume5 ?? 0, bidvolume6: 0, bidvolume7: 0, bidvolume8: 0, bidvolume9: 0, bidvolume10: 0, calloptionpremiums: item.calloptionpremiums ?? 0, calloptionpremiums2: item.calloptionpremiums2 ?? 0, calloptionpremiums3: item.calloptionpremiums3 ?? 0, calloptionpremiums4: item.calloptionpremiums4 ?? 0, calloptionpremiums5: item.calloptionpremiums5 ?? 0, cleartime: 0, exchangecode: item.exchangecode ?? 0, exchangedate: item.exchangedate ?? 0, goodscode: item.goodscode ?? '', grepmarketprice: 0, highest: item.highest ?? 0, holdincrement: 0, holdvolume: item.holdvolume ?? 0, iep: 0, iev: 0, inventory: item.inventory ?? 0, iscleared: 0, issettled: 0, last, lastlot: 0, lasttime, Lastturnover: 0, lastvolume: item.lastvolume ?? 0, limitdown: item.limitlow ?? 0, limitup: item.limithigh ?? 0, lowest: item.lowest ?? 0, nontotalholdervolume: 0, nontotallot: 0, nontotalturnover: 0, nontotalvolume: 0, opened: item.opened ?? 0, opentime: '', orderid: 0, preclose: item.preclose ?? 0, preholdvolume: item.preholdvolume ?? 0, presettle: item.presettle ?? 0, publictradetype: '', putoptionpremiums: item.putoptionpremiums ?? 0, putoptionpremiums2: item.putoptionpremiums2 ?? 0, putoptionpremiums3: item.putoptionpremiums3 ?? 0, putoptionpremiums4: item.putoptionpremiums4 ?? 0, putoptionpremiums5: item.putoptionpremiums5 ?? 0, settle: item.settle ?? 0, strikeprice: 0, totalaskvolume: 0, totalbidvolume: 0, totallot: 0, totalturnover: item.totalturnover ?? 0, totalvolume: item.totalvolume ?? 0, utclasttime: '' }) } }) }, 200) getters = { quoteList: this.quoteList } actions = { // 获取商品列表 getGoodsList: () => { this.state.loading = true return queryErmcpGoods({ success: (res) => { const codes = res.data.map((e) => e.goodscode) this.state.goodsList = res.data // 获取商品盘面信息 const getQuoteDay = () => { return queryQuoteDay({ data: { goodsCodes: codes.join(',') }, success: (res) => { this.state.quoteDayList = res.data }, complete: () => { // 每5分钟获取一次盘面 timerTask.setTimeout(() => getQuoteDay(), 5 * 60 * 1000, 'quoteDay') } }) } if (codes.length) { getQuoteDay() } }, complete: () => { this.state.loading = false } }) }, /** 通过 goodscode 获取盘面实时行情 */ getQuoteDayInfoByCode: (goodscode: string) => { return computed(() => this.state.quoteDayList.find((e) => e.goodscode.toUpperCase() === goodscode.toUpperCase())) }, /** 通过 goodscode 获取商品实时报价 */ getGoodsPriceByCode: (goodscode: string) => { return computed(() => { const quote = this.actions.getQuoteDayInfoByCode(goodscode) return quote.value?.last ?? 0 }) } } }) export function useFuturesStore() { return shallowReadonly({ ...toRefs(store.state), ...store.getters, ...store.actions, ...store.methods, }) }