refactor: 重构advice模块及添加MongoDB支持
This commit is contained in:
parent
a950a7b025
commit
6fedb76631
File diff suppressed because one or more lines are too long
|
|
@ -20,7 +20,7 @@ func main() {
|
||||||
log.Fatalf("加载配置失败: %v", err)
|
log.Fatalf("加载配置失败: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
app, cleanup, err := InitializeApp(bc, log.DefaultLogger())
|
app, cleanup, err := InitializeApp(ctx, bc, log.DefaultLogger())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("项目初始化失败: %v", err)
|
log.Fatalf("项目初始化失败: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"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 +27,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// InitializeApp 初始化应用程序
|
// InitializeApp 初始化应用程序
|
||||||
func InitializeApp(*config.Config, log.AllLogger) (*server.Servers, func(), error) {
|
func InitializeApp(ctx context.Context, *config.Config, log.AllLogger) (*server.Servers, func(), error) {
|
||||||
panic(wire.Build(
|
panic(wire.Build(
|
||||||
server.ProviderSetServer,
|
server.ProviderSetServer,
|
||||||
workflow.ProviderSetWorkflow,
|
workflow.ProviderSetWorkflow,
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,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: root:SD###sdf323r343@tcp(121.199.38.107:3306)/sys_ai_test?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
|
||||||
|
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,70 @@
|
||||||
|
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:-rootpassword123}
|
||||||
|
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
|
||||||
|
- ./mongodb/mongod.conf:/etc/mongod.conf
|
||||||
|
command:
|
||||||
|
--config /etc/mongod.conf
|
||||||
|
--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
|
||||||
|
|
@ -1,156 +0,0 @@
|
||||||
package biz
|
|
||||||
|
|
||||||
import (
|
|
||||||
"ai_scheduler/internal/biz/llm_service/third_party"
|
|
||||||
"ai_scheduler/internal/entitys"
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
|
|
||||||
"github.com/volcengine/volcengine-go-sdk/volcengine"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AdviceBiz struct {
|
|
||||||
hsyq *third_party.Hsyq
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewAdviceBiz(hsyq *third_party.Hsyq) *AdviceBiz {
|
|
||||||
return &AdviceBiz{
|
|
||||||
hsyq: hsyq,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
key = "236ba4b6-9daa-4755-b22f-2fd274cd223a"
|
|
||||||
modelName = "doubao-seed-1-8-251228"
|
|
||||||
)
|
|
||||||
|
|
||||||
var dataMap = map[string]string{
|
|
||||||
"DialectFeatures": (&entitys.DialectFeatures{}).Example(),
|
|
||||||
"SentencePatterns": (&entitys.SentencePatterns{}).Example(),
|
|
||||||
"PersonalityTags": (&entitys.PersonalityTags{}).Example(),
|
|
||||||
"ToneTags": (&entitys.ToneTags{}).Example(),
|
|
||||||
"SignatureDialogues": (&entitys.SignatureDialogues{}).Example(),
|
|
||||||
"RegionValue": (&entitys.RegionValue{}).Example(),
|
|
||||||
"CompetitionComparison": (&entitys.CompetitionComparison{}).Example(),
|
|
||||||
"CoreSellingPoints": (&entitys.CoreSellingPoints{}).Example(),
|
|
||||||
"SupportingFacilities": (&entitys.SupportingFacilities{}).Example(),
|
|
||||||
"DeveloperBacking": (&entitys.DeveloperBacking{}).Example(),
|
|
||||||
"NeedsMining": (&entitys.NeedsMining{}).Example(),
|
|
||||||
"PainPointResponse": (&entitys.PainPointResponse{}).Example(),
|
|
||||||
"ValueBuilding": (&entitys.ValueBuilding{}).Example(),
|
|
||||||
"ClosingTechniques": (&entitys.ClosingTechniques{}).Example(),
|
|
||||||
"CommunicationRhythm": (&entitys.CommunicationRhythm{}).Example(),
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AdviceBiz) WordAna(ctx context.Context, wordContent string) error {
|
|
||||||
examples := a.getAllExamples()
|
|
||||||
prompt := a.buildSimplePrompt(wordContent, examples)
|
|
||||||
os.WriteFile("requset.json", []byte(prompt), 0644)
|
|
||||||
anaContent, err := a.callLlm(ctx, prompt)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
os.WriteFile("res.json", []byte(anaContent), 0644)
|
|
||||||
data := a.parseResponse(anaContent)
|
|
||||||
|
|
||||||
jsonData, _ := json.MarshalIndent(data, "", " ")
|
|
||||||
os.WriteFile("extracted.json", jsonData, 0644)
|
|
||||||
fmt.Println("✅ 数据已保存到 extracted.json")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AdviceBiz) callLlm(ctx context.Context, prompt 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 *AdviceBiz) getAllExamples() map[string]string {
|
|
||||||
return dataMap
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AdviceBiz) buildSimplePrompt(wordContent string, examples map[string]string) string {
|
|
||||||
// 最简单的提示词模板
|
|
||||||
template := `分析以下房地产销售对话,按指定格式提取信息:
|
|
||||||
|
|
||||||
对话内容:
|
|
||||||
%s
|
|
||||||
|
|
||||||
请按照以下` + fmt.Sprintf("%d", len(examples)) + `个格式生成JSON数据,key为格式名称,value为对应值:
|
|
||||||
|
|
||||||
%s
|
|
||||||
|
|
||||||
输出要求:
|
|
||||||
1. 每个结构体一个JSON对象
|
|
||||||
2. 所有内容必须严格基于提供的对话原文,不得编造
|
|
||||||
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 ===\n示例:%s\n\n", name, example))
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf(template, wordContent, formats.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AdviceBiz) parseResponse(response string) map[string]interface{} {
|
|
||||||
result := make(map[string]interface{})
|
|
||||||
|
|
||||||
// 按空行分割
|
|
||||||
parts := strings.Split(response, "\n\n")
|
|
||||||
|
|
||||||
for _, part := range parts {
|
|
||||||
part = strings.TrimSpace(part)
|
|
||||||
if part == "" || !strings.Contains(part, "{") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// 找到第一个 { 和最后一个 }
|
|
||||||
start := strings.Index(part, "{")
|
|
||||||
end := strings.LastIndex(part, "}")
|
|
||||||
|
|
||||||
if start == -1 || end == -1 || end <= start {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonStr := part[start : end+1]
|
|
||||||
|
|
||||||
// 尝试解析
|
|
||||||
var data interface{}
|
|
||||||
if err := json.Unmarshal([]byte(jsonStr), &data); err == nil {
|
|
||||||
// 判断是什么结构体
|
|
||||||
for _, name := range getStructNames() {
|
|
||||||
if strings.Contains(jsonStr, `"`+name+`"`) || strings.Contains(part, name) {
|
|
||||||
result[name] = data
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func getStructNames() []string {
|
|
||||||
var res = make([]string, 0, len(dataMap))
|
|
||||||
for k, _ := range dataMap {
|
|
||||||
res = append(res, k)
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
package biz
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ai_scheduler/internal/data/impl"
|
||||||
|
"ai_scheduler/internal/data/model"
|
||||||
|
"ai_scheduler/internal/entitys"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"xorm.io/builder"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AdviceAdvicerBiz struct {
|
||||||
|
advicerImpl *impl.AdviceAdvicerImpl
|
||||||
|
adviceAdvicerVersionImpl *impl.AdviceAdvicerVersionImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdviceAdvicerBiz(
|
||||||
|
advicerImpl *impl.AdviceAdvicerImpl,
|
||||||
|
adviceAdvicerVersionImpl *impl.AdviceAdvicerVersionImpl,
|
||||||
|
) *AdviceAdvicerBiz {
|
||||||
|
return &AdviceAdvicerBiz{
|
||||||
|
advicerImpl: advicerImpl,
|
||||||
|
adviceAdvicerVersionImpl: adviceAdvicerVersionImpl,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
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) VersionUpdate(ctx context.Context, param *entitys.AdvicerVersionInitReq) (err error) {
|
||||||
|
|
||||||
|
if param.VersionID == 0 {
|
||||||
|
_, err = a.adviceAdvicerVersionImpl.Add(param)
|
||||||
|
} else {
|
||||||
|
cond := builder.NewCond()
|
||||||
|
cond = cond.And(builder.Eq{"version_id": param.VersionID})
|
||||||
|
err = a.adviceAdvicerVersionImpl.UpdateByCond(&cond, param)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceAdvicerBiz) VersionList(ctx context.Context, data *entitys.AdvicerVersionListReq) ([]map[string]interface{}, error) {
|
||||||
|
cond := builder.NewCond()
|
||||||
|
cond = cond.And(builder.Eq{"advicer_id": data.AdvicerID})
|
||||||
|
list, err := a.adviceAdvicerVersionImpl.GetRange(&cond)
|
||||||
|
return list, err
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,189 @@
|
||||||
|
package biz
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ai_scheduler/internal/biz/llm_service/third_party"
|
||||||
|
"ai_scheduler/internal/entitys"
|
||||||
|
"ai_scheduler/internal/pkg"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2/log"
|
||||||
|
"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]entitys.AdviceData{
|
||||||
|
"dialectFeatures": &entitys.DialectFeatures{},
|
||||||
|
"sentencePatterns": &entitys.SentencePatterns{},
|
||||||
|
"personalityTags": &entitys.PersonalityTags{},
|
||||||
|
"toneTags": &entitys.ToneTags{},
|
||||||
|
"signatureDialogues": &entitys.SignatureDialogues{},
|
||||||
|
"regionValue": &entitys.RegionValue{},
|
||||||
|
"competitionComparison": &entitys.CompetitionComparison{},
|
||||||
|
"coreSellingPoints": &entitys.CoreSellingPoints{},
|
||||||
|
"supportingFacilities": &entitys.SupportingFacilities{},
|
||||||
|
"developerBacking": &entitys.DeveloperBacking{},
|
||||||
|
"needsMining": &entitys.NeedsMining{},
|
||||||
|
"painPointResponse": &entitys.PainPointResponse{},
|
||||||
|
"valueBuilding": &entitys.ValueBuilding{},
|
||||||
|
"closingTechniques": &entitys.ClosingTechniques{},
|
||||||
|
"communicationRhythm": &entitys.CommunicationRhythm{},
|
||||||
|
"customer": &entitys.Customer{},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceFileBiz) WordAna(ctx context.Context, wordContent string) (map[entitys.AdviceRole]map[string]entitys.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]entitys.AdviceData) map[entitys.AdviceRole]map[string]entitys.AdviceData {
|
||||||
|
var res = make(map[entitys.AdviceRole]map[string]entitys.AdviceData)
|
||||||
|
for k, v := range data {
|
||||||
|
if _, ok := res[v.Role()]; !ok {
|
||||||
|
res[v.Role()] = make(map[string]entitys.AdviceData)
|
||||||
|
}
|
||||||
|
res[v.Role()][k] = v
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceFileBiz) parseResponse(ctx context.Context, responseByte []byte) (resultOutPut map[string]entitys.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]entitys.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
|
||||||
|
}
|
||||||
|
log.Info("token用量:", res.Usage.TotalTokens)
|
||||||
|
return *res.Choices[0].Message.Content.StringValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceFileBiz) getAllExamples() map[string]entitys.AdviceData {
|
||||||
|
return DataMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AdviceFileBiz) buildSimplePrompt(wordContent string, examples map[string]entitys.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, entitys.RoleDesc[example.Role()], example.Desc(), example.Example()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf(template, wordContent, formats.String())
|
||||||
|
}
|
||||||
|
|
@ -22,6 +22,7 @@ var ProviderSetBiz = wire.NewSet(
|
||||||
NewQywxAppBiz,
|
NewQywxAppBiz,
|
||||||
NewGroupConfigBiz,
|
NewGroupConfigBiz,
|
||||||
do.NewMacro,
|
do.NewMacro,
|
||||||
NewAdviceBiz,
|
NewAdviceFileBiz,
|
||||||
third_party.NewHsyq,
|
third_party.NewHsyq,
|
||||||
|
NewAdviceAdvicerBiz,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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"`
|
||||||
|
|
@ -165,6 +166,15 @@ type DB struct {
|
||||||
IsDebug bool `mapstructure:"isDebug"`
|
IsDebug bool `mapstructure:"isDebug"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Mongo struct {
|
||||||
|
Source string `mapstructure:"source"`
|
||||||
|
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"`
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,9 @@ import (
|
||||||
|
|
||||||
type AdviceAdvicerImpl struct {
|
type AdviceAdvicerImpl struct {
|
||||||
dataTemp.DataTemp
|
dataTemp.DataTemp
|
||||||
BaseRepository[model.AiTask]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAdviceAdvicerImplImpl(db *utils.Db) *AdviceAdvicerImpl {
|
func NewAdviceAdvicerImpl(db *utils.Db) *AdviceAdvicerImpl {
|
||||||
return &AdviceAdvicerImpl{
|
return &AdviceAdvicerImpl{
|
||||||
DataTemp: *dataTemp.NewDataTemp(db, new(model.AiAdviceAdvicer)),
|
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)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,7 +18,9 @@ var ProviderImpl = wire.NewSet(
|
||||||
NewBotGroupConfigImpl,
|
NewBotGroupConfigImpl,
|
||||||
NewBotGroupQywxImpl,
|
NewBotGroupQywxImpl,
|
||||||
NewReportDailyCacheImpl,
|
NewReportDailyCacheImpl,
|
||||||
NewAdviceAdvicerImplImpl,
|
NewAdviceAdvicerImpl,
|
||||||
NewAdviceProjectImpl,
|
NewAdviceProjectImpl,
|
||||||
NewAdviceTalkImpl,
|
NewAdviceTalkImpl,
|
||||||
|
NewAdviceAdvicerVersionImpl,
|
||||||
|
NewAdviceClientImpl,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,7 @@ type AiAdviceAdvicer struct {
|
||||||
Birth time.Time `gorm:"column:birth;not null;comment:用户名称" json:"birth"` // 用户名称
|
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:女
|
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"` // 工作年限
|
WorkingYears int32 `gorm:"column:working_years;not null;default:1;comment:工作年限" json:"working_years"` // 工作年限
|
||||||
ContactTags string `gorm:"column:contact_tags;not null;comment:联系方式" json:"contact_tags"` // 联系方式
|
CreateAt *time.Time `gorm:"column:create_at;default:CURRENT_TIMESTAMP" json:"create_at"`
|
||||||
NativeRegion string `gorm:"column:native_region;not null;comment:籍贯" json:"native_region"` // 籍贯
|
|
||||||
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 AiAdviceAdvicer's table name
|
// TableName AiAdviceAdvicer's table name
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,26 @@
|
||||||
package entitys
|
package entitys
|
||||||
|
|
||||||
type WordAnaReq struct {
|
type AdviceData interface {
|
||||||
WordFileUrl string `json:"word_file_url"`
|
Example() string
|
||||||
|
Copy() AdviceData
|
||||||
|
Role() AdviceRole
|
||||||
|
Desc() string
|
||||||
|
}
|
||||||
|
|
||||||
|
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: "客户",
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------顾问
|
// -------顾问
|
||||||
|
|
@ -17,6 +36,18 @@ func (e *DialectFeatures) Example() string {
|
||||||
return `{"region":"四川成都话","intensity":0.4,"key_words":["噻","要得","没得","不晓得","是不是"]}`
|
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 句子模式
|
// SentencePatterns 句子模式
|
||||||
type SentencePatterns struct {
|
type SentencePatterns struct {
|
||||||
OpeningMode []string `json:"openingMode"` //开场模式
|
OpeningMode []string `json:"openingMode"` //开场模式
|
||||||
|
|
@ -30,12 +61,35 @@ func (e *SentencePatterns) Example() string {
|
||||||
return `{"openingMode":["我给你介绍一下","我们先来看一下"],"explanationMode":["是这样的","我跟你讲","你发现没得"],"confirmationMode":["对吧?","是不是嘛?","你晓得不?","明白了噻?"],"summaryMode":["所以说","简单说就是"],"transitionMode":["然后的话","再其次","还有一点"]}`
|
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 个性标签
|
// PersonalityTags 个性标签
|
||||||
type PersonalityTags []string
|
type PersonalityTags []string
|
||||||
|
|
||||||
func (e *PersonalityTags) Example() string {
|
func (e *PersonalityTags) Example() string {
|
||||||
return `["耐心细致","细节控"]`
|
return `["耐心细致","细节控"]`
|
||||||
}
|
}
|
||||||
|
func (e *PersonalityTags) Copy() AdviceData {
|
||||||
|
return new(PersonalityTags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PersonalityTags) Role() AdviceRole {
|
||||||
|
return RoleAdvicer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PersonalityTags) Desc() string {
|
||||||
|
return "个性标签"
|
||||||
|
}
|
||||||
|
|
||||||
// ToneTags 语气标签
|
// ToneTags 语气标签
|
||||||
type ToneTags struct {
|
type ToneTags struct {
|
||||||
|
|
@ -50,8 +104,20 @@ func (e *ToneTags) Example() string {
|
||||||
return `{"enthusiasm":0.8,"patience":0.9,"confidence":0.85,"friendliness":0.75,"persuasion":0.7}`
|
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 "语气标签"
|
||||||
|
}
|
||||||
|
|
||||||
// SignatureDialogues 代表性对话示例
|
// SignatureDialogues 代表性对话示例
|
||||||
type SignatureDialogues struct {
|
type SignatureDialogues []struct {
|
||||||
Context string `json:"context"`
|
Context string `json:"context"`
|
||||||
Dialogue string `json:"dialogue"` //解释
|
Dialogue string `json:"dialogue"` //解释
|
||||||
}
|
}
|
||||||
|
|
@ -60,36 +126,94 @@ 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,真的性价比高!现在不买,以后这个板块可能就买不起了。"}]`
|
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 "代表性对话示例"
|
||||||
|
}
|
||||||
|
|
||||||
// -------项目
|
// -------项目
|
||||||
|
|
||||||
// RegionValue 区域价值话术库
|
// RegionValue 区域价值话术库
|
||||||
type RegionValue map[string]string
|
type RegionValue map[string][]string
|
||||||
|
|
||||||
func (e *RegionValue) Example() string {
|
func (e *RegionValue) Example() string {
|
||||||
return `{"区位层级":["成华区2.5环内侧,这个位置真的稀缺","槐树店板块现在是成华区的number one板块","北接三板桥商圈,西靠万象城,东临火车东站","属于淮舜板块,万象城东的核心位置"],"地价论证":["我们地价19500,华晨府20400,棕榈也是2万+","2.5环内现在地价没有低于19000的","面粉贵了,面包不可能便宜"],"板块热度":["从21年新希望锦麟一品开始,这边全是高端盘","龙湖最高端的滨江系列在这里,新希望的锦麟系列也在这里","各大品牌开发商争相恐后都在这边拿地"],"发展规划":["槐树店板块是棋盘成钢之后第二个富人区","整个板块都是300万到900万的总价段","未来全是改善型住宅,没有刚需盘"]}`
|
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 竞品对比话术
|
// CompetitionComparison 竞品对比话术
|
||||||
type CompetitionComparison map[string]string
|
type CompetitionComparison map[string]map[string]string
|
||||||
|
|
||||||
func (e *CompetitionComparison) Example() string {
|
func (e *CompetitionComparison) Example() string {
|
||||||
return `{"龙湖滨江云河颂":{"优点承认":"龙湖位置确实好,看沙河公园","价格对比":"他们单价32000-35000,但得房率只有95%,套内算下来36000+","优势突出":"我们得房率118平实得132平,套内单价才33000"},"邦泰云锦":{"定位相似":"邦泰也是首个项目,要打造口碑","价格参考":"他们当时12800拿地,现在卖34000","品质对比":"我们外立面全玻璃幕墙,比他们成本高30%"}}`
|
return `{"龙湖滨江云河颂":{"优点承认":"龙湖位置确实好,看沙河公园","价格对比":"他们单价32000-35000,但得房率只有95%,套内算下来36000+","优势突出":"我们得房率118平实得132平,套内单价才33000"},"邦泰云锦":{"定位相似":"邦泰也是首个项目,要打造口碑","价格参考":"他们当时12800拿地,现在卖34000","品质对比":"我们外立面全玻璃幕墙,比他们成本高30%"}}`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CoreSellingPoints 竞品对比话术
|
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
|
type CoreSellingPoints map[string]string
|
||||||
|
|
||||||
func (e *CoreSellingPoints) Example() string {
|
func (e *CoreSellingPoints) Example() string {
|
||||||
return `{"龙湖滨江云河颂":{"优点承认":"龙湖位置确实好,看沙河公园","价格对比":"他们单价32000-35000,但得房率只有95%,套内算下来36000+","优势突出":"我们得房率118平实得132平,套内单价才33000"},"邦泰云锦":{"定位相似":"邦泰也是首个项目,要打造口碑","价格参考":"他们当时12800拿地,现在卖34000","品质对比":"我们外立面全玻璃幕墙,比他们成本高30%"}`
|
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 配套体系
|
// SupportingFacilities 配套体系
|
||||||
type SupportingFacilities map[string]string
|
type SupportingFacilities map[string]map[string]string
|
||||||
|
|
||||||
func (e *SupportingFacilities) Example() string {
|
func (e *SupportingFacilities) Example() string {
|
||||||
return `{"交通配套":{"地铁":"双店路站350米(7号线),槐树店站550米(4号线),未来12号线","道路":"中环路、成洛大道,到春熙路5个站","通达性":"到火车东站2个站,到华西30分钟内"},"教育配套":{"幼儿园":"楼下公立幼儿园","小学":"城市附小锦汇东城(成华区生源最好的学校)","生源优势":"周边新盘都是300万+,生源纯粹"},"医疗配套":{"三甲医院":"市六医院、市二医院3公里内","顶尖医疗":"华西医院锦江院区30分钟车程","便利性":"到华西本部也是30分钟内"}}`
|
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 开发商背书
|
// DeveloperBacking 开发商背书
|
||||||
type DeveloperBacking map[string]string
|
type DeveloperBacking map[string]string
|
||||||
|
|
||||||
|
|
@ -97,39 +221,167 @@ func (e *DeveloperBacking) Example() string {
|
||||||
return `{"公司实力":"中信资产,多元化民营企业","资金安全":"在河南渑池有铝土矿,每年稳定收入10亿","开发经验":"宜宾有5个项目,贵州2个,成都是首个项目","合作方":"招商铂金物业,首次与外部企业合作"}`
|
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 "开发商背书"
|
||||||
|
}
|
||||||
|
|
||||||
// -------销售话术
|
// -------销售话术
|
||||||
|
|
||||||
// NeedsMining 需求挖掘话术
|
// NeedsMining 需求挖掘话术
|
||||||
type NeedsMining map[string]string
|
type NeedsMining map[string][]string
|
||||||
|
|
||||||
func (e *NeedsMining) Example() string {
|
func (e *NeedsMining) Example() string {
|
||||||
return `{"预算需求":["你们总价想控制在多少以内?","是考虑按揭还是一次性?","月供能接受多少范围?"],"居住需求":["几个人住?有老人小孩吗?","主要是自住还是考虑投资?","现在住哪里?想改善哪些方面?"],"通勤需求":["在哪个位置上班?","主要开车还是坐地铁?","对地铁距离有要求吗?"]}`
|
return `{"预算需求":["你们总价想控制在多少以内?","是考虑按揭还是一次性?","月供能接受多少范围?"],"居住需求":["几个人住?有老人小孩吗?","主要是自住还是考虑投资?","现在住哪里?想改善哪些方面?"],"通勤需求":["在哪个位置上班?","主要开车还是坐地铁?","对地铁距离有要求吗?"]}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *NeedsMining) Copy() AdviceData {
|
||||||
|
return new(NeedsMining)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *NeedsMining) Role() AdviceRole {
|
||||||
|
return RoleSkill
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *NeedsMining) Desc() string {
|
||||||
|
return "需求挖掘话术"
|
||||||
|
}
|
||||||
|
|
||||||
// PainPointResponse 痛点应对策略
|
// PainPointResponse 痛点应对策略
|
||||||
type PainPointResponse map[string]string
|
type PainPointResponse map[string]map[string]string
|
||||||
|
|
||||||
func (e *PainPointResponse) Example() string {
|
func (e *PainPointResponse) Example() string {
|
||||||
return `{"地块太小":{"承认事实":"14亩确实不大","普遍现象":"2.5环内都是小地块,万景13亩,中铁建8.8亩","转化优势":"但人少安静,楼间距反而更开阔","对比竞品":"339的邦泰才11亩,人家上千万豪宅"},"物业费高":{"理解感受":"我懂你,我们也觉得有点贵","价值分析":"但6块里3块是增值服务(保洁、送外卖)","价格补贴":"前三年补贴到5块,跟其他盘差不多"}}`
|
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 价值塑造技巧
|
// ValueBuilding 价值塑造技巧
|
||||||
type ValueBuilding map[string]string
|
type ValueBuilding map[string][]string
|
||||||
|
|
||||||
func (e *ValueBuilding) Example() string {
|
func (e *ValueBuilding) Example() string {
|
||||||
return `{"地段价值塑造":["买房最重要的是地段、地段、还是地段","核心地段的核心资产才保值增值","2.5环内的地卖一块少一块,不可再生"],"产品价值塑造":["我们是用改善的价格,买豪宅的标准","很多细节都是3000万豪宅才有的配置","外立面成本比竞品高30%,但单价差不多"]}`
|
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 促单话术
|
// ClosingTechniques 促单话术
|
||||||
type ClosingTechniques map[string]string
|
type ClosingTechniques map[string]map[string][]string
|
||||||
|
|
||||||
func (e *ClosingTechniques) Example() string {
|
func (e *ClosingTechniques) Example() string {
|
||||||
return `{"紧迫感营造":{"时间紧迫":["今天是月底最后一天,领导有压力价格可谈","我们刚刚开盘,还有额外优惠","月底冲业绩,价格最有弹性"],"房源稀缺":["118只剩20多套了,好楼层不多","这栋楼就60户,卖一套少一套","特价房只有这几套,今天不定可能就没了"]},"优惠策略":{"价格优惠":["今天定的话,我可以跟领导申请额外折扣","买车位的话,总价多给两个点优惠","一次性付款再优惠一个点"],"附加价值":["送一年物业费","送品牌家电礼包","优先选车位"]},"决策推动":{"小步推进":["要不先交个小定保留房源?","可以先排个号,有优惠优先通知你","今天不定的话,我帮你留意好楼层"]}}`
|
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 沟通节奏控制
|
// CommunicationRhythm 沟通节奏控制
|
||||||
type CommunicationRhythm map[string]string
|
type CommunicationRhythm map[string]map[string]string
|
||||||
|
|
||||||
func (e *CommunicationRhythm) Example() string {
|
func (e *CommunicationRhythm) Example() string {
|
||||||
return `{"开场阶段":{"时间占比":"5%","目标":"建立关系,了解需求","关键动作":"亲切称呼,简单寒暄,确认看房重点"},"沙盘讲解":{"时间占比":"30%","目标":"建立价值认知","关键动作":"板块价值→周边配套→项目亮点→开发商介绍"}}`
|
return `{"开场阶段":{"时间占比":"5%","目标":"建立关系,了解需求","关键动作":"亲切称呼,简单寒暄,确认看房重点"},"沙盘讲解":{"时间占比":"30%","目标":"建立价值认知","关键动作":"板块价值→周边配套→项目亮点→开发商介绍"}}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *CommunicationRhythm) Copy() AdviceData {
|
||||||
|
return new(CommunicationRhythm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *CommunicationRhythm) Role() AdviceRole {
|
||||||
|
return RoleSkill
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *CommunicationRhythm) Desc() string {
|
||||||
|
return "沟通节奏控制"
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------客户
|
||||||
|
|
||||||
|
// 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":"夫妻+1孩+父母同住"},"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,29 @@
|
||||||
|
package entitys
|
||||||
|
|
||||||
|
type AdvicerInitReq struct {
|
||||||
|
AdvicerID int32 `json:"advicer_id"`
|
||||||
|
ProjectID int32 `json:"project_id"`
|
||||||
|
Name string `json:"name"` // 姓名
|
||||||
|
Birth string `json:"birth"` // 用户名称
|
||||||
|
Gender int32 `json:"gender"` // 1:男,2:女
|
||||||
|
WorkingYears int32 `json:"working_years"` // 工作年限
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerListReq struct {
|
||||||
|
ProjectId int32 `json:"project_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerVersionInitReq struct {
|
||||||
|
VersionID int32 `json:"version_id"`
|
||||||
|
AdvicerID int32 `json:"advicer_id"`
|
||||||
|
VersionDesc string `json:"version_desc"` // 版本名称
|
||||||
|
DialectFeatures string `json:"dialect_features"` // 语言风格
|
||||||
|
SentencePatterns string `json:"sentence_patterns"` // 句子模式
|
||||||
|
ToneTags string `json:"tone_tags"` // 语气标签
|
||||||
|
PersonalityTags string `json:"personality_tags"` // 个性标签
|
||||||
|
SignatureDialogues string `json:"signature_dialogues"` // 代表性对话示例
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdvicerVersionListReq struct {
|
||||||
|
AdvicerID int32 `json:"advicer_id"`
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,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,35 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
return &Mongo{
|
||||||
|
Client: transDBClient,
|
||||||
|
}, func() {
|
||||||
|
transDBClient.Disconnect(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -24,4 +24,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,6 +4,7 @@ import (
|
||||||
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"
|
||||||
|
|
@ -26,7 +27,7 @@ type RouterServer struct {
|
||||||
// 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, advice *services.AdviceService,
|
capabilityService *services.CapabilityService, adviceFile *advice.FileService, adviceData *advice.DataService,
|
||||||
) {
|
) {
|
||||||
app.Use(func(c *fiber.Ctx) error {
|
app.Use(func(c *fiber.Ctx) error {
|
||||||
// 设置 CORS 头
|
// 设置 CORS 头
|
||||||
|
|
@ -100,7 +101,30 @@ func SetupRoutes(app *fiber.App, ChatService *services.ChatService, sessionServi
|
||||||
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 := r.Group("advice/")
|
||||||
advicer.Post("file/word", advice.WordAna)
|
advicer.Post("file/word/ana", adviceFile.WordAna)
|
||||||
|
//顾问
|
||||||
|
advicer.Post("file/advicer/add", adviceData.AdvicerAdd)
|
||||||
|
advicer.Post("file/advicer/update", adviceData.AdvicerUpdate)
|
||||||
|
advicer.Post("file/advicer/list", adviceData.AdvicerList)
|
||||||
|
advicer.Post("file/advicer/version/add", adviceData.AdvicerVersionAdd)
|
||||||
|
advicer.Post("file/advicer/version/update", adviceFile.WordAna)
|
||||||
|
advicer.Post("file/advicer/version/del", adviceFile.WordAna)
|
||||||
|
advicer.Post("file/advicer/version/list", adviceFile.WordAna)
|
||||||
|
//聊天技巧
|
||||||
|
advicer.Post("file/skill/list", adviceFile.WordAna)
|
||||||
|
advicer.Post("file/skill/init", adviceFile.WordAna)
|
||||||
|
advicer.Post("file/skill/add", adviceFile.WordAna)
|
||||||
|
advicer.Post("file/skill/update", adviceFile.WordAna)
|
||||||
|
advicer.Post("file/skill/del", adviceFile.WordAna)
|
||||||
|
advicer.Post("file/skill/list", adviceFile.WordAna)
|
||||||
|
//项目
|
||||||
|
advicer.Post("file/project/init", adviceFile.WordAna)
|
||||||
|
advicer.Post("file/project/add", adviceFile.WordAna)
|
||||||
|
advicer.Post("file/project/update", adviceFile.WordAna)
|
||||||
|
//客户
|
||||||
|
advicer.Post("file/client/init", adviceFile.WordAna)
|
||||||
|
advicer.Post("file/client/add", adviceFile.WordAna)
|
||||||
|
advicer.Post("file/client/update", adviceFile.WordAna)
|
||||||
}
|
}
|
||||||
|
|
||||||
func routerSocket(app *fiber.App, chatService *services.ChatService) {
|
func routerSocket(app *fiber.App, chatService *services.ChatService) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
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/entitys"
|
||||||
|
"ai_scheduler/utils"
|
||||||
|
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/valyala/fasthttp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_WordAna(t *testing.T) {
|
||||||
|
Run(nil)
|
||||||
|
ana, err := file.WordAnat("https://attachment-public.oss-cn-hangzhou.aliyuncs.com/ai-scheduler/data-analytics/word/content3.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([]byte(reqBody))
|
||||||
|
|
||||||
|
err := advicer.AdvicerUpdate(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]entitys.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 *DataService
|
||||||
|
configConfig *config.Config
|
||||||
|
fiberCtx *fiber.Ctx
|
||||||
|
)
|
||||||
|
|
||||||
|
// run 函数是程序的入口函数,负责初始化和配置各个组件
|
||||||
|
func Run(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)
|
||||||
|
advicerImpl := impl.NewAdviceAdvicerImpl(db)
|
||||||
|
advicerVersionImpl := impl.NewAdviceAdvicerVersionImpl(db)
|
||||||
|
hsyq := third_party.NewHsyq()
|
||||||
|
advicerfilebiz := biz.NewAdviceFileBiz(hsyq)
|
||||||
|
advicerbiz := biz.NewAdviceAdvicerBiz(advicerImpl, advicerVersionImpl)
|
||||||
|
file = NewFileService(advicerfilebiz, configConfig)
|
||||||
|
advicer = NewDataService(advicerbiz, configConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
var dataMap = map[string]entitys.AdviceData{
|
||||||
|
"DialectFeatures": &entitys.DialectFeatures{},
|
||||||
|
"SentencePatterns": &entitys.SentencePatterns{},
|
||||||
|
"PersonalityTags": &entitys.PersonalityTags{},
|
||||||
|
"ToneTags": &entitys.ToneTags{},
|
||||||
|
"SignatureDialogues": &entitys.SignatureDialogues{},
|
||||||
|
"RegionValue": &entitys.RegionValue{},
|
||||||
|
"CompetitionComparison": &entitys.CompetitionComparison{},
|
||||||
|
"CoreSellingPoints": &entitys.CoreSellingPoints{},
|
||||||
|
"SupportingFacilities": &entitys.SupportingFacilities{},
|
||||||
|
"DeveloperBacking": &entitys.DeveloperBacking{},
|
||||||
|
"NeedsMining": &entitys.NeedsMining{},
|
||||||
|
"PainPointResponse": &entitys.PainPointResponse{},
|
||||||
|
"ValueBuilding": &entitys.ValueBuilding{},
|
||||||
|
"ClosingTechniques": &entitys.ClosingTechniques{},
|
||||||
|
"CommunicationRhythm": &entitys.CommunicationRhythm{},
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
package advice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ai_scheduler/internal/biz"
|
||||||
|
"ai_scheduler/internal/config"
|
||||||
|
"ai_scheduler/internal/entitys"
|
||||||
|
"ai_scheduler/internal/pkg"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DataService 数据处理
|
||||||
|
type DataService struct {
|
||||||
|
adviceBiz *biz.AdviceAdvicerBiz
|
||||||
|
cfg *config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDataService
|
||||||
|
func NewDataService(
|
||||||
|
adviceBiz *biz.AdviceAdvicerBiz,
|
||||||
|
cfg *config.Config,
|
||||||
|
) *DataService {
|
||||||
|
return &DataService{
|
||||||
|
adviceBiz: adviceBiz,
|
||||||
|
cfg: cfg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DataService) AdvicerAdd(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 *DataService) 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 *DataService) 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 *DataService) AdvicerVersionAdd(c *fiber.Ctx) error {
|
||||||
|
req := &entitys.AdvicerVersionInitReq{}
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.adviceBiz.VersionUpdate(c.UserContext(), req)
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
package services
|
package advice
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ai_scheduler/internal/biz"
|
"ai_scheduler/internal/biz"
|
||||||
"ai_scheduler/internal/config"
|
"ai_scheduler/internal/config"
|
||||||
"ai_scheduler/internal/entitys"
|
"ai_scheduler/internal/entitys"
|
||||||
|
"ai_scheduler/internal/pkg"
|
||||||
"ai_scheduler/internal/pkg/file_download"
|
"ai_scheduler/internal/pkg/file_download"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
@ -13,24 +14,24 @@ import (
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ChatHandler 聊天处理器
|
// FileService 文件处理
|
||||||
type AdviceService struct {
|
type FileService struct {
|
||||||
adviceBiz *biz.AdviceBiz
|
adviceBiz *biz.AdviceFileBiz
|
||||||
cfg *config.Config
|
cfg *config.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewChatHandler 创建聊天处理器
|
// NewFileService
|
||||||
func NewAdviceService(
|
func NewFileService(
|
||||||
adviceBiz *biz.AdviceBiz,
|
adviceBiz *biz.AdviceFileBiz,
|
||||||
cfg *config.Config,
|
cfg *config.Config,
|
||||||
) *AdviceService {
|
) *FileService {
|
||||||
return &AdviceService{
|
return &FileService{
|
||||||
adviceBiz: adviceBiz,
|
adviceBiz: adviceBiz,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AdviceService) WordAna(c *fiber.Ctx) error {
|
func (a *FileService) WordAna(c *fiber.Ctx) error {
|
||||||
req := &entitys.WordAnaReq{}
|
req := &entitys.WordAnaReq{}
|
||||||
if err := c.BodyParser(req); err != nil {
|
if err := c.BodyParser(req); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -44,19 +45,29 @@ func (a *AdviceService) WordAna(c *fiber.Ctx) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return a.adviceBiz.WordAna(c.UserContext(), result)
|
ana, err := a.adviceBiz.WordAna(context.Background(), result)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = c.JSON(ana)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AdviceService) WordAnat(path string) error {
|
func (a *FileService) WordAnat(path string) ([]byte, error) {
|
||||||
|
|
||||||
// URL 解码
|
// URL 解码
|
||||||
fileURL, err := url.PathUnescape(path)
|
fileURL, err := url.PathUnescape(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("URL 解码失败")
|
return nil, errors.New("URL 解码失败")
|
||||||
}
|
}
|
||||||
result, _, err := file_download.GetWordTextFromURL(fileURL, file_download.IsWordFile)
|
result, _, err := file_download.GetWordTextFromURL(fileURL, file_download.IsWordFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
return a.adviceBiz.WordAna(context.Background(), result)
|
ana, err := a.adviceBiz.WordAna(context.Background(), result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pkg.JsonByteIgonErr(ana), err
|
||||||
}
|
}
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
package services
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_WordAna(t *testing.T) {
|
|
||||||
Run()
|
|
||||||
|
|
||||||
advicer.WordAnat("https://attachment-public.oss-cn-hangzhou.aliyuncs.com/ai-scheduler/data-analytics/word/content.docx")
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
advicer *AdviceService
|
|
||||||
)
|
|
||||||
|
|
@ -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,5 +16,5 @@ var ProviderSetServices = wire.NewSet(
|
||||||
NewHistoryService,
|
NewHistoryService,
|
||||||
NewCapabilityService,
|
NewCapabilityService,
|
||||||
NewCronService,
|
NewCronService,
|
||||||
NewAdviceService,
|
advice.NewAdviceService,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue