|
@@ -0,0 +1,252 @@
|
|
|
|
|
+import { reactive, computed, onUnmounted } from 'vue'
|
|
|
|
|
+import { defineStore } from 'pinia'
|
|
|
|
|
+import { isMatch } from 'lodash'
|
|
|
|
|
+import { TaskQueue } from '@/utils/queue'
|
|
|
|
|
+import { queryMemberGoodsLimitConfig } from '@/services/api/common'
|
|
|
|
|
+import { queryErmcpGoods, queryQuoteDay } from '@/services/api/goods'
|
|
|
|
|
+import Worker from 'worker-loader!./thread'
|
|
|
|
|
+import Service from '../service'
|
|
|
|
|
+
|
|
|
|
|
+const worker = new Worker()
|
|
|
|
|
+
|
|
|
|
|
+// 初始化 Worker
|
|
|
|
|
+Service.onReady(() => {
|
|
|
|
|
+ worker.postMessage({
|
|
|
|
|
+ type: 'init',
|
|
|
|
|
+ data: {
|
|
|
|
|
+ url: Service.getServiceConfig('quoteUrl'),
|
|
|
|
|
+ protocols: []
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+export const useGoodsStore = defineStore('goods', () => {
|
|
|
|
|
+ const state = reactive({
|
|
|
|
|
+ loading: false,
|
|
|
|
|
+ goodsMap: new Map<number, Model.GoodsRsp>(), // 商品合集
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ const goodsList = computed(() => Array.from(state.goodsMap.values()))
|
|
|
|
|
+
|
|
|
|
|
+ const getGoodsById = (id: number) => state.goodsMap.get(id)
|
|
|
|
|
+
|
|
|
|
|
+ // 获取商品对象
|
|
|
|
|
+ const getGoodsItem = (query: Partial<Model.GoodsQuote>) => {
|
|
|
|
|
+ return goodsList.value.find((item) => isMatch(item, query))
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 获取会员商品列表
|
|
|
|
|
+ const fetchGoods = async () => {
|
|
|
|
|
+ if (state.loading) return
|
|
|
|
|
+ state.loading = true
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 待优化,缓存到 userStore
|
|
|
|
|
+ const { data: configs } = await queryMemberGoodsLimitConfig({
|
|
|
|
|
+ data: {
|
|
|
|
|
+ roletype: 7
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ const { data: goodsItems } = await queryErmcpGoods()
|
|
|
|
|
+
|
|
|
|
|
+ const configMap = new Map(configs.map(config => [config.goodsid, config]))
|
|
|
|
|
+
|
|
|
|
|
+ const filteredItems = goodsItems.filter((item) => configMap.get(item.goodsid)?.isnodisplay !== 1)
|
|
|
|
|
+
|
|
|
|
|
+ // 根据 goodscode 字母升序排序
|
|
|
|
|
+ const sortedItems = filteredItems.sort((a, b) => a.goodscode.localeCompare(b.goodscode))
|
|
|
|
|
+
|
|
|
|
|
+ state.goodsMap = new Map(sortedItems.map((item) => [item.goodsid, item]))
|
|
|
|
|
+
|
|
|
|
|
+ worker.postMessage({
|
|
|
|
|
+ type: 'updateGoods',
|
|
|
|
|
+ data: sortedItems,
|
|
|
|
|
+ })
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ state.loading = false
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ goodsList,
|
|
|
|
|
+ fetchGoods,
|
|
|
|
|
+ getGoodsById,
|
|
|
|
|
+ getGoodsItem
|
|
|
|
|
+ }
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+export const useQuoteStore = defineStore('quote', () => {
|
|
|
|
|
+ const goodsStore = useGoodsStore()
|
|
|
|
|
+ const taskQueue = new TaskQueue()
|
|
|
|
|
+ const subscribeMap = new Map<symbol, string[]>()
|
|
|
|
|
+
|
|
|
|
|
+ const state = reactive({
|
|
|
|
|
+ loading: false,
|
|
|
|
|
+ quoteServerConnected: false,
|
|
|
|
|
+ quoteMap: new Map<string, Model.QuoteDayRsp>(), // 盘面合集
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 行情服务连接状态
|
|
|
|
|
+ const quoteServerConnected = computed(() => state.quoteServerConnected)
|
|
|
|
|
+
|
|
|
|
|
+ // 商品行情
|
|
|
|
|
+ const quoteList = computed(() => {
|
|
|
|
|
+ const result = []
|
|
|
|
|
+ for (const goods of goodsStore.goodsList) {
|
|
|
|
|
+ const quote = state.quoteMap.get(goods.goodscode)
|
|
|
|
|
+ if (quote) {
|
|
|
|
|
+ result.push({ goods, quote })
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return result
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ const getQuoteByCode = (code: string) => state.quoteMap.get(code)
|
|
|
|
|
+
|
|
|
|
|
+ // 获取行情对象
|
|
|
|
|
+ const getQuoteItem = (query: Partial<Model.GoodsQuote>) => {
|
|
|
|
|
+ return quoteList.value.find(({ goods }) => isMatch(goods, query))
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 获取盘面列表
|
|
|
|
|
+ const fetchQuotes = async () => {
|
|
|
|
|
+ if (state.loading || !goodsStore.goodsList.length) return
|
|
|
|
|
+ state.loading = true
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await queryQuoteDay({
|
|
|
|
|
+ data: {
|
|
|
|
|
+ goodsCodes: goodsStore.goodsList.map((item) => item.goodscode).join(',')
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ state.quoteMap = new Map(res.data.map((item) => [item.goodscode, item]))
|
|
|
|
|
+
|
|
|
|
|
+ worker.postMessage({
|
|
|
|
|
+ type: 'updateQuotes',
|
|
|
|
|
+ data: res.data
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ taskQueue.run()
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ state.loading = false
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 行情订阅
|
|
|
|
|
+ const subscribe = (goodsCodes: string | string[], key = Symbol()) => {
|
|
|
|
|
+ const values = Array.isArray(goodsCodes) ? goodsCodes : [goodsCodes]
|
|
|
|
|
+ subscribeMap.set(key, values)
|
|
|
|
|
+
|
|
|
|
|
+ const codes = Array.from(subscribeMap.values()).flat() // 扁平化
|
|
|
|
|
+ const uniqueCodes = Array.from(new Set(codes)) // 去重处理
|
|
|
|
|
+
|
|
|
|
|
+ worker.postMessage({
|
|
|
|
|
+ type: 'subscribe',
|
|
|
|
|
+ data: uniqueCodes.map(code => ({
|
|
|
|
|
+ exchangeCode: 250,
|
|
|
|
|
+ goodsCode: code,
|
|
|
|
|
+ subState: 0
|
|
|
|
|
+ }))
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ const cancel = () => {
|
|
|
|
|
+ if (subscribeMap.has(key)) {
|
|
|
|
|
+ subscribeMap.delete(key)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ onUnmounted(() => cancel())
|
|
|
|
|
+
|
|
|
|
|
+ return cancel
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const onReady = (callback: () => void) => {
|
|
|
|
|
+ if (state.quoteMap.size) {
|
|
|
|
|
+ setTimeout(() => callback(), 0)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ const cancel = taskQueue.add(callback)
|
|
|
|
|
+ onUnmounted(() => cancel())
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 监听来自 Worker 的消息
|
|
|
|
|
+ worker.onmessage = (event) => {
|
|
|
|
|
+ const message = event.data
|
|
|
|
|
+
|
|
|
|
|
+ if (typeof message === 'object') {
|
|
|
|
|
+ switch (message.type) {
|
|
|
|
|
+ case 'ready': {
|
|
|
|
|
+ console.log('QuoteSocket is ready')
|
|
|
|
|
+ state.quoteServerConnected = true
|
|
|
|
|
+ break
|
|
|
|
|
+ }
|
|
|
|
|
+ case 'closed': {
|
|
|
|
|
+ console.log('QuoteSocket is closed')
|
|
|
|
|
+ state.quoteServerConnected = false
|
|
|
|
|
+ break
|
|
|
|
|
+ }
|
|
|
|
|
+ case 'publish': {
|
|
|
|
|
+ if (!state.loading) {
|
|
|
|
|
+ const item = message.data
|
|
|
|
|
+ state.quoteMap.set(item.goodscode, item)
|
|
|
|
|
+ }
|
|
|
|
|
+ break
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ console.warn('Error from worker:', event.data)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ quoteServerConnected,
|
|
|
|
|
+ quoteList,
|
|
|
|
|
+ onReady,
|
|
|
|
|
+ fetchQuotes,
|
|
|
|
|
+ getQuoteByCode,
|
|
|
|
|
+ getQuoteItem,
|
|
|
|
|
+ subscribe
|
|
|
|
|
+ }
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+export const useMarketStore = defineStore('market', () => {
|
|
|
|
|
+ const quoteStore = useQuoteStore()
|
|
|
|
|
+
|
|
|
|
|
+ const state = reactive({
|
|
|
|
|
+ selectedMarketIdsSet: new Set<number>(),
|
|
|
|
|
+ selectedGoodsId: 0, // 当前选中的商品ID
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ const marketQuoteList = computed(() => quoteStore.quoteList.filter((e) => state.selectedMarketIdsSet.has(e.goods.marketid)))
|
|
|
|
|
+
|
|
|
|
|
+ // const getMarketIdByTradeMode = (values: number | number[]) => {
|
|
|
|
|
+ // const tradeMode = Array.isArray(values) ? values : [values]
|
|
|
|
|
+ // }
|
|
|
|
|
+
|
|
|
|
|
+ const selectMarkets = (values: number | number[]) => {
|
|
|
|
|
+ const ids = new Set(Array.isArray(values) ? values : [values])
|
|
|
|
|
+ state.selectedMarketIdsSet = ids
|
|
|
|
|
+
|
|
|
|
|
+ const list = marketQuoteList.value
|
|
|
|
|
+ if (list.length) {
|
|
|
|
|
+ // 查找当前选中的商品ID
|
|
|
|
|
+ if (!list.every((e) => e.goods.goodsid === state.selectedGoodsId)) {
|
|
|
|
|
+ state.selectedGoodsId = list[0].goods.goodsid
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ state.selectedGoodsId = 0
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const selectGoods = (id: number) => {
|
|
|
|
|
+ state.selectedGoodsId = id
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ marketQuoteList,
|
|
|
|
|
+ selectMarkets,
|
|
|
|
|
+ selectGoods,
|
|
|
|
|
+ }
|
|
|
|
|
+})
|