li.shaoyi %!s(int64=2) %!d(string=hai) anos
pai
achega
26b957a9da

+ 1 - 1
app/package.json

@@ -1,6 +1,6 @@
 {
   "name": "trading",
-  "version": "1.0.16",
+  "version": "1.0.13",
   "main": "main.js",
   "dependencies": {
     "electron-updater": "^6.1.4",

+ 2 - 2
oem/tjmd/config/appconfig.json

@@ -1,8 +1,8 @@
 {
   "appId": "com.muchinfo.tjmd",
   "appName": "海南掉期市场",
-  "version": "1.0.12",
-  "versionCode": "100012",
+  "version": "1.0.13",
+  "versionCode": "100013",
   "apiUrl": "http://192.168.31.132:8080/cfg?key=test_132",
   "tradeChannel": "ws",
   "modules": [

+ 3 - 3
oem/tjmd/manifest.json

@@ -5,9 +5,9 @@
     "name" : "海南掉期市场",
     /*应用名称,程序桌面图标名称*/
     "version" : {
-        "name" : "1.0.11",
+        "name" : "1.0.13",
         /*应用版本名称*/
-        "code" : 100011
+        "code" : 100013
     },
     "description" : "",
     /*应用描述信息*/
@@ -155,7 +155,7 @@
                 ],
                 "abiFilters" : [ "armeabi-v7a", "arm64-v8a" ],
                 "autoSdkPermissions" : false,
-                "minSdkVersion" : 29
+                "minSdkVersion" : 26
             },
             /*使用Native.js调用原生安卓API需要使用到的系统权限*/
             "orientation" : [ "portrait-primary" ],

+ 1 - 1
package.json

@@ -35,7 +35,7 @@
     "lint": "vue-cli-service lint",
     "dev": "electron .",
     "build": "node app/electron.build.js",
-    "proto": "npx pbjs -t json-module -w commonjs -o public/proto/mtp.js public/proto/mtp.proto"
+    "proto": "npx pbjs -t json-module -w commonjs -o public/proto/mtp.js public/proto/*.proto"
   },
   "dependencies": {
     "@tinymce/tinymce-vue": "^5.0.0",

+ 39 - 0
public/proto/config.proto

@@ -0,0 +1,39 @@
+//保证金信息
+message MarginInfoStruct{
+    optional uint64 AccountID = 1;                // 资金账号
+    optional uint32 GoodsID = 2;                  // 商品id 
+    optional uint32 MarginAlgorithm = 3;          // 保证金计算方式
+    optional double MarketMarginValue = 4;        // 即市保证金值
+    optional double ReckonMarginValue = 5;        // 结算保证金值
+    optional double LockMarginValue = 6;          // 锁仓保证金
+    optional double RealMarginValue = 7;          // 实付比例
+    optional uint32 RealMarginAlgorithm = 8;      // 实付保证金计算方式
+}
+
+//交易规则信息子集
+message TradeRule{
+    optional uint32 RuleID = 1;                   // 交易规则ID
+    optional double ParamValue = 2;               // 参数值
+}
+
+//交易规则信息
+message TradeRuleInfoStruct{
+    optional uint64 AccountID = 1;                // 资金账号
+    optional uint32 GoodsID = 2;                  // 商品id 
+    repeated TradeRule TradeRules = 3;            // 交易规则
+}
+
+//交易费用信息子集
+message TradeFee{
+    optional uint32 FeeID = 1;                    // 交易规则ID
+    optional uint32 FeeAlgorithm = 2;             // 费用算法
+    optional double ExchangeValue = 3;            // 交易所费用值
+    optional double MemberDefaultValue = 4;       // 会员费用默认值
+}
+
+//交易费用信息
+message TradeFeeInfoStruct{
+    optional uint64 AccountID = 1;                // 资金账号
+    optional uint32 GoodsID = 2;                  // 商品id 
+    repeated TradeFee TradeFees = 3;              // 交易费用
+}

+ 102 - 0
public/proto/mtp.js

@@ -5,6 +5,108 @@ var $protobuf = require("protobufjs/light");
 
 var $root = ($protobuf.roots["default"] || ($protobuf.roots["default"] = new $protobuf.Root()))
 .addJSON({
+  MarginInfoStruct: {
+    fields: {
+      AccountID: {
+        type: "uint64",
+        id: 1
+      },
+      GoodsID: {
+        type: "uint32",
+        id: 2
+      },
+      MarginAlgorithm: {
+        type: "uint32",
+        id: 3
+      },
+      MarketMarginValue: {
+        type: "double",
+        id: 4
+      },
+      ReckonMarginValue: {
+        type: "double",
+        id: 5
+      },
+      LockMarginValue: {
+        type: "double",
+        id: 6
+      },
+      RealMarginValue: {
+        type: "double",
+        id: 7
+      },
+      RealMarginAlgorithm: {
+        type: "uint32",
+        id: 8
+      }
+    }
+  },
+  TradeRule: {
+    fields: {
+      RuleID: {
+        type: "uint32",
+        id: 1
+      },
+      ParamValue: {
+        type: "double",
+        id: 2
+      }
+    }
+  },
+  TradeRuleInfoStruct: {
+    fields: {
+      AccountID: {
+        type: "uint64",
+        id: 1
+      },
+      GoodsID: {
+        type: "uint32",
+        id: 2
+      },
+      TradeRules: {
+        rule: "repeated",
+        type: "TradeRule",
+        id: 3
+      }
+    }
+  },
+  TradeFee: {
+    fields: {
+      FeeID: {
+        type: "uint32",
+        id: 1
+      },
+      FeeAlgorithm: {
+        type: "uint32",
+        id: 2
+      },
+      ExchangeValue: {
+        type: "double",
+        id: 3
+      },
+      MemberDefaultValue: {
+        type: "double",
+        id: 4
+      }
+    }
+  },
+  TradeFeeInfoStruct: {
+    fields: {
+      AccountID: {
+        type: "uint64",
+        id: 1
+      },
+      GoodsID: {
+        type: "uint32",
+        id: 2
+      },
+      TradeFees: {
+        rule: "repeated",
+        type: "TradeFee",
+        id: 3
+      }
+    }
+  },
   MessageHead: {
     fields: {
       FunCode: {

+ 1 - 1
src/business/position/index.ts

@@ -23,7 +23,7 @@ export const usePosition = (...tradeMode: number[]) => {
 
     onMounted(() => {
         store.getTradePosition().then(() => {
-            const goodsCodes = positionList.value.map((e) => e.goodscode)
+            const goodsCodes = positionList.value.map((e) => e.refgoodscode || e.goodscode)
             subscribe.start(...goodsCodes)
         })
     })

+ 0 - 1
src/packages/mobile/components/base/router-transition/index.less

@@ -2,7 +2,6 @@
 @transition-duration: 220ms;
 
 /* 无过渡效果 */
-.delay-enter-active,
 .delay-leave-active {
     transition-duration: @transition-duration;
 }

+ 2 - 0
src/packages/mobile/components/modules/quote/tik/index.vue

@@ -59,6 +59,8 @@ useRequest(queryMarketRun, {
                 startTime: formatDate(market.startTime),
                 endTime: formatDate(market.endTime),
             })
+        } else {
+            getHistoryTikDatas()
         }
     }
 })

+ 1 - 1
src/packages/mobile/views/order/position/components/swap/list/Index.vue

@@ -13,7 +13,7 @@
                 </div>
                 <div class="g-order-list__content">
                     <ul>
-                        <li :class="lastColor(item.goodscode)">{{ last(item.goodscode) }}</li>
+                        <li :class="lastColor(item.refgoodscode)">{{ last(item.refgoodscode) }}</li>
                     </ul>
                     <ul>
                         <li>

+ 32 - 53
src/packages/mobile/views/swap/detail/Index.vue

@@ -11,29 +11,28 @@
         </template>
         <component :is="Price" v-bind="{ goodsCode }" />
         <component :is="Chart" v-bind="{ goodsCode }" />
+        <Tabs v-model:active="tabIndex" @click="onRefresh" style="margin-top: .2rem;">
+            <Tab title="买大厅" :name="BuyOrSell.Buy" />
+            <Tab title="卖大厅" :name="BuyOrSell.Sell" />
+        </Tabs>
+        <div class="trade-section sell" v-if="dataList.length">
+            <app-list :columns="columns" :data-list="dataList">
+                <template #orderprice="{ row }">
+                    <span :class="quote?.lastColor">{{ row.pricemode === 3 ? estimateprice(row) : row.orderprice
+                    }}</span>
+                </template>
+                <template #username="{ row }">
+                    <span>{{ row.userid }}/{{ row.username }}</span>
+                </template>
+                <template #operate="{ row }">
+                    <Button size="small" type="primary" :disabled="isDisable(row)" @click="onDelisting(row)">
+                        {{ getDelistingButtonTitles(tabIndex, row) }}
+                    </Button>
+                </template>
+            </app-list>
+        </div>
+        <Empty description="暂无数据" v-else />
         <component :is="Tik" v-bind="{ goodsCode }" />
-        <app-pull-refresh ref="pullRefreshRef" v-model:loading="loading" v-model:error="error" v-model:pageIndex="pageIndex"
-            :page-count="pageCount" @refresh="onRefresh">
-            <Tabs v-model:active="tabIndex" @click="onTabChange">
-                <Tab title="买大厅" :name="BuyOrSell.Buy" />
-                <Tab title="卖大厅" :name="BuyOrSell.Sell" />
-            </Tabs>
-            <div class="trade-section sell" v-if="dataList.length">
-                <app-list :columns="columns" :data-list="dataList">
-                    <template #orderprice="{ row }">
-                        <span :class="quote?.lastColor">{{ row.pricemode === 3 ? estimateprice(row) : row.orderprice }}</span>
-                    </template>
-                    <template #username="{ row }">
-                        <span>{{ row.userid }}/{{ row.username }}</span>
-                    </template>
-                    <template #operate="{ row }">
-                        <Button size="small" type="primary" :disabled="isDisable(row)" @click="onDelisting(row)">
-                            {{ getDelistingButtonTitles(tabIndex, row) }}
-                        </Button>
-                    </template>
-                </app-list>
-            </div>
-        </app-pull-refresh>
         <component ref="componentRef" :is="componentMap.get(componentId)" v-bind="{ selectedRow, tabIndex, item }"
             @closed="closeComponent" v-if="componentId" />
     </app-view>
@@ -41,7 +40,7 @@
 
 <script lang="ts" setup>
 import { shallowRef, defineAsyncComponent, computed } from 'vue'
-import { Tab, Tabs, Button, showToast } from 'vant'
+import { Tab, Tabs, Button, showToast,Empty } from 'vant'
 import { useRequest } from '@/hooks/request'
 import { useNavigation } from '@mobile/router/navigation'
 import { useComponent } from '@/hooks/component'
@@ -49,13 +48,12 @@ import { BuyOrSell } from '@/constants/order'
 import { queryTjmdTradeOrderDetail, queryMdUserSwapProtocol } from '@/services/api/swap'
 import { useFuturesStore } from '@/stores'
 import { useLoginStore, useUserStore } from '@/stores'
-import AppPullRefresh from '@mobile/components/base/pull-refresh/index.vue'
 import AppList from '@mobile/components/base/list/index.vue'
 import { getUserId } from '@/services/methods/user'
 import { formatDecimal } from '@/filters'
 
 const Price = defineAsyncComponent(() => import('@mobile/components/modules/quote/price/index.vue'))
-const Chart = defineAsyncComponent(() => import('@mobile/components/modules/quote/chart/index.vue'))
+const Chart = defineAsyncComponent(() => import('@mobile/components/modules/hqchart/index.vue'))
 const Tik = defineAsyncComponent(() => import('@mobile/components/modules/quote/tik/index.vue'))
 
 const componentMap = new Map<string, unknown>([
@@ -63,23 +61,20 @@ const componentMap = new Map<string, unknown>([
     ['listing', defineAsyncComponent(() => import('./components/listing/Index.vue'))],
 ])
 
-const pullRefreshRef = shallowRef()
 const loginStore = useLoginStore()
 const { getGlobalUrlParams, router } = useNavigation()
 const item: Model.QuoteGoodsListRsp = getGlobalUrlParams()
 const tabIndex = shallowRef(0)
 const selectedRow = shallowRef<Model.TjmdTradeOrderDetailRsp>()
-const error = shallowRef(false)
-const dataList = shallowRef<Model.TjmdTradeOrderDetailRsp[]>([])
 const futuresStore = useFuturesStore()
 const quote = futuresStore.getGoodsQuote(item.refgoodsid)
 const goodsCode = computed(() => quote.value?.goodscode ?? '')
 const userStore = useUserStore()
 
-const { componentRef, componentId, openComponent, closeComponent } = useComponent(() => onTabChange())
+const { componentRef, componentId, openComponent, closeComponent } = useComponent(() => onRefresh())
 
 /// 查询用户掉期协议签署表
-const { dataList: protocolList} = useRequest(queryMdUserSwapProtocol, {
+const { dataList: protocolList } = useRequest(queryMdUserSwapProtocol, {
     params: {
         userId: getUserId()
     }
@@ -88,37 +83,27 @@ const { dataList: protocolList} = useRequest(queryMdUserSwapProtocol, {
 /// 估算价格
 const estimateprice = (row: Model.TjmdTradeOrderDetailRsp) => {
     const decimalplace = (item.decimalplace ?? 2.0)
-    return formatDecimal((row.marketmaxsub+(quote.value?.last ?? 0.0)), decimalplace)
+    return formatDecimal((row.marketmaxsub + (quote.value?.last ?? 0.0)), decimalplace)
 }
 
 const isDisable = (row: Model.TjmdTradeOrderDetailRsp) => {
-    return row.userid === getUserId() || ( status(row) === 2 || status(row) === 3 )
+    return row.userid === getUserId() || (status(row) === 2 || status(row) === 3)
 }
 
 const status = (row: Model.TjmdTradeOrderDetailRsp) => {
     return protocolList.value.find((e) => e.areauserid === row.userid)?.protocolstatus ?? 1
 }
 
-const { pageIndex, loading, run, pageCount } = useRequest(queryTjmdTradeOrderDetail, {
+const { dataList, run } = useRequest(queryTjmdTradeOrderDetail, {
     params: {
-        pagesize: 20,
         goodsid: item.goodsid,
         buyorsell: tabIndex.value
-    },
-    onSuccess: (res) => {
-        if (pageIndex.value === 1) {
-            dataList.value = []
-        }
-        dataList.value.push(...res.data)
-    },
-    onError: () => {
-        error.value = true
     }
 })
 
 const getDelistingButtonTitles = (tab: number, row: Model.TjmdTradeOrderDetailRsp) => {
     /// 如果是机构自己的单 不能签署
-    if (row.userid === getUserId() ) {
+    if (row.userid === getUserId()) {
         return '摘牌'
     }
     switch (status(row)) {
@@ -137,7 +122,7 @@ const getDelistingButtonTitles = (tab: number, row: Model.TjmdTradeOrderDetailRs
 setTimeout(() => {
     // 获取买卖大厅数据
     run()
-}, 30*1000)
+}, 30 * 1000)
 
 const onRefresh = () => {
     run({
@@ -145,12 +130,6 @@ const onRefresh = () => {
     })
 }
 
-const onTabChange = () => {
-    /// 重置为1
-    pageIndex.value = 1
-    onRefresh()
-}
-
 const columns: Model.TableColumn[] = [
     { prop: 'orderprice', label: '价格' },
     { prop: 'orderqty', label: '数量' },
@@ -172,8 +151,8 @@ const onDelisting = (row: Model.TjmdTradeOrderDetailRsp) => {
         case 1:  /// 未签署
             /// 传对应的机构 ID
             /// 进行下一步
-            router.push({ name: 'account-protocol', query: { memberUserId: row.userid.toString() }})
-            break; 
+            router.push({ name: 'account-protocol', query: { memberUserId: row.userid.toString() } })
+            break;
         default: /// 其他状态
             break;
     }

+ 27 - 7
src/packages/pc/assets/themes/global/global.less

@@ -15,16 +15,36 @@
     }
 }
 
-.g-price-up {
-    color: #ff3333;
-}
+.g-price {
+    &-normal {
+        color: #333333;
+    }
+
+    &-up {
+        color: #ff3333;
+    }
 
-.g-price-normal {
-    color: #333333;
+    &-down {
+        color: #0baf1f;
+    }
 }
 
-.g-price-down {
-    color: #0baf1f;
+.g-risk {
+    &-green {
+        color: #0baf1f;
+    }
+
+    &-red {
+        color: #ff3333;
+    }
+
+    &-orange {
+        color: orange;
+    }
+
+    &-yellow {
+        color: yellow;
+    }
 }
 
 .g-text-message {

+ 5 - 1
src/packages/pc/components/layouts/header/components/notice/index.vue

@@ -78,7 +78,11 @@ const collapseChange = (index: number) => {
     noticeStore.updateNoticeReaded(item.autoid)
 }
 
-onMounted(() => tabChange(selectedTab.value))
+onMounted(() => {
+    noticeStore.getNoticeList().then(() => {
+        tabChange(selectedTab.value)
+    })
+})
 </script>
 
 <style lang="less">

+ 1 - 1
src/packages/pc/components/layouts/page/index.vue

@@ -47,7 +47,7 @@
           <li>
             <el-popover trigger="hover" title="公式" content="风险率 = (占用 / 净值) * 100%" :width="220">
               <template #reference>
-                <span style="cursor: pointer;">风险率:{{ parsePercent(accountStore.currentAccount.hazardRatio) }}</span>
+                <span :class="accountStore.currentAccount.hazardRatioColor" style="cursor: pointer;">风险率:{{ parsePercent(accountStore.currentAccount.hazardRatio) }}</span>
               </template>
             </el-popover>
           </li>

+ 4 - 2
src/packages/pc/components/modules/quote/tik/index.vue

@@ -40,7 +40,7 @@ const { run: getHistoryTikDatas } = useRequest(queryHistoryTikDatas, {
     manual: true,
     params: {
         goodsCode: props.goodsCode,
-        count: 20
+        count: 30
     },
     onSuccess: (res) => {
         dataList.value = res.data
@@ -58,6 +58,8 @@ useRequest(queryMarketRun, {
                 startTime: formatDate(market.startTime),
                 endTime: formatDate(market.endTime),
             })
+        } else {
+            getHistoryTikDatas()
         }
     }
 })
@@ -67,7 +69,7 @@ quoteWatch(props.goodsCode, (quote) => {
     const { last = 0, lasttime = '', lastvolume = 0 } = quote
     if (last && (lastvolume || quoteDay.value?.trademode === 99)) {
         const list = dataList.value
-        if (list.length > 19) {
+        if (list.length > 29) {
             // 移除列表最后一条记录
             list.pop()
         }

+ 2 - 2
src/packages/pc/views/market/trade/index.vue

@@ -16,7 +16,7 @@ import { useFuturesStore, useUserStore } from '@/stores'
 import AppTabs from '@/components/base/tabs/index.vue'
 
 const componentMap = new Map([
-    ['goodsList', defineAsyncComponent(() => import('./goods/list/index.vue'))], // 订单挂牌
+    ['tradeModel_50', defineAsyncComponent(() => import('./goods/list/index.vue'))], // 订单挂牌
     ['tradeModel_51', defineAsyncComponent(() => import('./presell/ballot/index.vue'))], // 预售中签
     ['tradeModel_49', defineAsyncComponent(() => import('./presell/transfer/index.vue'))], // 定金转让
     ['tradeModel_17', defineAsyncComponent(() => import('./spot/index.vue'))], // 现货挂牌
@@ -67,7 +67,7 @@ const onMarketChange = (index: number) => {
         futuresStore.setMarketId(...marketids)
         switch (true) {
             case [50, 16].includes(item.trademode): {
-                componentId.value = 'goodsList'
+                componentId.value = 'tradeModel_50'
                 break
             }
             default: {

+ 15 - 1
src/services/api/account/index.ts

@@ -1,10 +1,11 @@
 import service from '@/services'
 import http from '@/services/http'
 import { RequestConfig } from '@/services/http/types'
-import { useGlobalStore, useLoginStore } from '@/stores'
+import { useGlobalStore, useLoginStore,useAccountStore } from '@/stores'
 
 const globalStore = useGlobalStore()
 const loginStore = useLoginStore()
+const accountStore = useAccountStore()
 
 /**
  * 账户登录
@@ -101,6 +102,19 @@ export function queryUserAccount(config: RequestConfig<{ userID: number }> = {})
 }
 
 /**
+ * 获取资金账户今日配置信息
+ */
+export function getTodayAccountConfigInfo(config: RequestConfig<Model.TodayAccountConfigInfoReq> = {}) {
+    return http.commonRequest<Model.TodayAccountConfigInfoRsp>({
+        url: '/User/GetTodayAccountConfigInfo',
+        params: {
+            accountid: accountStore.currentAccountId,
+            ...config.data
+        },
+    })
+}
+
+/**
  * 查询资金账户信息
  */
 export function queryTaAccounts(config: RequestConfig<Model.TaAccountsReq> = {}) {

+ 6 - 9
src/services/api/goods/index.ts

@@ -1,9 +1,6 @@
-import { useLoginStore } from '@/stores'
-import http from '@/services/http'
+import { getUserId } from '@/services/methods/user'
 import { RequestConfig } from '@/services/http/types'
-
-const loginStore = useLoginStore()
-const { userId } = loginStore.$toRefs()
+import http from '@/services/http'
 
 /**
  * 查询企业风管期货商品信息
@@ -83,7 +80,7 @@ export function removeUserFavoriteGoods(config: RequestConfig<Model.UserFavorite
         method: 'post',
         url: '/User/RemoveUserFavoriteGoods',
         data: {
-            userID: userId.value,
+            userID: getUserId(),
             ...config.data
         },
     })
@@ -97,7 +94,7 @@ export function addUserFavoriteGoods(config: RequestConfig<Model.UserFavoriteGoo
         method: 'post',
         url: '/User/AddUserFavoriteGoods',
         data: {
-            userID: userId.value,
+            userID: getUserId(),
             ...config.data
         },
     })
@@ -110,7 +107,7 @@ export function queryTHJTradeData(config: RequestConfig<Model.THJTradeDataReq> =
     return http.commonRequest<Model.THJTradeDataRsp[]>({
         url: '/Ferroalloy/QueryTHJTradeData',
         params: {
-            userid: userId.value,
+            userid: getUserId(),
             ...config.data
         },
     })
@@ -123,7 +120,7 @@ export function queryTHJProduct(config: RequestConfig<Model.THJProductReq> = {})
     return http.commonRequest<Model.THJProductRsp[]>({
         url: '/Ferroalloy/QueryTHJProduct',
         params: {
-            userid: userId.value,
+            userid: getUserId(),
             ...config.data
         },
     })

+ 4 - 6
src/services/api/user/index.ts

@@ -1,8 +1,6 @@
-import { useLoginStore } from '@/stores'
-import http from '@/services/http'
+import { getUserId } from '@/services/methods/user'
 import { RequestConfig } from '@/services/http/types'
-
-const loginStore = useLoginStore()
+import http from '@/services/http'
 
 /**
  * 查询收货地址信息
@@ -11,7 +9,7 @@ export function queryUserReceiveInfo(config: RequestConfig<Model.UserReceiveInfo
     return http.commonRequest<Model.UserReceiveInfoRsp[]>({
         url: '/Qhj/QueryUserReceiveInfo',
         params: {
-            userid: loginStore.userId,
+            userid: getUserId(),
             ...config.data
         },
     })
@@ -24,7 +22,7 @@ export function queryWrUserReceiptInfo(config: RequestConfig<Model.WrUserReceipt
     return http.commonRequest<Model.WrUserReceiptInfoRsp[]>({
         url: '/WrTrade2/QueryWrUserReceiptInfo',
         params: {
-            userid: loginStore.userId,
+            userid: getUserId(),
             ...config.data
         },
     })

+ 1 - 1
src/services/websocket/package/crypto.ts

@@ -38,7 +38,7 @@ function uint8ArrayToWordArray(arr: Uint8Array): CryptoJS.lib.WordArray {
  * 将WordArray数据转化为Uint8Array数据的方法
  * @param wordArray WordArray数据
  */
-function wordArrayToUint8Array(wordArray: CryptoJS.lib.WordArray): Uint8Array {
+export function wordArrayToUint8Array(wordArray: CryptoJS.lib.WordArray): Uint8Array {
     const arrayOfWords = Object.prototype.hasOwnProperty.call(wordArray, 'words') ? wordArray.words : [];
     const length = Object.prototype.hasOwnProperty.call(wordArray, 'sigBytes') ? wordArray.sigBytes : arrayOfWords.length * 4;
     const uInt8Array = new Uint8Array(length);

+ 59 - 4
src/stores/modules/account.ts

@@ -1,10 +1,13 @@
-import { toRefs, computed, reactive } from 'vue'
-import { queryTaAccounts } from '@/services/api/account'
+import { toRefs, computed, reactive, shallowRef } from 'vue'
+import { queryTaAccounts, getTodayAccountConfigInfo } from '@/services/api/account'
 import { defineStore } from '../store'
 import { useLoginStore } from './login'
 import { useUserStore } from './user'
 import { usePositionStore } from './position'
+import { wordArrayToUint8Array } from '@/services/websocket/package/crypto'
+import { decodeProto } from '@/services/websocket/package/package50/proto'
 import eventBus from '@/services/bus'
+import CryptoJS from 'crypto-js'
 
 /**
  * 账号存储对象
@@ -18,11 +21,17 @@ export const useAccountStore = defineStore(() => {
     const state = reactive({
         loading: false,
         accountList: <Model.TaAccountsRsp[]>[],
-        currentAccountId: 0,
+        currentAccountId: 0
     })
 
+    // 资金账户今日配置信息
+    const accountConfig = shallowRef<Model.TodayAccountConfigInfoRsp>()
+
     // 资金账户计算列表
     const accountComputedList = computed(() => {
+        const { riskRatioType } = accountConfig.value ?? {}
+        const { addmarginriskratio = 0, notemarginriskratio = 0, cutriskratio = 0 } = riskRatioType ?? {}
+
         const result: (Model.TaAccountsRsp & {
             freezeMargin: number; // 冻结资金
             avaiableMoney: number; // 可用资金
@@ -30,6 +39,7 @@ export const useAccountStore = defineStore(() => {
             profitLoss: number; // 浮动盈亏
             hazardValue: number; // 风险净值
             hazardRatio: number; // 风险率
+            hazardRatioColor: string; // 风险率颜色
         })[] = []
 
         state.accountList.forEach((item) => {
@@ -92,6 +102,21 @@ export const useAccountStore = defineStore(() => {
                 hazardRatio = (item.usedmargin - item.mortgagecredit) / (hazardValue - item.mortgagecredit)
             }
 
+            // 风险率颜色
+            let hazardRatioColor = ''
+            if (hazardRatio < notemarginriskratio) {
+                hazardRatioColor = 'g-risk-green'
+            }
+            if (hazardRatio >= notemarginriskratio && hazardRatio < addmarginriskratio) {
+                hazardRatioColor = 'g-risk-orange'
+            }
+            if (hazardRatio >= addmarginriskratio && hazardRatio < cutriskratio) {
+                hazardRatioColor = 'g-risk-yellow'
+            }
+            if (hazardRatio >= cutriskratio && cutriskratio > 0) {
+                hazardRatioColor = 'g-risk-red'
+            }
+
             result.push({
                 ...item,
                 freezeMargin,
@@ -99,7 +124,8 @@ export const useAccountStore = defineStore(() => {
                 netvalue,
                 profitLoss,
                 hazardValue,
-                hazardRatio
+                hazardRatio,
+                hazardRatioColor
             })
         })
 
@@ -129,11 +155,39 @@ export const useAccountStore = defineStore(() => {
             if (!data.every((e) => e.accountid === state.currentAccountId)) {
                 state.currentAccountId = data[0]?.accountid ?? 0
             }
+
+            // 获取资金账户今日配置信息
+            if (!accountConfig.value) {
+                getTodayAccountConfigInfo().then((res) => {
+                    accountConfig.value = res.data
+                })
+            }
         } finally {
             state.loading = false
         }
     }
 
+    // 获取账户商品配置
+    const getAccountGoodsConfig = (goodsId: number) => {
+        const config = accountConfig.value?.todayAccountMargins.find((item, index, self) => {
+            // 优先返回账户的商品配置
+            if (item.accountid === state.currentAccountId && item.goodsid === goodsId) {
+                return true
+            }
+            // 返回通用的商品配置
+            if (!self.some((e) => e.accountid === state.currentAccountId) && item.goodsid === goodsId) {
+                return true
+            }
+            return false
+        })
+        if (config) {
+            const wordArray = CryptoJS.enc.Base64.parse(config.infocontent) // 解析base64
+            const uint8Array = wordArrayToUint8Array(wordArray)
+            return decodeProto<Proto.MarginInfoStruct>('MarginInfoStruct', uint8Array) // proto数据解析
+        }
+        return Promise.reject('商品配置不存在')
+    }
+
     // 接收资金变动通知
     const moneyChangedNotify = eventBus.$on('MoneyChangedNotify', () => getAccountList())
 
@@ -143,5 +197,6 @@ export const useAccountStore = defineStore(() => {
         currentAccount,
         moneyChangedNotify,
         getAccountList,
+        getAccountGoodsConfig,
     }
 })

+ 4 - 1
src/stores/modules/position.ts

@@ -40,7 +40,10 @@ export const usePositionStore = defineStore(() => {
 
         state.positionList.forEach((item) => {
             const quote = futuresStore.getGoodsQuote(item.goodscode)
-            const { presettle = 0, last = 0 } = quote.value ?? {}
+            const refQuote = futuresStore.getGoodsQuote(item.refgoodscode)
+
+            const last = refQuote.value?.last || quote.value?.last || 0 // 有 refgoodscode 的优先取 refgoodscode 行情
+            const presettle = quote.value?.presettle || 0
             const price = last || presettle // 没有最新价取昨结价
 
             // 计算市值 = 现价 * 数量 * 合约单位

+ 69 - 1
src/types/model/user.d.ts

@@ -74,7 +74,7 @@ declare namespace Model {
         memberUserId?: number; // 所属会员ID
         recordId?: number   // 记录ID
         templateConfigId?: number // 模板配置ID
-        templatetype?:number  // 模板类型 - 1:实名认证 2:开户协议 3:日结算单 4:交易协议
+        templatetype?: number  // 模板类型 - 1:实名认证 2:开户协议 3:日结算单 4:交易协议
     }
 
     /** 查询腾讯用户电子签记录 响应 */
@@ -129,4 +129,72 @@ declare namespace Model {
         customername: string; // 客户名称
         userid: number; // 用户ID
     }
+
+    /** 获取资金账户今日配置信息 请求 */
+    interface TodayAccountConfigInfoReq {
+        accountid: number; // 资金账户ID
+    }
+
+    /** 获取资金账户今日配置信息 响应 */
+    interface TodayAccountConfigInfoRsp {
+        riskRatioType: RiskRatioType;
+        todayAccountMargins: TodayAccountMargin[]; // 今日账户保证金表
+        todayAccountTradeRules: TodayAccountTradeRule[]; // 今日账户交易规则信表
+        todayAccountTradefees: TodayAccountTradefee[]; // 今日账户交易费用表
+    }
+
+    interface RiskRatioType {
+        addmarginriskratio: number; // 追加保证金风险率
+        addsaferatio: number; // 追加安全度
+        createtime: string; // 创建时间
+        creatorid: number; // 创建人
+        customertype: number; // 客户类别(机构/投资者客户类别枚举项值)
+        cutbackriskratio: number; // 斩仓恢复风险率(投资者用,会员转全部净头寸)
+        cutmarginseq: string; // 斩仓市场顺序(按市场)
+        cutriskratio: number; // 斩仓风险率
+        cutsaferatio: number; // 斩仓安全度
+        cutthreshold: number; // 净值斩仓阈值(做市会员用)
+        cutthresholdflag: number; // 净值斩仓标志 - 0:不启用 1:启用(做市会员用)
+        isdefault: number; // 是否默认(每个风控模式必有且只能设置一个默认) - 0:非默认 1:默认
+        issendcancel: number; // 到达追加风险率是否撤单(交易) - 0:不撤 1:撤单
+        issenddeliverycancel: number; // 到达追加风险率是否撤单(交割) - 0:不撤 1:撤单
+        modifierid: number; // 修改人ID
+        modifytime: string; // 修改时间
+        notemarginriskratio: number; // 提示保证金风险率
+        notesaferatio: number; // 提示安全度
+        recoversaferatio: number; // 恢复正常安全度(会员)
+        recovertraderiskratio: number; // 恢复正常交易风险率(会员)
+        riskcontrolmode: number; // 适用交易用户类型 1:投资者/机构自营 2:机构做市
+        riskratiocalcmode: number; // 风险率计算方法 - 1:占用/净值
+        riskrationame: string; // 类型名称
+    }
+
+    /** 今日账户保证金表 */
+    interface TodayAccountMargin {
+        accountid: number; // 账号ID
+        createdate: string; // 创建时间
+        goodsid: number; // 商品ID
+        infocontent: string; // 保证金信息(存储配置的Protobuffer串)
+        marketid: number; // 市场ID
+    }
+
+    /** 今日账户交易规则信表 */
+    interface TodayAccountTradeRule {
+        accountid: number; // 账号ID
+        createdate: string; // 创建时间
+        goodsid: number; // 商品ID
+        infocontent: string; // 交易规则信息(存储配置的Protobuffer串)
+        marketid: number; // 市场ID
+        tradetype: number; // 交易类型 - 1:投资者\自营 2:做市
+    }
+
+    /** 今日账户交易费用表 */
+    interface TodayAccountTradefee {
+        accountid: number; // 账号ID
+        createdate: string; // 创建时间
+        goodsid: number; // 商品ID
+        infocontent: string; // 交易费用信息(存储配置的Protobuffer串)
+        marketid: number; // 市场ID
+        tradetype: number; // 交易类型 - 1:投资者\自营 2:做市
+    }
 }

+ 41 - 0
src/types/proto/common.d.ts

@@ -0,0 +1,41 @@
+declare namespace Proto {
+    /** 保证金信息 */
+    interface MarginInfoStruct {
+        AccountID: number; // 资金账号
+        GoodsID: number; // 商品id 
+        MarginAlgorithm: number; // 保证金计算方式
+        MarketMarginValue: number; // 即市保证金值
+        ReckonMarginValue: number; // 结算保证金值
+        LockMarginValue: number; // 锁仓保证金
+        RealMarginValue: number; // 实付比例
+        RealMarginAlgorithm: number; // 实付保证金计算方式
+    }
+
+    /** 交易规则信息子集 */
+    interface TradeRule {
+        RuleID: number; // 交易规则ID
+        ParamValue: number; // 参数值
+    }
+
+    /** 交易规则信息 */
+    interface TradeRuleInfoStruct {
+        AccountID: number; // 资金账号
+        GoodsID: number; // 商品id 
+        TradeRules: TradeRule[]; // 交易规则
+    }
+
+    /** 交易费用信息子集 */
+    interface TradeFee {
+        FeeID: number; // 交易规则ID
+        FeeAlgorithm: number; // 费用算法
+        ExchangeValue: number; // 交易所费用值
+        MemberDefaultValue: number; // 会员费用默认值
+    }
+
+    /** 交易费用信息 */
+    interface TradeFeeInfoStruct {
+        AccountID: number; // 资金账号
+        GoodsID: number; // 商品id 
+        TradeFees: TradeFee[]; // 交易费用
+    }
+}