鉴权 & 白名单
使用 group 加上 Jwt Parse 实现 Check
|
|
同样的 我们在 Websocket 那边也是使用 JWT 进行鉴权
|
|
用户模块
支持 Login、Info、Create、Edit、Delete、List、UpdatePassword
这一部分基本上都是 CRUD 并且没有做并发控制。
|
|
表结构设计
|
|
Department
-
Soa方法 会根据所有的部门构建一个部门树进行展示- 全量查出所有部门
- 按 ParentPath 分组
- 否则按照父路径放进 groupDep
|
|
其他的 设计基本上都是 CRUD
|
|
表结构设计
设计一个 部门数据表 一个部门用户表
|
|
|
|
Todo
基本上也都是CRUD接口
|
|
表结构设计

|
|
审批
基本上也是CRUD
|
|
表结构设计
一张超级无敌巨宽的表

|
|
Chat
AiHelper WebSocket 聊天技术总结
结论:聊天能力集中在后端独立 WS 服务(gorilla/websocket),与 Gin REST 共用 JWT;front/ 为空,无正式客户端。仓库内无架构图,设计意图主要体现在代码注释与 Chat_API_测试指南.md。
1. 整体架构
进程内双服务并行启动(共享同一 svc.ServiceContext):
| 服务 | 框架 | 默认地址 | 职责 |
|---|---|---|---|
| REST API | Gin | 0.0.0.0:8888 |
登录、用户管理等 |
| WebSocket | net/http + gorilla |
0.0.0.0:9000,路径 /ws |
实时私聊/群聊 |
|
|
入口:/Users/gzm/gopath/src/AiHelper/backend/main.go
api.NewHandle(svcContext).Run()ws.NewWs(svcContext).Run()
依赖:github.com/gorilla/websocket v1.5.3(无 nhooyr、无 gin-websocket)
2. 连接生命周期
核心文件:/Users/gzm/gopath/src/AiHelper/backend/internal/handler/ws/ws.go
| 阶段 | 函数 | 行为 |
|---|---|---|
| 启动 | Ws.Run |
http.HandleFunc("/ws", ServerWs) + ListenAndServe |
| 握手前鉴权 | Ws.auth |
读 Header websocket,JWT 解析取 uid |
| 升级 | ServerWs → Upgrader.Upgrade |
响应头回写 websocket: <token> |
| 登记连接 | addConn |
写入双向 map;同 uid 旧连接先 Close(单点在线) |
| 读循环 | handleConn(goroutine) |
ReadMessage → JSON → 按 chatType 分发 |
| 发送 | send / sendByUids |
TextMessage + JSON |
| 断开 | closeConn |
读失败或处理失败时清理 map 并关闭 |
要点:
CheckOrigin恒为true(注释写明生产应收紧)- 鉴权失败只打日志并 return,不写 HTTP 错误体
- JSON 解析失败或业务错误会
return结束循环,但不一定走closeConn(潜在连接泄漏) - 无 ping/pong、心跳、重连协议
3. 消息协议
领域模型:domain.Message(/Users/gzm/gopath/src/AiHelper/backend/internal/domain/ws.go)
|
|
| 字段 | 说明 |
|---|---|
conversationId |
群聊强制 "all";私聊空则服务端生成 |
recvId |
私聊必填;群聊可空 |
sendId |
服务端强制覆盖为 JWT uid,防伪造 |
chatType |
见下表(以代码为准) |
content / contentType |
内容;contentType 注释为 1 文字 / 2 图片 / 3 表情等,未入库 |
model.ChatType(chatlogtypes.go):
| 值 | 常量 | 含义 |
|---|---|---|
| 1 | GroupChatType |
群聊 |
| 2 | SingleChatType |
私聊 |
分发(handleConn):
SingleChatType→privateChat→logic.Chat.PrivateChat→sendByUids(recvId)GroupChatType→groupChat→logic.Chat.GroupChat→sendByUids()(全员广播)
注意:backend/doc/Chat_API_测试指南.md 把私聊写成 chatType:1、群聊写成 2,与代码相反;以 domain/model 注释为准。
无独立事件类型字段(无 event/type/ack);协议即「一条 JSON = 一条聊天消息」。
4. Auth / Session 与 REST 关系
登录(REST)
POST /v1/user/login→User.Login→logic.user.Logintoken.GetJwtToken(secret, iat, expire, user.ID.Hex())- Claims:
exp/iat+ 自定义键aihelper(常量token.Identify)= uid - 响应:
LoginResp.AccessToken(JSON 字段名token)
REST 鉴权
- Header:
Authorization: Bearer <JWT> - 中间件:
middleware.Jwt.Handler→token.Parse.ParseWithContext
WS 鉴权
- Header:
websocket: <JWT>(非 Bearer) Ws.auth→ 同一token.Parse+ 同一Jwt.Secret- 连接期把 uid/token 放入
context(Ws.context),供业务/日志使用
会话模型:
- 无独立 chat room / WS session 表
- 在线态 = 进程内
uidToConn/ConnToUid - 会话 ID:
- 群聊:
"all"(全局大厅) - 私聊:
GenerateUniqueID(sendId, recvId)= 排序后拼接 → SHA256 → Base64Raw 取前 22 位(顺序无关、稳定)
- 群聊:
持久化:ChatLogModel.Insert → MongoDB chat_log;无按会话拉历史的 REST/WS API(模型仅有 Insert/FindOne/Update/Delete)。
5. 关键组件与数据流
关键文件
| 路径 | 角色 |
|---|---|
backend/main.go |
API + WS 双 goroutine 启动 |
backend/internal/handler/ws/ws.go |
连接管理、鉴权、读写循环 |
backend/internal/handler/ws/conversation.go |
privateChat / groupChat 推送 |
backend/internal/domain/ws.go |
Message 协议结构 |
backend/internal/logic/chat.go |
落库、GenerateUniqueID |
backend/internal/model/chatlogtypes.go |
ChatLog、ChatType |
backend/internal/model/chatlogmodel.go |
MongoDAO |
backend/pkg/token/{token,ctxtoken}.go |
JWT 签发/解析 |
backend/internal/middleware/jwt.go |
REST JWT |
backend/internal/logic/user.go |
登录发 Token |
backend/internal/config/config.go + etc/*/api.yaml |
Ws.Addr、Jwt.* |
backend/doc/Chat_API_测试指南.md |
手工联调文档(部分与代码不一致) |
backend/internal/logic/chat_test.go |
仅测 GenerateUniqueID |
客户端
front/目录为空- 文档用
wscat -c ws://localhost:9000/ws -H "websocket:{token}"
数据流(私聊)
- A 发 JSON(
recvId=B,chatType=2) handleConn设SendId=APrivateChat→ 必要时生成conversationId→InsertsendByUids(ctx, msg, B)→ 仅 B 在线则推送- 发送者 A 收不到自己消息的 echo(代码注释已说明)
数据流(群聊)
chatType=1→ConversationId="all"→ 落库sendByUids无 uid → 广播所有在线用户(含发送者)- 文档写「除发送者外」与代码不符;也不是真实群组房间,而是全站在线广播
6. 重要设计决策
- API/WS 端口分离:Gin 与裸
net/http各占端口,共享 Mongo/JWT 配置 - 共享 ServiceContext:避免双实例初始化竞态(
main.go注释) - Header 传 Token:自定义
websocket头,规避部分 WS 客户端难带Authorization的问题 - 服务端信任边界:
sendId只认 JWT,客户端不可伪造 - 单连接/用户:新连踢旧连
- 轻量会话:无私聊房间实体;群聊固定
"all" - 先存后推:落库成功再推送;接收方离线则只落库、不推
- 内存连接表:单机、无 Redis;水平扩展需改造
- 协议极简:无 ACK、回执、已读、错误帧、心跳
- 安全简化:全放行 Origin;鉴权失败无标准 HTTP 状态
7. 配置与联调
本地配置示例(backend/etc/local/api.yaml):
- API:
0.0.0.0:8888 - WS:
0.0.0.0:9000 - JWT:
Secret+Expire
推荐联调顺序(与文档一致、chatType 按代码):
POST /v1/user/login取token与idwscat带websocket头连/ws- 私聊:
chatType: 2+recvId - 群聊:
chatType: 1
8. 缺口与风险(实现现状)
- 无前端 WS 客户端
- 无聊天历史查询 API
contentType未持久化- 文档
chatType、群聊是否回显发送者与代码不一致 - 处理错误路径可能未清理连接
- 群聊非真实群组,而是全局广播
- 无多实例在线路由能力