diff --git a/api/go/core/app_server.go b/api/go/core/app_server.go index ed2e5630..75188af8 100644 --- a/api/go/core/app_server.go +++ b/api/go/core/app_server.go @@ -23,14 +23,14 @@ var logger = logger2.GetLogger() type AppServer struct { AppConfig *types.AppConfig Engine *gin.Engine - ChatContexts map[string][]types.Message // 聊天上下文 [chatId] => []Message - ChatConfig *types.ChatConfig // 聊天配置 + ChatContexts *types.LMap[string, []types.Message] // 聊天上下文 Map [chatId] => []Message + ChatConfig *types.ChatConfig // 聊天配置 // 保存 Websocket 会话 UserId, 每个 UserId 只能连接一次 // 防止第三方直接连接 socket 调用 OpenAI API - ChatSession map[string]types.ChatSession //map[sessionId]UserId - ChatClients map[string]*WsClient // Websocket 连接集合 - ReqCancelFunc map[string]context.CancelFunc // HttpClient 请求取消 handle function + ChatSession *types.LMap[string, types.ChatSession] //map[sessionId]UserId + ChatClients *types.LMap[string, *types.WsClient] // Websocket 连接集合 + ReqCancelFunc *types.LMap[string, context.CancelFunc] // HttpClient 请求取消 handle function } func NewServer(appConfig *types.AppConfig) *AppServer { @@ -39,10 +39,10 @@ func NewServer(appConfig *types.AppConfig) *AppServer { return &AppServer{ AppConfig: appConfig, Engine: gin.Default(), - ChatContexts: make(map[string][]types.Message, 16), - ChatSession: make(map[string]types.ChatSession), - ChatClients: make(map[string]*WsClient), - ReqCancelFunc: make(map[string]context.CancelFunc), + ChatContexts: types.NewLMap[string, []types.Message](), + ChatSession: types.NewLMap[string, types.ChatSession](), + ChatClients: types.NewLMap[string, *types.WsClient](), + ReqCancelFunc: types.NewLMap[string, context.CancelFunc](), } } @@ -149,7 +149,8 @@ func authorizeMiddleware(s *AppServer) gin.HandlerFunc { // WebSocket 连接请求验证 if c.Request.URL.Path == "/api/chat" { sessionId := c.Query("sessionId") - if session, ok := s.ChatSession[sessionId]; ok && session.ClientIP == c.ClientIP() { + session := s.ChatSession.Get(sessionId) + if session.ClientIP == c.ClientIP() { c.Next() } else { c.Abort() diff --git a/api/go/core/client.go b/api/go/core/types/client.go similarity index 98% rename from api/go/core/client.go rename to api/go/core/types/client.go index a4fa3283..90a21f4a 100644 --- a/api/go/core/client.go +++ b/api/go/core/types/client.go @@ -1,4 +1,4 @@ -package core +package types import ( "errors" diff --git a/api/go/core/types/locked_map.go b/api/go/core/types/locked_map.go new file mode 100644 index 00000000..f05f5b3f --- /dev/null +++ b/api/go/core/types/locked_map.go @@ -0,0 +1,63 @@ +package types + +import ( + "context" + "sync" +) + +type MKey interface { + string | int +} +type MValue interface { + *WsClient | ChatSession | []Message | context.CancelFunc +} +type LMap[K MKey, T MValue] struct { + lock sync.RWMutex + data map[K]T +} + +func NewLMap[K MKey, T MValue]() *LMap[K, T] { + return &LMap[K, T]{ + lock: sync.RWMutex{}, + data: make(map[K]T), + } +} + +func (m *LMap[K, T]) Put(key K, value T) { + m.lock.Lock() + defer m.lock.Unlock() + + m.data[key] = value +} + +func (m *LMap[K, T]) Get(key K) T { + m.lock.RLock() + defer m.lock.RUnlock() + + return m.data[key] +} + +func (m *LMap[K, T]) Has(key K) bool { + m.lock.RLock() + defer m.lock.RUnlock() + _, ok := m.data[key] + return ok +} + +func (m *LMap[K, T]) Delete(key K) { + m.lock.Lock() + defer m.lock.Unlock() + + delete(m.data, key) +} + +func (m *LMap[K, T]) ToList() []T { + m.lock.Lock() + defer m.lock.Unlock() + + var s = make([]T, 0) + for _, v := range m.data { + s = append(s, v) + } + return s +} diff --git a/api/go/go.mod b/api/go/go.mod index 3b82de02..f689603d 100644 --- a/api/go/go.mod +++ b/api/go/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/BurntSushi/toml v1.1.0 github.com/gin-contrib/sessions v0.0.5 - github.com/gin-gonic/gin v1.7.7 + github.com/gin-gonic/gin v1.9.0 github.com/gorilla/websocket v1.5.0 github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20230415042440-a5e3d8259ae0 github.com/pkoukk/tiktoken-go v0.1.1-0.20230418101013-cae809389480 @@ -15,29 +15,40 @@ require ( ) require ( + github.com/bytedance/sonic v1.8.0 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/dlclark/regexp2 v1.8.1 // indirect github.com/go-sql-driver/mysql v1.7.0 // indirect + github.com/goccy/go-json v0.10.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect + github.com/klauspost/cpuid/v2 v2.0.9 // indirect + github.com/pelletier/go-toml/v2 v2.0.6 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect go.uber.org/dig v1.16.1 // indirect + golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect + golang.org/x/net v0.7.0 // indirect + golang.org/x/text v0.7.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) require ( github.com/gin-contrib/sse v0.1.0 // indirect - github.com/go-playground/locales v0.13.0 // indirect - github.com/go-playground/universal-translator v0.17.0 // indirect - github.com/go-playground/validator/v10 v10.4.1 // indirect - github.com/golang/protobuf v1.3.3 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.11.2 // indirect + github.com/golang/protobuf v1.5.0 // indirect github.com/golang/snappy v0.0.1 // indirect github.com/gorilla/context v1.1.1 // indirect github.com/gorilla/securecookie v1.1.1 // indirect github.com/gorilla/sessions v1.2.1 // indirect - github.com/json-iterator/go v1.1.9 // indirect - github.com/leodido/go-urn v1.2.0 // indirect - github.com/mattn/go-isatty v0.0.12 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/leodido/go-urn v1.2.1 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect - github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect - github.com/ugorji/go/codec v1.1.7 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/ugorji/go/codec v1.2.9 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/fx v1.19.3 go.uber.org/multierr v1.6.0 // indirect diff --git a/api/go/go.sum b/api/go/go.sum index b22b7c21..87f34f12 100644 --- a/api/go/go.sum +++ b/api/go/go.sum @@ -1,6 +1,12 @@ github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA= +github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -13,22 +19,34 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs= github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= +github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= +github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU= +github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s= github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= +github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= @@ -47,35 +65,58 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20230415042440-a5e3d8259ae0 h1:LgmjED/yQILqmUED4GaXjrINWe7YJh4HM6z2EvEINPs= github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20230415042440-a5e3d8259ae0/go.mod h1:C5LA5UO2ZXJrLaPLYtE1wUJMiyd/nwWaCO5cw/2pSHs= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= +github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkoukk/tiktoken-go v0.1.1-0.20230418101013-cae809389480 h1:IFhPCcB0/HtnEN+ZoUGDT55YgFCymbFJ15kXqs3nv5w= github.com/pkoukk/tiktoken-go v0.1.1-0.20230418101013-cae809389480/go.mod h1:BijIqAP84FMYC4XbdJgjyMpiSjusU8x0Y0W9K2t0QtU= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.9 h1:rmenucSohSTiyL09Y+l2OCk+FrMxGMzho2+tjr5ticU= +github.com/ugorji/go/codec v1.2.9/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/dig v1.16.1 h1:+alNIBsl0qfY0j6epRubp/9obgtrObRAc5aD+6jbWY8= @@ -87,6 +128,8 @@ go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= @@ -94,17 +137,25 @@ golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= @@ -115,9 +166,12 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/mysql v1.4.7 h1:rY46lkCspzGHn7+IYsNpSfEv9tA+SU4SkkB+GFX125Y= gorm.io/driver/mysql v1.4.7/go.mod h1:SxzItlnT1cb6e1e4ZRpgJN2VYtcqJgqnHxWr4wsP8oc= gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= gorm.io/gorm v1.25.1 h1:nsSALe5Pr+cM3V1qwwQ7rOkw+6UeLrX5O4v3llhHa64= gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/api/go/handler/chat_handler.go b/api/go/handler/chat_handler.go index ccea19a7..cac3239d 100644 --- a/api/go/handler/chat_handler.go +++ b/api/go/handler/chat_handler.go @@ -48,12 +48,13 @@ func (h *ChatHandler) ChatHandle(c *gin.Context) { logger.Error(err) return } - sessionId := c.Query("sessionId") - roleId := param.GetInt(c, "roleId", 0) - chatId := c.Query("chatId") + sessionId := c.Query("session_id") + roleId := param.GetInt(c, "role_id", 0) + chatId := c.Query("chat_id") chatModel := c.Query("model") - session, ok := h.app.ChatSession[sessionId] - if !ok { + + session := h.app.ChatSession.Get(sessionId) + if session.SessionId == "" { logger.Info("用户未登录") c.Abort() return @@ -70,7 +71,7 @@ func (h *ChatHandler) ChatHandle(c *gin.Context) { session.ChatId = chatId session.Model = chatModel logger.Infof("New websocket connected, IP: %s, Username: %s", c.Request.RemoteAddr, session.Username) - client := core.NewWsClient(ws) + client := types.NewWsClient(ws) var chatRole model.ChatRole res = h.db.First(&chatRole, roleId) if res.Error != nil || !chatRole.Enable { @@ -80,21 +81,21 @@ func (h *ChatHandler) ChatHandle(c *gin.Context) { } // 保存会话连接 - h.app.ChatClients[chatId] = client + h.app.ChatClients.Put(sessionId, client) go func() { for { _, message, err := client.Receive() if err != nil { logger.Error(err) client.Close() - delete(h.app.ChatClients, chatId) - delete(h.app.ReqCancelFunc, chatId) + h.app.ChatClients.Delete(sessionId) + h.app.ReqCancelFunc.Delete(sessionId) return } logger.Info("Receive a message: ", string(message)) //replyMessage(client, "这是一条测试消息!") ctx, cancel := context.WithCancel(context.Background()) - h.app.ReqCancelFunc[chatId] = cancel + h.app.ReqCancelFunc.Put(sessionId, cancel) // 回复消息 err = h.sendMessage(ctx, session, chatRole, string(message), client) if err != nil { @@ -109,7 +110,7 @@ func (h *ChatHandler) ChatHandle(c *gin.Context) { } // 将消息发送给 ChatGPT 并获取结果,通过 WebSocket 推送到客户端 -func (h *ChatHandler) sendMessage(ctx context.Context, session types.ChatSession, role model.ChatRole, prompt string, ws core.Client) error { +func (h *ChatHandler) sendMessage(ctx context.Context, session types.ChatSession, role model.ChatRole, prompt string, ws types.Client) error { promptCreatedAt := time.Now() // 记录提问时间 var user model.User @@ -152,8 +153,8 @@ func (h *ChatHandler) sendMessage(ctx context.Context, session types.ChatSession // 加载聊天上下文 var chatCtx []types.Message if userVo.ChatConfig.EnableContext { - if v, ok := h.app.ChatContexts[session.ChatId]; ok { - chatCtx = v + if h.app.ChatContexts.Has(session.ChatId) { + chatCtx = h.app.ChatContexts.Get(session.ChatId) } else { // 加载角色信息 var messages []types.Message @@ -262,7 +263,7 @@ func (h *ChatHandler) sendMessage(ctx context.Context, session types.ChatSession if userVo.ChatConfig.EnableContext { chatCtx = append(chatCtx, useMsg) // 提问消息 chatCtx = append(chatCtx, message) // 回复消息 - h.app.ChatContexts[session.ChatId] = chatCtx + h.app.ChatContexts.Put(session.ChatId, chatCtx) } // 追加聊天记录 @@ -348,9 +349,9 @@ func (h *ChatHandler) sendMessage(ctx context.Context, session types.ChatSession } else if strings.Contains(res.Error.Message, "This model's maximum context length") { replyMessage(ws, "当前会话上下文长度超出限制,已为您删减会话上下文!") // 只保留最近的三条记录 - chatContext := h.app.ChatContexts[session.ChatId] + chatContext := h.app.ChatContexts.Get(session.ChatId) chatContext = chatContext[len(chatContext)-3:] - h.app.ChatContexts[session.ChatId] = chatContext + h.app.ChatContexts.Put(session.ChatId, chatContext) return h.sendMessage(ctx, session, role, prompt, ws) } else { replyMessage(ws, "请求 OpenAI API 失败:"+res.Error.Message) @@ -410,20 +411,20 @@ func (h *ChatHandler) doRequest(ctx context.Context, user vo.User, apiKey *strin } // 回复客户片段端消息 -func replyChunkMessage(client core.Client, message types.WsMessage) { +func replyChunkMessage(client types.Client, message types.WsMessage) { msg, err := json.Marshal(message) if err != nil { logger.Errorf("Error for decoding json data: %v", err.Error()) return } - err = client.(*core.WsClient).Send(msg) + err = client.(*types.WsClient).Send(msg) if err != nil { logger.Errorf("Error for reply message: %v", err.Error()) } } // 回复客户端一条完整的消息 -func replyMessage(ws core.Client, message string) { +func replyMessage(ws types.Client, message string) { replyChunkMessage(ws, types.WsMessage{Type: types.WsStart}) replyChunkMessage(ws, types.WsMessage{Type: types.WsMiddle, Content: message}) replyChunkMessage(ws, types.WsMessage{Type: types.WsEnd}) @@ -444,10 +445,10 @@ func (h *ChatHandler) Tokens(c *gin.Context) { // StopGenerate 停止生成 func (h *ChatHandler) StopGenerate(c *gin.Context) { - chatId := c.Query("chat_id") - if cancel, ok := h.app.ReqCancelFunc[chatId]; ok { - cancel() - delete(h.app.ReqCancelFunc, chatId) + sessionId := c.Query("session_id") + if h.app.ReqCancelFunc.Has(sessionId) { + h.app.ReqCancelFunc.Get(sessionId)() + h.app.ReqCancelFunc.Delete(sessionId) } resp.SUCCESS(c, types.OkMsg) } diff --git a/api/go/handler/chat_history_handler.go b/api/go/handler/chat_history_handler.go index 1fe1d9f1..39816fba 100644 --- a/api/go/handler/chat_history_handler.go +++ b/api/go/handler/chat_history_handler.go @@ -89,7 +89,7 @@ func (h *ChatHandler) Remove(c *gin.Context) { } // 清空会话上下文 - delete(h.app.ChatContexts, chatId) + h.app.ChatContexts.Delete(chatId) resp.SUCCESS(c, types.OkMsg) } @@ -144,7 +144,7 @@ func (h *ChatHandler) Clear(c *gin.Context) { logger.Warnf("Failed to delele chat history for ChatID: %s", chat.ChatId) } // 清空会话上下文 - delete(h.app.ChatContexts, chat.ChatId) + h.app.ChatContexts.Delete(chat.ChatId) } // 删除所有的会话记录 res = h.db.Where("user_id = ?", user.Id).Delete(&model.ChatItem{}) diff --git a/api/go/handler/user_handler.go b/api/go/handler/user_handler.go index 6a20c8f0..625d8f46 100644 --- a/api/go/handler/user_handler.go +++ b/api/go/handler/user_handler.go @@ -168,7 +168,7 @@ func (h *UserHandler) Login(c *gin.Context) { } // 记录登录信息在服务器 - h.app.ChatSession[sessionId] = types.ChatSession{ClientIP: c.ClientIP(), UserId: user.Id, Username: data.Username, SessionId: sessionId} + h.app.ChatSession.Put(sessionId, types.ChatSession{ClientIP: c.ClientIP(), UserId: user.Id, Username: data.Username, SessionId: sessionId}) // 加载用户订阅的聊天角色 var roleMap map[string]int @@ -237,9 +237,10 @@ func (h *UserHandler) Logout(c *gin.Context) { logger.Error("Error for save session: ", err) } // 删除 websocket 会话列表 - delete(h.app.ChatSession, sessionId) + h.app.ChatSession.Delete(sessionId) // 关闭 socket 连接 - if client, ok := h.app.ChatClients[sessionId]; ok { + client := h.app.ChatClients.Get(sessionId) + if client != nil { client.Close() } resp.SUCCESS(c) @@ -248,7 +249,8 @@ func (h *UserHandler) Logout(c *gin.Context) { // Session 获取/验证会话 func (h *UserHandler) Session(c *gin.Context) { sessionId := c.GetHeader(types.TokenSessionName) - if session, ok := h.app.ChatSession[sessionId]; ok && session.ClientIP == c.ClientIP() { + session := h.app.ChatSession.Get(sessionId) + if session.ClientIP == c.ClientIP() { resp.SUCCESS(c, session) } else { resp.NotAuth(c) diff --git a/api/go/test/test.go b/api/go/test/test.go index 99167d5a..be9c81c7 100644 --- a/api/go/test/test.go +++ b/api/go/test/test.go @@ -1,6 +1,7 @@ package main import ( + "chatplus/core/types" "chatplus/store/model" "chatplus/store/vo" "chatplus/utils" @@ -16,7 +17,11 @@ import ( ) func main() { - //resp.JSON(nil, types.Success, types.OkMsg, new) + lMap := types.NewLMap[string, types.ChatSession]() + lMap.Put("name", types.ChatSession{SessionId: utils.RandString(32)}) + + item := lMap.Get("abc") + fmt.Println(item) } // Http client 取消操作 diff --git a/web/src/views/ChatPlus.vue b/web/src/views/ChatPlus.vue index d0a1941d..c53df95a 100644 --- a/web/src/views/ChatPlus.vue +++ b/web/src/views/ChatPlus.vue @@ -210,7 +210,6 @@ import {httpGet, httpPost} from "@/utils/http"; import {useRouter} from "vue-router"; import Clipboard from "clipboard"; import ConfigDialog from "@/components/ConfigDialog.vue"; -import FooterBar from "@/components/FooterBar.vue"; const title = ref('ChatGPT-智能助手'); const logo = 'images/logo.png'; @@ -439,7 +438,7 @@ const connect = function (chat_id, role_id) { host = 'ws://'+location.host; } } - const _socket = new WebSocket(host + `/api/chat/new?sessionId=${_sessionId}&roleId=${role_id}&chatId=${chat_id}&model=${model.value}`); + const _socket = new WebSocket(host + `/api/chat/new?session_id=${_sessionId}&role_id=${role_id}&chat_id=${chat_id}&model=${model.value}`); _socket.addEventListener('open', () => { chatData.value = []; // 初始化聊天数据 previousText.value = ''; @@ -555,6 +554,7 @@ const inputKeyDown = function (e) { return; } e.preventDefault(); + sendMessage(); } } // 发送消息 @@ -666,7 +666,7 @@ const loadChatHistory = function (chatId) { const stopGenerate = function () { showStopGenerate.value = false; - httpGet("/api/chat/stop?chat_id=" + activeChat.value['chat_id']).then(() => { + httpGet("/api/chat/stop?session_id=" + getSessionId() ).then(() => { canSend.value = true; if (previousText.value !== '') { showReGenerate.value = true;