From 96b79a34ef36ec6b7f2ccb2505365877b6d28f74 Mon Sep 17 00:00:00 2001 From: yanweidong Date: Mon, 9 Feb 2026 17:36:20 +0800 Subject: [PATCH] Initial commit --- .gitea/workflows/cicd.yaml | 100 +++ .gitignore | 33 + cmd/check/main.go | 67 ++ cmd/main/main.go | 48 ++ cmd/test/main.go | 16 + etc/stock_dev.yaml | 12 + etc/stock_prod.yaml | 11 + etc/stock_test.yaml | 11 + go.mod | 77 +++ go.sum | 224 +++++++ internal/config/config.go | 36 + internal/cron/boot.go | 36 + internal/impl/impl.go | 25 + internal/logic/a/basic.go | 103 +++ internal/logic/a/daily.go | 72 ++ internal/logic/a/fina_indicator.go | 836 ++++++++++++++++++++++++ internal/logic/a/indicator.go | 78 +++ internal/logic/a/markdown.go | 467 +++++++++++++ internal/logic/a/markdown_builder.go | 79 +++ internal/logic/a/new.go | 39 ++ internal/logic/hello/ping.go | 16 + internal/models/query.go | 1 + internal/models/stock_basic.go | 34 + internal/models/stock_daily.go | 30 + internal/models/stock_fina_indicator.go | 213 ++++++ internal/models/stock_indicator.go | 37 ++ internal/routers/register.go | 30 + scripts/build.sh | 5 + 28 files changed, 2736 insertions(+) create mode 100644 .gitea/workflows/cicd.yaml create mode 100644 .gitignore create mode 100644 cmd/check/main.go create mode 100644 cmd/main/main.go create mode 100644 cmd/test/main.go create mode 100644 etc/stock_dev.yaml create mode 100644 etc/stock_prod.yaml create mode 100644 etc/stock_test.yaml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/config/config.go create mode 100644 internal/cron/boot.go create mode 100644 internal/impl/impl.go create mode 100644 internal/logic/a/basic.go create mode 100644 internal/logic/a/daily.go create mode 100644 internal/logic/a/fina_indicator.go create mode 100644 internal/logic/a/indicator.go create mode 100644 internal/logic/a/markdown.go create mode 100644 internal/logic/a/markdown_builder.go create mode 100644 internal/logic/a/new.go create mode 100644 internal/logic/hello/ping.go create mode 100644 internal/models/query.go create mode 100644 internal/models/stock_basic.go create mode 100644 internal/models/stock_daily.go create mode 100644 internal/models/stock_fina_indicator.go create mode 100644 internal/models/stock_indicator.go create mode 100644 internal/routers/register.go create mode 100644 scripts/build.sh diff --git a/.gitea/workflows/cicd.yaml b/.gitea/workflows/cicd.yaml new file mode 100644 index 0000000..4aae571 --- /dev/null +++ b/.gitea/workflows/cicd.yaml @@ -0,0 +1,100 @@ +name: Actions +run-name: ${{ gitea.actor }} 🚀 +on: + push: + tags: + - 'v*' + + +jobs: + Explore-Gitea-Actions: + runs-on: ubuntu-latest + steps: + - name: Clean temp directory + run: rm -rf * + # 设置 Go 环境 + - name: 设置 Go + uses: https://git.apinb.com/github/setup-go@v5 + with: + go-version: '1.24.6' + tag_name: ${{ gitea.ref }} + release_name: ${{ gitea.ref }} + + - run: echo "🎉 The job was automatically triggered by a ${{ gitea.event_name }} event." + - run: echo "🔎 The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository_owner }}." + - name: Extract repository name + id: extract_repo + run: | + repo_name=$(echo ${{ gitea.repository }} | cut -d'/' -f2) + echo "Repository name: $repo_name" + echo "::set-output name=repo_name::$repo_name" + + # 提取tag标签名称 + - name: Extract tag name + id: extract_tag + run: echo "::set-output name=tag_name::$(echo ${{ gitea.ref }} | sed 's/^refs\/tags\///')" + - run: echo "🏷 The tag name is ${{ steps.extract_tag.outputs.tag_name }}." + + # 检查出代码 + - name: Check out repository code + uses: https://git.apinb.com/github/checkout@v4 + + # 缓存 Go 依赖项 + - name: Cache Go modules + uses: https://git.apinb.com/github/cache@v4 + with: + path: | + ~/.cache/go-build + ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + + - name: set last version + run: | + echo "export srv_version='${{ steps.extract_tag.outputs.tag_name }}';export srv_name='${{ steps.extract_repo.outputs.repo_name }}'" > ${{ gitea.repository_owner }}-${{ steps.extract_repo.outputs.repo_name }}_last_version.sh + - run: go env -w GOPROXY=https://goproxy.cn + - run: go env -w GOPRIVATE=git.apinb.com/* + - run: go env -w GONOPROXY=git.apinb.com/* + - run: go env -w GOINSECURE=git.apinb.com/* + - run: go env -w GONOSUMDB=git.apinb.com/* + - run: go build -o ${{ gitea.repository_owner }}-${{ steps.extract_repo.outputs.repo_name }} ./cmd/main/main.go + - run: echo "🍏 This job's status is ${{ job.status }}." + - name: upload bin to minio oss - 1 + uses: https://git.apinb.com/github/minio-upload@main + with: + endpoint: http://172.24.0.20:9000 + access-key-id: 9jtPPB7wwJpe5R2164bS + access-key-secret: ho9LYavUIGdkf0b50aaOduIA4zdx8FQFWpUYx30p + bucket: reles + source: ./${{ gitea.repository_owner }}-${{ steps.extract_repo.outputs.repo_name }}_last_version.sh + insecure: true + recursive: true + + - name: upload bin to minio oss - 2 + uses: https://git.apinb.com/github/minio-upload@main + with: + endpoint: http://172.24.0.20:9000 + access-key-id: 9jtPPB7wwJpe5R2164bS + access-key-secret: ho9LYavUIGdkf0b50aaOduIA4zdx8FQFWpUYx30p + bucket: reles + source: ./${{ gitea.repository_owner }}-${{ steps.extract_repo.outputs.repo_name }} + target: /${{ gitea.repository_owner }}-${{ steps.extract_repo.outputs.repo_name }}@${{ steps.extract_tag.outputs.tag_name }}/ + insecure: true + recursive: false + + - name: upload bin to minio oss - 3 + uses: https://git.apinb.com/github/minio-upload@main + with: + endpoint: http://172.24.0.20:9000 + access-key-id: 9jtPPB7wwJpe5R2164bS + access-key-secret: ho9LYavUIGdkf0b50aaOduIA4zdx8FQFWpUYx30p + bucket: reles + source: ./etc + target: /${{ gitea.repository_owner }}-${{ steps.extract_repo.outputs.repo_name }}@${{ steps.extract_tag.outputs.tag_name }}/ + insecure: true + recursive: true + + - name: upload success + run: echo "build&upload success." \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0fb5c4f --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +# ---> Go +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +vendor/ +gen/ +.cache/ +logs/ +.idea/ +.vscode/ +.builds/ + +result/ +results/ + +# Go workspace file +go.work + +output/ \ No newline at end of file diff --git a/cmd/check/main.go b/cmd/check/main.go new file mode 100644 index 0000000..5a01d0d --- /dev/null +++ b/cmd/check/main.go @@ -0,0 +1,67 @@ +package main + +import ( + "encoding/csv" + "log" + "os" + + "git.apinb.com/bsm-sdk/core/utils" + "git.apinb.com/dataset/stock/internal/config" + "git.apinb.com/dataset/stock/internal/impl" + "git.apinb.com/dataset/stock/internal/logic/a" + "git.apinb.com/dataset/stock/internal/models" +) + +var ( + ServiceKey = "stock" +) + +func main() { + // 配置初始化 + config.New(ServiceKey) + + // 创建实现层 + impl.NewImpl() + + body := [][]string{} + for _, code := range a.GetStockCodes() { + var dCnt int64 + impl.DBService.Model(&models.StockDaily{}).Where("ts_code=?", code).Count(&dCnt) + var iCnt int64 + impl.DBService.Model(&models.StockIndicator{}).Where("ts_code=?", code).Count(&iCnt) + var fCnt int64 + impl.DBService.Model(&models.StockFinaIndicator{}).Where("ts_code=?", code).Count(&fCnt) + + log.Println("Checking...", code, "Daily", dCnt, "Indicator", iCnt, "FinaIndicator", fCnt) + body = append(body, []string{code, utils.Int2String(int(dCnt)), utils.Int2String(int(iCnt)), utils.Int2String(int(fCnt))}) + } + + WriterCSV("./result/check.csv", body) +} + +// csv文件写入 +func WriterCSV(fileName string, datas [][]string) { + + //OpenFile读取文件,不存在时则创建 + File, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) + if err != nil { + log.Println("文件打开失败!") + } + defer File.Close() + + //创建写入接口 + WriterCsv := csv.NewWriter(File) + str := []string{"Code", "Daily", "Indicator", "FinaIndicator"} //需要写入csv的数据,切片类型 + + //写入一条数据,传入数据为切片(追加模式) + err1 := WriterCsv.Write(str) + if err1 != nil { + log.Println("WriterCsv写入文件失败") + } + + for _, s := range datas { + WriterCsv.Write(s) + } + WriterCsv.Flush() //刷新,不刷新是无法写入的 + log.Println("数据写入成功...") +} diff --git a/cmd/main/main.go b/cmd/main/main.go new file mode 100644 index 0000000..d310b8c --- /dev/null +++ b/cmd/main/main.go @@ -0,0 +1,48 @@ +package main + +import ( + "fmt" + + "git.apinb.com/bsm-sdk/core/infra" + "git.apinb.com/bsm-sdk/core/middleware" + "git.apinb.com/dataset/stock/internal/config" + "git.apinb.com/dataset/stock/internal/cron" + "git.apinb.com/dataset/stock/internal/impl" + "git.apinb.com/dataset/stock/internal/routers" + "github.com/gin-gonic/gin" +) + +var ( + ServiceKey = "stock" +) + +func main() { + // 配置初始化 + config.New(ServiceKey) + + // 创建实现层 + impl.NewImpl() + + // starter crontab list + cron.Boot() + + // 初始化Gin引擎 + app := gin.Default() + + // 使用中间件 + middleware.Mode(app) + app.Use(middleware.Cors()) + app.Use(gin.Recovery()) + + // register health check + app.HEAD("/", infra.Health) + + // register routers + routers.Register(ServiceKey, app) + + // start + err := app.Run(fmt.Sprintf(":%s", config.Spec.Port)) + if err != nil { + panic(err) + } +} diff --git a/cmd/test/main.go b/cmd/test/main.go new file mode 100644 index 0000000..cad0642 --- /dev/null +++ b/cmd/test/main.go @@ -0,0 +1,16 @@ +package main + +import ( + "encoding/json" + "fmt" + + "git.apinb.com/dataset/stock/internal/logic/a" +) + +func main() { + a.NewApiClient() + reply, err := a.SetFinaIndicator("000029.SZ", "20230331") + fmt.Println("Err", err) + jsonBytes, _ := json.Marshal(reply.Data.Items) + fmt.Println("Reply", string(jsonBytes)) +} diff --git a/etc/stock_dev.yaml b/etc/stock_dev.yaml new file mode 100644 index 0000000..13afdab --- /dev/null +++ b/etc/stock_dev.yaml @@ -0,0 +1,12 @@ +Service: folo +Port: 15935 + +Databases: + Driver: postgres + Source: + - host=8.137.107.29 user=postgres password=Weidong2023~! dbname=stock_test port=19432 sslmode=disable TimeZone=Asia/Shanghai + +# cache DB的选择请在后面直接带参数,不带会自动HASH计算选择DB库。 +Cache: redis://null:Weidong2023~!@8.137.107.29:19379/ + + diff --git a/etc/stock_prod.yaml b/etc/stock_prod.yaml new file mode 100644 index 0000000..cd1e151 --- /dev/null +++ b/etc/stock_prod.yaml @@ -0,0 +1,11 @@ +Service: folo +Port: 15935 + +Databases: + Driver: postgres + Source: + - host=8.137.107.29 user=postgres password=Weidong2023~! dbname=stock_prod port=19432 sslmode=disable TimeZone=Asia/Shanghai + +# cache DB的选择请在后面直接带参数,不带会自动HASH计算选择DB库。 +Cache: redis://null:Weidong2023~!@8.137.107.29:19379/ + diff --git a/etc/stock_test.yaml b/etc/stock_test.yaml new file mode 100644 index 0000000..941fc1b --- /dev/null +++ b/etc/stock_test.yaml @@ -0,0 +1,11 @@ +Service: folo +Port: 15935 + +Databases: + Driver: postgres + Source: + - host=8.137.107.29 user=postgres password=Weidong2023~! dbname=stock_test port=19432 sslmode=disable TimeZone=Asia/Shanghai + +# cache DB的选择请在后面直接带参数,不带会自动HASH计算选择DB库。 +Cache: redis://null:Weidong2023~!@8.137.107.29:19379/ + diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..4ac0a94 --- /dev/null +++ b/go.mod @@ -0,0 +1,77 @@ +module git.apinb.com/dataset/stock + +go 1.25.1 + +require ( + git.apinb.com/bsm-sdk/core v0.1.8 + github.com/ShawnRong/tushare-go v0.0.0-20200418035301-a5d4e0f72854 + github.com/allegro/bigcache/v3 v3.1.0 + github.com/gin-gonic/gin v1.11.0 + gorm.io/gorm v1.31.1 +) + +require ( + filippo.io/edwards25519 v1.1.0 // indirect + github.com/bytedance/gopkg v0.1.3 // indirect + github.com/bytedance/sonic v1.14.2 // indirect + github.com/bytedance/sonic/loader v0.4.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cloudwego/base64x v0.1.6 // indirect + github.com/coreos/go-semver v0.3.1 // indirect + github.com/coreos/go-systemd/v22 v22.6.0 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/gabriel-vasile/mimetype v1.4.12 // indirect + github.com/gin-contrib/cors v1.7.6 // indirect + github.com/gin-contrib/sse v1.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.30.1 // indirect + github.com/go-sql-driver/mysql v1.9.3 // indirect + github.com/goccy/go-json v0.10.5 // indirect + github.com/goccy/go-yaml v1.19.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v5 v5.3.0 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.8.0 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/oklog/ulid/v2 v2.1.1 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/quic-go/qpack v0.6.0 // indirect + github.com/quic-go/quic-go v0.59.0 // indirect + github.com/redis/go-redis/v9 v9.17.2 // indirect + github.com/robfig/cron/v3 v3.0.1 + github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.3.1 // indirect + go.etcd.io/etcd/api/v3 v3.6.7 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.6.7 // indirect + go.etcd.io/etcd/client/v3 v3.6.7 // indirect + go.uber.org/mock v0.6.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.1 // indirect + golang.org/x/arch v0.23.0 // indirect + golang.org/x/crypto v0.47.0 // indirect + golang.org/x/net v0.49.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.40.0 // indirect + golang.org/x/text v0.33.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260112192933-99fd39fd28a9 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260112192933-99fd39fd28a9 // indirect + google.golang.org/grpc v1.78.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + gorm.io/driver/mysql v1.6.0 // indirect + gorm.io/driver/postgres v1.6.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..64af219 --- /dev/null +++ b/go.sum @@ -0,0 +1,224 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +git.apinb.com/bsm-sdk/core v0.1.8 h1:INSp+Yw+X+SzRV1Refxx1kqnFCflWc0RyMdPy+r7YHk= +git.apinb.com/bsm-sdk/core v0.1.8/go.mod h1:rCmMma8R2pvByImgoZDm2OPLdr+IUNr7LBPyayb8aN0= +github.com/ShawnRong/tushare-go v0.0.0-20200418035301-a5d4e0f72854 h1:JuJt9mJKjvALjxD9qOoHivQNr5Cw58nDQLr3dmbXOYU= +github.com/ShawnRong/tushare-go v0.0.0-20200418035301-a5d4e0f72854/go.mod h1:itk99hhMTCA0hAiX614YcECQZaqd56qysQzZluyCbs0= +github.com/allegro/bigcache/v3 v3.1.0 h1:H2Vp8VOvxcrB91o86fUSVJFqeuz8kpyyB02eH3bSzwk= +github.com/allegro/bigcache/v3 v3.1.0/go.mod h1:aPyh7jEvrog9zAwx5N7+JUQX5dZTSGpxF1LAR4dr35I= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= +github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= +github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE= +github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980= +github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o= +github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= +github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= +github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= +github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= +github.com/coreos/go-systemd/v22 v22.6.0 h1:aGVa/v8B7hpb0TKl0MWoAavPDmHvobFe5R5zn0bCJWo= +github.com/coreos/go-systemd/v22 v22.6.0/go.mod h1:iG+pp635Fo7ZmV/j14KUcmEyWF+0X7Lua8rrTWzYgWU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= +github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY= +github.com/gin-contrib/cors v1.7.6/go.mod h1:Ulcl+xN4jel9t1Ry8vqph23a60FwH9xVLd+3ykmTjOk= +github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= +github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= +github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= +github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w= +github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM= +github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= +github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= +github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= +github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4 h1:kEISI/Gx67NzH3nJxAmY/dGac80kKZgZt134u7Y/k1s= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4/go.mod h1:6Nz966r3vQYCqIzWsuEl9d7cf7mRhtDmm++sOxlnfxI= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo= +github.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s= +github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= +github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= +github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= +github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= +github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= +github.com/redis/go-redis/v9 v9.17.2 h1:P2EGsA4qVIM3Pp+aPocCJ7DguDHhqrXNhVcEp4ViluI= +github.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= +github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.etcd.io/etcd/api/v3 v3.6.7 h1:7BNJ2gQmc3DNM+9cRkv7KkGQDayElg8x3X+tFDYS+E0= +go.etcd.io/etcd/api/v3 v3.6.7/go.mod h1:xJ81TLj9hxrYYEDmXTeKURMeY3qEDN24hqe+q7KhbnI= +go.etcd.io/etcd/client/pkg/v3 v3.6.7 h1:vvzgyozz46q+TyeGBuFzVuI53/yd133CHceNb/AhBVs= +go.etcd.io/etcd/client/pkg/v3 v3.6.7/go.mod h1:2IVulJ3FZ/czIGl9T4lMF1uxzrhRahLqe+hSgy+Kh7Q= +go.etcd.io/etcd/client/v3 v3.6.7 h1:9WqA5RpIBtdMxAy1ukXLAdtg2pAxNqW5NUoO2wQrE6U= +go.etcd.io/etcd/client/v3 v3.6.7/go.mod h1:2XfROY56AXnUqGsvl+6k29wrwsSbEh1lAouQB1vHpeE= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= +go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= +go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= +go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg= +golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= +golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/api v0.0.0-20260112192933-99fd39fd28a9 h1:4DKBrmaqeptdEzp21EfrOEh8LE7PJ5ywH6wydSbOfGY= +google.golang.org/genproto/googleapis/api v0.0.0-20260112192933-99fd39fd28a9/go.mod h1:dd646eSK+Dk9kxVBl1nChEOhJPtMXriCcVb4x3o6J+E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260112192933-99fd39fd28a9 h1:IY6/YYRrFUk0JPp0xOVctvFIVuRnjccihY5kxf5g0TE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260112192933-99fd39fd28a9/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= +google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg= +gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo= +gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4= +gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo= +gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg= +gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..6e8a895 --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,36 @@ +package config + +import ( + "git.apinb.com/bsm-sdk/core/conf" + "git.apinb.com/bsm-sdk/core/env" +) + +var ( + Spec SrvConfig +) + +type SrvConfig struct { + conf.Base `yaml:",inline"` + Databases *conf.DBConf `yaml:"Databases"` +} + +func New(srvKey string) { + + // 初始化通用配置 创建一个新的配置实例,用于服务配置 + conf.New(srvKey, &Spec) + // 配置校验 服务端口如果不合规,则随机分配端口 + Spec.Port = conf.CheckPort(Spec.Port) + + // 初始化自定义环境变量 + Spec.SecretKey = env.Runtime.JwtSecretKey + + // InitRootUser 配置说明: + // 由于bool类型零值是false,如果配置文件中未设置该字段,默认值为false + // 为了保持向后兼容和默认启用初始化,我们采用以下策略: + // - 如果配置文件中显式设置为false,则禁用初始化 + // - 如果配置文件中未设置或设置为true,则启用初始化 + // 注意:InitRootUserData函数内部已有幂等性检查,重复执行是安全的 + + // 配置校验 服务名称地址及监听地址不能为空 + conf.NotNil(Spec.Service, Spec.Cache) +} diff --git a/internal/cron/boot.go b/internal/cron/boot.go new file mode 100644 index 0000000..1bc68eb --- /dev/null +++ b/internal/cron/boot.go @@ -0,0 +1,36 @@ +package cron + +import ( + "os" + + "git.apinb.com/bsm-sdk/core/utils" + "git.apinb.com/dataset/stock/internal/logic/a" + "github.com/robfig/cron/v3" +) + +func Boot() { + if !utils.PathExists("./markdata/") { + os.MkdirAll("./markdata/", 0755) + } + + scheduler := cron.New() + // 每天运行3次来更新数据 + scheduler.AddFunc("30 20 * * *", func() { + updateTask() + }) + scheduler.Start() +} + +func updateTask() { + a.NewApiClient() + // 获取股票列表 + a.GetStockBasic() + // 获取股票日线 + a.GetStockDaily() + // 获取股票指标 + a.GetStockIndicator() + // 获取股票财务指标数据 + a.GetFinaIndicator() + // 生成Markdown文件 + a.GenMarkData() +} diff --git a/internal/impl/impl.go b/internal/impl/impl.go new file mode 100644 index 0000000..a5a1a23 --- /dev/null +++ b/internal/impl/impl.go @@ -0,0 +1,25 @@ +package impl + +import ( + "git.apinb.com/bsm-sdk/core/cache/redis" + "git.apinb.com/bsm-sdk/core/with" + "git.apinb.com/dataset/stock/internal/config" + "github.com/allegro/bigcache/v3" + "gorm.io/gorm" +) + +var ( + RedisService *redis.RedisClient // Redis 客户端服务 + DBService *gorm.DB + MemoryService *bigcache.BigCache // 内存缓存服务 +) + +// NewImpl 初始化各类服务实例 +func NewImpl() { + // 初始化内存缓存 + MemoryService = with.Memory(nil) + // 初始化 Redis 缓存 + RedisService = with.RedisCache(config.Spec.Cache) + // 初始化数据库服务 + DBService = with.Databases(config.Spec.Databases, nil) // model +} diff --git a/internal/logic/a/basic.go b/internal/logic/a/basic.go new file mode 100644 index 0000000..0ebe851 --- /dev/null +++ b/internal/logic/a/basic.go @@ -0,0 +1,103 @@ +package a + +import ( + "errors" + "log" + "strings" + + "git.apinb.com/dataset/stock/internal/impl" + "git.apinb.com/dataset/stock/internal/models" + "gorm.io/gorm" +) + +func GetStockBasic() { + // 参数 + params := map[string]string{ + "list_status": "L", // 上市状态 L上市 D退市 P暂停上市,默认是L + } + + // 字段 + fields := strings.Split("ts_code,symbol,name,area,industry,fullname,enname,cnspell,market,exchange,list_date,is_hs,act_name,act_ent_type", ",") + + // 根据api 请求对应的接口 + reply, _ := TushareClient.StockBasic(params, fields) + if reply.Code != 0 { + log.Println("ERROR", "GetStockBasic", reply.Code, reply.Msg) + return + } + + for _, item := range reply.Data.Items { + var record models.StockBasic + data := Anys2Strings(item) + + err := impl.DBService.Where("ts_code=?", data[0]).First(&record).Error + if errors.Is(err, gorm.ErrRecordNotFound) { + // create + record = models.StockBasic{ + TsCode: data[0], + Symbol: data[1], + Name: data[2], + Area: data[3], + Industry: data[4], + FullName: data[5], + EnName: data[6], + CnSpell: data[7], + Market: data[8], + Exchange: data[9], + ListDate: data[10], + IsHS: data[11], + ActName: data[12], + ActEntType: data[13], + } + impl.DBService.Create(&record) + } else if err == nil { + // check updated. + upd := make(map[string]any) + if record.Name != data[2] { + upd["name"] = data[2] + } + if record.Area != data[3] { + upd["area"] = data[3] + } + if record.Industry != data[4] { + upd["industry"] = data[4] + } + if record.FullName != data[5] { + upd["full_name"] = data[5] + } + if record.EnName != data[6] { + upd["en_name"] = data[6] + } + if record.CnSpell != data[7] { + upd["cn_spell"] = data[7] + } + if record.Market != data[8] { + upd["market"] = data[8] + } + if record.Exchange != data[9] { + upd["exchange"] = data[9] + } + if record.ListDate != data[10] { + upd["list_date"] = data[10] + } + if record.IsHS != data[11] { + upd["is_hs"] = data[11] + } + if record.ActName != data[12] { + upd["act_name"] = data[12] + } + if record.ActEntType != data[13] { + upd["act_ent_type"] = data[13] + } + if len(upd) > 0 { + impl.DBService.Model(record).Updates(upd) + } + } + } +} + +func GetStockCodes() []string { + var col []string + impl.DBService.Model(&models.StockBasic{}).Pluck("ts_code", &col) + return col +} diff --git a/internal/logic/a/daily.go b/internal/logic/a/daily.go new file mode 100644 index 0000000..9bbd724 --- /dev/null +++ b/internal/logic/a/daily.go @@ -0,0 +1,72 @@ +package a + +import ( + "log" + "time" + + "git.apinb.com/bsm-sdk/core/utils" + "git.apinb.com/dataset/stock/internal/impl" + "git.apinb.com/dataset/stock/internal/models" +) + +func GetStockDaily() { + end := time.Now().Format("20060102") + for _, code := range GetStockCodes() { + var start string = "20230101" + var last models.StockDaily + err := impl.DBService.Where("ts_code=?", code).Order("trade_date DESC").First(&last).Error + if err == nil { + start = utils.Int2String(last.TradeDate) + } + params := map[string]string{ + "ts_code": code, + "start_date": start, + "end_date": end, + } + + if start == end { + continue + } + + fields := []string{} + reply, err := TushareClient.Daily(params, fields) + if err != nil { + log.Println("ERROR", "GetStockDaily", err) + return + } + + records := make([]*models.StockDaily, 0) + for _, item := range reply.Data.Items { + t := utils.String2Int(item[1].(string)) + if t == 0 { + continue + } + + var cnt int64 + impl.DBService.Model(&models.StockDaily{}).Where("ts_code=? and trade_date=?", item[0].(string), t).Count(&cnt) + if cnt == 0 { + records = append(records, &models.StockDaily{ + TsCode: item[0].(string), + TradeDate: t, + Open: item[2].(float64), + High: item[3].(float64), + Low: item[4].(float64), + Close: item[5].(float64), + PreClose: item[6].(float64), + Change: item[7].(float64), + PctChg: item[8].(float64), + Vol: item[9].(float64), + Amount: item[10].(float64), + }) + } + } + + if len(records) > 0 { + impl.DBService.CreateInBatches(records, 100) + } + + time.Sleep(200 * time.Microsecond) + + } + +} diff --git a/internal/logic/a/fina_indicator.go b/internal/logic/a/fina_indicator.go new file mode 100644 index 0000000..87232e2 --- /dev/null +++ b/internal/logic/a/fina_indicator.go @@ -0,0 +1,836 @@ +package a + +import ( + "log" + "strings" + "time" + + "git.apinb.com/bsm-sdk/core/utils" + "git.apinb.com/dataset/stock/internal/impl" + "git.apinb.com/dataset/stock/internal/models" + tushare "github.com/ShawnRong/tushare-go" +) + +var PeriodArgs []string = []string{ + "20230331", + "20230630", + "20230930", + "20231231", + "20240331", + "20240630", + "20240930", + "20241231", + "20250331", + "20250630", + "20250930", + "20251231", +} + +func SetFinaIndicator(code, period string) (*tushare.APIResponse, error) { + params := map[string]string{ + "ts_code": code, + "period": period, + } + + fields := strings.Split("ts_code,ann_date,end_date,eps,dt_eps,total_revenue_ps,revenue_ps,capital_rese_ps,surplus_rese_ps,undist_profit_ps,extra_item,profit_dedt,gross_margin,current_ratio,quick_ratio,cash_ratio,invturn_days,arturn_days,inv_turn,ar_turn,ca_turn,fa_turn,assets_turn,op_income,valuechange_income,interst_income,daa,ebit,ebitda,fcff,fcfe,current_exint,noncurrent_exint,interestdebt,netdebt,tangible_asset,working_capital,networking_capital,invest_capital,retained_earnings,diluted2_eps,bps,ocfps,retainedps,cfps,ebit_ps,fcff_ps,fcfe_ps,netprofit_margin,grossprofit_margin,cogs_of_sales,expense_of_sales,profit_to_gr,saleexp_to_gr,adminexp_of_gr,finaexp_of_gr,impai_ttm,gc_of_gr,op_of_gr,ebit_of_gr,roe,roe_waa,roe_dt,roa,npta,roic,roe_yearly,roa2_yearly,roe_avg,opincome_of_ebt,investincome_of_ebt,n_op_profit_of_ebt,tax_to_ebt,dtprofit_to_profit,salescash_to_or,ocf_to_or,ocf_to_opincome,capitalized_to_da,debt_to_assets,assets_to_eqt,dp_assets_to_eqt,ca_to_assets,nca_to_assets,tbassets_to_totalassets,int_to_talcap,eqt_to_talcapital,currentdebt_to_debt,longdeb_to_debt,ocf_to_shortdebt,debt_to_eqt,eqt_to_debt,eqt_to_interestdebt,tangibleasset_to_debt,tangasset_to_intdebt,tangibleasset_to_netdebt,ocf_to_debt,ocf_to_interestdebt,ocf_to_netdebt,ebit_to_interest,longdebt_to_workingcapital,ebitda_to_debt,turn_days,roa_yearly,roa_dp,fixed_assets,profit_prefin_exp,non_op_profit,op_to_ebt,nop_to_ebt,ocf_to_profit,cash_to_liqdebt,cash_to_liqdebt_withinterest,op_to_liqdebt,op_to_debt,roic_yearly,total_fa_trun,profit_to_op,q_opincome,q_investincome,q_dtprofit,q_eps,q_netprofit_margin,q_gsprofit_margin,q_exp_to_sales,q_profit_to_gr,q_saleexp_to_gr,q_adminexp_to_gr,q_finaexp_to_gr,q_impair_to_gr_ttm,q_gc_to_gr,q_op_to_gr,q_roe,q_dt_roe,q_npta,q_opincome_to_ebt,q_investincome_to_ebt,q_dtprofit_to_profit,q_salescash_to_or,q_ocf_to_sales,q_ocf_to_or,basic_eps_yoy,dt_eps_yoy,cfps_yoy,op_yoy,ebt_yoy,netprofit_yoy,dt_netprofit_yoy,ocf_yoy,roe_yoy,bps_yoy,assets_yoy,eqt_yoy,tr_yoy,or_yoy,q_gr_yoy,q_gr_qoq,q_sales_yoy,q_sales_qoq,q_op_yoy,q_op_qoq,q_profit_yoy,q_profit_qoq,q_netprofit_yoy,q_netprofit_qoq,equity_yoy,rd_exp,update_flag", ",") + return TushareClient.FinaIndicator(params, fields) +} + +func ExistFinaIndicator(code, period string) bool { + var cnt int64 + impl.DBService.Model(&models.StockFinaIndicator{}).Where("ts_code=? and period=?", code, utils.String2Int(period)).Count(&cnt) + if cnt == 0 { + return false + } else { + return true + } +} + +func ReFinaIndicator(in *tushare.APIResponse) (bool, map[string]any) { + fiLen := len(in.Data.Items) + if fiLen == 0 { + return false, nil + } + + itemData := make(map[string]any) + for i := 0; i < len(in.Data.Items[0]); i++ { + itemData[in.Data.Fields[i]] = in.Data.Items[0][i] + } + + if itemData == nil { + return false, nil + } + + return true, itemData + +} + +func GetFinaIndicator() { + for _, code := range GetStockCodes() { + + for _, period := range PeriodArgs { + // 请求API + reply, err := SetFinaIndicator(code, period) + if err != nil { + log.Println("ERROR", "GetFinaIndicator", err) + continue + } + + if ok, item := ReFinaIndicator(reply); ok { + exist := ExistFinaIndicator(code, period) + if !exist { + CreateFinaIndicator(code, period, item) + } else { + UpdateFinaIndicator(code, period, item) + } + } + + for _, item := range reply.Data.Items { + // 重构数据 + itemData := make(map[string]any) + for i := 0; i < len(item); i++ { + itemData[reply.Data.Fields[i]] = item[i] + } + + } + } + + time.Sleep(200 * time.Microsecond) + } +} + +func CreateFinaIndicator(code, period string, itemData map[string]any) { + // 不存在则创建 + data := models.StockFinaIndicator{ + TsCode: code, + Period: utils.String2Int(period), + AnnDate: utils.AnyToString(itemData["ann_date"]), + EndDate: utils.AnyToString(itemData["end_date"]), + + // 每股指标 + Eps: Any2Float(itemData["eps"]), + DtEps: Any2Float(itemData["dt_eps"]), + TotalRevenuePs: Any2Float(itemData["total_revenue_ps"]), + RevenuePs: Any2Float(itemData["revenue_ps"]), + CapitalResePs: Any2Float(itemData["capital_rese_ps"]), + SurplusResePs: Any2Float(itemData["surplus_rese_ps"]), + UndistProfitPs: Any2Float(itemData["undist_profit_ps"]), + Diluted2Eps: Any2Float(itemData["diluted2_eps"]), + Bps: Any2Float(itemData["bps"]), + Ocfps: Any2Float(itemData["ocfps"]), + Retainedps: Any2Float(itemData["retainedps"]), + Cfps: Any2Float(itemData["cfps"]), + EbitPs: Any2Float(itemData["ebit_ps"]), + FcffPs: Any2Float(itemData["fcff_ps"]), + FcfePs: Any2Float(itemData["fcfe_ps"]), + + // 利润表相关 + ExtraItem: Any2Float(itemData["extra_item"]), + ProfitDedt: Any2Float(itemData["profit_dedt"]), + GrossMargin: Any2Float(itemData["gross_margin"]), + OpIncome: Any2Float(itemData["op_income"]), + ValuechangeIncome: Any2Float(itemData["valuechange_income"]), + InterstIncome: Any2Float(itemData["interst_income"]), + Daa: Any2Float(itemData["daa"]), + Ebit: Any2Float(itemData["ebit"]), + Ebitda: Any2Float(itemData["ebitda"]), + Fcff: Any2Float(itemData["fcff"]), + Fcfe: Any2Float(itemData["fcfe"]), + RdExp: Any2Float(itemData["rd_exp"]), + FixedAssets: Any2Float(itemData["fixed_assets"]), + ProfitPrefinExp: Any2Float(itemData["profit_prefin_exp"]), + NonOpProfit: Any2Float(itemData["non_op_profit"]), + + // 资产负债表相关 + CurrentExint: Any2Float(itemData["current_exint"]), + NoncurrentExint: Any2Float(itemData["noncurrent_exint"]), + Interestdebt: Any2Float(itemData["interestdebt"]), + Netdebt: Any2Float(itemData["netdebt"]), + TangibleAsset: Any2Float(itemData["tangible_asset"]), + WorkingCapital: Any2Float(itemData["working_capital"]), + NetworkingCapital: Any2Float(itemData["networking_capital"]), + InvestCapital: Any2Float(itemData["invest_capital"]), + RetainedEarnings: Any2Float(itemData["retained_earnings"]), + + // 偿债能力指标 + CurrentRatio: Any2Float(itemData["current_ratio"]), + QuickRatio: Any2Float(itemData["quick_ratio"]), + CashRatio: Any2Float(itemData["cash_ratio"]), + DebtToAssets: Any2Float(itemData["debt_to_assets"]), + AssetsToEqt: Any2Float(itemData["assets_to_eqt"]), + DpAssetsToEqt: Any2Float(itemData["dp_assets_to_eqt"]), + DebtToEqt: Any2Float(itemData["debt_to_eqt"]), + EqtToDebt: Any2Float(itemData["eqt_to_debt"]), + OcfToShortdebt: Any2Float(itemData["ocf_to_shortdebt"]), + EbitToInterest: Any2Float(itemData["ebit_to_interest"]), + + // 运营能力指标 + InvturnDays: Any2Float(itemData["invturn_days"]), + ArturnDays: Any2Float(itemData["arturn_days"]), + InvTurn: Any2Float(itemData["inv_turn"]), + ArTurn: Any2Float(itemData["ar_turn"]), + CaTurn: Any2Float(itemData["ca_turn"]), + FaTurn: Any2Float(itemData["fa_turn"]), + AssetsTurn: Any2Float(itemData["assets_turn"]), + TurnDays: Any2Float(itemData["turn_days"]), + TotalFaTrun: Any2Float(itemData["total_fa_trun"]), + + // 盈利能力指标 + NetprofitMargin: Any2Float(itemData["netprofit_margin"]), + GrossprofitMargin: Any2Float(itemData["grossprofit_margin"]), + CogsOfSales: Any2Float(itemData["cogs_of_sales"]), + ExpenseOfSales: Any2Float(itemData["expense_of_sales"]), + Roe: Any2Float(itemData["roe"]), + RoeWaa: Any2Float(itemData["roe_waa"]), + RoeDt: Any2Float(itemData["roe_dt"]), + Roa: Any2Float(itemData["roa"]), + Npta: Any2Float(itemData["npta"]), + Roic: Any2Float(itemData["roic"]), + RoaDp: Any2Float(itemData["roa_dp"]), + + // 结构指标 + CaToAssets: Any2Float(itemData["ca_to_assets"]), + NcaToAssets: Any2Float(itemData["nca_to_assets"]), + TbassetsToTotalassets: Any2Float(itemData["tbassets_to_totalassets"]), + IntToTalcap: Any2Float(itemData["int_to_talcap"]), + EqtToTalcapital: Any2Float(itemData["eqt_to_talcapital"]), + CurrentdebtToDebt: Any2Float(itemData["currentdebt_to_debt"]), + LongdebToDebt: Any2Float(itemData["longdeb_to_debt"]), + TangibleassetToDebt: Any2Float(itemData["tangibleasset_to_debt"]), + + // 单季度指标 + QOpincome: Any2Float(itemData["q_opincome"]), + QInvestincome: Any2Float(itemData["q_investincome"]), + QDtprofit: Any2Float(itemData["q_dtprofit"]), + QEps: Any2Float(itemData["q_eps"]), + QNetprofitMargin: Any2Float(itemData["q_netprofit_margin"]), + QGscaleprofitMargin: Any2Float(itemData["q_gsprofit_margin"]), + QExpToSales: Any2Float(itemData["q_exp_to_sales"]), + QRoe: Any2Float(itemData["q_roe"]), + QDtRoe: Any2Float(itemData["q_dt_roe"]), + QNpta: Any2Float(itemData["q_npta"]), + + // 同比增长率 + BasicEpsYoy: Any2Float(itemData["basic_eps_yoy"]), + DtEpsYoy: Any2Float(itemData["dt_eps_yoy"]), + CfpsYoy: Any2Float(itemData["cfps_yoy"]), + OpYoy: Any2Float(itemData["op_yoy"]), + EbtYoy: Any2Float(itemData["ebt_yoy"]), + NetprofitYoy: Any2Float(itemData["netprofit_yoy"]), + DtNetprofitYoy: Any2Float(itemData["dt_netprofit_yoy"]), + OcfYoy: Any2Float(itemData["ocf_yoy"]), + RoeYoy: Any2Float(itemData["roe_yoy"]), + BpsYoy: Any2Float(itemData["bps_yoy"]), + AssetsYoy: Any2Float(itemData["assets_yoy"]), + EqtYoy: Any2Float(itemData["eqt_yoy"]), + TrYoy: Any2Float(itemData["tr_yoy"]), + OrYoy: Any2Float(itemData["or_yoy"]), + EquityYoy: Any2Float(itemData["equity_yoy"]), + + // 其他比率指标 + ProfitToGr: Any2Float(itemData["profit_to_gr"]), + SaleexpToGr: Any2Float(itemData["saleexp_to_gr"]), + AdminexpOfGr: Any2Float(itemData["adminexp_of_gr"]), + FinaexpOfGr: Any2Float(itemData["finaexp_of_gr"]), + ImpaiTtm: Any2Float(itemData["impai_ttm"]), + GcOfGr: Any2Float(itemData["gc_of_gr"]), + OpOfGr: Any2Float(itemData["op_of_gr"]), + EbitOfGr: Any2Float(itemData["ebit_of_gr"]), + OpincomeOfEbt: Any2Float(itemData["opincome_of_ebt"]), + InvestincomeOfEbt: Any2Float(itemData["investincome_of_ebt"]), + NOpProfitOfEbt: Any2Float(itemData["n_op_profit_of_ebt"]), + TaxToEbt: Any2Float(itemData["tax_to_ebt"]), + DtprofitToProfit: Any2Float(itemData["dtprofit_to_profit"]), + SalescashToOr: Any2Float(itemData["salescash_to_or"]), + OcfToOr: Any2Float(itemData["ocf_to_or"]), + OcfToOpincome: Any2Float(itemData["ocf_to_opincome"]), + CapitalizedToDa: Any2Float(itemData["capitalized_to_da"]), + OcfToDebt: Any2Float(itemData["ocf_to_debt"]), + OcfToInterestdebt: Any2Float(itemData["ocf_to_interestdebt"]), + OcfToNetdebt: Any2Float(itemData["ocf_to_netdebt"]), + LongdebtToWorkingcapital: Any2Float(itemData["longdebt_to_workingcapital"]), + EbitdaToDebt: Any2Float(itemData["ebitda_to_debt"]), + OpToEbt: Any2Float(itemData["op_to_ebt"]), + NopToEbt: Any2Float(itemData["nop_to_ebt"]), + OcfToProfit: Any2Float(itemData["ocf_to_profit"]), + CashToLiqdebt: Any2Float(itemData["cash_to_liqdebt"]), + CashToLiqdebtWithinterest: Any2Float(itemData["cash_to_liqdebt_withinterest"]), + OpToLiqdebt: Any2Float(itemData["op_to_liqdebt"]), + OpToDebt: Any2Float(itemData["op_to_debt"]), + ProfitToOp: Any2Float(itemData["profit_to_op"]), + + // 年度化指标 + RoeYearly: Any2Float(itemData["roe_yearly"]), + Roa2Yearly: Any2Float(itemData["roa2_yearly"]), + RoaYearly: Any2Float(itemData["roa_yearly"]), + RoicYearly: Any2Float(itemData["roic_yearly"]), + RoeAvg: Any2Float(itemData["roe_avg"]), + + // 单季度增长比率 + QGrYoy: Any2Float(itemData["q_gr_yoy"]), + QGrQoq: Any2Float(itemData["q_gr_qoq"]), + QSalesYoy: Any2Float(itemData["q_sales_yoy"]), + QSalesQoq: Any2Float(itemData["q_sales_qoq"]), + QOpYoy: Any2Float(itemData["q_op_yoy"]), + QOpQoq: Any2Float(itemData["q_op_qoq"]), + QProfitYoy: Any2Float(itemData["q_profit_yoy"]), + QProfitQoq: Any2Float(itemData["q_profit_qoq"]), + QNetprofitYoy: Any2Float(itemData["q_netprofit_yoy"]), + QNetprofitQoq: Any2Float(itemData["q_netprofit_qoq"]), + + // 单季度比率指标 + QProfitToGr: Any2Float(itemData["q_profit_to_gr"]), + QSaleexpToGr: Any2Float(itemData["q_saleexp_to_gr"]), + QAdminexpToGr: Any2Float(itemData["q_adminexp_to_gr"]), + QFinaexpToGr: Any2Float(itemData["q_finaexp_to_gr"]), + QImpairToGrTtm: Any2Float(itemData["q_impair_to_gr_ttm"]), + QGcToGr: Any2Float(itemData["q_gc_to_gr"]), + QOpToGr: Any2Float(itemData["q_op_to_gr"]), + QOpincomeToEbt: Any2Float(itemData["q_opincome_to_ebt"]), + QInvestincomeToEbt: Any2Float(itemData["q_investincome_to_ebt"]), + QDtprofitToProfit: Any2Float(itemData["q_dtprofit_to_profit"]), + QSalescashToOr: Any2Float(itemData["q_salescash_to_or"]), + QOcfToSales: Any2Float(itemData["q_ocf_to_sales"]), + QOcfToOr: Any2Float(itemData["q_ocf_to_or"]), + + // 其他 + UpdateFlag: itemData["update_flag"].(string), + } + + impl.DBService.Create(&data) +} + +func UpdateFinaIndicator(code, period string, item map[string]any) { + var old models.StockFinaIndicator + err := impl.DBService.Model(&models.StockFinaIndicator{}).Where("ts_code=? and period=?", code, utils.String2Int(period)).First(&old).Error + if err != nil { + return + } + + upd := make(map[string]any) + + // Compare each field and only add to update if different + // Date fields + if old.AnnDate != utils.AnyToString(item["ann_date"]) { + upd["ann_date"] = utils.AnyToString(item["ann_date"]) + } + if old.EndDate != utils.AnyToString(item["end_date"]) { + upd["end_date"] = utils.AnyToString(item["end_date"]) + } + + // 每股指标 + if old.Eps != Any2Float(item["eps"]) { + upd["eps"] = Any2Float(item["eps"]) + } + if old.DtEps != Any2Float(item["dt_eps"]) { + upd["dt_eps"] = Any2Float(item["dt_eps"]) + } + if old.TotalRevenuePs != Any2Float(item["total_revenue_ps"]) { + upd["total_revenue_ps"] = Any2Float(item["total_revenue_ps"]) + } + if old.RevenuePs != Any2Float(item["revenue_ps"]) { + upd["revenue_ps"] = Any2Float(item["revenue_ps"]) + } + if old.CapitalResePs != Any2Float(item["capital_rese_ps"]) { + upd["capital_rese_ps"] = Any2Float(item["capital_rese_ps"]) + } + if old.SurplusResePs != Any2Float(item["surplus_rese_ps"]) { + upd["surplus_rese_ps"] = Any2Float(item["surplus_rese_ps"]) + } + if old.UndistProfitPs != Any2Float(item["undist_profit_ps"]) { + upd["undist_profit_ps"] = Any2Float(item["undist_profit_ps"]) + } + if old.Diluted2Eps != Any2Float(item["diluted2_eps"]) { + upd["diluted2_eps"] = Any2Float(item["diluted2_eps"]) + } + if old.Bps != Any2Float(item["bps"]) { + upd["bps"] = Any2Float(item["bps"]) + } + if old.Ocfps != Any2Float(item["ocfps"]) { + upd["ocfps"] = Any2Float(item["ocfps"]) + } + if old.Retainedps != Any2Float(item["retainedps"]) { + upd["retainedps"] = Any2Float(item["retainedps"]) + } + if old.Cfps != Any2Float(item["cfps"]) { + upd["cfps"] = Any2Float(item["cfps"]) + } + if old.EbitPs != Any2Float(item["ebit_ps"]) { + upd["ebit_ps"] = Any2Float(item["ebit_ps"]) + } + if old.FcffPs != Any2Float(item["fcff_ps"]) { + upd["fcff_ps"] = Any2Float(item["fcff_ps"]) + } + if old.FcfePs != Any2Float(item["fcfe_ps"]) { + upd["fcfe_ps"] = Any2Float(item["fcfe_ps"]) + } + + // 利润表相关 + if old.ExtraItem != Any2Float(item["extra_item"]) { + upd["extra_item"] = Any2Float(item["extra_item"]) + } + if old.ProfitDedt != Any2Float(item["profit_dedt"]) { + upd["profit_dedt"] = Any2Float(item["profit_dedt"]) + } + if old.GrossMargin != Any2Float(item["gross_margin"]) { + upd["gross_margin"] = Any2Float(item["gross_margin"]) + } + if old.OpIncome != Any2Float(item["op_income"]) { + upd["op_income"] = Any2Float(item["op_income"]) + } + if old.ValuechangeIncome != Any2Float(item["valuechange_income"]) { + upd["valuechange_income"] = Any2Float(item["valuechange_income"]) + } + if old.InterstIncome != Any2Float(item["interst_income"]) { + upd["interst_income"] = Any2Float(item["interst_income"]) + } + if old.Daa != Any2Float(item["daa"]) { + upd["daa"] = Any2Float(item["daa"]) + } + if old.Ebit != Any2Float(item["ebit"]) { + upd["ebit"] = Any2Float(item["ebit"]) + } + if old.Ebitda != Any2Float(item["ebitda"]) { + upd["ebitda"] = Any2Float(item["ebitda"]) + } + if old.Fcff != Any2Float(item["fcff"]) { + upd["fcff"] = Any2Float(item["fcff"]) + } + if old.Fcfe != Any2Float(item["fcfe"]) { + upd["fcfe"] = Any2Float(item["fcfe"]) + } + if old.RdExp != Any2Float(item["rd_exp"]) { + upd["rd_exp"] = Any2Float(item["rd_exp"]) + } + if old.FixedAssets != Any2Float(item["fixed_assets"]) { + upd["fixed_assets"] = Any2Float(item["fixed_assets"]) + } + if old.ProfitPrefinExp != Any2Float(item["profit_prefin_exp"]) { + upd["profit_prefin_exp"] = Any2Float(item["profit_prefin_exp"]) + } + if old.NonOpProfit != Any2Float(item["non_op_profit"]) { + upd["non_op_profit"] = Any2Float(item["non_op_profit"]) + } + + // 资产负债表相关 + if old.CurrentExint != Any2Float(item["current_exint"]) { + upd["current_exint"] = Any2Float(item["current_exint"]) + } + if old.NoncurrentExint != Any2Float(item["noncurrent_exint"]) { + upd["noncurrent_exint"] = Any2Float(item["noncurrent_exint"]) + } + if old.Interestdebt != Any2Float(item["interestdebt"]) { + upd["interestdebt"] = Any2Float(item["interestdebt"]) + } + if old.Netdebt != Any2Float(item["netdebt"]) { + upd["netdebt"] = Any2Float(item["netdebt"]) + } + if old.TangibleAsset != Any2Float(item["tangible_asset"]) { + upd["tangible_asset"] = Any2Float(item["tangible_asset"]) + } + if old.WorkingCapital != Any2Float(item["working_capital"]) { + upd["working_capital"] = Any2Float(item["working_capital"]) + } + if old.NetworkingCapital != Any2Float(item["networking_capital"]) { + upd["networking_capital"] = Any2Float(item["networking_capital"]) + } + if old.InvestCapital != Any2Float(item["invest_capital"]) { + upd["invest_capital"] = Any2Float(item["invest_capital"]) + } + if old.RetainedEarnings != Any2Float(item["retained_earnings"]) { + upd["retained_earnings"] = Any2Float(item["retained_earnings"]) + } + + // 偿债能力指标 + if old.CurrentRatio != Any2Float(item["current_ratio"]) { + upd["current_ratio"] = Any2Float(item["current_ratio"]) + } + if old.QuickRatio != Any2Float(item["quick_ratio"]) { + upd["quick_ratio"] = Any2Float(item["quick_ratio"]) + } + if old.CashRatio != Any2Float(item["cash_ratio"]) { + upd["cash_ratio"] = Any2Float(item["cash_ratio"]) + } + if old.DebtToAssets != Any2Float(item["debt_to_assets"]) { + upd["debt_to_assets"] = Any2Float(item["debt_to_assets"]) + } + if old.AssetsToEqt != Any2Float(item["assets_to_eqt"]) { + upd["assets_to_eqt"] = Any2Float(item["assets_to_eqt"]) + } + if old.DpAssetsToEqt != Any2Float(item["dp_assets_to_eqt"]) { + upd["dp_assets_to_eqt"] = Any2Float(item["dp_assets_to_eqt"]) + } + if old.DebtToEqt != Any2Float(item["debt_to_eqt"]) { + upd["debt_to_eqt"] = Any2Float(item["debt_to_eqt"]) + } + if old.EqtToDebt != Any2Float(item["eqt_to_debt"]) { + upd["eqt_to_debt"] = Any2Float(item["eqt_to_debt"]) + } + if old.OcfToShortdebt != Any2Float(item["ocf_to_shortdebt"]) { + upd["ocf_to_shortdebt"] = Any2Float(item["ocf_to_shortdebt"]) + } + if old.EbitToInterest != Any2Float(item["ebit_to_interest"]) { + upd["ebit_to_interest"] = Any2Float(item["ebit_to_interest"]) + } + + // 运营能力指标 + if old.InvturnDays != Any2Float(item["invturn_days"]) { + upd["invturn_days"] = Any2Float(item["invturn_days"]) + } + if old.ArturnDays != Any2Float(item["arturn_days"]) { + upd["arturn_days"] = Any2Float(item["arturn_days"]) + } + if old.InvTurn != Any2Float(item["inv_turn"]) { + upd["inv_turn"] = Any2Float(item["inv_turn"]) + } + if old.ArTurn != Any2Float(item["ar_turn"]) { + upd["ar_turn"] = Any2Float(item["ar_turn"]) + } + if old.CaTurn != Any2Float(item["ca_turn"]) { + upd["ca_turn"] = Any2Float(item["ca_turn"]) + } + if old.FaTurn != Any2Float(item["fa_turn"]) { + upd["fa_turn"] = Any2Float(item["fa_turn"]) + } + if old.AssetsTurn != Any2Float(item["assets_turn"]) { + upd["assets_turn"] = Any2Float(item["assets_turn"]) + } + if old.TurnDays != Any2Float(item["turn_days"]) { + upd["turn_days"] = Any2Float(item["turn_days"]) + } + if old.TotalFaTrun != Any2Float(item["total_fa_trun"]) { + upd["total_fa_trun"] = Any2Float(item["total_fa_trun"]) + } + + // 盈利能力指标 + if old.NetprofitMargin != Any2Float(item["netprofit_margin"]) { + upd["netprofit_margin"] = Any2Float(item["netprofit_margin"]) + } + if old.GrossprofitMargin != Any2Float(item["grossprofit_margin"]) { + upd["grossprofit_margin"] = Any2Float(item["grossprofit_margin"]) + } + if old.CogsOfSales != Any2Float(item["cogs_of_sales"]) { + upd["cogs_of_sales"] = Any2Float(item["cogs_of_sales"]) + } + if old.ExpenseOfSales != Any2Float(item["expense_of_sales"]) { + upd["expense_of_sales"] = Any2Float(item["expense_of_sales"]) + } + if old.Roe != Any2Float(item["roe"]) { + upd["roe"] = Any2Float(item["roe"]) + } + if old.RoeWaa != Any2Float(item["roe_waa"]) { + upd["roe_waa"] = Any2Float(item["roe_waa"]) + } + if old.RoeDt != Any2Float(item["roe_dt"]) { + upd["roe_dt"] = Any2Float(item["roe_dt"]) + } + if old.Roa != Any2Float(item["roa"]) { + upd["roa"] = Any2Float(item["roa"]) + } + if old.Npta != Any2Float(item["npta"]) { + upd["npta"] = Any2Float(item["npta"]) + } + if old.Roic != Any2Float(item["roic"]) { + upd["roic"] = Any2Float(item["roic"]) + } + if old.RoaDp != Any2Float(item["roa_dp"]) { + upd["roa_dp"] = Any2Float(item["roa_dp"]) + } + + // 结构指标 + if old.CaToAssets != Any2Float(item["ca_to_assets"]) { + upd["ca_to_assets"] = Any2Float(item["ca_to_assets"]) + } + if old.NcaToAssets != Any2Float(item["nca_to_assets"]) { + upd["nca_to_assets"] = Any2Float(item["nca_to_assets"]) + } + if old.TbassetsToTotalassets != Any2Float(item["tbassets_to_totalassets"]) { + upd["tbassets_to_totalassets"] = Any2Float(item["tbassets_to_totalassets"]) + } + if old.IntToTalcap != Any2Float(item["int_to_talcap"]) { + upd["int_to_talcap"] = Any2Float(item["int_to_talcap"]) + } + if old.EqtToTalcapital != Any2Float(item["eqt_to_talcapital"]) { + upd["eqt_to_talcapital"] = Any2Float(item["eqt_to_talcapital"]) + } + if old.CurrentdebtToDebt != Any2Float(item["currentdebt_to_debt"]) { + upd["currentdebt_to_debt"] = Any2Float(item["currentdebt_to_debt"]) + } + if old.LongdebToDebt != Any2Float(item["longdeb_to_debt"]) { + upd["longdeb_to_debt"] = Any2Float(item["longdeb_to_debt"]) + } + if old.TangibleassetToDebt != Any2Float(item["tangibleasset_to_debt"]) { + upd["tangibleasset_to_debt"] = Any2Float(item["tangibleasset_to_debt"]) + } + + // 单季度指标 + if old.QOpincome != Any2Float(item["q_opincome"]) { + upd["q_opincome"] = Any2Float(item["q_opincome"]) + } + if old.QInvestincome != Any2Float(item["q_investincome"]) { + upd["q_investincome"] = Any2Float(item["q_investincome"]) + } + if old.QDtprofit != Any2Float(item["q_dtprofit"]) { + upd["q_dtprofit"] = Any2Float(item["q_dtprofit"]) + } + if old.QEps != Any2Float(item["q_eps"]) { + upd["q_eps"] = Any2Float(item["q_eps"]) + } + if old.QNetprofitMargin != Any2Float(item["q_netprofit_margin"]) { + upd["q_netprofit_margin"] = Any2Float(item["q_netprofit_margin"]) + } + if old.QGscaleprofitMargin != Any2Float(item["q_gsprofit_margin"]) { + upd["q_gsprofit_margin"] = Any2Float(item["q_gsprofit_margin"]) + } + if old.QExpToSales != Any2Float(item["q_exp_to_sales"]) { + upd["q_exp_to_sales"] = Any2Float(item["q_exp_to_sales"]) + } + if old.QRoe != Any2Float(item["q_roe"]) { + upd["q_roe"] = Any2Float(item["q_roe"]) + } + if old.QDtRoe != Any2Float(item["q_dt_roe"]) { + upd["q_dt_roe"] = Any2Float(item["q_dt_roe"]) + } + if old.QNpta != Any2Float(item["q_npta"]) { + upd["q_npta"] = Any2Float(item["q_npta"]) + } + + // 同比增长率 + if old.BasicEpsYoy != Any2Float(item["basic_eps_yoy"]) { + upd["basic_eps_yoy"] = Any2Float(item["basic_eps_yoy"]) + } + if old.DtEpsYoy != Any2Float(item["dt_eps_yoy"]) { + upd["dt_eps_yoy"] = Any2Float(item["dt_eps_yoy"]) + } + if old.CfpsYoy != Any2Float(item["cfps_yoy"]) { + upd["cfps_yoy"] = Any2Float(item["cfps_yoy"]) + } + if old.OpYoy != Any2Float(item["op_yoy"]) { + upd["op_yoy"] = Any2Float(item["op_yoy"]) + } + if old.EbtYoy != Any2Float(item["ebt_yoy"]) { + upd["ebt_yoy"] = Any2Float(item["ebt_yoy"]) + } + if old.NetprofitYoy != Any2Float(item["netprofit_yoy"]) { + upd["netprofit_yoy"] = Any2Float(item["netprofit_yoy"]) + } + if old.DtNetprofitYoy != Any2Float(item["dt_netprofit_yoy"]) { + upd["dt_netprofit_yoy"] = Any2Float(item["dt_netprofit_yoy"]) + } + if old.OcfYoy != Any2Float(item["ocf_yoy"]) { + upd["ocf_yoy"] = Any2Float(item["ocf_yoy"]) + } + if old.RoeYoy != Any2Float(item["roe_yoy"]) { + upd["roe_yoy"] = Any2Float(item["roe_yoy"]) + } + if old.BpsYoy != Any2Float(item["bps_yoy"]) { + upd["bps_yoy"] = Any2Float(item["bps_yoy"]) + } + if old.AssetsYoy != Any2Float(item["assets_yoy"]) { + upd["assets_yoy"] = Any2Float(item["assets_yoy"]) + } + if old.EqtYoy != Any2Float(item["eqt_yoy"]) { + upd["eqt_yoy"] = Any2Float(item["eqt_yoy"]) + } + if old.TrYoy != Any2Float(item["tr_yoy"]) { + upd["tr_yoy"] = Any2Float(item["tr_yoy"]) + } + if old.OrYoy != Any2Float(item["or_yoy"]) { + upd["or_yoy"] = Any2Float(item["or_yoy"]) + } + if old.EquityYoy != Any2Float(item["equity_yoy"]) { + upd["equity_yoy"] = Any2Float(item["equity_yoy"]) + } + + // 其他比率指标 + if old.ProfitToGr != Any2Float(item["profit_to_gr"]) { + upd["profit_to_gr"] = Any2Float(item["profit_to_gr"]) + } + if old.SaleexpToGr != Any2Float(item["saleexp_to_gr"]) { + upd["saleexp_to_gr"] = Any2Float(item["saleexp_to_gr"]) + } + if old.AdminexpOfGr != Any2Float(item["adminexp_of_gr"]) { + upd["adminexp_of_gr"] = Any2Float(item["adminexp_of_gr"]) + } + if old.FinaexpOfGr != Any2Float(item["finaexp_of_gr"]) { + upd["finaexp_of_gr"] = Any2Float(item["finaexp_of_gr"]) + } + if old.ImpaiTtm != Any2Float(item["impai_ttm"]) { + upd["impai_ttm"] = Any2Float(item["impai_ttm"]) + } + if old.GcOfGr != Any2Float(item["gc_of_gr"]) { + upd["gc_of_gr"] = Any2Float(item["gc_of_gr"]) + } + if old.OpOfGr != Any2Float(item["op_of_gr"]) { + upd["op_of_gr"] = Any2Float(item["op_of_gr"]) + } + if old.EbitOfGr != Any2Float(item["ebit_of_gr"]) { + upd["ebit_of_gr"] = Any2Float(item["ebit_of_gr"]) + } + if old.OpincomeOfEbt != Any2Float(item["opincome_of_ebt"]) { + upd["opincome_of_ebt"] = Any2Float(item["opincome_of_ebt"]) + } + if old.InvestincomeOfEbt != Any2Float(item["investincome_of_ebt"]) { + upd["investincome_of_ebt"] = Any2Float(item["investincome_of_ebt"]) + } + if old.NOpProfitOfEbt != Any2Float(item["n_op_profit_of_ebt"]) { + upd["n_op_profit_of_ebt"] = Any2Float(item["n_op_profit_of_ebt"]) + } + if old.TaxToEbt != Any2Float(item["tax_to_ebt"]) { + upd["tax_to_ebt"] = Any2Float(item["tax_to_ebt"]) + } + if old.DtprofitToProfit != Any2Float(item["dtprofit_to_profit"]) { + upd["dtprofit_to_profit"] = Any2Float(item["dtprofit_to_profit"]) + } + if old.SalescashToOr != Any2Float(item["salescash_to_or"]) { + upd["salescash_to_or"] = Any2Float(item["salescash_to_or"]) + } + if old.OcfToOr != Any2Float(item["ocf_to_or"]) { + upd["ocf_to_or"] = Any2Float(item["ocf_to_or"]) + } + if old.OcfToOpincome != Any2Float(item["ocf_to_opincome"]) { + upd["ocf_to_opincome"] = Any2Float(item["ocf_to_opincome"]) + } + if old.CapitalizedToDa != Any2Float(item["capitalized_to_da"]) { + upd["capitalized_to_da"] = Any2Float(item["capitalized_to_da"]) + } + if old.OcfToDebt != Any2Float(item["ocf_to_debt"]) { + upd["ocf_to_debt"] = Any2Float(item["ocf_to_debt"]) + } + if old.OcfToInterestdebt != Any2Float(item["ocf_to_interestdebt"]) { + upd["ocf_to_interestdebt"] = Any2Float(item["ocf_to_interestdebt"]) + } + if old.OcfToNetdebt != Any2Float(item["ocf_to_netdebt"]) { + upd["ocf_to_netdebt"] = Any2Float(item["ocf_to_netdebt"]) + } + if old.LongdebtToWorkingcapital != Any2Float(item["longdebt_to_workingcapital"]) { + upd["longdebt_to_workingcapital"] = Any2Float(item["longdebt_to_workingcapital"]) + } + if old.EbitdaToDebt != Any2Float(item["ebitda_to_debt"]) { + upd["ebitda_to_debt"] = Any2Float(item["ebitda_to_debt"]) + } + if old.OpToEbt != Any2Float(item["op_to_ebt"]) { + upd["op_to_ebt"] = Any2Float(item["op_to_ebt"]) + } + if old.NopToEbt != Any2Float(item["nop_to_ebt"]) { + upd["nop_to_ebt"] = Any2Float(item["nop_to_ebt"]) + } + if old.OcfToProfit != Any2Float(item["ocf_to_profit"]) { + upd["ocf_to_profit"] = Any2Float(item["ocf_to_profit"]) + } + if old.CashToLiqdebt != Any2Float(item["cash_to_liqdebt"]) { + upd["cash_to_liqdebt"] = Any2Float(item["cash_to_liqdebt"]) + } + if old.CashToLiqdebtWithinterest != Any2Float(item["cash_to_liqdebt_withinterest"]) { + upd["cash_to_liqdebt_withinterest"] = Any2Float(item["cash_to_liqdebt_withinterest"]) + } + if old.OpToLiqdebt != Any2Float(item["op_to_liqdebt"]) { + upd["op_to_liqdebt"] = Any2Float(item["op_to_liqdebt"]) + } + if old.OpToDebt != Any2Float(item["op_to_debt"]) { + upd["op_to_debt"] = Any2Float(item["op_to_debt"]) + } + if old.ProfitToOp != Any2Float(item["profit_to_op"]) { + upd["profit_to_op"] = Any2Float(item["profit_to_op"]) + } + + // 年度化指标 + if old.RoeYearly != Any2Float(item["roe_yearly"]) { + upd["roe_yearly"] = Any2Float(item["roe_yearly"]) + } + if old.Roa2Yearly != Any2Float(item["roa2_yearly"]) { + upd["roa2_yearly"] = Any2Float(item["roa2_yearly"]) + } + if old.RoaYearly != Any2Float(item["roa_yearly"]) { + upd["roa_yearly"] = Any2Float(item["roa_yearly"]) + } + if old.RoicYearly != Any2Float(item["roic_yearly"]) { + upd["roic_yearly"] = Any2Float(item["roic_yearly"]) + } + if old.RoeAvg != Any2Float(item["roe_avg"]) { + upd["roe_avg"] = Any2Float(item["roe_avg"]) + } + + // 单季度增长比率 + if old.QGrYoy != Any2Float(item["q_gr_yoy"]) { + upd["q_gr_yoy"] = Any2Float(item["q_gr_yoy"]) + } + if old.QGrQoq != Any2Float(item["q_gr_qoq"]) { + upd["q_gr_qoq"] = Any2Float(item["q_gr_qoq"]) + } + if old.QSalesYoy != Any2Float(item["q_sales_yoy"]) { + upd["q_sales_yoy"] = Any2Float(item["q_sales_yoy"]) + } + if old.QSalesQoq != Any2Float(item["q_sales_qoq"]) { + upd["q_sales_qoq"] = Any2Float(item["q_sales_qoq"]) + } + if old.QOpYoy != Any2Float(item["q_op_yoy"]) { + upd["q_op_yoy"] = Any2Float(item["q_op_yoy"]) + } + if old.QOpQoq != Any2Float(item["q_op_qoq"]) { + upd["q_op_qoq"] = Any2Float(item["q_op_qoq"]) + } + if old.QProfitYoy != Any2Float(item["q_profit_yoy"]) { + upd["q_profit_yoy"] = Any2Float(item["q_profit_yoy"]) + } + if old.QProfitQoq != Any2Float(item["q_profit_qoq"]) { + upd["q_profit_qoq"] = Any2Float(item["q_profit_qoq"]) + } + if old.QNetprofitYoy != Any2Float(item["q_netprofit_yoy"]) { + upd["q_netprofit_yoy"] = Any2Float(item["q_netprofit_yoy"]) + } + if old.QNetprofitQoq != Any2Float(item["q_netprofit_qoq"]) { + upd["q_netprofit_qoq"] = Any2Float(item["q_netprofit_qoq"]) + } + + // 单季度比率指标 + if old.QProfitToGr != Any2Float(item["q_profit_to_gr"]) { + upd["q_profit_to_gr"] = Any2Float(item["q_profit_to_gr"]) + } + if old.QSaleexpToGr != Any2Float(item["q_saleexp_to_gr"]) { + upd["q_saleexp_to_gr"] = Any2Float(item["q_saleexp_to_gr"]) + } + if old.QAdminexpToGr != Any2Float(item["q_adminexp_to_gr"]) { + upd["q_adminexp_to_gr"] = Any2Float(item["q_adminexp_to_gr"]) + } + if old.QFinaexpToGr != Any2Float(item["q_finaexp_to_gr"]) { + upd["q_finaexp_to_gr"] = Any2Float(item["q_finaexp_to_gr"]) + } + if old.QImpairToGrTtm != Any2Float(item["q_impair_to_gr_ttm"]) { + upd["q_impair_to_gr_ttm"] = Any2Float(item["q_impair_to_gr_ttm"]) + } + if old.QGcToGr != Any2Float(item["q_gc_to_gr"]) { + upd["q_gc_to_gr"] = Any2Float(item["q_gc_to_gr"]) + } + if old.QOpToGr != Any2Float(item["q_op_to_gr"]) { + upd["q_op_to_gr"] = Any2Float(item["q_op_to_gr"]) + } + if old.QOpincomeToEbt != Any2Float(item["q_opincome_to_ebt"]) { + upd["q_opincome_to_ebt"] = Any2Float(item["q_opincome_to_ebt"]) + } + if old.QInvestincomeToEbt != Any2Float(item["q_investincome_to_ebt"]) { + upd["q_investincome_to_ebt"] = Any2Float(item["q_investincome_to_ebt"]) + } + if old.QDtprofitToProfit != Any2Float(item["q_dtprofit_to_profit"]) { + upd["q_dtprofit_to_profit"] = Any2Float(item["q_dtprofit_to_profit"]) + } + if old.QSalescashToOr != Any2Float(item["q_salescash_to_or"]) { + upd["q_salescash_to_or"] = Any2Float(item["q_salescash_to_or"]) + } + if old.QOcfToSales != Any2Float(item["q_ocf_to_sales"]) { + upd["q_ocf_to_sales"] = Any2Float(item["q_ocf_to_sales"]) + } + if old.QOcfToOr != Any2Float(item["q_ocf_to_or"]) { + upd["q_ocf_to_or"] = Any2Float(item["q_ocf_to_or"]) + } + + // 其他 + if old.UpdateFlag != item["update_flag"].(string) { + upd["update_flag"] = item["update_flag"].(string) + } + + // Only perform update if there are actual changes + if len(upd) > 0 { + impl.DBService.Model(&models.StockFinaIndicator{}).Where("ts_code=? and period=?", code, utils.String2Int(period)).Updates(upd) + } +} diff --git a/internal/logic/a/indicator.go b/internal/logic/a/indicator.go new file mode 100644 index 0000000..7fc13c8 --- /dev/null +++ b/internal/logic/a/indicator.go @@ -0,0 +1,78 @@ +package a + +import ( + "log" + "strings" + "time" + + "git.apinb.com/bsm-sdk/core/utils" + "git.apinb.com/dataset/stock/internal/impl" + "git.apinb.com/dataset/stock/internal/models" +) + +func GetStockIndicator() { + end := time.Now().Format("20060102") + for _, code := range GetStockCodes() { + var start string = "20230101" + var last models.StockIndicator + err := impl.DBService.Where("ts_code=?", code).Order("trade_date DESC").First(&last).Error + if err == nil { + start = utils.Int2String(last.TradeDate) + } + params := map[string]string{ + "ts_code": code, + "start_date": start, + "end_date": end, + } + + if start == end { + continue + } + + fields := strings.Split("ts_code,trade_date,close,turnover_rate,turnover_rate_f,volume_ratio,pe,pe_ttm,pb,ps,ps_ttm,dv_ratio,dv_ttm,total_share,float_share,free_share,total_mv,circ_mv", ",") + reply, err := TushareClient.DailyBasic(params, fields) + if err != nil { + log.Println("ERROR", "GetStockIndicator", err, "PARAMS", params) + return + } + + records := make([]*models.StockIndicator, 0) + for _, item := range reply.Data.Items { + t := utils.String2Int(item[1].(string)) + if t == 0 { + continue + } + + var cnt int64 + impl.DBService.Model(&models.StockIndicator{}).Where("ts_code=? and trade_date=?", item[0].(string), t).Count(&cnt) + if cnt == 0 { + records = append(records, &models.StockIndicator{ + TsCode: item[0].(string), + TradeDate: t, + Close: Any2Float(item[2]), + TurnoverRate: Any2Float(item[3]), + TurnoverRateF: Any2Float(item[4]), + VolumeRatio: Any2Float(item[5]), + Pe: Any2Float(item[6]), + PeTtm: Any2Float(item[7]), + Pb: Any2Float(item[8]), + Ps: Any2Float(item[9]), + PsTtm: Any2Float(item[10]), + DvRatio: Any2Float(item[11]), + DvTtm: Any2Float(item[12]), + TotalShare: Any2Float(item[13]), + FloatShare: Any2Float(item[14]), + FreeShare: Any2Float(item[15]), + TotalMv: Any2Float(item[16]), + CircMv: Any2Float(item[17]), + }) + } + } + + if len(records) > 0 { + impl.DBService.CreateInBatches(records, 100) + } + + time.Sleep(200 * time.Microsecond) + } +} diff --git a/internal/logic/a/markdown.go b/internal/logic/a/markdown.go new file mode 100644 index 0000000..1e14a03 --- /dev/null +++ b/internal/logic/a/markdown.go @@ -0,0 +1,467 @@ +package a + +import ( + "fmt" + + "git.apinb.com/bsm-sdk/core/utils" + "git.apinb.com/dataset/stock/internal/impl" + "git.apinb.com/dataset/stock/internal/models" +) + +func GenMarkData() { + for _, code := range GetStockCodes() { + md := NewMarkdownBuilder() + + var basic models.StockBasic + impl.DBService.Where("ts_code = ?", code).First(&basic) + + md.Title(basic.TsCode + " " + basic.Name + " 股票详情") + md.Catalog("公司基础情况") + md.KvText("股票代码", basic.TsCode) + md.KvText("股票名称", basic.Name) + md.KvText("股票行业", basic.Industry) + md.KvText("股票地域", basic.Area) + md.KvText("上市日期", basic.ListDate) + md.KvText("股票市场", basic.Market) + md.KvText("股票类型", basic.Market) + md.KvText("股票是否沪深港通", basic.IsHS) + md.KvText("股票中文全称", basic.FullName) + md.KvText("股票实控人", basic.ActName) + md.KvText("股票实控人企业性质", basic.ActEntType) + md.BR() + + var daily []models.StockDaily + impl.DBService.Where("ts_code = ?", code).Order("trade_date desc").Limit(200).Find(&daily) + var data [][]string + for _, row := range daily { + data = append(data, []string{ + utils.Int2String(row.TradeDate), + fmt.Sprintf("%.2f", row.Open), + fmt.Sprintf("%.2f", row.Close), + fmt.Sprintf("%.2f", row.High), + fmt.Sprintf("%.2f", row.Low), + fmt.Sprintf("%.2f", row.Vol), + fmt.Sprintf("%.2f", row.Amount), + }) + } + md.Table("最近200天股票行情(倒序)", []string{"日期", "开盘价", "收盘价", "最高价", "最低价", "成交量", "成交额"}, data) + md.BR() + + var indicator []models.StockIndicator + impl.DBService.Where("ts_code = ?", code).Order("trade_date desc").Limit(200).Find(&indicator) + var indiData [][]string + for _, row := range indicator { + indiData = append(indiData, []string{ + utils.Int2String(row.TradeDate), + fmt.Sprintf("%.2f", row.Close), + fmt.Sprintf("%.2f%%", row.TurnoverRate), + fmt.Sprintf("%.2f", row.VolumeRatio), + fmt.Sprintf("%.2f", row.Pe), + fmt.Sprintf("%.2f", row.PeTtm), + fmt.Sprintf("%.2f", row.Pb), + fmt.Sprintf("%.2f", row.Ps), + fmt.Sprintf("%.2f", row.PsTtm), + fmt.Sprintf("%.2f", row.DvRatio), + fmt.Sprintf("%.2f", row.DvTtm), + fmt.Sprintf("%.2f", row.TotalShare), + fmt.Sprintf("%.2f", row.FloatShare), + fmt.Sprintf("%.2f", row.FreeShare), + fmt.Sprintf("%.2f", row.TotalMv), + fmt.Sprintf("%.2f", row.CircMv), + }) + } + md.Table("最近200天股票指标(倒序)", []string{"日期", "当日收盘价", "换手率", "量比", "市盈率", "市盈率 TTM", "市净率", "市销率", "市销率 TTM", "股息率", "股息率 TTM", "总股本", "流通股本", "自由流通股本", "总市值", "流通市值"}, indiData) + md.BR() + + var finaIndicator []models.StockFinaIndicator + impl.DBService.Where("ts_code = ?", code).Order("period desc").Find(&finaIndicator) + var fiHeaders []string = []string{ + "报告期数", + "公告日期", + "报告期", + + // 每股指标 + "基本每股收益", + "稀释每股收益", + "每股营业总收入", + "每股营业收入", + "每股资本公积", + "每股盈余公积", + "每股未分配利润", + "期末摊薄每股收益", + "每股净资产", + "每股经营活动产生的现金流量净额", + "每股留存收益", + "每股现金流量净额", + "每股息税前利润", + "每股企业自由现金流量", + "每股股东自由现金流量", + + // 利润表相关 + "非经常性损益", + "扣除非经常性损益后的净利润", + "毛利", + "经营活动净收益", + "价值变动净收益", + "利息费用", + "折旧与摊销", + "息税前利润", + "息税折旧摊销前利润", + "企业自由现金流量", + "股权自由现金流量", + "研发费用", + "固定资产合计", + "扣除财务费用前营业利润", + "非营业利润", + + // 资产负债表相关 + "无息流动负债", + "无息非流动负债", + "带息债务", + "净债务", + "有形资产", + "营运资金", + "营运流动资本", + "全部投入资本", + "留存收益", + + // 偿债能力指标 + "流动比率", + "速动比率", + "保守速动比率", + "资产负债率", + "权益乘数", + "权益乘数(杜邦分析)", + "产权比率", + "归属于母公司的股东权益/负债合计", + "经营活动产生的现金流量净额/流动负债", + "已获利息倍数", + + // 运营能力指标 + "存货周转天数", + "应收账款周转天数", + "存货周转率", + "应收账款周转率", + "流动资产周转率", + "固定资产周转率", + "总资产周转率", + "营业周期", + + // 盈利能力指标 + "销售净利率", + "销售毛利率", + "销售成本率", + "销售期间费用率", + "净资产收益率", + "加权平均净资产收益率", + "净资产收益率(扣除非经常损益)", + "总资产报酬率", + "总资产净利润", + "投入资本回报率", + "总资产净利率(杜邦分析)", + + // 结构指标 + "流动资产/总资产", + "非流动资产/总资产", + "有形资产/总资产", + "带息债务/全部投入资本", + "归属于母公司的股东权益/全部投入资本", + "流动负债/负债合计", + "非流动负债/负债合计", + "有形资产/负债合计", + + // 单季度指标 + "经营活动单季度净收益", + "价值变动单季度净收益", + "扣除非经常损益后的单季度净利润", + "每股收益(单季度)", + "销售净利率(单季度)", + "销售毛利率(单季度)", + "销售期间费用率(单季度)", + "净资产收益率(单季度)", + "净资产单季度收益率(扣除非经常损益)", + "总资产净利润(单季度)", + + // 同比增长率 + "基本每股收益同比增长率(%)", + "稀释每股收益同比增长率(%)", + "每股经营活动产生的现金流量净额同比增长率(%)", + "营业利润同比增长率(%)", + "利润总额同比增长率(%)", + "归属母公司股东的净利润同比增长率(%)", + "归属母公司股东的净利润-扣除非经常损益同比增长率(%)", + "经营活动产生的现金流量净额同比增长率(%)", + "净资产收益率(摊薄)同比增长率(%)", + "每股净资产相对年初增长率(%)", + "资产总计相对年初增长率(%)", + "归属母公司的股东权益相对年初增长率(%)", + "营业总收入同比增长率(%)", + "营业收入同比增长率(%)", + "净资产同比增长率", + + // 其他比率指标 + "净利润/营业总收入", + "销售费用/营业总收入", + "管理费用/营业总收入", + "财务费用/营业总收入", + "资产减值损失/营业总收入", + "营业总成本/营业总收入", + "营业利润/营业总收入", + "息税前利润/营业总收入", + "经营活动净收益/利润总额", + "价值变动净收益/利润总额", + "营业外收支净额/利润总额", + "所得税/利润总额", + "扣除非经常损益后的净利润/净利润", + "销售商品提供劳务收到的现金/营业收入", + "经营活动产生的现金流量净额/营业收入", + "经营活动产生的现金流量净额/经营活动净收益", + "资本支出/折旧和摊销", + "经营活动产生的现金流量净额/负债合计", + "经营活动产生的现金流量净额/带息债务", + "经营活动产生的现金流量净额/净债务", + "长期债务与营运资金比率", + "息税折旧摊销前利润/负债合计", + "营业利润/利润总额", + "非营业利润/利润总额", + "经营活动产生的现金流量净额/营业利润", + "货币资金/流动负债", + "货币资金/带息流动负债", + "营业利润/流动负债", + "营业利润/负债合计", + "利润总额/营业收入", + + // 年度化指标 + "年化净资产收益率", + "年化总资产报酬率", + "年化总资产净利率", + "年化投入资本回报率", + "平均净资产收益率(增发条件)", + + // 单季度增长比率 + "营业总收入同比增长率(%)(单季度)", + "营业总收入环比增长率(%)(单季度)", + "营业收入同比增长率(%)(单季度)", + "营业收入环比增长率(%)(单季度)", + "营业利润同比增长率(%)(单季度)", + "营业利润环比增长率(%)(单季度)", + "净利润同比增长率(%)(单季度)", + "净利润环比增长率(%)(单季度)", + "归属母公司股东的净利润同比增长率(%)(单季度)", + "归属母公司股东的净利润环比增长率(%)(单季度)", + + // 单季度比率指标 + "净利润/营业总收入(单季度)", + "销售费用/营业总收入 (单季度)", + "管理费用/营业总收入 (单季度)", + "财务费用/营业总收入 (单季度)", + "资产减值损失/营业总收入(单季度)", + "营业总成本/营业总收入 (单季度)", + "营业利润/营业总收入(单季度)", + "经营活动净收益/利润总额(单季度)", + "价值变动净收益/利润总额(单季度)", + "扣除非经常损益后的净利润/净利润(单季度)", + "销售商品提供劳务收到的现金/营业收入(单季度)", + "经营活动产生的现金流量净额/营业收入(单季度)", + "经营活动产生的现金流量净额/经营活动净收益(单季度)", + } + var fiData [][]string + for _, row := range finaIndicator { + fiData = append(fiData, []string{ + fmt.Sprintf("%d", row.Period), + row.AnnDate, + row.EndDate, + + // 每股指标 + fmt.Sprintf("%.2f", row.Eps), + fmt.Sprintf("%.2f", row.DtEps), + fmt.Sprintf("%.2f", row.TotalRevenuePs), + fmt.Sprintf("%.2f", row.RevenuePs), + fmt.Sprintf("%.2f", row.CapitalResePs), + fmt.Sprintf("%.2f", row.SurplusResePs), + fmt.Sprintf("%.2f", row.UndistProfitPs), + fmt.Sprintf("%.2f", row.Diluted2Eps), + fmt.Sprintf("%.2f", row.Bps), + fmt.Sprintf("%.2f", row.Ocfps), + fmt.Sprintf("%.2f", row.Retainedps), + fmt.Sprintf("%.2f", row.Cfps), + fmt.Sprintf("%.2f", row.EbitPs), + fmt.Sprintf("%.2f", row.FcffPs), + fmt.Sprintf("%.2f", row.FcfePs), + + // 利润表相关 + fmt.Sprintf("%.2f", row.ExtraItem), + fmt.Sprintf("%.2f", row.ProfitDedt), + fmt.Sprintf("%.2f", row.GrossMargin), + fmt.Sprintf("%.2f", row.OpIncome), + fmt.Sprintf("%.2f", row.ValuechangeIncome), + fmt.Sprintf("%.2f", row.InterstIncome), + fmt.Sprintf("%.2f", row.Daa), + fmt.Sprintf("%.2f", row.Ebit), + fmt.Sprintf("%.2f", row.Ebitda), + fmt.Sprintf("%.2f", row.Fcff), + fmt.Sprintf("%.2f", row.Fcfe), + fmt.Sprintf("%.2f", row.RdExp), + fmt.Sprintf("%.2f", row.FixedAssets), + fmt.Sprintf("%.2f", row.ProfitPrefinExp), + fmt.Sprintf("%.2f", row.NonOpProfit), + + // 资产负债表相关 + fmt.Sprintf("%.2f", row.CurrentExint), + fmt.Sprintf("%.2f", row.NoncurrentExint), + fmt.Sprintf("%.2f", row.Interestdebt), + fmt.Sprintf("%.2f", row.Netdebt), + fmt.Sprintf("%.2f", row.TangibleAsset), + fmt.Sprintf("%.2f", row.WorkingCapital), + fmt.Sprintf("%.2f", row.NetworkingCapital), + fmt.Sprintf("%.2f", row.InvestCapital), + fmt.Sprintf("%.2f", row.RetainedEarnings), + + // 偿债能力指标 + fmt.Sprintf("%.2f", row.CurrentRatio), + fmt.Sprintf("%.2f", row.QuickRatio), + fmt.Sprintf("%.2f", row.CashRatio), + fmt.Sprintf("%.2f", row.DebtToAssets), + fmt.Sprintf("%.2f", row.AssetsToEqt), + fmt.Sprintf("%.2f", row.DpAssetsToEqt), + fmt.Sprintf("%.2f", row.DebtToEqt), + fmt.Sprintf("%.2f", row.EqtToDebt), + fmt.Sprintf("%.2f", row.OcfToShortdebt), + fmt.Sprintf("%.2f", row.EbitToInterest), + + // 运营能力指标 + fmt.Sprintf("%.2f", row.InvturnDays), + fmt.Sprintf("%.2f", row.ArturnDays), + fmt.Sprintf("%.2f", row.InvTurn), + fmt.Sprintf("%.2f", row.ArTurn), + fmt.Sprintf("%.2f", row.CaTurn), + fmt.Sprintf("%.2f", row.FaTurn), + fmt.Sprintf("%.2f", row.AssetsTurn), + fmt.Sprintf("%.2f", row.TurnDays), + fmt.Sprintf("%.2f", row.TotalFaTrun), + + // 盈利能力指标 + fmt.Sprintf("%.2f", row.NetprofitMargin), + fmt.Sprintf("%.2f", row.GrossprofitMargin), + fmt.Sprintf("%.2f", row.CogsOfSales), + fmt.Sprintf("%.2f", row.ExpenseOfSales), + fmt.Sprintf("%.2f", row.Roe), + fmt.Sprintf("%.2f", row.RoeWaa), + fmt.Sprintf("%.2f", row.RoeDt), + fmt.Sprintf("%.2f", row.Roa), + fmt.Sprintf("%.2f", row.Npta), + fmt.Sprintf("%.2f", row.Roic), + fmt.Sprintf("%.2f", row.RoaDp), + + // 结构指标 + fmt.Sprintf("%.2f", row.CaToAssets), + fmt.Sprintf("%.2f", row.NcaToAssets), + fmt.Sprintf("%.2f", row.TbassetsToTotalassets), + fmt.Sprintf("%.2f", row.IntToTalcap), + fmt.Sprintf("%.2f", row.EqtToTalcapital), + fmt.Sprintf("%.2f", row.CurrentdebtToDebt), + fmt.Sprintf("%.2f", row.LongdebToDebt), + fmt.Sprintf("%.2f", row.TangibleassetToDebt), + + // 单季度指标 + fmt.Sprintf("%.2f", row.QOpincome), + fmt.Sprintf("%.2f", row.QInvestincome), + fmt.Sprintf("%.2f", row.QDtprofit), + fmt.Sprintf("%.2f", row.QEps), + fmt.Sprintf("%.2f", row.QNetprofitMargin), + fmt.Sprintf("%.2f", row.QGscaleprofitMargin), + fmt.Sprintf("%.2f", row.QExpToSales), + fmt.Sprintf("%.2f", row.QRoe), + fmt.Sprintf("%.2f", row.QDtRoe), + fmt.Sprintf("%.2f", row.QNpta), + + // 同比增长率 + fmt.Sprintf("%.2f%%", row.BasicEpsYoy), + fmt.Sprintf("%.2f%%", row.DtEpsYoy), + fmt.Sprintf("%.2f%%", row.CfpsYoy), + fmt.Sprintf("%.2f%%", row.OpYoy), + fmt.Sprintf("%.2f%%", row.EbtYoy), + fmt.Sprintf("%.2f%%", row.NetprofitYoy), + fmt.Sprintf("%.2f%%", row.DtNetprofitYoy), + fmt.Sprintf("%.2f%%", row.OcfYoy), + fmt.Sprintf("%.2f%%", row.RoeYoy), + fmt.Sprintf("%.2f%%", row.BpsYoy), + fmt.Sprintf("%.2f%%", row.AssetsYoy), + fmt.Sprintf("%.2f%%", row.EqtYoy), + fmt.Sprintf("%.2f%%", row.TrYoy), + fmt.Sprintf("%.2f%%", row.OrYoy), + fmt.Sprintf("%.2f%%", row.EquityYoy), + + // 其他比率指标 + fmt.Sprintf("%.2f", row.ProfitToGr), + fmt.Sprintf("%.2f", row.SaleexpToGr), + fmt.Sprintf("%.2f", row.AdminexpOfGr), + fmt.Sprintf("%.2f", row.FinaexpOfGr), + fmt.Sprintf("%.2f", row.ImpaiTtm), + fmt.Sprintf("%.2f", row.GcOfGr), + fmt.Sprintf("%.2f", row.OpOfGr), + fmt.Sprintf("%.2f", row.EbitOfGr), + fmt.Sprintf("%.2f", row.OpincomeOfEbt), + fmt.Sprintf("%.2f", row.InvestincomeOfEbt), + fmt.Sprintf("%.2f", row.NOpProfitOfEbt), + fmt.Sprintf("%.2f", row.TaxToEbt), + fmt.Sprintf("%.2f", row.DtprofitToProfit), + fmt.Sprintf("%.2f", row.SalescashToOr), + fmt.Sprintf("%.2f", row.OcfToOr), + fmt.Sprintf("%.2f", row.OcfToOpincome), + fmt.Sprintf("%.2f", row.CapitalizedToDa), + fmt.Sprintf("%.2f", row.OcfToDebt), + fmt.Sprintf("%.2f", row.OcfToInterestdebt), + fmt.Sprintf("%.2f", row.OcfToNetdebt), + fmt.Sprintf("%.2f", row.LongdebtToWorkingcapital), + fmt.Sprintf("%.2f", row.EbitdaToDebt), + fmt.Sprintf("%.2f", row.OpToEbt), + fmt.Sprintf("%.2f", row.NopToEbt), + fmt.Sprintf("%.2f", row.OcfToProfit), + fmt.Sprintf("%.2f", row.CashToLiqdebt), + fmt.Sprintf("%.2f", row.CashToLiqdebtWithinterest), + fmt.Sprintf("%.2f", row.OpToLiqdebt), + fmt.Sprintf("%.2f", row.OpToDebt), + fmt.Sprintf("%.2f", row.ProfitToOp), + + // 年度化指标 + fmt.Sprintf("%.2f", row.RoeYearly), + fmt.Sprintf("%.2f", row.Roa2Yearly), + fmt.Sprintf("%.2f", row.RoaYearly), + fmt.Sprintf("%.2f", row.RoicYearly), + fmt.Sprintf("%.2f", row.RoeAvg), + + // 单季度增长比率 + fmt.Sprintf("%.2f%%", row.QGrYoy), + fmt.Sprintf("%.2f%%", row.QGrQoq), + fmt.Sprintf("%.2f%%", row.QSalesYoy), + fmt.Sprintf("%.2f%%", row.QSalesQoq), + fmt.Sprintf("%.2f%%", row.QOpYoy), + fmt.Sprintf("%.2f%%", row.QOpQoq), + fmt.Sprintf("%.2f%%", row.QProfitYoy), + fmt.Sprintf("%.2f%%", row.QProfitQoq), + fmt.Sprintf("%.2f%%", row.QNetprofitYoy), + fmt.Sprintf("%.2f%%", row.QNetprofitQoq), + + // 单季度比率指标 + fmt.Sprintf("%.2f", row.QProfitToGr), + fmt.Sprintf("%.2f", row.QSaleexpToGr), + fmt.Sprintf("%.2f", row.QAdminexpToGr), + fmt.Sprintf("%.2f", row.QFinaexpToGr), + fmt.Sprintf("%.2f", row.QImpairToGrTtm), + fmt.Sprintf("%.2f", row.QGcToGr), + fmt.Sprintf("%.2f", row.QOpToGr), + fmt.Sprintf("%.2f", row.QOpincomeToEbt), + fmt.Sprintf("%.2f", row.QInvestincomeToEbt), + fmt.Sprintf("%.2f", row.QDtprofitToProfit), + fmt.Sprintf("%.2f", row.QSalescashToOr), + fmt.Sprintf("%.2f", row.QOcfToSales), + fmt.Sprintf("%.2f", row.QOcfToOr), + }) + } + md.Table("最近3年的股票财务指标(倒序)", fiHeaders, fiData) + md.BR() + + md.SaveToFile("./markdata/" + code + ".md") + } +} diff --git a/internal/logic/a/markdown_builder.go b/internal/logic/a/markdown_builder.go new file mode 100644 index 0000000..cb2b178 --- /dev/null +++ b/internal/logic/a/markdown_builder.go @@ -0,0 +1,79 @@ +package a + +import ( + "bytes" + "io" + "os" + "strings" +) + +type MarkdownBuilder struct { + buf bytes.Buffer +} + +func NewMarkdownBuilder() *MarkdownBuilder { + return &MarkdownBuilder{} +} +func (m *MarkdownBuilder) Title(title string) { + m.buf.WriteString("# " + title + "\n\n") +} + +func (m *MarkdownBuilder) Catalog(text string) { + m.buf.WriteString("## " + text + "\n\n") +} +func (m *MarkdownBuilder) Text(text string) { + m.buf.WriteString(text + "\n\n") +} + +func (m *MarkdownBuilder) KvText(key, val string) { + m.buf.WriteString(key + " :" + val + " \n") +} + +func (m *MarkdownBuilder) BR() { + m.buf.WriteString("\n") +} + +func (m *MarkdownBuilder) Code(lang, code string) { + m.buf.WriteString("```" + lang + "\n") + m.buf.WriteString(code + "\n") + m.buf.WriteString("```\n\n") +} + +// 生成Markdown表格 +// | Header1 | Header2 | +// |---------|---------| +// | Cell1 | Cell2 | +func (m *MarkdownBuilder) Table(catalog string, headers []string, rows [][]string) { + if catalog != "" { + m.buf.WriteString("## " + catalog + "\n") + } + m.buf.WriteString("| " + strings.Join(headers, " | ") + " |\n") + + m.buf.WriteString("| ") + for _, header := range headers { + m.buf.WriteString(strings.Repeat("-", len(header)) + "|") + } + m.buf.WriteString("\n") + for _, row := range rows { + m.buf.WriteString("| " + strings.Join(row, " | ") + " |\n") + } +} + +func (m *MarkdownBuilder) Build() string { + return m.buf.String() +} + +func (m *MarkdownBuilder) SaveToFile(filePath string) error { + rf, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm) + if err != nil { + return err + } + defer rf.Close() + + // 将buffer内容复制到文件 + _, err = io.Copy(rf, &m.buf) + if err != nil { + return err + } + return nil +} diff --git a/internal/logic/a/new.go b/internal/logic/a/new.go new file mode 100644 index 0000000..5f16a3c --- /dev/null +++ b/internal/logic/a/new.go @@ -0,0 +1,39 @@ +package a + +import ( + "time" + + "git.apinb.com/bsm-sdk/core/utils" + tushare "github.com/ShawnRong/tushare-go" +) + +var ( + TushareToken = "432de9ba12b6711948450273ed6e478dbac9e3e712323bbf823ada11" + TushareClient *tushare.TuShare +) + +func NewApiClient() { + TushareClient = tushare.New(TushareToken) +} + +func ReturnLastDay() (string, string) { + now := time.Now() + end := now.Format("20060102") + start := now.AddDate(0, 0, -1).Format("20060102") + return start, end +} + +func Anys2Strings(any []any) []string { + var ret []string + for _, item := range any { + ret = append(ret, utils.AnyToString(item)) + } + return ret +} + +func Any2Float(in any) float64 { + if in == nil { + return -1 + } + return in.(float64) +} diff --git a/internal/logic/hello/ping.go b/internal/logic/hello/ping.go new file mode 100644 index 0000000..91b2165 --- /dev/null +++ b/internal/logic/hello/ping.go @@ -0,0 +1,16 @@ +package hello + +import ( + "net/http" + "time" + + "github.com/gin-gonic/gin" +) + +func Ping(ctx *gin.Context) { + ctx.JSON(http.StatusOK, map[string]any{ + "PING": "PONG", + "TIMESEQ": time.Now().Nanosecond(), + }) + return +} diff --git a/internal/models/query.go b/internal/models/query.go new file mode 100644 index 0000000..778a4c0 --- /dev/null +++ b/internal/models/query.go @@ -0,0 +1 @@ +package models \ No newline at end of file diff --git a/internal/models/stock_basic.go b/internal/models/stock_basic.go new file mode 100644 index 0000000..0382efb --- /dev/null +++ b/internal/models/stock_basic.go @@ -0,0 +1,34 @@ +package models + +import ( + "git.apinb.com/bsm-sdk/core/database" + "gorm.io/gorm" +) + +// StockBasic 股票基本信息表 +type StockBasic struct { + gorm.Model + TsCode string `gorm:"type:varchar(50);not null;index;comment:TS代码"` + Symbol string `gorm:"type:varchar(50);not null;comment:股票代码"` + Name string `gorm:"type:varchar(50);not null;comment:股票名称"` + Area string `gorm:"type:varchar(50);not null;default:'';comment:地域"` + Industry string `gorm:"type:varchar(50);not null;default:'';comment:所属行业"` + FullName string `gorm:"type:varchar(500);comment:股票全称"` + EnName string `gorm:"type:varchar(200);comment:英文全称"` + CnSpell string `gorm:"type:varchar(50);not null;default:'';comment:拼音缩写"` + Market string `gorm:"type:varchar(50);not null;comment:市场类型(主板/创业板/科创板/CDR)"` + Exchange string `gorm:"type:varchar(50);comment:交易所代码"` + ListDate string `gorm:"type:varchar(50);not null;comment:上市日期"` + IsHS string `gorm:"type:varchar(2);default:'N';comment:是否沪深港通标的,N否 H沪股通 S深股通"` + ActName string `gorm:"type:varchar(500);not null;default:'';comment:实控人名称"` + ActEntType string `gorm:"type:varchar(50);not null;default:'';comment:实控人企业性质"` +} + +func init() { + database.AppendMigrate(&StockBasic{}) +} + +// TableName 设置表名 +func (StockBasic) TableName() string { + return "stock_basic" +} diff --git a/internal/models/stock_daily.go b/internal/models/stock_daily.go new file mode 100644 index 0000000..13e754f --- /dev/null +++ b/internal/models/stock_daily.go @@ -0,0 +1,30 @@ +package models + +import ( + "git.apinb.com/bsm-sdk/core/database" +) + +// LastDaily 股票日线数据 +type StockDaily struct { + ID uint `gorm:"primarykey;autoIncrement" json:"id"` + TsCode string `gorm:"type:varchar(20);not null;index:idx_ts_code;uniqueIndex:un_code_date;comment:股票代码" json:"ts_code"` + TradeDate int `gorm:"index:idx_trade_date;uniqueIndex:un_code_date;comment:交易日期" json:"trade_date"` + Open float64 `gorm:"type:decimal(10,4);comment:开盘价" json:"open"` + High float64 `gorm:"type:decimal(10,4);comment:最高价" json:"high"` + Low float64 `gorm:"type:decimal(10,4);comment:最低价" json:"low"` + Close float64 `gorm:"type:decimal(10,4);comment:收盘价" json:"close"` + PreClose float64 `gorm:"type:decimal(10,4);comment:昨收价(除权价)" json:"pre_close"` + Change float64 `gorm:"type:decimal(10,4);comment:涨跌额" json:"change"` + PctChg float64 `gorm:"type:decimal(10,6);comment:涨跌幅(%)" json:"pct_chg"` + Vol float64 `gorm:"type:decimal(15,2);comment:成交量(手)" json:"vol"` + Amount float64 `gorm:"type:decimal(20,2);comment:成交额(千元)" json:"amount"` +} + +func init() { + database.AppendMigrate(&StockDaily{}) +} + +// TableName 指定表名 +func (StockDaily) TableName() string { + return "stock_daily" +} diff --git a/internal/models/stock_fina_indicator.go b/internal/models/stock_fina_indicator.go new file mode 100644 index 0000000..2bfa804 --- /dev/null +++ b/internal/models/stock_fina_indicator.go @@ -0,0 +1,213 @@ +package models + +import ( + "git.apinb.com/bsm-sdk/core/database" + "gorm.io/gorm" +) + +// StockFinaIndicator 财务指标模型 +type StockFinaIndicator struct { + gorm.Model + TsCode string `gorm:"type:varchar(20);not null;index:fi_ts_code;uniqueIndex:un_fi_code_date;comment:TS代码"` + Period int `gorm:"index:idx_period;uniqueIndex:un_fi_code_date;comment:报告期数"` + AnnDate string `gorm:"index:idx_ann_date;comment:公告日期"` + EndDate string `gorm:"index:idx_end_date;comment:报告期"` + + // 每股指标 + Eps float64 `gorm:"type:decimal(20,4);comment:基本每股收益"` + DtEps float64 `gorm:"type:decimal(20,4);comment:稀释每股收益"` + TotalRevenuePs float64 `gorm:"type:decimal(20,4);comment:每股营业总收入"` + RevenuePs float64 `gorm:"type:decimal(20,4);comment:每股营业收入"` + CapitalResePs float64 `gorm:"type:decimal(20,4);comment:每股资本公积"` + SurplusResePs float64 `gorm:"type:decimal(20,4);comment:每股盈余公积"` + UndistProfitPs float64 `gorm:"type:decimal(20,4);comment:每股未分配利润"` + Diluted2Eps float64 `gorm:"type:decimal(20,4);comment:期末摊薄每股收益"` + Bps float64 `gorm:"type:decimal(20,4);comment:每股净资产"` + Ocfps float64 `gorm:"type:decimal(20,4);comment:每股经营活动产生的现金流量净额"` + Retainedps float64 `gorm:"type:decimal(20,4);comment:每股留存收益"` + Cfps float64 `gorm:"type:decimal(20,4);comment:每股现金流量净额"` + EbitPs float64 `gorm:"type:decimal(20,4);comment:每股息税前利润"` + FcffPs float64 `gorm:"type:decimal(20,4);comment:每股企业自由现金流量"` + FcfePs float64 `gorm:"type:decimal(20,4);comment:每股股东自由现金流量"` + + // 利润表相关 + ExtraItem float64 `gorm:"type:decimal(20,4);comment:非经常性损益"` + ProfitDedt float64 `gorm:"type:decimal(20,4);comment:扣除非经常性损益后的净利润"` + GrossMargin float64 `gorm:"type:decimal(20,4);comment:毛利"` + OpIncome float64 `gorm:"type:decimal(20,4);comment:经营活动净收益"` + ValuechangeIncome float64 `gorm:"type:decimal(20,4);comment:价值变动净收益"` + InterstIncome float64 `gorm:"type:decimal(20,4);comment:利息费用"` + Daa float64 `gorm:"type:decimal(20,4);comment:折旧与摊销"` + Ebit float64 `gorm:"type:decimal(20,4);comment:息税前利润"` + Ebitda float64 `gorm:"type:decimal(20,4);comment:息税折旧摊销前利润"` + Fcff float64 `gorm:"type:decimal(20,4);comment:企业自由现金流量"` + Fcfe float64 `gorm:"type:decimal(20,4);comment:股权自由现金流量"` + RdExp float64 `gorm:"type:decimal(20,4);comment:研发费用"` + FixedAssets float64 `gorm:"type:decimal(20,4);comment:固定资产合计"` + ProfitPrefinExp float64 `gorm:"type:decimal(20,4);comment:扣除财务费用前营业利润"` + NonOpProfit float64 `gorm:"type:decimal(20,4);comment:非营业利润"` + + // 资产负债表相关 + CurrentExint float64 `gorm:"type:decimal(20,4);comment:无息流动负债"` + NoncurrentExint float64 `gorm:"type:decimal(20,4);comment:无息非流动负债"` + Interestdebt float64 `gorm:"type:decimal(20,4);comment:带息债务"` + Netdebt float64 `gorm:"type:decimal(20,4);comment:净债务"` + TangibleAsset float64 `gorm:"type:decimal(20,4);comment:有形资产"` + WorkingCapital float64 `gorm:"type:decimal(20,4);comment:营运资金"` + NetworkingCapital float64 `gorm:"type:decimal(20,4);comment:营运流动资本"` + InvestCapital float64 `gorm:"type:decimal(20,4);comment:全部投入资本"` + RetainedEarnings float64 `gorm:"type:decimal(20,4);comment:留存收益"` + + // 偿债能力指标 + CurrentRatio float64 `gorm:"type:decimal(20,4);comment:流动比率"` + QuickRatio float64 `gorm:"type:decimal(20,4);comment:速动比率"` + CashRatio float64 `gorm:"type:decimal(20,4);comment:保守速动比率"` + DebtToAssets float64 `gorm:"type:decimal(20,4);comment:资产负债率"` + AssetsToEqt float64 `gorm:"type:decimal(20,4);comment:权益乘数"` + DpAssetsToEqt float64 `gorm:"type:decimal(20,4);comment:权益乘数(杜邦分析)"` + DebtToEqt float64 `gorm:"type:decimal(20,4);comment:产权比率"` + EqtToDebt float64 `gorm:"type:decimal(20,4);comment:归属于母公司的股东权益/负债合计"` + OcfToShortdebt float64 `gorm:"type:decimal(20,4);comment:经营活动产生的现金流量净额/流动负债"` + EbitToInterest float64 `gorm:"type:decimal(20,4);comment:已获利息倍数"` + + // 运营能力指标 + InvturnDays float64 `gorm:"type:decimal(20,4);comment:存货周转天数"` + ArturnDays float64 `gorm:"type:decimal(20,4);comment:应收账款周转天数"` + InvTurn float64 `gorm:"type:decimal(20,4);comment:存货周转率"` + ArTurn float64 `gorm:"type:decimal(20,4);comment:应收账款周转率"` + CaTurn float64 `gorm:"type:decimal(20,4);comment:流动资产周转率"` + FaTurn float64 `gorm:"type:decimal(20,4);comment:固定资产周转率"` + AssetsTurn float64 `gorm:"type:decimal(20,4);comment:总资产周转率"` + TurnDays float64 `gorm:"type:decimal(20,4);comment:营业周期"` + TotalFaTrun float64 `gorm:"type:decimal(20,4);comment:固定资产合计周转率"` + + // 盈利能力指标 + NetprofitMargin float64 `gorm:"type:decimal(20,4);comment:销售净利率"` + GrossprofitMargin float64 `gorm:"type:decimal(20,4);comment:销售毛利率"` + CogsOfSales float64 `gorm:"type:decimal(20,4);comment:销售成本率"` + ExpenseOfSales float64 `gorm:"type:decimal(20,4);comment:销售期间费用率"` + Roe float64 `gorm:"type:decimal(20,4);comment:净资产收益率"` + RoeWaa float64 `gorm:"type:decimal(20,4);comment:加权平均净资产收益率"` + RoeDt float64 `gorm:"type:decimal(20,4);comment:净资产收益率(扣除非经常损益)"` + Roa float64 `gorm:"type:decimal(20,4);comment:总资产报酬率"` + Npta float64 `gorm:"type:decimal(20,4);comment:总资产净利润"` + Roic float64 `gorm:"type:decimal(20,4);comment:投入资本回报率"` + RoaDp float64 `gorm:"type:decimal(20,4);comment:总资产净利率(杜邦分析)"` + + // 结构指标 + CaToAssets float64 `gorm:"type:decimal(20,4);comment:流动资产/总资产"` + NcaToAssets float64 `gorm:"type:decimal(20,4);comment:非流动资产/总资产"` + TbassetsToTotalassets float64 `gorm:"type:decimal(20,4);comment:有形资产/总资产"` + IntToTalcap float64 `gorm:"type:decimal(20,4);comment:带息债务/全部投入资本"` + EqtToTalcapital float64 `gorm:"type:decimal(20,4);comment:归属于母公司的股东权益/全部投入资本"` + CurrentdebtToDebt float64 `gorm:"type:decimal(20,4);comment:流动负债/负债合计"` + LongdebToDebt float64 `gorm:"type:decimal(20,4);comment:非流动负债/负债合计"` + TangibleassetToDebt float64 `gorm:"type:decimal(20,4);comment:有形资产/负债合计"` + + // 单季度指标 + QOpincome float64 `gorm:"type:decimal(20,4);comment:经营活动单季度净收益"` + QInvestincome float64 `gorm:"type:decimal(20,4);comment:价值变动单季度净收益"` + QDtprofit float64 `gorm:"type:decimal(20,4);comment:扣除非经常损益后的单季度净利润"` + QEps float64 `gorm:"type:decimal(20,4);comment:每股收益(单季度)"` + QNetprofitMargin float64 `gorm:"type:decimal(20,4);comment:销售净利率(单季度)"` + QGscaleprofitMargin float64 `gorm:"type:decimal(20,4);comment:销售毛利率(单季度)"` + QExpToSales float64 `gorm:"type:decimal(20,4);comment:销售期间费用率(单季度)"` + QRoe float64 `gorm:"type:decimal(20,4);comment:净资产收益率(单季度)"` + QDtRoe float64 `gorm:"type:decimal(20,4);comment:净资产单季度收益率(扣除非经常损益)"` + QNpta float64 `gorm:"type:decimal(20,4);comment:总资产净利润(单季度)"` + + // 同比增长率 + BasicEpsYoy float64 `gorm:"type:decimal(20,4);comment:基本每股收益同比增长率(%)"` + DtEpsYoy float64 `gorm:"type:decimal(20,4);comment:稀释每股收益同比增长率(%)"` + CfpsYoy float64 `gorm:"type:decimal(20,4);comment:每股经营活动产生的现金流量净额同比增长率(%)"` + OpYoy float64 `gorm:"type:decimal(20,4);comment:营业利润同比增长率(%)"` + EbtYoy float64 `gorm:"type:decimal(20,4);comment:利润总额同比增长率(%)"` + NetprofitYoy float64 `gorm:"type:decimal(20,4);comment:归属母公司股东的净利润同比增长率(%)"` + DtNetprofitYoy float64 `gorm:"type:decimal(20,4);comment:归属母公司股东的净利润-扣除非经常损益同比增长率(%)"` + OcfYoy float64 `gorm:"type:decimal(20,4);comment:经营活动产生的现金流量净额同比增长率(%)"` + RoeYoy float64 `gorm:"type:decimal(20,4);comment:净资产收益率(摊薄)同比增长率(%)"` + BpsYoy float64 `gorm:"type:decimal(20,4);comment:每股净资产相对年初增长率(%)"` + AssetsYoy float64 `gorm:"type:decimal(20,4);comment:资产总计相对年初增长率(%)"` + EqtYoy float64 `gorm:"type:decimal(20,4);comment:归属母公司的股东权益相对年初增长率(%)"` + TrYoy float64 `gorm:"type:decimal(20,4);comment:营业总收入同比增长率(%)"` + OrYoy float64 `gorm:"type:decimal(20,4);comment:营业收入同比增长率(%)"` + EquityYoy float64 `gorm:"type:decimal(20,4);comment:净资产同比增长率"` + + // 其他比率指标 + ProfitToGr float64 `gorm:"type:decimal(20,4);comment:净利润/营业总收入"` + SaleexpToGr float64 `gorm:"type:decimal(20,4);comment:销售费用/营业总收入"` + AdminexpOfGr float64 `gorm:"type:decimal(20,4);comment:管理费用/营业总收入"` + FinaexpOfGr float64 `gorm:"type:decimal(20,4);comment:财务费用/营业总收入"` + ImpaiTtm float64 `gorm:"type:decimal(20,4);comment:资产减值损失/营业总收入"` + GcOfGr float64 `gorm:"type:decimal(20,4);comment:营业总成本/营业总收入"` + OpOfGr float64 `gorm:"type:decimal(20,4);comment:营业利润/营业总收入"` + EbitOfGr float64 `gorm:"type:decimal(20,4);comment:息税前利润/营业总收入"` + OpincomeOfEbt float64 `gorm:"type:decimal(20,4);comment:经营活动净收益/利润总额"` + InvestincomeOfEbt float64 `gorm:"type:decimal(20,4);comment:价值变动净收益/利润总额"` + NOpProfitOfEbt float64 `gorm:"type:decimal(20,4);comment:营业外收支净额/利润总额"` + TaxToEbt float64 `gorm:"type:decimal(20,4);comment:所得税/利润总额"` + DtprofitToProfit float64 `gorm:"type:decimal(20,4);comment:扣除非经常损益后的净利润/净利润"` + SalescashToOr float64 `gorm:"type:decimal(20,4);comment:销售商品提供劳务收到的现金/营业收入"` + OcfToOr float64 `gorm:"type:decimal(20,4);comment:经营活动产生的现金流量净额/营业收入"` + OcfToOpincome float64 `gorm:"type:decimal(20,4);comment:经营活动产生的现金流量净额/经营活动净收益"` + CapitalizedToDa float64 `gorm:"type:decimal(20,4);comment:资本支出/折旧和摊销"` + OcfToDebt float64 `gorm:"type:decimal(20,4);comment:经营活动产生的现金流量净额/负债合计"` + OcfToInterestdebt float64 `gorm:"type:decimal(20,4);comment:经营活动产生的现金流量净额/带息债务"` + OcfToNetdebt float64 `gorm:"type:decimal(20,4);comment:经营活动产生的现金流量净额/净债务"` + LongdebtToWorkingcapital float64 `gorm:"type:decimal(20,4);comment:长期债务与营运资金比率"` + EbitdaToDebt float64 `gorm:"type:decimal(20,4);comment:息税折旧摊销前利润/负债合计"` + OpToEbt float64 `gorm:"type:decimal(20,4);comment:营业利润/利润总额"` + NopToEbt float64 `gorm:"type:decimal(20,4);comment:非营业利润/利润总额"` + OcfToProfit float64 `gorm:"type:decimal(20,4);comment:经营活动产生的现金流量净额/营业利润"` + CashToLiqdebt float64 `gorm:"type:decimal(20,4);comment:货币资金/流动负债"` + CashToLiqdebtWithinterest float64 `gorm:"type:decimal(20,4);comment:货币资金/带息流动负债"` + OpToLiqdebt float64 `gorm:"type:decimal(20,4);comment:营业利润/流动负债"` + OpToDebt float64 `gorm:"type:decimal(20,4);comment:营业利润/负债合计"` + ProfitToOp float64 `gorm:"type:decimal(20,4);comment:利润总额/营业收入"` + + // 年度化指标 + RoeYearly float64 `gorm:"type:decimal(20,4);comment:年化净资产收益率"` + Roa2Yearly float64 `gorm:"type:decimal(20,4);comment:年化总资产报酬率"` + RoaYearly float64 `gorm:"type:decimal(20,4);comment:年化总资产净利率"` + RoicYearly float64 `gorm:"type:decimal(20,4);comment:年化投入资本回报率"` + RoeAvg float64 `gorm:"type:decimal(20,4);comment:平均净资产收益率(增发条件)"` + + // 单季度增长比率 + QGrYoy float64 `gorm:"type:decimal(20,4);comment:营业总收入同比增长率(%)(单季度)"` + QGrQoq float64 `gorm:"type:decimal(20,4);comment:营业总收入环比增长率(%)(单季度)"` + QSalesYoy float64 `gorm:"type:decimal(20,4);comment:营业收入同比增长率(%)(单季度)"` + QSalesQoq float64 `gorm:"type:decimal(20,4);comment:营业收入环比增长率(%)(单季度)"` + QOpYoy float64 `gorm:"type:decimal(20,4);comment:营业利润同比增长率(%)(单季度)"` + QOpQoq float64 `gorm:"type:decimal(20,4);comment:营业利润环比增长率(%)(单季度)"` + QProfitYoy float64 `gorm:"type:decimal(20,4);comment:净利润同比增长率(%)(单季度)"` + QProfitQoq float64 `gorm:"type:decimal(20,4);comment:净利润环比增长率(%)(单季度)"` + QNetprofitYoy float64 `gorm:"type:decimal(20,4);comment:归属母公司股东的净利润同比增长率(%)(单季度)"` + QNetprofitQoq float64 `gorm:"type:decimal(20,4);comment:归属母公司股东的净利润环比增长率(%)(单季度)"` + + // 单季度比率指标 + QProfitToGr float64 `gorm:"type:decimal(20,4);comment:净利润/营业总收入(单季度)"` + QSaleexpToGr float64 `gorm:"type:decimal(20,4);comment:销售费用/营业总收入 (单季度)"` + QAdminexpToGr float64 `gorm:"type:decimal(20,4);comment:管理费用/营业总收入 (单季度)"` + QFinaexpToGr float64 `gorm:"type:decimal(20,4);comment:财务费用/营业总收入 (单季度)"` + QImpairToGrTtm float64 `gorm:"type:decimal(20,4);comment:资产减值损失/营业总收入(单季度)"` + QGcToGr float64 `gorm:"type:decimal(20,4);comment:营业总成本/营业总收入 (单季度)"` + QOpToGr float64 `gorm:"type:decimal(20,4);comment:营业利润/营业总收入(单季度)"` + QOpincomeToEbt float64 `gorm:"type:decimal(20,4);comment:经营活动净收益/利润总额(单季度)"` + QInvestincomeToEbt float64 `gorm:"type:decimal(20,4);comment:价值变动净收益/利润总额(单季度)"` + QDtprofitToProfit float64 `gorm:"type:decimal(20,4);comment:扣除非经常损益后的净利润/净利润(单季度)"` + QSalescashToOr float64 `gorm:"type:decimal(20,4);comment:销售商品提供劳务收到的现金/营业收入(单季度)"` + QOcfToSales float64 `gorm:"type:decimal(20,4);comment:经营活动产生的现金流量净额/营业收入(单季度)"` + QOcfToOr float64 `gorm:"type:decimal(20,4);comment:经营活动产生的现金流量净额/经营活动净收益(单季度)"` + + // 其他 + UpdateFlag string `gorm:"type:varchar(1);comment:更新标识"` +} + +func init() { + database.AppendMigrate(&StockFinaIndicator{}) +} + +// TableName 设置表名 +func (StockFinaIndicator) TableName() string { + return "fina_indicator" +} diff --git a/internal/models/stock_indicator.go b/internal/models/stock_indicator.go new file mode 100644 index 0000000..b236ffc --- /dev/null +++ b/internal/models/stock_indicator.go @@ -0,0 +1,37 @@ +package models + +import ( + "git.apinb.com/bsm-sdk/core/database" +) + +// StockIndicator 股票日线数据模型 +type StockIndicator struct { + ID uint `gorm:"primarykey;autoIncrement"` + TsCode string `gorm:"type:varchar(20);not null;index:si_ts_code;uniqueIndex:un_si_code_date;comment:股票代码" json:"ts_code"` + TradeDate int `gorm:"index:si_trade_date;uniqueIndex:un_si_code_date;comment:交易日期" json:"trade_date"` + Close float64 `gorm:"type:decimal(10,4);comment:当日收盘价"` + TurnoverRate float64 `gorm:"type:decimal(10,6);comment:换手率(%)"` + TurnoverRateF float64 `gorm:"type:decimal(10,6);comment:换手率(自由流通股)"` + VolumeRatio float64 `gorm:"type:decimal(10,4);comment:量比"` + Pe float64 `gorm:"type:decimal(10,4);comment:市盈率(总市值/净利润)"` + PeTtm float64 `gorm:"type:decimal(10,4);comment:市盈率(TTM)"` + Pb float64 `gorm:"type:decimal(10,4);comment:市净率"` + Ps float64 `gorm:"type:decimal(10,4);comment:市销率"` + PsTtm float64 `gorm:"type:decimal(10,4);comment:市销率(TTM)"` + DvRatio float64 `gorm:"type:decimal(10,6);comment:股息率(%)"` + DvTtm float64 `gorm:"type:decimal(10,6);comment:股息率(TTM)(%)"` + TotalShare float64 `gorm:"type:decimal(15,2);comment:总股本(万股)"` + FloatShare float64 `gorm:"type:decimal(15,2);comment:流通股本(万股)"` + FreeShare float64 `gorm:"type:decimal(15,2);comment:自由流通股本(万)"` + TotalMv float64 `gorm:"type:decimal(15,2);comment:总市值(万元)"` + CircMv float64 `gorm:"type:decimal(15,2);comment:流通市值(万元)"` +} + +func init() { + database.AppendMigrate(&StockIndicator{}) +} + +// TableName 设置表名 +func (StockIndicator) TableName() string { + return "stock_indicator" +} diff --git a/internal/routers/register.go b/internal/routers/register.go new file mode 100644 index 0000000..f987f09 --- /dev/null +++ b/internal/routers/register.go @@ -0,0 +1,30 @@ +package routers + +import ( + "fmt" + + "git.apinb.com/dataset/stock/internal/logic/hello" + "github.com/gin-gonic/gin" +) + +// 完成请求地址: ip:port/srvKey/v1/group/xxx +func Register(srvKey string, engine *gin.Engine) { + v1_key := fmt.Sprintf("/%s/%s", srvKey, "v1") + registerAnonymous(v1_key, engine) + registerRouters(v1_key, engine) +} + +// registerAnonymous 不需要auth的接口 +func registerAnonymous(v1_key string, engine *gin.Engine) { + // Anonymous router. + fmt.Println(v1_key) + anonymous := engine.Group(v1_key) + { + anonymous.GET("/ping", hello.Ping) + + } +} + +func registerRouters(v1_key string, engine *gin.Engine) { + +} diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100644 index 0000000..84dd778 --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +GOARCH=amd64 GOOS=linux go build -o ../builds/stock ./cmd/main/main.go + +nohup ./stock > /data/app/logs/stock.log 2>&1 & \ No newline at end of file