Commit d3c206b4 by Ford

添加了用户画像的创建和查询相关世界的用户画像

parent fb5e23a9
...@@ -211,7 +211,7 @@ ...@@ -211,7 +211,7 @@
}, },
"npc_role_detail_translations": { "npc_role_detail_translations": {
"roleName": "角色名", "roleName": "角色名",
"introduction": "简介", "introduction": "简介",
"consumptionExpectations": "消费心理预期", "consumptionExpectations": "消费心理预期",
"psychologicalTraits": "心理特征", "psychologicalTraits": "心理特征",
......
package controllers package controllers
import ( import (
"WorldEpcho/src/config"
"WorldEpcho/src/config/e" "WorldEpcho/src/config/e"
database "WorldEpcho/src/datasource" database "WorldEpcho/src/datasource"
"WorldEpcho/src/models" "WorldEpcho/src/models"
"WorldEpcho/src/service" "WorldEpcho/src/service"
"WorldEpcho/src/utils"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
...@@ -33,38 +35,17 @@ import ( ...@@ -33,38 +35,17 @@ import (
*/ */
//创建角色画像的请求体 //创建角色画像的请求体
type NpcRoleRequest struct { type NpcRoleRequest struct {
Name string `json:"name"` // 名称 Name string `json:"name"` // 名称
Details Details `json:"details"` //详情 Details models.Details `json:"details"` //详情
Gender string `json:"gender"` //性别 Gender string `json:"gender"` //性别
Tags string `json:"tags"` //世界标签集 Tags string `json:"tags"` //世界标签集
RelatedWorlds string `json:"relatedWorlds"` //相关世界 RelatedWorlds string `json:"relatedWorlds"` //相关世界
UpdateTime int64 `json:"updateTime"` //更新时间 UpdateTime int64 `json:"updateTime"` //更新时间
CreateTime int64 `json:"createTime"` //构造时间 CreateTime int64 `json:"createTime"` //构造时间
ServiceProviderId string `json:"authId,omitempty"` // ServiceProviderId ServiceProviderId string `json:"authId,omitempty"` // ServiceProviderId
} }
// Detail 用于解析详细信息中的 JSON 字符串
type Details struct {
RoleName string `json:"roleName"` //角色名称
Introduction Introduction `json:"introduction"` //简介
ConsumptionExpectations []string `json:"consumptionExpectations"` //消费心理预期
PsychologicalTraits []string `json:"psychologicalTraits"` //心理特征
Appearance string `json:"appearance"` //外观
Personality string `json:"personality"` //性格
DecisionMakingStyle string `json:"decisionMakingStyle"` //决策风格
RiskPreference string `json:"riskPreference"` //风险偏好
AttitudeTowardsUsers string `json:"attitudeTowardsUsers"` //对用户的态度
}
//个人传记 Personal profile
type Introduction struct {
EducationLevel string `json:"educationLevel"` //教育水平
ProfessionalBackground string `json:"professionalBackground"` //职业背景
Purpose string `json:"purpose"` //目的性
KnownInformation string `json:"knownInformation"` //已知信息
}
//创建Npc用户画像的接口 //创建Npc用户画像的接口
func CreateNpcUserPortrait(ctx *gin.Context) { func CreateNpcUserPortrait(ctx *gin.Context) {
...@@ -138,24 +119,22 @@ func CreateNpcUserPortrait(ctx *gin.Context) { ...@@ -138,24 +119,22 @@ func CreateNpcUserPortrait(ctx *gin.Context) {
/* configData 英文键转换为中文 */ /* configData 英文键转换为中文 */
/* var data map[string]interface{}
var data map[string]interface{} if err := json.Unmarshal([]byte(string(detailsJSON)), &data); err != nil {
if err := json.Unmarshal([]byte(string(detailsJSON)), &data); err != nil { ctx.JSON(http.StatusOK, gin.H{"code": e.ParamParseError, "data": nil, "message": "解析用户画像详细信息数据出错"})
ctx.JSON(http.StatusOK, gin.H{"code": e.ParamParseError, "data": nil, "message": "解析用户画像详细信息数据出错"}) return
return }
} // 转换键名 => 英文转中文
// 转换键名 => 英文转中文 NpcRoleDetailTranslations := config.Conf.NpcRoleDetailTranslations
NpcRoleDetailTranslations := config.Conf.NpcRoleDetailTranslations translatedData := utils.TranslateKeysEnglishToChinese(NpcRoleDetailTranslations, data)
translatedData := utils.TranslateKeysEnglishToChinese(NpcRoleDetailTranslations, data)
translatedJSON, err := json.MarshalIndent(translatedData, "", " ")
translatedJSON, err := json.MarshalIndent(translatedData, "", " ") if err != nil {
if err != nil { fmt.Println("Error marshalling JSON:", err)
fmt.Println("Error marshalling JSON:", err) ctx.JSON(http.StatusOK, gin.H{"code": e.ParamParseError, "data": nil, "message": "解析用户画像详细信息数据出错"})
ctx.JSON(http.StatusOK, gin.H{"code": e.ParamParseError, "data": nil, "message": "解析用户画像详细信息数据出错"}) return
return }
} fmt.Println(" Details translatedJSON ==> ", string(translatedJSON))
fmt.Println(" Details translatedJSON ==> ", string(translatedJSON))
*/
// 创建一个空切片存储相关世界 // 创建一个空切片存储相关世界
relatedWorldIds := []int64{} relatedWorldIds := []int64{}
...@@ -178,16 +157,18 @@ func CreateNpcUserPortrait(ctx *gin.Context) { ...@@ -178,16 +157,18 @@ func CreateNpcUserPortrait(ctx *gin.Context) {
} }
//用户画像详情信息 //用户画像详情信息
// 将Details结构体转换为 JSON /*
jsonData, err := json.Marshal(NpcRequest.Details) // 将Details结构体转换为 JSON
if err != nil { jsonData, err := json.Marshal(NpcRequest.Details)
fmt.Println("Error:", err) if err != nil {
return fmt.Println("Error:", err)
} return
}
*/
npcUserRole := &models.NpcRole{ npcUserRole := &models.NpcRole{
Name: NpcRequest.Name, Name: NpcRequest.Name,
Details: string(jsonData), Details: string(translatedJSON),
Gender: NpcRequest.Gender, Gender: NpcRequest.Gender,
Tags: matchingTags, Tags: matchingTags,
RelatedWorlds: relatedWorldIds, RelatedWorlds: relatedWorldIds,
...@@ -296,6 +277,30 @@ func QueryNpcRoleInfo(ctx *gin.Context) { ...@@ -296,6 +277,30 @@ func QueryNpcRoleInfo(ctx *gin.Context) {
fmt.Println("根据相关世界ID查询用户画像信息出错") fmt.Println("根据相关世界ID查询用户画像信息出错")
return return
} }
/*
转换世界配置信息中的英文字段
*/
var data map[string]interface{}
for i, npcRole := range npcRoles {
if err := json.Unmarshal([]byte(npcRole.Details), &data); err != nil {
ctx.JSON(http.StatusOK, gin.H{"code": e.ParamParseError, "data": nil, "message": "解析用户画像详情信息数据出错"})
return
}
// 转换键名
// 中文转英文
ReverseTranslations := config.Conf.NpcRoleDetailReverseTranslations
translatedData := utils.TranslateKeysChineseToEnglish(ReverseTranslations, data)
translatedJSON, err := json.MarshalIndent(translatedData, "", " ")
if err != nil {
fmt.Println("Error marshalling JSON:", err)
ctx.JSON(http.StatusOK, gin.H{"code": e.ParamParseError, "data": nil, "message": "解析用户画像详情信息数据出错"})
return
}
npcRoles[i].Details = string(translatedJSON)
fmt.Println(" configData translatedJSON ==> ", string(translatedJSON))
}
//提交事务 //提交事务
err = session.Commit() err = session.Commit()
if err != nil { if err != nil {
......
...@@ -4,7 +4,6 @@ import ( ...@@ -4,7 +4,6 @@ import (
"WorldEpcho/src/config" "WorldEpcho/src/config"
"WorldEpcho/src/datasource" "WorldEpcho/src/datasource"
"WorldEpcho/src/https_auth" "WorldEpcho/src/https_auth"
"WorldEpcho/src/models"
"WorldEpcho/src/routers" "WorldEpcho/src/routers"
"WorldEpcho/src/service" "WorldEpcho/src/service"
"WorldEpcho/src/utils" "WorldEpcho/src/utils"
...@@ -25,10 +24,10 @@ func main() { ...@@ -25,10 +24,10 @@ func main() {
config.RandInit() config.RandInit()
datasource.InitMysql() datasource.InitMysql()
// 同步结构体与数据库表 // 同步结构体与数据库表
err := datasource.Engine.Sync2(new(models.NpcRole)) //err := datasource.Engine.Sync2(new(models.NpcRole))
if err != nil { //if err != nil {
fmt.Printf("创建表失败:", err) // fmt.Printf("创建表失败:", err)
} //}
//生成一个9位的邀请码 //生成一个9位的邀请码
//println("生成一个9位的邀请码: ", utils.GenerateInviteCode(1)) //println("生成一个9位的邀请码: ", utils.GenerateInviteCode(1))
......
...@@ -20,6 +20,27 @@ type NpcRole struct { ...@@ -20,6 +20,27 @@ type NpcRole struct {
ServiceProviderId string `xorm:"-" json:"authId,omitempty"` // ServiceProviderId 在数据库中忽略 ServiceProviderId string `xorm:"-" json:"authId,omitempty"` // ServiceProviderId 在数据库中忽略
} }
// Detail 用于解析详细信息中的 JSON 字符串
type Details struct {
RoleName string `json:"roleName"` //角色名称
Introduction Introduction `json:"introduction"` //简介
ConsumptionExpectations []string `json:"consumptionExpectations"` //消费心理预期
PsychologicalTraits []string `json:"psychologicalTraits"` //心理特征
Appearance string `json:"appearance"` //外观
Personality string `json:"personality"` //性格
DecisionMakingStyle string `json:"decisionMakingStyle"` //决策风格
RiskPreference string `json:"riskPreference"` //风险偏好
AttitudeTowardsUsers string `json:"attitudeTowardsUsers"` //对用户的态度
}
//个人传记 Personal profile
type Introduction struct {
EducationLevel string `json:"educationLevel"` //教育水平
ProfessionalBackground string `json:"professionalBackground"` //职业背景
Purpose string `json:"purpose"` //目的性
KnownInformation string `json:"knownInformation"` //已知信息
}
// 增加一个新的 NPC 角色 // 增加一个新的 NPC 角色
func CreateNpcRole(session *xorm.Session, npcRole *NpcRole) (*NpcRole, error) { func CreateNpcRole(session *xorm.Session, npcRole *NpcRole) (*NpcRole, error) {
_, err := session.Insert(npcRole) _, err := session.Insert(npcRole)
...@@ -62,7 +83,7 @@ func UpdateNpcRole(npcRole *NpcRole) error { ...@@ -62,7 +83,7 @@ func UpdateNpcRole(npcRole *NpcRole) error {
} }
// 根据 ID 查询单个 NPC 角色 // 根据 ID 查询单个 NPC 角色
func GetNpcRoleByID(id int) (*NpcRole, error) { func GetNpcRoleByID(id int64) (*NpcRole, error) {
npcRole := new(NpcRole) npcRole := new(NpcRole)
has, err := datasource.Engine.ID(id).Get(npcRole) has, err := datasource.Engine.ID(id).Get(npcRole)
if err != nil { if err != nil {
......
...@@ -126,7 +126,8 @@ type WorldErrorMessage struct { ...@@ -126,7 +126,8 @@ type WorldErrorMessage struct {
// Wrapper 结构体仅包含一个字符串字段,用于存储 JSON 字符串 // Wrapper 结构体仅包含一个字符串字段,用于存储 JSON 字符串
type Wrapper struct { type Wrapper struct {
ConfigData map[string]interface{} `json:"ConfigData"` ConfigData map[string]interface{} `json:"ConfigData"`
EmbeddingNpcs []map[string]interface{} `json:"EmbeddingNpcs" `
} }
//世界websocket控制器 //世界websocket控制器
...@@ -245,6 +246,7 @@ func WorldWsHandler(ctx *gin.Context) { ...@@ -245,6 +246,7 @@ func WorldWsHandler(ctx *gin.Context) {
world_Id := ctx.Query("worldId") world_Id := ctx.Query("worldId")
var world *models.WorldInfo var world *models.WorldInfo
var userInfo string var userInfo string
var roleInfo *models.NpcRole
if worldName != "" { if worldName != "" {
world, err = models.GetWorldInfoByName(worldName) world, err = models.GetWorldInfoByName(worldName)
...@@ -279,6 +281,20 @@ func WorldWsHandler(ctx *gin.Context) { ...@@ -279,6 +281,20 @@ func WorldWsHandler(ctx *gin.Context) {
ctx.JSON(http.StatusOK, gin.H{"code": e.NotFound, "data": nil, "message": "世界名称不存在"}) ctx.JSON(http.StatusOK, gin.H{"code": e.NotFound, "data": nil, "message": "世界名称不存在"})
return return
} }
NpcRoleId := ctx.Query("NpcRoleId")
if NpcRoleId != "" {
NpcRole_Id, err := strconv.ParseInt(NpcRoleId, 10, 64)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"code": 0, "message": "用户画像ID类型转换出错"})
return
}
roleInfo, err = models.GetNpcRoleByID(NpcRole_Id)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"code": 0, "message": "根据用户画像ID查询画像信息出错"})
return
}
}
if b || SP != nil { if b || SP != nil {
fmt.Println("ServiceProvider ===> ", SP) fmt.Println("ServiceProvider ===> ", SP)
...@@ -287,6 +303,7 @@ func WorldWsHandler(ctx *gin.Context) { ...@@ -287,6 +303,7 @@ func WorldWsHandler(ctx *gin.Context) {
if user_info != "" { if user_info != "" {
userInfo = user_info userInfo = user_info
} }
//拼接会话主题 //拼接会话主题
//ConversationTitle = "用户Id为" + user_Id + "的用户和数字人" + strings.Join(DpNames, "和") + "开始会话" //ConversationTitle = "用户Id为" + user_Id + "的用户和数字人" + strings.Join(DpNames, "和") + "开始会话"
//fmt.Println("ConversationTitle: ", ConversationTitle) //fmt.Println("ConversationTitle: ", ConversationTitle)
...@@ -295,10 +312,22 @@ func WorldWsHandler(ctx *gin.Context) { ...@@ -295,10 +312,22 @@ func WorldWsHandler(ctx *gin.Context) {
BgInfo = world.Background BgInfo = world.Background
} }
if user_info == "" { if user_info == "" {
// 创建 Wrapper 实例并将 JSON 字符串封装 //var details models.Details
//若用户画像存在
// 创建 Wrapper 实例并将 JSON 字符串封装
wrapper := Wrapper{ConfigData: make(map[string]interface{})} wrapper := Wrapper{ConfigData: make(map[string]interface{})}
fmt.Println("world.ConfigData ==> ", world.ConfigData)
if roleInfo != nil {
var npcMap map[string]interface{}
err = json.Unmarshal([]byte(roleInfo.Details), &npcMap)
if err != nil {
log.Fatalf("Error unmarshalling JSON: %v", err)
}
wrapper.EmbeddingNpcs = append(wrapper.EmbeddingNpcs, npcMap)
}
// 将 JSON 字符串解析为 map[string]interface{} // 将 JSON 字符串解析为 map[string]interface{}
//err := json.Unmarshal([]byte(world.ConfigData), &wrapper.ConfigData)
err := json.Unmarshal([]byte(world.ConfigData), &wrapper.ConfigData) err := json.Unmarshal([]byte(world.ConfigData), &wrapper.ConfigData)
if err != nil { if err != nil {
log.Printf("Error parsing JSON: %v", err) log.Printf("Error parsing JSON: %v", err)
...@@ -307,7 +336,6 @@ func WorldWsHandler(ctx *gin.Context) { ...@@ -307,7 +336,6 @@ func WorldWsHandler(ctx *gin.Context) {
// 打印解析后的数据 // 打印解析后的数据
//fmt.Printf("Wrapper ConfigData: %+v\n", wrapper.ConfigData) //fmt.Printf("Wrapper ConfigData: %+v\n", wrapper.ConfigData)
// 序列化 Wrapper 实例以生成所需的最终 JSON 输出 // 序列化 Wrapper 实例以生成所需的最终 JSON 输出
finalJSON, err := json.Marshal(wrapper) finalJSON, err := json.Marshal(wrapper)
if err != nil { if err != nil {
......
package service
import (
"WorldEpcho/src/config"
"WorldEpcho/src/config/e"
"WorldEpcho/src/datasource"
"WorldEpcho/src/models"
"WorldEpcho/src/utils"
"github.com/gin-contrib/sessions"
"os"
"strconv"
"strings"
"time"
//"time"
//"chat/cache"
//"chat/conf"
"encoding/json"
"fmt"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"log"
"net/http"
//"strconv"
//"time"
)
//const month = 60 * 60 * 24 * 30 // 按照30天算一个月
// 发送消息的类型
type WorldSendMsg struct {
Type int `json:"type"`
Content string `json:"content"`
}
// 回复的消息
/* 这个要注释掉 */
type WorldReplyMsg struct {
From string `json:"from"`
Code int `json:"code"`
Content string `json:"content"`
StatusCode int `json:"statusCode,omitempty"`
}
// 转发AI接口意识流响应给前端
type WorldSoulReplyMsg struct {
Code int `json:"code"`
WObj map[string]interface{} `json:"WObj"`
ISLIU string `json:"ISLIU"`
WorldName string `json:"WorldName"`
IsLIUId *string `json:"IsLIUId,omitempty"` // 使用指针类型,并添加 omitempty 标签,可选字段
MessageStatusType string `json:"messageStatusType,omitempty"` // Optional field to indicate message type
ErrorMessage string `json:"ErrorMessage,omitempty"` //可选字段
}
/*
构建Client
*/
type WorldClient struct {
//DpConversations *models.DpConversations
WorldConversations *models.WorldConversation
Socket *websocket.Conn
//Send chan *WorldEchoResponse
Send chan *WorldSoulReplyMsg
WorldName string
AuthId string
UserSource string
}
// 用户类
/*
type Client struct {
Id string
SendID string
Socket *websocket.Conn
Send chan []byte
}
*/
// 广播类,包括广播内容和源用户
/*
type Broadcast struct {
Client *Client
Message []byte
Type int
}
*/
// 用户管理
type WorldClientManager struct {
Client map[string]*WorldClient
//Broadcast chan *Broadcast
Reply chan *WorldClient
Register chan *WorldClient
Unregister chan *WorldClient
}
// Message 信息转JSON (包括:发送者、接收者、内容)
type WorldMessage struct {
Sender string `json:"sender,omitempty"`
Recipient string `json:"recipient,omitempty"`
Content string `json:"content,omitempty"`
}
var WorldManager = WorldClientManager{
Client: make(map[string]*WorldClient), // 参与连接的用户,出于性能的考虑,需要设置最大连接数
//Broadcast: make(chan *Broadcast),
Register: make(chan *WorldClient),
Reply: make(chan *WorldClient),
Unregister: make(chan *WorldClient),
}
//错误消息响应结构体
type WorldErrorMessage struct {
Code int `json:"code"`
Message string `json:"message"`
}
/*
type WorldEchoResponseAndErrorMsg struct {
SoulReplyMsg WorldSoulReplyMsg //WorldSoulReplyMsg
ErrorMessage WorldErrorMessage `json:"ErrorMessage,omitempty"`
}
*/
// Wrapper 结构体仅包含一个字符串字段,用于存储 JSON 字符串
type Wrapper struct {
ConfigData map[string]interface{} `json:"ConfigData"`
}
//世界websocket控制器
func WorldWsHandler(ctx *gin.Context) {
/*
http协议升级为webSocket协议
*/
conn, err := (&websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool { // CheckOrigin解决跨域问题
return true
}}).Upgrade(ctx.Writer, ctx.Request, nil) // 升级成ws协议
if err != nil {
http.NotFound(ctx.Writer, ctx.Request)
return
}
/*
session里获取用户id
*/
session := sessions.Default(ctx)
userId := session.Get("WorldUserID")
if userId == nil {
// 处理session中未找到值的情况,返回错误信息
/*这里是双保险为了测试用*/
userId = ctx.Query("uid")
if userId == nil {
ctx.JSON(http.StatusOK, gin.H{"code": e.NotLoginUser, "data": nil, "message": "Session中未找到用户ID,用户未登录"})
fmt.Println(config.ColorYellow, "Session中未找到用户ID,用户未登录", config.ColorReset)
return
}
}
fmt.Println("world chat session UserID:", userId)
var SP *models.ServiceProvider
// 服务商id获取
//从配置文件读取咪咕服务商ID
miGuAuthId := config.Conf.MiGuAuthId
ServiceProviderId := ctx.Query("AuthId")
if ServiceProviderId != "" || ServiceProviderId == miGuAuthId {
// 从请求头中获取 JWT token
tokenString := ctx.GetHeader("Token")
if tokenString == "" {
fmt.Printf("no Authorization token provided")
ctx.JSON(http.StatusOK, gin.H{"code": e.EmptyParamsError, "data": nil, "message": "请求头中无token信息!"})
return
}
isValid, err := IsValidMiGuToken(tokenString)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"code": e.TokenAuthError, "data": nil, "message": "解析token出错!"})
return
}
if !isValid {
ctx.JSON(http.StatusOK, gin.H{"code": e.InvalidToken, "data": nil, "message": "无效的token"})
return
}
SP, err = models.GetServiceProviderById(ServiceProviderId)
if err != nil {
fmt.Println("根据服务商id查询出错:", err)
ctx.JSON(http.StatusOK, gin.H{"code": 0, "data": nil, "message": "根据服务商id查询服务商出错"})
return
}
if SP == nil {
ctx.JSON(http.StatusOK, gin.H{"code": 0, "data": nil, "message": "非法的服务商"})
return
}
}
user_Id, ok := userId.(string)
if !ok {
// 类型断言失败,处理类型不匹配的情况
ctx.JSON(http.StatusOK, gin.H{"code": 0, "data": nil, "message": "用户ID类型错误"})
return
}
//userId作为string类型的值进行后续操作
_, b := config.WorldLoginCacheCode.Get(user_Id)
uid, err := strconv.ParseInt(user_Id, 10, 64)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"code": 0, "data": nil, "message": "数字人id类型转换出错"})
//continue
return
}
if user_Id == "" && ServiceProviderId == "" {
fmt.Println(config.ColorPurple, "用户id或者服务商id为空", config.ColorReset)
ctx.JSON(http.StatusOK, gin.H{"code": 0, "data": nil, "message": "用户id或者服务商id为空"})
return
}
if SP != nil {
if SP.InspirationValue == 0 {
ctx.JSON(http.StatusOK, gin.H{"code": 0, "data": nil, "message": "服务商的灵感值余额不足,无法进行数字人聊天"})
return
}
}
//校验用户是不是服务商那边的用户
if user_Id != "" && SP != nil {
user, err := models.GetUserByID(uid)
if err != nil {
fmt.Println(config.ColorPurple, "根据用户ID查询用户信息出错", config.ColorReset)
ctx.JSON(http.StatusOK, gin.H{"code": 0, "message": "根据用户ID查询用户信息出错"})
return
}
if user.RegisteredSource != SP.Name {
ctx.JSON(http.StatusOK, gin.H{"code": 0, "message": " 该用户不是服务商的用户不能连接! "})
fmt.Println("该用户不是服务商的用户不能连接!")
return
}
}
/*
判断用户是否登录
*/
//定义会话主题
//var ConversationTitle string
worldName := ctx.Query("world_name")
world_Id := ctx.Query("worldId")
var world *models.WorldInfo
var userInfo string
if worldName != "" {
world, err = models.GetWorldInfoByName(worldName)
if err != nil {
log.Println("根据世界名称,查询世界信息失败")
ctx.JSON(http.StatusOK, gin.H{"code": 0, "message": "根据世界名称,查询世界信息失败"})
return
}
}
if world_Id != "" {
worldId, err := strconv.ParseInt(world_Id, 10, 64)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"code": 0, "message": "世界ID类型转换出错"})
return
}
world, _, err = models.GetWorldInfoById(worldId)
if err != nil {
log.Println("根据世界名称,查询世界信息失败")
ctx.JSON(http.StatusOK, gin.H{"code": e.ErrorDatabase, "data": nil, "message": "根据世界名称,查询世界信息失败"})
return
}
if world == nil {
log.Println("世界信息不存在")
ctx.JSON(http.StatusOK, gin.H{"code": e.NotFound, "data": nil, "message": "世界信息不存在"})
return
}
}
if world == nil {
log.Println("世界名称不存在")
ctx.JSON(http.StatusOK, gin.H{"code": e.NotFound, "data": nil, "message": "世界名称不存在"})
return
}
if b || SP != nil {
fmt.Println("ServiceProvider ===> ", SP)
BgInfo := ctx.Query("BgInfo")
user_info := ctx.Query("userInfo")
if user_info != "" {
userInfo = user_info
}
//拼接会话主题
//ConversationTitle = "用户Id为" + user_Id + "的用户和数字人" + strings.Join(DpNames, "和") + "开始会话"
//fmt.Println("ConversationTitle: ", ConversationTitle)
if SP != nil && world != nil {
if BgInfo == "" {
BgInfo = world.Background
}
if user_info == "" {
// 创建 Wrapper 实例并将 JSON 字符串封装
wrapper := Wrapper{ConfigData: make(map[string]interface{})}
// JSON 字符串解析为 map[string]interface{}
err := json.Unmarshal([]byte(world.ConfigData), &wrapper.ConfigData)
if err != nil {
log.Printf("Error parsing JSON: %v", err)
return
}
// 打印解析后的数据
//fmt.Printf("Wrapper ConfigData: %+v\n", wrapper.ConfigData)
// 序列化 Wrapper 实例以生成所需的最终 JSON 输出
finalJSON, err := json.Marshal(wrapper)
if err != nil {
fmt.Println("Error marshaling final JSON: ", err)
ctx.JSON(http.StatusOK, gin.H{"code": 0, "message": "业务经理考核系统配置信息解析失败"})
return
}
fmt.Println("ConfigData ===>", string(finalJSON))
//userInfo = string(finalJSON)
userInfo = string(finalJSON)
}
}
worldConversation, _, err := models.GetWorldConversationByUidAndWorldName(uid, world.Name)
if err != nil {
log.Println("查询世界信息失败")
ctx.JSON(http.StatusOK, gin.H{"code": 0, "message": "查询世界信息失败"})
return
}
ISULU_ID := ""
var world_response WorldCreateResponse
var isNewHuaSoul bool
if worldConversation == nil {
//世界意识流不存在调用new接口创建
world, new_conversation, err := CreateVirtualWorldHandler(uid, world.Name, BgInfo, userInfo)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"code": 0, "content": "创建世界失败!"})
return
}
// 把新创建的世界会话赋值给会话对象
worldConversation = new_conversation
isNewHuaSoul = true
ISULU_ID = world.ISLIUid
world_response.Code = world.Code
world_response.ISLIUid = world.ISLIUid
world_response.WObj = world.WObj
world_response.ISLIU = world.ISLIU
//这里加了一个消息状态类型
world_response.MessageStatusType = world.MessageStatusType
} else {
ISULU_ID = worldConversation.IsLiuId
isNewHuaSoul = false
// 如果查询到记录,调用查询意识流Id接口判断意识流id是否有效,若无效则修改意识流ID为新的意识流ID
flag, err := FindWorldISLIU(uid, world.Name)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"code": 0, "content": "查询世界意识流id失败!"})
return
}
fmt.Println("查询世界意识流Id, flag: ", flag)
//没找到
if !flag {
//调用new接口创建世界
world, new_conversation, err := CreateVirtualWorldHandler(uid, world.Name, BgInfo, userInfo)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"code": 0, "content": "创建世界失败!"})
return
}
// 把新创建的世界会话赋值给会话对象
worldConversation = new_conversation
isNewHuaSoul = true
ISULU_ID = world.ISLIUid
world_response.Code = world.Code
world_response.ISLIUid = world.ISLIUid
world_response.WObj = world.WObj
world_response.ISLIU = world.ISLIU
//这里加了一个消息状态类型
world_response.MessageStatusType = world.MessageStatusType
}
}
//根据uid查询用户来源是否为咪咕
user, err := models.GetUserByID(worldConversation.Uid)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"code": 0, "message": "根据用户id查询用户出错!"})
return
}
// 创建一个用户客户端会话实例
newClient := &WorldClient{
WorldConversations: worldConversation,
WorldName: world.Name,
Socket: conn,
//Send: nil,
Send: make(chan *WorldSoulReplyMsg, 1024), // 创建一个有缓冲的管道
AuthId: ServiceProviderId,
}
if user != nil && user.RegisteredSource != "" {
newClient.UserSource = user.RegisteredSource
}
go newClient.WorldWrite() // 为每个客户端启动写消息协程
// 用户会话注册到用户管理上
//WorldManager.Register <- newClient
WorldManager.Register <- newClient // 注册客户端
if isNewHuaSoul {
// 注册成功后,把世界信息开场白写入Socket
if world_response.ISLIU == "" || world_response.WObj == nil {
log.Println("世界开场白,意识流为空...")
// 向前端返回数据格式不正确的状态码和消息
errorMsg := WorldErrorMessage{
Code: -1,
Message: "世界开场白,意识流为空...",
}
errmsg, _ := json.Marshal(errorMsg)
_ = newClient.Socket.WriteMessage(websocket.TextMessage, errmsg)
fmt.Println("返回世界开场白信息失败: ", errmsg)
return
}
/* 创建响应体 WorldEchoRespones */
HSReplyMsg := WorldSoulReplyMsg{
Code: world_response.Code,
WObj: world_response.WObj,
ISLIU: world_response.ISLIU,
WorldName: world.Name,
//加了一个消息状态类型
IsLIUId: &world_response.ISLIUid,
MessageStatusType: world_response.MessageStatusType,
}
//向管道写入开场白消息
newClient.Send <- &HSReplyMsg
/*
msg, _ := json.Marshal(HSReplyMsg)
write_err := newClient.Socket.WriteMessage(websocket.TextMessage, msg)
if write_err != nil {
log.Println("返回意识流给客户端时出错! ")
WorldDestroyWs(newClient)
return
}*/
/* 保存世界的独白到数据库 */
senderType := "world"
// 拼接会话Id
conversation_Id := utils.Strval(newClient.WorldConversations.Uid) + "_" + utils.Strval(newClient.WorldConversations.WorldId)
_, chatDB_err := datasource.SaveWorldChatRecordMongoDB(conversation_Id, newClient.WorldConversations.WorldId, newClient.WorldConversations.WorldName, world_response.ISLIU, world_response.WObj, senderType)
// 错误处理
if chatDB_err != nil {
log.Println(config.ColorRed, "保存数字人聊天记录至数据库出错!", config.ColorReset)
errorMsg := WorldSoulReplyMsg{
Code: -1,
ErrorMessage: "保存数字人聊天记录至数据库出错!",
}
/* errorMsg := WorldErrorMessage{
Code: -1,
Message: "保存数字人聊天记录至数据库出错!",
}
errmsg, _ := json.Marshal(errorMsg)
_ = newClient.Socket.WriteMessage(websocket.TextMessage, errmsg)*/
newClient.Send <- &errorMsg
return
}
}
// ------------------上传意识流---------------------- //
url := config.Conf.SoulUrl + "/world/neural"
fileName := "./neural/" + ISULU_ID + ".CFN"
// 获取文件信息
_, err = os.Stat(fileName)
if err == nil {
fmt.Println("文件存在")
// 构造请求参数
request := SoulNeuralRequest{
Auth: config.Conf.SoulAuth,
Type: 1, // 1表示上载
ISLIUid: ISULU_ID,
}
// 调用函数
_, err := SoulNeuralFileUpload(url, request, fileName)
if err != nil {
log.Printf("Error during soul neural transfer: %v", err)
}
// 处理响应
//fmt.Println("Response received: ", response)
} else if os.IsNotExist(err) {
fmt.Println("文件不存在")
} else {
fmt.Println("获取文件信息出错:", err)
}
// ---------------------------------------- //
//fmt.Println("调用socket的Read方法前:", ISULU_ID)
go newClient.WorldRead()
//go newClient.WorldWrite()
} else {
ctx.JSON(http.StatusOK, gin.H{"code": e.NotLoginUser, "data": nil, "message": "用户未登录"})
return
}
}
func WorldDestroyWs(c *WorldClient) { // 避免忘记关闭,所以要加上close
//关闭websocket之前,下载关于数字人的神经网络记忆文件
url := config.Conf.SoulUrl + "/world/neural"
fileName := "./neural/" + c.WorldConversations.IsLiuId + ".CFN"
// 构造请求参数
request := SoulNeuralRequest{
Auth: config.Conf.SoulAuth,
Type: 0, // 0表示下载
ISLIUid: c.WorldConversations.IsLiuId,
}
// 调用函数
err1 := SoulNeuralFileDownload(url, request, fileName)
if err1 != nil {
log.Printf("Error during soul neural transfer: %v", err1)
}
//注销websocket的客户端
WorldManager.Unregister <- c
_ = c.Socket.Close()
c.Socket = nil
}
/*
websocket读取客户端用户的消息,然后服务器回应前端一个消息
*/
func (c *WorldClient) WorldRead() {
//从配置文件读取咪咕服务商ID
miGuAuthId := config.Conf.MiGuAuthId
//延迟关闭websocket
defer WorldDestroyWs(c)
/*
死循环执行websocket消息接收和发送
*/
for {
c.Socket.PongHandler()
sendMsg := new(WorldSendMsg)
err := c.Socket.ReadJSON(&sendMsg)
if err != nil {
log.Println("数据格式不正确", err)
/* // 向前端返回数据格式不正确的状态码和消息
errorMsg := WorldErrorMessage{
Code: -1,
Message: "数据格式不正确",
}
errmsg, errMarshal := json.Marshal(errorMsg)
if errMarshal != nil {
// 如果JSON编码失败,则打印错误消息
log.Printf("JSON编码错误: %s\n", errMarshal)
} else {
// 发送错误消息
errWrite := c.Socket.WriteMessage(websocket.TextMessage, errmsg)
if errWrite != nil {
// 如果WebSocket发送失败,则打印错误消息
log.Printf("WebSocket发送错误: %s\n", errWrite)
}
}
*/
errorMsg := WorldSoulReplyMsg{
Code: -1,
ErrorMessage: "客户端发送的数据格式不正确!",
}
c.Send <- &errorMsg
//fmt.Println("errmsg: ", errmsg)
//剔除注册的客户端用户
WorldManager.Unregister <- c
fmt.Println(" Socket.Close() ... ")
//关闭webSocket
_ = c.Socket.Close()
break
}
//打印前端发送过来的消息
sendMsgMarshal, err := json.Marshal(sendMsg)
if err != nil {
// 打印错误信息的字符串表示
log.Printf("Error marshaling sendMsg to JSON: %s", err.Error())
// 在这里还可以做其它错误处理,例如返回一个错误响应等
} else {
// 打印 sendMsg JSON 字符串
fmt.Printf("【==== sendMsg ===】 : %s\n", string(sendMsgMarshal))
// 在这里可以继续处理 sendMsg JSON 字符串,比如发送它
}
/*
*/
go func() {
/*
响应类型0 交互
*/
// 拼接会话Id
conversation_Id := utils.Strval(c.WorldConversations.Uid) + "_" + utils.Strval(c.WorldConversations.WorldId)
/* dpIdsStr := utils.Int64SliceToStringSlice(c.DpConversations.DpIds)
conversation_Id := utils.Strval(c.DpConversations.Uid) + "_" + strings.Join(dpIdsStr, ",") + "_" + utils.Strval(c.DpConversations.AppId)*/
// 调用AI的会话接口
echoResponse, chat_err := c.WorldEchoAPI(sendMsg)
if c.Socket == nil {
return
}
if chat_err != nil {
log.Println("会话失败")
// 向前端返回数据格式不正确的状态码和消息
/*errorMsg := WorldErrorMessage{
Code: -1,
Message: "会话失败",
}
// 将错误消息对象转换为 JSON 字符串
errmsg, errMarshal := json.Marshal(errorMsg)
if errMarshal != nil {
// 如果 JSON 编码失败,则打印错误
log.Printf("WorldEchoAPI error: %v", errMarshal)
} else {
// 发送错误消息
errWrite := c.Socket.WriteMessage(websocket.TextMessage, errmsg)
if errWrite != nil {
//如果发送 WebSocket消息失败,则打印错误
log.Printf("Error writing websocket message: %v", errWrite)
}
}*/
errorMsg := WorldSoulReplyMsg{
Code: -1,
ErrorMessage: "请求AI模型会话失败!",
}
c.Send <- &errorMsg
// 删除会话记录
/* if chat_err.Error() == "调用会话接口失败无法获取意识流" {
dropConversationErr := models.DeleteConversation()
if dropConversationErr != nil {
log.Println("删除会话失败")
}
}
*/
return
} /*else {
c.Send <- echoResponse // 将回应消息放入 `Send` 频道
}*/
resCode := echoResponse.Code
Wobj := echoResponse.WObj
ISLIU := echoResponse.ISLIU
if ISLIU == "" || Wobj == nil {
log.Println("会话意识流为空...")
// 向前端返回数据格式不正确的状态码和消息
/*errorMsg := WorldErrorMessage{
Code: -1,
Message: "会话意识流为空...",
}
errmsg, _ := json.Marshal(errorMsg)
_ = c.Socket.WriteMessage(websocket.TextMessage, errmsg)*/
fmt.Println("AI echo error: 会话意识流为空... ")
errorMsg := WorldSoulReplyMsg{
Code: -1,
ErrorMessage: "AI模型会话意识流为空.",
}
c.Send <- &errorMsg
return
}
// 读取前端发送过来的消息并打印
fmt.Println("客户端消息: ", sendMsg)
if sendMsg.Type == 0 {
/*
收到前端发送过来的消息,保存至数据库
*/
senderType := "user"
user_status := make(map[string]interface{})
_, chatDB_err := datasource.SaveWorldChatRecordMongoDB(conversation_Id, c.WorldConversations.Uid, c.WorldConversations.WorldName, sendMsg.Content, user_status, senderType)
// 错误处理
if chatDB_err != nil {
log.Println("保存用户聊天记录至数据库出错!")
/*
errorMsg := ErrorMessage{
Code: -1,
Message: "保存用户聊天记录至数据库出错!",
}
errmsg, _ := json.Marshal(errorMsg)
_ = c.Socket.WriteMessage(websocket.TextMessage, errmsg)
*/
errorMsg := WorldSoulReplyMsg{
Code: -1,
ErrorMessage: "保存用户聊天记录至数据库出错!",
}
c.Send <- &errorMsg
return
}
/*
调用AI模型接口回复前端用户的消息
*/
HSReplyMsg := WorldSoulReplyMsg{
Code: resCode,
WObj: Wobj,
ISLIU: ISLIU,
WorldName: c.WorldName,
IsLIUId: &c.WorldConversations.IsLiuId,
}
//判断是不是咪咕服务商
if c.AuthId == miGuAuthId {
reformatHSReplyMsg := ParseEndStrAndReformat(&HSReplyMsg)
/* msg, _ := json.Marshal(reformatHSReplyMsg)
write_err := c.Socket.WriteMessage(websocket.TextMessage, msg)
if write_err != nil {
log.Println("返回意识流给客户端时出错! ")
WorldDestroyWs(c)
return
}*/
// 将处理后的消息发送到Write协程
c.Send <- reformatHSReplyMsg
} else {
/*
msg, _ := json.Marshal(HSReplyMsg)
write_err := c.Socket.WriteMessage(websocket.TextMessage, msg)
if write_err != nil {
log.Println("返回意识流给客户端时出错! ")
WorldDestroyWs(c)
return
}
*/
c.Send <- &HSReplyMsg
}
/*
mongoDB保存数字人发送给前端用户的聊天记录
*/
//最后一次聊天的数字人
//c.LastMsg = ISLIU
//LastDpId := c.DpConversations.DpIds[c.LastIndex]
senderType2 := "world"
maxTimeStamp, chatDB_err2 := datasource.SaveWorldChatRecordMongoDB(conversation_Id, c.WorldConversations.WorldId, c.WorldConversations.WorldName, ISLIU, Wobj, senderType2)
// 错误处理
if chatDB_err2 != nil {
log.Println(config.ColorRed, "保存数字人聊天记录至数据库出错!", config.ColorReset)
/*
errorMsg := ErrorMessage{
Code: -1,
Message: "保存数字人聊天记录至数据库出错!",
}
errmsg, _ := json.Marshal(errorMsg)
_ = c.Socket.WriteMessage(websocket.TextMessage, errmsg)
*/
errorMsg := WorldSoulReplyMsg{
Code: -1,
ErrorMessage: "保存数字人聊天记录至数据库出错!",
}
c.Send <- &errorMsg
return
}
if c.AuthId == miGuAuthId {
if endStr, ok := echoResponse.WObj["EndStr"]; ok {
if endStr != "" {
fmt.Println(" endStr ====> :", endStr)
score, err := ExtractRating(*echoResponse)
if err != nil {
fmt.Println("错误提取评分: ", err)
return
}
fmt.Println("推演到结局,保存结局内容...")
// 创建 OutcomeContent 对象
outcome := OutcomeContent{
WObj: echoResponse.WObj,
ISLIU: echoResponse.ISLIU,
}
// OutcomeContent 对象转换成 JSON 字符串
outcomeContentBytes, err := json.Marshal(outcome)
if err != nil {
fmt.Println("无法将推演结局内容序列化:", err)
return
}
outcomeContentStr := string(outcomeContentBytes)
// 创建 WorldDeductionResult 对象
deductionResult := models.WorldDeductionResult{
WorldId: c.WorldConversations.WorldId,
UserId: c.WorldConversations.Uid,
WorldName: c.WorldName,
OutcomeTitle: "推演结局", // 使用 EndStr 作为结局标题
OutcomeContent: outcomeContentStr,
ChatStartTime: maxTimeStamp,
ChatEndTime: time.Now().Unix(),
Score: score,
}
// 保存到数据库
_, err = models.AddWorldDeductionResult(&deductionResult)
if err != nil {
fmt.Println("保存推演结局内容到数据库失败:", err)
return
}
fmt.Println("推演结局内容已成功保存到mysql数据库,推演结果表中。")
chatRecordArray := datasource.WorldChatRecordArray{
ConversationId: utils.Strval(c.WorldConversations.Uid) + "_" + utils.Strval(c.WorldConversations.WorldId),
Timestamp: time.Now().Unix(),
//存个空的聊天记录数组
Records: []datasource.WorldChatRecord{},
}
err = datasource.SaveWorldChatRecord(chatRecordArray)
if err != nil {
log.Println("MongoDB数据库,新插入一条数据出错,err: ", err)
//ctx.JSON(http.StatusOK, gin.H{"code": 0, "message": "重置世界记忆文件,插入数据失败!"})
return
}
fmt.Println(config.ColorBlue, "用户聊天到结局,MongoDB创建新的聊天数组", config.ColorReset)
}
}
}
/*
发送消息成功,resCode1
*/
if resCode == 1 {
/* //增加数字人的交互量
err := models.IncrementDigitalPersonInteractionCount(c.DpConversations.DpIds[c.LastIndex])
if err != nil {
log.Println(config.ColorRed, "更新交互量失败:", err, config.ColorReset)
return
}
fmt.Println(config.ColorGreen, "交互量更新成功!", config.ColorReset)
*/
if c.AuthId != "" {
sp, err := models.GetServiceProviderById(c.AuthId)
if err != nil {
fmt.Println("根据服务商id查询出错:", err)
return
}
if sp != nil && c.UserSource == sp.Name {
/* 添加服务商调用AI会话,灵感消耗日志信息 */
museNum := int64(echoResponse.MuseValue)
// 构建一个InspirationUsageLog实例
/* 更新服务商的灵感值 */
if museNum > sp.InspirationValue {
// 处理错误或返回
fmt.Println("服务商灵感值不足本次扣除")
return
}
sp.InspirationValue = sp.InspirationValue - museNum
sp.TotalInspiration = sp.TotalInspiration + museNum
err := models.UpdateServiceProvider(sp)
if err != nil {
log.Println(config.ColorRed, "更新服务商灵感消耗值失败:", err, config.ColorReset)
return
} else {
log.Println(config.ColorBlue, "更新服务商灵感消耗值成功,剩余灵感值:", sp.InspirationValue, config.ColorReset)
}
}
}
}
}
/*
type1,表示用户无回应,数字人进行主动发送信息,进行主动关怀!!
*/
if sendMsg.Type == 1 {
//响应给客户端
HSReplyMsg := WorldSoulReplyMsg{
Code: resCode,
WObj: Wobj,
ISLIU: ISLIU,
WorldName: c.WorldName,
IsLIUId: &c.WorldConversations.IsLiuId,
}
//var msg []byte
//判断是不是咪咕服务商
if c.AuthId == miGuAuthId {
reformatHSReplyMsg := ParseEndStrAndReformat(&HSReplyMsg)
//msg, _ = json.Marshal(reformatHSReplyMsg)
// 将处理后的消息发送到Write协程
c.Send <- reformatHSReplyMsg
} else {
//msg, _ = json.Marshal(HSReplyMsg)
c.Send <- &HSReplyMsg
}
//解析AI接口返回的数据
//msg, _ := json.Marshal(HSReplyMsg)
//c.LastMsg = ISLIU
/*
mongoDB保存数字人发送给前端用户的聊天记录
*/
senderType := "world"
maxTimeStamp, chatDB_err := datasource.SaveWorldChatRecordMongoDB(conversation_Id, c.WorldConversations.WorldId, c.WorldConversations.WorldName, ISLIU, Wobj, senderType)
// 错误处理
if chatDB_err != nil {
log.Println(config.ColorRed, "保存数字人聊天记录至数据库出错!", config.ColorReset)
/* errorMsg := ErrorMessage{
Code: -1,
Message: "保存数字人聊天记录至数据库出错!",
}
errmsg, _ := json.Marshal(errorMsg)
_ = c.Socket.WriteMessage(websocket.TextMessage, errmsg)*/
errorMsg := WorldSoulReplyMsg{
Code: -1,
ErrorMessage: "保存数字人聊天记录至数据库出错!",
}
c.Send <- &errorMsg
return
}
// 回复数据至前端用户
/* write_err := c.Socket.WriteMessage(websocket.TextMessage, msg)
if write_err != nil {
log.Println(config.ColorRed, "数字人主动关怀,返回意识流给客户端时出错!", config.ColorReset)
WorldDestroyWs(c)
return
}
*/
c.Send <- &HSReplyMsg
if endStr, ok := echoResponse.WObj["EndStr"]; ok {
if endStr != "" {
fmt.Println("推演到结局,保存结局内容...")
// 创建 OutcomeContent 对象
outcome := OutcomeContent{
WObj: echoResponse.WObj,
ISLIU: echoResponse.ISLIU,
}
// OutcomeContent 对象转换成 JSON 字符串
outcomeContentBytes, err := json.Marshal(outcome)
if err != nil {
fmt.Println("无法将推演结局内容序列化:", err)
return
}
outcomeContentStr := string(outcomeContentBytes)
// 创建 WorldDeductionResult 对象
deductionResult := models.WorldDeductionResult{
WorldId: c.WorldConversations.WorldId,
UserId: c.WorldConversations.Uid,
WorldName: c.WorldName,
OutcomeTitle: "推演结局", // 使用 EndStr 作为结局标题
OutcomeContent: outcomeContentStr,
ChatStartTime: maxTimeStamp,
ChatEndTime: time.Now().Unix(),
}
// 保存到数据库
_, err = models.AddWorldDeductionResult(&deductionResult)
if err != nil {
fmt.Println("保存推演结局内容到数据库失败:", err)
return
}
fmt.Println("推演结局内容已成功保存到mysql数据库,推演结果表中。")
chatRecordArray := datasource.WorldChatRecordArray{
ConversationId: utils.Strval(c.WorldConversations.Uid) + "_" + utils.Strval(c.WorldConversations.WorldId),
Timestamp: time.Now().Unix(),
//存个空的聊天记录数组
Records: []datasource.WorldChatRecord{},
}
err = datasource.SaveWorldChatRecord(chatRecordArray)
if err != nil {
log.Println("MongoDB数据库,新插入一条数据出错,err: ", err)
//ctx.JSON(http.StatusOK, gin.H{"code": 0, "message": "重置世界记忆文件,插入数据失败!"})
return
}
fmt.Println(config.ColorBlue, "用户聊天到结局,MongoDB创建新的聊天数组", config.ColorReset)
}
}
/*
发送消息成功,resCode1
*/
if resCode == 1 {
/*//增加数字人的交互量
err := models.IncrementDigitalPersonInteractionCount(c.DpConversations.DpIds[c.LastIndex])
if err != nil {
log.Println(config.ColorRed, "更新交互量失败:", err, config.ColorReset)
}
fmt.Println(config.ColorGreen, "交互量更新成功!", config.ColorReset)*/
if c.AuthId != "" {
sp, err := models.GetServiceProviderById(c.AuthId)
if err != nil {
fmt.Println("根据服务商id查询出错:", err)
return
}
if sp != nil && c.UserSource == sp.Name {
/* 添加服务商调用AI会话,灵感消耗日志信息 */
museNum := int64(echoResponse.MuseValue)
/* 更新服务商的灵感值 */
if museNum > sp.InspirationValue {
// 处理错误或返回
fmt.Println("服务商灵感值不足本次扣除")
return
}
sp.InspirationValue = sp.InspirationValue - museNum
sp.TotalInspiration = sp.TotalInspiration + museNum
err := models.UpdateServiceProvider(sp)
if err != nil {
log.Println(config.ColorRed, "更新服务商灵感消耗值失败:", err, config.ColorReset)
return
} else {
log.Println(config.ColorBlue, "更新服务商灵感消耗值成功,剩余灵感值:", sp.InspirationValue, config.ColorReset)
}
}
}
}
}
}()
}
}
// ExtractRating 从响应中提取整体评分
func ExtractRating(response WorldEchoResponse) (int, error) {
endStr, ok := response.WObj["EndStr"].(string)
if !ok || endStr == "" {
return 0, nil
}
lines := strings.Split(endStr, "\n")
for _, line := range lines {
if strings.Contains(line, "【整体评分】") {
scoreStr := strings.TrimSpace(strings.Split(line, ":")[1])
score, err := strconv.Atoi(scoreStr)
if err != nil {
return 0, fmt.Errorf("评分转换错误: %v", err)
}
return score, nil
}
}
return 0, fmt.Errorf("未找到评分信息")
}
func ExtractRating1(response WorldEchoResponse) string {
endStr, ok := response.WObj["EndStr"].(string)
if !ok {
return "评分信息不可用"
}
lines := strings.Split(endStr, "\n")
for _, line := range lines {
if strings.Contains(line, "【整体评分】") {
// 假设评分总是单个数字跟在冒号和换行符后面
return strings.TrimSpace(strings.Split(line, ":")[1])
}
}
return "评分信息不可用"
}
/* ============================================================================================= */
// ParseEndStrAndReformat 重新解析 WorldSoulReplyMsg 中的 WObj 并返回一个新的结构体
func ParseEndStrAndReformat(response *WorldSoulReplyMsg) *WorldSoulReplyMsg {
if response == nil || response.WObj == nil {
return nil // 如果输入是nil,直接返回nil
}
//newResponse := *response // 创建一个新的响应体对象副本
newResponse := *DeepCopy(response)
if len(newResponse.WObj) == 0 && strings.Contains(newResponse.ISLIU, "【任务提示】") {
newResponse.MessageStatusType = "prologue"
} else if endStr, ok := newResponse.WObj["EndStr"].(string); ok && endStr != "" {
newResponse.MessageStatusType = "end"
} else {
newResponse.MessageStatusType = "chatting"
}
endStr, ok := newResponse.WObj["EndStr"].(string)
if ok {
if endStr != "" {
// 解析 "EndStr" 中的详细字段
title := strings.Split(endStr, "@")[0] // 提取 '@' 前的标题
overallScore := extractBetween(endStr, "【整体评分】:", "\n") // 提取整体评分
objectiveEvaluation := extractBetween(endStr, "【客观评价】:", "## 【整体评分】") // 提取客观评价至字符串末尾
// "EndStr" 结构化为 JSON 对象
endStrObj := map[string]interface{}{
"title": title,
"overallScore": strings.TrimSpace(overallScore),
"objectiveEvaluation": strings.TrimSpace(objectiveEvaluation),
}
newResponse.WObj["EndStr"] = endStrObj // 将格式化后的结论字符串对象重新赋值给响应
} else {
newResponse.WObj["EndStr"] = nil
}
}
// 无论 "EndStr" 是否存在,均检查和处理 '地点' '表情'
if address, exists := newResponse.WObj["地点"]; exists {
newResponse.WObj["address"] = address
delete(newResponse.WObj, "地点")
}
if emotion, exists := newResponse.WObj["表情"]; exists {
newResponse.WObj["emotion"] = emotion
delete(newResponse.WObj, "表情")
}
if chatTime, exists := newResponse.WObj["时间"]; exists {
newResponse.WObj["time"] = chatTime
delete(newResponse.WObj, "时间")
}
return &newResponse // 返回修改后的新响应体
}
// extractBetween 查找并提取开始和结束分隔符之间的子字符串
func extractBetween(s, start, end string) string {
startIdx := strings.Index(s, start)
if startIdx == -1 {
return ""
}
startPos := startIdx + len(start)
endIdx := strings.Index(s[startPos:], end)
if endIdx == -1 {
return s[startPos:]
}
return s[startPos : startPos+endIdx]
}
// extractToEnd 查找并提取从指定开始分隔符到字符串末尾的内容
func extractToEnd(s, start string) string {
startIdx := strings.Index(s, start)
if startIdx == -1 {
return ""
}
return s[startIdx+len(start):]
}
//深拷贝对象
func DeepCopy(msg *WorldSoulReplyMsg) *WorldSoulReplyMsg {
// 创建一个新的 WorldSoulReplyMsg 实例
newMsg := &WorldSoulReplyMsg{
Code: msg.Code,
ISLIU: msg.ISLIU,
WorldName: msg.WorldName,
}
// 深拷贝 WObj map
if msg.WObj != nil {
newMsg.WObj = make(map[string]interface{})
for k, v := range msg.WObj {
newMsg.WObj[k] = v // 注意这里只是简单拷贝,对于更复杂的对象可能需要更深层次的拷贝
}
}
// 复制可选指针类型字段 IsLIUId
if msg.IsLIUId != nil {
newIsLIUId := *msg.IsLIUId // 创建 IsLIUId 的副本
newMsg.IsLIUId = &newIsLIUId
}
// 复制其他可选字段
if msg.MessageStatusType != "" {
newMsg.MessageStatusType = msg.MessageStatusType
}
return newMsg
}
/*
实现WorldWrite方法
WorldWrite方法需要修改以适应从通道中读取*WorldEchoRespones类型的数据并将其发送到WebSocket
*/
func (c *WorldClient) WorldWrite() {
defer func() {
_ = c.Socket.Close()
}()
for {
select {
case message, ok := <-c.Send:
if !ok {
// 如果通道被关闭,发送WebSocket关闭消息并退出goroutine
_ = c.Socket.WriteMessage(websocket.CloseMessage, []byte{})
return
}
// 序列化消息为JSON
msg, err := json.Marshal(message)
if err != nil {
log.Printf("Error marshaling message: %s\n", err)
continue
}
// 发送序列化后的消息到WebSocket连接
if err := c.Socket.WriteMessage(websocket.TextMessage, msg); err != nil {
// 处理写入错误,例如可以记录日志或关闭连接
log.Printf("WebSocket write error: %s\n", err)
return
}
}
}
}
/* ============================================================================================= */
func (c *WorldClient) WorldWrite1() {
defer func() {
_ = c.Socket.Close()
}()
for {
select {
case message, ok := <-c.Send:
if !ok {
_ = c.Socket.WriteMessage(websocket.CloseMessage, []byte{})
return
}
/*
resCode := message.Code
status := message.Status
ISLIU := message.ISLIU
*/
log.Println(c.WorldConversations.Uid, "接受消息:", message)
replyMsg := WorldReplyMsg{
Code: e.WebsocketSuccess,
Content: fmt.Sprintf("%s", message),
}
msg, _ := json.Marshal(replyMsg)
_ = c.Socket.WriteMessage(websocket.TextMessage, msg)
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment