refactor(auth): delegate access control to be-system
Remove Agent Desk users, roles, login sessions, tokens, and local permission persistence. Expose the backend as an embeddable ai-agent module with host-provided subject lookup and operation authorization callbacks, and complete the frontend/backend repository split.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"agent-desk/internal/models"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/models"
|
||||
|
||||
"github.com/mlogclub/codegen"
|
||||
)
|
||||
@@ -19,19 +19,10 @@ func main() {
|
||||
WebEdit: false,
|
||||
},
|
||||
codegen.GetGenerateStruct(&models.Migration{}),
|
||||
codegen.GetGenerateStruct(&models.User{}),
|
||||
codegen.GetGenerateStruct(&models.UserIdentity{}),
|
||||
codegen.GetGenerateStruct(&models.Company{}),
|
||||
codegen.GetGenerateStruct(&models.Customer{}),
|
||||
codegen.GetGenerateStruct(&models.CustomerIdentity{}),
|
||||
codegen.GetGenerateStruct(&models.CustomerContact{}),
|
||||
codegen.GetGenerateStruct(&models.Role{}),
|
||||
codegen.GetGenerateStruct(&models.Permission{}),
|
||||
codegen.GetGenerateStruct(&models.UserRole{}),
|
||||
codegen.GetGenerateStruct(&models.RolePermission{}),
|
||||
codegen.GetGenerateStruct(&models.UserPermission{}),
|
||||
codegen.GetGenerateStruct(&models.LoginSession{}),
|
||||
codegen.GetGenerateStruct(&models.LoginCredentialLog{}),
|
||||
codegen.GetGenerateStruct(&models.Asset{}),
|
||||
codegen.GetGenerateStruct(&models.Tag{}),
|
||||
codegen.GetGenerateStruct(&models.Conversation{}),
|
||||
|
||||
@@ -3,9 +3,9 @@ package main
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"agent-desk/internal/bootstrap"
|
||||
"agent-desk/internal/pkg/config"
|
||||
"agent-desk/internal/pkg/logx"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/bootstrap"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/config"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/logx"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
+2
-2
@@ -4,8 +4,8 @@ import (
|
||||
"flag"
|
||||
"log/slog"
|
||||
|
||||
"agent-desk/internal/bootstrap"
|
||||
"agent-desk/internal/pkg/config"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/bootstrap"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/config"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
Vendored
-200
@@ -1,200 +0,0 @@
|
||||
package agentteam
|
||||
|
||||
import (
|
||||
"agent-desk/cmd/testdata/seedlang"
|
||||
"agent-desk/cmd/testdata/seeds"
|
||||
"agent-desk/internal/models"
|
||||
"agent-desk/internal/pkg/constants"
|
||||
"agent-desk/internal/pkg/enums"
|
||||
"agent-desk/internal/repositories"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/mlogclub/simple/sqls"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type InitResult struct {
|
||||
TeamCreated bool
|
||||
UsersCreated int
|
||||
ProfilesCreated int
|
||||
UpdatesApplied int
|
||||
}
|
||||
|
||||
// Init 初始化客服组和客服用户
|
||||
// 创建:
|
||||
// 1. 客服组,组长为管理员用户
|
||||
// 2. 客服A 用户
|
||||
// 3. 客服B 用户
|
||||
// 4. 为客服A和客服B创建客服档案,关联到该客服组
|
||||
func Init(lang seedlang.Language) (*InitResult, error) {
|
||||
result := &InitResult{}
|
||||
|
||||
// 获取管理员用户
|
||||
adminUser := repositories.UserRepository.Take(
|
||||
sqls.DB(),
|
||||
"username = ?",
|
||||
constants.BootstrapAdminUsername,
|
||||
)
|
||||
if adminUser == nil {
|
||||
return result, fmt.Errorf("bootstrap admin user not found")
|
||||
}
|
||||
|
||||
err := sqls.WithTransaction(func(ctx *sqls.TxContext) error {
|
||||
return initTeamAndUsers(ctx, adminUser, result, lang)
|
||||
})
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("init team and users failed: %w", err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func initTeamAndUsers(ctx *sqls.TxContext, leaderUser *models.User, result *InitResult, lang seedlang.Language) error {
|
||||
teamName := seeds.AgentTeamName(lang)
|
||||
now := time.Now()
|
||||
|
||||
team := repositories.AgentTeamRepository.Take(ctx.Tx, "name = ?", teamName)
|
||||
if team != nil {
|
||||
if err := ctx.Tx.Model(team).Updates(map[string]any{
|
||||
"leader_user_id": leaderUser.ID,
|
||||
"update_user_id": constants.SystemAuditUserID,
|
||||
"update_user_name": constants.SystemAuditUserName,
|
||||
"updated_at": now,
|
||||
}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
team = &models.AgentTeam{
|
||||
Name: teamName,
|
||||
LeaderUserID: leaderUser.ID,
|
||||
Status: enums.StatusOk,
|
||||
Description: "Local testdata seed - default service team",
|
||||
AuditFields: models.AuditFields{
|
||||
CreatedAt: now,
|
||||
CreateUserID: constants.SystemAuditUserID,
|
||||
CreateUserName: constants.SystemAuditUserName,
|
||||
UpdatedAt: now,
|
||||
UpdateUserID: constants.SystemAuditUserID,
|
||||
UpdateUserName: constants.SystemAuditUserName,
|
||||
},
|
||||
}
|
||||
if err := ctx.Tx.Create(team).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
result.TeamCreated = true
|
||||
}
|
||||
|
||||
agentUsers := seeds.AgentUsers(lang, leaderUser.Username)
|
||||
for _, agentUser := range agentUsers {
|
||||
userID, userCreated, err := createOrGetUser(ctx, agentUser.Username, agentUser.Nickname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if userCreated {
|
||||
result.UsersCreated++
|
||||
}
|
||||
|
||||
// 创建或更新客服档案
|
||||
profileCreated, err := createOrUpdateProfile(ctx, userID, team.ID, agentUser.Code, agentUser.Nickname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if profileCreated {
|
||||
result.ProfilesCreated++
|
||||
} else {
|
||||
result.UpdatesApplied++
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createOrGetUser(ctx *sqls.TxContext, username, nickname string) (int64, bool, error) {
|
||||
user := repositories.UserRepository.Take(
|
||||
ctx.Tx,
|
||||
"username = ?",
|
||||
username,
|
||||
)
|
||||
|
||||
if user != nil {
|
||||
return user.ID, false, nil
|
||||
}
|
||||
|
||||
// 创建新用户
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(constants.BootstrapAdminPassword), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return 0, false, err
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
newUser := &models.User{
|
||||
Username: username,
|
||||
Nickname: nickname,
|
||||
Password: string(hashedPassword),
|
||||
Status: enums.StatusOk,
|
||||
Remark: "Local testdata seed",
|
||||
AuditFields: models.AuditFields{
|
||||
CreatedAt: now,
|
||||
CreateUserID: constants.SystemAuditUserID,
|
||||
CreateUserName: constants.SystemAuditUserName,
|
||||
UpdatedAt: now,
|
||||
UpdateUserID: constants.SystemAuditUserID,
|
||||
UpdateUserName: constants.SystemAuditUserName,
|
||||
},
|
||||
}
|
||||
|
||||
if err := ctx.Tx.Create(newUser).Error; err != nil {
|
||||
return 0, false, err
|
||||
}
|
||||
|
||||
return newUser.ID, true, nil
|
||||
}
|
||||
|
||||
func createOrUpdateProfile(ctx *sqls.TxContext, userID, teamID int64, agentCode, displayName string) (bool, error) {
|
||||
profile := repositories.AgentProfileRepository.Take(
|
||||
ctx.Tx,
|
||||
"user_id = ?",
|
||||
userID,
|
||||
)
|
||||
|
||||
now := time.Now()
|
||||
|
||||
if profile != nil {
|
||||
// 档案已存在,更新关键信息
|
||||
return false, ctx.Tx.Model(profile).Updates(map[string]any{
|
||||
"team_id": teamID,
|
||||
"agent_code": agentCode,
|
||||
"display_name": displayName,
|
||||
"status": enums.StatusOk,
|
||||
"update_user_id": constants.SystemAuditUserID,
|
||||
"update_user_name": constants.SystemAuditUserName,
|
||||
"updated_at": now,
|
||||
}).Error
|
||||
}
|
||||
|
||||
// 创建新档案
|
||||
newProfile := &models.AgentProfile{
|
||||
UserID: userID,
|
||||
TeamID: teamID,
|
||||
AgentCode: agentCode,
|
||||
DisplayName: displayName,
|
||||
Avatar: "",
|
||||
ServiceStatus: enums.ServiceStatusIdle,
|
||||
MaxConcurrentCount: 5,
|
||||
PriorityLevel: 10,
|
||||
AutoAssignEnabled: true,
|
||||
Status: enums.StatusOk,
|
||||
Remark: "Local testdata seed",
|
||||
AuditFields: models.AuditFields{
|
||||
CreatedAt: now,
|
||||
CreateUserID: constants.SystemAuditUserID,
|
||||
CreateUserName: constants.SystemAuditUserName,
|
||||
UpdatedAt: now,
|
||||
UpdateUserID: constants.SystemAuditUserID,
|
||||
UpdateUserName: constants.SystemAuditUserName,
|
||||
},
|
||||
}
|
||||
|
||||
return true, ctx.Tx.Create(newProfile).Error
|
||||
}
|
||||
Vendored
-23
@@ -1,23 +0,0 @@
|
||||
package agentteam
|
||||
|
||||
import (
|
||||
"agent-desk/cmd/testdata/seedlang"
|
||||
"agent-desk/cmd/testdata/seeds"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var hanTextPattern = regexp.MustCompile(`\p{Han}`)
|
||||
|
||||
func TestEnglishAgentTeamTextDoesNotContainChineseText(t *testing.T) {
|
||||
values := []string{seeds.AgentTeamName(seedlang.English)}
|
||||
for _, user := range seeds.AgentUsers(seedlang.English, "admin") {
|
||||
values = append(values, user.Nickname)
|
||||
}
|
||||
|
||||
for _, value := range values {
|
||||
if hanTextPattern.MatchString(value) {
|
||||
t.Fatalf("english agent team seed contains Chinese text: %q", value)
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+6
-6
@@ -1,12 +1,12 @@
|
||||
package aiagent
|
||||
|
||||
import (
|
||||
"agent-desk/cmd/testdata/seedlang"
|
||||
"agent-desk/cmd/testdata/seeds"
|
||||
"agent-desk/internal/models"
|
||||
"agent-desk/internal/pkg/enums"
|
||||
"agent-desk/internal/pkg/utils"
|
||||
"agent-desk/internal/repositories"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seedlang"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seeds"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/models"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/utils"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/repositories"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
Vendored
+2
-2
@@ -1,8 +1,8 @@
|
||||
package aiagent
|
||||
|
||||
import (
|
||||
"agent-desk/cmd/testdata/seedlang"
|
||||
"agent-desk/cmd/testdata/seeds"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seedlang"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seeds"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
Vendored
+4
-4
@@ -6,10 +6,10 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"agent-desk/internal/models"
|
||||
"agent-desk/internal/pkg/constants"
|
||||
"agent-desk/internal/pkg/enums"
|
||||
"agent-desk/internal/repositories"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/models"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/constants"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/repositories"
|
||||
|
||||
"github.com/mlogclub/simple/sqls"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
Vendored
+5
-5
@@ -1,11 +1,11 @@
|
||||
package channel
|
||||
|
||||
import (
|
||||
"agent-desk/cmd/testdata/seedlang"
|
||||
"agent-desk/cmd/testdata/seeds"
|
||||
"agent-desk/internal/models"
|
||||
"agent-desk/internal/pkg/enums"
|
||||
"agent-desk/internal/repositories"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seedlang"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seeds"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/models"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/repositories"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
|
||||
Vendored
+2
-2
@@ -1,8 +1,8 @@
|
||||
package channel
|
||||
|
||||
import (
|
||||
"agent-desk/cmd/testdata/seedlang"
|
||||
"agent-desk/cmd/testdata/seeds"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seedlang"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seeds"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
Vendored
+6
-6
@@ -1,12 +1,12 @@
|
||||
package kb
|
||||
|
||||
import (
|
||||
"agent-desk/cmd/testdata/seedlang"
|
||||
"agent-desk/cmd/testdata/seeds"
|
||||
"agent-desk/internal/models"
|
||||
"agent-desk/internal/pkg/constants"
|
||||
"agent-desk/internal/pkg/enums"
|
||||
"agent-desk/internal/repositories"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seedlang"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seeds"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/models"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/constants"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/repositories"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
|
||||
Vendored
+2
-2
@@ -1,8 +1,8 @@
|
||||
package kb
|
||||
|
||||
import (
|
||||
"agent-desk/cmd/testdata/seedlang"
|
||||
"agent-desk/cmd/testdata/seeds"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seedlang"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seeds"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
Vendored
+10
-21
@@ -1,17 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"agent-desk/cmd/testdata/agentteam"
|
||||
"agent-desk/cmd/testdata/aiagent"
|
||||
"agent-desk/cmd/testdata/aiconfig"
|
||||
"agent-desk/cmd/testdata/channel"
|
||||
"agent-desk/cmd/testdata/kb"
|
||||
"agent-desk/cmd/testdata/quickreply"
|
||||
"agent-desk/cmd/testdata/seedlang"
|
||||
"agent-desk/cmd/testdata/skill"
|
||||
"agent-desk/cmd/testdata/tag"
|
||||
"agent-desk/internal/bootstrap"
|
||||
"agent-desk/internal/pkg/config"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/aiagent"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/aiconfig"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/channel"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/kb"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/quickreply"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seedlang"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/skill"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/tag"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/bootstrap"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/config"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
@@ -94,16 +93,6 @@ func run() error {
|
||||
}
|
||||
slog.Info("skill init success", slog.Int("created", skillResult.Created), slog.Int("updated", skillResult.Updated))
|
||||
|
||||
agentTeamResult, err := agentteam.Init(lang)
|
||||
if err != nil {
|
||||
return fmt.Errorf("init agent team failed: %w", err)
|
||||
}
|
||||
slog.Info("agent team init success", slog.Bool("teamCreated", agentTeamResult.TeamCreated),
|
||||
slog.Int("usersCreated", agentTeamResult.UsersCreated),
|
||||
slog.Int("profilesCreated", agentTeamResult.ProfilesCreated),
|
||||
slog.Int("updatesApplied", agentTeamResult.UpdatesApplied),
|
||||
)
|
||||
|
||||
aiAgentResult, err := aiagent.Init(lang)
|
||||
if err != nil {
|
||||
return fmt.Errorf("init ai agent failed: %w", err)
|
||||
|
||||
Vendored
+4
-4
@@ -1,10 +1,10 @@
|
||||
package quickreply
|
||||
|
||||
import (
|
||||
"agent-desk/cmd/testdata/seedlang"
|
||||
"agent-desk/cmd/testdata/seeds"
|
||||
"agent-desk/internal/models"
|
||||
"agent-desk/internal/repositories"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seedlang"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seeds"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/models"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/repositories"
|
||||
"time"
|
||||
|
||||
"github.com/mlogclub/simple/sqls"
|
||||
|
||||
Vendored
+2
-2
@@ -1,8 +1,8 @@
|
||||
package quickreply
|
||||
|
||||
import (
|
||||
"agent-desk/cmd/testdata/seedlang"
|
||||
"agent-desk/cmd/testdata/seeds"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seedlang"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seeds"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
package seeds
|
||||
|
||||
import "agent-desk/cmd/testdata/seedlang"
|
||||
import "code.tczkiot.com/wlw/ai-agent/cmd/testdata/seedlang"
|
||||
|
||||
type AgentUserSeed struct {
|
||||
Username string
|
||||
|
||||
Vendored
+2
-2
@@ -1,8 +1,8 @@
|
||||
package seeds
|
||||
|
||||
import (
|
||||
"agent-desk/cmd/testdata/seedlang"
|
||||
"agent-desk/internal/pkg/enums"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seedlang"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
)
|
||||
|
||||
type AIAgentSeed struct {
|
||||
|
||||
Vendored
+3
-3
@@ -1,9 +1,9 @@
|
||||
package seeds
|
||||
|
||||
import (
|
||||
"agent-desk/cmd/testdata/seedlang"
|
||||
"agent-desk/internal/pkg/dto"
|
||||
"agent-desk/internal/pkg/enums"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seedlang"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/dto"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
|
||||
"github.com/mlogclub/simple/common/jsons"
|
||||
)
|
||||
|
||||
Vendored
+1
-2
@@ -1,6 +1,6 @@
|
||||
package seeds
|
||||
|
||||
import "agent-desk/cmd/testdata/seedlang"
|
||||
import "code.tczkiot.com/wlw/ai-agent/cmd/testdata/seedlang"
|
||||
|
||||
type KnowledgeBaseSeed struct {
|
||||
Name string
|
||||
@@ -83,7 +83,6 @@ func chineseKnowledgeFAQSeeds() []KnowledgeFAQSeed {
|
||||
{Question: "新成员加入后如何开通后台账号?", Answer: "企业管理员进入“组织设置-成员管理”,点击“新增成员”,填写姓名、邮箱、所属团队和角色后保存。系统会自动发送激活邮件,成员首次登录时设置密码即可。若你们开通了单点登录,也可以直接从企业身份系统同步成员。", SimilarQuestions: []string{"怎么给新客服开账号", "新增员工账号在哪里", "成员怎么加入后台"}, Remark: "成员管理"},
|
||||
{Question: "成员离职后如何停用账号?", Answer: "请在“组织设置-成员管理”中找到对应成员,点击“停用”即可。停用后该账号无法继续登录,但历史会话、工单处理记录和质检数据会保留,不会影响报表统计。若后续确认不再使用,也可以在完成交接后删除账号。", SimilarQuestions: []string{"离职员工账号怎么处理", "怎么禁用成员账号", "停用客服账号"}, Remark: "成员管理"},
|
||||
{Question: "角色权限修改后多久生效?", Answer: "角色权限保存后通常即时生效。已在线的成员可能需要刷新页面或重新登录,才能拿到最新权限菜单和接口授权。如果修改后仍能访问原页面,请清理浏览器缓存后再试。", SimilarQuestions: []string{"权限修改什么时候生效", "调整角色后没变化", "角色更新后要重登吗"}, Remark: "成员管理"},
|
||||
{Question: "支持企业微信或钉钉单点登录吗?", Answer: "支持对接企业微信、钉钉和标准 SAML/OIDC 单点登录。开通后成员可通过企业身份系统直接登录平台,无需单独维护密码。具体配置需要管理员在“安全设置-单点登录”中填写回调地址、应用凭证并完成测试。", SimilarQuestions: []string{"能接企业微信登录吗", "支持钉钉 SSO 吗", "单点登录怎么接"}, Remark: "成员管理"},
|
||||
{Question: "坐席在线、忙碌、离线状态有什么区别?", Answer: "在线表示可正常接待新会话,忙碌表示当前暂不分配新会话但仍可处理已有会话,离线表示不参与会话分配也不接收实时提醒。若开启自动状态切换,长时间无操作或退出登录后,系统会自动变更为离线。", SimilarQuestions: []string{"客服状态怎么理解", "在线忙碌离线区别", "坐席状态说明"}, Remark: "坐席接待"},
|
||||
{Question: "会话是怎么分配给坐席的?", Answer: "默认按技能组和轮询策略分配,也可结合坐席当前负载、最近响应时长和优先级规则进行智能分流。若客户命中了指定渠道、语言或标签条件,系统会优先路由到匹配该条件的团队或坐席。", SimilarQuestions: []string{"客户咨询怎么分配", "会话路由规则是什么", "新会话按什么分给客服"}, Remark: "坐席接待"},
|
||||
{Question: "如何把会话转接给其他团队?", Answer: "在会话详情页点击“转接”,选择目标团队或指定坐席,并填写转接备注即可。转接后,原坐席仍可在历史记录中查看会话内容,但新消息会优先提醒接收方。若目标团队离线人数过多,建议先确认有人值班。", SimilarQuestions: []string{"会话怎么转给别的组", "转接客服在哪里", "咨询如何分配给其他团队"}, Remark: "坐席接待"},
|
||||
|
||||
Vendored
+2
-2
@@ -1,8 +1,8 @@
|
||||
package seeds
|
||||
|
||||
import (
|
||||
"agent-desk/cmd/testdata/seedlang"
|
||||
"agent-desk/internal/pkg/enums"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seedlang"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
)
|
||||
|
||||
type QuickReplySeed struct {
|
||||
|
||||
Vendored
+2
-2
@@ -1,8 +1,8 @@
|
||||
package seeds
|
||||
|
||||
import (
|
||||
"agent-desk/cmd/testdata/seedlang"
|
||||
"agent-desk/internal/pkg/enums"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seedlang"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
)
|
||||
|
||||
type SkillDefinitionSeed struct {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
package seeds
|
||||
|
||||
import "agent-desk/cmd/testdata/seedlang"
|
||||
import "code.tczkiot.com/wlw/ai-agent/cmd/testdata/seedlang"
|
||||
|
||||
type TagSeed struct {
|
||||
ID int64
|
||||
|
||||
Vendored
+4
-4
@@ -1,10 +1,10 @@
|
||||
package skill
|
||||
|
||||
import (
|
||||
"agent-desk/cmd/testdata/seedlang"
|
||||
"agent-desk/cmd/testdata/seeds"
|
||||
"agent-desk/internal/models"
|
||||
"agent-desk/internal/repositories"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seedlang"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seeds"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/models"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/repositories"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
Vendored
+2
-2
@@ -1,8 +1,8 @@
|
||||
package skill
|
||||
|
||||
import (
|
||||
"agent-desk/cmd/testdata/seedlang"
|
||||
"agent-desk/cmd/testdata/seeds"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seedlang"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seeds"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
Vendored
+5
-5
@@ -1,11 +1,11 @@
|
||||
package tag
|
||||
|
||||
import (
|
||||
"agent-desk/cmd/testdata/seedlang"
|
||||
"agent-desk/cmd/testdata/seeds"
|
||||
"agent-desk/internal/models"
|
||||
"agent-desk/internal/pkg/enums"
|
||||
"agent-desk/internal/repositories"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seedlang"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seeds"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/models"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/repositories"
|
||||
"time"
|
||||
|
||||
"github.com/mlogclub/simple/sqls"
|
||||
|
||||
Vendored
+2
-2
@@ -1,8 +1,8 @@
|
||||
package tag
|
||||
|
||||
import (
|
||||
"agent-desk/cmd/testdata/seedlang"
|
||||
"agent-desk/cmd/testdata/seeds"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seedlang"
|
||||
"code.tczkiot.com/wlw/ai-agent/cmd/testdata/seeds"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user