mirror of
https://github.com/ArvinLovegood/go-stock.git
synced 2025-07-19 00:00:09 +08:00
feat(stock):添加A股盘口数据解析和展示功能
- 在 stock.vue 中添加盘口数据展示组件 - 在 stock_data_api.go 中增加 A 股盘口数据解析逻辑 - 优化数据库自动迁移逻辑,提取到单独的函数中 - 更新测试用例以覆盖新的盘口数据解析功能
This commit is contained in:
parent
58d93c76f6
commit
7e24424ea0
@ -53,6 +53,7 @@
|
||||
| 不再强制依赖Chrome浏览器 | ✅ | 默认使用edge浏览器抓取新闻资讯 |
|
||||
|
||||
## 👀 更新日志
|
||||
### 2025.05.08 添加A股盘口数据解析和展示功能
|
||||
### 2025.05.07 优化分时图的展示
|
||||
### 2025.04.29 补全港股/美股基础数据,优化港股股价延迟问题,优化初始化逻辑
|
||||
### 2025.04.25 市场资讯支持AI分析和总结:让AI帮你读市场!
|
||||
|
@ -295,12 +295,16 @@ func (receiver StockDataApi) GetStockCodeRealTimeData(StockCodes ...string) (*[]
|
||||
stockInfos := make([]StockInfo, 0)
|
||||
|
||||
hkcodes := slice.Filter(StockCodes, func(i int, s string) bool {
|
||||
return strutil.HasPrefixAny(s, []string{"hk", "HK"})
|
||||
return strutil.HasPrefixAny(s, []string{"hk", "HK", "sh", "sz"})
|
||||
})
|
||||
|
||||
if hkcodes != nil && len(hkcodes) > 0 {
|
||||
hkcodesStr := slice.JoinFunc(hkcodes, ",", func(s string) string {
|
||||
return "r_" + strings.ToLower(s)
|
||||
if strutil.HasPrefixAny(s, []string{"hk", "HK"}) {
|
||||
return "r_" + strings.ToLower(s)
|
||||
} else {
|
||||
return strings.ToLower(s)
|
||||
}
|
||||
})
|
||||
url := fmt.Sprintf(txStockUrl, time.Now().Unix(), hkcodesStr)
|
||||
resp, err := receiver.client.R().
|
||||
@ -336,7 +340,7 @@ func (receiver StockDataApi) GetStockCodeRealTimeData(StockCodes ...string) (*[]
|
||||
}
|
||||
|
||||
szzsusCodes := slice.Filter(StockCodes, func(i int, s string) bool {
|
||||
return !strutil.HasPrefixAny(s, []string{"hk", "HK"})
|
||||
return !strutil.HasPrefixAny(s, []string{"hk", "HK", "sh", "sz"})
|
||||
})
|
||||
|
||||
codes := slice.JoinFunc(szzsusCodes, ",", func(s string) string {
|
||||
@ -556,13 +560,16 @@ func GB18030ToUTF8(bs []byte) string {
|
||||
func ParseTxStockData(data string) (*StockInfo, error) {
|
||||
//v_r_hk09660="100~地平线机器人-W~09660~6.240~5.690~5.800~192659034.0~0~0~6.240~0~0~0~0~0~0~0~0~0~6.240~0~0~0~0~0~0~0~0~0~192659034.0~2025/04/29
|
||||
//13:41:04~0.550~9.67~6.450~5.710~6.240~192659034.0~1180471843.140~0~32.51~~0~0~13.01~691.1364~823.6983~HORIZONROBOT-W~0.00~10.380~3.320~1.07~-16.03~0~0~0~0~0~32.51~6.40~1.74~600~73.33~17.96~GP~19.70~11.51~-0.95~-18.54~44.44~13200293682.00~11075904412.00~32.51~0.000~6.127~56.39~HKD~1~30";
|
||||
//v_sz002241="51~歌尔股份~002241~22.26~22.27~0.00~0~0~0~22.26~1004~0.00~0~0.00~0~0.00~0~0.00~0~22.26~1004~0.00~558~0.00~0~0.00~0~0.00~0~~20250509092233~-0.01~-0.04~0.00~0.00~22.26/0/0~0~0~0.00~28.21~~0.00~0.00~0.00~686.46~777.09~2.31~24.50~20.04~0.00~-558~0.00~41.44~29.16~~~1.24~0.0000~0.0000~0~
|
||||
//~GP-A~-13.75~6.76~1.09~8.18~3.39~30.63~15.70~6.87~17.47~-23.95~3083811231~3490989083~-21.75~12.02~3083811231~~~39.36~-0.04~~CNY~0~~0.00~0";
|
||||
|
||||
datas := strutil.SplitAndTrim(data, "=", "\"")
|
||||
if len(datas) < 2 {
|
||||
return nil, fmt.Errorf("invalid data format")
|
||||
}
|
||||
var result map[string]string
|
||||
var err error
|
||||
if strutil.ContainsAny(datas[0], []string{"v_r_hk", "v_hk"}) {
|
||||
if strutil.ContainsAny(datas[0], []string{"v_r_hk", "v_hk", "v_sz", "v_sh"}) {
|
||||
result, err = ParseTxHKStockData(datas)
|
||||
}
|
||||
|
||||
@ -640,12 +647,50 @@ func ParseTxHKStockData(datas []string) (map[string]string, error) {
|
||||
|
||||
result["今日最高价"] = parts[33]
|
||||
result["今日最低价"] = parts[34]
|
||||
timestr := strutil.ReplaceWithMap(parts[30], map[string]string{
|
||||
"/": "-",
|
||||
})
|
||||
|
||||
if strutil.HasPrefixAny(stockCode, []string{"sz", "sh"}) {
|
||||
result["买一报价"] = parts[9]
|
||||
result["买一申报"] = parts[10]
|
||||
result["买二报价"] = parts[11]
|
||||
result["买二申报"] = parts[12]
|
||||
result["买三报价"] = parts[13]
|
||||
result["买三申报"] = parts[14]
|
||||
result["买四报价"] = parts[15]
|
||||
result["买四申报"] = parts[16]
|
||||
result["买五报价"] = parts[17]
|
||||
result["买五申报"] = parts[18]
|
||||
|
||||
result["卖一报价"] = parts[19]
|
||||
result["卖一申报"] = parts[20]
|
||||
result["卖二报价"] = parts[21]
|
||||
result["卖二申报"] = parts[22]
|
||||
result["卖三报价"] = parts[23]
|
||||
result["卖三申报"] = parts[24]
|
||||
result["卖四报价"] = parts[25]
|
||||
result["卖四申报"] = parts[26]
|
||||
result["卖五报价"] = parts[27]
|
||||
result["卖五申报"] = parts[28]
|
||||
|
||||
}
|
||||
|
||||
timestr := ""
|
||||
|
||||
if strutil.ContainsAny(parts[30], []string{"/"}) {
|
||||
timestr = strutil.ReplaceWithMap(parts[30], map[string]string{
|
||||
"/": "-",
|
||||
"\n": " ",
|
||||
})
|
||||
result["日期"] = strutil.SplitAndTrim(timestr, " ", "")[0]
|
||||
result["时间"] = strutil.SplitAndTrim(timestr, " ", "")[1]
|
||||
} else {
|
||||
result["日期"] = strutil.Trim(parts[29])[0:4] + "-" + strutil.Trim(parts[29])[4:6] + "-" + strutil.Trim(parts[29])[6:8]
|
||||
result["时间"] = strutil.Trim(parts[29])[8:10] + ":" + strutil.Trim(parts[29])[10:12] + ":" + strutil.Trim(parts[29])[12:14]
|
||||
result["今日最高价"] = parts[32]
|
||||
result["今日最低价"] = parts[33]
|
||||
}
|
||||
//logger.SugaredLogger.Infof("股票数据解析完成 %s %s 时间: %s,%s", parts[1], parts[3], parts[29], parts[30])
|
||||
|
||||
//logger.SugaredLogger.Infof("股票数据解析完成 时间: %v", timestr)
|
||||
result["日期"] = strutil.SplitAndTrim(timestr, " ", "")[0]
|
||||
result["时间"] = strutil.SplitAndTrim(timestr, " ", "")[1]
|
||||
|
||||
//logger.SugaredLogger.Infof("股票数据解析完成: %v", result)
|
||||
|
||||
|
@ -116,6 +116,9 @@ func TestGetHKStockInfo(t *testing.T) {
|
||||
|
||||
func TestParseTxStockData(t *testing.T) {
|
||||
str := "v_r_hk09660=\"100~地平线机器人-W~09660~6.340~5.690~5.800~210980204.0~0~0~6.340~0~0~0~0~0~0~0~0~0~6.340~0~0~0~0~0~0~0~0~0~210980204.0~2025/04/29\n14:14:52~0.650~11.42~6.450~5.710~6.340~210980204.0~1295585259.040~0~33.03~~0~0~13.01~702.2123~836.8986~HORIZONROBOT-W~0.00~10.380~3.320~1.00~-53.74~0~0~0~0~0~33.03~6.50~1.90~600~76.11~19.85~GP~19.70~11.51~0.63~-17.23~46.76~13200293682.00~11075904412.00~33.03~0.000~6.141~58.90~HKD~1~30\";"
|
||||
//str = "v_sz002241=\"51~歌尔股份~002241~22.26~22.27~0.00~0~0~0~22.26~1004~0.00~0~0.00~0~0.00~0~0.00~0~22.26~1004~0.00~558~0.00~0~0.00~0~0.00~0~~20250509092233~-0.01~-0.04~0.00~0.00~22.26/0/0~0~0~0.00~28.21~~0.00~0.00~0.00~686.46~777.09~2.31~24.50~20.04~0.00~-558~0.00~41.44~29.16~~~1.24~0.0000~0.0000~0~\n~GP-A~-13.75~6.76~1.09~8.18~3.39~30.63~15.70~6.87~17.47~-23.95~3083811231~3490989083~-21.75~12.02~3083811231~~~39.36~-0.04~~CNY~0~~0.00~0\";"
|
||||
str = "v_sz002241=\"51~歌尔股份~002241~21.92~22.27~22.14~109872~40211~69642~21.91~25~21.90~961~21.89~257~21.88~748~21.87~665~21.92~86~21.93~168~21.94~556~21.95~171~21.96~85~~20250509094209~-0.35~-1.57~22.16~21.84~21.92/109872/241183171~109872~24118~0.36~27.78~~22.16~21.84~1.44~675.97~765.22~2.27~24.50~20.04~2.57~1590~21.95~40.80~28.71~~~1.24~24118.3171~0.0000~0~\n~GP-A~-15.07~5.13~1.11~8.18~3.39~30.63~15.70~5.23~15.67~-25.11~3083811231~3490989083~42.72~10.31~3083811231~~~37.23~0.18~~CNY~0~~21.85~1952\";"
|
||||
//str = "v_r_hk09660=\"100~地平线机器人-W~09660~6.860~7.000~7.010~21157200.0~0~0~6.860~0~0~0~0~0~0~0~0~0~6.860~0~0~0~0~0~0~0~0~0~21157200.0~2025/05/09\n09:43:13~-0.140~-2.00~7.030~6.730~6.860~21157200.0~144331073.000~0~35.74~~0~0~4.29~759.8070~905.5401~HORIZONROBOT-W~0.00~10.380~3.320~2.93~11.10~0~0~0~0~0~35.74~7.04~0.19~600~90.56~4.73~GP~19.70~11.51~17.26~48.48~13.58~13200293682.00~11075904412.00~35.74~0.000~6.822~71.93~HKD~1~30\";"
|
||||
info, _ := ParseTxStockData(str)
|
||||
logger.SugaredLogger.Infof("%+#v", info)
|
||||
}
|
||||
|
@ -632,7 +632,7 @@ function showFsChart(code, name) {
|
||||
|
||||
let option = {
|
||||
title: {
|
||||
subtext: "["+result.date+"] 开盘:"+openprice+" 收盘:"+closeprice+" 最高:"+max+" 最低:"+min,
|
||||
subtext: "["+result.date+"] 开盘:"+openprice+" 最新:"+closeprice+" 最高:"+max+" 最低:"+min,
|
||||
left: 'center',
|
||||
top: '10',
|
||||
textStyle: {
|
||||
@ -1585,6 +1585,52 @@ function delStockGroup(code,name,groupId){
|
||||
<n-text :type="'info'">{{"今开 "+result["今日开盘价"]}}</n-text>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
<n-collapse accordion v-if="result['买一报价']>0">
|
||||
<n-collapse-item title="盘口" name="1" v-if="result['买一报价']>0">
|
||||
<template #header-extra>
|
||||
<n-flex justify="space-between">
|
||||
<n-text :type="'info'">{{"买一 "+result["买一报价"]+'('+result["买一申报"]+")"}}</n-text>
|
||||
<n-text :type="'info'">{{"卖一 "+result["卖一报价"]+'('+result["卖一申报"]+")"}}</n-text>
|
||||
</n-flex>
|
||||
</template>
|
||||
<n-grid :cols="2" :y-gap="4" :x-gap="4" >
|
||||
<n-gi v-if="result['买一报价']>0">
|
||||
<n-text :type="'info'">{{"买一 "+result["买一报价"]+'('+result["买一申报"]+")"}}</n-text>
|
||||
</n-gi>
|
||||
<n-gi v-if="result['卖一报价']>0">
|
||||
<n-text :type="'info'">{{"卖一 "+result["卖一报价"]+'('+result["卖一申报"]+")"}}</n-text>
|
||||
</n-gi>
|
||||
|
||||
<n-gi v-if="result['买二报价']>0">
|
||||
<n-text :type="'info'">{{"买二 "+result["买二报价"]+'('+result["买二申报"]+")"}}</n-text>
|
||||
</n-gi>
|
||||
<n-gi v-if="result['卖二报价']>0">
|
||||
<n-text :type="'info'">{{"卖二 "+result["卖二报价"]+'('+result["卖二申报"]+")"}}</n-text>
|
||||
</n-gi>
|
||||
|
||||
<n-gi v-if="result['买三报价']>0">
|
||||
<n-text :type="'info'">{{"买三 "+result["买三报价"]+'('+result["买三申报"]+")"}}</n-text>
|
||||
</n-gi>
|
||||
<n-gi v-if="result['卖三报价']>0">
|
||||
<n-text :type="'info'">{{"买三 "+result["卖三报价"]+'('+result["卖三申报"]+")"}}</n-text>
|
||||
</n-gi>
|
||||
|
||||
<n-gi v-if="result['买四报价']>0">
|
||||
<n-text :type="'info'">{{"买四 "+result["买四报价"]+'('+result["买四申报"]+")"}}</n-text>
|
||||
</n-gi>
|
||||
<n-gi v-if="result['卖四报价']>0">
|
||||
<n-text :type="'info'">{{"卖四 "+result["卖四报价"]+'('+result["卖四申报"]+")"}}</n-text>
|
||||
</n-gi>
|
||||
|
||||
<n-gi v-if="result['买五报价']>0">
|
||||
<n-text :type="'info'">{{"买五 "+result["买五报价"]+'('+result["买五申报"]+")"}}</n-text>
|
||||
</n-gi>
|
||||
<n-gi v-if="result['卖五报价']>0">
|
||||
<n-text :type="'info'">{{"卖五 "+result["卖五报价"]+'('+result["卖五申报"]+")"}}</n-text>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
</n-collapse-item>
|
||||
</n-collapse>
|
||||
<template #header-extra>
|
||||
|
||||
<n-tag size="small" :bordered="false">{{result['股票代码']}}</n-tag>
|
||||
@ -1650,6 +1696,52 @@ function delStockGroup(code,name,groupId){
|
||||
<n-text :type="'info'">{{"今开 "+result["今日开盘价"]}}</n-text>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
<n-collapse accordion v-if="result['买一报价']>0">
|
||||
<n-collapse-item title="盘口" name="1" v-if="result['买一报价']>0">
|
||||
<template #header-extra>
|
||||
<n-flex justify="space-between">
|
||||
<n-text :type="'info'">{{"买一 "+result["买一报价"]+'('+result["买一申报"]+")"}}</n-text>
|
||||
<n-text :type="'info'">{{"卖一 "+result["卖一报价"]+'('+result["卖一申报"]+")"}}</n-text>
|
||||
</n-flex>
|
||||
</template>
|
||||
<n-grid :cols="2" :y-gap="4" :x-gap="4" >
|
||||
<n-gi v-if="result['买一报价']>0">
|
||||
<n-text :type="'info'">{{"买一 "+result["买一报价"]+'('+result["买一申报"]+")"}}</n-text>
|
||||
</n-gi>
|
||||
<n-gi v-if="result['卖一报价']>0">
|
||||
<n-text :type="'info'">{{"卖一 "+result["卖一报价"]+'('+result["卖一申报"]+")"}}</n-text>
|
||||
</n-gi>
|
||||
|
||||
<n-gi v-if="result['买二报价']>0">
|
||||
<n-text :type="'info'">{{"买二 "+result["买二报价"]+'('+result["买二申报"]+")"}}</n-text>
|
||||
</n-gi>
|
||||
<n-gi v-if="result['卖二报价']>0">
|
||||
<n-text :type="'info'">{{"卖二 "+result["卖二报价"]+'('+result["卖二申报"]+")"}}</n-text>
|
||||
</n-gi>
|
||||
|
||||
<n-gi v-if="result['买三报价']>0">
|
||||
<n-text :type="'info'">{{"买三 "+result["买三报价"]+'('+result["买三申报"]+")"}}</n-text>
|
||||
</n-gi>
|
||||
<n-gi v-if="result['卖三报价']>0">
|
||||
<n-text :type="'info'">{{"买三 "+result["卖三报价"]+'('+result["卖三申报"]+")"}}</n-text>
|
||||
</n-gi>
|
||||
|
||||
<n-gi v-if="result['买四报价']>0">
|
||||
<n-text :type="'info'">{{"买四 "+result["买四报价"]+'('+result["买四申报"]+")"}}</n-text>
|
||||
</n-gi>
|
||||
<n-gi v-if="result['卖四报价']>0">
|
||||
<n-text :type="'info'">{{"卖四 "+result["卖四报价"]+'('+result["卖四申报"]+")"}}</n-text>
|
||||
</n-gi>
|
||||
|
||||
<n-gi v-if="result['买五报价']>0">
|
||||
<n-text :type="'info'">{{"买五 "+result["买五报价"]+'('+result["买五申报"]+")"}}</n-text>
|
||||
</n-gi>
|
||||
<n-gi v-if="result['卖五报价']>0">
|
||||
<n-text :type="'info'">{{"卖五 "+result["卖五报价"]+'('+result["卖五申报"]+")"}}</n-text>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
</n-collapse-item>
|
||||
</n-collapse>
|
||||
<template #header-extra>
|
||||
|
||||
<n-tag size="small" :bordered="false">{{result['股票代码']}}</n-tag>
|
||||
|
36
main.go
36
main.go
@ -55,22 +55,7 @@ var VersionCommit string
|
||||
func main() {
|
||||
checkDir("data")
|
||||
db.Init("")
|
||||
db.Dao.AutoMigrate(&data.StockInfo{})
|
||||
db.Dao.AutoMigrate(&data.StockBasic{})
|
||||
db.Dao.AutoMigrate(&data.FollowedStock{})
|
||||
db.Dao.AutoMigrate(&data.IndexBasic{})
|
||||
db.Dao.AutoMigrate(&data.Settings{})
|
||||
db.Dao.AutoMigrate(&models.AIResponseResult{})
|
||||
db.Dao.AutoMigrate(&models.StockInfoHK{})
|
||||
db.Dao.AutoMigrate(&models.StockInfoUS{})
|
||||
db.Dao.AutoMigrate(&data.FollowedFund{})
|
||||
db.Dao.AutoMigrate(&data.FundBasic{})
|
||||
db.Dao.AutoMigrate(&models.PromptTemplate{})
|
||||
db.Dao.AutoMigrate(&data.Group{})
|
||||
db.Dao.AutoMigrate(&data.GroupStock{})
|
||||
db.Dao.AutoMigrate(&models.Tags{})
|
||||
db.Dao.AutoMigrate(&models.Telegraph{})
|
||||
db.Dao.AutoMigrate(&models.TelegraphTags{})
|
||||
go AutoMigrate()
|
||||
|
||||
//db.Dao.Model(&data.Group{}).Where("id = ?", 0).FirstOrCreate(&data.Group{
|
||||
// Name: "默认分组",
|
||||
@ -214,6 +199,25 @@ func main() {
|
||||
|
||||
}
|
||||
|
||||
func AutoMigrate() {
|
||||
db.Dao.AutoMigrate(&data.StockInfo{})
|
||||
db.Dao.AutoMigrate(&data.StockBasic{})
|
||||
db.Dao.AutoMigrate(&data.FollowedStock{})
|
||||
db.Dao.AutoMigrate(&data.IndexBasic{})
|
||||
db.Dao.AutoMigrate(&data.Settings{})
|
||||
db.Dao.AutoMigrate(&models.AIResponseResult{})
|
||||
db.Dao.AutoMigrate(&models.StockInfoHK{})
|
||||
db.Dao.AutoMigrate(&models.StockInfoUS{})
|
||||
db.Dao.AutoMigrate(&data.FollowedFund{})
|
||||
db.Dao.AutoMigrate(&data.FundBasic{})
|
||||
db.Dao.AutoMigrate(&models.PromptTemplate{})
|
||||
db.Dao.AutoMigrate(&data.Group{})
|
||||
db.Dao.AutoMigrate(&data.GroupStock{})
|
||||
db.Dao.AutoMigrate(&models.Tags{})
|
||||
db.Dao.AutoMigrate(&models.Telegraph{})
|
||||
db.Dao.AutoMigrate(&models.TelegraphTags{})
|
||||
}
|
||||
|
||||
func initStockDataUS() {
|
||||
var v []models.StockInfoUS
|
||||
err := json.Unmarshal(stocksBinUS, &v)
|
||||
|
Loading…
x
Reference in New Issue
Block a user