diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e6104e2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +*/.idea \ No newline at end of file diff --git a/README.md b/README.md index 0b61be9..ca258cd 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ # service-qgdzs +奇怪的知识-服务端 \ No newline at end of file diff --git a/app/app.go b/app/app.go new file mode 100644 index 0000000..4d15b58 --- /dev/null +++ b/app/app.go @@ -0,0 +1,58 @@ +package app + +import ( + "fmt" + "git.hlsq.asia/mmorpg/service-common/discover" + "git.hlsq.asia/mmorpg/service-common/log" + "git.hlsq.asia/mmorpg/service-qgdzs/config" + "github.com/judwhite/go-svc" +) + +type Program struct { + moduleList []Module // 模块列表 +} + +type Module interface { + init() error + start() error + stop() error +} + +func (p *Program) Init(_ svc.Environment) error { + p.moduleList = append(p.moduleList, &ModuleBase{}) + p.moduleList = append(p.moduleList, &ModuleDB{}) + p.moduleList = append(p.moduleList, &ModuleGrpcServer{}) + + for _, module := range p.moduleList { + if err := module.init(); err != nil { + return err + } + } + log.Infof(fmt.Sprintf("%v Init successful...", config.Get().App.Name)) + return nil +} + +func (p *Program) Start() error { + for _, module := range p.moduleList { + if err := module.start(); err != nil { + return err + } + } + discover.Listen() + + log.Infof(fmt.Sprintf("%v Start successful...", config.Get().App.Name)) + return nil +} + +func (p *Program) Stop() error { + discover.Close() + for i := len(p.moduleList) - 1; i >= 0; i-- { + module := p.moduleList[i] + if err := module.stop(); err != nil { + log.Errorf("module stop error: %v", err) + } + } + + log.Infof(fmt.Sprintf("%v Stop successful...", config.Get().App.Name)) + return nil +} diff --git a/app/base.go b/app/base.go new file mode 100644 index 0000000..320323e --- /dev/null +++ b/app/base.go @@ -0,0 +1,33 @@ +package app + +import ( + "git.hlsq.asia/mmorpg/service-common/log" + "git.hlsq.asia/mmorpg/service-common/utils" + "git.hlsq.asia/mmorpg/service-qgdzs/config" + "math/rand" +) + +// ModuleBase 基础模块,或者一些零散的模块 +type ModuleBase struct { +} + +func (p *ModuleBase) init() error { + // 配置 + if err := config.LoadConfig(); err != nil { + return err + } + cfg := config.Get() + // 日志 + log.Init(cfg.Log.Debug, cfg.Log.MaxSize, cfg.Log.MaxBackups, cfg.Log.MaxAge, cfg.Log.Level) + // 雪花 + utils.InitSnowflake(int64(rand.Intn(1000))) + return nil +} + +func (p *ModuleBase) start() error { + return nil +} + +func (p *ModuleBase) stop() error { + return nil +} diff --git a/app/db.go b/app/db.go new file mode 100644 index 0000000..456905d --- /dev/null +++ b/app/db.go @@ -0,0 +1,23 @@ +package app + +import ( + "git.hlsq.asia/mmorpg/service-common/db" + "git.hlsq.asia/mmorpg/service-qgdzs/config" +) + +// ModuleDB 数据库模块 +type ModuleDB struct { + dbModule *db.ModuleDB +} + +func (p *ModuleDB) init() error { + return p.dbModule.Init(config.Get().DB) +} + +func (p *ModuleDB) start() error { + return nil +} + +func (p *ModuleDB) stop() error { + return p.dbModule.Stop() +} diff --git a/app/grpc.go b/app/grpc.go new file mode 100644 index 0000000..b1f3774 --- /dev/null +++ b/app/grpc.go @@ -0,0 +1,27 @@ +package app + +import ( + "git.hlsq.asia/mmorpg/service-common/net/grpc/service" + "git.hlsq.asia/mmorpg/service-qgdzs/config" + "git.hlsq.asia/mmorpg/service-qgdzs/internal/grpc_server/server" +) + +// ModuleGrpcServer Grpc服务模块 +type ModuleGrpcServer struct { + server service.IService +} + +func (m *ModuleGrpcServer) init() error { + return nil +} + +func (m *ModuleGrpcServer) start() error { + m.server = server.NewServer(config.Get().Serve.Grpc.TTL) + m.server.Init(config.Get().Serve.Grpc.Address, config.Get().Serve.Grpc.Port) + return nil +} + +func (m *ModuleGrpcServer) stop() error { + m.server.Close() + return nil +} diff --git a/config/config.dev.yaml b/config/config.dev.yaml new file mode 100644 index 0000000..e9d9593 --- /dev/null +++ b/config/config.dev.yaml @@ -0,0 +1,31 @@ +app: + name: "qgdzs-dev" + +log: + debug: true + level: "debug" + maxSize: 10 + maxBackups: 100 + maxAge: 7 + +db: + etcd: + endpoints: [ "10.0.40.9:2379" ] + mysql: + qgdzs_db: + dsn: "root:gR9pV4tY7zR6qL3e@tcp(47.108.184.184:3306)/qgdzs_db?charset=utf8mb4&parseTime=True&loc=Local" + maxOpenConn: 50 + maxIdleConn: 20 + connMaxLifetimeSec: 600 + connMaxIdleTimeSec: 180 + logLevel: "warn" + redis: + addr: "47.108.184.184:6379" + password: "lQ7aM8oB6lK0iD5k" + db: 0 + +serve: + grpc: + address: "10.0.40.199" + port: 8603 + ttl: 20 diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..17e398b --- /dev/null +++ b/config/config.go @@ -0,0 +1,25 @@ +package config + +import "git.hlsq.asia/mmorpg/service-common/config" + +const path = "./config" + +type Config struct { + App *config.AppConfig `yaml:"app"` + Log *config.LogConfig `yaml:"log"` + DB *config.DBConfig `yaml:"db"` + Serve *config.ServeConfig `yaml:"serve"` +} + +var cfg *Config + +// LoadConfig 加载应用配置 +func LoadConfig() error { + c, err := config.LoadConfig(path, cfg) + cfg = c + return err +} + +func Get() *Config { + return cfg +} diff --git a/config/config.prod.yaml b/config/config.prod.yaml new file mode 100644 index 0000000..52f372d --- /dev/null +++ b/config/config.prod.yaml @@ -0,0 +1,31 @@ +app: + name: "qgdzs-prod" + +log: + debug: false + level: "debug" + maxSize: 10 + maxBackups: 100 + maxAge: 7 + +db: + etcd: + endpoints: [ "172.18.28.0:2379" ] + mysql: + qgdzs_db: + dsn: "root:gR9pV4tY7zR6qL3e@tcp(172.18.28.0:3306)/qgdzs_db?charset=utf8mb4&parseTime=True&loc=Local" + maxOpenConn: 50 + maxIdleConn: 20 + connMaxLifetimeSec: 600 + connMaxIdleTimeSec: 180 + logLevel: "warn" + redis: + addr: "172.18.28.0:6379" + password: "lQ7aM8oB6lK0iD5k" + db: 0 + +serve: + grpc: + address: "172.18.28.0" + port: 8603 + ttl: 20 diff --git a/deploy/Dockerfile b/deploy/Dockerfile new file mode 100644 index 0000000..2ff0437 --- /dev/null +++ b/deploy/Dockerfile @@ -0,0 +1,12 @@ +FROM alpine:3.23.2 + +RUN apk add --no-cache tzdata && \ + ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ + echo "Asia/Shanghai" > /etc/timezone + +COPY service-qgdzs /app/service-qgdzs +COPY config /app/config +RUN chmod 777 /app/service-qgdzs + +WORKDIR /app +CMD ["./service-qgdzs"] \ No newline at end of file diff --git a/deploy/Jenkinsfile b/deploy/Jenkinsfile new file mode 100644 index 0000000..c676dfa --- /dev/null +++ b/deploy/Jenkinsfile @@ -0,0 +1,133 @@ +pipeline { + agent none + + environment { + // 仓库 + REPO_URL = 'https://git.hlsq.asia/mmorpg/service-qgdzs.git' + REPO_CREDENTIALS_ID = '80805ba2-f4ac-4d84-aee6-d4cce5fc0a96' + + // Registry + REGISTRY_URL = 'registry.hlsq.asia' + REGISTRY_CREDENTIALS_ID = '0d79fc0b-a150-470b-bd0d-2639b2826031' + + // 部署目标服务器 + SERVER_HOST = 'www.hlsq.asia' + SERVER_USER = 'root' + SSH_CREDENTIALS_ID = '10e0830d-4d03-4879-9ee4-03a4c55513ad' + + // 基础信息 + APP_NAME = 'service-qgdzs' + GO_MOD_CACHE_DIR = '/home/pi/Desktop/docker/jenkins/caches/go-mod' + GO_BUILD_CACHE_DIR = '/home/pi/Desktop/docker/jenkins/caches/go-build' + } + + options { + skipDefaultCheckout true + } + + stages { + stage('Checkout') { + agent any + steps { + checkout([ + $class: 'GitSCM', + branches: [[name: '*/master']], + doGenerateSubmoduleConfigurations: false, + extensions: [], + userRemoteConfigs: [[ + credentialsId: env.REPO_CREDENTIALS_ID, + url: env.REPO_URL + ]] + ]) + // 立刻保存Git的Commit,避免并发问题 + script { + def shortCommit = sh(script: 'git rev-parse --short=8 HEAD', returnStdout: true).trim() + env.IMAGE_TAG = "${env.REGISTRY_URL}/${env.APP_NAME}:${shortCommit}" + echo "Checked out commit: ${env.IMAGE_TAG}" + } + } + } + + stage('Build Go Binary') { + agent { + docker { + image 'golang:1.23.1-alpine' + reuseNode true + args '-u root:root -v $GO_MOD_CACHE_DIR:/go/pkg/mod -v $GO_BUILD_CACHE_DIR:/root/.cache/go-build' + } + } + steps { + sh """ + export GOPROXY=https://goproxy.cn,direct + export CGO_ENABLED=0 + export GOOS=linux + export GOARCH=amd64 + + go build -o ${env.APP_NAME} . + """ + } + } + + stage('Push Docker Image') { + agent any + steps { + script { + withCredentials([usernamePassword( + credentialsId: env.REGISTRY_CREDENTIALS_ID, + usernameVariable: 'DOCKER_USER', + passwordVariable: 'DOCKER_PASS' + )]) { + sh """ + echo "$DOCKER_PASS" | docker login --username "$DOCKER_USER" --password-stdin ${env.REGISTRY_URL} + docker buildx build --platform linux/amd64 -t ${env.IMAGE_TAG} -f deploy/Dockerfile . --push + docker logout ${env.REGISTRY_URL} + """ + } + } + } + } + + stage('Deploy to Server') { + agent any + steps { + script { + withCredentials([ + usernamePassword( + credentialsId: env.REGISTRY_CREDENTIALS_ID, + usernameVariable: 'DOCKER_USER', + passwordVariable: 'DOCKER_PASS' + ) + ]) { + sshagent (credentials: [env.SSH_CREDENTIALS_ID]) { + sh """ + ssh -o StrictHostKeyChecking=no ${env.SERVER_USER}@${env.SERVER_HOST} << EOF + set -e + echo '${DOCKER_PASS}' | docker login --username '${DOCKER_USER}' --password-stdin ${env.REGISTRY_URL} + docker pull ${env.IMAGE_TAG} + docker stop ${env.APP_NAME} 2>/dev/null || true + docker rm ${env.APP_NAME} 2>/dev/null || true + docker run -d \\ + --name ${env.APP_NAME} \\ + --restart unless-stopped \\ + -p 8603:8603 \\ + --env XH_G_ENV=prod \\ + -v /root/service/logs/qgdzs_log/:/app/logs \\ + ${env.IMAGE_TAG} + docker logout ${env.REGISTRY_URL} + """ + } + } + } + } + } + } + + post { + success { + echo '✅ 构建、推送镜像与部署成功!' + } + failure { + echo '❌ 构建失败,请检查日志。' + } + } +} \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..b9c9d2b --- /dev/null +++ b/go.mod @@ -0,0 +1,89 @@ +module git.hlsq.asia/mmorpg/service-qgdzs + +go 1.23.1 + +require ( + git.hlsq.asia/mmorpg/service-common v0.0.0-20260112082258-b1e7d33940d7 + github.com/judwhite/go-svc v1.2.1 + google.golang.org/grpc v1.71.1 + gorm.io/gen v0.3.27 + gorm.io/gorm v1.31.1 + gorm.io/plugin/dbresolver v1.6.2 +) + +require ( + filippo.io/edwards25519 v1.1.0 // indirect + github.com/bwmarrin/snowflake v0.3.0 // indirect + github.com/bytedance/sonic v1.14.0 // indirect + github.com/bytedance/sonic/loader v0.3.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.5.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.8 // indirect + github.com/gin-contrib/sse v1.1.0 // indirect + github.com/gin-gonic/gin v1.11.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.27.0 // indirect + github.com/go-sql-driver/mysql v1.8.1 // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/goccy/go-yaml v1.18.0 // 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.26.3 // 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-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/natefinch/lumberjack v2.0.0+incompatible // indirect + github.com/panjf2000/gnet/v2 v2.9.7 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/quic-go/qpack v0.5.1 // indirect + github.com/quic-go/quic-go v0.54.0 // indirect + github.com/redis/go-redis/v9 v9.10.0 // indirect + github.com/sagikazarmark/locafero v0.11.0 // indirect + github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect + github.com/spf13/afero v1.15.0 // indirect + github.com/spf13/cast v1.10.0 // indirect + github.com/spf13/pflag v1.0.10 // indirect + github.com/spf13/viper v1.21.0 // indirect + github.com/stretchr/testify v1.11.1 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.3.0 // indirect + go.etcd.io/etcd/api/v3 v3.6.1 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.6.1 // indirect + go.etcd.io/etcd/client/v3 v3.6.1 // indirect + go.uber.org/mock v0.5.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/arch v0.20.0 // indirect + golang.org/x/crypto v0.40.0 // indirect + golang.org/x/mod v0.26.0 // indirect + golang.org/x/net v0.42.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/text v0.28.0 // indirect + golang.org/x/tools v0.35.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect + google.golang.org/protobuf v1.36.9 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + gorm.io/datatypes v1.2.4 // indirect + gorm.io/driver/mysql v1.6.0 // indirect + gorm.io/hints v1.1.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..4d8b631 --- /dev/null +++ b/go.sum @@ -0,0 +1,271 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +git.hlsq.asia/mmorpg/service-common v0.0.0-20260112082258-b1e7d33940d7 h1:C3quCA54dyFgmlCVgJXx+0rNqa+JZgGggdotbvHAsnA= +git.hlsq.asia/mmorpg/service-common v0.0.0-20260112082258-b1e7d33940d7/go.mod h1:xv6m1I2jUA6mudKVznygpnzMoshBQarthHD1QnkW4qc= +github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= +github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +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/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0= +github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE= +github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ= +github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA= +github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= +github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= +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.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +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/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= +github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= +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.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/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.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4= +github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= +github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +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-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= +github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= +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.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= +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-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= +github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= +github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/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.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +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/judwhite/go-svc v1.2.1 h1:a7fsJzYUa33sfDJRF2N/WXhA+LonCEEY8BJb1tuS5tA= +github.com/judwhite/go-svc v1.2.1/go.mod h1:mo/P2JNX8C07ywpP9YtO2gnBgnUiFTHqtsZekJrUuTk= +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/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE= +github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/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/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM= +github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= +github.com/panjf2000/gnet/v2 v2.9.7 h1:6zW7Jl3oAfXwSuh1PxHLndoL2MQRWx0AJR6aaQjxUgA= +github.com/panjf2000/gnet/v2 v2.9.7/go.mod h1:WQTxDWYuQ/hz3eccH0FN32IVuvZ19HewEWx0l62fx7E= +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.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= +github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= +github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg= +github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY= +github.com/redis/go-redis/v9 v9.10.0 h1:FxwK3eV8p/CQa0Ch276C7u2d0eNC9kCmAYQ7mCXCzVs= +github.com/redis/go-redis/v9 v9.10.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= +github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= +github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= +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/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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +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/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +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.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= +github.com/ugorji/go/codec v1.3.0/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.1 h1:yJ9WlDih9HT457QPuHt/TH/XtsdN2tubyxyQHSHPsEo= +go.etcd.io/etcd/api/v3 v3.6.1/go.mod h1:lnfuqoGsXMlZdTJlact3IB56o3bWp1DIlXPIGKRArto= +go.etcd.io/etcd/client/pkg/v3 v3.6.1 h1:CxDVv8ggphmamrXM4Of8aCC8QHzDM4tGcVr9p2BSoGk= +go.etcd.io/etcd/client/pkg/v3 v3.6.1/go.mod h1:aTkCp+6ixcVTZmrJGa7/Mc5nMNs59PEgBbq+HCmWyMc= +go.etcd.io/etcd/client/v3 v3.6.1 h1:KelkcizJGsskUXlsxjVrSmINvMMga0VWwFF0tSPGEP0= +go.etcd.io/etcd/client/v3 v3.6.1/go.mod h1:fCbPUdjWNLfx1A6ATo9syUmFVxqHH9bCnPLBZmnLmMY= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= +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.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= +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.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c= +golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= +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.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= +golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +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/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= +golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= +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.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +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.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +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.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +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.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +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/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= +golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= +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= +google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950= +google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= +google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI= +google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= +google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= +google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +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/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +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/datatypes v1.2.4 h1:uZmGAcK/QZ0uyfCuVg0VQY1ZmV9h1fuG0tMwKByO1z4= +gorm.io/datatypes v1.2.4/go.mod h1:f4BsLcFAX67szSv8svwLRjklArSHAvHLeE3pXAS5DZI= +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.5.0 h1:u2FXTy14l45qc3UeCJ7QaAXZmZfDDv0YrthvmRq1l0U= +gorm.io/driver/postgres v1.5.0/go.mod h1:FUZXzO+5Uqg5zzwzv4KK49R8lvGIyscBOqYrtI1Ce9A= +gorm.io/driver/sqlite v1.1.6/go.mod h1:W8LmC/6UvVbHKah0+QOC7Ja66EaZXHwUTjgXY8YNWX8= +gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ= +gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8= +gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0= +gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHDnJLig= +gorm.io/gen v0.3.27 h1:ziocAFLpE7e0g4Rum69pGfB9S6DweTxK8gAun7cU8as= +gorm.io/gen v0.3.27/go.mod h1:9zquz2xD1f3Eb/eHq4oLn2z6vDVvQlCY5S3uMBLv4EA= +gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= +gorm.io/gorm v1.22.2/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= +gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg= +gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= +gorm.io/hints v1.1.0 h1:Lp4z3rxREufSdxn4qmkK3TLDltrM10FLTHiuqwDPvXw= +gorm.io/hints v1.1.0/go.mod h1:lKQ0JjySsPBj3uslFzY3JhYDtqEwzm+G1hv8rWujB6Y= +gorm.io/plugin/dbresolver v1.6.2 h1:F4b85TenghUeITqe3+epPSUtHH7RIk3fXr5l83DF8Pc= +gorm.io/plugin/dbresolver v1.6.2/go.mod h1:tctw63jdrOezFR9HmrKnPkmig3m5Edem9fdxk9bQSzM= diff --git a/internal/ai/ai_client.go b/internal/ai/ai_client.go new file mode 100644 index 0000000..35153c2 --- /dev/null +++ b/internal/ai/ai_client.go @@ -0,0 +1,55 @@ +package ai + +import ( + "git.hlsq.asia/mmorpg/service-common/log" +) + +type Client struct { + enableSearch bool // 是否开启搜索 + searchStrategy string // 搜索策略(开了搜索才生效) + temperature float32 // 采样温度 +} + +func NewAIClient(enableSearch bool, searchStrategy string, temperature float32) *Client { + return &Client{ + enableSearch: enableSearch, + searchStrategy: searchStrategy, + temperature: temperature, + } +} + +func (c *Client) RequestAI(prompt []string, cb func(content string, i int) error) error { + inToken, outToken := int32(0), int32(0) + messages := make([]Message, 0) + for i, step := range prompt { + messages = append(messages, Message{ + Role: "user", + Content: step, + }) + r, err := request("deepseek-v3.2", Parameters{ + EnableSearch: c.enableSearch, + SearchOptions: SearchOptions{ + SearchStrategy: c.searchStrategy, + }, + ResultFormat: "message", + Temperature: c.temperature, + }, messages) + if err != nil { + return err + } + inToken += r.Usage.InputTokens + outToken += r.Usage.OutputTokens + if len(r.Output.Choices) == 0 { + log.Infof("AI response err, r: %#+v", r) + return err + } else { + messages = append(messages, r.Output.Choices[0].Message) + } + log.Infof("AI 回答 %v: %v", i, r.Output.Choices[0].Message.Content) + if err = cb(r.Output.Choices[0].Message.Content, i); err != nil { + return err + } + } + log.Infof("AI finished, use token: in-%v out-%v", inToken, outToken) + return nil +} diff --git a/internal/ai/ai_request.go b/internal/ai/ai_request.go new file mode 100644 index 0000000..8c37496 --- /dev/null +++ b/internal/ai/ai_request.go @@ -0,0 +1,82 @@ +package ai + +import ( + "bytes" + "encoding/json" + "io" + "net/http" +) + +type Message struct { + Role string `json:"role"` + Content string `json:"content"` +} + +type Input struct { + Messages []Message `json:"messages"` +} + +type Parameters struct { + EnableSearch bool `json:"enable_search,omitempty"` + SearchOptions SearchOptions `json:"search_options,omitempty"` + ResultFormat string `json:"result_format,omitempty"` + Temperature float32 `json:"temperature,omitempty"` +} + +type SearchOptions struct { + SearchStrategy string `json:"search_strategy"` +} + +type RequestBody struct { + Model string `json:"model"` + Input Input `json:"input"` + Parameters Parameters `json:"parameters"` +} + +type ResponseBody struct { + Output struct { + Choices []struct { + Message Message `json:"message"` + FinishReason string `json:"finish_reason"` + } `json:"choices"` + } `json:"output"` + Usage struct { + InputTokens int32 `json:"input_tokens"` + OutputTokens int32 `json:"output_tokens"` + TotalTokens int32 `json:"total_tokens"` + } `json:"usage"` +} + +func request(model string, parameters Parameters, messages []Message) (*ResponseBody, error) { + requestBody := RequestBody{ + Model: model, + Input: Input{ + Messages: messages, + }, + Parameters: parameters, + } + jsonData, _ := json.Marshal(requestBody) + + req, err := http.NewRequest("POST", "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation", bytes.NewBuffer(jsonData)) + if err != nil { + return nil, err + } + req.Header.Set("Authorization", "Bearer "+"sk-4daf94ad2fa94288b198d2a1d8924cef") + req.Header.Set("Content-Type", "application/json") + + resp, err := (&http.Client{}).Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + var res ResponseBody + if err = json.Unmarshal(body, &res); err != nil { + return nil, err + } + return &res, nil +} diff --git a/internal/dao/model/questions.gen.go b/internal/dao/model/questions.gen.go new file mode 100644 index 0000000..d30d358 --- /dev/null +++ b/internal/dao/model/questions.gen.go @@ -0,0 +1,42 @@ +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. + +package model + +import ( + "time" + + "git.hlsq.asia/mmorpg/service-common/utils" + "gorm.io/gorm" +) + +const TableNameQuestion = "questions" + +// Question mapped from table +type Question struct { + ID uint64 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"` + Sn string `gorm:"column:sn;not null;comment:业务唯一编号" json:"sn"` // 业务唯一编号 + Question string `gorm:"column:question;not null;comment:题干" json:"question"` // 题干 + Options string `gorm:"column:options;not null;comment:选项" json:"options"` // 选项 + Answer string `gorm:"column:answer;not null;comment:答案" json:"answer"` // 答案 + Explanation string `gorm:"column:explanation;not null;comment:解析" json:"explanation"` // 解析 + Difficulty int32 `gorm:"column:difficulty;not null;comment:难度分 0 - 100" json:"difficulty"` // 难度分 0 - 100 + Category string `gorm:"column:category;not null;comment:分类" json:"category"` // 分类 + CreatedAt time.Time `gorm:"column:created_at;not null" json:"created_at"` + UpdatedAt time.Time `gorm:"column:updated_at;not null" json:"updated_at"` + DeletedAt gorm.DeletedAt `gorm:"column:deleted_at" json:"deleted_at"` +} + +// TableName Question's table name +func (*Question) TableName() string { + return TableNameQuestion +} + +// Auto sn +func (m *Question) BeforeCreate(_ *gorm.DB) error { + if m.Sn == "" { + m.Sn = utils.SnowflakeInstance().Generate().String() + } + return nil +} diff --git a/internal/dao/query/gen.go b/internal/dao/query/gen.go new file mode 100644 index 0000000..1c33cf3 --- /dev/null +++ b/internal/dao/query/gen.go @@ -0,0 +1,93 @@ +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. + +package query + +import ( + "context" + "database/sql" + + "gorm.io/gorm" + + "gorm.io/gen" + + "gorm.io/plugin/dbresolver" +) + +func Use(db *gorm.DB, opts ...gen.DOOption) *Query { + return &Query{ + db: db, + Question: newQuestion(db, opts...), + } +} + +type Query struct { + db *gorm.DB + + Question question +} + +func (q *Query) Available() bool { return q.db != nil } + +func (q *Query) clone(db *gorm.DB) *Query { + return &Query{ + db: db, + Question: q.Question.clone(db), + } +} + +func (q *Query) ReadDB() *Query { + return q.ReplaceDB(q.db.Clauses(dbresolver.Read)) +} + +func (q *Query) WriteDB() *Query { + return q.ReplaceDB(q.db.Clauses(dbresolver.Write)) +} + +func (q *Query) ReplaceDB(db *gorm.DB) *Query { + return &Query{ + db: db, + Question: q.Question.replaceDB(db), + } +} + +type queryCtx struct { + Question *questionDo +} + +func (q *Query) WithContext(ctx context.Context) *queryCtx { + return &queryCtx{ + Question: q.Question.WithContext(ctx), + } +} + +func (q *Query) Transaction(fc func(tx *Query) error, opts ...*sql.TxOptions) error { + return q.db.Transaction(func(tx *gorm.DB) error { return fc(q.clone(tx)) }, opts...) +} + +func (q *Query) Begin(opts ...*sql.TxOptions) *QueryTx { + tx := q.db.Begin(opts...) + return &QueryTx{Query: q.clone(tx), Error: tx.Error} +} + +type QueryTx struct { + *Query + Error error +} + +func (q *QueryTx) Commit() error { + return q.db.Commit().Error +} + +func (q *QueryTx) Rollback() error { + return q.db.Rollback().Error +} + +func (q *QueryTx) SavePoint(name string) error { + return q.db.SavePoint(name).Error +} + +func (q *QueryTx) RollbackTo(name string) error { + return q.db.RollbackTo(name).Error +} diff --git a/internal/dao/query/questions.gen.go b/internal/dao/query/questions.gen.go new file mode 100644 index 0000000..efc265e --- /dev/null +++ b/internal/dao/query/questions.gen.go @@ -0,0 +1,367 @@ +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. + +package query + +import ( + "context" + + "gorm.io/gorm" + "gorm.io/gorm/clause" + "gorm.io/gorm/schema" + + "gorm.io/gen" + "gorm.io/gen/field" + + "gorm.io/plugin/dbresolver" + + "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/model" +) + +func newQuestion(db *gorm.DB, opts ...gen.DOOption) question { + _question := question{} + + _question.questionDo.UseDB(db, opts...) + _question.questionDo.UseModel(&model.Question{}) + + tableName := _question.questionDo.TableName() + _question.ALL = field.NewAsterisk(tableName) + _question.ID = field.NewUint64(tableName, "id") + _question.Sn = field.NewString(tableName, "sn") + _question.Question = field.NewString(tableName, "question") + _question.Options = field.NewString(tableName, "options") + _question.Answer = field.NewString(tableName, "answer") + _question.Explanation = field.NewString(tableName, "explanation") + _question.Difficulty = field.NewInt32(tableName, "difficulty") + _question.Category = field.NewString(tableName, "category") + _question.CreatedAt = field.NewTime(tableName, "created_at") + _question.UpdatedAt = field.NewTime(tableName, "updated_at") + _question.DeletedAt = field.NewField(tableName, "deleted_at") + + _question.fillFieldMap() + + return _question +} + +type question struct { + questionDo questionDo + + ALL field.Asterisk + ID field.Uint64 + Sn field.String // 业务唯一编号 + Question field.String // 题干 + Options field.String // 选项 + Answer field.String // 答案 + Explanation field.String // 解析 + Difficulty field.Int32 // 难度分 0 - 100 + Category field.String // 分类 + CreatedAt field.Time + UpdatedAt field.Time + DeletedAt field.Field + + fieldMap map[string]field.Expr +} + +func (q question) Table(newTableName string) *question { + q.questionDo.UseTable(newTableName) + return q.updateTableName(newTableName) +} + +func (q question) As(alias string) *question { + q.questionDo.DO = *(q.questionDo.As(alias).(*gen.DO)) + return q.updateTableName(alias) +} + +func (q *question) updateTableName(table string) *question { + q.ALL = field.NewAsterisk(table) + q.ID = field.NewUint64(table, "id") + q.Sn = field.NewString(table, "sn") + q.Question = field.NewString(table, "question") + q.Options = field.NewString(table, "options") + q.Answer = field.NewString(table, "answer") + q.Explanation = field.NewString(table, "explanation") + q.Difficulty = field.NewInt32(table, "difficulty") + q.Category = field.NewString(table, "category") + q.CreatedAt = field.NewTime(table, "created_at") + q.UpdatedAt = field.NewTime(table, "updated_at") + q.DeletedAt = field.NewField(table, "deleted_at") + + q.fillFieldMap() + + return q +} + +func (q *question) WithContext(ctx context.Context) *questionDo { return q.questionDo.WithContext(ctx) } + +func (q question) TableName() string { return q.questionDo.TableName() } + +func (q question) Alias() string { return q.questionDo.Alias() } + +func (q question) Columns(cols ...field.Expr) gen.Columns { return q.questionDo.Columns(cols...) } + +func (q *question) GetFieldByName(fieldName string) (field.OrderExpr, bool) { + _f, ok := q.fieldMap[fieldName] + if !ok || _f == nil { + return nil, false + } + _oe, ok := _f.(field.OrderExpr) + return _oe, ok +} + +func (q *question) fillFieldMap() { + q.fieldMap = make(map[string]field.Expr, 11) + q.fieldMap["id"] = q.ID + q.fieldMap["sn"] = q.Sn + q.fieldMap["question"] = q.Question + q.fieldMap["options"] = q.Options + q.fieldMap["answer"] = q.Answer + q.fieldMap["explanation"] = q.Explanation + q.fieldMap["difficulty"] = q.Difficulty + q.fieldMap["category"] = q.Category + q.fieldMap["created_at"] = q.CreatedAt + q.fieldMap["updated_at"] = q.UpdatedAt + q.fieldMap["deleted_at"] = q.DeletedAt +} + +func (q question) clone(db *gorm.DB) question { + q.questionDo.ReplaceConnPool(db.Statement.ConnPool) + return q +} + +func (q question) replaceDB(db *gorm.DB) question { + q.questionDo.ReplaceDB(db) + return q +} + +type questionDo struct{ gen.DO } + +func (q questionDo) Debug() *questionDo { + return q.withDO(q.DO.Debug()) +} + +func (q questionDo) WithContext(ctx context.Context) *questionDo { + return q.withDO(q.DO.WithContext(ctx)) +} + +func (q questionDo) ReadDB() *questionDo { + return q.Clauses(dbresolver.Read) +} + +func (q questionDo) WriteDB() *questionDo { + return q.Clauses(dbresolver.Write) +} + +func (q questionDo) Session(config *gorm.Session) *questionDo { + return q.withDO(q.DO.Session(config)) +} + +func (q questionDo) Clauses(conds ...clause.Expression) *questionDo { + return q.withDO(q.DO.Clauses(conds...)) +} + +func (q questionDo) Returning(value interface{}, columns ...string) *questionDo { + return q.withDO(q.DO.Returning(value, columns...)) +} + +func (q questionDo) Not(conds ...gen.Condition) *questionDo { + return q.withDO(q.DO.Not(conds...)) +} + +func (q questionDo) Or(conds ...gen.Condition) *questionDo { + return q.withDO(q.DO.Or(conds...)) +} + +func (q questionDo) Select(conds ...field.Expr) *questionDo { + return q.withDO(q.DO.Select(conds...)) +} + +func (q questionDo) Where(conds ...gen.Condition) *questionDo { + return q.withDO(q.DO.Where(conds...)) +} + +func (q questionDo) Order(conds ...field.Expr) *questionDo { + return q.withDO(q.DO.Order(conds...)) +} + +func (q questionDo) Distinct(cols ...field.Expr) *questionDo { + return q.withDO(q.DO.Distinct(cols...)) +} + +func (q questionDo) Omit(cols ...field.Expr) *questionDo { + return q.withDO(q.DO.Omit(cols...)) +} + +func (q questionDo) Join(table schema.Tabler, on ...field.Expr) *questionDo { + return q.withDO(q.DO.Join(table, on...)) +} + +func (q questionDo) LeftJoin(table schema.Tabler, on ...field.Expr) *questionDo { + return q.withDO(q.DO.LeftJoin(table, on...)) +} + +func (q questionDo) RightJoin(table schema.Tabler, on ...field.Expr) *questionDo { + return q.withDO(q.DO.RightJoin(table, on...)) +} + +func (q questionDo) Group(cols ...field.Expr) *questionDo { + return q.withDO(q.DO.Group(cols...)) +} + +func (q questionDo) Having(conds ...gen.Condition) *questionDo { + return q.withDO(q.DO.Having(conds...)) +} + +func (q questionDo) Limit(limit int) *questionDo { + return q.withDO(q.DO.Limit(limit)) +} + +func (q questionDo) Offset(offset int) *questionDo { + return q.withDO(q.DO.Offset(offset)) +} + +func (q questionDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *questionDo { + return q.withDO(q.DO.Scopes(funcs...)) +} + +func (q questionDo) Unscoped() *questionDo { + return q.withDO(q.DO.Unscoped()) +} + +func (q questionDo) Create(values ...*model.Question) error { + if len(values) == 0 { + return nil + } + return q.DO.Create(values) +} + +func (q questionDo) CreateInBatches(values []*model.Question, batchSize int) error { + return q.DO.CreateInBatches(values, batchSize) +} + +// Save : !!! underlying implementation is different with GORM +// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values) +func (q questionDo) Save(values ...*model.Question) error { + if len(values) == 0 { + return nil + } + return q.DO.Save(values) +} + +func (q questionDo) First() (*model.Question, error) { + if result, err := q.DO.First(); err != nil { + return nil, err + } else { + return result.(*model.Question), nil + } +} + +func (q questionDo) Take() (*model.Question, error) { + if result, err := q.DO.Take(); err != nil { + return nil, err + } else { + return result.(*model.Question), nil + } +} + +func (q questionDo) Last() (*model.Question, error) { + if result, err := q.DO.Last(); err != nil { + return nil, err + } else { + return result.(*model.Question), nil + } +} + +func (q questionDo) Find() ([]*model.Question, error) { + result, err := q.DO.Find() + return result.([]*model.Question), err +} + +func (q questionDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Question, err error) { + buf := make([]*model.Question, 0, batchSize) + err = q.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error { + defer func() { results = append(results, buf...) }() + return fc(tx, batch) + }) + return results, err +} + +func (q questionDo) FindInBatches(result *[]*model.Question, batchSize int, fc func(tx gen.Dao, batch int) error) error { + return q.DO.FindInBatches(result, batchSize, fc) +} + +func (q questionDo) Attrs(attrs ...field.AssignExpr) *questionDo { + return q.withDO(q.DO.Attrs(attrs...)) +} + +func (q questionDo) Assign(attrs ...field.AssignExpr) *questionDo { + return q.withDO(q.DO.Assign(attrs...)) +} + +func (q questionDo) Joins(fields ...field.RelationField) *questionDo { + for _, _f := range fields { + q = *q.withDO(q.DO.Joins(_f)) + } + return &q +} + +func (q questionDo) Preload(fields ...field.RelationField) *questionDo { + for _, _f := range fields { + q = *q.withDO(q.DO.Preload(_f)) + } + return &q +} + +func (q questionDo) FirstOrInit() (*model.Question, error) { + if result, err := q.DO.FirstOrInit(); err != nil { + return nil, err + } else { + return result.(*model.Question), nil + } +} + +func (q questionDo) FirstOrCreate() (*model.Question, error) { + if result, err := q.DO.FirstOrCreate(); err != nil { + return nil, err + } else { + return result.(*model.Question), nil + } +} + +func (q questionDo) FindByPage(offset int, limit int) (result []*model.Question, count int64, err error) { + result, err = q.Offset(offset).Limit(limit).Find() + if err != nil { + return + } + + if size := len(result); 0 < limit && 0 < size && size < limit { + count = int64(size + offset) + return + } + + count, err = q.Offset(-1).Limit(-1).Count() + return +} + +func (q questionDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) { + count, err = q.Count() + if err != nil { + return + } + + err = q.Offset(offset).Limit(limit).Scan(result) + return +} + +func (q questionDo) Scan(result interface{}) (err error) { + return q.DO.Scan(result) +} + +func (q questionDo) Delete(models ...*model.Question) (result gen.ResultInfo, err error) { + return q.DO.Delete(models) +} + +func (q *questionDo) withDO(do gen.Dao) *questionDo { + q.DO = *do.(*gen.DO) + return q +} diff --git a/internal/dao/repository/define.go b/internal/dao/repository/define.go new file mode 100644 index 0000000..b330a69 --- /dev/null +++ b/internal/dao/repository/define.go @@ -0,0 +1,16 @@ +package repository + +import ( + "fmt" + "git.hlsq.asia/mmorpg/service-common/db/mysql" +) + +var dbName mysql.DBName = "qgdzs_db" + +var ( + cacheBySn = "c:%v:s:%v" +) + +func keyCacheBySn(sn string, tableName string) string { + return fmt.Sprintf(cacheBySn, tableName, sn) +} diff --git a/internal/dao/repository/questions.go b/internal/dao/repository/questions.go new file mode 100644 index 0000000..30c3c6f --- /dev/null +++ b/internal/dao/repository/questions.go @@ -0,0 +1,83 @@ +package repository + +import ( + "context" + "git.hlsq.asia/mmorpg/service-common/db/mysql" + "git.hlsq.asia/mmorpg/service-common/db/redis" + "git.hlsq.asia/mmorpg/service-common/utils" + "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/model" + "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/query" + "gorm.io/gorm" + "time" +) + +type QuestionDao struct { + ctx context.Context + query *query.Query + cache *redis.CacheClient +} + +func NewQuestionDao(ctx context.Context, cache ...*redis.CacheClient) *QuestionDao { + dao := &QuestionDao{ + ctx: ctx, + query: query.Use(mysql.GetDB(dbName)), + } + if len(cache) > 0 { + dao.cache = cache[0] + } + return dao +} + +func (d *QuestionDao) Create(question *model.Question) error { + err := d.query.Question.WithContext(d.ctx). + Create(question) + return err +} + +func (d *QuestionDao) FindByRandom() (*model.Question, error) { + count, err := d.query.Question.WithContext(d.ctx).Count() + if err != nil { + return nil, err + } + if count == 0 { + return nil, gorm.ErrRecordNotFound + } + first, err := d.query.Question.WithContext(d.ctx). + Offset(utils.RandInt(0, int(count-1))). + First() + if err != nil { + return nil, err + } + return first, nil +} + +func (d *QuestionDao) FindBySn(sn string) (*model.Question, error) { + if d.cache != nil { + var question model.Question + if ok := d.cache.Get(d.ctx, keyCacheBySn(sn, question.TableName()), &question); ok { + return &question, nil + } + } + first, err := d.query.Question.WithContext(d.ctx). + Where(d.query.Question.Sn.Eq(sn)). + First() + if err != nil { + return nil, err + } + if d.cache != nil { + d.cache.Set(d.ctx, keyCacheBySn(sn, first.TableName()), first, 5*time.Minute) + } + return first, nil +} + +func (d *QuestionDao) FindCategory() ([]string, error) { + var categories []string + err := d.query.Question.WithContext(d.ctx). + Select(d.query.Question.Category). + Distinct(). + Scan(&categories) + if err != nil { + return nil, err + } + return categories, nil +} diff --git a/internal/grpc_server/server/server_init.go b/internal/grpc_server/server/server_init.go new file mode 100644 index 0000000..10d33ca --- /dev/null +++ b/internal/grpc_server/server/server_init.go @@ -0,0 +1,32 @@ +package server + +import ( + "git.hlsq.asia/mmorpg/service-common/discover/common" + "git.hlsq.asia/mmorpg/service-common/net/grpc/service" + "git.hlsq.asia/mmorpg/service-common/proto/rs/grpc_pb" + "google.golang.org/grpc" +) + +type Server struct { + grpc_pb.UnimplementedQgdzsServer + service.Base +} + +func NewServer(ttl int64) *Server { + s := &Server{ + Base: service.Base{ + Target: common.KeyDiscoverQgdzs, + EtcdTTL: ttl, + }, + } + s.Base.OnInit = s.OnInit + s.Base.OnClose = s.OnClose + return s +} + +func (s *Server) OnInit(serve *grpc.Server) { + grpc_pb.RegisterQgdzsServer(serve, s) +} + +func (s *Server) OnClose() { +} diff --git a/internal/grpc_server/server/server_question.go b/internal/grpc_server/server/server_question.go new file mode 100644 index 0000000..b2b9b51 --- /dev/null +++ b/internal/grpc_server/server/server_question.go @@ -0,0 +1,156 @@ +package server + +import ( + "context" + "encoding/json" + "fmt" + "git.hlsq.asia/mmorpg/service-common/db/redis" + "git.hlsq.asia/mmorpg/service-common/log" + "git.hlsq.asia/mmorpg/service-common/proto/rs/grpc_pb" + "git.hlsq.asia/mmorpg/service-qgdzs/internal/ai" + "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/model" + "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/repository" + "time" +) + +//var prompt = []string{` +//你是一个微信小游戏的内容策划,请生成 %v 道“每日趣味题”,要求: +//- 避免敏感、争议或超纲内容 +//- 每次从 [动物冷知识, 科学趣理, 语言陷阱, 历史轶事, 地理奇观, 数学谜题, 艺术人文, 综合杂学] 中**随机选一个类型** +//- 确保选项有迷惑性,解析有趣 +//- 绝对要避免重复,现在的时间是:%v +//- 输出格式为 JSON 数组,不要任何解释、不要Markdown、不要任何其他字符: +// [{ +// "question": "题目文本", // 简洁,30字以内 +// "options": ["A. 选项1", "B. 选项2", "C. 选项3", "D. 选项4"], // 提供4个选项(A/B/C/D),其中仅1个正确 +// "answer": "C", // 答案 +// "explanation": "解析文本", // 200字以内,尽量幽默有趣 +// "category": "分类", // 只能从上面列出的类型中选择 +// "difficulty": 100, // 难度分 0 - 100 +// }] +//`} + +var prompt = []string{` +你是一个微信小游戏的内容策划,我需要生成“每日趣味题”,要求: +- 避免敏感、争议或超纲内容 +- 确保选项有迷惑性,解析有趣 +- 绝对要避免重复,现在的时间是:%v +- 这些是我目前的分类:%v +- 输出格式为 JSON 数组,不要任何解释、不要Markdown、不要任何其他字符: + [{ + "question": "题目文本", // 简洁,30字以内 + "options": ["A. 选项1", "B. 选项2", "C. 选项3", "D. 选项4"], // 提供4个选项(A/B/C/D),其中仅1个正确 + "answer": "C", // 答案 + "explanation": "解析文本", // 200字以内,尽量幽默有趣 + "category": "分类", // 尽量从上述分类中选择,你也可以增加,但是命名风格要类似 + "difficulty": 100, // 难度分 0 - 100 + }] +如果你准备好了,请回答”好的“,不要有其他任何字符 +`, ` +请帮我生成 %v 道,只允许回答 JSON 数组: +`, ` +请继续生成 %v 道,只允许回答 JSON 数组: +`, ` +请继续生成 %v 道,只允许回答 JSON 数组: +`, ` +请继续生成 %v 道,只允许回答 JSON 数组: +`, ` +请继续生成 %v 道,只允许回答 JSON 数组: +`} + +type Question struct { + Question string `json:"question"` // 题干 + Options []string `json:"options"` // 选项 + Answer string `json:"answer"` // 答案 + Explanation string `json:"explanation"` // 解析 + Category string `json:"category"` // 分类 + Difficulty int32 `json:"difficulty"` // 难度分 +} + +func (s *Server) GenerateQuestion(ctx context.Context, req *grpc_pb.GenerateQuestionReq) (*grpc_pb.GenerateQuestionResp, error) { + category, err := repository.NewQuestionDao(ctx).FindCategory() + if err != nil { + log.Errorf("GenerateQuestion FindCategory error: %v", err) + return nil, err + } + question := make([]*Question, 0) + err = ai.NewAIClient(false, "", 0.9). + RequestAI( + []string{ + fmt.Sprintf(prompt[0], time.Now().Format("2006-01-02 15:04:05"), category), + fmt.Sprintf(prompt[1], req.Num, req.Category), + fmt.Sprintf(prompt[2], req.Num), + fmt.Sprintf(prompt[3], req.Num), + fmt.Sprintf(prompt[4], req.Num), + fmt.Sprintf(prompt[5], req.Num), + }, + func(content string, i int) error { + if i == 0 { + return nil + } + step := make([]*Question, 0) + if err := json.Unmarshal([]byte(content), &step); err != nil { + log.Errorf("RequestAI json.Unmarshal error: %v, data: %v", err, content) + return err + } + question = append(question, step...) + return nil + }, + ) + if err != nil { + log.Errorf("RequestAI error: %v", err) + return nil, err + } + + questionDao := repository.NewQuestionDao(ctx, redis.GetCacheClient()) + for _, q := range question { + marshal, _ := json.Marshal(q.Options) + if err = questionDao.Create(&model.Question{ + Question: q.Question, + Options: string(marshal), + Answer: q.Answer, + Explanation: q.Explanation, + Category: q.Category, + Difficulty: q.Difficulty, + }); err != nil { + log.Errorf("GenerateQuestion Create error: %v", err) + return nil, err + } + } + return nil, nil +} + +func (s *Server) GetQuestion(ctx context.Context, req *grpc_pb.GetQuestionReq) (*grpc_pb.GetQuestionResp, error) { + question, err := repository.NewQuestionDao(ctx).FindByRandom() + if err != nil { + log.Errorf("GetQuestion error: %v", err) + return nil, err + } + options := make([]string, 0) + if err = json.Unmarshal([]byte(question.Options), &options); err != nil { + log.Errorf("GetQuestion json.Unmarshal error: %v, data: %v", err, question.Options) + return nil, err + } + return &grpc_pb.GetQuestionResp{ + Sn: question.Sn, + Question: question.Question, + Options: options, + }, nil +} + +func (s *Server) AnswerQuestion(ctx context.Context, req *grpc_pb.AnswerQuestionReq) (*grpc_pb.AnswerQuestionResp, error) { + question, err := repository.NewQuestionDao(ctx).FindBySn(req.Sn) + if err != nil { + log.Errorf("AnswerQuestion error: %v", err) + return nil, err + } + options := make([]string, 0) + if err = json.Unmarshal([]byte(question.Options), &options); err != nil { + log.Errorf("AnswerQuestion json.Unmarshal error: %v, data: %v", err, question.Options) + return nil, err + } + return &grpc_pb.AnswerQuestionResp{ + Answer: question.Answer, + Explanation: question.Explanation, + }, nil +} diff --git a/internal/grpc_server/server/server_question_temp.go b/internal/grpc_server/server/server_question_temp.go new file mode 100644 index 0000000..a7365f7 --- /dev/null +++ b/internal/grpc_server/server/server_question_temp.go @@ -0,0 +1,161 @@ +package server + +// +//import ( +// "bytes" +// "context" +// "encoding/json" +// "git.hlsq.asia/mmorpg/service-common/log" +// "git.hlsq.asia/mmorpg/service-common/proto/rs/grpc_pb" +// "io" +// "net/http" +//) +// +//var question = []string{ +// "本次对话 数据来源必须:东方财富,巨潮资讯,官网,数据截止:自上市至2025年第3季度\n帮我查询一下京东方科技集团股份有限公司公司的所有财报数量", +// "帮我从东方财富 / 巨潮资讯 / 官网这三个来源整理一下重大事项公告的数量,按照三个维度划分( 并购重组公告披露,关联交易公告,对外投资公告)", +// "帮我从东方财富 / 巨潮资讯 / 官网这三个来源整理一下投资者关系活动,按照三个维度划分( 年度业绩说明次数, 投资者调研次数,接待机构数量)", +// "帮我查询一下ESG与治理的信息各个维度评分评级", +// "帮我查询一下这个公司监管合规信息,综合评估一下本年度监管合规(信披评分,公司治理,内控合规,ESG表现,财务合规,业务运营合规,股东与投资者权益保护合规,合规风险管理)百分制", +// "将本次对话的所有内容总结和完整的装入这个结构体生成json对象数据返回,注意:必须以json(markdown代码)的形式返回,并且只输出json即可不用输出其它说明,下面内容就是结构体: \n// CompanyComplianceScore 公司合规评分\ntype CompanyComplianceScore struct {\n\tInformationDisclosureRating string `json:\"informationDisclosureRating\" bson:\"information_disclosure_rating\" comment:\"信息披露评级\"`//获取交易所的信披评级,如A/B/C等\n\tRegularReportFrequencyCount *RegularReportFrequency `json:\"regularReportFrequency\" bson:\"regular_report_frequency\" comment:\"定期报告披露频次\"`\n\tMajorAnnouncementCount *MajorAnnouncementCount `json:\"majorAnnouncementCount\" bson:\"major_announcement_count\" comment:\"重大事项公告\"`\n\tInvestorRelationsActivity *InvestorRelationsActivity `json:\"InvestorRelationsActivity\" bson:\"investor_relations_activity\" comment:\"投资者关系活动\"`\n\tEsgReport *EsgReport `json:\"esgReport\" bson:\"esg_report\" comment:\"ESG与治理\"`\n\tDisclosureScore float64 `json:\"disclosureScore\" bson:\"disclosure_score\" comment:\"信息披露专项评分\"`\n\tCorporateGovernanceScore float64 `json:\"corporateGovernanceScore\" bson:\"corporate_governance_score\" comment:\"公司治理评分\"`\n\tInternalControlCompliance float64 `json:\"internalControlCompliance\" bson:\"internal_control_compliance\" comment:\"内控合规评分\"`\n\tEsgPerformanceScore float64 `json:\"esgPerformanceScore\" bson:\"esg_performance_score\" comment:\"ESG表现评分\"`\n\tFinancialComplianceScore float64 `json:\"financialComplianceScore\" bson:\"financial_compliance_score\" comment:\"财务合规评分\"`\n\tBusinessOperationCompliance float64 `json:\"businessOperationCompliance\" bson:\"business_operation_compliance\" comment:\"业务运营合规评分\"`\n\tShareholderRightsCompliance float64 `json:\"shareholderRightsCompliance\" bson:\"shareholder_rights_compliance\" comment:\"股东权益保护合规评分\"`\n\tComplianceRiskManagement float64 `json:\"complianceRiskManagement\" bson:\"compliance_risk_management\" comment:\"合规风险管理评分\"`\n\tOverallComprehensiveScore float64 `json:\"overallComprehensiveScore\" bson:\"overall_comprehensive_score\" comment:\"综合评分\"`\n}\n\n// RegularReportFrequency 定期报告披露频次\ntype RegularReportFrequency struct {\n\tAnnualReportCount int `json:\"annualReportCount\" bson:\"annual_report_count\" comment:\"年报数量\"`\n\tSemiAnnualReportCount int `json:\"semiAnnualReportCount\" bson:\"semi_annual_report_count\" comment:\"半年报数量\"`\n\tQuarterlyReportCount int `json:\"quarterlyReportCount\" bson:\"quarterly_report_count\" comment:\"季度报告数量\"`\n\tTotalReportCount int `json:\"totalReportCount\" bson:\"total_report_count\" comment:\"报告总数\"`\n}\n\n// MajorAnnouncementCount 重大事项公告\ntype MajorAnnouncementCount struct {\n\tMnaAnnouncementCount int `json:\"mnaAnnouncementCount\" bson:\"mna_announcement_count\" comment:\"并购重组公告数量\"`\n\tRelatedTransactionCount int `json:\"relatedTransactionCount\" bson:\"related_transaction_count\" comment:\"关联交易公告数量\"`\n\tForeignInvestmentCount int `json:\"foreignInvestmentCount\" bson:\"foreign_investment_count\" comment:\"对外投资公告数量\"`\n}\n\n// InvestorRelationsActivity 投资者关系活动\ntype InvestorRelationsActivity struct {\n\tAnnualPerformanceBriefings int `json:\"annualPerformanceBriefings\" bson:\"annual_performance_briefings\" comment:\"年度业绩说明次数\"`\n\tInvestorResearchCount int `json:\"investorResearchCount\" bson:\"investor_research_count\" comment:\"投资者调研次数\"`\n\tInstitutionsReceivedCount int `json:\"institutionsReceivedCount\" bson:\"institutions_received_count\" comment:\"接待机构数量\"`\n}\n\n// EsgReport ESG与治理\ntype EsgReport struct {\n\tEsgOverallScore float64 `json:\"esgOverallScore\" bson:\"esg_overall_score\" comment:\"ESG综合评分\"`//通过【上海华证指数信息服务有限公司】披露的公司ESG评级,例如BBB,不允许返回分数\n\tEnvironmentalScore float64 `json:\"environmentalScore\" bson:\"environmental_score\" comment:\"环境评分\"`\n\tSocialScore float64 `json:\"socialScore\" bson:\"social_score\" comment:\"社会评分\"`\n\tGovernanceScore float64 `json:\"governanceScore\" bson:\"governance_score\" comment:\"治理评分\"`\n}", +//} +// +//type Message struct { +// Role string `json:"role"` +// Content string `json:"content"` +//} +// +//type Input struct { +// Messages []Message `json:"messages"` +//} +// +//type Parameters struct { +// EnableSearch bool `json:"enable_search"` +// ResultFormat string `json:"result_format"` +// SearchOptions SearchOptions `json:"search_options"` +//} +// +//type SearchOptions struct { +// SearchStrategy string `json:"search_strategy"` +//} +// +//type RequestBody struct { +// Model string `json:"model"` +// Input Input `json:"input"` +// Parameters Parameters `json:"parameters"` +//} +// +//type ResponseBody struct { +// Output struct { +// Choices []struct { +// Message Message `json:"message"` +// FinishReason string `json:"finish_reason"` +// } `json:"choices"` +// } `json:"output"` +// Usage struct { +// InputTokens int32 `json:"input_tokens"` +// OutputTokens int32 `json:"output_tokens"` +// TotalTokens int32 `json:"total_tokens"` +// } `json:"usage"` +//} +// +//func (s *Server) GenerateQuestion(ctx context.Context, req *grpc_pb.GenerateQuestionReq) (*grpc_pb.GenerateQuestionResp, error) { +// inToken, outToken := int32(0), int32(0) +// +// messages := make([]Message, 0) +// for i, step := range question { +// messages = append(messages, newUserMessage(step)) +// +// //chatCompletion, err := client.Chat.Completions.New( +// // context.Background(), openai.ChatCompletionNewParams{ +// // Messages: messages, +// // Model: "qwen3-max", +// // }, +// //) +// //if err != nil { +// // return nil, err +// //} +// r, err := request(messages) +// if err != nil { +// return nil, err +// } +// inToken += r.Usage.InputTokens +// outToken += r.Usage.OutputTokens +// log.Infof("回答 %v:%v", i, r) +// //break +// messages = append(messages, r.Output.Choices[0].Message) +// +// //log.Infof("回答 %v:%v", i, chatCompletion.Choices[0].Message.Content) +// //messages = append(messages, openai.AssistantMessage(chatCompletion.Choices[0].Message.Content)) +// } +// log.Infof("返回:%v", messages[len(messages)-1].Content) +// //log.Infof("消耗token:%v %v, 费用:%v", inToken, outToken, float64(inToken)/1000*0.0032+float64(outToken)/1000*0.0128) +// log.Infof("消耗token:%v %v, 费用:%v", inToken, outToken, float64(inToken)/1000*0.002+float64(outToken)/1000*0.003) +// +// //chatCompletion, err := client.Chat.Completions.New( +// // context.Background(), openai.ChatCompletionNewParams{ +// // Messages: []openai.ChatCompletionMessageParamUnion{ +// // openai.UserMessage("你能做什么"), +// // }, +// // Model: "qwen3-max", +// // }, +// //) +// //if err != nil { +// // return nil, err +// //} +// //for i, choice := range chatCompletion.Choices { +// // log.Infof("chatCompletion %v: %#+v \n", i, choice) +// //} +// //log.Infof("返回:%v", chatCompletion.Choices[0].Message.Content) +// return nil, nil +//} +// +//func newUserMessage(content string) Message { +// return Message{ +// Role: "user", +// Content: content, +// } +//} +// +//func request(messages []Message) (*ResponseBody, error) { +// client := &http.Client{} +// requestBody := RequestBody{ +// //Model: "qwen3-max", +// Model: "deepseek-v3.2", +// Input: Input{ +// Messages: messages, +// }, +// Parameters: Parameters{ +// EnableSearch: true, +// SearchOptions: SearchOptions{ +// SearchStrategy: "max", +// }, +// +// ResultFormat: "message", +// }, +// } +// jsonData, _ := json.Marshal(requestBody) +// +// req, err := http.NewRequest("POST", "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation", bytes.NewBuffer(jsonData)) +// if err != nil { +// return nil, err +// } +// req.Header.Set("Authorization", "Bearer "+"sk-4daf94ad2fa94288b198d2a1d8924cef") +// req.Header.Set("Content-Type", "application/json") +// +// resp, err := client.Do(req) +// if err != nil { +// return nil, err +// } +// defer resp.Body.Close() +// +// body, err := io.ReadAll(resp.Body) +// if err != nil { +// return nil, err +// } +// var res ResponseBody +// if err = json.Unmarshal(body, &res); err != nil { +// return nil, err +// } +// return &res, nil +//} diff --git a/main.go b/main.go new file mode 100644 index 0000000..0ff46a5 --- /dev/null +++ b/main.go @@ -0,0 +1,14 @@ +package main + +import ( + "fmt" + "git.hlsq.asia/mmorpg/service-qgdzs/app" + "github.com/judwhite/go-svc" + "syscall" +) + +func main() { + if err := svc.Run(&app.Program{}, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL); err != nil { + fmt.Println(err) + } +}