Merge branch 'feature/rzy/talk_advicer' into test
# Conflicts: # internal/biz/provider_set.go # internal/data/impl/provider_set.go # internal/server/router/router.go # internal/services/dtalk_bot_test.go
This commit is contained in:
commit
46049475c1
File diff suppressed because one or more lines are too long
|
|
@ -20,8 +20,7 @@ func main() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("加载配置失败: %v", err)
|
log.Fatalf("加载配置失败: %v", err)
|
||||||
}
|
}
|
||||||
|
app, cleanup, err := InitializeApp(ctx, bc, log.DefaultLogger())
|
||||||
app, cleanup, err := InitializeApp(bc, log.DefaultLogger())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("项目初始化失败: %v", err)
|
log.Fatalf("项目初始化失败: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,14 @@ import (
|
||||||
"ai_scheduler/internal/biz/tools_regis"
|
"ai_scheduler/internal/biz/tools_regis"
|
||||||
"ai_scheduler/internal/config"
|
"ai_scheduler/internal/config"
|
||||||
"ai_scheduler/internal/data/impl"
|
"ai_scheduler/internal/data/impl"
|
||||||
|
"ai_scheduler/internal/data/mongo_model"
|
||||||
"ai_scheduler/internal/domain/component"
|
"ai_scheduler/internal/domain/component"
|
||||||
"ai_scheduler/internal/domain/repo"
|
"ai_scheduler/internal/domain/repo"
|
||||||
"ai_scheduler/internal/domain/workflow"
|
"ai_scheduler/internal/domain/workflow"
|
||||||
"ai_scheduler/internal/pkg"
|
"ai_scheduler/internal/pkg"
|
||||||
"ai_scheduler/internal/server"
|
"ai_scheduler/internal/server"
|
||||||
"ai_scheduler/internal/services"
|
"ai_scheduler/internal/services"
|
||||||
|
"context"
|
||||||
|
|
||||||
// "ai_scheduler/internal/tool_callback"
|
// "ai_scheduler/internal/tool_callback"
|
||||||
"ai_scheduler/internal/tools"
|
"ai_scheduler/internal/tools"
|
||||||
|
|
@ -26,7 +28,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// InitializeApp 初始化应用程序
|
// InitializeApp 初始化应用程序
|
||||||
func InitializeApp(*config.Config, log.AllLogger) (*server.Servers, func(), error) {
|
func InitializeApp(context.Context, *config.Config, log.AllLogger) (*server.Servers, func(), error) {
|
||||||
panic(wire.Build(
|
panic(wire.Build(
|
||||||
server.ProviderSetServer,
|
server.ProviderSetServer,
|
||||||
workflow.ProviderSetWorkflow,
|
workflow.ProviderSetWorkflow,
|
||||||
|
|
@ -42,6 +44,7 @@ func InitializeApp(*config.Config, log.AllLogger) (*server.Servers, func(), erro
|
||||||
// tool_callback.ProviderSetCallBackTools,
|
// tool_callback.ProviderSetCallBackTools,
|
||||||
component.ProviderSet,
|
component.ProviderSet,
|
||||||
repo.ProviderSet,
|
repo.ProviderSet,
|
||||||
|
mongo_model.ProviderSetMongo,
|
||||||
))
|
))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,14 @@ redis:
|
||||||
db:
|
db:
|
||||||
driver: mysql
|
driver: mysql
|
||||||
source: root:SD###sdf323r343@tcp(121.199.38.107:3306)/sys_ai?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
|
source: root:SD###sdf323r343@tcp(121.199.38.107:3306)/sys_ai?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
|
||||||
|
mongo:
|
||||||
|
source: mongodb://root:lsxd2026123@192.168.6.115:27017
|
||||||
|
dataBase: ai_scheduler
|
||||||
|
maxPoolSize: 100
|
||||||
|
minPoolSize: 10
|
||||||
|
maxConnIdleTime: 30
|
||||||
|
connectTimeout: 10
|
||||||
|
socketTimeout: 30
|
||||||
oss:
|
oss:
|
||||||
access_key: "LTAI5tGGZzjf3tvqWk8SQj2G"
|
access_key: "LTAI5tGGZzjf3tvqWk8SQj2G"
|
||||||
secret_key: "S0NKOAUaYWoK4EGSxrMFmYDzllhvpq"
|
secret_key: "S0NKOAUaYWoK4EGSxrMFmYDzllhvpq"
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,14 @@ redis:
|
||||||
db:
|
db:
|
||||||
driver: mysql
|
driver: mysql
|
||||||
source: root:SD###sdf323r343@tcp(121.199.38.107:3306)/sys_ai_test?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
|
source: root:SD###sdf323r343@tcp(121.199.38.107:3306)/sys_ai_test?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
|
||||||
|
mongo:
|
||||||
|
source: mongodb://root:lsxd2026123@192.168.6.115:27017
|
||||||
|
dataBase: ai_scheduler_test
|
||||||
|
maxPoolSize: 100
|
||||||
|
minPoolSize: 10
|
||||||
|
maxConnIdleTime: 30
|
||||||
|
connectTimeout: 10
|
||||||
|
socketTimeout: 30
|
||||||
oss:
|
oss:
|
||||||
access_key: "LTAI5tGGZzjf3tvqWk8SQj2G"
|
access_key: "LTAI5tGGZzjf3tvqWk8SQj2G"
|
||||||
secret_key: "S0NKOAUaYWoK4EGSxrMFmYDzllhvpq"
|
secret_key: "S0NKOAUaYWoK4EGSxrMFmYDzllhvpq"
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,15 @@ redis:
|
||||||
db:
|
db:
|
||||||
driver: mysql
|
driver: mysql
|
||||||
source: root:SD###sdf323r343@tcp(121.199.38.107:3306)/sys_ai_test?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
|
source: root:SD###sdf323r343@tcp(121.199.38.107:3306)/sys_ai_test?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
|
||||||
|
mongo:
|
||||||
|
source: mongodb://root:lsxd2026123@192.168.6.115:27017
|
||||||
|
dataBase: ai_scheduler_test
|
||||||
|
maxPoolSize: 100
|
||||||
|
minPoolSize: 10
|
||||||
|
maxConnIdleTime: 30
|
||||||
|
connectTimeout: 10
|
||||||
|
socketTimeout: 30
|
||||||
|
|
||||||
oss:
|
oss:
|
||||||
access_key: "LTAI5tGGZzjf3tvqWk8SQj2G"
|
access_key: "LTAI5tGGZzjf3tvqWk8SQj2G"
|
||||||
secret_key: "S0NKOAUaYWoK4EGSxrMFmYDzllhvpq"
|
secret_key: "S0NKOAUaYWoK4EGSxrMFmYDzllhvpq"
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
#export GO111MODULE=on
|
|
||||||
#export GOPROXY=https://goproxy.cn,direct
|
|
||||||
#export GOPATH=/root/go
|
|
||||||
#export GOCACHE=/root/.cache/go-build
|
|
||||||
export CONTAINER_NAME=ai_scheduler
|
export CONTAINER_NAME=ai_scheduler
|
||||||
export NETWORK_NAME=ai_scheduler_network
|
export NETWORK_NAME=ai_scheduler_network
|
||||||
#export CGO_ENABLED='0'
|
|
||||||
|
|
||||||
|
|
||||||
MODE="$1"
|
MODE="$1"
|
||||||
|
|
@ -27,8 +23,7 @@ fi
|
||||||
git fetch origin
|
git fetch origin
|
||||||
git checkout "$BRANCH"
|
git checkout "$BRANCH"
|
||||||
git pull origin "$BRANCH"
|
git pull origin "$BRANCH"
|
||||||
#go mod tidy
|
|
||||||
#make build
|
|
||||||
docker build -t ${CONTAINER_NAME} .
|
docker build -t ${CONTAINER_NAME} .
|
||||||
docker stop ${CONTAINER_NAME}
|
docker stop ${CONTAINER_NAME}
|
||||||
docker rm -f ${CONTAINER_NAME}
|
docker rm -f ${CONTAINER_NAME}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
# MySQL 8.0 服务
|
||||||
|
mysql:
|
||||||
|
image: mysql:8.0
|
||||||
|
container_name: mysql_db
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-lsxd2026}
|
||||||
|
MYSQL_DATABASE: ${MYSQL_DATABASE:-myapp}
|
||||||
|
MYSQL_USER: ${MYSQL_USER:-myuser}
|
||||||
|
MYSQL_PASSWORD: ${MYSQL_PASSWORD:-mypassword}
|
||||||
|
ports:
|
||||||
|
- "3306:3306"
|
||||||
|
volumes:
|
||||||
|
- mysql_data:/var/lib/mysql
|
||||||
|
- ./mysql/my.cnf:/etc/mysql/conf.d/my.cnf
|
||||||
|
networks:
|
||||||
|
- ai_scheduler_network
|
||||||
|
command:
|
||||||
|
--default-authentication-plugin=mysql_native_password
|
||||||
|
--character-set-server=utf8mb4
|
||||||
|
--collation-server=utf8mb4_unicode_ci
|
||||||
|
--max_connections=1000
|
||||||
|
|
||||||
|
# MongoDB 服务
|
||||||
|
mongodb:
|
||||||
|
image: mongo:latest
|
||||||
|
container_name: mongodb
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
MONGO_INITDB_ROOT_USERNAME: ${MONGO_ROOT_USERNAME:-root}
|
||||||
|
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ROOT_PASSWORD:-lsxd2026123}
|
||||||
|
ports:
|
||||||
|
- "27017:27017"
|
||||||
|
volumes:
|
||||||
|
- mongodb_data:/data/db
|
||||||
|
command:
|
||||||
|
--auth
|
||||||
|
--bind_ip_all # 允许所有IP连接
|
||||||
|
networks:
|
||||||
|
- ai_scheduler_network
|
||||||
|
|
||||||
|
# Redis 服务up
|
||||||
|
redis:
|
||||||
|
image: redis:alpine
|
||||||
|
container_name: redis
|
||||||
|
restart: unless-stopped
|
||||||
|
command: redis-server --requirepass ${REDIS_PASSWORD:-redispassword123} --bind 0.0.0.0
|
||||||
|
ports:
|
||||||
|
- "6379:6379"
|
||||||
|
volumes:
|
||||||
|
- redis_data:/data
|
||||||
|
- ./redis/redis.conf:/usr/local/etc/redis/redis.conf
|
||||||
|
networks:
|
||||||
|
- ai_scheduler_network
|
||||||
|
|
||||||
|
networks:
|
||||||
|
ai_scheduler_network:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mysql_data:
|
||||||
|
driver: local
|
||||||
|
mongodb_data:
|
||||||
|
driver: local
|
||||||
|
redis_data:
|
||||||
|
driver: local
|
||||||
13
go.mod
13
go.mod
|
|
@ -33,7 +33,10 @@ require (
|
||||||
github.com/spf13/viper v1.17.0
|
github.com/spf13/viper v1.17.0
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
github.com/tmc/langchaingo v0.1.13
|
github.com/tmc/langchaingo v0.1.13
|
||||||
|
github.com/valyala/fasthttp v1.51.0
|
||||||
|
github.com/volcengine/volcengine-go-sdk v1.2.9
|
||||||
github.com/xuri/excelize/v2 v2.10.0
|
github.com/xuri/excelize/v2 v2.10.0
|
||||||
|
go.mongodb.org/mongo-driver v1.14.0
|
||||||
golang.org/x/sync v0.17.0
|
golang.org/x/sync v0.17.0
|
||||||
google.golang.org/grpc v1.64.0
|
google.golang.org/grpc v1.64.0
|
||||||
gorm.io/driver/mysql v1.6.0
|
gorm.io/driver/mysql v1.6.0
|
||||||
|
|
@ -71,11 +74,13 @@ require (
|
||||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
||||||
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
github.com/goph/emperror v0.17.2 // indirect
|
github.com/goph/emperror v0.17.2 // indirect
|
||||||
github.com/gorilla/websocket v1.5.3 // indirect
|
github.com/gorilla/websocket v1.5.3 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/compress v1.17.9 // indirect
|
github.com/klauspost/compress v1.17.9 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||||
|
|
@ -89,6 +94,7 @@ require (
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
|
||||||
github.com/nikolalohinski/gonja v1.5.3 // indirect
|
github.com/nikolalohinski/gonja v1.5.3 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
|
@ -111,12 +117,16 @@ require (
|
||||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasthttp v1.51.0 // indirect
|
|
||||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||||
|
github.com/volcengine/volc-sdk-golang v1.0.23 // indirect
|
||||||
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
|
github.com/xdg-go/scram v1.1.2 // indirect
|
||||||
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
github.com/xuri/efp v0.0.1 // indirect
|
github.com/xuri/efp v0.0.1 // indirect
|
||||||
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 // indirect
|
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 // indirect
|
||||||
github.com/yargevad/filepathx v1.0.0 // indirect
|
github.com/yargevad/filepathx v1.0.0 // indirect
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||||
go.uber.org/atomic v1.9.0 // indirect
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
go.uber.org/multierr v1.9.0 // indirect
|
||||||
golang.org/x/arch v0.11.0 // indirect
|
golang.org/x/arch v0.11.0 // indirect
|
||||||
|
|
@ -129,5 +139,6 @@ require (
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect
|
||||||
google.golang.org/protobuf v1.34.1 // indirect
|
google.golang.org/protobuf v1.34.1 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
31
go.sum
31
go.sum
|
|
@ -101,6 +101,7 @@ github.com/aliyun/credentials-go v1.4.6 h1:CG8rc/nxCNKfXbZWpWDzI9GjF4Tuu3Es14qT8
|
||||||
github.com/aliyun/credentials-go v1.4.6/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=
|
github.com/aliyun/credentials-go v1.4.6/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=
|
||||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||||
|
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
|
||||||
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
|
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
|
||||||
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
|
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
|
||||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
||||||
|
|
@ -239,6 +240,9 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||||
|
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
|
@ -250,6 +254,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
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/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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
|
@ -268,6 +273,7 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe
|
||||||
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
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/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/wire v0.7.0 h1:JxUKI6+CVBgCO2WToKy/nQk0sS+amI9z9EjVmdaocj4=
|
github.com/google/wire v0.7.0 h1:JxUKI6+CVBgCO2WToKy/nQk0sS+amI9z9EjVmdaocj4=
|
||||||
|
|
@ -295,6 +301,10 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
|
||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
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 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
|
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||||
|
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||||
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||||
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
|
|
@ -312,6 +322,7 @@ github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
|
@ -345,6 +356,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
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/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
|
||||||
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/nikolalohinski/gonja v1.5.3 h1:GsA+EEaZDZPGJ8JtpeGN78jidhOlxeJROpqMT9fTj9c=
|
github.com/nikolalohinski/gonja v1.5.3 h1:GsA+EEaZDZPGJ8JtpeGN78jidhOlxeJROpqMT9fTj9c=
|
||||||
github.com/nikolalohinski/gonja v1.5.3/go.mod h1:RmjwxNiXAEqcq1HeK5SSMmqFJvKOfTfXhkJv6YBtPa4=
|
github.com/nikolalohinski/gonja v1.5.3/go.mod h1:RmjwxNiXAEqcq1HeK5SSMmqFJvKOfTfXhkJv6YBtPa4=
|
||||||
|
|
@ -451,10 +464,20 @@ github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1S
|
||||||
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
|
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
|
||||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||||
|
github.com/volcengine/volc-sdk-golang v1.0.23 h1:anOslb2Qp6ywnsbyq9jqR0ljuO63kg9PY+4OehIk5R8=
|
||||||
|
github.com/volcengine/volc-sdk-golang v1.0.23/go.mod h1:AfG/PZRUkHJ9inETvbjNifTDgut25Wbkm2QoYBTbvyU=
|
||||||
|
github.com/volcengine/volcengine-go-sdk v1.2.9 h1:du2gnImtyWXKkQFnJW/GXCs+UBibGGOXIbP1Ams2pB8=
|
||||||
|
github.com/volcengine/volcengine-go-sdk v1.2.9/go.mod h1:oxoVo+A17kvkwPkIeIHPVLjSw7EQAm+l/Vau1YGHN+A=
|
||||||
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||||
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
|
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
|
||||||
github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg=
|
github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg=
|
||||||
github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
|
github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
|
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
||||||
|
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
||||||
|
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||||
|
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||||
github.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8=
|
github.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8=
|
||||||
github.com/xuri/efp v0.0.1/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
|
github.com/xuri/efp v0.0.1/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
|
||||||
github.com/xuri/excelize/v2 v2.10.0 h1:8aKsP7JD39iKLc6dH5Tw3dgV3sPRh8uRVXu/fMstfW4=
|
github.com/xuri/excelize/v2 v2.10.0 h1:8aKsP7JD39iKLc6dH5Tw3dgV3sPRh8uRVXu/fMstfW4=
|
||||||
|
|
@ -463,6 +486,8 @@ github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 h1:+C0TIdyyYmzadGaL/HBL
|
||||||
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
|
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
|
||||||
github.com/yargevad/filepathx v1.0.0 h1:SYcT+N3tYGi+NvazubCNlvgIPbzAk7i7y2dwg3I5FYc=
|
github.com/yargevad/filepathx v1.0.0 h1:SYcT+N3tYGi+NvazubCNlvgIPbzAk7i7y2dwg3I5FYc=
|
||||||
github.com/yargevad/filepathx v1.0.0/go.mod h1:BprfX/gpYNJHJfc35GjRRpVcwWXS89gGulUIU5tK3tA=
|
github.com/yargevad/filepathx v1.0.0/go.mod h1:BprfX/gpYNJHJfc35GjRRpVcwWXS89gGulUIU5tK3tA=
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
|
@ -470,6 +495,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||||
|
go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80=
|
||||||
|
go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
|
@ -683,6 +710,7 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
|
|
@ -841,11 +869,14 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
gopkg.in/bsm/ratelimit.v1 v1.0.0-20160220154919-db14e161995a/go.mod h1:KF9sEfUPAXdG8Oev9e99iLGnl2uJMjc5B+4y3O7x610=
|
gopkg.in/bsm/ratelimit.v1 v1.0.0-20160220154919-db14e161995a/go.mod h1:KF9sEfUPAXdG8Oev9e99iLGnl2uJMjc5B+4y3O7x610=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/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 h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,208 @@
|
||||||
|
package biz
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ai_scheduler/internal/data/impl"
|
||||||
|
"ai_scheduler/internal/data/model"
|
||||||
|
"ai_scheduler/internal/data/mongo_model"
|
||||||
|
"ai_scheduler/internal/entitys"
|
||||||
|
"ai_scheduler/internal/pkg"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
"xorm.io/builder"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AdviceAdvicerBiz struct {
|
||||||
|
advicerImpl *impl.AdviceAdvicerImpl
|
||||||
|
advicerVersionMongo *mongo_model.AdvicerVersionMongo
|
||||||
|
mongo *pkg.Mongo
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdviceAdvicerBiz(
|
||||||
|
advicerImpl *impl.AdviceAdvicerImpl,
|
||||||
|
advicerVersionMongo *mongo_model.AdvicerVersionMongo,
|
||||||
|
mongo *pkg.Mongo,
|
||||||
|
) *AdviceAdvicerBiz {
|
||||||
|
return &AdviceAdvicerBiz{
|
||||||
|
advicerImpl: advicerImpl,
|
||||||
|
advicerVersionMongo: advicerVersionMongo,
|
||||||
|
mongo: mongo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceAdvicerBiz) Update(ctx context.Context, data *entitys.AdvicerInitReq) error {
|
||||||
|
birth, err := time.Parse("2006-01-02", data.Birth)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
param := &model.AiAdviceAdvicer{
|
||||||
|
AdvicerID: data.AdvicerID,
|
||||||
|
ProjectID: data.ProjectID,
|
||||||
|
Name: data.Name,
|
||||||
|
Birth: birth,
|
||||||
|
Gender: data.Gender,
|
||||||
|
WorkingYears: data.WorkingYears,
|
||||||
|
}
|
||||||
|
if param.AdvicerID == 0 {
|
||||||
|
_, err = a.advicerImpl.Add(param)
|
||||||
|
} else {
|
||||||
|
cond := builder.NewCond()
|
||||||
|
cond = cond.And(builder.Eq{"advicer_id": param.AdvicerID})
|
||||||
|
err = a.advicerImpl.UpdateByCond(&cond, param)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceAdvicerBiz) List(ctx context.Context, data *entitys.AdvicerListReq) ([]map[string]interface{}, error) {
|
||||||
|
|
||||||
|
cond := builder.NewCond()
|
||||||
|
cond = cond.And(builder.Eq{"project_id": data.ProjectId})
|
||||||
|
list, err := a.advicerImpl.GetRange(&cond)
|
||||||
|
return list, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceAdvicerBiz) VersionAdd(ctx context.Context, param *entitys.AdvicerVersionAddReq) (err error) {
|
||||||
|
cond := builder.NewCond()
|
||||||
|
cond = cond.And(builder.Eq{"advicer_id": param.AdvicerID})
|
||||||
|
_, err = a.advicerImpl.GetOneBySearch(&cond)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("顾问不存在")
|
||||||
|
}
|
||||||
|
_, err = a.mongo.Co(a.advicerVersionMongo).InsertOne(ctx, &mongo_model.AdvicerVersionMongo{
|
||||||
|
AdvicerId: param.AdvicerID,
|
||||||
|
VersionDesc: param.VersionDesc,
|
||||||
|
DialectFeatures: param.DialectFeatures,
|
||||||
|
SentencePatterns: param.SentencePatterns,
|
||||||
|
ToneTags: param.ToneTags,
|
||||||
|
PersonalityTags: param.PersonalityTags,
|
||||||
|
SignatureDialogues: param.SignatureDialogues,
|
||||||
|
LastUpdateTime: time.Now(),
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceAdvicerBiz) VersionUpdate(ctx context.Context, param *entitys.AdvicerVersionUpdateReq) (err error) {
|
||||||
|
filter := bson.M{}
|
||||||
|
if len(param.Id) == 0 {
|
||||||
|
return errors.New("ID不能为空")
|
||||||
|
}
|
||||||
|
objectID, err := primitive.ObjectIDFromHex(param.Id)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ID转换失败: %w", err)
|
||||||
|
}
|
||||||
|
filter["_id"] = objectID
|
||||||
|
update := bson.M{
|
||||||
|
"$set": &mongo_model.AdvicerVersionMongo{
|
||||||
|
AdvicerId: param.AdvicerID,
|
||||||
|
VersionDesc: param.VersionDesc,
|
||||||
|
DialectFeatures: param.DialectFeatures,
|
||||||
|
SentencePatterns: param.SentencePatterns,
|
||||||
|
ToneTags: param.ToneTags,
|
||||||
|
PersonalityTags: param.PersonalityTags,
|
||||||
|
SignatureDialogues: param.SignatureDialogues,
|
||||||
|
LastUpdateTime: time.Now(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
res := a.mongo.Co(a.advicerVersionMongo).FindOneAndUpdate(ctx, filter, update)
|
||||||
|
|
||||||
|
return res.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceAdvicerBiz) VersionList(ctx context.Context, param *entitys.AdvicerVersionListReq) (list []mongo_model.AdvicerVersionMongo, err error) {
|
||||||
|
filter := bson.M{}
|
||||||
|
// 1. advicer_id 条件
|
||||||
|
if param.AdvicerId != 0 {
|
||||||
|
filter["AdvicerId"] = param.AdvicerId
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. _id 条件
|
||||||
|
if len(param.Id) != 0 {
|
||||||
|
objectID, err := primitive.ObjectIDFromHex(param.Id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("ID转换失败: %w", err)
|
||||||
|
}
|
||||||
|
filter["_id"] = objectID
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. version_desc 模糊查询
|
||||||
|
if len(param.VersionDesc) != 0 {
|
||||||
|
// 正确的方式:指定字段名
|
||||||
|
filter["VersionDesc"] = bson.M{
|
||||||
|
"$regex": primitive.Regex{
|
||||||
|
Pattern: param.VersionDesc,
|
||||||
|
Options: "i",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor, err := a.mongo.Co(a.advicerVersionMongo).Find(ctx, filter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// 遍历结果
|
||||||
|
for cursor.Next(ctx) {
|
||||||
|
var advicerVersion mongo_model.AdvicerVersionMongo
|
||||||
|
if err := cursor.Decode(&advicerVersion); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
list = append(list, advicerVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cursor.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return list, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceAdvicerBiz) VersionDel(ctx context.Context, param *entitys.AdvicerVersionDelReq) (err error) {
|
||||||
|
filter := bson.M{}
|
||||||
|
// 1. advicer_id 条件
|
||||||
|
|
||||||
|
if len(param.Id) != 0 {
|
||||||
|
objectID, err := primitive.ObjectIDFromHex(param.Id)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ID转换失败: %w", err)
|
||||||
|
}
|
||||||
|
filter["_id"] = objectID
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = a.mongo.Co(a.advicerVersionMongo).DeleteOne(ctx, filter)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceAdvicerBiz) VersionInfo(ctx context.Context, param *entitys.AdvicerVersionInfoReq) (info mongo_model.AdvicerVersionMongo, err error) {
|
||||||
|
filter := bson.M{}
|
||||||
|
if len(param.Id) != 0 {
|
||||||
|
objectID, err := primitive.ObjectIDFromHex(param.Id)
|
||||||
|
if err != nil {
|
||||||
|
return info, fmt.Errorf("ID转换失败: %w", err)
|
||||||
|
|
||||||
|
}
|
||||||
|
filter["_id"] = objectID
|
||||||
|
}
|
||||||
|
|
||||||
|
res := a.mongo.Co(a.advicerVersionMongo).FindOne(ctx, filter)
|
||||||
|
if res.Err() != nil {
|
||||||
|
return info, res.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := res.Decode(&info); err != nil {
|
||||||
|
return info, err
|
||||||
|
}
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceAdvicerBiz) AdvicerInfo(ctx context.Context, param *entitys.AdvicerInfoReq) (info model.AiAdviceAdvicer, err error) {
|
||||||
|
cond := builder.NewCond()
|
||||||
|
cond = cond.And(builder.Eq{"advicer_id": param.AdvicerID})
|
||||||
|
|
||||||
|
err = a.advicerImpl.GetOneBySearchToStrut(&cond, &info)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,124 @@
|
||||||
|
package biz
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ai_scheduler/internal/biz/llm_service/third_party"
|
||||||
|
"ai_scheduler/internal/entitys"
|
||||||
|
"ai_scheduler/internal/pkg"
|
||||||
|
"ai_scheduler/utils"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
|
||||||
|
"github.com/volcengine/volcengine-go-sdk/volcengine"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AdviceChatBiz struct {
|
||||||
|
hsyq *third_party.Hsyq
|
||||||
|
rdb *utils.Rdb
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdviceChatBiz(
|
||||||
|
hsyq *third_party.Hsyq,
|
||||||
|
rdb *utils.Rdb,
|
||||||
|
) *AdviceChatBiz {
|
||||||
|
return &AdviceChatBiz{
|
||||||
|
hsyq: hsyq,
|
||||||
|
rdb: rdb,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceChatBiz) Regis(ctx context.Context, chatData *entitys.ChatData) (string, error) {
|
||||||
|
sessionId := uuid.New().String()
|
||||||
|
prompt, err := a.buildBasePrompt(ctx, chatData)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
err = a.rdb.Rdb.SetEx(ctx, sessionId, pkg.JsonStringIgonErr(prompt), 3600*time.Second).Err()
|
||||||
|
|
||||||
|
return sessionId, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceChatBiz) Chat(ctx context.Context, chat *entitys.AdvicerChatReq) ([]string, error) {
|
||||||
|
if len(chat.Content) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
basePromptJson, err := a.getChatDataFromStringSessionId(ctx, chat.SessionId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
prompt, err := a.setContent(ctx, basePromptJson, chat.Content)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resContent, err := a.callLlm(ctx, prompt, fileModel)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resSlice := strings.Split(resContent, "\n")
|
||||||
|
return resSlice, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceChatBiz) buildBasePrompt(ctx context.Context, chatData *entitys.ChatData) ([]*model.ChatCompletionMessage, error) {
|
||||||
|
var message = make([]*model.ChatCompletionMessage, 3)
|
||||||
|
message[0] = &model.ChatCompletionMessage{
|
||||||
|
Role: model.ChatMessageRoleSystem,
|
||||||
|
Content: &model.ChatCompletionMessageContent{
|
||||||
|
StringValue: volcengine.String(a.sysPrompt(chatData)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
message[1] = &model.ChatCompletionMessage{
|
||||||
|
Role: model.ChatMessageRoleUser,
|
||||||
|
Content: &model.ChatCompletionMessageContent{
|
||||||
|
StringValue: volcengine.String("{{chat_content}}"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
message[2] = &model.ChatCompletionMessage{
|
||||||
|
Role: model.ChatMessageRoleAssistant,
|
||||||
|
Content: &model.ChatCompletionMessageContent{
|
||||||
|
StringValue: volcengine.String(a.assistantPrompt(chatData)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return message, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceChatBiz) setContent(ctx context.Context, basePromptJson string, content string) ([]*model.ChatCompletionMessage, error) {
|
||||||
|
promptJson := strings.ReplaceAll(basePromptJson, "{{chat_content}}", content)
|
||||||
|
var basePrompt []*model.ChatCompletionMessage
|
||||||
|
err := json.Unmarshal([]byte(promptJson), &basePrompt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return basePrompt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceChatBiz) sysPrompt(chatData *entitys.ChatData) string {
|
||||||
|
return "【场景】现在你是专业的顾问,我们在聊天软件里面聊天,当前时间是2026/01/30 16:13:30\n【要求】提供的json内容包含顾问,项目,对话技巧,客户信息,请根据json内容进行模仿真人进行对话,** 绝对不能让对方看出来你是ai!**\n【规则】\n1.必须模仿顾问的聊天方式,不能有markdown格式,聊天内容必须贴近聊天软件的正常聊天,非客户要求不要自顾自的介绍项目!开场白不要太长!\n2.不要有任何特殊格式,有比较长的对话内容注意提行\n3.尽量邀约对方来项目上来\n4.不能有任何画外音的文字格式出现!比如(等待对方回复)"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceChatBiz) assistantPrompt(chatData *entitys.ChatData) string {
|
||||||
|
return pkg.JsonStringIgonErr(chatData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceChatBiz) getChatDataFromStringSessionId(ctx context.Context, sessionId string) (basePromptJson string, err error) {
|
||||||
|
cache := a.rdb.Rdb.Get(ctx, sessionId)
|
||||||
|
if cache.Err() != nil {
|
||||||
|
err = cache.Err()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return cache.Val(), cache.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceChatBiz) callLlm(ctx context.Context, prompt []*model.ChatCompletionMessage, modelName string) (string, error) {
|
||||||
|
res, err := a.hsyq.RequestHsyq(ctx, key, modelName, prompt)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return *res.Choices[0].Message.Content.StringValue, nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
package biz
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ai_scheduler/internal/data/mongo_model"
|
||||||
|
"ai_scheduler/internal/entitys"
|
||||||
|
"ai_scheduler/internal/pkg"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AdviceClientBiz struct {
|
||||||
|
AdvicerClientMongo *mongo_model.AdvicerClientMongo
|
||||||
|
mongo *pkg.Mongo
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdviceClientBiz(
|
||||||
|
advicerClientMongo *mongo_model.AdvicerClientMongo,
|
||||||
|
mongo *pkg.Mongo,
|
||||||
|
) *AdviceClientBiz {
|
||||||
|
return &AdviceClientBiz{
|
||||||
|
AdvicerClientMongo: advicerClientMongo,
|
||||||
|
mongo: mongo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceClientBiz) Add(ctx context.Context, param *entitys.AdvicerClientAddReq) (err error) {
|
||||||
|
|
||||||
|
_, err = a.mongo.Co(a.AdvicerClientMongo).InsertOne(ctx, &mongo_model.AdvicerClientMongo{
|
||||||
|
ProjectId: param.ProjectId,
|
||||||
|
AdvicerId: param.AdvicerId,
|
||||||
|
PersonalInfo: param.PersonalInfo,
|
||||||
|
PurchasePurpose: param.PurchasePurpose,
|
||||||
|
CoreDemands: param.CoreDemands,
|
||||||
|
Concerns: param.Concerns,
|
||||||
|
DecisionProfile: param.DecisionProfile,
|
||||||
|
LastUpdateTime: time.Now(),
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceClientBiz) Update(ctx context.Context, param *entitys.AdvicerrClientUpdateReq) (err error) {
|
||||||
|
filter := bson.M{}
|
||||||
|
if len(param.Id) == 0 {
|
||||||
|
return errors.New("ID不能为空")
|
||||||
|
}
|
||||||
|
objectID, err := primitive.ObjectIDFromHex(param.Id)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ID转换失败: %w", err)
|
||||||
|
}
|
||||||
|
filter["_id"] = objectID
|
||||||
|
update := bson.M{
|
||||||
|
"$set": &mongo_model.AdvicerClientMongo{
|
||||||
|
ProjectId: param.ProjectId,
|
||||||
|
AdvicerId: param.AdvicerId,
|
||||||
|
PersonalInfo: param.PersonalInfo,
|
||||||
|
PurchasePurpose: param.PurchasePurpose,
|
||||||
|
CoreDemands: param.CoreDemands,
|
||||||
|
Concerns: param.Concerns,
|
||||||
|
DecisionProfile: param.DecisionProfile,
|
||||||
|
LastUpdateTime: time.Now(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
res := a.mongo.Co(a.AdvicerClientMongo).FindOneAndUpdate(ctx, filter, update)
|
||||||
|
return res.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceClientBiz) List(ctx context.Context, param *entitys.AdvicerClientListReq) (list []mongo_model.AdvicerClientMongo, err error) {
|
||||||
|
filter := bson.M{}
|
||||||
|
// 1. advicer_id 条件
|
||||||
|
if param.AdvicerId != 0 {
|
||||||
|
filter["AdvicerId"] = param.AdvicerId
|
||||||
|
}
|
||||||
|
|
||||||
|
if param.ProjectId != 0 {
|
||||||
|
filter["projectId"] = param.ProjectId
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. _id 条件
|
||||||
|
if len(param.Id) != 0 {
|
||||||
|
objectID, err := primitive.ObjectIDFromHex(param.Id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("ID转换失败: %w", err)
|
||||||
|
}
|
||||||
|
filter["_id"] = objectID
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor, err := a.mongo.Co(a.AdvicerClientMongo).Find(ctx, filter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// 遍历结果
|
||||||
|
for cursor.Next(ctx) {
|
||||||
|
var advicerVersion mongo_model.AdvicerClientMongo
|
||||||
|
if err := cursor.Decode(&advicerVersion); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
list = append(list, advicerVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cursor.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return list, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceClientBiz) Del(ctx context.Context, param *entitys.AdvicerClientDelReq) (err error) {
|
||||||
|
filter := bson.M{}
|
||||||
|
|
||||||
|
if len(param.Id) != 0 {
|
||||||
|
objectID, err := primitive.ObjectIDFromHex(param.Id)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ID转换失败: %w", err)
|
||||||
|
}
|
||||||
|
filter["_id"] = objectID
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = a.mongo.Co(a.AdvicerClientMongo).DeleteOne(ctx, filter)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceClientBiz) Info(ctx context.Context, param *entitys.AdvicerClientInfoReq) (info mongo_model.AdvicerClientMongo, err error) {
|
||||||
|
filter := bson.M{}
|
||||||
|
|
||||||
|
if len(param.Id) != 0 {
|
||||||
|
objectID, err := primitive.ObjectIDFromHex(param.Id)
|
||||||
|
if err != nil {
|
||||||
|
return info, fmt.Errorf("ID转换失败: %w", err)
|
||||||
|
|
||||||
|
}
|
||||||
|
filter["_id"] = objectID
|
||||||
|
}
|
||||||
|
|
||||||
|
res := a.mongo.Co(a.AdvicerClientMongo).FindOne(ctx, filter)
|
||||||
|
if res.Err() != nil {
|
||||||
|
return info, res.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = res.Decode(&info); err != nil {
|
||||||
|
return info, err
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,187 @@
|
||||||
|
package biz
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ai_scheduler/internal/biz/llm_service/third_party"
|
||||||
|
"ai_scheduler/internal/data/mongo_model"
|
||||||
|
"ai_scheduler/internal/pkg"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
|
||||||
|
"github.com/volcengine/volcengine-go-sdk/volcengine"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AdviceFileBiz struct {
|
||||||
|
hsyq *third_party.Hsyq
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdviceFileBiz(hsyq *third_party.Hsyq) *AdviceFileBiz {
|
||||||
|
return &AdviceFileBiz{
|
||||||
|
hsyq: hsyq,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
key = "236ba4b6-9daa-4755-b22f-2fd274cd223a"
|
||||||
|
fileModel = "doubao-seed-1-8-251228"
|
||||||
|
jsonModel = "doubao-seed-1-6-flash-250828"
|
||||||
|
)
|
||||||
|
|
||||||
|
var DataMap = map[string]mongo_model.AdviceData{
|
||||||
|
"dialectFeatures": &mongo_model.DialectFeatures{},
|
||||||
|
"sentencePatterns": &mongo_model.SentencePatterns{},
|
||||||
|
"personalityTags": &mongo_model.PersonalityTags{},
|
||||||
|
"toneTags": &mongo_model.ToneTags{},
|
||||||
|
"signatureDialogues": &mongo_model.SignatureDialogues{},
|
||||||
|
"regionValue": &mongo_model.RegionValue{},
|
||||||
|
"competitionComparison": &mongo_model.CompetitionComparison{},
|
||||||
|
"coreSellingPoints": &mongo_model.CoreSellingPoints{},
|
||||||
|
"supportingFacilities": &mongo_model.SupportingFacilities{},
|
||||||
|
"developerBacking": &mongo_model.DeveloperBacking{},
|
||||||
|
"needsMining": &mongo_model.NeedsMining{},
|
||||||
|
"painPointResponse": &mongo_model.PainPointResponse{},
|
||||||
|
"valueBuilding": &mongo_model.ValueBuilding{},
|
||||||
|
"closingTechniques": &mongo_model.ClosingTechniques{},
|
||||||
|
"communicationRhythm": &mongo_model.CommunicationRhythm{},
|
||||||
|
"customer": &mongo_model.Customer{},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceFileBiz) WordAna(ctx context.Context, wordContent string) (map[mongo_model.AdviceRole]map[string]mongo_model.AdviceData, error) {
|
||||||
|
timeSte := time.Now().Format("200601021504")
|
||||||
|
dir := "./cache/" + timeSte
|
||||||
|
os.Mkdir(dir, 0755)
|
||||||
|
//获取示例
|
||||||
|
examples := a.getAllExamples()
|
||||||
|
|
||||||
|
//构建提示词
|
||||||
|
prompt := a.buildSimplePrompt(wordContent, examples)
|
||||||
|
os.WriteFile(dir+"/requset.json", []byte(prompt), 0644)
|
||||||
|
|
||||||
|
//llm提取信息
|
||||||
|
anaContent, err := a.callLlm(ctx, prompt, fileModel)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
os.WriteFile(dir+"/res.json", []byte(anaContent), 0644)
|
||||||
|
|
||||||
|
//格式整理
|
||||||
|
data, err := a.parseResponse(ctx, []byte(anaContent))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//组装数据
|
||||||
|
resData := a.cateData(data)
|
||||||
|
os.WriteFile("./cache/"+timeSte+"/extracted.json", pkg.JsonByteIgonErr(resData), 0644)
|
||||||
|
return resData, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceFileBiz) cateData(data map[string]mongo_model.AdviceData) map[mongo_model.AdviceRole]map[string]mongo_model.AdviceData {
|
||||||
|
var res = make(map[mongo_model.AdviceRole]map[string]mongo_model.AdviceData)
|
||||||
|
for k, v := range data {
|
||||||
|
if _, ok := res[v.Role()]; !ok {
|
||||||
|
res[v.Role()] = make(map[string]mongo_model.AdviceData)
|
||||||
|
}
|
||||||
|
res[v.Role()][k] = v
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceFileBiz) parseResponse(ctx context.Context, responseByte []byte) (resultOutPut map[string]mongo_model.AdviceData, err error) {
|
||||||
|
//只尝试修复一次
|
||||||
|
if isValid := json.Valid(responseByte); !isValid {
|
||||||
|
responseByte, err = a.fixJson(ctx, responseByte)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("json格式错误,修复失败:%s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isValid := json.Valid(responseByte); !isValid {
|
||||||
|
return nil, fmt.Errorf("json格式错误")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
result map[string]interface{}
|
||||||
|
)
|
||||||
|
|
||||||
|
resultOutPut = make(map[string]mongo_model.AdviceData)
|
||||||
|
if err = json.Unmarshal(responseByte, &result); err != nil {
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for k, v := range result {
|
||||||
|
if _, ok := DataMap[k]; !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var vbyte []byte
|
||||||
|
if vbyte, err = json.Marshal(v); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
newData := DataMap[k].Copy()
|
||||||
|
|
||||||
|
if err = json.Unmarshal(vbyte, newData); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resultOutPut[k] = newData
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceFileBiz) fixJson(ctx context.Context, json []byte) ([]byte, error) {
|
||||||
|
prompt := "你是一个专业的JSON修复专家。请帮我修复以下错误的JSON格式。\n\n要求:\n1. 保持原有数据的结构和内容不变\n2. 修复JSON语法错误\n3. 输出格式化的正确JSON\n4. 简要说明修复了哪些问题\n\n错误的JSON:\n" + string(json) + "\n\n请直接输出修复后的JSON。"
|
||||||
|
call, err := a.callLlm(ctx, prompt, jsonModel)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(call), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceFileBiz) callLlm(ctx context.Context, prompt string, modelName string) (string, error) {
|
||||||
|
var message = make([]*model.ChatCompletionMessage, 1)
|
||||||
|
message[0] = &model.ChatCompletionMessage{
|
||||||
|
Role: model.ChatMessageRoleUser,
|
||||||
|
Content: &model.ChatCompletionMessageContent{
|
||||||
|
StringValue: volcengine.String(prompt),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
res, err := a.hsyq.RequestHsyq(ctx, key, modelName, message)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return *res.Choices[0].Message.Content.StringValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceFileBiz) getAllExamples() map[string]mongo_model.AdviceData {
|
||||||
|
return DataMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceFileBiz) buildSimplePrompt(wordContent string, examples map[string]mongo_model.AdviceData) string {
|
||||||
|
// 最简单的提示词模板
|
||||||
|
template := `分析以下房地产销售对话,按指定格式提取信息:
|
||||||
|
|
||||||
|
对话内容:
|
||||||
|
%s
|
||||||
|
|
||||||
|
请按照以下` + fmt.Sprintf("%d", len(examples)) + `个格式生成JSON数据,key为格式名称,value为对应值:
|
||||||
|
|
||||||
|
%s
|
||||||
|
|
||||||
|
输出要求:
|
||||||
|
1. 所有内容必须严格基于提供的对话原文,不得编造(重要!)
|
||||||
|
2. 每个结构体一个JSON对象
|
||||||
|
3. 严格按照示例格式
|
||||||
|
4. 将上述生成的` + fmt.Sprintf("%d", len(examples)) + `个JSON对象,json不需要有可读性,不要有特殊符号,比如"\n",用map[string]json来包裹所有json对象:{"SupportingFacilities":{...},"SignatureDialogues":[{...},{...}]}`
|
||||||
|
// 构建格式部分
|
||||||
|
var formats strings.Builder
|
||||||
|
for name, example := range examples {
|
||||||
|
formats.WriteString(fmt.Sprintf("=== %s (%s:%s)===\n示例:%s\n\n", name, mongo_model.RoleDesc[example.Role()], example.Desc(), example.Example()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf(template, wordContent, formats.String())
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
package biz
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ai_scheduler/internal/data/mongo_model"
|
||||||
|
"ai_scheduler/internal/entitys"
|
||||||
|
"ai_scheduler/internal/pkg"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AdviceProjectBiz struct {
|
||||||
|
AdvicerProjectMongo *mongo_model.AdvicerProjectMongo
|
||||||
|
mongo *pkg.Mongo
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdviceProjectBiz(
|
||||||
|
advicerProjectMongo *mongo_model.AdvicerProjectMongo,
|
||||||
|
mongo *pkg.Mongo,
|
||||||
|
) *AdviceProjectBiz {
|
||||||
|
return &AdviceProjectBiz{
|
||||||
|
AdvicerProjectMongo: advicerProjectMongo,
|
||||||
|
mongo: mongo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceProjectBiz) Add(ctx context.Context, param *entitys.AdvicerProjectAddReq) (err error) {
|
||||||
|
|
||||||
|
_, err = a.mongo.Co(a.AdvicerProjectMongo).InsertOne(ctx, &mongo_model.AdvicerProjectMongo{
|
||||||
|
ProjectId: param.ProjectId,
|
||||||
|
ProjectInfo: param.ProjectInfo,
|
||||||
|
RegionValue: param.RegionValue,
|
||||||
|
CompetitionComparison: param.CompetitionComparison,
|
||||||
|
CoreSellingPoints: param.CoreSellingPoints,
|
||||||
|
SupportingFacilities: param.SupportingFacilities,
|
||||||
|
DeveloperBacking: param.DeveloperBacking,
|
||||||
|
LastUpdateTime: time.Now(),
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceProjectBiz) Update(ctx context.Context, param *entitys.AdvicerrProjectUpdateReq) (err error) {
|
||||||
|
filter := bson.M{}
|
||||||
|
if len(param.Id) == 0 {
|
||||||
|
return errors.New("ID不能为空")
|
||||||
|
}
|
||||||
|
objectID, err := primitive.ObjectIDFromHex(param.Id)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ID转换失败: %w", err)
|
||||||
|
}
|
||||||
|
filter["_id"] = objectID
|
||||||
|
update := bson.M{
|
||||||
|
"$set": &mongo_model.AdvicerProjectMongo{
|
||||||
|
ProjectId: param.ProjectId,
|
||||||
|
RegionValue: param.RegionValue,
|
||||||
|
CompetitionComparison: param.CompetitionComparison,
|
||||||
|
CoreSellingPoints: param.CoreSellingPoints,
|
||||||
|
SupportingFacilities: param.SupportingFacilities,
|
||||||
|
DeveloperBacking: param.DeveloperBacking,
|
||||||
|
LastUpdateTime: time.Now(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
res := a.mongo.Co(a.AdvicerProjectMongo).FindOneAndUpdate(ctx, filter, update)
|
||||||
|
return res.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceProjectBiz) Info(ctx context.Context, param *entitys.AdvicerProjectInfoReq) (info mongo_model.AdvicerProjectMongo, err error) {
|
||||||
|
filter := bson.M{}
|
||||||
|
|
||||||
|
if param.ProjectId != 0 {
|
||||||
|
filter["projectId"] = param.ProjectId
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. _id 条件
|
||||||
|
if len(param.Id) != 0 {
|
||||||
|
objectID, err := primitive.ObjectIDFromHex(param.Id)
|
||||||
|
if err != nil {
|
||||||
|
return info, fmt.Errorf("ID转换失败: %w", err)
|
||||||
|
}
|
||||||
|
filter["_id"] = objectID
|
||||||
|
}
|
||||||
|
|
||||||
|
res := a.mongo.Co(a.AdvicerProjectMongo).FindOne(ctx, filter)
|
||||||
|
if res.Err() != nil {
|
||||||
|
return info, res.Err()
|
||||||
|
}
|
||||||
|
// 遍历结果
|
||||||
|
|
||||||
|
if err := res.Decode(&info); err != nil {
|
||||||
|
return info, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,164 @@
|
||||||
|
package biz
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ai_scheduler/internal/data/mongo_model"
|
||||||
|
"ai_scheduler/internal/entitys"
|
||||||
|
"ai_scheduler/internal/pkg"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AdviceSkillBiz struct {
|
||||||
|
AdvicerTalkSkillMongo *mongo_model.AdvicerTalkSkillMongo
|
||||||
|
mongo *pkg.Mongo
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdviceSkillBiz(
|
||||||
|
advicerTalkSkillMongo *mongo_model.AdvicerTalkSkillMongo,
|
||||||
|
mongo *pkg.Mongo,
|
||||||
|
) *AdviceSkillBiz {
|
||||||
|
return &AdviceSkillBiz{
|
||||||
|
AdvicerTalkSkillMongo: advicerTalkSkillMongo,
|
||||||
|
mongo: mongo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceSkillBiz) VersionAdd(ctx context.Context, param *entitys.AdvicerTalkSkillAddReq) (err error) {
|
||||||
|
|
||||||
|
_, err = a.mongo.Co(a.AdvicerTalkSkillMongo).InsertOne(ctx, &mongo_model.AdvicerTalkSkillMongo{
|
||||||
|
ProjectId: param.ProjectId,
|
||||||
|
AdvicerId: param.AdvicerId,
|
||||||
|
Desc: param.Desc,
|
||||||
|
NeedsMining: param.NeedsMining,
|
||||||
|
PainPointResponse: param.PainPointResponse,
|
||||||
|
ValueBuilding: param.ValueBuilding,
|
||||||
|
ClosingTechniques: param.ClosingTechniques,
|
||||||
|
CommunicationRhythm: param.CommunicationRhythm,
|
||||||
|
LastUpdateTime: time.Now(),
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceSkillBiz) VersionUpdate(ctx context.Context, param *entitys.AdvicerTalkSkillUpdateReq) (err error) {
|
||||||
|
filter := bson.M{}
|
||||||
|
if len(param.Id) == 0 {
|
||||||
|
return errors.New("ID不能为空")
|
||||||
|
}
|
||||||
|
objectID, err := primitive.ObjectIDFromHex(param.Id)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ID转换失败: %w", err)
|
||||||
|
}
|
||||||
|
filter["_id"] = objectID
|
||||||
|
update := bson.M{
|
||||||
|
"$set": &mongo_model.AdvicerTalkSkillMongo{
|
||||||
|
AdvicerId: param.AdvicerId,
|
||||||
|
ProjectId: param.ProjectId,
|
||||||
|
Desc: param.Desc,
|
||||||
|
NeedsMining: param.NeedsMining,
|
||||||
|
PainPointResponse: param.PainPointResponse,
|
||||||
|
ValueBuilding: param.ValueBuilding,
|
||||||
|
ClosingTechniques: param.ClosingTechniques,
|
||||||
|
CommunicationRhythm: param.CommunicationRhythm,
|
||||||
|
LastUpdateTime: time.Now(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
res := a.mongo.Co(a.AdvicerTalkSkillMongo).FindOneAndUpdate(ctx, filter, update)
|
||||||
|
return res.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceSkillBiz) VersionList(ctx context.Context, param *entitys.AdvicerTalkSkillListReq) (list []mongo_model.AdvicerTalkSkillMongo, err error) {
|
||||||
|
filter := bson.M{}
|
||||||
|
// 1. advicer_id 条件
|
||||||
|
if param.AdvicerId != 0 {
|
||||||
|
filter["AdvicerId"] = param.AdvicerId
|
||||||
|
}
|
||||||
|
|
||||||
|
if param.ProjectId != 0 {
|
||||||
|
filter["projectId"] = param.ProjectId
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. _id 条件
|
||||||
|
if len(param.Id) != 0 {
|
||||||
|
objectID, err := primitive.ObjectIDFromHex(param.Id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("ID转换失败: %w", err)
|
||||||
|
}
|
||||||
|
filter["_id"] = objectID
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. version_desc 模糊查询
|
||||||
|
if len(param.Desc) != 0 {
|
||||||
|
// 正确的方式:指定字段名
|
||||||
|
filter["desc"] = bson.M{
|
||||||
|
"$regex": primitive.Regex{
|
||||||
|
Pattern: param.Desc,
|
||||||
|
Options: "i",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor, err := a.mongo.Co(a.AdvicerTalkSkillMongo).Find(ctx, filter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// 遍历结果
|
||||||
|
for cursor.Next(ctx) {
|
||||||
|
var advicerVersion mongo_model.AdvicerTalkSkillMongo
|
||||||
|
if err := cursor.Decode(&advicerVersion); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
list = append(list, advicerVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cursor.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return list, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceSkillBiz) VersionDel(ctx context.Context, param *entitys.AdvicerTalkSkillDelReq) (err error) {
|
||||||
|
filter := bson.M{}
|
||||||
|
|
||||||
|
if len(param.Id) != 0 {
|
||||||
|
objectID, err := primitive.ObjectIDFromHex(param.Id)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ID转换失败: %w", err)
|
||||||
|
}
|
||||||
|
filter["_id"] = objectID
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = a.mongo.Co(a.AdvicerTalkSkillMongo).DeleteOne(ctx, filter)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceSkillBiz) Info(ctx context.Context, param *entitys.AdvicerTalkSkillInfoReq) (info mongo_model.AdvicerTalkSkillMongo, err error) {
|
||||||
|
filter := bson.M{}
|
||||||
|
|
||||||
|
if len(param.Id) != 0 {
|
||||||
|
objectID, err := primitive.ObjectIDFromHex(param.Id)
|
||||||
|
if err != nil {
|
||||||
|
return info, fmt.Errorf("ID转换失败: %w", err)
|
||||||
|
}
|
||||||
|
filter["_id"] = objectID
|
||||||
|
}
|
||||||
|
res := a.mongo.Co(a.AdvicerTalkSkillMongo).FindOne(ctx, filter)
|
||||||
|
if res.Err() != nil {
|
||||||
|
return info, err
|
||||||
|
}
|
||||||
|
// 遍历结果
|
||||||
|
|
||||||
|
if err = res.Decode(&info); err != nil {
|
||||||
|
return info, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
package third_party
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2/log"
|
||||||
|
"github.com/volcengine/volcengine-go-sdk/service/arkruntime"
|
||||||
|
"github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
|
||||||
|
"github.com/volcengine/volcengine-go-sdk/service/arkruntime/model/responses"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Hsyq struct {
|
||||||
|
mapClient map[string]*arkruntime.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHsyq() *Hsyq {
|
||||||
|
return &Hsyq{
|
||||||
|
mapClient: make(map[string]*arkruntime.Client),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hsyq) getClient(key string) *arkruntime.Client {
|
||||||
|
var client *arkruntime.Client
|
||||||
|
if _, ok := h.mapClient[key]; ok {
|
||||||
|
client = h.mapClient[key]
|
||||||
|
} else {
|
||||||
|
client = arkruntime.NewClientWithApiKey(
|
||||||
|
key,
|
||||||
|
arkruntime.WithRegion("cn-beijing"),
|
||||||
|
arkruntime.WithTimeout(2*time.Minute),
|
||||||
|
arkruntime.WithRetryTimes(2),
|
||||||
|
)
|
||||||
|
h.mapClient[key] = client
|
||||||
|
}
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
// 火山引擎
|
||||||
|
func (h *Hsyq) RequestHsyq(ctx context.Context, key string, modelName string, prompt []*model.ChatCompletionMessage) (model.ChatCompletionResponse, error) {
|
||||||
|
req := model.CreateChatCompletionRequest{
|
||||||
|
Model: modelName,
|
||||||
|
Messages: prompt,
|
||||||
|
Stream: new(bool),
|
||||||
|
Thinking: &model.Thinking{Type: model.ThinkingTypeDisabled},
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := h.getClient(key).CreateChatCompletion(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return model.ChatCompletionResponse{ID: ""}, err
|
||||||
|
}
|
||||||
|
log.Info("token用量:", resp.Usage.TotalTokens, "输入:", resp.Usage.PromptTokens, "输出:", resp.Usage.CompletionTokens)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hsyq) RequestHsyqJson(ctx context.Context, key string, modelName string, prompt []*responses.InputItem) (*responses.ResponseObject, error) {
|
||||||
|
req := responses.ResponsesRequest{
|
||||||
|
Model: modelName,
|
||||||
|
Input: &responses.ResponsesInput{
|
||||||
|
Union: &responses.ResponsesInput_ListValue{
|
||||||
|
ListValue: &responses.InputItemList{ListValue: prompt},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Stream: new(bool),
|
||||||
|
Thinking: &responses.ResponsesThinking{Type: responses.ThinkingType_disabled.Enum()},
|
||||||
|
Text: &responses.ResponsesText{Format: &responses.TextFormat{Type: responses.TextType_json_object}},
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := h.getClient(key).CreateResponses(ctx, &req)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
log.Info("token用量:", resp.Usage.TotalTokens)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ package biz
|
||||||
import (
|
import (
|
||||||
"ai_scheduler/internal/biz/do"
|
"ai_scheduler/internal/biz/do"
|
||||||
"ai_scheduler/internal/biz/llm_service"
|
"ai_scheduler/internal/biz/llm_service"
|
||||||
|
"ai_scheduler/internal/biz/llm_service/third_party"
|
||||||
|
|
||||||
"github.com/google/wire"
|
"github.com/google/wire"
|
||||||
)
|
)
|
||||||
|
|
@ -22,4 +23,11 @@ var ProviderSetBiz = wire.NewSet(
|
||||||
NewGroupConfigBiz,
|
NewGroupConfigBiz,
|
||||||
do.NewMacro,
|
do.NewMacro,
|
||||||
NewCallbackBiz,
|
NewCallbackBiz,
|
||||||
|
NewAdviceFileBiz,
|
||||||
|
third_party.NewHsyq,
|
||||||
|
NewAdviceAdvicerBiz,
|
||||||
|
NewAdviceSkillBiz,
|
||||||
|
NewAdviceProjectBiz,
|
||||||
|
NewAdviceClientBiz,
|
||||||
|
NewAdviceChatBiz,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ type Config struct {
|
||||||
Logging LoggingConfig `mapstructure:"logging"`
|
Logging LoggingConfig `mapstructure:"logging"`
|
||||||
Redis Redis `mapstructure:"redis"`
|
Redis Redis `mapstructure:"redis"`
|
||||||
DB DB `mapstructure:"db"`
|
DB DB `mapstructure:"db"`
|
||||||
|
Mongo Mongo `mapstructure:"mongo"`
|
||||||
Oss Oss `mapstructure:"oss"`
|
Oss Oss `mapstructure:"oss"`
|
||||||
DefaultPrompt SysPrompt `mapstructure:"default_prompt"`
|
DefaultPrompt SysPrompt `mapstructure:"default_prompt"`
|
||||||
PermissionConfig PermissionConfig `mapstructure:"permissionConfig"`
|
PermissionConfig PermissionConfig `mapstructure:"permissionConfig"`
|
||||||
|
|
@ -188,7 +189,8 @@ type Redis struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type DB struct {
|
type DB struct {
|
||||||
Driver string `mapstructure:"driver"`
|
Driver string `mapstructure:"driver"`
|
||||||
|
|
||||||
Source string `mapstructure:"source"`
|
Source string `mapstructure:"source"`
|
||||||
MaxIdle int32 `mapstructure:"maxIdle"`
|
MaxIdle int32 `mapstructure:"maxIdle"`
|
||||||
MaxOpen int32 `mapstructure:"maxOpen"`
|
MaxOpen int32 `mapstructure:"maxOpen"`
|
||||||
|
|
@ -196,6 +198,16 @@ type DB struct {
|
||||||
IsDebug bool `mapstructure:"isDebug"`
|
IsDebug bool `mapstructure:"isDebug"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Mongo struct {
|
||||||
|
Source string `mapstructure:"source"`
|
||||||
|
DataBase string `mapstructure:"dataBase"`
|
||||||
|
MaxPoolSize uint64 `mapstructure:"maxPoolSize"`
|
||||||
|
MinPoolSize uint64 `mapstructure:"minPoolSize"`
|
||||||
|
MaxConnIdleTime int32 `mapstructure:"maxConnIdleTime"`
|
||||||
|
ConnectTimeout int32 `mapstructure:"connectTimeout"`
|
||||||
|
SocketTimeout int32 `mapstructure:"socketTimeout"`
|
||||||
|
}
|
||||||
|
|
||||||
// Oss 阿里云OSS配置
|
// Oss 阿里云OSS配置
|
||||||
type Oss struct {
|
type Oss struct {
|
||||||
AccessKey string `mapstructure:"access_key"`
|
AccessKey string `mapstructure:"access_key"`
|
||||||
|
|
@ -354,7 +366,7 @@ func LoadConfigWithEnv() (*Config, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
viper.SetConfigFile(modularDir + "/config/config_env.yaml")
|
viper.SetConfigFile(modularDir + "/config/config_test.yaml")
|
||||||
viper.SetConfigType("yaml")
|
viper.SetConfigType("yaml")
|
||||||
// 读取配置文件
|
// 读取配置文件
|
||||||
if err := viper.ReadInConfig(); err != nil {
|
if err := viper.ReadInConfig(); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package impl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ai_scheduler/internal/data/model"
|
||||||
|
"ai_scheduler/tmpl/dataTemp"
|
||||||
|
"ai_scheduler/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AdviceAdvicerImpl struct {
|
||||||
|
dataTemp.DataTemp
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdviceAdvicerImpl(db *utils.Db) *AdviceAdvicerImpl {
|
||||||
|
return &AdviceAdvicerImpl{
|
||||||
|
DataTemp: *dataTemp.NewDataTemp(db, new(model.AiAdviceAdvicer)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package impl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ai_scheduler/internal/data/model"
|
||||||
|
"ai_scheduler/tmpl/dataTemp"
|
||||||
|
"ai_scheduler/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AdviceAdvicerVersionImpl struct {
|
||||||
|
dataTemp.DataTemp
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdviceAdvicerVersionImpl(db *utils.Db) *AdviceAdvicerVersionImpl {
|
||||||
|
return &AdviceAdvicerVersionImpl{
|
||||||
|
DataTemp: *dataTemp.NewDataTemp(db, new(model.AiAdviceAdvicerVersion)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package impl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ai_scheduler/internal/data/model"
|
||||||
|
"ai_scheduler/tmpl/dataTemp"
|
||||||
|
"ai_scheduler/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AdviceClientImpl struct {
|
||||||
|
dataTemp.DataTemp
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdviceClientImpl(db *utils.Db) *AdviceClientImpl {
|
||||||
|
return &AdviceClientImpl{
|
||||||
|
DataTemp: *dataTemp.NewDataTemp(db, new(model.AiAdviceClient)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package impl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ai_scheduler/internal/data/model"
|
||||||
|
"ai_scheduler/tmpl/dataTemp"
|
||||||
|
"ai_scheduler/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AdviceProjectImpl struct {
|
||||||
|
dataTemp.DataTemp
|
||||||
|
BaseRepository[model.AiTask]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdviceProjectImpl(db *utils.Db) *AdviceProjectImpl {
|
||||||
|
return &AdviceProjectImpl{
|
||||||
|
DataTemp: *dataTemp.NewDataTemp(db, new(model.AiAdviceProject)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package impl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ai_scheduler/internal/data/model"
|
||||||
|
"ai_scheduler/tmpl/dataTemp"
|
||||||
|
"ai_scheduler/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AdviceTalkImpl struct {
|
||||||
|
dataTemp.DataTemp
|
||||||
|
BaseRepository[model.AiTask]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdviceTalkImpl(db *utils.Db) *AdviceTalkImpl {
|
||||||
|
return &AdviceTalkImpl{
|
||||||
|
DataTemp: *dataTemp.NewDataTemp(db, new(model.AiAdviceTalk)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -19,4 +19,9 @@ var ProviderImpl = wire.NewSet(
|
||||||
NewBotGroupQywxImpl,
|
NewBotGroupQywxImpl,
|
||||||
NewReportDailyCacheImpl,
|
NewReportDailyCacheImpl,
|
||||||
NewIssueImpl,
|
NewIssueImpl,
|
||||||
|
NewAdviceAdvicerImpl,
|
||||||
|
NewAdviceProjectImpl,
|
||||||
|
NewAdviceTalkImpl,
|
||||||
|
NewAdviceAdvicerVersionImpl,
|
||||||
|
NewAdviceClientImpl,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
const TableNameAiAdviceAdvicer = "ai_advice_advicer"
|
||||||
|
|
||||||
|
// AiAdviceAdvicer mapped from table <ai_advice_advicer>
|
||||||
|
type AiAdviceAdvicer struct {
|
||||||
|
AdvicerID int32 `gorm:"column:advicer_id;primaryKey;autoIncrement:true" json:"advicer_id"`
|
||||||
|
ProjectID int32 `gorm:"column:project_id;not null" json:"project_id"`
|
||||||
|
Name string `gorm:"column:name;not null;comment:姓名" json:"name"` // 姓名
|
||||||
|
Birth time.Time `gorm:"column:birth;not null;comment:用户名称" json:"birth"` // 用户名称
|
||||||
|
Gender int32 `gorm:"column:gender;not null;comment:1:男,2:女" json:"gender"` // 1:男,2:女
|
||||||
|
WorkingYears int32 `gorm:"column:working_years;not null;default:1;comment:工作年限" json:"working_years"` // 工作年限
|
||||||
|
CreateAt time.Time `gorm:"column:create_at;default:CURRENT_TIMESTAMP" json:"create_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName AiAdviceAdvicer's table name
|
||||||
|
func (*AiAdviceAdvicer) TableName() string {
|
||||||
|
return TableNameAiAdviceAdvicer
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
// 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 (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AiAdviceAdvicerEntity struct {
|
||||||
|
Name string `json:"name"` // 姓名
|
||||||
|
Birth string `json:"birth"` // 用户名称
|
||||||
|
Gender string `json:"gender"` // 1:男,2:女
|
||||||
|
WorkingYears string `json:"working_years"` // 工作年限
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AiAdviceAdvicer) Entity() *AiAdviceAdvicerEntity {
|
||||||
|
var (
|
||||||
|
gender string
|
||||||
|
)
|
||||||
|
switch a.Gender {
|
||||||
|
case 1:
|
||||||
|
gender = "男"
|
||||||
|
case 2:
|
||||||
|
gender = "女"
|
||||||
|
default:
|
||||||
|
gender = "未知"
|
||||||
|
}
|
||||||
|
return &AiAdviceAdvicerEntity{
|
||||||
|
Name: a.Name,
|
||||||
|
Birth: a.Birth.Format(time.DateOnly),
|
||||||
|
Gender: gender,
|
||||||
|
WorkingYears: fmt.Sprintf("%d年", a.WorkingYears),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
const TableNameAiAdviceAdvicerVersion = "ai_advice_advicer_version"
|
||||||
|
|
||||||
|
// AiAdviceAdvicerVersion mapped from table <ai_advice_advicer_version>
|
||||||
|
type AiAdviceAdvicerVersion struct {
|
||||||
|
VersionID int32 `gorm:"column:version_id;primaryKey;autoIncrement:true" json:"version_id"`
|
||||||
|
AdvicerID int32 `gorm:"column:advicer_id;not null" json:"advicer_id"`
|
||||||
|
VersionDesc string `gorm:"column:version_desc;not null;comment:版本名称" json:"version_desc"` // 版本名称
|
||||||
|
DialectFeatures string `gorm:"column:dialect_features;not null;comment:语言风格" json:"dialect_features"` // 语言风格
|
||||||
|
SentencePatterns string `gorm:"column:sentence_patterns;comment:句子模式" json:"sentence_patterns"` // 句子模式
|
||||||
|
ToneTags string `gorm:"column:tone_tags;comment:语气标签" json:"tone_tags"` // 语气标签
|
||||||
|
PersonalityTags string `gorm:"column:personality_tags;not null;comment:个性标签" json:"personality_tags"` // 个性标签
|
||||||
|
SignatureDialogues string `gorm:"column:signature_dialogues;comment:代表性对话示例" json:"signature_dialogues"` // 代表性对话示例
|
||||||
|
CreateAt time.Time `gorm:"column:create_at;default:CURRENT_TIMESTAMP" json:"create_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName AiAdviceAdvicerVersion's table name
|
||||||
|
func (*AiAdviceAdvicerVersion) TableName() string {
|
||||||
|
return TableNameAiAdviceAdvicerVersion
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
const TableNameAiAdviceClient = "ai_advice_client"
|
||||||
|
|
||||||
|
// AiAdviceClient mapped from table <ai_advice_client>
|
||||||
|
type AiAdviceClient struct {
|
||||||
|
ClientID int32 `gorm:"column:client_id;primaryKey;autoIncrement:true" json:"client_id"`
|
||||||
|
PersonalInfo string `gorm:"column:personal_info;comment:区域价值话术库" json:"personal_info"` // 区域价值话术库
|
||||||
|
PurchasePurpose string `gorm:"column:purchase_purpose;comment:竞品对比话术" json:"purchase_purpose"` // 竞品对比话术
|
||||||
|
CoreDemands string `gorm:"column:core_demands;comment:项目核心卖点" json:"core_demands"` // 项目核心卖点
|
||||||
|
Concerns string `gorm:"column:concerns;comment:配套体系" json:"concerns"` // 配套体系
|
||||||
|
DecisionProfile string `gorm:"column:decision_profile;comment:开发商背书" json:"decision_profile"` // 开发商背书
|
||||||
|
CreateAt time.Time `gorm:"column:create_at;default:CURRENT_TIMESTAMP" json:"create_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName AiAdviceClient's table name
|
||||||
|
func (*AiAdviceClient) TableName() string {
|
||||||
|
return TableNameAiAdviceClient
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
const TableNameAiAdviceProject = "ai_advice_project"
|
||||||
|
|
||||||
|
// AiAdviceProject mapped from table <ai_advice_project>
|
||||||
|
type AiAdviceProject struct {
|
||||||
|
ProjectID int32 `gorm:"column:project_id;primaryKey;autoIncrement:true" json:"project_id"`
|
||||||
|
Name string `gorm:"column:name;not null;comment:姓名" json:"name"` // 姓名
|
||||||
|
RegionValue string `gorm:"column:region_value;comment:区域价值话术库" json:"region_value"` // 区域价值话术库
|
||||||
|
CompetitionComparison string `gorm:"column:competition_comparison;comment:竞品对比话术" json:"competition_comparison"` // 竞品对比话术
|
||||||
|
CoreSellingPoints string `gorm:"column:core_selling_points;comment:项目核心卖点" json:"core_selling_points"` // 项目核心卖点
|
||||||
|
SupportingFacilities string `gorm:"column:supporting_facilities;comment:配套体系" json:"supporting_facilities"` // 配套体系
|
||||||
|
DeveloperBacking string `gorm:"column:developer_backing;comment:开发商背书" json:"developer_backing"` // 开发商背书
|
||||||
|
CreateAt time.Time `gorm:"column:create_at;default:CURRENT_TIMESTAMP" json:"create_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName AiAdviceProject's table name
|
||||||
|
func (*AiAdviceProject) TableName() string {
|
||||||
|
return TableNameAiAdviceProject
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
const TableNameAiAdviceTalk = "ai_advice_talk"
|
||||||
|
|
||||||
|
// AiAdviceTalk mapped from table <ai_advice_talk>
|
||||||
|
type AiAdviceTalk struct {
|
||||||
|
TalkID int32 `gorm:"column:talk_id;primaryKey;autoIncrement:true" json:"talk_id"`
|
||||||
|
NeedsMining string `gorm:"column:needs_mining;comment:需求挖掘话术" json:"needs_mining"` // 需求挖掘话术
|
||||||
|
PainPointResponse string `gorm:"column:pain_point_response;comment:痛点应对策略" json:"pain_point_response"` // 痛点应对策略
|
||||||
|
ValueBuilding string `gorm:"column:value_building;comment:价值塑造技巧" json:"value_building"` // 价值塑造技巧
|
||||||
|
ClosingTechniques string `gorm:"column:closing_techniques;comment:促单话术" json:"closing_techniques"` // 促单话术
|
||||||
|
CommunicationRhythm string `gorm:"column:communication_rhythm;comment:沟通节奏控制" json:"communication_rhythm"` // 沟通节奏控制
|
||||||
|
CreateAt time.Time `gorm:"column:create_at;default:CURRENT_TIMESTAMP" json:"create_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName AiAdviceTalk's table name
|
||||||
|
func (*AiAdviceTalk) TableName() string {
|
||||||
|
return TableNameAiAdviceTalk
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
package mongo_model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AdvicerClientMongo struct {
|
||||||
|
ProjectId int32 `json:"projectId" bson:"projectId"`
|
||||||
|
AdvicerId int32 `json:"advicerId" bson:"advicerId"`
|
||||||
|
PersonalInfo PersonalInfo `json:"personalInfo" bson:"personalInfo"`
|
||||||
|
PurchasePurpose PurchasePurpose `json:"purchasePurpose" bson:"purchasePurpose"`
|
||||||
|
CoreDemands CoreDemands `json:"coreDemands" bson:"coreDemands"`
|
||||||
|
Concerns []string `json:"concerns" bson:"concerns"`
|
||||||
|
DecisionProfile []string `json:"decisionProfile" bson:"decisionProfile"`
|
||||||
|
LastUpdateTime time.Time `json:"lastUpdateTime" bson:"lastUpdateTime"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdvicerClientMongo() *AdvicerClientMongo {
|
||||||
|
return &AdvicerClientMongo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdvicerClientMongo) MongoTableName() string {
|
||||||
|
return "advicer_client"
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerClientMongoEntity struct {
|
||||||
|
PersonalInfo PersonalInfo `json:"personalInfo"`
|
||||||
|
PurchasePurpose PurchasePurpose `json:"purchasePurpose"`
|
||||||
|
CoreDemands CoreDemands `json:"coreDemands"`
|
||||||
|
Concerns []string `json:"concerns"`
|
||||||
|
DecisionProfile []string `json:"decisionProfile"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdvicerClientMongo) Entity() *AdvicerClientMongoEntity {
|
||||||
|
return &AdvicerClientMongoEntity{
|
||||||
|
PersonalInfo: a.PersonalInfo,
|
||||||
|
PurchasePurpose: a.PurchasePurpose,
|
||||||
|
CoreDemands: a.CoreDemands,
|
||||||
|
Concerns: a.Concerns,
|
||||||
|
DecisionProfile: a.DecisionProfile,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Customer 客户信息
|
||||||
|
type Customer []ClientInfo
|
||||||
|
type ClientInfo struct {
|
||||||
|
// 个人信息
|
||||||
|
PersonalInfo PersonalInfo `json:"personalInfo"`
|
||||||
|
|
||||||
|
// 购房目的
|
||||||
|
PurchasePurpose PurchasePurpose `json:"purchasePurpose"`
|
||||||
|
|
||||||
|
// 核心需求
|
||||||
|
CoreDemands CoreDemands `json:"coreDemands"`
|
||||||
|
|
||||||
|
// 关注点与顾虑
|
||||||
|
Concerns []string `json:"concerns"`
|
||||||
|
|
||||||
|
// 决策建议
|
||||||
|
DecisionProfile []string `json:"decisionProfile"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PersonalInfo struct {
|
||||||
|
Name string `json:"name"` // 姓氏
|
||||||
|
Gender string `json:"gender"` // 性别
|
||||||
|
Location string `json:"location"` // 来源地/当前居住地
|
||||||
|
IsFirstHome bool `json:"isFirstHome"` // 是否首套房
|
||||||
|
FamilyOrganize string `json:"familyOrganize"` // 家庭人数
|
||||||
|
}
|
||||||
|
|
||||||
|
type PurchasePurpose struct {
|
||||||
|
PrimaryPurpose string `json:"primaryPurpose"` // 主要目的
|
||||||
|
SecondaryPurpose string `json:"secondaryPurpose"` // 次要目的
|
||||||
|
DecisionMakers string `json:"decisionMakers"` // 决策人
|
||||||
|
}
|
||||||
|
|
||||||
|
type CoreDemands struct {
|
||||||
|
TotalBudget string `json:"totalBudget"` // 预算范围
|
||||||
|
PreferredLayout string `json:"preferredLayout"` // 偏好户型
|
||||||
|
CoreAppeal string `json:"coreAppeal"` // 核心述求
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Customer) Example() string {
|
||||||
|
return `[{"personalInfo":{"name":"唐","gender":"男","location":"成都北门","isFirstHome":true,"familyOrganize":"夫妻"},"purchasePurpose":{"primaryPurpose":"首次置业,解决自住","secondaryPurpose":"资产保值,未来可出租","decisionMakers":"夫妻双方"},"coreDemands":{"totalBudget":"350-400"","preferredLayout":"118㎡四房三卫双套房","coreAppeal":"在有限预算内满足家庭居住功能,确保房产保值"},"concerns":["总价超预算风险","板块保值能力"],"decisionProfile":["预算导向,严格控制总价","重点关注户型功能性和实用性"]},{"personalInfo":{"name":"冯女士","gender":"女","location":"","isFirstHome":false,"familyOrganize":"夫妻+1孩+父母同住"},"purchasePurpose":{"primaryPurpose":"改善居住条件","secondaryPurpose":"子女教育质量提升","decisionMakers":"夫妻双方需家庭共同商议]},"coreDemands":{"totalBudget":"400-500","preferredLayout":"118㎡四房三卫(非全景户型)","coreAppeal":"安静舒适、学区有保障的改善型住房"},"concerns":["临路噪音影响老人休息","学区质量和稳定性","社区小,绿化空间有限","得房率是否足够","二八板块学区对比"],"decisionProfile":["对噪音敏感,需要安静环境","重视教育资源配置","关注社区品质和舒适度","需要详细对比不同板块学区优势"]}]`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Customer) Copy() AdviceData {
|
||||||
|
return new(Customer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Customer) Role() AdviceRole {
|
||||||
|
return RoleClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Customer) Desc() string {
|
||||||
|
return "客户信息"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
package mongo_model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AdvicerProjectMongo struct {
|
||||||
|
ProjectId int32 `json:"projectId" bson:"projectId"`
|
||||||
|
ProjectInfo ProjectInfo `json:"projectInfo" bson:"projectInfo"`
|
||||||
|
RegionValue RegionValue `json:"regionValue" bson:"regionValue"`
|
||||||
|
CompetitionComparison CompetitionComparison `json:"competitionComparison" bson:"competitionComparison"`
|
||||||
|
CoreSellingPoints CoreSellingPoints `json:"coreSellingPoints" bson:"coreSellingPoints"`
|
||||||
|
SupportingFacilities SupportingFacilities `json:"supportingFacilities" bson:"supportingFacilities"`
|
||||||
|
DeveloperBacking DeveloperBacking `json:"developerBacking" bson:"developerBacking"`
|
||||||
|
LastUpdateTime time.Time `json:"lastUpdateTime" bson:"lastUpdateTime"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdvicerProjectMongo() *AdvicerProjectMongo {
|
||||||
|
return &AdvicerProjectMongo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdvicerProjectMongo) MongoTableName() string {
|
||||||
|
return "advicer_project"
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerProjectMongoEntity struct {
|
||||||
|
RegionValue RegionValue `json:"regionValue" bson:"regionValue"`
|
||||||
|
CompetitionComparison CompetitionComparison `json:"competitionComparison" bson:"competitionComparison"`
|
||||||
|
CoreSellingPoints CoreSellingPoints `json:"coreSellingPoints" bson:"coreSellingPoints"`
|
||||||
|
SupportingFacilities SupportingFacilities `json:"supportingFacilities" bson:"supportingFacilities"`
|
||||||
|
DeveloperBacking DeveloperBacking `json:"developerBacking" bson:"developerBacking"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdvicerProjectMongo) Entity() *AdvicerProjectMongoEntity {
|
||||||
|
return &AdvicerProjectMongoEntity{
|
||||||
|
RegionValue: a.RegionValue,
|
||||||
|
CompetitionComparison: a.CompetitionComparison,
|
||||||
|
CoreSellingPoints: a.CoreSellingPoints,
|
||||||
|
SupportingFacilities: a.SupportingFacilities,
|
||||||
|
DeveloperBacking: a.DeveloperBacking,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProjectInfo struct {
|
||||||
|
Name string `json:"projectName" bson:"projectName"`
|
||||||
|
Address string `json:"projectAddress" bson:"projectAddress"`
|
||||||
|
Area string `json:"area" bson:"area"`
|
||||||
|
HouseTypes []HouseType `json:"houseTypes" bson:"houseTypes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HouseType struct {
|
||||||
|
Name string `json:"name" bson:"name"`
|
||||||
|
BuildArea string `json:"buildArea" bson:"buildArea"`
|
||||||
|
InnerArea string `json:"innerArea" bson:"innerArea"`
|
||||||
|
UnitPrice string `json:"unitPrice" bson:"unitPrice"`
|
||||||
|
TotalPrice string `json:"totalPrice" bson:"totalPrice"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegionValue 区域价值话术库
|
||||||
|
type RegionValue map[string][]string
|
||||||
|
|
||||||
|
func (e *RegionValue) Example() string {
|
||||||
|
return `{"区位层级":["成华区2.5环内侧,这个位置真的稀缺","槐树店板块现在是成华区的number one板块","北接三板桥商圈,西靠万象城,东临火车东站","属于淮舜板块,万象城东的核心位置"],"地价论证":["我们地价19500,华晨府20400,棕榈也是2万+","2.5环内现在地价没有低于19000的","面粉贵了,面包不可能便宜"],"板块热度":["从21年新希望锦麟一品开始,这边全是高端盘","龙湖最高端的滨江系列在这里,新希望的锦麟系列也在这里","各大品牌开发商争相恐后都在这边拿地"],"发展规划":["槐树店板块是棋盘成钢之后第二个富人区","整个板块都是300万到900万的总价段","未来全是改善型住宅,没有刚需盘"]}`
|
||||||
|
}
|
||||||
|
func (e *RegionValue) Copy() AdviceData {
|
||||||
|
return new(RegionValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *RegionValue) Role() AdviceRole {
|
||||||
|
return RoleProject
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *RegionValue) Desc() string {
|
||||||
|
return "区域价值话术"
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompetitionComparison 竞品对比话术
|
||||||
|
type CompetitionComparison map[string]map[string]string
|
||||||
|
|
||||||
|
func (e *CompetitionComparison) Example() string {
|
||||||
|
return `{"龙湖滨江云河颂":{"优点承认":"龙湖位置确实好,看沙河公园","价格对比":"他们单价32000-35000,但得房率只有95%,套内算下来36000+","优势突出":"我们得房率118平实得132平,套内单价才33000"},"邦泰云锦":{"定位相似":"邦泰也是首个项目,要打造口碑","价格参考":"他们当时12800拿地,现在卖34000","品质对比":"我们外立面全玻璃幕墙,比他们成本高30%"}}`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *CompetitionComparison) Copy() AdviceData {
|
||||||
|
return new(CompetitionComparison)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *CompetitionComparison) Role() AdviceRole {
|
||||||
|
return RoleProject
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *CompetitionComparison) Desc() string {
|
||||||
|
return "竞品对比话术"
|
||||||
|
}
|
||||||
|
|
||||||
|
// CoreSellingPoints 核心卖点
|
||||||
|
type CoreSellingPoints map[string]string
|
||||||
|
|
||||||
|
func (e *CoreSellingPoints) Example() string {
|
||||||
|
return `{"产品配置高端":"全玻璃幕墙+铝单板外立面,三层中空氩气玻璃,3.2米层高,方太Y9烟机灶具,高仪卫浴","地段稀缺性":"成华区2.5环内侧核心地段,槐树店板块是成华区number one板块,被三板桥、万象城、火车东站包围","得房率高":"118平实得132平,套内单价33000,比龙湖滨江云河颂套内单价低3000"}`
|
||||||
|
}
|
||||||
|
func (e *CoreSellingPoints) Copy() AdviceData {
|
||||||
|
return new(CoreSellingPoints)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *CoreSellingPoints) Role() AdviceRole {
|
||||||
|
return RoleProject
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *CoreSellingPoints) Desc() string {
|
||||||
|
return "核心卖点"
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportingFacilities 配套体系
|
||||||
|
type SupportingFacilities map[string]map[string]string
|
||||||
|
|
||||||
|
func (e *SupportingFacilities) Example() string {
|
||||||
|
return `{"交通配套":{"地铁":"双店路站350米(7号线),槐树店站550米(4号线),未来12号线","道路":"中环路、成洛大道,到春熙路5个站","通达性":"到火车东站2个站,到华西30分钟内"},"教育配套":{"幼儿园":"楼下公立幼儿园","小学":"城市附小锦汇东城(成华区生源最好的学校)","生源优势":"周边新盘都是300万+,生源纯粹"},"医疗配套":{"三甲医院":"市六医院、市二医院3公里内","顶尖医疗":"华西医院锦江院区30分钟车程","便利性":"到华西本部也是30分钟内"}}`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SupportingFacilities) Copy() AdviceData {
|
||||||
|
return new(SupportingFacilities)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SupportingFacilities) Role() AdviceRole {
|
||||||
|
return RoleProject
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SupportingFacilities) Desc() string {
|
||||||
|
return "配套体系"
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeveloperBacking 开发商背书
|
||||||
|
type DeveloperBacking map[string]string
|
||||||
|
|
||||||
|
func (e *DeveloperBacking) Example() string {
|
||||||
|
return `{"公司实力":"中信资产,多元化民营企业","资金安全":"在河南渑池有铝土矿,每年稳定收入10亿","开发经验":"宜宾有5个项目,贵州2个,成都是首个项目","合作方":"招商铂金物业,首次与外部企业合作"}`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *DeveloperBacking) Copy() AdviceData {
|
||||||
|
return new(DeveloperBacking)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *DeveloperBacking) Role() AdviceRole {
|
||||||
|
return RoleProject
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *DeveloperBacking) Desc() string {
|
||||||
|
return "开发商背书"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,137 @@
|
||||||
|
package mongo_model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AdvicerTalkSkillMongo struct {
|
||||||
|
ProjectId int32 `json:"projectId" bson:"projectId"`
|
||||||
|
AdvicerId int32 `json:"advicerId" bson:"advicerId"`
|
||||||
|
Desc string `json:"desc" bson:"desc"`
|
||||||
|
NeedsMining NeedsMining `json:"needsMining" bson:"needsMining"`
|
||||||
|
PainPointResponse PainPointResponse `json:"painPointResponse" bson:"painPointResponse"`
|
||||||
|
ValueBuilding ValueBuilding `json:"valueBuilding" bson:"valueBuilding"`
|
||||||
|
ClosingTechniques ClosingTechniques `json:"closingTechniques" bson:"closingTechniques"`
|
||||||
|
CommunicationRhythm CommunicationRhythm `json:"communicationRhythm" bson:"communicationRhythm"`
|
||||||
|
LastUpdateTime time.Time `json:"lastUpdateTime" bson:"lastUpdateTime"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdvicerTalkSkillMongo() *AdvicerTalkSkillMongo {
|
||||||
|
return &AdvicerTalkSkillMongo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdvicerTalkSkillMongo) MongoTableName() string {
|
||||||
|
return "advicer_talk_skill"
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerTalkSkillMongoEntity struct {
|
||||||
|
NeedsMining NeedsMining `json:"needsMining"`
|
||||||
|
PainPointResponse PainPointResponse `json:"painPointResponse"`
|
||||||
|
ValueBuilding ValueBuilding `json:"valueBuilding"`
|
||||||
|
ClosingTechniques ClosingTechniques `json:"closingTechniques"`
|
||||||
|
CommunicationRhythm CommunicationRhythm `json:"communicationRhythm"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdvicerTalkSkillMongo) Entity() *AdvicerTalkSkillMongoEntity {
|
||||||
|
return &AdvicerTalkSkillMongoEntity{
|
||||||
|
NeedsMining: a.NeedsMining,
|
||||||
|
PainPointResponse: a.PainPointResponse,
|
||||||
|
ValueBuilding: a.ValueBuilding,
|
||||||
|
ClosingTechniques: a.ClosingTechniques,
|
||||||
|
CommunicationRhythm: a.CommunicationRhythm,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NeedsMining 需求挖掘话术
|
||||||
|
type NeedsMining map[string][]string
|
||||||
|
|
||||||
|
func (e *NeedsMining) Example() string {
|
||||||
|
return `{"预算需求":["你们总价想控制在多少以内?","是考虑按揭还是一次性?","月供能接受多少范围?"],"居住需求":["几个人住?有老人小孩吗?","主要是自住还是考虑投资?","现在住哪里?想改善哪些方面?"],"通勤需求":["在哪个位置上班?","主要开车还是坐地铁?","对地铁距离有要求吗?"]}`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *NeedsMining) Copy() AdviceData {
|
||||||
|
return new(NeedsMining)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *NeedsMining) Role() AdviceRole {
|
||||||
|
return RoleSkill
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *NeedsMining) Desc() string {
|
||||||
|
return "需求挖掘话术"
|
||||||
|
}
|
||||||
|
|
||||||
|
// PainPointResponse 痛点应对策略
|
||||||
|
type PainPointResponse map[string]map[string]string
|
||||||
|
|
||||||
|
func (e *PainPointResponse) Example() string {
|
||||||
|
return `{"地块太小":{"承认事实":"14亩确实不大","普遍现象":"2.5环内都是小地块,万景13亩,中铁建8.8亩","转化优势":"但人少安静,楼间距反而更开阔","对比竞品":"339的邦泰才11亩,人家上千万豪宅"},"物业费高":{"理解感受":"我懂你,我们也觉得有点贵","价值分析":"但6块里3块是增值服务(保洁、送外卖)","价格补贴":"前三年补贴到5块,跟其他盘差不多"}}`
|
||||||
|
}
|
||||||
|
func (e *PainPointResponse) Copy() AdviceData {
|
||||||
|
return new(PainPointResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PainPointResponse) Role() AdviceRole {
|
||||||
|
return RoleSkill
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PainPointResponse) Desc() string {
|
||||||
|
return "痛点应对策略"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValueBuilding 价值塑造技巧
|
||||||
|
type ValueBuilding map[string][]string
|
||||||
|
|
||||||
|
func (e *ValueBuilding) Example() string {
|
||||||
|
return `{"地段价值塑造":["买房最重要的是地段、地段、还是地段","核心地段的核心资产才保值增值","2.5环内的地卖一块少一块,不可再生"],"产品价值塑造":["我们是用改善的价格,买豪宅的标准","很多细节都是3000万豪宅才有的配置","外立面成本比竞品高30%,但单价差不多"]}`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ValueBuilding) Copy() AdviceData {
|
||||||
|
return new(ValueBuilding)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ValueBuilding) Role() AdviceRole {
|
||||||
|
return RoleSkill
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ValueBuilding) Desc() string {
|
||||||
|
return "价值塑造技巧"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClosingTechniques 促单话术
|
||||||
|
type ClosingTechniques map[string]map[string][]string
|
||||||
|
|
||||||
|
func (e *ClosingTechniques) Example() string {
|
||||||
|
return `{"紧迫感营造":{"时间紧迫":["今天是月底最后一天,领导有压力价格可谈","我们刚刚开盘,还有额外优惠","月底冲业绩,价格最有弹性"],"房源稀缺":["118只剩20多套了,好楼层不多","这栋楼就60户,卖一套少一套","特价房只有这几套,今天不定可能就没了"]},"优惠策略":{"价格优惠":["今天定的话,我可以跟领导申请额外折扣","买车位的话,总价多给两个点优惠","一次性付款再优惠一个点"],"附加价值":["送一年物业费","送品牌家电礼包","优先选车位"]},"决策推动":{"小步推进":["要不先交个小定保留房源?","可以先排个号,有优惠优先通知你","今天不定的话,我帮你留意好楼层"]}}`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ClosingTechniques) Copy() AdviceData {
|
||||||
|
return new(ClosingTechniques)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ClosingTechniques) Role() AdviceRole {
|
||||||
|
return RoleSkill
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ClosingTechniques) Desc() string {
|
||||||
|
return "促单话术"
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommunicationRhythm 沟通节奏控制
|
||||||
|
type CommunicationRhythm map[string]map[string]string
|
||||||
|
|
||||||
|
func (e *CommunicationRhythm) Example() string {
|
||||||
|
return `{"开场阶段":{"时间占比":"5%","目标":"建立关系,了解需求","关键动作":"亲切称呼,简单寒暄,确认看房重点"},"沙盘讲解":{"时间占比":"30%","目标":"建立价值认知","关键动作":"板块价值→周边配套→项目亮点→开发商介绍"}}`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *CommunicationRhythm) Copy() AdviceData {
|
||||||
|
return new(CommunicationRhythm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *CommunicationRhythm) Role() AdviceRole {
|
||||||
|
return RoleSkill
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *CommunicationRhythm) Desc() string {
|
||||||
|
return "沟通节奏控制"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,155 @@
|
||||||
|
package mongo_model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AdvicerVersionMongo struct {
|
||||||
|
AdvicerId int32 `json:"advicerId" bson:"advicerId"`
|
||||||
|
VersionDesc string `json:"versionDesc" bson:"versionDesc"`
|
||||||
|
DialectFeatures DialectFeatures `json:"dialectFeatures" bson:"DialectFeatures"`
|
||||||
|
SentencePatterns SentencePatterns `json:"sentencePatterns" bson:"sentencePatterns"`
|
||||||
|
ToneTags ToneTags `json:"toneTags" bson:"toneTags"`
|
||||||
|
PersonalityTags PersonalityTags `json:"personalityTags" bson:"personalityTags"`
|
||||||
|
SignatureDialogues SignatureDialogues `json:"signatureDialogues" bson:"signatureDialogues"`
|
||||||
|
LastUpdateTime time.Time `json:"lastUpdateTime" bson:"lastUpdateTime"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdvicerVersionMongo() *AdvicerVersionMongo {
|
||||||
|
return &AdvicerVersionMongo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdvicerVersionMongo) MongoTableName() string {
|
||||||
|
return "advicer_version"
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerVersionMongoEntity struct {
|
||||||
|
DialectFeatures DialectFeatures `json:"dialectFeatures"`
|
||||||
|
SentencePatterns SentencePatterns `json:"sentencePatterns"`
|
||||||
|
ToneTags ToneTags `json:"toneTags"`
|
||||||
|
PersonalityTags PersonalityTags `json:"personalityTags"`
|
||||||
|
SignatureDialogues SignatureDialogues `json:"signatureDialogues"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdvicerVersionMongo) Entity() *AdvicerVersionMongoEntity {
|
||||||
|
return &AdvicerVersionMongoEntity{
|
||||||
|
DialectFeatures: a.DialectFeatures,
|
||||||
|
SentencePatterns: a.SentencePatterns,
|
||||||
|
ToneTags: a.ToneTags,
|
||||||
|
PersonalityTags: a.PersonalityTags,
|
||||||
|
SignatureDialogues: a.SignatureDialogues,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignatureDialogues 代表性对话示例
|
||||||
|
type SignatureDialogues []struct {
|
||||||
|
Context string `json:"context"`
|
||||||
|
Dialogue string `json:"dialogue"` //解释
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialectFeatures 方言特征
|
||||||
|
type DialectFeatures struct {
|
||||||
|
Region string `json:"region"` //方言使用程度
|
||||||
|
Intensity float64 `json:"intensity"` // 方言使用强度(0-1)
|
||||||
|
KeyWords []string `json:"KeyWords"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *DialectFeatures) Example() string {
|
||||||
|
return `{"region":"四川成都话","intensity":0.4,"key_words":["噻","要得","没得","不晓得","是不是"]}`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *DialectFeatures) Copy() AdviceData {
|
||||||
|
return new(DialectFeatures)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *DialectFeatures) Role() AdviceRole {
|
||||||
|
return RoleAdvicer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *DialectFeatures) Desc() string {
|
||||||
|
return "方言特征"
|
||||||
|
}
|
||||||
|
|
||||||
|
// SentencePatterns 句子模式
|
||||||
|
type SentencePatterns struct {
|
||||||
|
OpeningMode []string `json:"openingMode"` //开场模式
|
||||||
|
ExplanationMode []string `json:"explanationMode"` //解释模式
|
||||||
|
ConfirmationMode []string `json:"confirmationMode"` //确认模式
|
||||||
|
SummaryMode []string `json:"summaryMode"` //总结模式
|
||||||
|
TransitionMode []string `json:"transitionMode"` //过渡模式
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SentencePatterns) Example() string {
|
||||||
|
return `{"openingMode":["我给你介绍一下","我们先来看一下"],"explanationMode":["是这样的","我跟你讲","你发现没得"],"confirmationMode":["对吧?","是不是嘛?","你晓得不?","明白了噻?"],"summaryMode":["所以说","简单说就是"],"transitionMode":["然后的话","再其次","还有一点"]}`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SentencePatterns) Copy() AdviceData {
|
||||||
|
return new(SentencePatterns)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SentencePatterns) Role() AdviceRole {
|
||||||
|
return RoleAdvicer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SentencePatterns) Desc() string {
|
||||||
|
return "句子模式"
|
||||||
|
}
|
||||||
|
|
||||||
|
// PersonalityTags 个性标签
|
||||||
|
type PersonalityTags []string
|
||||||
|
|
||||||
|
func (e *PersonalityTags) Example() string {
|
||||||
|
return `["耐心细致","细节控"]`
|
||||||
|
}
|
||||||
|
func (e *PersonalityTags) Copy() AdviceData {
|
||||||
|
return new(PersonalityTags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PersonalityTags) Role() AdviceRole {
|
||||||
|
return RoleAdvicer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PersonalityTags) Desc() string {
|
||||||
|
return "个性标签"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToneTags 语气标签
|
||||||
|
type ToneTags struct {
|
||||||
|
Enthusiasm float64 `json:"enthusiasm"`
|
||||||
|
Patience float64 `json:"patience"`
|
||||||
|
Confidence float64 `json:"confidence"`
|
||||||
|
Friendliness float64 `json:"friendliness"`
|
||||||
|
Persuasion float64 `json:"persuasion"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ToneTags) Example() string {
|
||||||
|
return `{"enthusiasm":0.8,"patience":0.9,"confidence":0.85,"friendliness":0.75,"persuasion":0.7}`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ToneTags) Copy() AdviceData {
|
||||||
|
return new(ToneTags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ToneTags) Role() AdviceRole {
|
||||||
|
return RoleAdvicer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ToneTags) Desc() string {
|
||||||
|
return "语气标签"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SignatureDialogues) Example() string {
|
||||||
|
return `[{"context":"客户质疑地块大小","dialogue":"哥,14亩确实不大,但你要在成都是2.5环内城买房,这种是个普遍存在的一个现象。你看万景和绿城都是13亩,中铁建只有8.8亩,339那个帮泰只有11亩。我们虽然地小,但楼间距开阔啊,看过去都是200多米!"},{"context":"客户担心物业费高","dialogue":"姐,我懂你意思,我们也觉得物业费是有点贵。但招商物业是铂金服务,有管家送外卖、免费宠物喂养这些增值服务。你算一下,就算贵一块钱,十年也就多14000,但好物业让房子增值不止这点!"},{"context":"客户犹豫价格","dialogue":"说实话,这个地段的地价都比28板块贵5000多,但我们单价只贵3000。你看龙湖滨江云河颂套内单价都36000了,我们才33000,真的性价比高!现在不买,以后这个板块可能就买不起了。"}]`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SignatureDialogues) Copy() AdviceData {
|
||||||
|
return new(SignatureDialogues)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SignatureDialogues) Role() AdviceRole {
|
||||||
|
return RoleAdvicer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SignatureDialogues) Desc() string {
|
||||||
|
return "代表性对话示例"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package mongo_model
|
||||||
|
|
||||||
|
type AdviceRole string
|
||||||
|
|
||||||
|
const (
|
||||||
|
RoleAdvicer AdviceRole = "advicer"
|
||||||
|
RoleProject AdviceRole = "project"
|
||||||
|
RoleSkill AdviceRole = "skill"
|
||||||
|
RoleClient AdviceRole = "client"
|
||||||
|
)
|
||||||
|
|
||||||
|
var RoleDesc = map[AdviceRole]string{
|
||||||
|
RoleAdvicer: "顾问",
|
||||||
|
RoleProject: "项目",
|
||||||
|
RoleSkill: "沟通技巧",
|
||||||
|
RoleClient: "客户",
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdviceData interface {
|
||||||
|
Example() string
|
||||||
|
Copy() AdviceData
|
||||||
|
Role() AdviceRole
|
||||||
|
Desc() string
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
package mongo_model
|
||||||
|
|
||||||
|
import "github.com/google/wire"
|
||||||
|
|
||||||
|
var ProviderSetMongo = wire.NewSet(
|
||||||
|
NewAdvicerVersionMongo,
|
||||||
|
NewAdvicerTalkSkillMongo,
|
||||||
|
NewAdvicerProjectMongo,
|
||||||
|
NewAdvicerClientMongo,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package entitys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ai_scheduler/internal/data/model"
|
||||||
|
"ai_scheduler/internal/data/mongo_model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ChatData struct {
|
||||||
|
ClientInfo *mongo_model.AdvicerClientMongoEntity `json:"clientInfo"`
|
||||||
|
TalkSkill *mongo_model.AdvicerTalkSkillMongoEntity `json:"talkSkill"`
|
||||||
|
ProjectInfo *mongo_model.AdvicerProjectMongoEntity `json:"projectInfo"`
|
||||||
|
AdvicerInfo *model.AiAdviceAdvicerEntity `json:"advicerInfo"`
|
||||||
|
AdvicerVersion *mongo_model.AdvicerVersionMongoEntity `json:"advicerVersion"`
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,173 @@
|
||||||
|
package entitys
|
||||||
|
|
||||||
|
import "ai_scheduler/internal/data/mongo_model"
|
||||||
|
|
||||||
|
type AdvicerInitReq struct {
|
||||||
|
AdvicerID int32 `json:"AdvicerId"`
|
||||||
|
ProjectID int32 `json:"ProjectId"`
|
||||||
|
Name string `json:"name"` // 姓名
|
||||||
|
Birth string `json:"birth"` // 用户名称
|
||||||
|
Gender int32 `json:"gender"` // 1:男,2:女
|
||||||
|
WorkingYears int32 `json:"WorkingYears"` // 工作年限
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerInfoReq struct {
|
||||||
|
AdvicerID int32 `json:"AdvicerId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerListReq struct {
|
||||||
|
ProjectId int32 `json:"ProjectId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerVersionAddReq struct {
|
||||||
|
AdvicerID int32 `json:"advicerId"`
|
||||||
|
VersionDesc string `json:"versionDesc"`
|
||||||
|
DialectFeatures mongo_model.DialectFeatures `json:"dialectFeatures"`
|
||||||
|
PersonalityTags mongo_model.PersonalityTags `json:"personalityTags"`
|
||||||
|
SentencePatterns mongo_model.SentencePatterns `json:"sentencePatterns"`
|
||||||
|
SignatureDialogues mongo_model.SignatureDialogues `json:"signatureDialogues"`
|
||||||
|
ToneTags mongo_model.ToneTags `json:"toneTags"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerVersionUpdateReq struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
AdvicerID int32 `json:"advicerId"`
|
||||||
|
VersionDesc string `json:"versionDesc"`
|
||||||
|
DialectFeatures mongo_model.DialectFeatures `json:"dialectFeatures"`
|
||||||
|
PersonalityTags mongo_model.PersonalityTags `json:"personalityTags"`
|
||||||
|
SentencePatterns mongo_model.SentencePatterns `json:"sentencePatterns"`
|
||||||
|
SignatureDialogues mongo_model.SignatureDialogues `json:"signatureDialogues"`
|
||||||
|
ToneTags mongo_model.ToneTags `json:"toneTags"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerVersionListReq struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
AdvicerId int32 `json:"advicerId"`
|
||||||
|
VersionDesc string `json:"versionDesc"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerVersionDelReq struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerVersionInfoReq struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerTalkSkillAddReq struct {
|
||||||
|
ProjectId int32 `json:"projectId" bson:"projectId"`
|
||||||
|
AdvicerId int32 `json:"advicerId" bson:"advicerId"`
|
||||||
|
Desc string `json:"desc" bson:"desc"`
|
||||||
|
NeedsMining mongo_model.NeedsMining `json:"needsMining" bson:"needsMining"`
|
||||||
|
PainPointResponse mongo_model.PainPointResponse `json:"painPointResponse" bson:"painPointResponse"`
|
||||||
|
ValueBuilding mongo_model.ValueBuilding `json:"valueBuilding" bson:"valueBuilding"`
|
||||||
|
ClosingTechniques mongo_model.ClosingTechniques `json:"closingTechniques" bson:"closingTechniques"`
|
||||||
|
CommunicationRhythm mongo_model.CommunicationRhythm `json:"communicationRhythm" bson:"communicationRhythm"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerTalkSkillUpdateReq struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
ProjectId int32 `json:"projectId" bson:"projectId"`
|
||||||
|
AdvicerId int32 `json:"advicerId" bson:"advicerId" :"advicer-id"`
|
||||||
|
Desc string `json:"desc" bson:"desc" :"desc"`
|
||||||
|
NeedsMining mongo_model.NeedsMining `json:"needsMining" bson:"needsMining" :"needs-mining"`
|
||||||
|
PainPointResponse mongo_model.PainPointResponse `json:"painPointResponse" bson:"painPointResponse" :"pain-point-response"`
|
||||||
|
ValueBuilding mongo_model.ValueBuilding `json:"valueBuilding" bson:"valueBuilding" :"value-building"`
|
||||||
|
ClosingTechniques mongo_model.ClosingTechniques `json:"closingTechniques" bson:"closingTechniques" :"closing-techniques"`
|
||||||
|
CommunicationRhythm mongo_model.CommunicationRhythm `json:"communicationRhythm" bson:"communicationRhythm" :"communication-rhythm"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerTalkSkillListReq struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
ProjectId int32 `json:"projectId" bson:"projectId"`
|
||||||
|
AdvicerId int32 `json:"advicerId" bson:"advicerId"`
|
||||||
|
Desc string `json:"desc" bson:"desc"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerTalkSkillDelReq struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerTalkSkillInfoReq struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerProjectAddReq struct {
|
||||||
|
ProjectId int32 `json:"projectId" bson:"projectId"`
|
||||||
|
ProjectInfo mongo_model.ProjectInfo `json:"projectInfo" bson:"projectInfo"`
|
||||||
|
RegionValue mongo_model.RegionValue `json:"regionValue" bson:"regionValue"`
|
||||||
|
CompetitionComparison mongo_model.CompetitionComparison `json:"competitionComparison" bson:"competitionComparison"`
|
||||||
|
CoreSellingPoints mongo_model.CoreSellingPoints `json:"coreSellingPoints" bson:"coreSellingPoints"`
|
||||||
|
SupportingFacilities mongo_model.SupportingFacilities `json:"supportingFacilities" bson:"supportingFacilities"`
|
||||||
|
DeveloperBacking mongo_model.DeveloperBacking `json:"developerBacking" bson:"developerBacking"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerrProjectUpdateReq struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
ProjectId int32 `json:"projectId" bson:"projectId"`
|
||||||
|
ProjectInfo mongo_model.ProjectInfo `json:"projectInfo" bson:"projectInfo"`
|
||||||
|
RegionValue mongo_model.RegionValue `json:"regionValue" bson:"regionValue"`
|
||||||
|
CompetitionComparison mongo_model.CompetitionComparison `json:"competitionComparison" bson:"competitionComparison"`
|
||||||
|
CoreSellingPoints mongo_model.CoreSellingPoints `json:"coreSellingPoints" bson:"coreSellingPoints"`
|
||||||
|
SupportingFacilities mongo_model.SupportingFacilities `json:"supportingFacilities" bson:"supportingFacilities"`
|
||||||
|
DeveloperBacking mongo_model.DeveloperBacking `json:"developerBacking" bson:"developerBacking"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerProjectInfoReq struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
ProjectId int32 `json:"projectId" bson:"projectId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerClientAddReq struct {
|
||||||
|
ProjectId int32 `json:"projectId" bson:"projectId"`
|
||||||
|
AdvicerId int32 `json:"advicerId" bson:"advicerId"`
|
||||||
|
PersonalInfo mongo_model.PersonalInfo `json:"personalInfo" bson:"personalInfo"`
|
||||||
|
PurchasePurpose mongo_model.PurchasePurpose `json:"purchasePurpose" bson:"purchasePurpose"`
|
||||||
|
CoreDemands mongo_model.CoreDemands `json:"coreDemands" bson:"coreDemands"`
|
||||||
|
Concerns []string `json:"concerns" bson:"concerns"`
|
||||||
|
DecisionProfile []string `json:"decisionProfile" bson:"decisionProfile"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerrClientUpdateReq struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
ProjectId int32 `json:"projectId" bson:"projectId"`
|
||||||
|
AdvicerId int32 `json:"advicerId" bson:"advicerId"`
|
||||||
|
PersonalInfo mongo_model.PersonalInfo `json:"personalInfo" bson:"personalInfo"`
|
||||||
|
PurchasePurpose mongo_model.PurchasePurpose `json:"purchasePurpose" bson:"purchasePurpose"`
|
||||||
|
CoreDemands mongo_model.CoreDemands `json:"coreDemands" bson:"coreDemands"`
|
||||||
|
Concerns []string `json:"concerns" bson:"concerns"`
|
||||||
|
DecisionProfile []string `json:"decisionProfile" bson:"decisionProfile"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerClientListReq struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
ProjectId int32 `json:"projectId" bson:"projectId"`
|
||||||
|
AdvicerId int32 `json:"advicerId" bson:"advicerId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerClientDelReq struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerClientInfoReq struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerChatRegistReq struct {
|
||||||
|
AdvicerVersionId string `json:"advicerVersionId"`
|
||||||
|
ClientId string `json:"clientId"`
|
||||||
|
TalkSkillId string `json:"talkSkillId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerChatRegistRes struct {
|
||||||
|
SessionId string `json:"sessionId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerChatReq struct {
|
||||||
|
SessionId string `json:"sessionId"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerChatRes struct {
|
||||||
|
Content int32 `json:"content"`
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,10 @@ import (
|
||||||
"gitea.cdlsxd.cn/self-tools/l-dingtalk-stream-sdk-go/chatbot"
|
"gitea.cdlsxd.cn/self-tools/l-dingtalk-stream-sdk-go/chatbot"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type WordAnaReq struct {
|
||||||
|
WordFileUrl string `json:"word_file_url"`
|
||||||
|
}
|
||||||
|
|
||||||
type RequireDataDingTalkBot struct {
|
type RequireDataDingTalkBot struct {
|
||||||
Histories []model.AiChatHi
|
Histories []model.AiChatHi
|
||||||
UserInfo *DingTalkUserInfo
|
UserInfo *DingTalkUserInfo
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,236 @@
|
||||||
|
package file_download
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"bytes"
|
||||||
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 下载文件
|
||||||
|
func DownloadFile(url string, validFunc func(resp *http.Response) error) ([]byte, string, error) {
|
||||||
|
// 设置超时
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送请求
|
||||||
|
resp, err := client.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, "", fmt.Errorf("HTTP %d: %s", resp.StatusCode, resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
if validFunc != nil {
|
||||||
|
err = validFunc(resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取文件数据
|
||||||
|
data, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取文件名
|
||||||
|
filename := getFilenameFromURL(url, resp)
|
||||||
|
|
||||||
|
return data, filename, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从 URL 或响应头获取文件名
|
||||||
|
func getFilenameFromURL(urlStr string, resp *http.Response) string {
|
||||||
|
// 1. 尝试从 Content-Disposition 头获取
|
||||||
|
contentDisposition := resp.Header.Get("Content-Disposition")
|
||||||
|
if contentDisposition != "" {
|
||||||
|
if strings.Contains(contentDisposition, "filename=") {
|
||||||
|
parts := strings.Split(contentDisposition, "filename=")
|
||||||
|
if len(parts) > 1 {
|
||||||
|
filename := strings.Trim(parts[1], `"' `)
|
||||||
|
return sanitizeFilename(filename)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 从 URL 路径获取
|
||||||
|
parsedURL, err := url.Parse(urlStr)
|
||||||
|
if err == nil {
|
||||||
|
path := parsedURL.Path
|
||||||
|
if path != "" {
|
||||||
|
filename := filepath.Base(path)
|
||||||
|
if filename != "" && filename != "." && filename != "/" {
|
||||||
|
return sanitizeFilename(filename)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 生成默认文件名
|
||||||
|
return fmt.Sprintf("word_%d.docx", time.Now().Unix())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理文件名
|
||||||
|
func sanitizeFilename(filename string) string {
|
||||||
|
// 移除非法字符
|
||||||
|
illegalChars := []string{"/", "\\", ":", "*", "?", "\"", "<", ">", "|"}
|
||||||
|
for _, char := range illegalChars {
|
||||||
|
filename = strings.ReplaceAll(filename, char, "_")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保有扩展名
|
||||||
|
if !strings.Contains(filename, ".") {
|
||||||
|
filename += ".docx"
|
||||||
|
}
|
||||||
|
|
||||||
|
return filename
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从URL获取Word文件的纯文本内容
|
||||||
|
func GetWordTextFromURL(url string, validFunc func(resp *http.Response) error) (string, string, error) {
|
||||||
|
// 1. 下载文件
|
||||||
|
data, fileName, err := DownloadFile(url, validFunc)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf("下载失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 解析Word文件
|
||||||
|
text, err := parseWordContent(data)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf("解析失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return text, fileName, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析Word内容 - 简单版本,只提取文字
|
||||||
|
func parseWordContent(data []byte) (string, error) {
|
||||||
|
reader := bytes.NewReader(data)
|
||||||
|
zipReader, err := zip.NewReader(reader, int64(len(data)))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("解压docx失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var textBuilder strings.Builder
|
||||||
|
|
||||||
|
// 遍历 ZIP 文件中的文件
|
||||||
|
for _, file := range zipReader.File {
|
||||||
|
// 只处理文档主体文件
|
||||||
|
if file.Name == "word/document.xml" {
|
||||||
|
rc, err := file.Open()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("打开文档文件失败: %v", err)
|
||||||
|
}
|
||||||
|
defer rc.Close()
|
||||||
|
|
||||||
|
// 读取 XML 内容
|
||||||
|
xmlData, err := io.ReadAll(rc)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("读取XML失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取文本
|
||||||
|
text, err := parseWordXML(xmlData)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("解析XML失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
textBuilder.WriteString(text)
|
||||||
|
break // 找到主文档后退出循环
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return textBuilder.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析 Word XML 文档
|
||||||
|
func parseWordXML(xmlData []byte) (string, error) {
|
||||||
|
type WordDocument struct {
|
||||||
|
XMLName xml.Name `xml:"document"`
|
||||||
|
Body struct {
|
||||||
|
Paragraphs []struct {
|
||||||
|
Runs []struct {
|
||||||
|
Text string `xml:"t"`
|
||||||
|
} `xml:"r"`
|
||||||
|
} `xml:"p"`
|
||||||
|
} `xml:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var doc WordDocument
|
||||||
|
if err := xml.Unmarshal(xmlData, &doc); err != nil {
|
||||||
|
// 尝试简化解析
|
||||||
|
return extractTextSimple(xmlData), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var textBuilder strings.Builder
|
||||||
|
for _, para := range doc.Body.Paragraphs {
|
||||||
|
for _, run := range para.Runs {
|
||||||
|
textBuilder.WriteString(run.Text)
|
||||||
|
}
|
||||||
|
textBuilder.WriteString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
return textBuilder.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 简化文本提取(处理更复杂的文档结构)
|
||||||
|
func extractTextSimple(xmlData []byte) string {
|
||||||
|
var textBuilder strings.Builder
|
||||||
|
|
||||||
|
// 简单提取 <w:t> 标签内容
|
||||||
|
decoder := xml.NewDecoder(bytes.NewReader(xmlData))
|
||||||
|
for {
|
||||||
|
token, err := decoder.Token()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if startElem, ok := token.(xml.StartElement); ok {
|
||||||
|
if startElem.Name.Local == "t" {
|
||||||
|
// 读取文本内容
|
||||||
|
if nextToken, err := decoder.Token(); err == nil {
|
||||||
|
if charData, ok := nextToken.(xml.CharData); ok {
|
||||||
|
textBuilder.WriteString(string(charData))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return textBuilder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否为 Word 文件
|
||||||
|
func IsWordFile(resp *http.Response) error {
|
||||||
|
contentType := resp.Header.Get("Content-Type")
|
||||||
|
wordContentTypes := []string{
|
||||||
|
"application/msword",
|
||||||
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||||
|
"application/vnd.ms-word",
|
||||||
|
"application/octet-stream", // 有些服务器可能返回这个
|
||||||
|
}
|
||||||
|
|
||||||
|
contentType = strings.ToLower(contentType)
|
||||||
|
for _, ct := range wordContentTypes {
|
||||||
|
if strings.Contains(contentType, ct) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errors.New("错误的文件类型")
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
package pkg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ai_scheduler/internal/config"
|
||||||
|
"ai_scheduler/internal/pkg/utils_mongo"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Mongo struct {
|
||||||
|
Client *mongo.Client
|
||||||
|
c *config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMongoDb(ctx context.Context, c *config.Config) (*Mongo, func()) {
|
||||||
|
transDBClient, err := utils_mongo.NewMongoClient(ctx, &utils_mongo.ClientStruct{
|
||||||
|
Uri: c.Mongo.Source,
|
||||||
|
MaxPoolSize: c.Mongo.MaxPoolSize,
|
||||||
|
MinPoolSize: c.Mongo.MinPoolSize,
|
||||||
|
MaxConnIdleTime: time.Duration(c.Mongo.MaxConnIdleTime) * time.Minute,
|
||||||
|
ConnectTimeout: time.Duration(c.Mongo.ConnectTimeout) * time.Second,
|
||||||
|
SocketTimeout: time.Duration(c.Mongo.SocketTimeout) * time.Second,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("mongo数据库错误: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = transDBClient.Ping(ctx, nil); err != nil {
|
||||||
|
panic(fmt.Sprintf("mongo链接失败: %v", err))
|
||||||
|
}
|
||||||
|
return &Mongo{
|
||||||
|
Client: transDBClient,
|
||||||
|
c: c,
|
||||||
|
}, func() {
|
||||||
|
transDBClient.Disconnect(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type MongoModel interface {
|
||||||
|
MongoTableName() string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Mongo) Co(mongoModel MongoModel) *mongo.Collection {
|
||||||
|
return m.Client.Database(m.c.Mongo.DataBase).Collection(mongoModel.MongoTableName())
|
||||||
|
}
|
||||||
|
|
@ -28,4 +28,5 @@ var ProviderSetClient = wire.NewSet(
|
||||||
|
|
||||||
utils_oss.NewClient,
|
utils_oss.NewClient,
|
||||||
lsxd.NewLogin,
|
lsxd.NewLogin,
|
||||||
|
NewMongoDb,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package pkg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleResponse(c *fiber.Ctx, data interface{}, e error) (err error) {
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
switch data.(type) {
|
||||||
|
case error:
|
||||||
|
err = data.(error)
|
||||||
|
case int, int32, int64, float32, float64, string, bool:
|
||||||
|
c.Response().SetBody([]byte(fmt.Sprintf("%s", data)))
|
||||||
|
case []byte:
|
||||||
|
c.Response().SetBody(data.([]byte))
|
||||||
|
default:
|
||||||
|
dataByte, _ := json.Marshal(data)
|
||||||
|
c.Response().SetBody(dataByte)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
package utils_mongo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ClientStruct struct {
|
||||||
|
Uri string
|
||||||
|
MaxPoolSize uint64
|
||||||
|
MinPoolSize uint64
|
||||||
|
MaxConnIdleTime time.Duration
|
||||||
|
ConnectTimeout time.Duration
|
||||||
|
SocketTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMongoClient(ctx context.Context, config *ClientStruct) (*mongo.Client, error) {
|
||||||
|
clientOptions := options.Client().ApplyURI(config.Uri).
|
||||||
|
SetMaxPoolSize(config.MaxPoolSize). // 最大连接数
|
||||||
|
SetMinPoolSize(config.MinPoolSize). // 最小连接数
|
||||||
|
SetMaxConnIdleTime(config.MaxConnIdleTime). // 连接最大空闲时间
|
||||||
|
SetConnectTimeout(config.ConnectTimeout). // 连接超时
|
||||||
|
SetSocketTimeout(config.ConnectTimeout) // 操作超时
|
||||||
|
|
||||||
|
return mongo.Connect(ctx, clientOptions)
|
||||||
|
}
|
||||||
|
|
@ -4,22 +4,13 @@ import (
|
||||||
"ai_scheduler/internal/gateway"
|
"ai_scheduler/internal/gateway"
|
||||||
"ai_scheduler/internal/server/router"
|
"ai_scheduler/internal/server/router"
|
||||||
"ai_scheduler/internal/services"
|
"ai_scheduler/internal/services"
|
||||||
|
"ai_scheduler/internal/services/advice"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/gofiber/fiber/v2/middleware/logger"
|
"github.com/gofiber/fiber/v2/middleware/logger"
|
||||||
"github.com/gofiber/fiber/v2/middleware/recover"
|
"github.com/gofiber/fiber/v2/middleware/recover"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HTTPServer struct {
|
|
||||||
app *fiber.App
|
|
||||||
service *services.ChatService
|
|
||||||
session *services.SessionService
|
|
||||||
gateway *gateway.Gateway
|
|
||||||
callback *services.CallbackService
|
|
||||||
chatHis *services.HistoryService
|
|
||||||
capabilityService *services.CapabilityService
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHTTPServer(
|
func NewHTTPServer(
|
||||||
service *services.ChatService,
|
service *services.ChatService,
|
||||||
session *services.SessionService,
|
session *services.SessionService,
|
||||||
|
|
@ -28,10 +19,16 @@ func NewHTTPServer(
|
||||||
callback *services.CallbackService,
|
callback *services.CallbackService,
|
||||||
chatHis *services.HistoryService,
|
chatHis *services.HistoryService,
|
||||||
capabilityService *services.CapabilityService,
|
capabilityService *services.CapabilityService,
|
||||||
|
adviceFile *advice.FileService,
|
||||||
|
adviceData *advice.AdvicerService,
|
||||||
|
adviceChat *advice.ChatService,
|
||||||
|
adviceProject *advice.ProjectService,
|
||||||
|
adviceTalkSkill *advice.TalkSkillService,
|
||||||
|
adviceClient *advice.ClientService,
|
||||||
) *fiber.App {
|
) *fiber.App {
|
||||||
//构建 server
|
//构建 server
|
||||||
app := initRoute()
|
app := initRoute()
|
||||||
router.SetupRoutes(app, service, session, task, gateway, callback, chatHis, capabilityService)
|
router.SetupRoutes(app, service, session, task, gateway, callback, chatHis, capabilityService, adviceFile, adviceData, adviceChat, adviceProject, adviceTalkSkill, adviceClient)
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
package router
|
package router
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
errorcode "ai_scheduler/internal/data/error"
|
||||||
errors "ai_scheduler/internal/data/error"
|
errors "ai_scheduler/internal/data/error"
|
||||||
"ai_scheduler/internal/gateway"
|
"ai_scheduler/internal/gateway"
|
||||||
"ai_scheduler/internal/services"
|
"ai_scheduler/internal/services"
|
||||||
|
"ai_scheduler/internal/services/advice"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -14,19 +16,11 @@ import (
|
||||||
"github.com/gofiber/websocket/v2"
|
"github.com/gofiber/websocket/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RouterServer struct {
|
|
||||||
app *fiber.App
|
|
||||||
service *services.ChatService
|
|
||||||
session *services.SessionService
|
|
||||||
gateway *gateway.Gateway
|
|
||||||
chatHist *services.HistoryService
|
|
||||||
capabilityService *services.CapabilityService
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetupRoutes 设置路由
|
// SetupRoutes 设置路由
|
||||||
func SetupRoutes(app *fiber.App, ChatService *services.ChatService, sessionService *services.SessionService, task *services.TaskService,
|
func SetupRoutes(app *fiber.App, ChatService *services.ChatService, sessionService *services.SessionService, task *services.TaskService,
|
||||||
gateway *gateway.Gateway, callbackService *services.CallbackService, chatHist *services.HistoryService,
|
gateway *gateway.Gateway, callbackService *services.CallbackService, chatHist *services.HistoryService,
|
||||||
capabilityService *services.CapabilityService,
|
capabilityService *services.CapabilityService, adviceFile *advice.FileService, adviceData *advice.AdvicerService,
|
||||||
|
adviceChat *advice.ChatService, adviceProject *advice.ProjectService, adviceTalkSkill *advice.TalkSkillService, adviceClient *advice.ClientService,
|
||||||
) {
|
) {
|
||||||
app.Use(func(c *fiber.Ctx) error {
|
app.Use(func(c *fiber.Ctx) error {
|
||||||
// 设置 CORS 头
|
// 设置 CORS 头
|
||||||
|
|
@ -102,6 +96,38 @@ func SetupRoutes(app *fiber.App, ChatService *services.ChatService, sessionServi
|
||||||
// 能力
|
// 能力
|
||||||
r.Post("/capability/product/ingest", capabilityService.ProductIngest) // 商品数据提取
|
r.Post("/capability/product/ingest", capabilityService.ProductIngest) // 商品数据提取
|
||||||
r.Post("/capability/product/ingest/:thread_id/confirm", capabilityService.ProductIngestConfirm) // 商品数据提取确认
|
r.Post("/capability/product/ingest/:thread_id/confirm", capabilityService.ProductIngestConfirm) // 商品数据提取确认
|
||||||
|
|
||||||
|
advicer := r.Group("advice/")
|
||||||
|
advicer.Post("file/word/ana", adviceFile.WordAna)
|
||||||
|
//顾问
|
||||||
|
advicer.Post("advicer/add", adviceData.AdvicerUpdate)
|
||||||
|
advicer.Post("advicer/update", adviceData.AdvicerUpdate)
|
||||||
|
advicer.Post("advicer/list", adviceData.AdvicerList)
|
||||||
|
advicer.Post("advicer/version/add", adviceData.AdvicerVersionAdd)
|
||||||
|
advicer.Post("advicer/version/update", adviceData.AdvicerVersionUpdate)
|
||||||
|
advicer.Post("advicer/version/del", adviceData.AdvicerVersionDel)
|
||||||
|
advicer.Post("advicer/version/list", adviceData.AdvicerVersionList)
|
||||||
|
//聊天技巧
|
||||||
|
advicer.Post("skill/list", adviceTalkSkill.TalkSkillList)
|
||||||
|
advicer.Post("skill/add", adviceTalkSkill.TalkSkillAdd)
|
||||||
|
advicer.Post("skill/update", adviceTalkSkill.TalkSkillUpdate)
|
||||||
|
advicer.Post("skill/del", adviceTalkSkill.TalkSkillUpdate)
|
||||||
|
|
||||||
|
//项目
|
||||||
|
advicer.Post("project/add", adviceProject.Add)
|
||||||
|
advicer.Post("project/update", adviceProject.Update)
|
||||||
|
advicer.Post("project/info", adviceProject.Info)
|
||||||
|
|
||||||
|
//客户
|
||||||
|
advicer.Post("client/add", adviceClient.Add)
|
||||||
|
advicer.Post("client/update", adviceClient.Update)
|
||||||
|
advicer.Post("client/list", adviceClient.List)
|
||||||
|
advicer.Post("client/del", adviceClient.Del)
|
||||||
|
|
||||||
|
//客户
|
||||||
|
advicer.Post("chat/regis", adviceChat.Regis)
|
||||||
|
advicer.Post("chat/chat", adviceChat.Chat)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func routerSocket(app *fiber.App, chatService *services.ChatService) {
|
func routerSocket(app *fiber.App, chatService *services.ChatService) {
|
||||||
|
|
@ -138,12 +164,13 @@ func registerCommon(c *fiber.Ctx, err error) error {
|
||||||
if c.Path() == "/api/v1/qywx/callback" {
|
if c.Path() == "/api/v1/qywx/callback" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
bsErr, ok := err.(*errors.BusinessErr)
|
|
||||||
if !ok {
|
|
||||||
bsErr = errors.SystemError
|
|
||||||
}
|
|
||||||
// 如果有错误发生
|
// 如果有错误发生
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
bsErr, ok := err.(*errors.BusinessErr)
|
||||||
|
if !ok {
|
||||||
|
bsErr = errorcode.SysErr(err.Error())
|
||||||
|
}
|
||||||
// 返回自定义错误响应
|
// 返回自定义错误响应
|
||||||
return c.JSON(fiber.Map{
|
return c.JSON(fiber.Map{
|
||||||
"message": bsErr.Error(),
|
"message": bsErr.Error(),
|
||||||
|
|
@ -156,16 +183,17 @@ func registerCommon(c *fiber.Ctx, err error) error {
|
||||||
// 是 SSE 请求
|
// 是 SSE 请求
|
||||||
return c.SendString("这是 SSE 请求")
|
return c.SendString("这是 SSE 请求")
|
||||||
}
|
}
|
||||||
var data interface{}
|
|
||||||
json.Unmarshal(c.Response().Body(), &data)
|
|
||||||
|
|
||||||
// 检查是否需要跳过响应包装
|
body := c.Response().Body()
|
||||||
if c.Locals("skip_response_wrap") == true {
|
var rawData json.RawMessage
|
||||||
return c.JSON(data)
|
if len(body) > 0 {
|
||||||
|
if err := json.Unmarshal(body, &rawData); err != nil {
|
||||||
|
// 解析失败,作为字符串包装成JSON
|
||||||
|
rawData = json.RawMessage(`"` + strings.ReplaceAll(string(body), `"`, `\"`) + `"`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(fiber.Map{
|
return c.JSON(fiber.Map{
|
||||||
"data": data,
|
"data": rawData,
|
||||||
"message": errors.Success.Error(),
|
"message": errors.Success.Error(),
|
||||||
"code": errors.Success.Code(),
|
"code": errors.Success.Code(),
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
package advice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ai_scheduler/internal/biz"
|
||||||
|
"ai_scheduler/internal/config"
|
||||||
|
"ai_scheduler/internal/entitys"
|
||||||
|
"ai_scheduler/internal/pkg"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AdvicerService 数据处理
|
||||||
|
type AdvicerService struct {
|
||||||
|
adviceBiz *biz.AdviceAdvicerBiz
|
||||||
|
cfg *config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDataService
|
||||||
|
func NewAdvicerService(
|
||||||
|
adviceBiz *biz.AdviceAdvicerBiz,
|
||||||
|
cfg *config.Config,
|
||||||
|
) *AdvicerService {
|
||||||
|
return &AdvicerService{
|
||||||
|
adviceBiz: adviceBiz,
|
||||||
|
cfg: cfg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AdvicerService) AdvicerUpdate(c *fiber.Ctx) error {
|
||||||
|
req := &entitys.AdvicerInitReq{}
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.adviceBiz.Update(c.UserContext(), req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AdvicerService) AdvicerList(c *fiber.Ctx) error {
|
||||||
|
req := &entitys.AdvicerListReq{}
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
list, err := d.adviceBiz.List(c.UserContext(), req)
|
||||||
|
return pkg.HandleResponse(c, list, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AdvicerService) AdvicerVersionAdd(c *fiber.Ctx) error {
|
||||||
|
req := &entitys.AdvicerVersionAddReq{}
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.adviceBiz.VersionAdd(c.UserContext(), req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AdvicerService) AdvicerVersionUpdate(c *fiber.Ctx) error {
|
||||||
|
req := &entitys.AdvicerVersionUpdateReq{}
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.adviceBiz.VersionUpdate(c.UserContext(), req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AdvicerService) AdvicerVersionList(c *fiber.Ctx) error {
|
||||||
|
req := &entitys.AdvicerVersionListReq{}
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
list, err := d.adviceBiz.VersionList(c.UserContext(), req)
|
||||||
|
return pkg.HandleResponse(c, list, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AdvicerService) AdvicerVersionDel(c *fiber.Ctx) error {
|
||||||
|
req := &entitys.AdvicerVersionDelReq{}
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.adviceBiz.VersionDel(c.UserContext(), req)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,153 @@
|
||||||
|
package advice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ai_scheduler/internal/biz"
|
||||||
|
"ai_scheduler/internal/biz/llm_service/third_party"
|
||||||
|
"ai_scheduler/internal/config"
|
||||||
|
"ai_scheduler/internal/data/impl"
|
||||||
|
"ai_scheduler/internal/data/mongo_model"
|
||||||
|
"ai_scheduler/internal/pkg"
|
||||||
|
"ai_scheduler/utils"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/valyala/fasthttp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_WordAna(t *testing.T) {
|
||||||
|
Run(context.Background(), nil)
|
||||||
|
ana, err := file.WordAnat("https://attachment-public.oss-cn-hangzhou.aliyuncs.com/ai-scheduler/data-analytics/word/content2.docx")
|
||||||
|
t.Log(ana, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_AdvicerInit(t *testing.T) {
|
||||||
|
reqBody := `{"advicer_id": 124, "name": "张三111", "birth": "1990-01-01", "gender": 1, "working_years": 10}`
|
||||||
|
Run(context.Background(), []byte(reqBody))
|
||||||
|
|
||||||
|
err := advicer.AdvicerUpdate(fiberCtx)
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_AdvicerVersionAdd(t *testing.T) {
|
||||||
|
reqBody := `{"advicerId":124,"versionDesc":"第三个版本","dialectFeatures":{"region":"四川成都话","intensity":0.6,"KeyWords":null},"personalityTags":["耐心细致","专业务实","经验丰富","善于引导"],"sentencePatterns":{"openingMode":["我给你介绍一下","我们先来看一下","这边请"],"explanationMode":["是这样的","我跟你讲","你发现没得","说白了"],"confirmationMode":["对吧?","是不是嘛?","你晓得不?","明白了噻?","对不对?"],"summaryMode":["所以说","简单说就是","其实"],"transitionMode":["然后的话","再其次","还有一点","另外"]},"signatureDialogues":[{"context":"客户质疑地块大小","dialogue":"哥,14亩确实不大,但你要在成都2.5环内城买房,这种是普遍现象。你看万景和绿城都是13亩,中铁建只有8.8亩,339那个帮泰只有11亩。我们虽然地小,但楼间距开阔啊,看过去都是200多米!而且小小区人少安静,圈层更纯粹!"},{"context":"客户担心物业费高","dialogue":"姐,我懂你意思,我们也觉得物业费是有点贵。但招商物业是铂金服务,有夜间送外卖、免费宠物喂养、年度保洁这些增值服务。而且前三年开发商补贴一块钱,只需要交5块,跟其他盘差不多!好物业能让房子后期保值增值更多!"},{"context":"客户犹豫价格","dialogue":"说实话,这个地段的地价都比二八板块贵5000多,但我们单价只贵3000。你看龙湖滨江云河颂套内单价都36000了,我们才33000,真的性价比高!现在不买,以后这个板块可能就买不起了。"},{"context":"客户担心小区小不保值","dialogue":"哥,你不用担心小地块不保值,东大街的九龙仓擎天半岛只有两栋楼,现在二手房还能卖3万左右,是当年的豪宅项目。还有望江名门、仁和春天29号院,都是小地块但照样是高端保值盘。核心还是地段,我们在槐树店这个成华区最贵的板块,保值根本没问题!"}],"toneTags":{"enthusiasm":0.8,"patience":0.9,"confidence":0.85,"friendliness":0.8,"persuasion":0.75}}`
|
||||||
|
Run(context.Background(), []byte(reqBody))
|
||||||
|
err := advicer.AdvicerVersionAdd(fiberCtx)
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_AdvicerVersionUpdate(t *testing.T) {
|
||||||
|
reqBody := `{"id":"69804b5a6532131383aeda3a","advicerId":124,"versionDesc":"第三个版本","dialectFeatures":{"region":"四川成都话","intensity":0.6,"KeyWords":null},"personalityTags":["耐心细致","专业务实","经验丰富","善于引导"],"sentencePatterns":{"openingMode":["我给你介绍一下","我们先来看一下","这边请"],"explanationMode":["是这样的","我跟你讲","你发现没得","说白了"],"confirmationMode":["对吧?","是不是嘛?","你晓得不?","明白了噻?","对不对?"],"summaryMode":["所以说","简单说就是","其实"],"transitionMode":["然后的话","再其次","还有一点","另外"]},"signatureDialogues":[{"context":"客户质疑地块大小","dialogue":"哥,14亩确实不大,但你要在成都2.5环内城买房,这种是普遍现象。你看万景和绿城都是13亩,中铁建只有8.8亩,339那个帮泰只有11亩。我们虽然地小,但楼间距开阔啊,看过去都是200多米!而且小小区人少安静,圈层更纯粹!"},{"context":"客户担心物业费高","dialogue":"姐,我懂你意思,我们也觉得物业费是有点贵。但招商物业是铂金服务,有夜间送外卖、免费宠物喂养、年度保洁这些增值服务。而且前三年开发商补贴一块钱,只需要交5块,跟其他盘差不多!好物业能让房子后期保值增值更多!"},{"context":"客户犹豫价格","dialogue":"说实话,这个地段的地价都比二八板块贵5000多,但我们单价只贵3000。你看龙湖滨江云河颂套内单价都36000了,我们才33000,真的性价比高!现在不买,以后这个板块可能就买不起了。"},{"context":"客户担心小区小不保值","dialogue":"哥,你不用担心小地块不保值,东大街的九龙仓擎天半岛只有两栋楼,现在二手房还能卖3万左右,是当年的豪宅项目。还有望江名门、仁和春天29号院,都是小地块但照样是高端保值盘。核心还是地段,我们在槐树店这个成华区最贵的板块,保值根本没问题!"}],"toneTags":{"enthusiasm":0.8,"patience":0.9,"confidence":0.85,"friendliness":0.8,"persuasion":0.75}}`
|
||||||
|
Run(context.Background(), []byte(reqBody))
|
||||||
|
err := advicer.AdvicerVersionUpdate(fiberCtx)
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_VersionList(t *testing.T) {
|
||||||
|
reqBody := `{"id":"69804060c17976e5e21858a8"}`
|
||||||
|
Run(context.Background(), []byte(reqBody))
|
||||||
|
err := advicer.AdvicerVersionList(fiberCtx)
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_AdvicerVersionDel(t *testing.T) {
|
||||||
|
reqBody := `{"id":"698056073059550befc4f0da"}`
|
||||||
|
Run(context.Background(), []byte(reqBody))
|
||||||
|
err := advicer.AdvicerVersionDel(fiberCtx)
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Json(t *testing.T) {
|
||||||
|
responseByte, err := os.ReadFile("./res.json")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
result map[string]interface{}
|
||||||
|
res = make(map[string]mongo_model.AdviceData)
|
||||||
|
)
|
||||||
|
|
||||||
|
if err = json.Unmarshal(responseByte, &result); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for k, v := range result {
|
||||||
|
if _, ok := dataMap[k]; !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var vbyte []byte
|
||||||
|
if vbyte, err = json.Marshal(v); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
newData := dataMap[k].Copy()
|
||||||
|
|
||||||
|
if err = json.Unmarshal(vbyte, newData); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
res[k] = newData
|
||||||
|
}
|
||||||
|
t.Log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file *FileService
|
||||||
|
advicer *AdvicerService
|
||||||
|
configConfig *config.Config
|
||||||
|
fiberCtx *fiber.Ctx
|
||||||
|
)
|
||||||
|
|
||||||
|
// run 函数是程序的入口函数,负责初始化和配置各个组件
|
||||||
|
func Run(ctx context.Context, reqBody []byte) {
|
||||||
|
if reqBody != nil {
|
||||||
|
app := fiber.New()
|
||||||
|
fctx := &fasthttp.RequestCtx{}
|
||||||
|
fctx.Request.Header.SetMethod("POST")
|
||||||
|
fctx.Request.SetBody(reqBody)
|
||||||
|
fctx.Request.Header.SetContentType("application/json")
|
||||||
|
fiberCtx = app.AcquireCtx(fctx)
|
||||||
|
}
|
||||||
|
configConfig, _ = config.LoadConfigWithEnv()
|
||||||
|
// 初始化数据库连接
|
||||||
|
db, _ := utils.NewGormDb(configConfig)
|
||||||
|
rdb := utils.NewRdb(configConfig)
|
||||||
|
advicerImpl := impl.NewAdviceAdvicerImpl(db)
|
||||||
|
advicerVersionMongo := mongo_model.NewAdvicerVersionMongo()
|
||||||
|
advicerTalkSkillMongo := mongo_model.NewAdvicerTalkSkillMongo()
|
||||||
|
advicerClientMongo := mongo_model.NewAdvicerClientMongo()
|
||||||
|
advicerProjectMongo := mongo_model.NewAdvicerProjectMongo()
|
||||||
|
hsyq := third_party.NewHsyq()
|
||||||
|
advicerfilebiz := biz.NewAdviceFileBiz(hsyq)
|
||||||
|
mongo, _ := pkg.NewMongoDb(ctx, configConfig)
|
||||||
|
adviceAdvicerBiz := biz.NewAdviceAdvicerBiz(advicerImpl, advicerVersionMongo, mongo)
|
||||||
|
skillBiz := biz.NewAdviceSkillBiz(advicerTalkSkillMongo, mongo)
|
||||||
|
clientBiz := biz.NewAdviceClientBiz(advicerClientMongo, mongo)
|
||||||
|
projectBiz := biz.NewAdviceProjectBiz(advicerProjectMongo, mongo)
|
||||||
|
chatBiz := biz.NewAdviceChatBiz(hsyq, rdb)
|
||||||
|
file = NewFileService(advicerfilebiz, configConfig)
|
||||||
|
advicer = NewAdvicerService(adviceAdvicerBiz, configConfig)
|
||||||
|
skill = NewTalkSkillService(skillBiz, configConfig)
|
||||||
|
client = NewClientService(clientBiz, configConfig)
|
||||||
|
project = NewProjectService(projectBiz, configConfig)
|
||||||
|
chat = NewChatService(chatBiz, clientBiz, adviceAdvicerBiz, projectBiz, skillBiz, configConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
var dataMap = map[string]mongo_model.AdviceData{
|
||||||
|
"DialectFeatures": &mongo_model.DialectFeatures{},
|
||||||
|
"SentencePatterns": &mongo_model.SentencePatterns{},
|
||||||
|
"PersonalityTags": &mongo_model.PersonalityTags{},
|
||||||
|
"ToneTags": &mongo_model.ToneTags{},
|
||||||
|
"SignatureDialogues": &mongo_model.SignatureDialogues{},
|
||||||
|
"RegionValue": &mongo_model.RegionValue{},
|
||||||
|
"CompetitionComparison": &mongo_model.CompetitionComparison{},
|
||||||
|
"CoreSellingPoints": &mongo_model.CoreSellingPoints{},
|
||||||
|
"SupportingFacilities": &mongo_model.SupportingFacilities{},
|
||||||
|
"DeveloperBacking": &mongo_model.DeveloperBacking{},
|
||||||
|
"NeedsMining": &mongo_model.NeedsMining{},
|
||||||
|
"PainPointResponse": &mongo_model.PainPointResponse{},
|
||||||
|
"ValueBuilding": &mongo_model.ValueBuilding{},
|
||||||
|
"ClosingTechniques": &mongo_model.ClosingTechniques{},
|
||||||
|
"CommunicationRhythm": &mongo_model.CommunicationRhythm{},
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,117 @@
|
||||||
|
package advice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ai_scheduler/internal/biz"
|
||||||
|
"ai_scheduler/internal/config"
|
||||||
|
errorcode "ai_scheduler/internal/data/error"
|
||||||
|
"ai_scheduler/internal/pkg"
|
||||||
|
|
||||||
|
"ai_scheduler/internal/data/mongo_model"
|
||||||
|
"ai_scheduler/internal/entitys"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/gofiber/fiber/v2/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileService 文件处理
|
||||||
|
type ChatService struct {
|
||||||
|
adviceChatBiz *biz.AdviceChatBiz
|
||||||
|
adviceClientBiz *biz.AdviceClientBiz
|
||||||
|
adviceAdvicerBiz *biz.AdviceAdvicerBiz
|
||||||
|
adviceProjectBiz *biz.AdviceProjectBiz
|
||||||
|
adviceSkillBiz *biz.AdviceSkillBiz
|
||||||
|
cfg *config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFileService
|
||||||
|
func NewChatService(
|
||||||
|
adviceChatBiz *biz.AdviceChatBiz,
|
||||||
|
adviceClientBiz *biz.AdviceClientBiz,
|
||||||
|
adviceAdvicerBiz *biz.AdviceAdvicerBiz,
|
||||||
|
adviceProjectBiz *biz.AdviceProjectBiz,
|
||||||
|
adviceSkillBiz *biz.AdviceSkillBiz,
|
||||||
|
cfg *config.Config,
|
||||||
|
) *ChatService {
|
||||||
|
return &ChatService{
|
||||||
|
adviceChatBiz: adviceChatBiz,
|
||||||
|
cfg: cfg,
|
||||||
|
adviceClientBiz: adviceClientBiz,
|
||||||
|
adviceAdvicerBiz: adviceAdvicerBiz,
|
||||||
|
adviceProjectBiz: adviceProjectBiz,
|
||||||
|
adviceSkillBiz: adviceSkillBiz,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ChatService) Regis(c *fiber.Ctx) error {
|
||||||
|
req := &entitys.AdvicerChatRegistReq{}
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(req.AdvicerVersionId) == 0 {
|
||||||
|
return errorcode.ParamErr("AdvicerVersionId is empty")
|
||||||
|
}
|
||||||
|
if len(req.TalkSkillId) == 0 {
|
||||||
|
return errorcode.ParamErr("talkSkillId is empty")
|
||||||
|
}
|
||||||
|
//顾问版本信息
|
||||||
|
versionInfo, err := a.adviceAdvicerBiz.VersionInfo(c.UserContext(), &entitys.AdvicerVersionInfoReq{
|
||||||
|
Id: req.AdvicerVersionId,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
//顾问信息
|
||||||
|
advicerInfo, err := a.adviceAdvicerBiz.AdvicerInfo(c.UserContext(), &entitys.AdvicerInfoReq{
|
||||||
|
AdvicerID: versionInfo.AdvicerId,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
//项目信息
|
||||||
|
projectInfo, err := a.adviceProjectBiz.Info(c.UserContext(), &entitys.AdvicerProjectInfoReq{
|
||||||
|
ProjectId: advicerInfo.ProjectID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
//销售技巧
|
||||||
|
talkSkill, err := a.adviceSkillBiz.Info(c.UserContext(), &entitys.AdvicerTalkSkillInfoReq{
|
||||||
|
Id: req.TalkSkillId,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
//客户信息
|
||||||
|
var clientInfo mongo_model.AdvicerClientMongo
|
||||||
|
if len(req.ClientId) != 0 {
|
||||||
|
|
||||||
|
clientInfo, err = a.adviceClientBiz.Info(c.UserContext(), &entitys.AdvicerClientInfoReq{
|
||||||
|
Id: req.ClientId,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chat := entitys.ChatData{
|
||||||
|
ClientInfo: clientInfo.Entity(),
|
||||||
|
TalkSkill: talkSkill.Entity(),
|
||||||
|
ProjectInfo: projectInfo.Entity(),
|
||||||
|
AdvicerInfo: advicerInfo.Entity(),
|
||||||
|
AdvicerVersion: versionInfo.Entity(),
|
||||||
|
}
|
||||||
|
sessionId, err := a.adviceChatBiz.Regis(c.UserContext(), &chat)
|
||||||
|
log.Info(sessionId)
|
||||||
|
return pkg.HandleResponse(c, sessionId, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ChatService) Chat(c *fiber.Ctx) error {
|
||||||
|
req := &entitys.AdvicerChatReq{}
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := a.adviceChatBiz.Chat(c.UserContext(), req)
|
||||||
|
log.Info(res)
|
||||||
|
return pkg.HandleResponse(c, res, err)
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,61 @@
|
||||||
|
package advice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ai_scheduler/internal/biz"
|
||||||
|
"ai_scheduler/internal/config"
|
||||||
|
"ai_scheduler/internal/entitys"
|
||||||
|
"ai_scheduler/internal/pkg"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ClientService 数据处理
|
||||||
|
type ClientService struct {
|
||||||
|
AdviceClientBiz *biz.AdviceClientBiz
|
||||||
|
cfg *config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDataService
|
||||||
|
func NewClientService(
|
||||||
|
AdviceClientBiz *biz.AdviceClientBiz,
|
||||||
|
cfg *config.Config,
|
||||||
|
) *ClientService {
|
||||||
|
return &ClientService{
|
||||||
|
AdviceClientBiz: AdviceClientBiz,
|
||||||
|
cfg: cfg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *ClientService) Add(c *fiber.Ctx) error {
|
||||||
|
req := &entitys.AdvicerClientAddReq{}
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.AdviceClientBiz.Add(c.UserContext(), req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *ClientService) Update(c *fiber.Ctx) error {
|
||||||
|
req := &entitys.AdvicerrClientUpdateReq{}
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.AdviceClientBiz.Update(c.UserContext(), req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *ClientService) List(c *fiber.Ctx) error {
|
||||||
|
req := &entitys.AdvicerClientListReq{}
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
list, err := d.AdviceClientBiz.List(c.UserContext(), req)
|
||||||
|
return pkg.HandleResponse(c, list, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *ClientService) Del(c *fiber.Ctx) error {
|
||||||
|
req := &entitys.AdvicerClientDelReq{}
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.AdviceClientBiz.Del(c.UserContext(), req)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
package advice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_ClientAdd(t *testing.T) {
|
||||||
|
reqBody := `{"projectId":1,"AdvicerId":1,"personalInfo":{"name":"杜先生","gender":"男","location":"","isFirstHome":false,"familyOrganize":"夫妻+2孩"},"purchasePurpose":{"primaryPurpose":"改善居住条件","secondaryPurpose":"资产保值","decisionMakers":"夫妻双方"},"coreDemands":{"totalBudget":"450-500万","preferredLayout":"140㎡四房三卫","coreAppeal":"户型实用、景观好、社区品质高"},"concerns":["小区小是否保值","价格是否有优惠","开发商交付能力"],"decisionProfile":["注重户型实用性和景观","关注社区品质和后期保值","对价格敏感,希望拿到优惠"]}`
|
||||||
|
Run(context.Background(), []byte(reqBody))
|
||||||
|
err := client.Add(fiberCtx)
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_ClientUpdate(t *testing.T) {
|
||||||
|
reqBody := `{"id":"698199fa0c5f4ae098e009ab","projectId":1,"AdvicerId":1,"personalInfo":{"name":"唐先生1","gender":"男","location":"成都北门","isFirstHome":false,"familyOrganize":"夫妻+1孩+父母同住"},"purchasePurpose":{"primaryPurpose":"改善居住条件","secondaryPurpose":"资产保值,方便子女上学","decisionMakers":"夫妻双方"},"coreDemands":{"totalBudget":"350-400万","preferredLayout":"118㎡四房三卫双套房","coreAppeal":"在预算内满足家庭居住功能,确保房产保值,临近学校"},"concerns":["总价超预算风险","板块保值能力","小区小是否影响居住体验","开发商资金实力"],"decisionProfile":["预算导向,严格控制总价","重点关注户型功能性和实用性","需要对比板块发展潜力","对开发商交付能力有顾虑"]}`
|
||||||
|
Run(context.Background(), []byte(reqBody))
|
||||||
|
err := client.Update(fiberCtx)
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_ClientList(t *testing.T) {
|
||||||
|
reqBody := `{"projectId":1}`
|
||||||
|
Run(context.Background(), []byte(reqBody))
|
||||||
|
err := client.List(fiberCtx)
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_ClientDel(t *testing.T) {
|
||||||
|
reqBody := `{"id":"698056073059550befc4f0da"}`
|
||||||
|
Run(context.Background(), []byte(reqBody))
|
||||||
|
err := advicer.AdvicerVersionDel(fiberCtx)
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
client *ClientService
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
package advice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ai_scheduler/internal/biz"
|
||||||
|
"ai_scheduler/internal/config"
|
||||||
|
"ai_scheduler/internal/entitys"
|
||||||
|
"ai_scheduler/internal/pkg"
|
||||||
|
"ai_scheduler/internal/pkg/file_download"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileService 文件处理
|
||||||
|
type FileService struct {
|
||||||
|
adviceBiz *biz.AdviceFileBiz
|
||||||
|
cfg *config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFileService
|
||||||
|
func NewFileService(
|
||||||
|
adviceBiz *biz.AdviceFileBiz,
|
||||||
|
cfg *config.Config,
|
||||||
|
) *FileService {
|
||||||
|
return &FileService{
|
||||||
|
adviceBiz: adviceBiz,
|
||||||
|
cfg: cfg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *FileService) WordAna(c *fiber.Ctx) error {
|
||||||
|
req := &entitys.WordAnaReq{}
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// URL 解码
|
||||||
|
fileURL, err := url.PathUnescape(req.WordFileUrl)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("URL 解码失败")
|
||||||
|
}
|
||||||
|
result, _, err := file_download.GetWordTextFromURL(fileURL, file_download.IsWordFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ana, err := a.adviceBiz.WordAna(context.Background(), result)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = c.JSON(ana)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *FileService) WordAnat(path string) ([]byte, error) {
|
||||||
|
|
||||||
|
// URL 解码
|
||||||
|
fileURL, err := url.PathUnescape(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("URL 解码失败")
|
||||||
|
}
|
||||||
|
result, _, err := file_download.GetWordTextFromURL(fileURL, file_download.IsWordFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ana, err := a.adviceBiz.WordAna(context.Background(), result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pkg.JsonByteIgonErr(ana), err
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
package advice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ai_scheduler/internal/biz"
|
||||||
|
"ai_scheduler/internal/config"
|
||||||
|
"ai_scheduler/internal/entitys"
|
||||||
|
"ai_scheduler/internal/pkg"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProjectService 数据处理
|
||||||
|
type ProjectService struct {
|
||||||
|
adviceProjectBiz *biz.AdviceProjectBiz
|
||||||
|
cfg *config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewProjectService
|
||||||
|
func NewProjectService(
|
||||||
|
adviceProjectBiz *biz.AdviceProjectBiz,
|
||||||
|
cfg *config.Config,
|
||||||
|
) *ProjectService {
|
||||||
|
return &ProjectService{
|
||||||
|
adviceProjectBiz: adviceProjectBiz,
|
||||||
|
cfg: cfg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *ProjectService) Add(c *fiber.Ctx) error {
|
||||||
|
req := &entitys.AdvicerProjectAddReq{}
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.adviceProjectBiz.Add(c.UserContext(), req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *ProjectService) Update(c *fiber.Ctx) error {
|
||||||
|
req := &entitys.AdvicerrProjectUpdateReq{}
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.adviceProjectBiz.Update(c.UserContext(), req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *ProjectService) Info(c *fiber.Ctx) error {
|
||||||
|
req := &entitys.AdvicerProjectInfoReq{}
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
list, err := d.adviceProjectBiz.Info(c.UserContext(), req)
|
||||||
|
return pkg.HandleResponse(c, list, err)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package advice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_ProjectAdd(t *testing.T) {
|
||||||
|
reqBody := `{"projectId":1,"projectInfo":{"projectName":"中信资产项目 *","projectAddress":"成华区槐树店板块,2.5环内侧 *","area":"成华区","houseTypes":[{"name":"118平户型","buildArea":"118㎡","innerArea":"132㎡ *","unitPrice":"约33000元/㎡ * (套内单价)","totalPrice":"约389万元 *"},{"name":"核心主力户型 *","buildArea":"120-140㎡ *","innerArea":"高得房率,约132-156㎡ *","unitPrice":"约32000-35000元/㎡ *","totalPrice":"约384-490万元 *"},{"name":"大平层户型 *","buildArea":"140㎡+ *","innerArea":"实得约156㎡+ * (得房率110%+)","unitPrice":"约35000元/㎡ *","totalPrice":"约490万元+ *"}]},"competitionComparison":{"华润置地云上":{"价格对比":"他们单价35000左右,还靠近安置小区","优势突出":"我们小区纯粹,没有安置小区,居住环境更安静","优点承认":"华润品牌影响力大"},"招商璟宸序":{"价格对比":"他们单价32000左右,但地段在28板块,地价比我们便宜5000","优势突出":"我们地段在槐树店,是成华区核心板块,未来增值空间更大","优点承认":"招商品牌大,物业也是自己的"},"龙湖滨江云河颂":{"价格对比":"他们单价32000-35000,但得房率只有95%,套内算下来36000+","优势突出":"我们118平实得132平,套内单价才33000,还做四房三卫,他们143平才双卫","优点承认":"龙湖位置确实好,能看沙河公园"}},"coreSellingPoints":{"产品配置高端":"3.2米层高、全玻璃幕墙+三层中空玻璃、无机磨石车库、方太Y9烟机、高仪卫浴、国千木作柜体","地段稀缺性":"成华区2.5环内侧槐树店板块,成华区房价天花板区域,被万象城、339、火车东站包围","得房率高":"118平实得132平,得房率超110%,四房三卫双套房设计,市面上同面积段没有竞品","物业优质":"招商局铂金物业,有夜间送外卖、免费宠物喂养、全屋保洁等增值服务"},"developerBacking":{"公司实力":"中信资产,多元化民营企业,涉及矿产、有色金属、生态农业、地产开发","合作方":"招商局物业,百年央企,首次与外部企业合作提供铂金服务","开发经验":"2011年开始做地产,在宜宾、贵州开发超过500万平米,成都是首个项目","资金安全":"在河南渑池有两座优质铝矿,每年稳定收入10亿,现金流充足"},"regionValue":{"区位层级":["成华区2.5环内侧,槐树店板块是成华区number one板块","北接339商圈,西靠万象城,东临火车东站","属于槐树店崔怀板块,成华区目前最好的开发板块"],"发展规划":["槐树店是成华区未来的富人区,板块还有大量待开发土地","未来这个区域会形成连片高端居住区,城市界面会越来越好"],"地价论证":["我们地价19500,比28板块贵5000多","华润华城府地价20400,我们和它同属一个板块,地价差距小","面粉都这么贵,面包不可能便宜"],"板块热度":["从2021年新希望锦麟一品开始,这边全是高端盘","龙湖最高端的滨江云河颂在这里,卖得特别火","各大品牌开发商都在这边拿地,未来全是改善盘"]},"supportingFacilities":{"交通配套":{"地铁":"7号线双店路站350米,4号线槐树店站550米,未来还有12号线","通达性":"到万象城2个站,到华西锦江院区30分钟车程","道路":"中环路、成洛大道,到春熙路5个站,到火车东站2个站"},"医疗配套":{"三甲医院":"市六医院、市二医院3公里内","顶尖医疗":"华西医院锦江院区、华西本部30分钟车程"},"商业配套":{"便利性":"到万象城2个站,到339商圈3个站","核心商圈":"万象城商圈、339商圈","社区商业":"和悦广场、东方希望上东里"},"教育配套":{"小学":"成华小学,1-3年级在项目附近,4-6年级在二环内","生源优势":"周边新盘都是300万+,生源纯粹"}}}`
|
||||||
|
Run(context.Background(), []byte(reqBody))
|
||||||
|
err := project.Add(fiberCtx)
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_ProjectUpdate(t *testing.T) {
|
||||||
|
reqBody := `{"id":"69804b5a6532131383aeda3a","advicerId":124,"versionDesc":"第三个版本","dialectFeatures":{"region":"四川成都话","intensity":0.6,"KeyWords":null},"personalityTags":["耐心细致","专业务实","经验丰富","善于引导"],"sentencePatterns":{"openingMode":["我给你介绍一下","我们先来看一下","这边请"],"explanationMode":["是这样的","我跟你讲","你发现没得","说白了"],"confirmationMode":["对吧?","是不是嘛?","你晓得不?","明白了噻?","对不对?"],"summaryMode":["所以说","简单说就是","其实"],"transitionMode":["然后的话","再其次","还有一点","另外"]},"signatureDialogues":[{"context":"客户质疑地块大小","dialogue":"哥,14亩确实不大,但你要在成都2.5环内城买房,这种是普遍现象。你看万景和绿城都是13亩,中铁建只有8.8亩,339那个帮泰只有11亩。我们虽然地小,但楼间距开阔啊,看过去都是200多米!而且小小区人少安静,圈层更纯粹!"},{"context":"客户担心物业费高","dialogue":"姐,我懂你意思,我们也觉得物业费是有点贵。但招商物业是铂金服务,有夜间送外卖、免费宠物喂养、年度保洁这些增值服务。而且前三年开发商补贴一块钱,只需要交5块,跟其他盘差不多!好物业能让房子后期保值增值更多!"},{"context":"客户犹豫价格","dialogue":"说实话,这个地段的地价都比二八板块贵5000多,但我们单价只贵3000。你看龙湖滨江云河颂套内单价都36000了,我们才33000,真的性价比高!现在不买,以后这个板块可能就买不起了。"},{"context":"客户担心小区小不保值","dialogue":"哥,你不用担心小地块不保值,东大街的九龙仓擎天半岛只有两栋楼,现在二手房还能卖3万左右,是当年的豪宅项目。还有望江名门、仁和春天29号院,都是小地块但照样是高端保值盘。核心还是地段,我们在槐树店这个成华区最贵的板块,保值根本没问题!"}],"toneTags":{"enthusiasm":0.8,"patience":0.9,"confidence":0.85,"friendliness":0.8,"persuasion":0.75}}`
|
||||||
|
Run(context.Background(), []byte(reqBody))
|
||||||
|
err := project.Update(fiberCtx)
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_ProjectInfo(t *testing.T) {
|
||||||
|
reqBody := `{"id":"69804b5a6532131383aeda3a","advicerId":124,"versionDesc":"第三个版本","dialectFeatures":{"region":"四川成都话","intensity":0.6,"KeyWords":null},"personalityTags":["耐心细致","专业务实","经验丰富","善于引导"],"sentencePatterns":{"openingMode":["我给你介绍一下","我们先来看一下","这边请"],"explanationMode":["是这样的","我跟你讲","你发现没得","说白了"],"confirmationMode":["对吧?","是不是嘛?","你晓得不?","明白了噻?","对不对?"],"summaryMode":["所以说","简单说就是","其实"],"transitionMode":["然后的话","再其次","还有一点","另外"]},"signatureDialogues":[{"context":"客户质疑地块大小","dialogue":"哥,14亩确实不大,但你要在成都2.5环内城买房,这种是普遍现象。你看万景和绿城都是13亩,中铁建只有8.8亩,339那个帮泰只有11亩。我们虽然地小,但楼间距开阔啊,看过去都是200多米!而且小小区人少安静,圈层更纯粹!"},{"context":"客户担心物业费高","dialogue":"姐,我懂你意思,我们也觉得物业费是有点贵。但招商物业是铂金服务,有夜间送外卖、免费宠物喂养、年度保洁这些增值服务。而且前三年开发商补贴一块钱,只需要交5块,跟其他盘差不多!好物业能让房子后期保值增值更多!"},{"context":"客户犹豫价格","dialogue":"说实话,这个地段的地价都比二八板块贵5000多,但我们单价只贵3000。你看龙湖滨江云河颂套内单价都36000了,我们才33000,真的性价比高!现在不买,以后这个板块可能就买不起了。"},{"context":"客户担心小区小不保值","dialogue":"哥,你不用担心小地块不保值,东大街的九龙仓擎天半岛只有两栋楼,现在二手房还能卖3万左右,是当年的豪宅项目。还有望江名门、仁和春天29号院,都是小地块但照样是高端保值盘。核心还是地段,我们在槐树店这个成华区最贵的板块,保值根本没问题!"}],"toneTags":{"enthusiasm":0.8,"patience":0.9,"confidence":0.85,"friendliness":0.8,"persuasion":0.75}}`
|
||||||
|
Run(context.Background(), []byte(reqBody))
|
||||||
|
err := project.Info(fiberCtx)
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var project *ProjectService
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
package advice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ai_scheduler/internal/biz"
|
||||||
|
"ai_scheduler/internal/config"
|
||||||
|
"ai_scheduler/internal/entitys"
|
||||||
|
"ai_scheduler/internal/pkg"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TalkSkillService 数据处理
|
||||||
|
type TalkSkillService struct {
|
||||||
|
adviceSkillBiz *biz.AdviceSkillBiz
|
||||||
|
cfg *config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTalkSkillService
|
||||||
|
func NewTalkSkillService(
|
||||||
|
adviceSkillBiz *biz.AdviceSkillBiz,
|
||||||
|
cfg *config.Config,
|
||||||
|
) *TalkSkillService {
|
||||||
|
return &TalkSkillService{
|
||||||
|
adviceSkillBiz: adviceSkillBiz,
|
||||||
|
cfg: cfg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *TalkSkillService) TalkSkillAdd(c *fiber.Ctx) error {
|
||||||
|
req := &entitys.AdvicerTalkSkillAddReq{}
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.adviceSkillBiz.VersionAdd(c.UserContext(), req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *TalkSkillService) TalkSkillUpdate(c *fiber.Ctx) error {
|
||||||
|
req := &entitys.AdvicerTalkSkillUpdateReq{}
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.adviceSkillBiz.VersionUpdate(c.UserContext(), req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *TalkSkillService) TalkSkillList(c *fiber.Ctx) error {
|
||||||
|
req := &entitys.AdvicerTalkSkillListReq{}
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
list, err := d.adviceSkillBiz.VersionList(c.UserContext(), req)
|
||||||
|
return pkg.HandleResponse(c, list, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *TalkSkillService) TalkSkillDel(c *fiber.Ctx) error {
|
||||||
|
req := &entitys.AdvicerTalkSkillDelReq{}
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.adviceSkillBiz.VersionDel(c.UserContext(), req)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
package advice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_TalkSkillAdd(t *testing.T) {
|
||||||
|
reqBody := `{"advicerId":124,"projectId":1,"desc":"第一版本","closingTechniques":{"优惠策略":{"价格优惠":["今天定的话,我可以跟领导申请额外1个点的折扣","买车位的话,总价再给你优惠2万块","一次性付款的话,还能再降1个点"],"附加价值":["送一年物业费","送品牌家电礼包","优先选车位"]},"决策推动":{"小步推进":["要不先交个小定保留房源?","可以先排个号,有优惠我第一时间通知你","今天不定的话,我帮你留意着这个好楼层"]},"紧迫感营造":{"房源稀缺":["118的户型只剩20多套了,好楼层只有这几套了","这栋楼一共就60户,卖一套少一套,现在不订可能就没了"],"时间紧迫":["今天是周末活动最后一天,这个价格只有今天能申请","月底冲业绩,领导给的权限最大,过了今天就没这个优惠了"]}},"communicationRhythm":{"开场阶段":{"关键动作":"亲切称呼,简单寒暄,确认客户关注点","时间占比":"5%","目标":"建立关系,了解需求"},"样板间带看":{"关键动作":"讲解户型功能→展示装修标准→强调细节品质","时间占比":"40%","目标":"体验产品优势"},"沙盘讲解":{"关键动作":"板块价值→周边配套→项目亮点→开发商介绍","时间占比":"30%","目标":"建立价值认知"},"洽谈阶段":{"关键动作":"算价格→对比竞品→解决顾虑→逼定成交","时间占比":"25%","目标":"促单成交"}},"needsMining":{"居住需求":["几个人住?有老人小孩吗?","主要是自住还是考虑投资?","现在住哪里?想改善哪些方面?"],"教育需求":["小孩在哪里上学?对学校距离有要求吗?","看重学校的哪些方面?"],"通勤需求":["在哪个位置上班?","主要开车还是坐地铁?","对地铁距离有要求吗?"],"预算需求":["你们总价想控制在多少以内?","是考虑按揭还是一次性?","月供能接受多少范围?"]},"painPointResponse":{"小区太小":{"对比竞品":"仁和春天29号院才29亩,照样是千万级豪宅","承认事实":"14亩确实不大","普遍现象":"2.5环内都是小地块,万景13亩,中铁建8.8亩,339的邦泰才11亩","转化优势":"但小区人少安静,楼间距开阔,200多米的楼间距比很多大楼盘还宽"},"担心保值":{"举例论证":"你看九龙仓擎天半岛,就两栋楼,现在二手房还是卖3万多;望江名门一栋楼,照样是千万级豪宅","承认顾虑":"我理解你担心小小区不保值","核心逻辑":"保值看的是地段,我们槐树店是成华区核心板块,地价19500,未来只会涨不会跌"},"物业费高":{"价值分析":"但6块里有2块是增值服务,招商物业是铂金服务,这些服务外面花钱都买不来","价格补贴":"前三年开发商补贴1块,你只需要交5块,和其他改善盘差不多","理解感受":"我懂你觉得6块有点贵"}},"valueBuilding":{"产品价值塑造":["我们是用改善的价格,买豪宅的配置","3.2米层高、全落地窗、无机磨石车库,这些都是千万级豪宅的标配","118平实得132平,得房率超过110%,市面上找不到第二家"],"地段价值塑造":["买房最重要的是地段、地段、还是地段","2.5环内的核心地段卖一块少一块,不可再生","槐树店是成华区房价天花板,买这里的房子保值有保障"]}}`
|
||||||
|
Run(context.Background(), []byte(reqBody))
|
||||||
|
err := skill.TalkSkillAdd(fiberCtx)
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_TalkSkillUpdate(t *testing.T) {
|
||||||
|
reqBody := `{"id":"698063ff5215bdb9c6344e88","advicerId":124,"projectId":3,"desc":"第0版本","closingTechniques":{"优惠策略":{"价格优惠":["双十一特价,118㎡优惠后360-400万,140㎡优惠后450-500万","渠道客户可额外申请优惠,相当于多一个点左右的优惠"],"附加价值":["车位双十一特惠,5.3米长车位9.8万,5.1米长车位8.8万"]},"决策推动":{"小步推进":["要不先交个小定保留房源?","可以先排个号,有优惠优先通知你","今天不定的话,我帮你留意好楼层"]},"紧迫感营造":{"房源稀缺":["118㎡只剩部分楼层,140㎡只有二十多套公园景观房","好楼层卖一套少一套,性价比高的楼层不多了"],"时间紧迫":["现在是双十一/年底冲刺,有特价优惠","优惠是阶段性的,错过就没有了"]}},"communicationRhythm":{"开场阶段":{"关键动作":"亲切称呼,简单寒暄,确认看房重点","时间占比":"5%","目标":"建立关系,了解需求"},"样板间带看":{"关键动作":"细节讲解→户型优势→空间体验→竞品对比","时间占比":"40%","目标":"强化产品感知"},"沙盘讲解":{"关键动作":"板块价值→周边配套→项目亮点→开发商介绍","时间占比":"30%","目标":"建立价值认知"},"洽谈阶段":{"关键动作":"需求匹配→痛点应对→优惠释放→决策推动","时间占比":"25%","目标":"解决顾虑,促进成交"}},"needsMining":{"居住需求":["几个人住?有老人小孩吗?","主要是自住还是考虑投资?","现在住哪里?想改善哪些方面?","对房间数量、卫生间数量有要求吗?"],"通勤需求":["在哪个位置上班?","主要开车还是坐地铁?","对地铁距离有要求吗?"],"预算需求":["你们总价想控制在多少以内?","是考虑按揭还是一次性?","月供能接受多少范围?"]},"painPointResponse":{"地块太小":{"对比竞品":"339的邦泰才11亩,人家是千万级豪宅","承认事实":"14亩确实不大","普遍现象":"2.5环内都是小地块,万景13亩,中铁建8.8亩","转化优势":"但人少安静,圈层更纯粹,楼间距反而更开阔"},"客户质疑开发商实力":{"合作背书":"招商物业首次外部合作,品牌物业认可开发商实力","实力展示":"公司有6000万吨铝矿,年稳定收入10亿,现金流雄厚","开发经验":"做房地产14年,在宜宾、贵州开发超500万平米项目"},"担心南侧住宅用地遮挡阳光":{"澄清方向":"我们主采光面朝南,南侧住宅用地规划会错开楼间距,不会遮挡","竞品类比":"南侧用地会做高端大户型,开发商会考虑业主采光,不会影响我们的日照"},"担心新小区不保值":{"产品稀缺":"新规产品得房率高,未来政策限制赠送,产品竞争力强","地段支撑":"槐树店是成华区地价最高的板块,周边都是高端项目,地价和高端项目带动房价保值","需求保障":"未来大量业主会置换新规产品,该板块是首选,供需决定价值"},"物业费高":{"价值分析":"但6块里1块是增值服务(保洁、送外卖、宠物服务)","价格补贴":"前三年补贴到5块,跟其他盘差不多","未来可协商":"后期业主委员会可以协商调整物业费,仁恒滨河湾就从7.9谈到5块","理解感受":"我懂你,我们也觉得有点贵"}},"valueBuilding":{"产品价值塑造":["我们是用改善的价格,买豪宅的标准","很多细节都是千万级豪宅才有的配置","外立面成本比竞品高,单价却相当","3.2米层高、无机磨石车库这些都是高端配置"],"地段价值塑造":["买房最重要的是地段、地段、还是地段","核心地段的核心资产才保值增值","2.5环内的地卖一块少一块,不可再生","槐树店是成华区地价最高的板块,地价高对应房价支撑强"]}}`
|
||||||
|
Run(context.Background(), []byte(reqBody))
|
||||||
|
err := skill.TalkSkillUpdate(fiberCtx)
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_TalkSkillList(t *testing.T) {
|
||||||
|
reqBody := `{"projectId":1}`
|
||||||
|
Run(context.Background(), []byte(reqBody))
|
||||||
|
err := skill.TalkSkillList(fiberCtx)
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_TalkSkillDel(t *testing.T) {
|
||||||
|
reqBody := `{"id":"698056073059550befc4f0da"}`
|
||||||
|
Run(context.Background(), []byte(reqBody))
|
||||||
|
err := skill.TalkSkillDel(fiberCtx)
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
skill *TalkSkillService
|
||||||
|
)
|
||||||
|
|
||||||
|
// run 函数是程序的入口函数,负责初始化和配置各个组件
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
dingtalk2 "ai_scheduler/internal/biz/handle/dingtalk"
|
dingtalk2 "ai_scheduler/internal/biz/handle/dingtalk"
|
||||||
"ai_scheduler/internal/biz/handle/qywx"
|
"ai_scheduler/internal/biz/handle/qywx"
|
||||||
"ai_scheduler/internal/biz/llm_service"
|
"ai_scheduler/internal/biz/llm_service"
|
||||||
|
"ai_scheduler/internal/biz/llm_service/third_party"
|
||||||
"ai_scheduler/internal/biz/tools_regis"
|
"ai_scheduler/internal/biz/tools_regis"
|
||||||
"ai_scheduler/internal/config"
|
"ai_scheduler/internal/config"
|
||||||
"ai_scheduler/internal/data/impl"
|
"ai_scheduler/internal/data/impl"
|
||||||
|
|
@ -29,13 +30,13 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_Report(t *testing.T) {
|
func Test_Report(t *testing.T) {
|
||||||
run()
|
Run()
|
||||||
a := cronService.CronReportSendDingTalk(context.Background())
|
a := cronService.CronReportSendDingTalk(context.Background())
|
||||||
t.Log(a)
|
t.Log(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_Report_QYWX(t *testing.T) {
|
func Test_Report_QYWX(t *testing.T) {
|
||||||
run()
|
Run()
|
||||||
a := cronService.CronReportSendQywx(context.Background())
|
a := cronService.CronReportSendQywx(context.Background())
|
||||||
t.Log(a)
|
t.Log(a)
|
||||||
}
|
}
|
||||||
|
|
@ -48,7 +49,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// run 函数是程序的入口函数,负责初始化和配置各个组件
|
// run 函数是程序的入口函数,负责初始化和配置各个组件
|
||||||
func run() {
|
func Run() {
|
||||||
// 加载测试配置
|
// 加载测试配置
|
||||||
// configConfig, err = config.LoadConfigWithTest()
|
// configConfig, err = config.LoadConfigWithTest()
|
||||||
configConfig, err = config.LoadConfigWithEnv()
|
configConfig, err = config.LoadConfigWithEnv()
|
||||||
|
|
@ -62,6 +63,7 @@ func run() {
|
||||||
botConfigImpl := impl.NewBotConfigImpl(db)
|
botConfigImpl := impl.NewBotConfigImpl(db)
|
||||||
botGroupImpl := impl.NewBotGroupImpl(db)
|
botGroupImpl := impl.NewBotGroupImpl(db)
|
||||||
botUserImpl := impl.NewBotUserImpl(db)
|
botUserImpl := impl.NewBotUserImpl(db)
|
||||||
|
reportDailyCacheImpl := impl.NewReportDailyCacheImpl(db)
|
||||||
// 初始化Do业务对象
|
// 初始化Do业务对象
|
||||||
doDo := do.NewDo(sessionImpl, sysImpl, taskImpl, chatHisImpl, configConfig)
|
doDo := do.NewDo(sessionImpl, sysImpl, taskImpl, chatHisImpl, configConfig)
|
||||||
// 初始化Ollama客户端
|
// 初始化Ollama客户端
|
||||||
|
|
@ -92,15 +94,11 @@ func run() {
|
||||||
// 初始化Ollama服务
|
// 初始化Ollama服务
|
||||||
ollamaService := llm_service.NewOllamaGenerate(client, utils_vllmClient, configConfig, chatHisImpl)
|
ollamaService := llm_service.NewOllamaGenerate(client, utils_vllmClient, configConfig, chatHisImpl)
|
||||||
// 初始化工具管理器
|
// 初始化工具管理器
|
||||||
manager := tools.NewManager(configConfig, client)
|
manager := tools.NewManager(configConfig, client, rdb)
|
||||||
// 初始化钉钉认证客户端
|
|
||||||
oauth2Client, _ := dingtalk.NewOauth2Client(rdb)
|
|
||||||
// 初始化钉钉联系人客户端
|
// 初始化钉钉联系人客户端
|
||||||
contactClient, _ := dingtalk.NewContactClient(oauth2Client)
|
contactClient, _ := dingtalk.NewContactClient(configConfig)
|
||||||
// 初始化钉钉记事本客户端
|
// 初始化钉钉记事本客户端
|
||||||
notableClient, _ := dingtalk.NewNotableClient(oauth2Client)
|
notableClient, _ := dingtalk.NewNotableClient(configConfig)
|
||||||
// 初始化钉钉卡片客户端
|
|
||||||
cardClient, _ := dingtalk.NewCardClient(oauth2Client)
|
|
||||||
// 初始化工具注册
|
// 初始化工具注册
|
||||||
toolRegis := tools_regis.NewToolsRegis(botToolsImpl)
|
toolRegis := tools_regis.NewToolsRegis(botToolsImpl)
|
||||||
// 初始化机器人聊天历史实现层
|
// 初始化机器人聊天历史实现层
|
||||||
|
|
@ -121,13 +119,15 @@ func run() {
|
||||||
botGroupConfigImpl := impl.NewBotGroupConfigImpl(db)
|
botGroupConfigImpl := impl.NewBotGroupConfigImpl(db)
|
||||||
botGroupQywxImpl := impl.NewBotGroupQywxImpl(db)
|
botGroupQywxImpl := impl.NewBotGroupQywxImpl(db)
|
||||||
qywxAuth := qywx.NewAuth(configConfig, rdb)
|
qywxAuth := qywx.NewAuth(configConfig, rdb)
|
||||||
|
macro := do.NewMacro(botGroupImpl, reportDailyCacheImpl, configConfig, rdb)
|
||||||
group := qywx.NewGroup(botGroupQywxImpl, qywxAuth)
|
group := qywx.NewGroup(botGroupQywxImpl, qywxAuth)
|
||||||
other := qywx.NewOther(qywxAuth)
|
other := qywx.NewOther(qywxAuth)
|
||||||
qywxAppBiz := biz.NewQywxAppBiz(configConfig, botGroupQywxImpl, group, other)
|
qywxAppBiz := biz.NewQywxAppBiz(configConfig, botGroupQywxImpl, group, other)
|
||||||
groupConfigBiz := biz.NewGroupConfigBiz(toolRegis, utils_ossClient, botGroupConfigImpl, botConfigImpl, registry, configConfig, impl.NewReportDailyCacheImpl(db), rdb, manager, cardClient)
|
groupConfigBiz := biz.NewGroupConfigBiz(toolRegis, utils_ossClient, botGroupConfigImpl, registry, configConfig, reportDailyCacheImpl, rdb, macro, manager, handle)
|
||||||
macro := do.NewMacro(botGroupImpl, impl.NewReportDailyCacheImpl(db))
|
dingTalkBotBiz := biz.NewDingTalkBotBiz(doDo, handle, botConfigImpl, botGroupImpl, user, botChatHisImpl, reportDailyCacheImpl, manager, configConfig, sendCardClient, groupConfigBiz, macro)
|
||||||
issueImpl := impl.NewIssueImpl(db)
|
|
||||||
dingTalkBotBiz := biz.NewDingTalkBotBiz(doDo, handle, botConfigImpl, botGroupImpl, botGroupConfigImpl, user, botChatHisImpl, botUserImpl, impl.NewReportDailyCacheImpl(db), manager, configConfig, sendCardClient, groupConfigBiz, macro, oauth2Client, oldClient, cardClient, rdb, issueImpl, sysImpl)
|
|
||||||
// 初始化钉钉机器人服务
|
// 初始化钉钉机器人服务
|
||||||
cronService = NewCronService(configConfig, dingTalkBotBiz, qywxAppBiz, groupConfigBiz)
|
cronService = NewCronService(configConfig, dingTalkBotBiz, qywxAppBiz, groupConfigBiz)
|
||||||
|
hsyq := third_party.NewHsyq()
|
||||||
|
advicerbiz := biz.NewAdviceBiz(hsyq)
|
||||||
|
advicer = NewAdviceService(advicerbiz, configConfig)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ai_scheduler/internal/gateway"
|
"ai_scheduler/internal/gateway"
|
||||||
|
"ai_scheduler/internal/services/advice"
|
||||||
|
|
||||||
"github.com/google/wire"
|
"github.com/google/wire"
|
||||||
)
|
)
|
||||||
|
|
@ -15,4 +16,10 @@ var ProviderSetServices = wire.NewSet(
|
||||||
NewHistoryService,
|
NewHistoryService,
|
||||||
NewCapabilityService,
|
NewCapabilityService,
|
||||||
NewCronService,
|
NewCronService,
|
||||||
|
advice.NewFileService,
|
||||||
|
advice.NewAdvicerService,
|
||||||
|
advice.NewTalkSkillService,
|
||||||
|
advice.NewProjectService,
|
||||||
|
advice.NewClientService,
|
||||||
|
advice.NewChatService,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 测试用的结构体
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
Address string
|
||||||
|
Email string
|
||||||
|
Phone string
|
||||||
|
Balance float64
|
||||||
|
Active bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回指针
|
||||||
|
func NewPersonPtr(name string, age int) *Person {
|
||||||
|
return &Person{
|
||||||
|
Name: name,
|
||||||
|
Age: age,
|
||||||
|
Address: "Some Address",
|
||||||
|
Email: "test@example.com",
|
||||||
|
Phone: "1234567890",
|
||||||
|
Balance: 1000.0,
|
||||||
|
Active: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回值
|
||||||
|
func NewPersonValue(name string, age int) Person {
|
||||||
|
return Person{
|
||||||
|
Name: name,
|
||||||
|
Age: age,
|
||||||
|
Address: "Some Address",
|
||||||
|
Email: "test@example.com",
|
||||||
|
Phone: "1234567890",
|
||||||
|
Balance: 1000.0,
|
||||||
|
Active: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var globalPtr *Person
|
||||||
|
var globalValue Person
|
||||||
|
|
||||||
|
func BenchmarkSmallStruct(b *testing.B) {
|
||||||
|
runtime.GC()
|
||||||
|
b.Run("ValueWithSmallStruct", func(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
p := NewPersonValue("John", 30)
|
||||||
|
globalValue = p
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
runtime.Gosched()
|
||||||
|
runtime.GC()
|
||||||
|
b.Run("PointerWithSmallStruct", func(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
p := NewPersonPtr("John", 30)
|
||||||
|
globalPtr = p
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var globalLargePtr *LargeStruct
|
||||||
|
var globalLargeValue LargeStruct
|
||||||
|
|
||||||
|
func BenchmarkLargeStruct(b *testing.B) {
|
||||||
|
runtime.GC()
|
||||||
|
b.Run("ValueWithLargeStruct", func(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
s := NewLargeValue(1)
|
||||||
|
globalLargeValue = s
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
runtime.Gosched()
|
||||||
|
runtime.GC()
|
||||||
|
b.Run("PointerWithLargeStruct", func(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
p := NewLargePtr(1)
|
||||||
|
globalLargePtr = p
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLargePtr(id int) *LargeStruct {
|
||||||
|
return &LargeStruct{
|
||||||
|
ID: id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回值
|
||||||
|
func NewLargeValue(id int) LargeStruct {
|
||||||
|
return LargeStruct{
|
||||||
|
ID: id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benchmark 大结构体指针
|
||||||
|
type LargeStruct struct {
|
||||||
|
Data [1024]byte
|
||||||
|
ID int
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue