Pārlūkot izejas kodu

用户已在线处理

WH01243 3 nedēļas atpakaļ
vecāks
revīzija
719dd858bf

+ 11 - 2
client/chat/chat.pb.go

@@ -112,6 +112,7 @@ func (x *PingResponse) GetTimestamp() int64 {
 type JoinRequest struct {
 type JoinRequest struct {
 	state         protoimpl.MessageState `protogen:"open.v1"`
 	state         protoimpl.MessageState `protogen:"open.v1"`
 	UserId        string                 `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
 	UserId        string                 `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
+	Force         bool                   `protobuf:"varint,2,opt,name=force,proto3" json:"force,omitempty"`
 	unknownFields protoimpl.UnknownFields
 	unknownFields protoimpl.UnknownFields
 	sizeCache     protoimpl.SizeCache
 	sizeCache     protoimpl.SizeCache
 }
 }
@@ -153,6 +154,13 @@ func (x *JoinRequest) GetUserId() string {
 	return ""
 	return ""
 }
 }
 
 
+func (x *JoinRequest) GetForce() bool {
+	if x != nil {
+		return x.Force
+	}
+	return false
+}
+
 type Message struct {
 type Message struct {
 	state         protoimpl.MessageState `protogen:"open.v1"`
 	state         protoimpl.MessageState `protogen:"open.v1"`
 	UserId        string                 `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
 	UserId        string                 `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
@@ -281,9 +289,10 @@ const file_proto_chat_proto_rawDesc = "" +
 	"\vPingRequest\"D\n" +
 	"\vPingRequest\"D\n" +
 	"\fPingResponse\x12\x16\n" +
 	"\fPingResponse\x12\x16\n" +
 	"\x06status\x18\x01 \x01(\tR\x06status\x12\x1c\n" +
 	"\x06status\x18\x01 \x01(\tR\x06status\x12\x1c\n" +
-	"\ttimestamp\x18\x02 \x01(\x03R\ttimestamp\"&\n" +
+	"\ttimestamp\x18\x02 \x01(\x03R\ttimestamp\"<\n" +
 	"\vJoinRequest\x12\x17\n" +
 	"\vJoinRequest\x12\x17\n" +
-	"\auser_id\x18\x01 \x01(\tR\x06userId\"l\n" +
+	"\auser_id\x18\x01 \x01(\tR\x06userId\x12\x14\n" +
+	"\x05force\x18\x02 \x01(\bR\x05force\"l\n" +
 	"\aMessage\x12\x17\n" +
 	"\aMessage\x12\x17\n" +
 	"\auser_id\x18\x01 \x01(\tR\x06userId\x12\x12\n" +
 	"\auser_id\x18\x01 \x01(\tR\x06userId\x12\x12\n" +
 	"\x04text\x18\x02 \x01(\tR\x04text\x12\x1c\n" +
 	"\x04text\x18\x02 \x01(\tR\x04text\x12\x1c\n" +

+ 2 - 1
client/config.json

@@ -1,4 +1,5 @@
 {
 {
-  "serviceAddress": "jybx3-webtest.jydev.jianyu360.com:50051",
+  "serviceAddress1": "jybx3-webtest.jydev.jianyu360.com:50051",
+  "serviceAddress": "127.0.0.1:50051",
   "informationDelay": 20
   "informationDelay": 20
 }
 }

+ 1 - 0
client/proto/chat.proto

@@ -17,6 +17,7 @@ message PingResponse {
 }
 }
 message JoinRequest {
 message JoinRequest {
   string user_id = 1;
   string user_id = 1;
+  bool force=2;
 }
 }
 
 
 message Message {
 message Message {

+ 3 - 3
client/service/chatClient.go

@@ -102,7 +102,7 @@ func (c *ChatClient) connect() error {
 		}
 		}
 	}
 	}
 	client := NewChatServiceClient(conn)
 	client := NewChatServiceClient(conn)
