ws
CI / changes (push) Successful in 1m31s
CI / ad (push) Successful in 32s
CI / admin (push) Successful in 22s
CI / bff (push) Successful in 19s
CI / chore (push) Successful in 18s
CI / equipment (push) Successful in 19s
CI / express (push) Successful in 18s
CI / factory (push) Successful in 17s
CI / message (push) Successful in 18s
CI / order (push) Successful in 17s
CI / payment (push) Successful in 17s
CI / percutaneous (push) Successful in 17s
CI / product (push) Successful in 17s
CI / record (push) Successful in 9m26s
CI / sale (push) Successful in 45s
CI / support (push) Successful in 3m8s
CI / task (push) Successful in 27s
CI / user (push) Successful in 21s
CI / wecom (push) Successful in 19s

This commit is contained in:
2026-10-08 16:37:33 +08:00
parent 6b8500ac5e
commit 0d8cfc695c
14 changed files with 272 additions and 393 deletions
@@ -41,9 +41,10 @@ func (l *AddUserLogic) AddUser(in *percutaneous.AddUserRequest) (*percutaneous.R
}
w := modelbase.Params{Eq: map[string]string{
"host_code": in.HostCode,
"host_user_id": strconv.FormatInt(adminInfo.ID, utils.NumberTen),
"user_id": strconv.FormatInt(in.UserId, utils.NumberTen),
"host_user_id": strconv.FormatInt(in.UserId, utils.NumberTen),
"user_id": strconv.FormatInt(adminInfo.ID, utils.NumberTen),
}}
var info dao.Group
err := model.GroupModel{}.Init().GetOne(w, &info)
if err != nil {
@@ -61,5 +62,13 @@ func (l *AddUserLogic) AddUser(in *percutaneous.AddUserRequest) (*percutaneous.R
}
}
// 取出等待中的ws会话
sess := l.svcCtx.SessionMgr.Pop(in.HostCode)
if sess == nil {
l.Logger.Error("no waiting ws session code is = ", in.HostCode)
return l.ok(utils.NumberOne)
}
sess.MsgCh <- []byte(strconv.FormatInt(adminInfo.ID, utils.NumberTen))
return l.ok(utils.NumberOne)
}
@@ -3,6 +3,8 @@ package logic
import (
"context"
"lone-services/pkg/utils"
user "lone-services/rpc/user/pb"
"strconv"
"time"
"lone-services/rpc/percutaneous/pb"
@@ -13,12 +15,29 @@ import (
"google.golang.org/grpc/status"
)
type grpcStreamWrapper struct {
stream percutaneous.Percutaneous_WsAddUserStreamServer
}
func (w *grpcStreamWrapper) Send(payload []byte) error {
frame := &percutaneous.WsStreamRequest{
RawBody: payload,
}
return w.stream.Send(frame)
}
type WsAddUserStreamLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
type userInfo struct {
Id int64 `json:"id"`
Name string `json:"name"`
Avatar string `json:"avatar"`
}
func NewWsAddUserStreamLogic(ctx context.Context, svcCtx *svc.ServiceContext) *WsAddUserStreamLogic {
return &WsAddUserStreamLogic{
ctx: ctx,
@@ -29,50 +48,85 @@ func NewWsAddUserStreamLogic(ctx context.Context, svcCtx *svc.ServiceContext) *W
// 添加用户
func (l *WsAddUserStreamLogic) WsAddUserStream(stream percutaneous.Percutaneous_WsAddUserStreamServer) error {
l.Logger.Error("in coming ws tunnel stream")
l.Logger.Info("in coming ws tunnel stream")
ctx := stream.Context()
// 扫码超时30s
timeout := 30 * time.Second
timeout := time.Duration(utils.GetConfigInt("ws.out_time")) * time.Second
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
defer func() {
cancel()
}()
for {
// 先判断上下文是否已经结束
if err := ctx.Err(); err != nil {
if err == context.DeadlineExceeded {
return status.Errorf(codes.DeadlineExceeded, "扫码超时")
}
// BFF自动发来第一条消息,读取host_code
recvFrame, err := stream.Recv()
if err != nil {
l.Logger.Errorf("recv host_code failed: %v", err)
return err
}
var hostCode struct {
Code string `json:"host_code"`
}
l.Logger.Errorf(" host_code raw msg is: %v", string(recvFrame.RawBody))
// 保留你确认的 !err 判断
if ok := utils.JsonStringToStruct(string(recvFrame.RawBody), &hostCode); !ok {
return status.Errorf(codes.InvalidArgument, "device code parse error")
}
l.Logger.Infof("device connected, code=%s", hostCode.Code)
msgCh := make(chan []byte, 1)
wrapper := &grpcStreamWrapper{stream: stream}
sess := &utils.WaitingSession{
Stream: wrapper,
Ctx: ctx,
Cancel: cancel,
MsgCh: msgCh,
}
l.svcCtx.SessionMgr.Register(hostCode.Code, sess)
defer l.svcCtx.SessionMgr.Remove(hostCode.Code)
l.Logger.Info("session register success, waiting for scan event")
select {
case <-ctx.Done():
if ctx.Err() == context.DeadlineExceeded {
return status.Errorf(codes.DeadlineExceeded, "扫码等待超时")
}
return ctx.Err()
case payload := <-msgCh:
id, _ := strconv.Atoi(string(payload))
info := l.getUserInfo(int64(id))
jsonData := utils.StructToJson(info)
if err := sess.Stream.Send([]byte(jsonData)); err != nil {
l.Logger.Errorf("send to device err: %v", err)
return err
}
// 阻塞等待客户端上报扫码消息
recvFrame, err := stream.Recv()
if err != nil {
// 客户端主动关闭流
return err
}
// 解析扫码payload
type res struct {
Id int64 `json:"id"`
Name string `json:"name"`
}
var resData res
if err := utils.JsonStringToStruct(string(recvFrame.RawBody), &resData); !err {
l.Logger.Errorf("parse scan payload failed: %v", err)
return status.Errorf(codes.InvalidArgument, "解析扫码数据失败")
}
l.Logger.Errorf("receive scan success data: %+v", resData)
// 下发结果给ws客户端(同步设备)
sendFrame := &percutaneous.WsStreamRequest{
RawBody: []byte(utils.StructToJson(resData)),
}
if err := stream.Send(sendFrame); err != nil {
l.Logger.Errorf("send result frame failed: %v", err)
return err
}
// 收发完成,直接关闭grpc双向流(ws连接随之断开)
return nil
}
}
func (l *WsAddUserStreamLogic) getUserInfo(id int64) *userInfo {
if id == utils.NumberZero {
return nil
}
logx.Errorf("get user id: %v", id)
cli, err := svc.GetRpcClient(l.svcCtx.UserName)
if err != nil {
logx.Errorf("get user err: %v", err)
return nil
}
client := user.NewUserClient(cli.Conn())
data := user.InternalGetUserInfoReq{
UserId: id,
}
res, err := client.InternalGetUserInfo(context.Background(), &data)
if err != nil {
logx.Errorf("get user err: %v", err)
return nil
}
return &userInfo{
Id: res.Id,
Name: res.Name,
Avatar: res.Avatar,
}
}