完善角色权限与数据隔离

This commit is contained in:
yml2213
2026-07-26 14:00:50 +08:00
parent b483cbc26d
commit 16d9ab0e5e
38 changed files with 2847 additions and 292 deletions
+43 -19
View File
@@ -84,7 +84,7 @@ func (h *SessionHandler) SendMessage(c *gin.Context) {
c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "发送失败"})
return
}
if middleware.GetRole(c) == "agent" {
if !middleware.CanAccessAllData(c, "session") {
model.DB.Model(&model.Session{}).Where("id = ?", session.ID).Update("last_read_seq", msg.Seq)
session.LastReadSeq = msg.Seq
}
@@ -104,7 +104,7 @@ type AssignSessionReq struct {
}
func isTenantManager(c *gin.Context) bool {
return middleware.HasAnyRole(c, "admin", "supervisor")
return middleware.CanAccessAllData(c, "session")
}
func loadTenantSession(c *gin.Context, id string) (*model.Session, bool) {
@@ -121,21 +121,22 @@ func loadTenantSession(c *gin.Context, id string) (*model.Session, bool) {
}
func canReadSession(c *gin.Context, session *model.Session) bool {
if isTenantManager(c) {
module := "session"
if session.Status == "ended" || session.Status == "archived" {
module = "chat_history"
}
if middleware.CanAccessAllData(c, module) {
return true
}
if middleware.GetRole(c) == "agent" {
userID := middleware.GetUserID(c)
return session.Status == "waiting" || (session.AgentID != nil && *session.AgentID == userID)
}
return false
userID := middleware.GetUserID(c)
return session.Status == "waiting" || (session.AgentID != nil && *session.AgentID == userID)
}
func canOperateSession(c *gin.Context, session *model.Session) bool {
if isTenantManager(c) {
return true
}
return middleware.GetRole(c) == "agent" && session.AgentID != nil && *session.AgentID == middleware.GetUserID(c)
return session.AgentID != nil && *session.AgentID == middleware.GetUserID(c)
}
func broadcastSessionMessage(session *model.Session, message model.Message) {
@@ -160,12 +161,34 @@ func loadAssignableAgent(tenantID, agentID uint) error {
if err := model.DB.First(&agent, agentID).Error; err != nil {
return err
}
if agent.TenantID != tenantID || agent.Role != "agent" || agent.Status == "disabled" {
if agent.TenantID != tenantID || agent.Status == "disabled" {
return gorm.ErrRecordNotFound
}
if agent.Role == "agent" {
return nil
}
var count int64
if err := model.DB.Table("roles r").
Joins("JOIN role_permissions rp ON rp.role_id = r.id").
Joins("JOIN permissions p ON p.id = rp.permission_id").
Where("r.tenant_id = ? AND r.code = ? AND r.type = ? AND p.code = ?", tenantID, agent.Role, "custom", "session.reply").
Count(&count).Error; err != nil || count == 0 {
return gorm.ErrRecordNotFound
}
return nil
}
func assignableRoleCodes(tenantID uint) []string {
codes := []string{"agent"}
var customCodes []string
model.DB.Table("roles r").
Joins("JOIN role_permissions rp ON rp.role_id = r.id").
Joins("JOIN permissions p ON p.id = rp.permission_id").
Where("r.tenant_id = ? AND r.type = ? AND p.code = ?", tenantID, "custom", "session.reply").
Distinct().Pluck("r.code", &customCodes)
return append(codes, customCodes...)
}
func unreadCount(session model.Session) int {
var count int64
model.DB.Model(&model.Message{}).
@@ -189,7 +212,7 @@ func (h *SessionHandler) List(c *gin.Context) {
var total int64
query := model.DB.Model(&model.Session{}).Where("sessions.tenant_id = ?", tenantID)
if middleware.GetRole(c) == "agent" {
if !middleware.CanAccessAllData(c, "session") || !middleware.CanAccessAllData(c, "chat_history") {
query = query.Where("sessions.agent_id = ? OR sessions.status = ?", middleware.GetUserID(c), "waiting")
}
if status != "" {
@@ -283,7 +306,7 @@ func (h *SessionHandler) List(c *gin.Context) {
items := make([]SessionListItem, 0, len(sessions))
for _, session := range sessions {
item := SessionListItem{Session: session, MessageCount: msgCountMap[session.ID]}
if middleware.GetRole(c) == "agent" && session.AgentID != nil && *session.AgentID == middleware.GetUserID(c) {
if !middleware.CanAccessAllData(c, "session") && session.AgentID != nil && *session.AgentID == middleware.GetUserID(c) {
item.UnreadCount = unreadCount(session)
}
if cu, ok := customerMap[session.CustomerID]; ok {
@@ -444,7 +467,7 @@ func (h *SessionHandler) MarkRead(c *gin.Context) {
c.JSON(http.StatusForbidden, gin.H{"code": 403, "message": "无权标记该会话已读"})
return
}
if session.AgentID == nil || middleware.GetRole(c) != "agent" || *session.AgentID != middleware.GetUserID(c) {
if session.AgentID == nil || middleware.CanAccessAllData(c, "session") || *session.AgentID != middleware.GetUserID(c) {
middleware.JSON(c, gin.H{"last_read_seq": session.LastReadSeq})
return
}
@@ -496,9 +519,10 @@ func (h *SessionHandler) ListAvailableAgents(c *gin.Context) {
}
// all=1 返回租户全部坐席(含离线),用于对话记录筛选;
// 默认:可转接坐席(role=agent,在线/忙碌),便于前端展示状态
q := model.DB.Where("tenant_id = ? AND role IN ?", middleware.GetTenantID(c), []string{"agent", "supervisor", "admin"})
tenantID := middleware.GetTenantID(c)
q := model.DB.Where("tenant_id = ? AND role IN ?", tenantID, assignableRoleCodes(tenantID))
if c.Query("all") != "1" {
q = q.Where("role = ? AND status IN ?", "agent", []string{"online", "busy"})
q = q.Where("status IN ?", []string{"online", "busy"})
}
var users []model.User
if err := q.Order("nickname asc").Find(&users).Error; err != nil {
@@ -584,9 +608,9 @@ func (h *SessionHandler) Assign(c *gin.Context) {
c.JSON(http.StatusConflict, gin.H{"code": 409, "message": "会话已被分配"})
return
}
if middleware.GetRole(c) == "agent" {
if !middleware.HasPermission(c, "session.transfer") {
req.AgentID = middleware.GetUserID(c)
} else if !isTenantManager(c) {
} else if !isTenantManager(c) && req.AgentID != middleware.GetUserID(c) {
c.JSON(http.StatusForbidden, gin.H{"code": 403, "message": "无权分配会话"})
return
}
@@ -784,7 +808,7 @@ func (h *SessionHandler) UpdatePriority(c *gin.Context) {
// Archive 将已结束会话归档(主管/管理员)
func (h *SessionHandler) Archive(c *gin.Context) {
if !isTenantManager(c) {
if !middleware.HasPermission(c, "chat_history.batch_archive") {
c.JSON(http.StatusForbidden, gin.H{"code": 403, "message": "仅主管或管理员可归档"})
return
}
@@ -816,7 +840,7 @@ func (h *SessionHandler) Archive(c *gin.Context) {
// BatchArchive 批量归档已结束会话
func (h *SessionHandler) BatchArchive(c *gin.Context) {
if !isTenantManager(c) {
if !middleware.HasPermission(c, "chat_history.batch_archive") {
c.JSON(http.StatusForbidden, gin.H{"code": 403, "message": "仅主管或管理员可归档"})
return
}