-	_, err = client.JoinChat(context.Background(), &JoinRequest{UserId: c.userID})
+	_, err = client.JoinChat(context.Background(), &JoinRequest{UserId: c.userID, Force: true})
 	if err != nil {
 	if err != nil {
 		_ = conn.Close()
 		_ = conn.Close()
 		return fmt.Errorf("连接测试失败: %v", err)
 		return fmt.Errorf("连接测试失败: %v", err)
@@ -261,7 +261,7 @@ func (c *ChatClient) establishStream() {
 			stream, err := func() (ChatService_JoinChatClient, error) {
 			stream, err := func() (ChatService_JoinChatClient, error) {
 				c.streamMutex.Lock()
 				c.streamMutex.Lock()
 				defer c.streamMutex.Unlock()
 				defer c.streamMutex.Unlock()
-				return c.client.JoinChat(c.ctx, &JoinRequest{UserId: c.userID})
+				return c.client.JoinChat(c.ctx, &JoinRequest{UserId: c.userID, Force: true})
 			}()
 			}()
 
 
 			if err != nil {
 			if err != nil {
@@ -422,7 +422,7 @@ func (c *ChatClient) checkHealth() error {
 	defer cancel()
 	defer cancel()
 
 
 	// 优先使用JoinChat作为健康检查
 	// 优先使用JoinChat作为健康检查
-	_, err := c.client.JoinChat(ctx, &JoinRequest{UserId: c.userID})
+	_, err := c.client.JoinChat(ctx, &JoinRequest{UserId: c.userID, Force: true})
 	if err != nil {
 	if err != nil {
 		st, ok := status.FromError(err)
 		st, ok := status.FromError(err)
 		if ok && st.Code() == codes.Unimplemented {
 		if ok && st.Code() == codes.Unimplemented {

+ 20 - 20
client/service/wx.go

@@ -137,17 +137,18 @@ func GetContacts() {
 		// 过滤不需要的联系人类型:
 		// 过滤不需要的联系人类型:
 		// 1. 以@openim结尾的联系人(可能是系统账号)
 		// 1. 以@openim结尾的联系人(可能是系统账号)
 		// 2. 备注为空的联系人
 		// 2. 备注为空的联系人
-		isavailable, phone := userJudge(c)
+		isavailable, phone, name := userJudge(c)
 		if !isavailable {
 		if !isavailable {
 			continue
 			continue
 		}
 		}
 		// 构建联系人信息字典
 		// 构建联系人信息字典
 		returnData = append(returnData, map[string]interface{}{
 		returnData = append(returnData, map[string]interface{}{
-			"name":   c.Name,   // 联系人昵称
-			"code":   c.Code,   // 联系人编码
-			"wxid":   c.Wxid,   // 微信ID
-			"remark": c.Remark, // 备注名
-			"phone":  phone,
+			"name":        c.Name,   // 联系人昵称
+			"code":        c.Code,   // 联系人编码
+			"wxid":        c.Wxid,   // 微信ID
+			"remark":      c.Remark, // 备注名
+			"phone":       phone,
+			"appellation": name,
 		})
 		})
 
 
 	}
 	}
@@ -158,15 +159,16 @@ func GetContacts() {
 }
 }
 
 
 // 备注解析
 // 备注解析
-func extractPhoneNumber(input string) (string, error) {
+func extractPhoneNumber(input string) (string, string, error) {
 	// 正则表达式匹配以字母开头、结尾为手机号的字符串
 	// 正则表达式匹配以字母开头、结尾为手机号的字符串
-	re := regexp.MustCompile(`^[A-Za-z].*?(\d{11})$`)
+	//re := regexp.MustCompile(`^[A-Za-z].*?(\d{11})$`)
+	re := regexp.MustCompile(`^[A-Za-z].*/([^/]+)(\d{11})$`)
 	matches := re.FindStringSubmatch(input)
 	matches := re.FindStringSubmatch(input)
-	if len(matches) < 2 {
-		return "", fmt.Errorf("未找到匹配的手机号")
+	if len(matches) < 3 {
+		return "", "", fmt.Errorf("未找到匹配的手机号")
 	}
 	}
 
 
-	return matches[1], nil
+	return matches[2], matches[1], nil
 }
 }
 
 
 // killWeChat 关闭微信
 // killWeChat 关闭微信
@@ -185,19 +187,16 @@ func killWeChat() {
 }
 }
 
 
 // 用户身份判断
 // 用户身份判断
-func userJudge(c *wcf.RpcContact) (bool, string) {
-	phone, err := extractPhoneNumber(c.Remark)
+func userJudge(c *wcf.RpcContact) (bool, string, string) {
+	phone, name, _ := extractPhoneNumber(c.Remark)
 	if strings.HasSuffix(c.Wxid, "@openim") || c.Remark == "" || phone == "" || c.Name == "语音记事本" || c.Name == "文件传输助手" {
 	if strings.HasSuffix(c.Wxid, "@openim") || c.Remark == "" || phone == "" || c.Name == "语音记事本" || c.Name == "文件传输助手" {
-		if phone == "" {
-			log.Println("手机号:", err)
-		}
-		return false, ""
+		return false, "", ""
 	}
 	}
-	return true, phone
+	return true, phone, name
 }
 }
 
 
 // 发送文本信息
 // 发送文本信息
-func sendText(content, wxId string) (bool, error) {
+func sendText(content, wxId, appellation string) (bool, error) {
 	if app.WxClient.SendTxt(content, wxId, nil) != 0 {
 	if app.WxClient.SendTxt(content, wxId, nil) != 0 {
 		return false, fmt.Errorf(fmt.Sprintf("%s%s文字消息发送失败", content, wxId))
 		return false, fmt.Errorf(fmt.Sprintf("%s%s文字消息发送失败", content, wxId))
 	}
 	}
@@ -306,6 +305,7 @@ func doSendTalk(dataStr string) error {
 	userMap := gconv.Map(dataMap["user"])
 	userMap := gconv.Map(dataMap["user"])
 	replyLanguage := gconv.String(dataMap["replyLanguage"])
 	replyLanguage := gconv.String(dataMap["replyLanguage"])
 	wxId := gconv.String(userMap["wxid"])
 	wxId := gconv.String(userMap["wxid"])
+	appellation := gconv.String(userMap["appellation"])
 	batchCode := gconv.String(dataMap["batchCode"])
 	batchCode := gconv.String(dataMap["batchCode"])
 	baseUserId := gconv.String(userMap["baseUserId"])
 	baseUserId := gconv.String(userMap["baseUserId"])
 	ok := true
 	ok := true
@@ -314,7 +314,7 @@ func doSendTalk(dataStr string) error {
 		content := gconv.String(v["content"])
 		content := gconv.String(v["content"])
 		switch gconv.String(v["content_type"]) {
 		switch gconv.String(v["content_type"]) {
 		case ContentTypeText, ContentTypeLink:
 		case ContentTypeText, ContentTypeLink:
-			ok1, err := sendText(content, wxId)
+			ok1, err := sendText(content, wxId, appellation)
 			if !ok1 {
 			if !ok1 {
 				ok = false
 				ok = false
 				log.Println(err)
 				log.Println(err)

+ 11 - 2
rpc/chat/chat.pb.go

@@ -104,6 +104,7 @@ func (x *PingResponse) GetStatus() string {
 type JoinRequest struct {
 type JoinRequest struct {
 	state         protoimpl.MessageState `protogen:"open.v1"`
 	state         protoimpl.MessageState `protogen:"open.v1"`
 	UserId        string                 `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
 	UserId        string                 `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
+	Force         bool                   `protobuf:"varint,2,opt,name=force,proto3" json:"force,omitempty"`
 	unknownFields protoimpl.UnknownFields
 	unknownFields protoimpl.UnknownFields
 	sizeCache     protoimpl.SizeCache
 	sizeCache     protoimpl.SizeCache
 }
 }
@@ -145,6 +146,13 @@ func (x *JoinRequest) GetUserId() string {
 	return ""
 	return ""
 }
 }
 
 
+func (x *JoinRequest) GetForce() bool {
+	if x != nil {
+		return x.Force
+	}
+	return false
+}
+
 type Message struct {
 type Message struct {
 	state         protoimpl.MessageState `protogen:"open.v1"`
 	state         protoimpl.MessageState `protogen:"open.v1"`
 	UserId        string                 `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
 	UserId        string                 `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
@@ -272,9 +280,10 @@ const file_proto_chat_proto_rawDesc = "" +
 	"\x10proto/chat.proto\x12\x04chat\"\r\n" +
 	"\x10proto/chat.proto\x12\x04chat\"\r\n" +
 	"\vPingRequest\"&\n" +
 	"\vPingRequest\"&\n" +
 	"\fPingResponse\x12\x16\n" +
 	"\fPingResponse\x12\x16\n" +
-	"\x06status\x18\x01 \x01(\tR\x06status\"&\n" +
+	"\x06status\x18\x01 \x01(\tR\x06status\"<\n" +
 	"\vJoinRequest\x12\x17\n" +
 	"\vJoinRequest\x12\x17\n" +
-	"\auser_id\x18\x01 \x01(\tR\x06userId\"l\n" +
+	"\auser_id\x18\x01 \x01(\tR\x06userId\x12\x14\n" +
+	"\x05force\x18\x02 \x01(\bR\x05force\"l\n" +
 	"\aMessage\x12\x17\n" +
 	"\aMessage\x12\x17\n" +
 	"\auser_id\x18\x01 \x01(\tR\x06userId\x12\x12\n" +
 	"\auser_id\x18\x01 \x01(\tR\x06userId\x12\x12\n" +
 	"\x04text\x18\x02 \x01(\tR\x04text\x12\x1c\n" +
 	"\x04text\x18\x02 \x01(\tR\x04text\x12\x1c\n" +

+ 217 - 0
rpc/go.sum

@@ -0,0 +1,217 @@
+app.yhyue.com/moapp/jybase v0.0.0-20250710095910-bbc834d8b6e8 h1:RwTxiG+yf5cvB9pDQuwAyqg/JsR1waxWKdLMJVHD5mc=
+app.yhyue.com/moapp/jybase v0.0.0-20250710095910-bbc834d8b6e8/go.mod h1:OEtMbsn7wY/7MLgV7yDUpVDKExUoj3B8h+4w4ZckJQQ=
+github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
+github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/ClickHouse/clickhouse-go v1.5.4/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
+github.com/ClickHouse/clickhouse-go/v2 v2.2.0 h1:dj00TDKY+xwuTJdbpspCSmTLFyWzRJerTHwaBxut1C0=
+github.com/ClickHouse/clickhouse-go/v2 v2.2.0/go.mod h1:8f2XZUi7XoeU+uPIytSi1cvx8fmJxi7vIgqpvYTF1+o=
+github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
+github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=
+github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
+github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
+github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
+github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
+github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
+github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
+github.com/garyburd/redigo v1.6.2 h1:yE/pwKCrbLpLpQICzYTeZ7JsTA/C53wFTJHaEtRqniM=
+github.com/garyburd/redigo v1.6.2/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
+github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
+github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
+github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/go-xweb/httpsession v0.0.0-20141220075701-356d3b4d38d6 h1:DUiWdm3rS8cC96Y0XeVkDeizzEK6X+qiNgXytLVtbkM=
+github.com/go-xweb/httpsession v0.0.0-20141220075701-356d3b4d38d6/go.mod h1:lwPk13GS+i/NK4FkMm68IcJrAwiu+HtjYa1Y4kW59aY=
+github.com/go-xweb/log v0.0.0-20140701090824-270d183ad77e h1:xmffs7hgrWpAOcquZrdlWpAEaAdlI9myaYcUUmhIP7k=
+github.com/go-xweb/log v0.0.0-20140701090824-270d183ad77e/go.mod h1:ASmYUSBf32lWkkNVX/pnOU4MLuUQpFH4qYHvWHt/l0w=
+github.com/go-xweb/uuid v0.0.0-20140604020037-d7dce341f851 h1:D46USD6oGNWzoJ/h5CWaFq3ELLoLoJzllJ03Xh78VYg=
+github.com/go-xweb/uuid v0.0.0-20140604020037-d7dce341f851/go.mod h1:OmDEC58ZYO1Esk+Uy32SB6LWof9lyROl7q76dBFOCWw=
+github.com/go-xweb/xweb v0.2.1 h1:u5t/ttuSfxiIMDTXj/Pouw9C2ASNABWT16JWHyrtdvY=
+github.com/go-xweb/xweb v0.2.1/go.mod h1:vPjYJgfidYAgBKIwiAyKFC1hfczlqsw9rRT8LtwrGew=
+github.com/gogf/gf/v2 v2.7.0 h1:CjxhbMiE7oqf6K8ZtGuKt3dQEwK4vL6LhiI+dI7tJGU=
+github.com/gogf/gf/v2 v2.7.0/go.mod h1:Qu8nimKt9aupJQcdUL85tWF4Mfxocz97zUt8UC4abVI=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
+github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
+github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
+github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0=
+github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
+github.com/howeyc/fsnotify v0.9.0 h1:0gtV5JmOKH4A8SsFxG2BczSeXWWPvcMT0euZt5gDAxY=
+github.com/howeyc/fsnotify v0.9.0/go.mod h1:41HzSPxBGeFRQKEEwgh49TRw/nKBsYZ2cF1OzPjSJsA=
+github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
+github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
+github.com/jinzhu/now v1.1.1 h1:g39TucaRWyV3dwDO++eEc6qf8TVIQ/Da48WmqjZ3i7E=
+github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
+github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lunny/csession v0.0.0-20130910075847-fe53c5de3dfd h1:DXxmBCahjva4Ox4AWOv6pR1Csv33zSj97SaLOElfIsw=
+github.com/lunny/csession v0.0.0-20130910075847-fe53c5de3dfd/go.mod h1:3w9PScemEkJoLw3OYvLWMoD8XRCmXgGwsSpT6pFpJ0g=
+github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
+github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+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/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
+github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=
+github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
+github.com/mkevac/debugcharts v0.0.0-20191222103121-ae1c48aa8615/go.mod h1:Ad7oeElCZqA1Ufj0U9/liOF4BtVepxRcTvr2ey7zTvM=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
+github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
+github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
+github.com/paulmach/orb v0.7.1 h1:Zha++Z5OX/l168sqHK3k4z18LDvr+YAO/VjK0ReQ9rU=
+github.com/paulmach/orb v0.7.1/go.mod h1:FWRlTgl88VI1RBx/MkrwWDRhQ96ctqMCh8boXhmqB/A=
+github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
+github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
+github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
+github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+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/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
+github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
+github.com/shirou/gopsutil v2.19.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
+github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
+github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
+github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
+github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
+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/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.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
+github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
+github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
+github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
+github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
+github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w=
+github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
+github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc=
+github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
+github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
+github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
+go.mongodb.org/mongo-driver v1.9.1 h1:m078y9v7sBItkt1aaoe2YlvWEXcD263e1a4E1fBrJ1c=
+go.mongodb.org/mongo-driver v1.9.1/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY=
+go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk=
+go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
+go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
+go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
+go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
+go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk=
+go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0=
+go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
+go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
+go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU=
+go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
+go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
+golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
+golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
+golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191220220014-0732a990476f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220429233432-b5fbb4746d32/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
+golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
+golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
+google.golang.org/grpc v1.69.0 h1:quSiOM1GJPmPH5XtU+BCoVXcDVJJAzNcoyfC2cCjGkI=
+google.golang.org/grpc v1.69.0/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
+google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/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.0.5 h1:WAAmvLK2rG0tCOqrf5XcLi2QUwugd4rcVJ/W3aoon9o=
+gorm.io/driver/mysql v1.0.5/go.mod h1:N1OIhHAIhx5SunkMGqWbGFVeh4yTNWKmMo1GOAsohLI=
+gorm.io/gorm v1.21.3 h1:qDFi55ZOsjZTwk5eN+uhAmHi8GysJ/qCTichM/yO7ME=
+gorm.io/gorm v1.21.3/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=

+ 1 - 0
rpc/proto/chat.proto

@@ -16,6 +16,7 @@ message PingResponse {
 }
 }
 message JoinRequest {
 message JoinRequest {
   string user_id = 1;
   string user_id = 1;
+  bool force = 2;
 }
 }
 
 
 message Message {
 message Message {

+ 19 - 8
rpc/service/chatServer.go

@@ -35,18 +35,31 @@ func NewChatServer() *ChatServer {
 
 
 // 建立连接
 // 建立连接
 func (s *ChatServer) JoinChat(req *JoinRequest, stream ChatService_JoinChatServer) error {
 func (s *ChatServer) JoinChat(req *JoinRequest, stream ChatService_JoinChatServer) error {
-	// 创建新通道
 	msgChan := make(chan *Message, 100)
 	msgChan := make(chan *Message, 100)
 
 
-	// 注册客户端
+	// 注册客户端(同步处理旧连接)
 	s.mu.Lock()
 	s.mu.Lock()
-	if _, exists := s.clients[req.UserId]; exists {
-		s.mu.Unlock()
-		return fmt.Errorf("用户 %s 已连接", req.UserId)
+	if existing, exists := s.clients[req.UserId]; exists {
+		if !req.Force {
+			s.mu.Unlock()
+			return fmt.Errorf("用户 %s 已连接", req.UserId)
+		}
+		close(existing) // 同步关闭旧 channel
+		delete(s.clients, req.UserId)
 	}
 	}
 	s.clients[req.UserId] = msgChan
 	s.clients[req.UserId] = msgChan
 	s.mu.Unlock()
 	s.mu.Unlock()
 
 
+	// 清理逻辑:确保只关闭自己的 channel
+	defer func() {
+		s.mu.Lock()
+		if s.clients[req.UserId] == msgChan {
+			close(msgChan)
+			delete(s.clients, req.UserId)
+		}
+		s.mu.Unlock()
+	}()
+
 	// 发送欢迎消息
 	// 发送欢迎消息
 	welcomeMsg := &Message{
 	welcomeMsg := &Message{
 		UserId:    "系统",
 		UserId:    "系统",
@@ -54,11 +67,9 @@ func (s *ChatServer) JoinChat(req *JoinRequest, stream ChatService_JoinChatServe
 		Timestamp: time.Now().Unix(),
 		Timestamp: time.Now().Unix(),
 	}
 	}
 	if err := stream.Send(welcomeMsg); err != nil {
 	if err := stream.Send(welcomeMsg); err != nil {
-		s.removeClient(req.UserId)
 		return err
 		return err
 	}
 	}
-	// 清理处理
-	defer s.removeClient(req.UserId)
+
 	// 消息循环
 	// 消息循环
 	for {
 	for {
 		select {
 		select {

+ 9 - 6
rpc/service/service.go

@@ -211,6 +211,7 @@ func SynchronousContacts(robotCode, contactsStr string) error {
 		baseUserId := gconv.Int64((*datas)[0]["base_user_id"])
 		baseUserId := gconv.Int64((*datas)[0]["base_user_id"])
 		contactData := map[string]interface{}{
 		contactData := map[string]interface{}{
 			"remark":       gconv.String(v["remark"]),
 			"remark":       gconv.String(v["remark"]),
+			"appellation":  gconv.String(v["appellation"]),
 			"phone":        phone,
 			"phone":        phone,
 			"base_user_id": baseUserId,
 			"base_user_id": baseUserId,
 			"update_time":  time.Now().Format(date.Date_Full_Layout),
 			"update_time":  time.Now().Format(date.Date_Full_Layout),
@@ -278,10 +279,10 @@ func AddChatRecord(robotCode string, data string) {
 					"update_time": time.Now().Format(date.Date_Full_Layout),
 					"update_time": time.Now().Format(date.Date_Full_Layout),
 				})
 				})
 				//发送一条 拒绝自动回复
 				//发送一条 拒绝自动回复
-				Chatserver.SpecifysystemMessage(robotCode, wxId, map[string]interface{}{
+				/*Chatserver.SpecifysystemMessage(robotCode, wxId, map[string]interface{}{
 					"content":      config.DbConf.ReplyAutomatically,
 					"content":      config.DbConf.ReplyAutomatically,
 					"content_type": 0,
 					"content_type": 0,
-				}, "reject")
+				}, "reject")*/
 			}
 			}
 
 
 		}
 		}
@@ -305,12 +306,14 @@ func InitUser() map[int64]map[string]interface{} {
 			baseUserId := gconv.Int64(v["base_user_id"])
 			baseUserId := gconv.Int64(v["base_user_id"])
 			wxId := gconv.String(v["wxid"])
 			wxId := gconv.String(v["wxid"])
 			robotCode := gconv.String(v["robotCode"])
 			robotCode := gconv.String(v["robotCode"])
+			appellation := gconv.String(v["appellation"])
 			if allUserMap[baseUserId] == nil {
 			if allUserMap[baseUserId] == nil {
 				allUserMap[baseUserId] = map[string]interface{}{
 				allUserMap[baseUserId] = map[string]interface{}{
-					"wxid":       wxId,
-					"userId":     robotCode,
-					"isRefuse":   gconv.Int64(v["is_refuse"]),
-					"baseUserId": gconv.Int64(baseUserId),
+					"wxid":        wxId,
+					"userId":      robotCode,
+					"isRefuse":    gconv.Int64(v["is_refuse"]),
+					"baseUserId":  gconv.Int64(baseUserId),
+					"appellation": appellation,
 				}
 				}
 			}
 			}
 		}
 		}